mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-11 08:34:34 +02:00
Merge branch 'master' into adiumpush
This commit is contained in:
22
ChangeLog
22
ChangeLog
@@ -1,6 +1,16 @@
|
||||
Version 0.1.0:
|
||||
* Fixed stations so they resolve against all available sources instead of
|
||||
only local and friend's collections.
|
||||
* Add a Song seed for stations and automatic playlists, and allow dragging of
|
||||
any tracks to the New Stations entry to create a pre-seeded station.
|
||||
* Added auto-completion for artists while filling in a station or automatic
|
||||
playlist.
|
||||
* SOCKS5 proxy support. Noproxy hosts are also supported but no wildcard
|
||||
support (yet).
|
||||
* Support loading of - and exporting to - .xspf playlists
|
||||
* Added Tomahawk:// protocol support and share links for many things including
|
||||
tracks, playlists, and stations.
|
||||
* Autoload automatically detected resolvers on startup.
|
||||
* Fix issue where track resolving spinner never stopped if tracks were
|
||||
removed from playlist while resolving.
|
||||
* Twitter & Jabber profile pictures are shown.
|
||||
@@ -9,18 +19,26 @@ Version 0.1.0:
|
||||
* Add new Google account type that is a thin wrapper around a Jabber plugin.
|
||||
* Overhaul the settings dialog interface.
|
||||
* Resolvers can now be enabled and disabled, and some can be configured
|
||||
directly in Tomahawk.
|
||||
directly in Tomahawk, for example the new Spotify resolver.
|
||||
* Split playlists and stations in sources sidebar. Show Recently Played
|
||||
as a node under the Super Collection.
|
||||
* Fix massive speed bottleneck on startup in the case of many recently
|
||||
played playlists.
|
||||
* Removed filter and song view from Super Collection, coming back in the next
|
||||
release.
|
||||
* Browse and play collections in our snappy tree-mode, which also shows
|
||||
images for artists and albums.
|
||||
* Fixed crash that could occur when playing a track from a browser.
|
||||
* Fixed a crash caused by sources going on- or offline.
|
||||
* Fixed a crash caused by sources going on or offline.
|
||||
* Huge optimizations in the resolving pipeline.
|
||||
* Improved the handling of automatic status messages for Google Talk
|
||||
* Switch to Phonon sound system, allowing us to support a wide variety
|
||||
of audio formats.
|
||||
* UI tweaks and cleanup.
|
||||
* (OS X) Open configuration dialogs as sliding sheets.
|
||||
* (OS X) Increase our available file watches to the maximum that the system
|
||||
reports.
|
||||
* (OS X) Added 'Window' menu with zoom/minimize actions
|
||||
|
||||
Version 0.0.3:
|
||||
* Show spinner while resolving playlists.
|
||||
|
@@ -85,8 +85,8 @@ function deplib_change
|
||||
install_name_tool -change $ORIGROOT/libtomahawk_sipzeroconf.dylib @executable_path/libtomahawk_sipzeroconf.dylib $1
|
||||
install_name_tool -change $ORIGROOT/libtomahawk_qtweetlib.dylib @executable_path/libtomahawk_qtweetlib.dylib $1
|
||||
install_name_tool -change $ORIGROOT/libtomahawk_portfwd.dylib @executable_path/libtomahawk_portfwd.dylib $1
|
||||
install_name_tool -change $ORIGROOT/libjreen.0.dylib @executable_path/libjreen.0.dylib $1
|
||||
install_name_tool -change /usr/local/Cellar/jreen/HEAD/lib/libjreen.dylib @executable_path/libjreen.0.dylib $1
|
||||
install_name_tool -change $ORIGROOT/libjreen.dylib @executable_path/libjreen.dylib $1
|
||||
install_name_tool -change /usr/local/Cellar/jreen/HEAD/lib/libjreen.dylib @executable_path/libjreen.dylib $1
|
||||
install_name_tool -change /usr/local/Cellar/qca/2.0.2/lib/qca.framework/Versions/2/qca @executable_path/../Frameworks/qca.framework/Versions/2/qca $1
|
||||
install_name_tool -change /usr/local/Cellar/gettext/0.18.1.1/lib/libintl.8.dylib @executable_path/libintl.8.dylib $1
|
||||
install_name_tool -change /usr/local/Cellar/vlc-git/HEAD/lib/libvlc.5.dylib @executable_path/libvlc.5.dylib $1
|
||||
@@ -127,7 +127,7 @@ import_lib /usr/local/Cellar/vlc-git/HEAD/lib/libvlc.5.dylib
|
||||
import_lib /usr/local/Cellar/vlc-git/HEAD/lib/libvlccore.4.dylib
|
||||
import_lib /usr/local/Cellar/gettext/0.18.1.1/lib/libintl.8.dylib
|
||||
|
||||
import_lib $ORIGROOT/libjreen.0.dylib
|
||||
import_lib $ORIGROOT/libjreen.dylib
|
||||
import_lib $ORIGROOT/libtomahawklib.dylib
|
||||
import_lib $ORIGROOT/libtomahawk_sipjabber.dylib
|
||||
import_lib $ORIGROOT/libtomahawk_sipgoogle.dylib
|
||||
|
@@ -15,17 +15,18 @@
|
||||
;-----------------------------------------------------------------------------
|
||||
; Some paths.
|
||||
;-----------------------------------------------------------------------------
|
||||
!define MING_PATH "/usr/i686-w64-mingw32/sys-root/mingw"
|
||||
!ifndef MING_PATH
|
||||
!define MING_PATH "/usr/i686-w64-mingw32/sys-root/mingw"
|
||||
!endif
|
||||
!define MING_BIN "${MING_PATH}/bin"
|
||||
!define MING_DLL_PATH "${MING_BIN}"
|
||||
!define MING_LIB "${MING_PATH}/lib"
|
||||
!define ROOT_PATH "..\..\.." ; assuming the script is in ROOT/admin/win/nsi
|
||||
!define BUILD_PATH "${ROOT_PATH}\build"
|
||||
!define QT_DLL_PATH "${MING_BIN}"
|
||||
!define SQLITE_DLL_PATH "${MING_LIB}/qt4/plugins/sqldrivers"
|
||||
!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt4/plugins/imageformats"
|
||||
!define VLC_PATH "${MING_BIN}"
|
||||
!define VLC_PLUGIN_PATH "${MING_LIB}\vlc\plugins"
|
||||
!define VLC_PATH "${ROOT_PATH}\..\vlc"
|
||||
!define VLC_PLUGIN_PATH "${VLC_PATH}\plugins"
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Increment installer revision number as part of this script.
|
||||
@@ -316,20 +317,20 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
;Cygwin/c++ stuff
|
||||
;File "${MING_DLL_PATH}\cygmad-0.dll"
|
||||
;File "${MING_DLL_PATH}\libgcc_s_dw2-1.dll"
|
||||
;File "${MING_DLL_PATH}\mingwm10.dll"
|
||||
File "${MING_DLL_PATH}\libgcc_s_sjlj-1.dll"
|
||||
File "${MING_DLL_PATH}\libstdc++-6.dll"
|
||||
;File "${MING_BIN}\cygmad-0.dll"
|
||||
;File "${MING_BIN}\libgcc_s_dw2-1.dll"
|
||||
;File "${MING_BIN}\mingwm10.dll"
|
||||
File "${MING_BIN}\libgcc_s_sjlj-1.dll"
|
||||
File "${MING_BIN}\libstdc++-6.dll"
|
||||
|
||||
;Phonon stuff
|
||||
|
||||
;Fix the phonon build to not use Dbus
|
||||
File "${QT_DLL_PATH}\QtDbus4.dll"
|
||||
File "${MING_DLL_PATH}\libdbus-1-3.dll"
|
||||
File "${MING_DLL_PATH}\dbus-daemon.exe"
|
||||
File "${MING_BIN}\libdbus-1-3.dll"
|
||||
File "${MING_BIN}\dbus-daemon.exe"
|
||||
|
||||
File "${MING_DLL_PATH}\libphonon.dll"
|
||||
File "${MING_BIN}\libphonon.dll"
|
||||
SetOutPath "$INSTDIR\phonon_backend"
|
||||
File "${MING_BIN}\phonon_backend\phonon_vlc.dll"
|
||||
SetOutPath "$INSTDIR"
|
||||
@@ -350,13 +351,13 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
|
||||
|
||||
|
||||
; Other
|
||||
File "${MING_DLL_PATH}\libqjson.dll"
|
||||
File "${MING_DLL_PATH}\libtag.dll"
|
||||
File "${MING_DLL_PATH}\libpng15-15.dll"
|
||||
File "${MING_DLL_PATH}\libjpeg-8.dll"
|
||||
File "${MING_DLL_PATH}\zlib1.dll"
|
||||
File "${MING_BIN}\libqjson.dll"
|
||||
File "${MING_BIN}\libtag.dll"
|
||||
File "${MING_BIN}\libpng15-15.dll"
|
||||
File "${MING_BIN}\libjpeg-8.dll"
|
||||
File "${MING_BIN}\zlib1.dll"
|
||||
|
||||
File "${MING_DLL_PATH}\libechonest.dll"
|
||||
File "${MING_BIN}\libechonest.dll"
|
||||
File "${MING_BIN}\libQTweetLib.dll"
|
||||
|
||||
; Jabber
|
||||
|
@@ -5,6 +5,8 @@ SET( OS_SPECIFIC_LINK_LIBRARIES
|
||||
${COREAUDIO_LIBRARY}
|
||||
${COREFOUNDATION_LIBRARY}
|
||||
|
||||
SPMediaKeyTap
|
||||
|
||||
/System/Library/Frameworks/AppKit.framework
|
||||
/System/Library/Frameworks/Carbon.framework
|
||||
/System/Library/Frameworks/DiskArbitration.framework
|
||||
|
@@ -60,6 +60,7 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
|
||||
sourcetree/items/genericpageitems.cpp
|
||||
|
||||
transferview.cpp
|
||||
PipelineStatusView.cpp
|
||||
tomahawktrayicon.cpp
|
||||
audiocontrols.cpp
|
||||
settingsdialog.cpp
|
||||
@@ -102,6 +103,7 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui}
|
||||
sourcetree/items/genericpageitems.h
|
||||
|
||||
transferview.h
|
||||
PipelineStatusView.h
|
||||
tomahawktrayicon.h
|
||||
audiocontrols.h
|
||||
settingsdialog.h
|
||||
@@ -160,6 +162,8 @@ IF( UNIX )
|
||||
ENDIF( UNIX )
|
||||
|
||||
IF( APPLE )
|
||||
INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/thirdparty/SPMediaKeyTap )
|
||||
|
||||
SET( tomahawkHeaders ${tomahawkHeaders} mac/tomahawkapp_mac.h mac/macshortcuthandler.h )
|
||||
SET( tomahawkSources ${tomahawkSources} mac/tomahawkapp_mac.mm mac/macshortcuthandler.cpp )
|
||||
|
||||
|
104
src/PipelineStatusView.cpp
Normal file
104
src/PipelineStatusView.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PipelineStatusView.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "libtomahawk/pipeline.h"
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
|
||||
PipelineStatusView::PipelineStatusView( AnimatedSplitter* parent )
|
||||
: AnimatedWidget( parent )
|
||||
, m_parent( parent )
|
||||
{
|
||||
setHiddenSize( QSize( 0, 0 ) );
|
||||
setLayout( new QVBoxLayout() );
|
||||
m_tree = new QTreeWidget( this );
|
||||
|
||||
layout()->setMargin( 0 );
|
||||
layout()->addWidget( m_tree );
|
||||
|
||||
QStringList headers;
|
||||
headers << tr( "Searching For" ) << tr( "Pending" );
|
||||
m_tree->setHeaderLabels( headers );
|
||||
|
||||
m_tree->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored );
|
||||
m_tree->setColumnCount( 2 );
|
||||
m_tree->setColumnWidth( 0, 200 );
|
||||
m_tree->setColumnWidth( 1, 50 );
|
||||
|
||||
m_tree->header()->setStretchLastSection( true );
|
||||
m_tree->setRootIsDecorated( false );
|
||||
|
||||
m_tree->setFrameShape( QFrame::NoFrame );
|
||||
m_tree->setAttribute( Qt::WA_MacShowFocusRect, 0 );
|
||||
|
||||
new QTreeWidgetItem( m_tree );
|
||||
|
||||
connect( Pipeline::instance(), SIGNAL( resolving( Tomahawk::query_ptr ) ), SLOT( onPipelineUpdate( Tomahawk::query_ptr ) ) );
|
||||
connect( Pipeline::instance(), SIGNAL( idle() ), SLOT( onPipelineUpdate() ) );
|
||||
|
||||
onPipelineUpdate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PipelineStatusView::onPipelineUpdate( const query_ptr& query )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
QTreeWidgetItem* ti = m_tree->invisibleRootItem()->child( 0 );
|
||||
|
||||
if ( Pipeline::instance()->activeQueryCount() && !query.isNull() )
|
||||
{
|
||||
ti->setText( 0, QString( "%1 - %2" ).arg( query->artist() ).arg( query->track() ) );
|
||||
ti->setText( 1, QString( "%1" ).arg( Pipeline::instance()->activeQueryCount() + Pipeline::instance()->pendingQueryCount() ) );
|
||||
|
||||
if ( isHidden() )
|
||||
emit showWidget();
|
||||
}
|
||||
else
|
||||
{
|
||||
ti->setText( 0, tr( "Idle" ) );
|
||||
ti->setText( 1, QString( "None" ) );
|
||||
|
||||
if ( !isHidden() )
|
||||
emit hideWidget();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QSize
|
||||
PipelineStatusView::sizeHint() const
|
||||
{
|
||||
unsigned int y = 0;
|
||||
y += m_tree->header()->height();
|
||||
y += m_tree->contentsMargins().top() + m_tree->contentsMargins().bottom();
|
||||
|
||||
if ( m_tree->invisibleRootItem()->childCount() )
|
||||
{
|
||||
unsigned int rowheight = m_tree->sizeHintForRow( 0 );
|
||||
y += rowheight * m_tree->invisibleRootItem()->childCount() + 2;
|
||||
}
|
||||
|
||||
return QSize( 0, y );
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
@@ -16,28 +16,36 @@
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <AppKit/NSApplication.h>
|
||||
#ifndef PIPELINESTATUSVIEW_H
|
||||
#define PIPELINESTATUSVIEW_H
|
||||
|
||||
#include "config.h"
|
||||
#include <QDebug>
|
||||
#include <QTreeWidget>
|
||||
|
||||
// this file copied and inspired by mac_startup.* in clementine player,
|
||||
// copyright David Sansome 2010
|
||||
namespace Tomahawk {
|
||||
class PlatformInterface;
|
||||
}
|
||||
#include "typedefs.h"
|
||||
#include "utils/animatedsplitter.h"
|
||||
|
||||
#ifdef SNOW_LEOPARD
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate> {
|
||||
#else
|
||||
@interface AppDelegate : NSObject {
|
||||
#endif
|
||||
Tomahawk::PlatformInterface* application_handler_;
|
||||
//NSMenu* dock_menu_;
|
||||
}
|
||||
class StreamConnection;
|
||||
|
||||
- (id) initWithHandler: (Tomahawk::PlatformInterface*)handler;
|
||||
// NSApplicationDelegate
|
||||
- (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag;
|
||||
//- (NSMenu*) applicationDockMenu: (NSApplication*)sender;
|
||||
//- (void) setDockMenu: (NSMenu*)menu;
|
||||
@end
|
||||
class PipelineStatusView : public AnimatedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PipelineStatusView( AnimatedSplitter* parent );
|
||||
virtual ~PipelineStatusView()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
}
|
||||
|
||||
QSize sizeHint() const;
|
||||
|
||||
private slots:
|
||||
void onPipelineUpdate( const Tomahawk::query_ptr& query = Tomahawk::query_ptr() );
|
||||
|
||||
private:
|
||||
QTreeWidget* m_tree;
|
||||
AnimatedSplitter* m_parent;
|
||||
};
|
||||
|
||||
#endif // TRANSFERVIEW_H
|
@@ -80,27 +80,23 @@ AudioControls::AudioControls( QWidget* parent )
|
||||
ui->metaDataArea->setStyleSheet( "QWidget#metaDataArea {\nborder-width: 4px;\nborder-image: url(" RESPATH "images/now-playing-panel.png) 4 4 4 4 stretch stretch; }" );
|
||||
|
||||
ui->seekSlider->setFixedHeight( 20 );
|
||||
ui->seekSlider->setEnabled( false );
|
||||
ui->seekSlider->setEnabled( true );
|
||||
ui->seekSlider->setStyleSheet( "QSlider::groove::horizontal {"
|
||||
"margin: 5px; border-width: 3px;"
|
||||
"border-image: url(" RESPATH "images/seek-slider-bkg.png) 3 3 3 3 stretch stretch;"
|
||||
"}"
|
||||
|
||||
"QSlider::handle::horizontal {"
|
||||
"margin-left: 5px; margin-right: -5px; "
|
||||
"width: 0px;"
|
||||
|
||||
//"margin-bottom: -7px; margin-top: -7px;"
|
||||
//"height: 17px; width: 16px;"
|
||||
//"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
|
||||
//"background-repeat: no-repeat;"
|
||||
"}"
|
||||
|
||||
"QSlider::sub-page:horizontal {"
|
||||
"margin: 5px; border-width: 3px;"
|
||||
"border-image: url(" RESPATH "images/seek-slider-level.png) 3 3 3 3 stretch stretch;"
|
||||
"}"
|
||||
);
|
||||
|
||||
"QSlider::handle::horizontal {"
|
||||
"margin-bottom: -7px; margin-top: -7px;"
|
||||
"height: 17px; width: 16px;"
|
||||
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
|
||||
"background-repeat: no-repeat;"
|
||||
"}" );
|
||||
|
||||
ui->volumeSlider->setFixedHeight( 20 );
|
||||
ui->volumeSlider->setRange( 0, 100 );
|
||||
@@ -120,9 +116,7 @@ AudioControls::AudioControls( QWidget* parent )
|
||||
"height: 17px; width: 16px;"
|
||||
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
|
||||
"background-repeat: no-repeat;"
|
||||
"}"
|
||||
|
||||
);
|
||||
"}" );
|
||||
|
||||
/* m_playAction = new QAction( this );
|
||||
m_pauseAction = new QAction( this );
|
||||
@@ -134,6 +128,7 @@ AudioControls::AudioControls( QWidget* parent )
|
||||
connect( m_prevAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( previous() ) );
|
||||
connect( m_nextAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( next() ) ); */
|
||||
|
||||
connect( ui->seekSlider, SIGNAL( valueChanged( int ) ), AudioEngine::instance(), SLOT( seek( int ) ) );
|
||||
connect( ui->volumeSlider, SIGNAL( valueChanged( int ) ), AudioEngine::instance(), SLOT( setVolume( int ) ) );
|
||||
connect( ui->prevButton, SIGNAL( clicked() ), AudioEngine::instance(), SLOT( previous() ) );
|
||||
connect( ui->playPauseButton, SIGNAL( clicked() ), AudioEngine::instance(), SLOT( play() ) );
|
||||
@@ -283,13 +278,11 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result )
|
||||
ui->ownerLabel->setText( result->friendlySource() );
|
||||
ui->coverImage->setPixmap( m_defaultCover );
|
||||
|
||||
if ( ui->timeLabel->text().isEmpty() )
|
||||
ui->timeLabel->setText( TomahawkUtils::timeToString( 0 ) );
|
||||
|
||||
if ( ui->timeLeftLabel->text().isEmpty() )
|
||||
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( result->duration() ) );
|
||||
ui->timeLabel->setText( TomahawkUtils::timeToString( 0 ) );
|
||||
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( result->duration() ) );
|
||||
|
||||
ui->seekSlider->setRange( 0, m_currentTrack->duration() * 1000 );
|
||||
ui->seekSlider->setValue( 0 );
|
||||
ui->seekSlider->setVisible( true );
|
||||
|
||||
/* m_playAction->setEnabled( false );
|
||||
@@ -357,10 +350,14 @@ AudioControls::onPlaybackTimer( qint64 msElapsed )
|
||||
if ( m_currentTrack.isNull() )
|
||||
return;
|
||||
|
||||
ui->seekSlider->blockSignals( true );
|
||||
|
||||
const int seconds = msElapsed / 1000;
|
||||
ui->timeLabel->setText( TomahawkUtils::timeToString( seconds ) );
|
||||
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( m_currentTrack->duration() - seconds ) );
|
||||
ui->seekSlider->setValue( msElapsed );
|
||||
|
||||
ui->seekSlider->blockSignals( false );
|
||||
}
|
||||
|
||||
|
||||
|
@@ -28,7 +28,6 @@ class DelegateConfigWrapper : public QDialog
|
||||
public:
|
||||
DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags = 0 ) : QDialog( parent, flags ), m_widget( conf )
|
||||
{
|
||||
m_widget->setVisible( true );
|
||||
m_widget->setWindowFlags( Qt::Sheet );
|
||||
|
||||
setWindowTitle( title );
|
||||
@@ -47,7 +46,10 @@ public:
|
||||
setSizeGripEnabled( false );
|
||||
setMinimumSize( sizeHint() );
|
||||
setMaximumSize( sizeHint() ); // to remove the resize grip on osx this is the only way
|
||||
|
||||
connect( conf, SIGNAL( sizeHintChanged() ), this, SLOT( updateSizeHint() ) );
|
||||
#endif
|
||||
m_widget->setVisible( true );
|
||||
}
|
||||
public slots:
|
||||
void closed( QAbstractButton* b )
|
||||
@@ -72,6 +74,15 @@ public slots:
|
||||
m_widget->setVisible( false );
|
||||
}
|
||||
|
||||
void updateSizeHint() {
|
||||
hide();
|
||||
setSizeGripEnabled( false );
|
||||
setMinimumSize( sizeHint() );
|
||||
setMaximumSize( sizeHint() );
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
private:
|
||||
QWidget* m_widget;
|
||||
};
|
||||
|
@@ -161,6 +161,7 @@ set( libSources
|
||||
utils/xspfgenerator.cpp
|
||||
|
||||
widgets/newplaylistwidget.cpp
|
||||
widgets/playlisttypeselectordlg.cpp
|
||||
widgets/welcomewidget.cpp
|
||||
widgets/welcomeplaylistmodel.cpp
|
||||
widgets/overlaywidget.cpp
|
||||
@@ -324,6 +325,7 @@ set( libHeaders
|
||||
utils/xspfgenerator.h
|
||||
|
||||
widgets/newplaylistwidget.h
|
||||
widgets/playlisttypeselectordlg.h
|
||||
widgets/welcomewidget.h
|
||||
widgets/welcomeplaylistmodel.h
|
||||
widgets/overlaywidget.h
|
||||
@@ -339,6 +341,7 @@ set( libHeaders_NoMOC
|
||||
playlist/dynamic/GeneratorInterface.h
|
||||
)
|
||||
set( libUI ${libUI}
|
||||
widgets/playlisttypeselectordlg.ui
|
||||
widgets/newplaylistwidget.ui
|
||||
widgets/welcomewidget.ui
|
||||
widgets/infowidgets/sourceinfowidget.ui
|
||||
|
@@ -66,7 +66,8 @@ AudioEngine::~AudioEngine()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
stop();
|
||||
m_mediaObject->stop();
|
||||
// stop();
|
||||
|
||||
delete m_audioOutput;
|
||||
delete m_mediaObject;
|
||||
@@ -148,6 +149,17 @@ AudioEngine::next()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioEngine::seek( int ms )
|
||||
{
|
||||
if ( isPlaying() || isPaused() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << ms;
|
||||
m_mediaObject->seek( ms );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioEngine::setVolume( int percentage )
|
||||
{
|
||||
@@ -188,7 +200,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
|
||||
{
|
||||
setCurrentTrack( result );
|
||||
|
||||
if ( !isHttpResult( m_currentTrack->url() ) )
|
||||
if ( !isHttpResult( m_currentTrack->url() ) && !isLocalResult( m_currentTrack->url() ) )
|
||||
{
|
||||
io = Servent::instance()->getIODeviceForUrl( m_currentTrack );
|
||||
|
||||
@@ -216,7 +228,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
|
||||
m_expectStop = true;
|
||||
}
|
||||
|
||||
if ( !isHttpResult( m_currentTrack->url() ) )
|
||||
if ( !isHttpResult( m_currentTrack->url() ) && !isLocalResult( m_currentTrack->url() ) )
|
||||
{
|
||||
m_mediaObject->setCurrentSource( io.data() );
|
||||
m_mediaObject->currentSource().setAutoDelete( false );
|
||||
@@ -229,7 +241,6 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
|
||||
{
|
||||
furl = QUrl( m_currentTrack->url().left( m_currentTrack->url().indexOf( '?' ) ) );
|
||||
furl.setEncodedQuery( QString( m_currentTrack->url().mid( m_currentTrack->url().indexOf( '?' ) + 1 ) ).toLocal8Bit() );
|
||||
qDebug() << Q_FUNC_INFO << furl;
|
||||
}
|
||||
m_mediaObject->setCurrentSource( furl );
|
||||
m_mediaObject->currentSource().setAutoDelete( true );
|
||||
@@ -322,10 +333,27 @@ void
|
||||
AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << oldState << newState;
|
||||
|
||||
if ( newState == Phonon::ErrorState )
|
||||
{
|
||||
qDebug() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType();
|
||||
}
|
||||
if ( oldState == Phonon::PlayingState && newState == Phonon::StoppedState )
|
||||
{
|
||||
qDebug() << "Expecting stop?" << m_expectStop;
|
||||
if ( !m_expectStop )
|
||||
{
|
||||
qDebug() << "Loading next track.";
|
||||
m_expectStop = false;
|
||||
loadNextTrack();
|
||||
}
|
||||
}
|
||||
else if ( oldState == Phonon::PlayingState && newState == Phonon::PausedState )
|
||||
{
|
||||
qDebug() << m_mediaObject->currentTime() << m_mediaObject->totalTime();
|
||||
if ( m_mediaObject->currentTime() == m_mediaObject->totalTime() )
|
||||
{
|
||||
qDebug() << "Loading next track.";
|
||||
m_expectStop = false;
|
||||
loadNextTrack();
|
||||
}
|
||||
@@ -380,8 +408,16 @@ AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result )
|
||||
m_currentTrack = result;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
AudioEngine::isHttpResult( const QString& url ) const
|
||||
{
|
||||
return url.startsWith( "http://" );
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
AudioEngine::isLocalResult( const QString& url ) const
|
||||
{
|
||||
return url.startsWith( "file://" );
|
||||
}
|
||||
|
@@ -64,6 +64,7 @@ public slots:
|
||||
void previous();
|
||||
void next();
|
||||
|
||||
void seek( int ms );
|
||||
void setVolume( int percentage );
|
||||
void lowerVolume() { setVolume( volume() - AUDIO_VOLUME_STEP ); }
|
||||
void raiseVolume() { setVolume( volume() + AUDIO_VOLUME_STEP ); }
|
||||
@@ -106,6 +107,7 @@ private slots:
|
||||
|
||||
private:
|
||||
bool isHttpResult( const QString& ) const;
|
||||
bool isLocalResult( const QString& ) const;
|
||||
|
||||
bool m_isPlayingHttp;
|
||||
QSharedPointer<QIODevice> m_input;
|
||||
|
@@ -45,7 +45,7 @@ DatabaseCommand_LogPlayback::postCommitHook()
|
||||
{
|
||||
emit trackPlayed( q );
|
||||
}
|
||||
else if ( m_action == Started )
|
||||
else if ( m_action == Started && QDateTime::fromTime_t( playtime() ).secsTo( QDateTime::currentDateTime() ) < 600 ) // if the play time is more than 10 minutes in the past, ignore
|
||||
{
|
||||
emit trackPlaying( q );
|
||||
}
|
||||
|
@@ -42,7 +42,10 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
|
||||
qDebug() << "Using result-hint to speed up resolving:" << m_query->resultHint();
|
||||
|
||||
Tomahawk::result_ptr result = lib->resultFromHint( m_query );
|
||||
if ( !result.isNull() && result->collection()->source()->isOnline() )
|
||||
/* qDebug() << "Result null:" << result.isNull();
|
||||
qDebug() << "Collection null:" << result->collection().isNull();
|
||||
qDebug() << "Source null:" << result->collection()->source().isNull();*/
|
||||
if ( !result.isNull() && !result->collection().isNull() && result->collection()->source()->isOnline() )
|
||||
{
|
||||
res << result;
|
||||
emit results( m_query->id(), res );
|
||||
|
@@ -85,14 +85,14 @@ GlobalActionManager::openLinkFromQuery( const Tomahawk::query_ptr& query ) const
|
||||
return link;
|
||||
}
|
||||
|
||||
void
|
||||
QString
|
||||
GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist )
|
||||
{
|
||||
QUrl link( QString( "tomahawk://%1/create/" ).arg( playlist->mode() == Tomahawk::OnDemand ? "station" : "autoplaylist" ) );
|
||||
|
||||
if( playlist->generator()->type() != "echonest" ) {
|
||||
qDebug() << "Only echonest generators are supported";
|
||||
return;
|
||||
return QString();
|
||||
}
|
||||
|
||||
link.addEncodedQueryItem( "type", "echonest" );
|
||||
@@ -123,6 +123,8 @@ GlobalActionManager::copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& p
|
||||
|
||||
QClipboard* cb = QApplication::clipboard();
|
||||
cb->setText( link.toEncoded() );
|
||||
|
||||
return link.toString();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -380,22 +382,22 @@ GlobalActionManager::handleSearchCommand( const QUrl& url )
|
||||
bool
|
||||
GlobalActionManager::handleAutoPlaylistCommand( const QUrl& url )
|
||||
{
|
||||
return loadDynamicPlaylist( url, false );
|
||||
return !loadDynamicPlaylist( url, false ).isNull();
|
||||
}
|
||||
|
||||
bool
|
||||
Tomahawk::dynplaylist_ptr
|
||||
GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station )
|
||||
{
|
||||
QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command
|
||||
if( parts.isEmpty() ) {
|
||||
qDebug() << "No specific station command:" << url.toString();
|
||||
return false;
|
||||
return Tomahawk::dynplaylist_ptr();
|
||||
}
|
||||
|
||||
if( parts[ 0 ] == "create" ) {
|
||||
if( !url.hasQueryItem( "title" ) || !url.hasQueryItem( "type" ) ) {
|
||||
qDebug() << "Station create command needs title and type..." << url.toString();
|
||||
return false;
|
||||
return Tomahawk::dynplaylist_ptr();
|
||||
}
|
||||
QString title = url.queryItemValue( "title" );
|
||||
QString type = url.queryItemValue( "type" );
|
||||
@@ -520,17 +522,17 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station )
|
||||
else
|
||||
pl->createNewRevision( uuid(), pl->currentrevision(), type, controls, pl->entries() );
|
||||
|
||||
return true;
|
||||
return pl;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Tomahawk::dynplaylist_ptr();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
GlobalActionManager::handleStationCommand( const QUrl& url )
|
||||
{
|
||||
return loadDynamicPlaylist( url, true );
|
||||
return !loadDynamicPlaylist( url, true ).isNull();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@@ -38,13 +38,14 @@ public:
|
||||
QUrl openLinkFromQuery( const Tomahawk::query_ptr& query ) const;
|
||||
|
||||
void copyToClipboard( const Tomahawk::query_ptr& query ) const;
|
||||
void copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist );
|
||||
QString copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist );
|
||||
void savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename );
|
||||
|
||||
public slots:
|
||||
bool parseTomahawkLink( const QString& link );
|
||||
void waitingForResolved( bool );
|
||||
|
||||
Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station );
|
||||
private slots:
|
||||
void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl );
|
||||
void showPlaylist();
|
||||
@@ -65,7 +66,6 @@ private:
|
||||
bool handleBookmarkCommand(const QUrl& url );
|
||||
bool handleOpenCommand(const QUrl& url );
|
||||
|
||||
bool loadDynamicPlaylist( const QUrl& url, bool station );
|
||||
bool doQueueAdd( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems );
|
||||
|
||||
Tomahawk::playlist_ptr m_toShow;
|
||||
|
@@ -84,59 +84,28 @@ InfoSystem::InfoSystem(QObject *parent)
|
||||
InfoSystem::~InfoSystem()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " beginning";
|
||||
|
||||
|
||||
if ( !m_worker.isNull() )
|
||||
{
|
||||
QMetaObject::invokeMethod( m_worker.data(), "deleteLater", Qt::QueuedConnection );
|
||||
while( !m_worker.isNull() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " worker not deleted, processing events";
|
||||
TomahawkUtils::Sleep::msleep( 50 );
|
||||
}
|
||||
m_infoSystemWorkerThreadController->quit();
|
||||
m_infoSystemWorkerThreadController->wait( 60000 );
|
||||
|
||||
if ( m_infoSystemWorkerThreadController )
|
||||
m_infoSystemWorkerThreadController->quit();
|
||||
|
||||
if( m_infoSystemWorkerThreadController )
|
||||
{
|
||||
while( !m_infoSystemWorkerThreadController->isFinished() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " worker thread controller not finished, processing events";
|
||||
TomahawkUtils::Sleep::msleep( 50 );
|
||||
}
|
||||
|
||||
delete m_infoSystemWorkerThreadController;
|
||||
m_infoSystemWorkerThreadController = 0;
|
||||
}
|
||||
delete m_worker.data();
|
||||
delete m_infoSystemWorkerThreadController;
|
||||
m_infoSystemWorkerThreadController = 0;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << " done deleting worker";
|
||||
|
||||
if ( !m_cache.isNull() )
|
||||
{
|
||||
QMetaObject::invokeMethod( m_cache.data(), "deleteLater", Qt::QueuedConnection );
|
||||
while( !m_cache.isNull() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " worker not deleted, processing events";
|
||||
TomahawkUtils::Sleep::msleep( 50 );
|
||||
}
|
||||
|
||||
if ( m_infoSystemCacheThreadController )
|
||||
m_infoSystemCacheThreadController->quit();
|
||||
|
||||
if( m_infoSystemCacheThreadController )
|
||||
{
|
||||
while( !m_infoSystemCacheThreadController->isFinished() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " worker thread controller not finished, processing events";
|
||||
TomahawkUtils::Sleep::msleep( 50 );
|
||||
}
|
||||
|
||||
delete m_infoSystemCacheThreadController;
|
||||
m_infoSystemCacheThreadController = 0;
|
||||
}
|
||||
m_infoSystemCacheThreadController->quit();
|
||||
m_infoSystemCacheThreadController->wait( 60000 );
|
||||
|
||||
delete m_cache.data();
|
||||
delete m_infoSystemCacheThreadController;
|
||||
m_infoSystemCacheThreadController = 0;
|
||||
}
|
||||
|
||||
|
||||
qDebug() << Q_FUNC_INFO << " done deleting cache";
|
||||
}
|
||||
|
||||
|
@@ -311,6 +311,7 @@ Pipeline::shunt( const query_ptr& q )
|
||||
|
||||
qDebug() << "Dispatching to resolver" << r->name() << q->toString() << q->solved() << q->id();
|
||||
r->resolve( q );
|
||||
emit resolving( q );
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
@@ -46,6 +46,9 @@ public:
|
||||
explicit Pipeline( QObject* parent = 0 );
|
||||
virtual ~Pipeline();
|
||||
|
||||
unsigned int pendingQueryCount() const { return m_queries_pending.count(); }
|
||||
unsigned int activeQueryCount() const { return m_qidsState.count(); }
|
||||
|
||||
void reportResults( QID qid, const QList< result_ptr >& results );
|
||||
|
||||
/// sorter to rank resolver priority
|
||||
@@ -75,6 +78,7 @@ public slots:
|
||||
|
||||
signals:
|
||||
void idle();
|
||||
void resolving( const Tomahawk::query_ptr& query );
|
||||
|
||||
private slots:
|
||||
void timeoutShunt( const query_ptr& q );
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "database/databasecommand_deleteplaylist.h"
|
||||
#include "database/databasecommand_renameplaylist.h"
|
||||
|
||||
#include "tomahawksettings.h"
|
||||
#include "pipeline.h"
|
||||
#include "source.h"
|
||||
#include "sourcelist.h"
|
||||
@@ -219,6 +220,9 @@ Playlist::load( const QString& guid )
|
||||
bool
|
||||
Playlist::remove( const playlist_ptr& playlist )
|
||||
{
|
||||
TomahawkSettings *s = TomahawkSettings::instance();
|
||||
s->removePlaylistSettings( playlist->guid() );
|
||||
|
||||
DatabaseCommand_DeletePlaylist* cmd = new DatabaseCommand_DeletePlaylist( playlist->author(), playlist->guid() );
|
||||
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
|
||||
|
||||
|
@@ -64,7 +64,10 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load
|
||||
QString
|
||||
DynamicModel::description() const
|
||||
{
|
||||
return m_playlist->generator()->sentenceSummary();
|
||||
if( !m_playlist.isNull() && !m_playlist->generator().isNull() )
|
||||
return m_playlist->generator()->sentenceSummary();
|
||||
else
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
|
@@ -63,7 +63,11 @@ DynamicControlList::init()
|
||||
m_layout->setColumnStretch( 2, 1 );
|
||||
m_layout->setMargin( 0 );
|
||||
m_layout->setVerticalSpacing( 0 );
|
||||
#ifdef Q_WS_MAC // on OS X we don't want the right edge of the toolbuttons against the window
|
||||
m_layout->setContentsMargins( 0, 0, 3, 0 );
|
||||
#else
|
||||
m_layout->setContentsMargins( 0, 0, 0, 0 );
|
||||
#endif
|
||||
m_layout->setSizeConstraint( QLayout::SetMinimumSize );
|
||||
|
||||
m_collapseLayout = new QHBoxLayout();
|
||||
@@ -139,7 +143,7 @@ void DynamicControlList::addNewControl()
|
||||
m_layout->removeItem( m_collapseLayout );
|
||||
|
||||
dyncontrol_ptr control = m_generator->createControl();
|
||||
m_controls.append( new DynamicControlWrapper( control, m_layout, m_controls.size(), this ) );
|
||||
m_controls.append( new DynamicControlWrapper( control, m_layout, m_layout->rowCount(), this ) );
|
||||
connect( m_controls.last(), SIGNAL( removeControl() ), this, SLOT( removeControl() ) );
|
||||
connect( m_controls.last(), SIGNAL( changed() ), this, SLOT( controlChanged() ) );
|
||||
|
||||
|
@@ -41,7 +41,6 @@ DynamicControlWrapper::DynamicControlWrapper( const Tomahawk::dyncontrol_ptr& co
|
||||
, m_typeSelector( 0 )
|
||||
, m_layout( QWeakPointer< QGridLayout >( layout ) )
|
||||
{
|
||||
|
||||
qDebug() << "CREATING DYNAMIC CONTROL WRAPPER WITH ROW:" << row << layout;
|
||||
|
||||
m_typeSelector = new QComboBox( m_parent );
|
||||
@@ -74,8 +73,6 @@ DynamicControlWrapper::DynamicControlWrapper( const Tomahawk::dyncontrol_ptr& co
|
||||
|
||||
m_layout.data()->addLayout( m_plusL, m_row, 3, Qt::AlignCenter );
|
||||
m_plusL->setCurrentIndex( 0 );
|
||||
|
||||
|
||||
}
|
||||
|
||||
DynamicControlWrapper::~DynamicControlWrapper()
|
||||
|
@@ -424,6 +424,7 @@ DynamicWidget::onDeleted()
|
||||
void
|
||||
DynamicWidget::onChanged()
|
||||
{
|
||||
if( !m_playlist.isNull() )
|
||||
if( !m_playlist.isNull() &&
|
||||
ViewManager::instance()->currentPage() == this )
|
||||
emit nameChanged( m_playlist->title() );
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ using namespace Tomahawk;
|
||||
PlaylistModel::PlaylistModel( QObject* parent )
|
||||
: TrackModel( parent )
|
||||
, m_waitForUpdate( false )
|
||||
, m_isTemporary( false )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
@@ -91,6 +92,7 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn
|
||||
setTitle( playlist->title() );
|
||||
setDescription( tr( "A playlist by %1" ).arg( playlist->author()->isLocal() ? tr( "you" ) : playlist->author()->friendlyName() ) );
|
||||
|
||||
m_isTemporary = false;
|
||||
if ( !loadEntries )
|
||||
return;
|
||||
|
||||
@@ -192,6 +194,12 @@ PlaylistModel::append( const Tomahawk::album_ptr& album )
|
||||
connect( album.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
|
||||
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
|
||||
|
||||
if( rowCount( QModelIndex() ) == 0 ) {
|
||||
setTitle( album->name() );
|
||||
setDescription( tr( "All tracks by %1 on album %2" ).arg( album->artist()->name() ).arg( album->name() ) );
|
||||
m_isTemporary = true;
|
||||
}
|
||||
|
||||
onTracksAdded( album->tracks() );
|
||||
}
|
||||
|
||||
@@ -205,6 +213,12 @@ PlaylistModel::append( const Tomahawk::artist_ptr& artist )
|
||||
connect( artist.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
|
||||
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
|
||||
|
||||
if( rowCount( QModelIndex() ) == 0 ) {
|
||||
setTitle( artist->name() );
|
||||
setDescription( tr( "All tracks by %1" ).arg( artist->name() ) );
|
||||
m_isTemporary = true;
|
||||
}
|
||||
|
||||
onTracksAdded( artist->tracks() );
|
||||
}
|
||||
|
||||
@@ -494,3 +508,9 @@ PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome )
|
||||
onPlaylistChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PlaylistModel::isTemporary() const
|
||||
{
|
||||
return m_isTemporary;
|
||||
}
|
||||
|
@@ -67,6 +67,7 @@ public:
|
||||
void remove( unsigned int row, bool moreToCome = false );
|
||||
virtual void removeIndex( const QModelIndex& index, bool moreToCome = false );
|
||||
|
||||
bool isTemporary() const;
|
||||
signals:
|
||||
void repeatModeChanged( PlaylistInterface::RepeatMode mode );
|
||||
void shuffleModeChanged( bool enabled );
|
||||
@@ -90,7 +91,7 @@ private:
|
||||
QList<Tomahawk::plentry_ptr> playlistEntries() const;
|
||||
|
||||
Tomahawk::playlist_ptr m_playlist;
|
||||
bool m_waitForUpdate;
|
||||
bool m_waitForUpdate, m_isTemporary;
|
||||
QList< Tomahawk::Query* > m_waitingForResolved;
|
||||
};
|
||||
|
||||
|
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "playlist/playlistproxymodel.h"
|
||||
#include "widgets/overlaywidget.h"
|
||||
#include "viewmanager.h"
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@@ -70,8 +71,12 @@ PlaylistView::setPlaylistModel( PlaylistModel* model )
|
||||
if ( !m_model->playlist().isNull() )
|
||||
setGuid( QString( "playlistview/%1" ).arg( m_model->playlist()->guid() ) );
|
||||
else
|
||||
{
|
||||
setGuid( "playlistview" );
|
||||
|
||||
m_model->title();
|
||||
m_model->description();
|
||||
}
|
||||
connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) );
|
||||
connect( m_model, SIGNAL( playlistDeleted() ), SLOT( onDeleted() ) );
|
||||
connect( m_model, SIGNAL( playlistChanged() ), SLOT( onChanged() ) );
|
||||
@@ -94,6 +99,7 @@ PlaylistView::setupMenus()
|
||||
|
||||
foreach( QAction* a, actions() )
|
||||
m_itemMenu.addAction( a );
|
||||
|
||||
// m_addItemsToPlaylistAction = m_itemMenu.addAction( tr( "&Add to Playlist" ) );
|
||||
// m_itemMenu.addSeparator();
|
||||
m_deleteItemsAction = m_itemMenu.addAction( i > 1 ? tr( "&Delete Items" ) : tr( "&Delete Item" ) );
|
||||
@@ -134,7 +140,7 @@ PlaylistView::keyPressEvent( QKeyEvent* event )
|
||||
if ( !model() )
|
||||
return;
|
||||
|
||||
if ( event->key() == Qt::Key_Delete && !model()->isReadOnly() )
|
||||
if ( ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace ) && !model()->isReadOnly() )
|
||||
{
|
||||
qDebug() << "Removing selected items";
|
||||
proxyModel()->removeIndexes( selectedIndexes() );
|
||||
@@ -186,6 +192,17 @@ PlaylistView::onDeleted()
|
||||
void
|
||||
PlaylistView::onChanged()
|
||||
{
|
||||
if ( m_model && !m_model->playlist().isNull() )
|
||||
if ( m_model && !m_model->playlist().isNull() &&
|
||||
ViewManager::instance()->currentPage() == this )
|
||||
emit nameChanged( m_model->playlist()->title() );
|
||||
}
|
||||
|
||||
bool
|
||||
PlaylistView::isTemporaryPage() const
|
||||
{
|
||||
if ( m_model ) {
|
||||
return m_model->isTemporary();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -52,6 +52,7 @@ public:
|
||||
virtual QPixmap pixmap() const { return QPixmap( RESPATH "images/playlist-icon.png" ); }
|
||||
|
||||
virtual bool jumpToCurrentTrack();
|
||||
virtual bool isTemporaryPage() const;
|
||||
|
||||
signals:
|
||||
void nameChanged( const QString& title );
|
||||
@@ -75,6 +76,8 @@ private:
|
||||
PlaylistModel* m_model;
|
||||
|
||||
QMenu m_itemMenu;
|
||||
QString m_customTitle;
|
||||
QString m_customDescripton;
|
||||
|
||||
QAction* m_playItemAction;
|
||||
QAction* m_addItemsToQueueAction;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
@@ -45,7 +45,7 @@ QueueView::QueueView( AnimatedSplitter* parent )
|
||||
m_queue->setFrameShape( QFrame::NoFrame );
|
||||
m_queue->setAttribute( Qt::WA_MacShowFocusRect, 0 );
|
||||
m_queue->overlay()->setEnabled( false );
|
||||
|
||||
|
||||
m_button = new QPushButton();
|
||||
m_button->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
|
||||
m_button->setText( tr( "Click to show queue" ) );
|
||||
@@ -64,13 +64,13 @@ QueueView::~QueueView()
|
||||
|
||||
|
||||
void
|
||||
QueueView::onShown( QWidget* widget )
|
||||
QueueView::onShown( QWidget* widget, bool animated )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << widget;
|
||||
if ( widget != this )
|
||||
return;
|
||||
|
||||
AnimatedWidget::onShown( widget );
|
||||
AnimatedWidget::onShown( widget, animated );
|
||||
|
||||
m_button->setText( tr( "Click to hide queue" ) );
|
||||
disconnect( m_button, SIGNAL( clicked() ), this, SIGNAL( showWidget() ) );
|
||||
@@ -79,14 +79,14 @@ QueueView::onShown( QWidget* widget )
|
||||
|
||||
|
||||
void
|
||||
QueueView::onHidden( QWidget* widget )
|
||||
QueueView::onHidden( QWidget* widget, bool animated )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << widget;
|
||||
if ( widget != this )
|
||||
return;
|
||||
|
||||
AnimatedWidget::onHidden( widget );
|
||||
|
||||
|
||||
AnimatedWidget::onHidden( widget, animated );
|
||||
|
||||
m_button->setText( tr( "Click to show queue" ) );
|
||||
disconnect( m_button, SIGNAL( clicked() ), this, SIGNAL( hideWidget() ) );
|
||||
connect( m_button, SIGNAL( clicked() ), SIGNAL( showWidget() ) );
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
@@ -39,8 +39,8 @@ public:
|
||||
QSize sizeHint() const { return QSize( 0, 200 ); }
|
||||
|
||||
public slots:
|
||||
virtual void onShown( QWidget* );
|
||||
virtual void onHidden( QWidget* );
|
||||
virtual void onShown( QWidget*, bool animated );
|
||||
virtual void onHidden( QWidget*, bool animated );
|
||||
|
||||
private:
|
||||
PlaylistView* m_queue;
|
||||
|
@@ -24,7 +24,6 @@
|
||||
#include "network/controlconnection.h"
|
||||
#include "database/databasecommand_addsource.h"
|
||||
#include "database/databasecommand_sourceoffline.h"
|
||||
#include "database/databasecommand_logplayback.h"
|
||||
#include "database/database.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
@@ -50,6 +49,10 @@ Source::Source( int id, const QString& username )
|
||||
m_isLocal = true;
|
||||
m_online = true;
|
||||
}
|
||||
|
||||
m_currentTrackTimer.setInterval( 600000 ); // 10 minutes
|
||||
m_currentTrackTimer.setSingleShot( true );
|
||||
connect( &m_currentTrackTimer, SIGNAL( timeout() ), this, SLOT( trackTimerFired() ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -278,4 +281,14 @@ Source::onPlaybackFinished( const Tomahawk::query_ptr& query )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << query->toString();
|
||||
emit playbackFinished( query );
|
||||
|
||||
m_currentTrackTimer.start();
|
||||
}
|
||||
|
||||
void
|
||||
Source::trackTimerFired()
|
||||
{
|
||||
m_currentTrack.clear();
|
||||
|
||||
emit stateChanged();
|
||||
}
|
||||
|
@@ -105,6 +105,7 @@ private slots:
|
||||
void onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::State oldstate, const QString& info );
|
||||
void onPlaybackStarted( const Tomahawk::query_ptr& query );
|
||||
void onPlaybackFinished( const Tomahawk::query_ptr& query );
|
||||
void trackTimerFired();
|
||||
|
||||
private:
|
||||
bool m_isLocal;
|
||||
@@ -118,6 +119,7 @@ private:
|
||||
|
||||
Tomahawk::query_ptr m_currentTrack;
|
||||
QString m_textStatus;
|
||||
QTimer m_currentTrackTimer;
|
||||
|
||||
ControlConnection* m_cc;
|
||||
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include "sip/SipHandler.h"
|
||||
#include "playlistinterface.h"
|
||||
|
||||
#define VERSION 3
|
||||
|
||||
@@ -187,7 +188,6 @@ TomahawkSettings::setWatchForChanges( bool watch )
|
||||
setValue( "watchForChanges", watch );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TomahawkSettings::setAcceptedLegalWarning( bool accept )
|
||||
{
|
||||
@@ -381,6 +381,36 @@ TomahawkSettings::setPlaylistColumnSizes( const QString& playlistid, const QByte
|
||||
setValue( QString( "ui/playlist/%1/columnSizes" ).arg( playlistid ), state );
|
||||
}
|
||||
|
||||
bool
|
||||
TomahawkSettings::shuffleState( const QString& playlistid ) const
|
||||
{
|
||||
return value( QString( "ui/playlist/%1/shuffleState" ).arg( playlistid )).toBool();
|
||||
}
|
||||
|
||||
void
|
||||
TomahawkSettings::setShuffleState( const QString& playlistid, bool state)
|
||||
{
|
||||
setValue( QString( "ui/playlist/%1/shuffleState" ).arg( playlistid ), state );
|
||||
}
|
||||
|
||||
void
|
||||
TomahawkSettings::removePlaylistSettings( const QString& playlistid )
|
||||
{
|
||||
remove( QString( "ui/playlist/%1/shuffleState" ).arg( playlistid ) );
|
||||
remove( QString( "ui/playlist/%1/repeatMode" ).arg( playlistid ) );
|
||||
}
|
||||
|
||||
void
|
||||
TomahawkSettings::setRepeatMode( const QString& playlistid, PlaylistInterface::RepeatMode mode )
|
||||
{
|
||||
setValue( QString( "ui/playlist/%1/repeatMode" ).arg( playlistid ), (int)mode );
|
||||
}
|
||||
|
||||
PlaylistInterface::RepeatMode
|
||||
TomahawkSettings::repeatMode( const QString& playlistid )
|
||||
{
|
||||
return (PlaylistInterface::RepeatMode)value( QString( "ui/playlist/%1/repeatMode" ).arg( playlistid )).toInt();
|
||||
}
|
||||
|
||||
QList<Tomahawk::playlist_ptr>
|
||||
TomahawkSettings::recentlyPlayedPlaylists() const
|
||||
|
@@ -68,6 +68,14 @@ public:
|
||||
QList<Tomahawk::playlist_ptr> recentlyPlayedPlaylists() const;
|
||||
QStringList recentlyPlayedPlaylistGuids( unsigned int amount = 0 ) const;
|
||||
void appendRecentlyPlayedPlaylist( const Tomahawk::playlist_ptr& playlist );
|
||||
|
||||
bool shuffleState( const QString& playlistid ) const;
|
||||
void setShuffleState( const QString& playlistid, bool state );
|
||||
PlaylistInterface::RepeatMode repeatMode( const QString& playlistid );
|
||||
void setRepeatMode( const QString& playlistid, PlaylistInterface::RepeatMode mode);
|
||||
|
||||
// remove shuffle state and repeat state
|
||||
void removePlaylistSettings( const QString& playlistid );
|
||||
|
||||
/// SIP plugins
|
||||
// all plugins we know about. loaded, unloaded, enabled, disabled.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
@@ -23,83 +23,25 @@
|
||||
|
||||
AnimatedSplitter::AnimatedSplitter( QWidget* parent )
|
||||
: QSplitter( parent )
|
||||
, m_animateIndex( -1 )
|
||||
, m_greedyIndex( 0 )
|
||||
{
|
||||
setHandleWidth( 1 );
|
||||
|
||||
m_timeLine = new QTimeLine( ANIMATION_TIME, this );
|
||||
m_timeLine->setUpdateInterval( 5 );
|
||||
m_timeLine->setEasingCurve( QEasingCurve::OutBack );
|
||||
|
||||
connect( m_timeLine, SIGNAL( frameChanged( int ) ), SLOT( onAnimationStep( int ) ) );
|
||||
connect( m_timeLine, SIGNAL( finished() ), SLOT( onAnimationFinished() ) );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AnimatedSplitter::show( int index, bool animate )
|
||||
{
|
||||
m_animateIndex = index;
|
||||
|
||||
QWidget* w = widget( index );
|
||||
QSize size = w->sizeHint();
|
||||
|
||||
if ( w->height() == size.height() )
|
||||
return;
|
||||
|
||||
emit shown( w );
|
||||
w->setMaximumHeight( QWIDGETSIZE_MAX );
|
||||
qDebug() << "animating to:" << size.height() << "from" << w->height();
|
||||
|
||||
m_animateForward = true;
|
||||
if ( animate )
|
||||
{
|
||||
if ( m_timeLine->state() == QTimeLine::Running )
|
||||
m_timeLine->stop();
|
||||
|
||||
m_timeLine->setFrameRange( w->height(), size.height() );
|
||||
m_timeLine->setDirection( QTimeLine::Forward );
|
||||
m_timeLine->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
onAnimationStep( size.height() );
|
||||
onAnimationFinished();
|
||||
}
|
||||
emit shown( w, animate );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AnimatedSplitter::hide( int index, bool animate )
|
||||
{
|
||||
m_animateIndex = index;
|
||||
|
||||
QWidget* w = widget( index );
|
||||
int minHeight = m_sizes.at( index ).height();
|
||||
|
||||
if ( w->height() == minHeight )
|
||||
return;
|
||||
|
||||
emit hidden( w );
|
||||
w->setMinimumHeight( minHeight );
|
||||
// qDebug() << "animating to:" << w->height() << "from" << minHeight;
|
||||
|
||||
m_animateForward = false;
|
||||
if ( animate )
|
||||
{
|
||||
if ( m_timeLine->state() == QTimeLine::Running )
|
||||
m_timeLine->stop();
|
||||
|
||||
m_timeLine->setFrameRange( minHeight, w->height() );
|
||||
m_timeLine->setDirection( QTimeLine::Backward );
|
||||
m_timeLine->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
onAnimationStep( minHeight );
|
||||
onAnimationFinished();
|
||||
}
|
||||
emit hidden( w, animate );
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +49,6 @@ void
|
||||
AnimatedSplitter::addWidget( QWidget* widget )
|
||||
{
|
||||
QSplitter::addWidget( widget );
|
||||
m_sizes << widget->minimumSize();
|
||||
}
|
||||
|
||||
|
||||
@@ -116,13 +57,11 @@ AnimatedSplitter::addWidget( AnimatedWidget* widget )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << widget;
|
||||
QSplitter::addWidget( widget );
|
||||
m_sizes << widget->hiddenSize();
|
||||
|
||||
connect( widget, SIGNAL( showWidget() ), SLOT( onShowRequest() ) );
|
||||
connect( widget, SIGNAL( hideWidget() ), SLOT( onHideRequest() ) );
|
||||
connect( widget, SIGNAL( hiddenSizeChanged() ), SLOT( onHiddenSizeChanged() ) );
|
||||
connect( this, SIGNAL( shown( QWidget* ) ), widget, SLOT( onShown( QWidget* ) ) );
|
||||
connect( this, SIGNAL( hidden( QWidget* ) ), widget, SLOT( onHidden( QWidget* ) ) );
|
||||
connect( this, SIGNAL( shown( QWidget*, bool ) ), widget, SLOT( onShown( QWidget*, bool ) ) );
|
||||
connect( this, SIGNAL( hidden( QWidget*, bool ) ), widget, SLOT( onHidden( QWidget*, bool ) ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -131,18 +70,9 @@ AnimatedSplitter::onShowRequest()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << sender();
|
||||
|
||||
int j = -1;
|
||||
for ( int i = 0; i < count(); i ++ )
|
||||
{
|
||||
if ( widget( i ) == sender() )
|
||||
{
|
||||
j = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( j > 0 )
|
||||
show( j );
|
||||
AnimatedWidget* w = (AnimatedWidget*)(sender());
|
||||
if ( indexOf( w ) > 0 )
|
||||
show( indexOf( w ) );
|
||||
else
|
||||
qDebug() << "Could not find widget:" << sender();
|
||||
}
|
||||
@@ -151,72 +81,16 @@ AnimatedSplitter::onShowRequest()
|
||||
void
|
||||
AnimatedSplitter::onHideRequest()
|
||||
{
|
||||
int j = -1;
|
||||
for ( int i = 0; i < count(); i ++ )
|
||||
{
|
||||
if ( widget( i ) == sender() )
|
||||
{
|
||||
j = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( j > 0 )
|
||||
hide( j );
|
||||
AnimatedWidget* w = (AnimatedWidget*)(sender());
|
||||
if ( indexOf( w ) > 0 )
|
||||
hide( indexOf( w ) );
|
||||
else
|
||||
qDebug() << "Could not find widget:" << sender();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AnimatedSplitter::onAnimationStep( int frame )
|
||||
{
|
||||
QList< int > sizes;
|
||||
|
||||
for ( int i = 0; i < count(); i ++ )
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
if ( i == m_greedyIndex )
|
||||
{
|
||||
j = height() - frame; // FIXME
|
||||
}
|
||||
else if ( i == m_animateIndex )
|
||||
{
|
||||
j = frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
j = widget( i )->height();
|
||||
}
|
||||
|
||||
sizes << j;
|
||||
}
|
||||
|
||||
setSizes( sizes );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AnimatedSplitter::onAnimationFinished()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
QWidget* w = widget( m_animateIndex );
|
||||
if ( m_animateForward )
|
||||
{
|
||||
w->setMinimumHeight( w->minimumHeight() );
|
||||
}
|
||||
else
|
||||
{
|
||||
w->setMaximumHeight( m_sizes.at( m_animateIndex ).height() );
|
||||
}
|
||||
|
||||
m_animateIndex = -1;
|
||||
}
|
||||
|
||||
void
|
||||
AnimatedSplitter::setGreedyWidget(int index)
|
||||
AnimatedSplitter::setGreedyWidget( int index )
|
||||
{
|
||||
m_greedyIndex = index;
|
||||
if( !widget( index ) )
|
||||
@@ -227,17 +101,7 @@ AnimatedSplitter::setGreedyWidget(int index)
|
||||
else
|
||||
policy.setVerticalStretch( 1 );
|
||||
widget( m_greedyIndex )->setSizePolicy( policy );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AnimatedSplitter::onHiddenSizeChanged()
|
||||
{
|
||||
AnimatedWidget* w = (AnimatedWidget*)(sender());
|
||||
int i = indexOf( w );
|
||||
|
||||
m_sizes.replace( i, w->hiddenSize() );
|
||||
}
|
||||
|
||||
|
||||
@@ -246,24 +110,98 @@ AnimatedWidget::AnimatedWidget( AnimatedSplitter* parent )
|
||||
, m_isHidden( false )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
m_timeLine = new QTimeLine( ANIMATION_TIME, this );
|
||||
m_timeLine->setUpdateInterval( 5 );
|
||||
m_timeLine->setEasingCurve( QEasingCurve::OutBack );
|
||||
|
||||
connect( m_timeLine, SIGNAL( frameChanged( int ) ), SLOT( onAnimationStep( int ) ) );
|
||||
connect( m_timeLine, SIGNAL( finished() ), SLOT( onAnimationFinished() ) );
|
||||
}
|
||||
|
||||
|
||||
AnimatedWidget::~AnimatedWidget()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AnimatedWidget::onShown( QWidget* )
|
||||
AnimatedWidget::onShown( QWidget* widget, bool animated )
|
||||
{
|
||||
if ( widget != this )
|
||||
return;
|
||||
|
||||
qDebug() << Q_FUNC_INFO << this;
|
||||
|
||||
m_animateForward = true;
|
||||
if ( animated )
|
||||
{
|
||||
if ( m_timeLine->state() == QTimeLine::Running )
|
||||
m_timeLine->stop();
|
||||
|
||||
m_timeLine->setFrameRange( height(), sizeHint().height() );
|
||||
m_timeLine->setDirection( QTimeLine::Forward );
|
||||
m_timeLine->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
onAnimationStep( sizeHint().height() );
|
||||
onAnimationFinished();
|
||||
}
|
||||
|
||||
m_isHidden = false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AnimatedWidget::onHidden( QWidget* )
|
||||
AnimatedWidget::onHidden( QWidget* widget, bool animated )
|
||||
{
|
||||
if ( widget != this )
|
||||
return;
|
||||
|
||||
qDebug() << Q_FUNC_INFO << this;
|
||||
|
||||
m_animateForward = false;
|
||||
int minHeight = hiddenSize().height();
|
||||
|
||||
if ( animated )
|
||||
{
|
||||
if ( m_timeLine->state() == QTimeLine::Running )
|
||||
m_timeLine->stop();
|
||||
|
||||
m_timeLine->setFrameRange( minHeight, height() );
|
||||
m_timeLine->setDirection( QTimeLine::Backward );
|
||||
m_timeLine->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
onAnimationStep( minHeight );
|
||||
onAnimationFinished();
|
||||
}
|
||||
|
||||
m_isHidden = true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AnimatedWidget::onAnimationStep( int frame )
|
||||
{
|
||||
setFixedHeight( frame );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AnimatedWidget::onAnimationFinished()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
if ( m_animateForward )
|
||||
{
|
||||
setMinimumHeight( hiddenSize().height() );
|
||||
setMaximumHeight( QWIDGETSIZE_MAX );
|
||||
}
|
||||
else
|
||||
{
|
||||
setFixedHeight( hiddenSize().height() );
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
@@ -43,56 +43,52 @@ public:
|
||||
void addWidget( AnimatedWidget* widget );
|
||||
|
||||
signals:
|
||||
void shown( QWidget* );
|
||||
void hidden( QWidget* );
|
||||
void shown( QWidget*, bool animated );
|
||||
void hidden( QWidget*, bool animated );
|
||||
|
||||
private slots:
|
||||
void onShowRequest();
|
||||
void onHideRequest();
|
||||
|
||||
void onAnimationStep( int frame );
|
||||
void onAnimationFinished();
|
||||
|
||||
void onHiddenSizeChanged();
|
||||
|
||||
private:
|
||||
int m_animateIndex;
|
||||
bool m_animateForward;
|
||||
|
||||
int m_greedyIndex;
|
||||
QList<QSize> m_sizes;
|
||||
QTimeLine* m_timeLine;
|
||||
};
|
||||
|
||||
class DLLEXPORT AnimatedWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AnimatedWidget( AnimatedSplitter* parent = 0 );
|
||||
explicit AnimatedWidget( AnimatedSplitter* parent );
|
||||
virtual ~AnimatedWidget();
|
||||
|
||||
|
||||
QSize hiddenSize() const { return m_hiddenSize; }
|
||||
void setHiddenSize( const QSize& size ) { m_hiddenSize = size; emit hiddenSizeChanged(); }
|
||||
|
||||
bool isHidden() const { return m_isHidden; }
|
||||
|
||||
public slots:
|
||||
virtual void onShown( QWidget* );
|
||||
virtual void onHidden( QWidget* );
|
||||
virtual void onShown( QWidget*, bool animated );
|
||||
virtual void onHidden( QWidget*, bool animated );
|
||||
|
||||
signals:
|
||||
void showWidget();
|
||||
void hideWidget();
|
||||
|
||||
void hiddenSizeChanged();
|
||||
|
||||
private slots:
|
||||
void onAnimationStep( int frame );
|
||||
void onAnimationFinished();
|
||||
|
||||
protected:
|
||||
|
||||
AnimatedSplitter* splitter() { return m_parent; }
|
||||
|
||||
|
||||
private:
|
||||
AnimatedSplitter* m_parent;
|
||||
bool m_animateForward;
|
||||
QSize m_hiddenSize;
|
||||
bool m_isHidden;
|
||||
QTimeLine* m_timeLine;
|
||||
};
|
||||
|
||||
#endif //ANIMATEDSPLITTER_H
|
||||
|
@@ -37,23 +37,6 @@ class QNetworkProxy;
|
||||
|
||||
namespace TomahawkUtils
|
||||
{
|
||||
class DLLEXPORT Sleep : public QThread
|
||||
{
|
||||
public:
|
||||
static void sleep( unsigned long secs )
|
||||
{
|
||||
QThread::sleep( secs );
|
||||
}
|
||||
static void msleep( unsigned long msecs )
|
||||
{
|
||||
QThread::msleep( msecs );
|
||||
}
|
||||
static void usleep( unsigned long usecs )
|
||||
{
|
||||
QThread::usleep( usecs );
|
||||
}
|
||||
};
|
||||
|
||||
class DLLEXPORT NetworkProxyFactory : public QNetworkProxyFactory
|
||||
{
|
||||
public:
|
||||
|
@@ -142,6 +142,7 @@ ViewManager::ViewManager( QObject* parent )
|
||||
|
||||
ViewManager::~ViewManager()
|
||||
{
|
||||
saveCurrentPlaylistSettings();
|
||||
delete m_widget;
|
||||
}
|
||||
|
||||
@@ -581,6 +582,9 @@ ViewManager::setPage( ViewPage* page, bool trackHistory )
|
||||
if ( !page )
|
||||
return;
|
||||
|
||||
// save the old playlist shuffle state in config before we change playlists
|
||||
saveCurrentPlaylistSettings();
|
||||
|
||||
unlinkPlaylist();
|
||||
|
||||
if ( !m_pageHistory.contains( page ) )
|
||||
@@ -599,24 +603,12 @@ ViewManager::setPage( ViewPage* page, bool trackHistory )
|
||||
setHistoryPosition( m_pageHistory.count() - 1 );
|
||||
}
|
||||
|
||||
if ( !playlistForInterface( currentPlaylistInterface() ).isNull() )
|
||||
emit playlistActivated( playlistForInterface( currentPlaylistInterface() ) );
|
||||
|
||||
else if ( dynamicPlaylistForInterface( currentPlaylistInterface() ) )
|
||||
emit dynamicPlaylistActivated( dynamicPlaylistForInterface( currentPlaylistInterface() ) );
|
||||
else if ( collectionForInterface( currentPlaylistInterface() ) )
|
||||
emit collectionActivated( collectionForInterface( currentPlaylistInterface() ) );
|
||||
else if ( isSuperCollectionVisible() )
|
||||
emit superCollectionActivated();
|
||||
else if( isNewPlaylistPageVisible() )
|
||||
emit newPlaylistActivated();
|
||||
/* TODO refactor. now we have rows in the sourcetreeview that are connected to pages, e.g. Stations, Recently Updated, etc
|
||||
else if ( !currentPlaylistInterface() )
|
||||
emit tempPageActivated();*/
|
||||
|
||||
qDebug() << "View page shown:" << page->title();
|
||||
emit viewPageActivated( page );
|
||||
|
||||
if( page->isTemporaryPage() )
|
||||
emit tempPageActivated( page );
|
||||
|
||||
if ( !AudioEngine::instance()->playlist() )
|
||||
AudioEngine::instance()->setPlaylist( currentPlaylistInterface() );
|
||||
|
||||
@@ -664,6 +656,24 @@ ViewManager::unlinkPlaylist()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ViewManager::saveCurrentPlaylistSettings()
|
||||
{
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
Tomahawk::playlist_ptr pl = playlistForInterface( currentPlaylistInterface() );
|
||||
|
||||
if ( !pl.isNull() ) {
|
||||
s->setShuffleState( pl->guid(), currentPlaylistInterface()->shuffled() );
|
||||
s->setRepeatMode( pl->guid(), currentPlaylistInterface()->repeatMode() );
|
||||
} else {
|
||||
Tomahawk::dynplaylist_ptr dynPl = dynamicPlaylistForInterface( currentPlaylistInterface() );
|
||||
if ( !dynPl.isNull() ) {
|
||||
s->setShuffleState( dynPl->guid(), currentPlaylistInterface()->shuffled() );
|
||||
s->setRepeatMode( dynPl->guid(), currentPlaylistInterface()->repeatMode() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ViewManager::updateView()
|
||||
@@ -716,8 +726,26 @@ ViewManager::updateView()
|
||||
m_infobar->setCaption( currentPage()->title() );
|
||||
m_infobar->setDescription( currentPage()->description() );
|
||||
m_infobar->setPixmap( currentPage()->pixmap() );
|
||||
|
||||
// turn on shuffle/repeat mode for the new playlist view if specified in config
|
||||
loadCurrentPlaylistSettings();
|
||||
}
|
||||
|
||||
void
|
||||
ViewManager::loadCurrentPlaylistSettings()
|
||||
{
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
Tomahawk::playlist_ptr pl = playlistForInterface( currentPlaylistInterface() );
|
||||
if ( !pl.isNull() ) {
|
||||
currentPlaylistInterface()->setShuffled( s->shuffleState( pl->guid() ));
|
||||
currentPlaylistInterface()->setRepeatMode( s->repeatMode( pl->guid() ));
|
||||
} else {
|
||||
Tomahawk::dynplaylist_ptr dynPl = dynamicPlaylistForInterface( currentPlaylistInterface() );
|
||||
if ( !dynPl.isNull() ) {
|
||||
currentPlaylistInterface()->setShuffled( s->shuffleState( dynPl->guid() ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ViewManager::onWidgetDestroyed( QWidget* widget )
|
||||
|
@@ -111,13 +111,7 @@ signals:
|
||||
void historyBackAvailable( bool avail );
|
||||
void historyForwardAvailable( bool avail );
|
||||
|
||||
void tempPageActivated();
|
||||
void superCollectionActivated();
|
||||
void collectionActivated( const Tomahawk::collection_ptr& collection );
|
||||
void playlistActivated( const Tomahawk::playlist_ptr& playlist );
|
||||
void dynamicPlaylistActivated( const Tomahawk::dynplaylist_ptr& playlist );
|
||||
|
||||
void newPlaylistActivated();
|
||||
void tempPageActivated( Tomahawk::ViewPage* );
|
||||
void viewPageActivated( Tomahawk::ViewPage* );
|
||||
|
||||
public slots:
|
||||
@@ -168,7 +162,9 @@ private:
|
||||
void setPage( Tomahawk::ViewPage* page, bool trackHistory = true );
|
||||
void updateView();
|
||||
void unlinkPlaylist();
|
||||
|
||||
void saveCurrentPlaylistSettings();
|
||||
void loadCurrentPlaylistSettings();
|
||||
|
||||
Tomahawk::playlist_ptr playlistForInterface( PlaylistInterface* interface ) const;
|
||||
Tomahawk::dynplaylist_ptr dynamicPlaylistForInterface( PlaylistInterface* interface ) const;
|
||||
Tomahawk::collection_ptr collectionForInterface( PlaylistInterface* interface ) const;
|
||||
|
@@ -51,7 +51,10 @@ public:
|
||||
|
||||
virtual bool jumpToCurrentTrack() = 0;
|
||||
|
||||
virtual bool isTemporaryPage() const { return false; }
|
||||
|
||||
/** subclasses implementing ViewPage can emit the following signals:
|
||||
* nameChanged( const QString& )
|
||||
* descriptionChanged( const QString& )
|
||||
* destroyed( QWidget* widget );
|
||||
*
|
||||
|
@@ -10,6 +10,9 @@
|
||||
<height>460</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
@@ -31,6 +34,9 @@
|
||||
<height>26</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
88
src/libtomahawk/widgets/playlisttypeselectordlg.cpp
Normal file
88
src/libtomahawk/widgets/playlisttypeselectordlg.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2011, Christopher Reichert <creichert07@gmail.com>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "widgets/newplaylistwidget.h"
|
||||
#include "viewmanager.h"
|
||||
#include "viewpage.h"
|
||||
#include "sourcelist.h"
|
||||
|
||||
#include "playlisttypeselectordlg.h"
|
||||
#include "ui_playlisttypeselectordlg.h"
|
||||
|
||||
|
||||
PlaylistTypeSelectorDlg::PlaylistTypeSelectorDlg( QWidget* parent, Qt::WindowFlags f )
|
||||
: QDialog( parent, f )
|
||||
, ui( new Ui::PlaylistTypeSelectorDlg )
|
||||
{
|
||||
ui->setupUi( this );
|
||||
|
||||
#ifdef Q_WS_MAC
|
||||
// ui->
|
||||
ui->horizontalLayout_2->setContentsMargins( 4, 4, 4, 4 );
|
||||
|
||||
setSizeGripEnabled( false );
|
||||
setMinimumSize( size() );
|
||||
setMaximumSize( size() ); // to remove the resize grip on osx this is the only way
|
||||
#endif
|
||||
|
||||
m_isAutoPlaylist = false;
|
||||
m_playlistName = "";
|
||||
|
||||
connect( ui->manualPlaylistButton, SIGNAL( clicked() ),
|
||||
this, SLOT( createNormalPlaylist() ));
|
||||
connect( ui->autoPlaylistButton, SIGNAL( clicked() ),
|
||||
this, SLOT( createAutomaticPlaylist() ));
|
||||
connect( ui->autoPlaylistNameLine, SIGNAL( textChanged( const QString& )),
|
||||
this, SLOT( enableAutoPlaylistButton( const QString& )));
|
||||
}
|
||||
|
||||
PlaylistTypeSelectorDlg::~PlaylistTypeSelectorDlg()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void
|
||||
PlaylistTypeSelectorDlg::createNormalPlaylist()
|
||||
{
|
||||
m_isAutoPlaylist = false;
|
||||
done( QDialog::Accepted ); // return code is used to vaidate we did not exit out of the Dialog
|
||||
}
|
||||
|
||||
void PlaylistTypeSelectorDlg::createAutomaticPlaylist() { m_isAutoPlaylist = true;
|
||||
m_playlistName = ui->autoPlaylistNameLine->text();
|
||||
done( QDialog::Accepted ); // return code is used to vaidate we did not exit out of the Dialog successfully
|
||||
}
|
||||
|
||||
QString
|
||||
PlaylistTypeSelectorDlg::playlistName() const
|
||||
{
|
||||
return m_playlistName;
|
||||
}
|
||||
|
||||
bool
|
||||
PlaylistTypeSelectorDlg::playlistTypeIsAuto() const
|
||||
{
|
||||
return m_isAutoPlaylist;
|
||||
}
|
||||
|
||||
void
|
||||
PlaylistTypeSelectorDlg::enableAutoPlaylistButton( const QString &text )
|
||||
{
|
||||
ui->autoPlaylistButton->setEnabled( !text.isEmpty() );
|
||||
}
|
||||
|
55
src/libtomahawk/widgets/playlisttypeselectordlg.h
Normal file
55
src/libtomahawk/widgets/playlisttypeselectordlg.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
* Copyright 2011, Christopher Reichert <creichert07@gmail.com>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tomahawk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PLAYLISTTYPESELECTORDLG_H
|
||||
#define PLAYLISTTYPESELECTORDLG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "dllmacro.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PlaylistTypeSelectorDlg;
|
||||
}
|
||||
|
||||
class DLLEXPORT PlaylistTypeSelectorDlg : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlaylistTypeSelectorDlg( QWidget* parent = 0, Qt::WindowFlags = 0 );
|
||||
~PlaylistTypeSelectorDlg();
|
||||
bool playlistTypeIsNormal() const;
|
||||
bool playlistTypeIsAuto() const;
|
||||
QString playlistName() const;
|
||||
|
||||
private slots:
|
||||
void createNormalPlaylist();
|
||||
void createAutomaticPlaylist();
|
||||
void enableAutoPlaylistButton( const QString& );
|
||||
|
||||
private:
|
||||
bool m_isAutoPlaylist; // if not an auto playlist then its a normal playlist
|
||||
|
||||
Ui::PlaylistTypeSelectorDlg *ui;
|
||||
QString m_playlistName;
|
||||
|
||||
};
|
||||
|
||||
#endif // PlaylistTypeSelectorDlg_H
|
184
src/libtomahawk/widgets/playlisttypeselectordlg.ui
Normal file
184
src/libtomahawk/widgets/playlisttypeselectordlg.ui
Normal file
@@ -0,0 +1,184 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PlaylistTypeSelectorDlg</class>
|
||||
<widget class="QDialog" name="PlaylistTypeSelectorDlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>554</width>
|
||||
<height>169</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>482</width>
|
||||
<height>145</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>10000</width>
|
||||
<height>10000</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>New Playlist</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1,0">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>221</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Just a regular old playlist... Give it a name, drag in some tracks, and go!</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>28</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="manualPlaylistButton">
|
||||
<property name="text">
|
||||
<string>Create Manual Playlist</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Don't know exactly what you want? Give Tomahawk a few pointers and let it build a playlist for you!</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Name:</string>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="autoPlaylistNameLine">
|
||||
<property name="placeholderText">
|
||||
<string>New Playlist...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="autoPlaylistButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create Automatic Playlist</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
40
src/mac/macdelegate.h
Normal file
40
src/mac/macdelegate.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef MACDELEGATE_H
|
||||
#define MACDELEGATE_H
|
||||
|
||||
// This file inspired by clementine's macdelegate.h
|
||||
|
||||
#import <AppKit/NSApplication.h>
|
||||
|
||||
#include "SPMediaKeyTap.h"
|
||||
|
||||
namespace Tomahawk {
|
||||
class MacShortcutHandler;
|
||||
class PlatformInterface;
|
||||
}
|
||||
|
||||
#ifdef SNOW_LEOPARD
|
||||
@interface AppDelegate :NSObject <NSApplicationDelegate> {
|
||||
#else
|
||||
@interface AppDelegate :NSObject {
|
||||
#endif
|
||||
Tomahawk::PlatformInterface* application_handler_;
|
||||
NSMenu* dock_menu_;
|
||||
SPMediaKeyTap* key_tap_;
|
||||
Tomahawk::MacShortcutHandler* shortcut_handler_;
|
||||
}
|
||||
|
||||
- (id) initWithHandler: (Tomahawk::PlatformInterface*)handler;
|
||||
// NSApplicationDelegate
|
||||
- (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag;
|
||||
- (NSMenu*) applicationDockMenu: (NSApplication*)sender;
|
||||
- (void) setDockMenu: (NSMenu*)menu;
|
||||
- (Tomahawk::MacShortcutHandler*) shortcutHandler;
|
||||
- (void) setShortcutHandler: (Tomahawk::MacShortcutHandler*)backend;
|
||||
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification;
|
||||
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender;
|
||||
- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#endif // MACDELEGATE_H
|
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "tomahawkapp_mac.h"
|
||||
#include "tomahawkapp_macdelegate.h"
|
||||
#include "macdelegate.h"
|
||||
#include "macshortcuthandler.h"
|
||||
#include <QDebug>
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
// See: http://www.rogueamoeba.com/utm/2007/09/29/apple-keyboard-media-key-event-handling/
|
||||
|
||||
@interface MacApplication :NSApplication {
|
||||
AppDelegate* delegate_;
|
||||
Tomahawk::MacShortcutHandler* shortcut_handler_;
|
||||
Tomahawk::PlatformInterface* application_handler_;
|
||||
}
|
||||
@@ -51,7 +52,6 @@
|
||||
|
||||
- (Tomahawk::PlatformInterface*) application_handler;
|
||||
- (void) setApplicationHandler: (Tomahawk::PlatformInterface*)handler;
|
||||
- (void) mediaKeyEvent: (int)key state: (BOOL)state repeat: (BOOL)repeat;
|
||||
@end
|
||||
|
||||
|
||||
@@ -59,14 +59,22 @@
|
||||
|
||||
- (id) init {
|
||||
if ((self = [super init])) {
|
||||
application_handler_ = nil;
|
||||
// dock_menu_ = nil;
|
||||
application_handler_ = nil;
|
||||
shortcut_handler_ = nil;
|
||||
//dock_menu_ = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) initWithHandler: (Tomahawk::PlatformInterface*)handler {
|
||||
application_handler_ = handler;
|
||||
|
||||
// Register defaults for the whitelist of apps that want to use media keys
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], @"SPApplicationsNeedingMediaKeys",
|
||||
nil]];
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -76,7 +84,7 @@
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
/*
|
||||
|
||||
- (void) setDockMenu: (NSMenu*)menu {
|
||||
dock_menu_ = menu;
|
||||
}
|
||||
@@ -84,7 +92,45 @@
|
||||
- (NSMenu*) applicationDockMenu: (NSApplication*)sender {
|
||||
return dock_menu_;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
- (Tomahawk::MacShortcutHandler*) shortcutHandler {
|
||||
return shortcut_handler_;
|
||||
}
|
||||
|
||||
- (void) setShortcutHandler: (Tomahawk::MacShortcutHandler*)handler {
|
||||
qDebug() << "Setting shortcut handler of MacApp";
|
||||
// should be the same as MacApplication's
|
||||
shortcut_handler_ = handler;
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
key_tap_ = [[SPMediaKeyTap alloc] initWithDelegate:self];
|
||||
if([SPMediaKeyTap usesGlobalMediaKeyTap])
|
||||
[key_tap_ startWatchingMediaKeys];
|
||||
else
|
||||
qWarning()<<"Media key monitoring disabled";
|
||||
|
||||
}
|
||||
|
||||
- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event {
|
||||
NSAssert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys, @"Unexpected NSEvent in mediaKeyTap:receivedMediaKeyEvent:");
|
||||
|
||||
int key_code = (([event data1] & 0xFFFF0000) >> 16);
|
||||
int key_flags = ([event data1] & 0x0000FFFF);
|
||||
BOOL key_is_pressed = (((key_flags & 0xFF00) >> 8)) == 0xA;
|
||||
// not used. keep just in case
|
||||
// int key_repeat = (key_flags & 0x1);
|
||||
|
||||
if (!shortcut_handler_) {
|
||||
qWarning() << "No shortcut handler when we get a media key event...";
|
||||
return;
|
||||
}
|
||||
if (key_is_pressed) {
|
||||
shortcut_handler_->macMediaKeyPressed(key_code);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) application: (NSApplication*)app openFile:(NSString*)filename {
|
||||
qDebug() << "Wants to open:" << [filename UTF8String];
|
||||
|
||||
@@ -94,6 +140,11 @@
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*) sender {
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MacApplication
|
||||
@@ -127,7 +178,7 @@
|
||||
}
|
||||
|
||||
- (void) setShortcutHandler: (Tomahawk::MacShortcutHandler*)handler {
|
||||
qDebug() << "Setting shortcut handler of MacAPp";
|
||||
// should be the same as AppDelegate's
|
||||
shortcut_handler_ = handler;
|
||||
}
|
||||
|
||||
@@ -136,30 +187,22 @@
|
||||
}
|
||||
|
||||
- (void) setApplicationHandler: (Tomahawk::PlatformInterface*)handler {
|
||||
AppDelegate* delegate = [[AppDelegate alloc] initWithHandler:handler];
|
||||
[self setDelegate:delegate];
|
||||
delegate_ = [[AppDelegate alloc] initWithHandler:handler];
|
||||
// App-shortcut-handler set before delegate is set.
|
||||
// this makes sure the delegate's shortcut_handler is set
|
||||
[delegate_ setShortcutHandler:shortcut_handler_];
|
||||
[self setDelegate:delegate_];
|
||||
}
|
||||
|
||||
-(void) sendEvent: (NSEvent*)event {
|
||||
if ([event type] == NSSystemDefined && [event subtype] == 8) {
|
||||
int keycode = (([event data1] & 0xFFFF0000) >> 16);
|
||||
int keyflags = ([event data1] & 0x0000FFFF);
|
||||
int keystate = (((keyflags & 0xFF00) >> 8)) == 0xA;
|
||||
int keyrepeat = (keyflags & 0x1);
|
||||
// If event tap is not installed, handle events that reach the app instead
|
||||
BOOL shouldHandleMediaKeyEventLocally = ![SPMediaKeyTap usesGlobalMediaKeyTap];
|
||||
|
||||
[self mediaKeyEvent: keycode state: keystate repeat: keyrepeat];
|
||||
}
|
||||
if(shouldHandleMediaKeyEventLocally && [event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys) {
|
||||
[(id)[self delegate] mediaKeyTap: nil receivedMediaKeyEvent: event];
|
||||
}
|
||||
|
||||
[super sendEvent: event];
|
||||
}
|
||||
|
||||
-(void) mediaKeyEvent: (int)key state: (BOOL)state repeat: (BOOL)repeat {
|
||||
if (!shortcut_handler_) {
|
||||
return;
|
||||
}
|
||||
if (state == 0) {
|
||||
shortcut_handler_->macMediaKeyPressed(key);
|
||||
}
|
||||
[super sendEvent: event];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@@ -136,29 +136,12 @@ MusicScanner::~MusicScanner()
|
||||
|
||||
if ( !m_dirLister.isNull() )
|
||||
{
|
||||
QMetaObject::invokeMethod( m_dirLister.data(), "deleteLater", Qt::QueuedConnection );
|
||||
while( !m_dirLister.isNull() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " scanner not deleted, processing events";
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 200 );
|
||||
TomahawkUtils::Sleep::msleep( 100 );
|
||||
}
|
||||
m_dirListerThreadController->quit();;
|
||||
m_dirListerThreadController->wait( 60000 );
|
||||
|
||||
if ( m_dirListerThreadController )
|
||||
m_dirListerThreadController->quit();
|
||||
|
||||
if( m_dirListerThreadController )
|
||||
{
|
||||
while( !m_dirListerThreadController->isFinished() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " scanner thread controller not finished, processing events";
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 200 );
|
||||
TomahawkUtils::Sleep::msleep( 100 );
|
||||
}
|
||||
|
||||
delete m_dirListerThreadController;
|
||||
m_dirListerThreadController = 0;
|
||||
}
|
||||
delete m_dirLister.data();
|
||||
delete m_dirListerThreadController;
|
||||
m_dirListerThreadController = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,29 +240,12 @@ MusicScanner::deleteLister()
|
||||
{
|
||||
if ( !m_dirLister.isNull() )
|
||||
{
|
||||
QMetaObject::invokeMethod( m_dirLister.data(), "deleteLater", Qt::QueuedConnection );
|
||||
while( !m_dirLister.isNull() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " scanner not deleted, processing events";
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 200 );
|
||||
TomahawkUtils::Sleep::msleep( 100 );
|
||||
}
|
||||
m_dirListerThreadController->quit();;
|
||||
m_dirListerThreadController->wait( 60000 );
|
||||
|
||||
if ( m_dirListerThreadController )
|
||||
m_dirListerThreadController->quit();
|
||||
|
||||
if( m_dirListerThreadController )
|
||||
{
|
||||
while( !m_dirListerThreadController->isFinished() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " scanner thread controller not finished, processing events";
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 200 );
|
||||
TomahawkUtils::Sleep::msleep( 100 );
|
||||
}
|
||||
|
||||
delete m_dirListerThreadController;
|
||||
m_dirListerThreadController = 0;
|
||||
}
|
||||
delete m_dirLister.data();
|
||||
delete m_dirListerThreadController;
|
||||
m_dirListerThreadController = 0;
|
||||
}
|
||||
emit finished();
|
||||
}
|
||||
|
@@ -85,29 +85,12 @@ ScanManager::~ScanManager()
|
||||
|
||||
if ( !m_scanner.isNull() )
|
||||
{
|
||||
QMetaObject::invokeMethod( m_scanner.data(), "deleteLater", Qt::QueuedConnection );
|
||||
while( !m_scanner.isNull() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " scanner not deleted, processing events";
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 200 );
|
||||
TomahawkUtils::Sleep::msleep( 100 );
|
||||
}
|
||||
m_musicScannerThreadController->quit();
|
||||
m_musicScannerThreadController->wait( 60000 );
|
||||
|
||||
if ( m_musicScannerThreadController )
|
||||
m_musicScannerThreadController->quit();
|
||||
|
||||
if( m_musicScannerThreadController )
|
||||
{
|
||||
while( !m_musicScannerThreadController->isFinished() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " scanner thread controller not finished, processing events";
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 200 );
|
||||
TomahawkUtils::Sleep::msleep( 100 );
|
||||
}
|
||||
|
||||
delete m_musicScannerThreadController;
|
||||
m_musicScannerThreadController = 0;
|
||||
}
|
||||
delete m_scanner.data();
|
||||
delete m_musicScannerThreadController;
|
||||
m_musicScannerThreadController = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,29 +233,12 @@ ScanManager::scannerFinished()
|
||||
{
|
||||
if ( !m_scanner.isNull() )
|
||||
{
|
||||
QMetaObject::invokeMethod( m_scanner.data(), "deleteLater", Qt::QueuedConnection );
|
||||
while( !m_scanner.isNull() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " scanner not deleted, processing events";
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 200 );
|
||||
TomahawkUtils::Sleep::msleep( 100 );
|
||||
}
|
||||
m_musicScannerThreadController->quit();
|
||||
m_musicScannerThreadController->wait( 60000 );
|
||||
|
||||
if ( m_musicScannerThreadController )
|
||||
m_musicScannerThreadController->quit();
|
||||
|
||||
if( m_musicScannerThreadController )
|
||||
{
|
||||
while( !m_musicScannerThreadController->isFinished() )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << " scanner thread controller not finished, processing events";
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 200 );
|
||||
TomahawkUtils::Sleep::msleep( 100 );
|
||||
}
|
||||
|
||||
delete m_musicScannerThreadController;
|
||||
m_musicScannerThreadController = 0;
|
||||
}
|
||||
delete m_scanner.data();
|
||||
delete m_musicScannerThreadController;
|
||||
m_musicScannerThreadController = 0;
|
||||
}
|
||||
emit finished();
|
||||
}
|
||||
|
@@ -391,7 +391,7 @@ SettingsDialog::onLastFmFinished()
|
||||
ui->pushButtonTestLastfmLogin->setText( tr( "Failed" ) );
|
||||
ui->pushButtonTestLastfmLogin->setEnabled( true );
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
qDebug() << "Couldn't get last.fm auth result";
|
||||
ui->pushButtonTestLastfmLogin->setText( tr( "Could not contact server" ) );
|
||||
@@ -537,6 +537,15 @@ SettingsDialog::sipFactoryClicked( SipPluginFactory* factory )
|
||||
SipPlugin* p = factory->createPlugin();
|
||||
bool added = false;
|
||||
if( p->configWidget() ) {
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// on osx a sheet needs to be non-modal
|
||||
DelegateConfigWrapper* dialog = new DelegateConfigWrapper( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this, Qt::Sheet );
|
||||
dialog->setProperty( "sipplugin", QVariant::fromValue< QObject* >( p ) );
|
||||
connect( dialog, SIGNAL( finished( int ) ), this, SLOT( sipCreateConfigClosed( int ) ) );
|
||||
|
||||
dialog->show();
|
||||
#else
|
||||
DelegateConfigWrapper dialog( p->configWidget(), QString("%1 Config" ).arg( p->friendlyName() ), this );
|
||||
QWeakPointer< DelegateConfigWrapper > watcher( &dialog );
|
||||
int ret = dialog.exec();
|
||||
@@ -553,13 +562,44 @@ SettingsDialog::sipFactoryClicked( SipPluginFactory* factory )
|
||||
// canceled, delete it
|
||||
added = false;
|
||||
}
|
||||
|
||||
handleSipPluginAdded( p, added );
|
||||
#endif
|
||||
} else {
|
||||
// no config, so just add it
|
||||
added = true;
|
||||
TomahawkSettings::instance()->addSipPlugin( p->pluginId() );
|
||||
SipHandler::instance()->addSipPlugin( p );
|
||||
|
||||
handleSipPluginAdded( p, added );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
SettingsDialog::sipCreateConfigClosed( int finished )
|
||||
{
|
||||
DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() );
|
||||
SipPlugin* p = qobject_cast< SipPlugin* >( dialog->property( "sipplugin" ).value< QObject* >() );
|
||||
Q_ASSERT( p );
|
||||
|
||||
bool added = false;
|
||||
if( finished == QDialog::Accepted ) {
|
||||
|
||||
p->saveConfig();
|
||||
TomahawkSettings::instance()->addSipPlugin( p->pluginId() );
|
||||
SipHandler::instance()->addSipPlugin( p );
|
||||
|
||||
added = true;
|
||||
}
|
||||
|
||||
handleSipPluginAdded( p, added );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SettingsDialog::handleSipPluginAdded( SipPlugin* p, bool added )
|
||||
{
|
||||
SipPluginFactory* f = SipHandler::instance()->factoryFromPlugin( p );
|
||||
if( added && f && f->isUnique() ) {
|
||||
// remove from actions list
|
||||
@@ -579,6 +619,7 @@ SettingsDialog::sipFactoryClicked( SipPluginFactory* factory )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SettingsDialog::sipContextMenuRequest( const QPoint& p )
|
||||
{
|
||||
@@ -634,9 +675,9 @@ ProxyDialog::ProxyDialog( QWidget *parent )
|
||||
, ui( new Ui::ProxyDialog )
|
||||
{
|
||||
ui->setupUi( this );
|
||||
|
||||
|
||||
// ugly, I know, but...
|
||||
|
||||
|
||||
int i = 0;
|
||||
ui->typeBox->insertItem( i, "No Proxy", QNetworkProxy::NoProxy );
|
||||
m_forwardMap[ QNetworkProxy::NoProxy ] = i;
|
||||
@@ -646,9 +687,9 @@ ProxyDialog::ProxyDialog( QWidget *parent )
|
||||
m_forwardMap[ QNetworkProxy::Socks5Proxy ] = i;
|
||||
m_backwardMap[ i ] = QNetworkProxy::Socks5Proxy;
|
||||
i++;
|
||||
|
||||
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
|
||||
|
||||
ui->typeBox->setCurrentIndex( m_forwardMap[s->proxyType()] );
|
||||
ui->hostLineEdit->setText( s->proxyHost() );
|
||||
ui->portSpinBox->setValue( s->proxyPort() );
|
||||
@@ -690,18 +731,18 @@ ProxyDialog::proxyTypeChangedSlot( int index )
|
||||
ui->passwordLineEdit->setEnabled( true );
|
||||
ui->checkBoxUseProxyForDns->setEnabled( true );
|
||||
ui->noHostLineEdit->setEnabled( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ProxyDialog::saveSettings()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
|
||||
//First set settings
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
s->setProxyHost( ui->hostLineEdit->text() );
|
||||
|
||||
|
||||
int port = ui->portSpinBox->value();
|
||||
s->setProxyPort( port );
|
||||
s->setProxyNoProxyHosts( ui->noHostLineEdit->text() );
|
||||
@@ -709,10 +750,10 @@ ProxyDialog::saveSettings()
|
||||
s->setProxyPassword( ui->passwordLineEdit->text() );
|
||||
s->setProxyType( ui->typeBox->itemData( ui->typeBox->currentIndex() ).toInt() );
|
||||
s->setProxyDns( ui->checkBoxUseProxyForDns->checkState() == Qt::Checked );
|
||||
|
||||
|
||||
if( s->proxyHost().isEmpty() )
|
||||
return;
|
||||
|
||||
|
||||
TomahawkUtils::NetworkProxyFactory* proxyFactory = new TomahawkUtils::NetworkProxyFactory();
|
||||
QNetworkProxy proxy( static_cast<QNetworkProxy::ProxyType>(s->proxyType()), s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() );
|
||||
proxyFactory->setProxy( proxy );
|
||||
|
@@ -48,7 +48,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void proxyTypeChangedSlot( int index );
|
||||
|
||||
|
||||
private:
|
||||
Ui::ProxyDialog* ui;
|
||||
QHash<int,int> m_forwardMap;
|
||||
@@ -95,12 +95,14 @@ private slots:
|
||||
// dialog slots
|
||||
void resolverConfigClosed( int value );
|
||||
void sipConfigClosed( int value );
|
||||
void sipCreateConfigClosed( int value );
|
||||
|
||||
void changePage( QListWidgetItem*, QListWidgetItem* );
|
||||
|
||||
private:
|
||||
void createIcons();
|
||||
void setupSipButtons();
|
||||
void handleSipPluginAdded( SipPlugin* p, bool added );
|
||||
|
||||
Ui_StackedSettingsDialog* ui;
|
||||
|
||||
|
@@ -782,20 +782,29 @@ TwitterPlugin::checkSettings()
|
||||
QString
|
||||
TwitterPlugin::twitterScreenName() const
|
||||
{
|
||||
if ( TomahawkSettings::instance()->contains( pluginId() + "/ScreenName" ) )
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
s->beginGroup( pluginId() );
|
||||
QStringList keys = s->childKeys();
|
||||
if ( keys.contains( "ScreenName", Qt::CaseSensitive ) )
|
||||
{
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/screenname_tmp",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/ScreenName" ).toString() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/ScreenName" );
|
||||
s->setValue( "screenname_tmp",
|
||||
s->value( "ScreenName" ).toString() );
|
||||
s->remove( "ScreenName" );
|
||||
|
||||
TomahawkSettings::instance()->sync();
|
||||
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/screenname",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/screenname_tmp" ).toString() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/screenname_tmp" );
|
||||
s->sync();
|
||||
}
|
||||
keys = s->childKeys();
|
||||
if ( keys.contains( "screenname_tmp", Qt::CaseSensitive ) )
|
||||
{
|
||||
s->setValue( "screenname",
|
||||
s->value( "screenname_tmp" ).toString() );
|
||||
s->remove( "screenname_tmp" );
|
||||
|
||||
return TomahawkSettings::instance()->value( pluginId() + "/screenname" ).toString();
|
||||
s->sync();
|
||||
}
|
||||
s->endGroup();
|
||||
|
||||
return s->value( pluginId() + "/screenname" ).toString();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -807,20 +816,30 @@ TwitterPlugin::setTwitterScreenName( const QString& screenName )
|
||||
QString
|
||||
TwitterPlugin::twitterOAuthToken() const
|
||||
{
|
||||
if ( TomahawkSettings::instance()->contains( pluginId() + "/OAuthToken" ) )
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
s->beginGroup( pluginId() );
|
||||
QStringList keys = s->childKeys();
|
||||
if ( keys.contains( "OAuthToken", Qt::CaseSensitive ) )
|
||||
{
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/oauthtoken_tmp",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/OAuthToken" ).toString() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/OAuthToken" );
|
||||
s->setValue( "oauthtoken_tmp",
|
||||
s->value( "OAuthToken" ).toString() );
|
||||
s->remove( "OAuthToken" );
|
||||
|
||||
TomahawkSettings::instance()->sync();
|
||||
s->sync();
|
||||
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/oauthtoken",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/oauthtoken_tmp" ).toString() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/oauthtoken_tmp" );
|
||||
}
|
||||
keys = s->childKeys();
|
||||
if ( keys.contains( "oauthtoken_tmp", Qt::CaseSensitive ) )
|
||||
{
|
||||
s->setValue( "oauthtoken",
|
||||
s->value( "oauthtoken_tmp" ).toString() );
|
||||
s->remove( "oauthtoken_tmp" );
|
||||
|
||||
return TomahawkSettings::instance()->value( pluginId() + "/oauthtoken" ).toString();
|
||||
s->sync();
|
||||
}
|
||||
s->endGroup();
|
||||
|
||||
return s->value( pluginId() + "/oauthtoken" ).toString();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -832,20 +851,29 @@ TwitterPlugin::setTwitterOAuthToken( const QString& oauthtoken )
|
||||
QString
|
||||
TwitterPlugin::twitterOAuthTokenSecret() const
|
||||
{
|
||||
if ( TomahawkSettings::instance()->contains( pluginId() + "/OAuthTokenSecret" ) )
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
s->beginGroup( pluginId() );
|
||||
QStringList keys = s->childKeys();
|
||||
if ( keys.contains( "OAuthTokenSecret", Qt::CaseSensitive ) )
|
||||
{
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/oauthtokensecret_tmp",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/OAuthTokenSecret" ).toString() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/OAuthTokenSecret" );
|
||||
s->setValue( "oauthtokensecret_tmp",
|
||||
s->value( "OAuthTokenSecret" ).toString() );
|
||||
s->remove( "OAuthTokenSecret" );
|
||||
|
||||
TomahawkSettings::instance()->sync();
|
||||
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/oauthtokensecret",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/oauthtokensecret_tmp" ).toString() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/oauthtokensecret_tmp" );
|
||||
s->sync();
|
||||
}
|
||||
keys = s->childKeys();
|
||||
if ( keys.contains( "oauthtokensecret_tmp", Qt::CaseSensitive ) )
|
||||
{
|
||||
s->setValue( "oauthtokensecret",
|
||||
s->value( "oauthtokensecret_tmp" ).toString() );
|
||||
s->remove( "oauthtokensecret_tmp" );
|
||||
|
||||
return TomahawkSettings::instance()->value( pluginId() + "/oauthtokensecret" ).toString();
|
||||
s->sync();
|
||||
}
|
||||
s->endGroup();
|
||||
|
||||
return s->value( pluginId() + "/oauthtokensecret" ).toString();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -857,20 +885,29 @@ TwitterPlugin::setTwitterOAuthTokenSecret( const QString& oauthtokensecret )
|
||||
qint64
|
||||
TwitterPlugin::twitterCachedFriendsSinceId() const
|
||||
{
|
||||
if ( TomahawkSettings::instance()->contains( pluginId() + "/CachedFriendsSinceID" ) )
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
s->beginGroup( pluginId() );
|
||||
QStringList keys = s->childKeys();
|
||||
if ( keys.contains( "CachedFriendsSinceID", Qt::CaseSensitive ) )
|
||||
{
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/cachedfriendssinceid_tmp",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/CachedFriendsSinceID" ).toLongLong() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/CachedFriendsSinceID" );
|
||||
s->setValue( "cachedfriendssinceid_tmp",
|
||||
s->value( "CachedFriendsSinceID" ).toLongLong() );
|
||||
s->remove( "CachedFriendsSinceID" );
|
||||
|
||||
TomahawkSettings::instance()->sync();
|
||||
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/cachedfriendssinceid",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/cachedfriendssinceid_tmp" ).toLongLong() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/cachedfriendssinceid_tmp" );
|
||||
s->sync();
|
||||
}
|
||||
keys = s->childKeys();
|
||||
if ( keys.contains( "cachedfriendssinceid_tmp", Qt::CaseSensitive ) )
|
||||
{
|
||||
s->setValue( "cachedfriendssinceid",
|
||||
s->value( "cachedfriendssinceid_tmp" ).toLongLong() );
|
||||
s->remove( "cachedfriendssinceid_tmp" );
|
||||
|
||||
return TomahawkSettings::instance()->value( pluginId() + "/cachedfriendssinceid", 0 ).toLongLong();
|
||||
s->sync();
|
||||
}
|
||||
s->endGroup();
|
||||
|
||||
return s->value( pluginId() + "/cachedfriendssinceid", 0 ).toLongLong();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -882,20 +919,29 @@ TwitterPlugin::setTwitterCachedFriendsSinceId( qint64 cachedId )
|
||||
qint64
|
||||
TwitterPlugin::twitterCachedMentionsSinceId() const
|
||||
{
|
||||
if ( TomahawkSettings::instance()->contains( pluginId() + "/CachedMentionsSinceID" ) )
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
s->beginGroup( pluginId() );
|
||||
QStringList keys = s->childKeys();
|
||||
if ( keys.contains( "CachedMentionsSinceID", Qt::CaseSensitive ) )
|
||||
{
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/cachedmentionssinceid_tmp",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/CachedMentionsSinceID" ).toLongLong() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/CachedMentionsSinceID" );
|
||||
s->setValue( "cachedmentionssinceid_tmp",
|
||||
s->value( "CachedMentionsSinceID" ).toLongLong() );
|
||||
s->remove( "CachedMentionsSinceID" );
|
||||
|
||||
TomahawkSettings::instance()->sync();
|
||||
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/cachedmentionssinceid",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/cachedmentionssinceid_tmp" ).toLongLong() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/cachedmentionssinceid_tmp" );
|
||||
s->sync();
|
||||
}
|
||||
keys = s->childKeys();
|
||||
if ( keys.contains( "cachedmentionssinceid_tmp", Qt::CaseSensitive ) )
|
||||
{
|
||||
s->setValue( "cachedmentionssinceid",
|
||||
s->value( "cachedmentionssinceid_tmp" ).toLongLong() );
|
||||
s->remove( "cachedmentionssinceid_tmp" );
|
||||
|
||||
return TomahawkSettings::instance()->value( pluginId() + "/cachedmentionssinceid", 0 ).toLongLong();
|
||||
s->sync();
|
||||
}
|
||||
s->endGroup();
|
||||
|
||||
return s->value( pluginId() + "/cachedmentionssinceid", 0 ).toLongLong();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -907,20 +953,29 @@ TwitterPlugin::setTwitterCachedMentionsSinceId( qint64 cachedId )
|
||||
qint64
|
||||
TwitterPlugin::twitterCachedDirectMessagesSinceId() const
|
||||
{
|
||||
if ( TomahawkSettings::instance()->contains( pluginId() + "/CachedDirectMessagesSinceID" ) )
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
s->beginGroup( pluginId() );
|
||||
QStringList keys = s->childKeys();
|
||||
if ( keys.contains( "CachedDirectMessagesSinceID", Qt::CaseSensitive ) )
|
||||
{
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/cacheddirectmessagessinceid_tmp",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/CachedDirectMessagesSinceID" ).toLongLong() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/CachedDirectMessagesSinceID" );
|
||||
s->setValue( "cacheddirectmessagessinceid_tmp",
|
||||
s->value( "CachedDirectMessagesSinceID" ).toLongLong() );
|
||||
s->remove( "CachedDirectMessagesSinceID" );
|
||||
|
||||
TomahawkSettings::instance()->sync();
|
||||
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/cacheddirectmessagessinceid",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/cacheddirectmessagessinceid_tmp" ).toLongLong() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/cacheddirectmessagessinceid_tmp" );
|
||||
s->sync();
|
||||
}
|
||||
keys = s->childKeys();
|
||||
if ( keys.contains( "cacheddirectmessagessinceid_tmp", Qt::CaseSensitive ) )
|
||||
{
|
||||
s->setValue( "cacheddirectmessagessinceid",
|
||||
s->value( "cacheddirectmessagessinceid_tmp" ).toLongLong() );
|
||||
s->remove( "cacheddirectmessagessinceid_tmp" );
|
||||
|
||||
return TomahawkSettings::instance()->value( pluginId() + "/cacheddirectmessagessinceid", 0 ).toLongLong();
|
||||
s->sync();
|
||||
}
|
||||
s->endGroup();
|
||||
|
||||
return s->value( pluginId() + "/cacheddirectmessagessinceid", 0 ).toLongLong();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -932,20 +987,29 @@ TwitterPlugin::setTwitterCachedDirectMessagesSinceId( qint64 cachedId )
|
||||
QHash<QString, QVariant>
|
||||
TwitterPlugin::twitterCachedPeers() const
|
||||
{
|
||||
if ( TomahawkSettings::instance()->contains( pluginId() + "/CachedPeers" ) )
|
||||
TomahawkSettings* s = TomahawkSettings::instance();
|
||||
s->beginGroup( pluginId() );
|
||||
QStringList keys = s->childKeys();
|
||||
if ( keys.contains( "CachedPeers", Qt::CaseSensitive ) )
|
||||
{
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/cachedpeers_tmp",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/CachedPeers" ).toHash() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/CachedPeers" );
|
||||
s->setValue( "cachedpeers_tmp",
|
||||
s->value( "CachedPeers" ).toHash() );
|
||||
s->remove( "CachedPeers" );
|
||||
|
||||
TomahawkSettings::instance()->sync();
|
||||
|
||||
TomahawkSettings::instance()->setValue( pluginId() + "/cachedpeers",
|
||||
TomahawkSettings::instance()->value( pluginId() + "/cachedpeers_tmp" ).toHash() );
|
||||
TomahawkSettings::instance()->remove( pluginId() + "/cachedpeers_tmp" );
|
||||
s->sync();
|
||||
}
|
||||
keys = s->childKeys();
|
||||
if ( keys.contains( "cachedpeers_tmp", Qt::CaseSensitive ) )
|
||||
{
|
||||
s->setValue( "cachedpeers",
|
||||
s->value( "cachedpeers_tmp" ).toHash() );
|
||||
s->remove( "cachedpeers_tmp" );
|
||||
|
||||
s->sync();
|
||||
}
|
||||
s->endGroup();
|
||||
|
||||
return TomahawkSettings::instance()->value( pluginId() + "/cachedpeers", QHash<QString, QVariant>() ).toHash();
|
||||
return s->value( pluginId() + "/cachedpeers", QHash<QString, QVariant>() ).toHash();
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -132,6 +132,7 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user )
|
||||
m_plugin->connectPlugin( false );
|
||||
|
||||
emit twitterAuthed( true );
|
||||
emit sizeHintChanged();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -161,6 +162,7 @@ TwitterConfigWidget::deauthenticateTwitter()
|
||||
ui->twitterTweetComboBox->setVisible( false );
|
||||
|
||||
emit twitterAuthed( false );
|
||||
emit sizeHintChanged();
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -45,6 +45,7 @@ public:
|
||||
signals:
|
||||
void twitterAuthed( bool authed );
|
||||
|
||||
void sizeHintChanged();
|
||||
private slots:
|
||||
void authDeauthTwitter();
|
||||
void startPostGotTomahawkStatus();
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include "tomahawkapp.h"
|
||||
#include "utils/tomahawkutils.h"
|
||||
#include "widgets/newplaylistwidget.h"
|
||||
#include "widgets/playlisttypeselectordlg.h"
|
||||
#include "viewmanager.h"
|
||||
#include "viewpage.h"
|
||||
#include "sourcelist.h"
|
||||
@@ -59,13 +60,30 @@ CategoryAddItem::activate()
|
||||
{
|
||||
switch( m_categoryType )
|
||||
{
|
||||
case SourcesModel::PlaylistsCategory:
|
||||
// only show if none is shown yet
|
||||
if( !ViewManager::instance()->isNewPlaylistPageVisible() ) {
|
||||
ViewPage* p = ViewManager::instance()->show( new NewPlaylistWidget() );
|
||||
model()->linkSourceItemToPage( this, p );
|
||||
case SourcesModel::PlaylistsCategory: {
|
||||
|
||||
PlaylistTypeSelectorDlg playlistSelectorDlg( TomahawkApp::instance()->mainWindow() );
|
||||
int successfulReturn = playlistSelectorDlg.exec();
|
||||
|
||||
if ( !playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) {
|
||||
|
||||
// only show if none is shown yet
|
||||
if( !ViewManager::instance()->isNewPlaylistPageVisible() ) {
|
||||
//fix this namespace resolution problem, was not there before
|
||||
Tomahawk::ViewPage* p = ViewManager::instance()->show( new NewPlaylistWidget() );
|
||||
model()->linkSourceItemToPage( this, p );
|
||||
}
|
||||
|
||||
} else if ( playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) {
|
||||
// create Auto Playlist
|
||||
QString playlistName = playlistSelectorDlg.playlistName();
|
||||
APP->mainWindow()->createAutomaticPlaylist( playlistName );
|
||||
} else if ( !successfulReturn ) {
|
||||
model()->viewPageActivated( ViewManager::instance()->currentPage() );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SourcesModel::StationsCategory:
|
||||
APP->mainWindow()->createStation();
|
||||
break;
|
||||
@@ -218,4 +236,5 @@ CategoryItem::activate()
|
||||
if( m_category == SourcesModel::StationsCategory ) {
|
||||
// TODO activate stations page
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include "playlistitems.h"
|
||||
#include "viewmanager.h"
|
||||
#include "playlist.h"
|
||||
#include "genericpageitems.h"
|
||||
|
||||
/// CollectionItem
|
||||
|
||||
@@ -31,8 +32,12 @@ CollectionItem::CollectionItem( SourcesModel* mdl, SourceTreeItem* parent, cons
|
||||
, m_source( source )
|
||||
, m_playlists( 0 )
|
||||
, m_stations( 0 )
|
||||
, m_tempItem( 0 )
|
||||
, m_curTempPage( 0 )
|
||||
{
|
||||
if( m_source.isNull() ) { // super collection
|
||||
connect( ViewManager::instance(), SIGNAL( tempPageActivated( Tomahawk::ViewPage*) ), this, SLOT( tempPageActivated( Tomahawk::ViewPage* ) ) );
|
||||
|
||||
return;
|
||||
}
|
||||
// create category items if there are playlists to show, or stations to show
|
||||
@@ -264,3 +269,40 @@ CollectionItem::onStationsDeleted( const QList< dynplaylist_ptr >& stations )
|
||||
{
|
||||
playlistsDeletedInternal( m_stations, stations );
|
||||
}
|
||||
|
||||
void
|
||||
CollectionItem::tempPageActivated( Tomahawk::ViewPage* v )
|
||||
{
|
||||
QString name = v->title();
|
||||
m_curTempPage = v;
|
||||
if( !m_tempItem ) {
|
||||
emit beginRowsAdded( children().count(), children().count() );
|
||||
m_tempItem = new GenericPageItem( model(), this, name, QIcon( RESPATH "images/playlist-icon.png" ),
|
||||
boost::bind( &CollectionItem::tempItemClicked, this ),
|
||||
boost::bind( &CollectionItem::getTempPage, this )
|
||||
);
|
||||
emit endRowsAdded();
|
||||
} else {
|
||||
m_tempItem->setText( name );
|
||||
}
|
||||
|
||||
model()->linkSourceItemToPage( m_tempItem, v );
|
||||
emit selectRequest( m_tempItem );
|
||||
}
|
||||
|
||||
ViewPage*
|
||||
CollectionItem::tempItemClicked()
|
||||
{
|
||||
if( m_curTempPage ) {
|
||||
// show the last temporary page the user displayed
|
||||
return ViewManager::instance()->show( m_curTempPage );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ViewPage*
|
||||
CollectionItem::getTempPage() const
|
||||
{
|
||||
return m_curTempPage;
|
||||
}
|
||||
|
@@ -19,7 +19,11 @@
|
||||
|
||||
#include "sourcetreeitem.h"
|
||||
|
||||
class GenericPageItem;
|
||||
class CategoryItem;
|
||||
namespace Tomahawk {
|
||||
class ViewPage;
|
||||
}
|
||||
|
||||
class CollectionItem : public SourceTreeItem
|
||||
{
|
||||
@@ -38,6 +42,7 @@ public:
|
||||
CategoryItem* playlistsCategory() const { return m_playlists; }
|
||||
void setStationsCategory( CategoryItem* item ) { m_stations = item; }
|
||||
void setPlaylistsCategory( CategoryItem* item ) { m_playlists = item; }
|
||||
|
||||
private slots:
|
||||
void onPlaylistsAdded( const QList<Tomahawk::playlist_ptr>& playlists );
|
||||
void onPlaylistsDeleted( const QList<Tomahawk::playlist_ptr>& playlists );
|
||||
@@ -46,6 +51,10 @@ private slots:
|
||||
void onStationsAdded( const QList<Tomahawk::dynplaylist_ptr>& stations );
|
||||
void onStationsDeleted( const QList<Tomahawk::dynplaylist_ptr>& stations );
|
||||
|
||||
void tempPageActivated( Tomahawk::ViewPage* );
|
||||
Tomahawk::ViewPage* tempItemClicked();
|
||||
Tomahawk::ViewPage* getTempPage() const;
|
||||
|
||||
private:
|
||||
void playlistsAddedInternal( SourceTreeItem* parent, const QList< Tomahawk::dynplaylist_ptr >& playlists );
|
||||
template< typename T >
|
||||
@@ -54,6 +63,9 @@ private:
|
||||
Tomahawk::source_ptr m_source;
|
||||
CategoryItem* m_playlists;
|
||||
CategoryItem* m_stations;
|
||||
|
||||
GenericPageItem* m_tempItem;
|
||||
Tomahawk::ViewPage* m_curTempPage;
|
||||
};
|
||||
|
||||
|
||||
|
@@ -63,3 +63,10 @@ GenericPageItem::willAcceptDrag(const QMimeData* data) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
GenericPageItem::setText( const QString &text )
|
||||
{
|
||||
m_text = text;
|
||||
emit updated();
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ public:
|
||||
virtual bool willAcceptDrag( const QMimeData* data ) const;
|
||||
virtual QIcon icon() const;
|
||||
|
||||
void setText( const QString& text );
|
||||
signals:
|
||||
void activated();
|
||||
|
||||
|
@@ -30,7 +30,7 @@ SourceTreeItem::SourceTreeItem( SourcesModel* model, SourceTreeItem* parent, Sou
|
||||
connect( this, SIGNAL( childRowsAdded() ), m_model, SLOT( onItemRowsAddedDone() ) );
|
||||
connect( this, SIGNAL( childRowsRemoved() ), m_model, SLOT( onItemRowsRemovedDone() ) );
|
||||
connect( this, SIGNAL( updated() ), m_model, SLOT( itemUpdated() ) );
|
||||
|
||||
connect( this, SIGNAL( selectRequest( SourceTreeItem* ) ), m_model, SLOT( itemSelectRequest( SourceTreeItem* ) ) );
|
||||
if( !m_parent )
|
||||
return;
|
||||
|
||||
|
@@ -62,6 +62,7 @@ public:
|
||||
|
||||
signals:
|
||||
void updated();
|
||||
void selectRequest( SourceTreeItem* );
|
||||
|
||||
void beginChildRowsAdded( int fromRow, int toRow );
|
||||
void childRowsAdded();
|
||||
|
@@ -35,6 +35,7 @@ using namespace Tomahawk;
|
||||
|
||||
SourcesModel::SourcesModel( QObject* parent )
|
||||
: QAbstractItemModel( parent )
|
||||
, m_rootItem( 0 )
|
||||
, m_viewPageDelayedCacheItem( 0 )
|
||||
{
|
||||
m_rootItem = new SourceTreeItem( this, 0, Invalid );
|
||||
@@ -193,7 +194,7 @@ SourcesModel::mimeData( const QModelIndexList& ) const
|
||||
bool
|
||||
SourcesModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent )
|
||||
{
|
||||
SourceTreeItem* item;
|
||||
SourceTreeItem* item = 0;
|
||||
qDebug() << "Got mime data dropped:" << row << column << parent << itemFromIndex( parent )->text();
|
||||
if( row == -1 && column == -1 )
|
||||
item = itemFromIndex( parent );
|
||||
@@ -389,7 +390,6 @@ SourcesModel::linkSourceItemToPage( SourceTreeItem* item, ViewPage* p )
|
||||
m_viewPageDelayedCacheItem = 0;
|
||||
}
|
||||
|
||||
|
||||
SourceTreeItem*
|
||||
SourcesModel::itemFromIndex( const QModelIndex& idx ) const
|
||||
{
|
||||
@@ -448,3 +448,9 @@ SourcesModel::rowForItem( SourceTreeItem* item ) const
|
||||
{
|
||||
return item->parent()->children().indexOf( item );
|
||||
}
|
||||
|
||||
void
|
||||
SourcesModel::itemSelectRequest( SourceTreeItem* item )
|
||||
{
|
||||
emit selectRequest( indexFromItem( item ) );
|
||||
}
|
||||
|
@@ -90,16 +90,6 @@ public:
|
||||
|
||||
QModelIndex indexFromItem( SourceTreeItem* item ) const;
|
||||
|
||||
signals:
|
||||
void selectRequest( const QModelIndex& idx );
|
||||
|
||||
private slots:
|
||||
void onSourcesAdded( const QList<Tomahawk::source_ptr>& sources );
|
||||
void onSourceAdded( const Tomahawk::source_ptr& source );
|
||||
void onSourceRemoved( const Tomahawk::source_ptr& source );
|
||||
|
||||
void viewPageActivated( Tomahawk::ViewPage* );
|
||||
|
||||
public slots:
|
||||
void loadSources();
|
||||
|
||||
@@ -108,6 +98,18 @@ public slots:
|
||||
void onItemRowsAddedDone();
|
||||
void onItemRowsRemovedBegin( int first, int last );
|
||||
void onItemRowsRemovedDone();
|
||||
|
||||
void viewPageActivated( Tomahawk::ViewPage* );
|
||||
|
||||
void itemSelectRequest( SourceTreeItem* item );
|
||||
signals:
|
||||
void selectRequest( const QModelIndex& idx );
|
||||
|
||||
private slots:
|
||||
void onSourcesAdded( const QList<Tomahawk::source_ptr>& sources );
|
||||
void onSourceAdded( const Tomahawk::source_ptr& source );
|
||||
void onSourceRemoved( const Tomahawk::source_ptr& source );
|
||||
|
||||
private:
|
||||
SourceTreeItem* itemFromIndex( const QModelIndex& idx ) const;
|
||||
int rowForItem( SourceTreeItem* item ) const;
|
||||
|
@@ -143,10 +143,20 @@ SourceTreeView::setupMenus()
|
||||
m_copyPlaylistAction = m_playlistMenu.addAction( tr( "&Copy Link" ) );
|
||||
m_deletePlaylistAction = m_playlistMenu.addAction( tr( "&Delete %1" ).arg( SourcesModel::rowTypeToString( type ) ) );
|
||||
|
||||
m_roPlaylistMenu.addAction( m_copyPlaylistAction );
|
||||
QString addToText = QString( "Add to my %1" );
|
||||
if ( type == SourcesModel::StaticPlaylist )
|
||||
addToText = addToText.arg( "Playlists" );
|
||||
if ( type == SourcesModel::AutomaticPlaylist )
|
||||
addToText = addToText.arg( "Automatic Playlists" );
|
||||
else if ( type == SourcesModel::Station )
|
||||
addToText = addToText.arg( "Stations" );
|
||||
|
||||
m_addToLocalAction = m_roPlaylistMenu.addAction( tr( addToText.toUtf8(), "Adds the given playlist, dynamic playlist, or station to the users's own list" ) );
|
||||
|
||||
m_roPlaylistMenu.addAction( m_copyPlaylistAction );
|
||||
m_deletePlaylistAction->setEnabled( !readonly );
|
||||
m_renamePlaylistAction->setEnabled( !readonly );
|
||||
m_addToLocalAction->setEnabled( readonly );
|
||||
|
||||
if ( type == SourcesModel::StaticPlaylist )
|
||||
m_copyPlaylistAction->setText( tr( "&Export Playlist" ) );
|
||||
@@ -155,6 +165,7 @@ SourceTreeView::setupMenus()
|
||||
connect( m_renamePlaylistAction, SIGNAL( triggered() ), SLOT( renamePlaylist() ) );
|
||||
connect( m_deletePlaylistAction, SIGNAL( triggered() ), SLOT( deletePlaylist() ) );
|
||||
connect( m_copyPlaylistAction, SIGNAL( triggered() ), SLOT( copyPlaylistLink() ) );
|
||||
connect( m_addToLocalAction, SIGNAL( triggered() ), SLOT( addToLocal() ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -198,9 +209,11 @@ SourceTreeView::onItemExpanded( const QModelIndex& idx )
|
||||
void
|
||||
SourceTreeView::selectRequest( const QModelIndex& idx )
|
||||
{
|
||||
if( !selectionModel()->selectedIndexes().contains( idx ) )
|
||||
if ( !selectionModel()->selectedIndexes().contains( idx ) )
|
||||
{
|
||||
scrollTo( idx, QTreeView::EnsureVisible );
|
||||
selectionModel()->select( idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -212,23 +225,23 @@ SourceTreeView::loadPlaylist()
|
||||
|
||||
|
||||
void
|
||||
SourceTreeView::deletePlaylist()
|
||||
SourceTreeView::deletePlaylist( const QModelIndex& idxIn )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
QModelIndex idx = m_contextMenuIndex;
|
||||
QModelIndex idx = idxIn.isValid() ? idxIn : m_contextMenuIndex;
|
||||
if ( !idx.isValid() )
|
||||
return;
|
||||
|
||||
SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt();
|
||||
SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( idx, SourcesModel::SourceTreeItemTypeRole ).toInt();
|
||||
if ( type == SourcesModel::StaticPlaylist )
|
||||
{
|
||||
PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex );
|
||||
PlaylistItem* item = itemFromIndex< PlaylistItem >( idx );
|
||||
playlist_ptr playlist = item->playlist();
|
||||
Playlist::remove( playlist );
|
||||
} else if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station )
|
||||
{
|
||||
DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( m_contextMenuIndex );
|
||||
DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( idx );
|
||||
dynplaylist_ptr playlist = item->dynPlaylist();
|
||||
DynamicPlaylist::remove( playlist );
|
||||
}
|
||||
@@ -257,6 +270,36 @@ SourceTreeView::copyPlaylistLink()
|
||||
}
|
||||
}
|
||||
|
||||
void SourceTreeView::addToLocal()
|
||||
{
|
||||
QModelIndex idx = m_contextMenuIndex;
|
||||
if ( !idx.isValid() )
|
||||
return;
|
||||
|
||||
SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt();
|
||||
if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station )
|
||||
{
|
||||
DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( m_contextMenuIndex );
|
||||
dynplaylist_ptr playlist = item->dynPlaylist();
|
||||
|
||||
// copy to a link and then generate a new playlist from that
|
||||
// this way we cheaply regenerate the needed controls
|
||||
QString link = GlobalActionManager::instance()->copyPlaylistToClipboard( playlist );
|
||||
dynplaylist_ptr p = GlobalActionManager::instance()->loadDynamicPlaylist( link, type == SourcesModel::Station );
|
||||
} else if ( type == SourcesModel::StaticPlaylist )
|
||||
{
|
||||
PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex );
|
||||
playlist_ptr playlist = item->playlist();
|
||||
|
||||
// just create the new playlist with the same values
|
||||
QList< query_ptr > queries;
|
||||
foreach( const plentry_ptr& e, playlist->entries() )
|
||||
queries << e->query();
|
||||
|
||||
playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), playlist->title(), playlist->info(), playlist->creator(), playlist->shared(), queries );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SourceTreeView::renamePlaylist()
|
||||
@@ -358,6 +401,25 @@ SourceTreeView::dropEvent( QDropEvent* event )
|
||||
m_dragging = false;
|
||||
}
|
||||
|
||||
void
|
||||
SourceTreeView::keyPressEvent( QKeyEvent *event )
|
||||
{
|
||||
if( ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace ) && !selectionModel()->selectedIndexes().isEmpty() )
|
||||
{
|
||||
QModelIndex idx = selectionModel()->selectedIndexes().first();
|
||||
if ( model()->data( idx, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::StaticPlaylist ||
|
||||
model()->data( idx, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::AutomaticPlaylist ||
|
||||
model()->data( idx, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Station )
|
||||
{
|
||||
PlaylistItem* item = itemFromIndex< PlaylistItem >( idx );
|
||||
Q_ASSERT( item );
|
||||
|
||||
if( item->playlist()->author()->isLocal() ) {
|
||||
deletePlaylist( idx );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SourceTreeView::paintEvent( QPaintEvent* event )
|
||||
|
@@ -52,8 +52,9 @@ private slots:
|
||||
void selectRequest( const QModelIndex& idx );
|
||||
|
||||
void loadPlaylist();
|
||||
void deletePlaylist();
|
||||
void deletePlaylist( const QModelIndex& = QModelIndex() );
|
||||
void copyPlaylistLink();
|
||||
void addToLocal();
|
||||
|
||||
void onCustomContextMenu( const QPoint& pos );
|
||||
protected:
|
||||
@@ -66,6 +67,7 @@ protected:
|
||||
virtual void dragLeaveEvent( QDragLeaveEvent* event ) { Q_UNUSED( event ); m_dragging = false; setDirtyRegion( m_dropRect ); }
|
||||
virtual void dragMoveEvent( QDragMoveEvent* event );
|
||||
virtual void dropEvent( QDropEvent* event );
|
||||
virtual void keyPressEvent( QKeyEvent* event );
|
||||
|
||||
private:
|
||||
void setupMenus();
|
||||
@@ -83,6 +85,7 @@ private:
|
||||
QAction* m_renamePlaylistAction;
|
||||
QAction* m_deletePlaylistAction;
|
||||
QAction* m_copyPlaylistAction;
|
||||
QAction* m_addToLocalAction;
|
||||
|
||||
bool m_dragging;
|
||||
QRect m_dropRect;
|
||||
|
@@ -330,26 +330,21 @@ TomahawkApp::~TomahawkApp()
|
||||
|
||||
#ifndef TOMAHAWK_HEADLESS
|
||||
delete m_mainwindow;
|
||||
delete m_audioEngine;
|
||||
#endif
|
||||
|
||||
delete m_infoSystem;
|
||||
|
||||
//FIXME: delete GeneratorFactory::registerFactory( "echonest", new EchonestFactory ); ?
|
||||
|
||||
delete m_database;
|
||||
|
||||
delete SipHandler::instance();
|
||||
|
||||
delete SipHandler::instance();
|
||||
delete m_servent;
|
||||
|
||||
Pipeline::instance()->stop();
|
||||
delete Pipeline::instance();
|
||||
|
||||
delete m_scanManager;
|
||||
delete m_database;
|
||||
|
||||
#ifndef TOMAHAWK_HEADLESS
|
||||
delete m_audioEngine;
|
||||
#endif
|
||||
Pipeline::instance()->stop();
|
||||
delete Pipeline::instance();
|
||||
|
||||
delete TomahawkUtils::proxyFactory();
|
||||
delete TomahawkUtils::nam();
|
||||
|
@@ -50,12 +50,14 @@
|
||||
#include "utils/widgetdragfilter.h"
|
||||
#include "utils/xspfloader.h"
|
||||
#include "widgets/newplaylistwidget.h"
|
||||
#include "widgets/playlisttypeselectordlg.h"
|
||||
|
||||
#include "audiocontrols.h"
|
||||
#include "settingsdialog.h"
|
||||
#include "diagnosticsdialog.h"
|
||||
#include "tomahawksettings.h"
|
||||
#include "sourcelist.h"
|
||||
#include "PipelineStatusView.h"
|
||||
#include "transferview.h"
|
||||
#include "tomahawktrayicon.h"
|
||||
#include "playlist/dynamic/GeneratorInterface.h"
|
||||
@@ -106,18 +108,19 @@ TomahawkWindow::TomahawkWindow( QWidget* parent )
|
||||
sidebar->setOrientation( Qt::Vertical );
|
||||
sidebar->setChildrenCollapsible( false );
|
||||
sidebar->setGreedyWidget( 0 );
|
||||
sidebar->setStretchFactor( 0, 3 );
|
||||
sidebar->setStretchFactor( 1, 1 );
|
||||
|
||||
m_sourcetree = new SourceTreeView();
|
||||
TransferView* transferView = new TransferView();
|
||||
TransferView* transferView = new TransferView( sidebar );
|
||||
PipelineStatusView* pipelineView = new PipelineStatusView( sidebar );
|
||||
|
||||
connect( ui->actionHideOfflineSources, SIGNAL( triggered() ), m_sourcetree, SLOT( hideOfflineSources() ) );
|
||||
connect( ui->actionShowOfflineSources, SIGNAL( triggered() ), m_sourcetree, SLOT( showOfflineSources() ) );
|
||||
|
||||
sidebar->addWidget( m_sourcetree );
|
||||
sidebar->addWidget( transferView );
|
||||
sidebar->addWidget( pipelineView );
|
||||
sidebar->hide( 1, false );
|
||||
sidebar->hide( 2, false );
|
||||
|
||||
/* QWidget* buttonWidget = new QWidget();
|
||||
buttonWidget->setLayout( new QVBoxLayout() );
|
||||
@@ -232,7 +235,6 @@ TomahawkWindow::loadSettings()
|
||||
restoreState( s->mainWindowState() );
|
||||
if ( !s->mainWindowSplitterState().isEmpty() )
|
||||
ui->splitter->restoreState( s->mainWindowSplitterState() );
|
||||
|
||||
#ifdef QT_MAC_USE_COCOA
|
||||
if( workaround ) {
|
||||
// Make it visible again
|
||||
@@ -274,7 +276,6 @@ TomahawkWindow::setupSignals()
|
||||
connect( ui->actionRescanCollection, SIGNAL( triggered() ), SLOT( updateCollectionManually() ) );
|
||||
connect( ui->actionLoadXSPF, SIGNAL( triggered() ), SLOT( loadSpiff() ));
|
||||
connect( ui->actionCreatePlaylist, SIGNAL( triggered() ), SLOT( createPlaylist() ));
|
||||
connect( ui->actionCreateAutomaticPlaylist, SIGNAL( triggered() ), SLOT( createAutomaticPlaylist() ));
|
||||
connect( ui->actionCreate_New_Station, SIGNAL( triggered() ), SLOT( createStation() ));
|
||||
connect( ui->actionAboutTomahawk, SIGNAL( triggered() ), SLOT( showAboutTomahawk() ) );
|
||||
connect( ui->actionExit, SIGNAL( triggered() ), qApp, SLOT( quit() ) );
|
||||
@@ -444,11 +445,11 @@ TomahawkWindow::loadSpiff()
|
||||
|
||||
|
||||
void
|
||||
TomahawkWindow::createAutomaticPlaylist()
|
||||
TomahawkWindow::createAutomaticPlaylist( QString playlistName )
|
||||
{
|
||||
bool ok;
|
||||
QString name = QInputDialog::getText( this, tr( "Create New Automatic Playlist" ), tr( "Name:" ), QLineEdit::Normal, tr( "New Automatic Playlist" ), &ok );
|
||||
if ( !ok || name.isEmpty() )
|
||||
QString name = playlistName;
|
||||
|
||||
if ( name.isEmpty() )
|
||||
return;
|
||||
|
||||
source_ptr author = SourceList::instance()->getLocal();
|
||||
@@ -484,7 +485,21 @@ TomahawkWindow::createStation()
|
||||
void
|
||||
TomahawkWindow::createPlaylist()
|
||||
{
|
||||
ViewManager::instance()->show( new NewPlaylistWidget() );
|
||||
PlaylistTypeSelectorDlg playlistSelectorDlg;
|
||||
int successfulReturn = playlistSelectorDlg.exec();
|
||||
|
||||
if ( !playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) {
|
||||
|
||||
// only show if none is shown yet
|
||||
if( !ViewManager::instance()->isNewPlaylistPageVisible() ) {
|
||||
ViewManager::instance()->show( new NewPlaylistWidget() );
|
||||
}
|
||||
|
||||
} else if ( playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) {
|
||||
// create Auto Playlist
|
||||
QString playlistName = playlistSelectorDlg.playlistName();
|
||||
APP->mainWindow()->createAutomaticPlaylist( playlistName );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -61,7 +61,7 @@ protected:
|
||||
void hideEvent( QHideEvent* e );
|
||||
|
||||
public slots:
|
||||
void createAutomaticPlaylist();
|
||||
void createAutomaticPlaylist( QString );
|
||||
void createStation();
|
||||
void createPlaylist();
|
||||
void loadSpiff();
|
||||
|
@@ -35,7 +35,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1000</width>
|
||||
<height>22</height>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuSettings">
|
||||
@@ -55,16 +55,6 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuPlaylist">
|
||||
<property name="title">
|
||||
<string>&Playlist</string>
|
||||
</property>
|
||||
<addaction name="actionCreatePlaylist"/>
|
||||
<addaction name="actionCreateAutomaticPlaylist"/>
|
||||
<addaction name="actionCreate_New_Station"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLoadXSPF"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuNetwork">
|
||||
<property name="title">
|
||||
<string>&Network</string>
|
||||
@@ -86,6 +76,15 @@
|
||||
<addaction name="actionDiagnostics"/>
|
||||
<addaction name="actionAboutTomahawk"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuPlaylist">
|
||||
<property name="title">
|
||||
<string>&Playlist</string>
|
||||
</property>
|
||||
<addaction name="actionCreatePlaylist"/>
|
||||
<addaction name="actionCreate_New_Station"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLoadXSPF"/>
|
||||
</widget>
|
||||
<addaction name="menuApp"/>
|
||||
<addaction name="menuPlaylist"/>
|
||||
<addaction name="menuNetwork"/>
|
||||
@@ -107,7 +106,7 @@
|
||||
</action>
|
||||
<action name="actionToggleConnect">
|
||||
<property name="text">
|
||||
<string>Go &online</string>
|
||||
<string>Go &Online</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAddFriendManually">
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
@@ -80,12 +80,6 @@ TransferView::streamFinished( StreamConnection* sc )
|
||||
emit showWidget();
|
||||
else
|
||||
emit hideWidget();
|
||||
|
||||
/* if ( m_index.contains( sc ) )
|
||||
{
|
||||
int i = m_index.value( sc );
|
||||
m_tree->invisibleRootItem()->child( i )->setText( 1, tr( "Finished" ) );
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
|
||||
*
|
||||
*
|
||||
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
|
||||
*
|
||||
* Tomahawk is free software: you can redistribute it and/or modify
|
||||
@@ -32,7 +32,7 @@ class TransferView : public AnimatedWidget
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TransferView( AnimatedSplitter* parent = 0 );
|
||||
explicit TransferView( AnimatedSplitter* parent );
|
||||
virtual ~TransferView()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
3
thirdparty/CMakeLists.txt
vendored
3
thirdparty/CMakeLists.txt
vendored
@@ -1,2 +1,5 @@
|
||||
ADD_SUBDIRECTORY( qxt )
|
||||
ADD_SUBDIRECTORY( liblastfm2 )
|
||||
IF( APPLE )
|
||||
ADD_SUBDIRECTORY( SPMediaKeyTap )
|
||||
ENDIF()
|
||||
|
13
thirdparty/SPMediaKeyTap/CMakeLists.txt
vendored
Normal file
13
thirdparty/SPMediaKeyTap/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
set(SPMEDIAKEY-SOURCES
|
||||
SPMediaKeyTap.m
|
||||
SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
|
||||
)
|
||||
|
||||
set(SPMEDIAKEY-HEADERS
|
||||
SPMediaKeyTap.h
|
||||
SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h
|
||||
)
|
||||
|
||||
ADD_LIBRARY(SPMediaKeyTap STATIC
|
||||
${SPMEDIAKEY-SOURCES}
|
||||
)
|
8
thirdparty/SPMediaKeyTap/LICENSE
vendored
Normal file
8
thirdparty/SPMediaKeyTap/LICENSE
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
Copyright (c) 2011, Joachim Bengtsson
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Neither the name of the organization 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
12
thirdparty/SPMediaKeyTap/README.md
vendored
Normal file
12
thirdparty/SPMediaKeyTap/README.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
SPMediaKeyTap
|
||||
=============
|
||||
|
||||
`SPMediaKeyTap` abstracts a `CGEventHook` and other nastiness in order to give you a relatively simple API to receive media key events (prev/next/playpause, on F7 to F9 on modern MacBook Pros) exclusively, without them reaching other applications like iTunes. `SPMediaKeyTap` is clever enough to resign its exclusive lock on media keys by looking for which application was active most recently: if that application is in `SPMediaKeyTap`'s whitelist, it will resign the keys. This is similar to the behavior of Apple's applications collaborating on media key handling exclusivity, but unfortunately, Apple are not exposing any APIs allowing third-parties to join in on this collaboration.
|
||||
|
||||
For now, the whitelist is just a hardcoded array in `+[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers]`. If your app starts using `SPMediaKeyTap`, please [mail me](mailto:nevyn@spotify.com) your bundle ID, and I'll include it in the canonical repository. This is a bad solution; a better solution would be to use distributed notifications to collaborate in creating this whitelist at runtime. Hopefully someone'll have the time and energy to write this soon.
|
||||
|
||||
In `Example/SPMediaKeyTapExampleAppDelegate.m` is an example of both how you use `SPMediaKeyTap`, and how you handle the semi-private `NSEvent` subtypes involved in media keys, including on how to fall back to non-event tap handling of these events.
|
||||
|
||||
`SPMediaKeyTap` and other `CGEventHook`s on the event type `NSSystemDefined` is known to interfere with each other and applications doing weird stuff with mouse input, because mouse clicks are also part of the `NSSystemDefined` category. The single issue we have had reported here at Spotify is Adobe Fireworks, in which item selection stops working with `SPMediaKeyTap` is active.
|
||||
|
||||
`SPMediaKeyTap` requires 10.5 to work, and disables itself on 10.4.
|
30
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h
vendored
Normal file
30
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SPInvocationGrabber : NSObject {
|
||||
id _object;
|
||||
NSInvocation *_invocation;
|
||||
int frameCount;
|
||||
char **frameStrings;
|
||||
BOOL backgroundAfterForward;
|
||||
BOOL onMainAfterForward;
|
||||
BOOL waitUntilDone;
|
||||
}
|
||||
-(id)initWithObject:(id)obj;
|
||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
||||
@property (readonly, retain, nonatomic) id object;
|
||||
@property (readonly, retain, nonatomic) NSInvocation *invocation;
|
||||
@property BOOL backgroundAfterForward;
|
||||
@property BOOL onMainAfterForward;
|
||||
@property BOOL waitUntilDone;
|
||||
-(void)invoke; // will release object and invocation
|
||||
-(void)printBacktrace;
|
||||
-(void)saveBacktrace;
|
||||
@end
|
||||
|
||||
@interface NSObject (SPInvocationGrabbing)
|
||||
-(id)grab;
|
||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
||||
-(id)nextRunloop;
|
||||
-(id)inBackground;
|
||||
-(id)onMainAsync:(BOOL)async;
|
||||
@end
|
128
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
vendored
Normal file
128
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
#import "NSObject+SPInvocationGrabbing.h"
|
||||
#import <execinfo.h>
|
||||
|
||||
#pragma mark Invocation grabbing
|
||||
@interface SPInvocationGrabber ()
|
||||
@property (readwrite, retain, nonatomic) id object;
|
||||
@property (readwrite, retain, nonatomic) NSInvocation *invocation;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SPInvocationGrabber
|
||||
- (id)initWithObject:(id)obj;
|
||||
{
|
||||
return [self initWithObject:obj stacktraceSaving:YES];
|
||||
}
|
||||
|
||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
||||
{
|
||||
self.object = obj;
|
||||
|
||||
if(saveStack)
|
||||
[self saveBacktrace];
|
||||
|
||||
return self;
|
||||
}
|
||||
-(void)dealloc;
|
||||
{
|
||||
free(frameStrings);
|
||||
self.object = nil;
|
||||
self.invocation = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
@synthesize invocation = _invocation, object = _object;
|
||||
|
||||
@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone;
|
||||
- (void)runInBackground;
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
@try {
|
||||
[self invoke];
|
||||
}
|
||||
@finally {
|
||||
[pool drain];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)forwardInvocation:(NSInvocation *)anInvocation {
|
||||
[anInvocation retainArguments];
|
||||
anInvocation.target = _object;
|
||||
self.invocation = anInvocation;
|
||||
|
||||
if(backgroundAfterForward)
|
||||
[NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil];
|
||||
else if(onMainAfterForward)
|
||||
[self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone];
|
||||
}
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector {
|
||||
NSMethodSignature *signature = [super methodSignatureForSelector:inSelector];
|
||||
if (signature == NULL)
|
||||
signature = [_object methodSignatureForSelector:inSelector];
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
- (void)invoke;
|
||||
{
|
||||
|
||||
@try {
|
||||
[_invocation invoke];
|
||||
}
|
||||
@catch (NSException * e) {
|
||||
NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e);
|
||||
[self printBacktrace];
|
||||
printf("\n");
|
||||
[e raise];
|
||||
}
|
||||
|
||||
self.invocation = nil;
|
||||
self.object = nil;
|
||||
}
|
||||
|
||||
-(void)saveBacktrace;
|
||||
{
|
||||
void *backtraceFrames[128];
|
||||
frameCount = backtrace(&backtraceFrames[0], 128);
|
||||
frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
|
||||
}
|
||||
-(void)printBacktrace;
|
||||
{
|
||||
int x;
|
||||
for(x = 3; x < frameCount; x++) {
|
||||
if(frameStrings[x] == NULL) { break; }
|
||||
printf("%s\n", frameStrings[x]);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSObject (SPInvocationGrabbing)
|
||||
-(id)grab;
|
||||
{
|
||||
return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease];
|
||||
}
|
||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
||||
{
|
||||
id grabber = [self grab];
|
||||
[NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO];
|
||||
return grabber;
|
||||
}
|
||||
- (id)nextRunloop;
|
||||
{
|
||||
return [self invokeAfter:0];
|
||||
}
|
||||
-(id)inBackground;
|
||||
{
|
||||
SPInvocationGrabber *grabber = [self grab];
|
||||
grabber.backgroundAfterForward = YES;
|
||||
return grabber;
|
||||
}
|
||||
-(id)onMainAsync:(BOOL)async;
|
||||
{
|
||||
SPInvocationGrabber *grabber = [self grab];
|
||||
grabber.onMainAfterForward = YES;
|
||||
grabber.waitUntilDone = !async;
|
||||
return grabber;
|
||||
}
|
||||
|
||||
@end
|
28
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile3.m
vendored
Normal file
28
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile3.m
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// A
|
||||
+(UIView*)flashAt:(CGRect)r in:(UIView*)parent color:(UIColor*)color;
|
||||
{
|
||||
float duration = 0.5;
|
||||
UIView *flash = [[[UIView alloc] initWithFrame:r] autorelease];
|
||||
flash.backgroundColor = color;
|
||||
[parent addSubview:flash];
|
||||
[[flash invokeAfter:duration+0.1] removeFromSuperview];
|
||||
|
||||
[UIView beginAnimations:@"SPFlash" context:NULL];
|
||||
[UIView setAnimationDuration:duration];
|
||||
flash.alpha = 0.0;
|
||||
[UIView commitAnimations];
|
||||
return flash;
|
||||
}
|
||||
|
||||
// B
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
// Force the animation to happen by calling this method again after a small
|
||||
// delay - see http://blog.instapaper.com/post/53568356
|
||||
[[self nextRunloop] delayedTableViewDidSelectRowAtIndexPath: indexPath];
|
||||
}
|
||||
|
||||
// C
|
||||
[[tableView invokeAfter:0.15] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
|
||||
[[tableView invokeAfter:0.30] deselectRowAtIndexPath:indexPath animated:YES];
|
||||
[[tableView invokeAfter:0.45] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
|
12
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile4.m
vendored
Normal file
12
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/gistfile4.m
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
@interface MyClass : NSObject
|
||||
-(BOOL)areTheNewViewersGoneYet:(Duck*)duck;
|
||||
@end
|
||||
...
|
||||
MyClass *myInstance = [[MyClass alloc] init];
|
||||
id invocationGrabber = [[[SPInvocationGrabber alloc] initWithTarget:myInstance] autorelease];
|
||||
|
||||
|
||||
[invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]]; // line 9
|
||||
|
||||
|
||||
NSInvocation *invocationForAreTheNewViewersGoneYet = [invocationGrabber invocation];
|
38
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/main.m
vendored
Normal file
38
thirdparty/SPMediaKeyTap/SPInvocationGrabbing/main.m
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "NSObject+SPInvocationGrabbing.h"
|
||||
|
||||
@interface Foo : NSObject {
|
||||
int a;
|
||||
}
|
||||
-(void)startIt;
|
||||
-(void)theBackgroundStuff;
|
||||
-(void)theForegroundStuff;
|
||||
@end
|
||||
|
||||
@implementation Foo
|
||||
-(void)startIt;
|
||||
{
|
||||
NSLog(@"Starting out on the main thread...");
|
||||
a = 3;
|
||||
[[self inBackground] theBackgroundStuff];
|
||||
}
|
||||
-(void)theBackgroundStuff;
|
||||
{
|
||||
NSLog(@"Woah, this is a background thread!");
|
||||
a += 6;
|
||||
[[self onMainAsync:YES] theForegroundStuff];
|
||||
}
|
||||
-(void)theForegroundStuff;
|
||||
{
|
||||
NSLog(@"Hey presto: %d", a);
|
||||
}
|
||||
@end
|
||||
|
||||
int main() {
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
Foo *foo = [Foo new];
|
||||
[foo startIt];
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
|
||||
[pool release];
|
||||
return 0;
|
||||
}
|
34
thirdparty/SPMediaKeyTap/SPMediaKeyTap.h
vendored
Normal file
34
thirdparty/SPMediaKeyTap/SPMediaKeyTap.h
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <IOKit/hidsystem/ev_keymap.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
// http://overooped.com/post/2593597587/mediakeys
|
||||
|
||||
#define SPSystemDefinedEventMediaKeys 8
|
||||
|
||||
@interface SPMediaKeyTap : NSObject {
|
||||
EventHandlerRef _app_switching_ref;
|
||||
EventHandlerRef _app_terminating_ref;
|
||||
CFMachPortRef _eventPort;
|
||||
CFRunLoopSourceRef _eventPortSource;
|
||||
CFRunLoopRef _tapThreadRL;
|
||||
BOOL _shouldInterceptMediaKeyEvents;
|
||||
id _delegate;
|
||||
// The app that is frontmost in this list owns media keys
|
||||
NSMutableArray *_mediaKeyAppList;
|
||||
}
|
||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
||||
|
||||
-(id)initWithDelegate:(id)delegate;
|
||||
|
||||
+(BOOL)usesGlobalMediaKeyTap;
|
||||
-(void)startWatchingMediaKeys;
|
||||
-(void)stopWatchingMediaKeys;
|
||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event;
|
||||
@end
|
||||
|
||||
@interface NSObject (SPMediaKeyTapDelegate)
|
||||
-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
||||
@end
|
||||
|
||||
extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey;
|
300
thirdparty/SPMediaKeyTap/SPMediaKeyTap.m
vendored
Normal file
300
thirdparty/SPMediaKeyTap/SPMediaKeyTap.m
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
// Copyright (c) 2010 Spotify AB
|
||||
#import "SPMediaKeyTap.h"
|
||||
#import "SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h" // https://gist.github.com/511181, in submodule
|
||||
|
||||
@interface SPMediaKeyTap ()
|
||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
||||
-(void)startWatchingAppSwitching;
|
||||
-(void)stopWatchingAppSwitching;
|
||||
-(void)eventTapThread;
|
||||
@end
|
||||
static SPMediaKeyTap *singleton = nil;
|
||||
|
||||
static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
||||
static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
||||
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon);
|
||||
|
||||
|
||||
// Inspired by http://gist.github.com/546311
|
||||
|
||||
@implementation SPMediaKeyTap
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Setup and teardown
|
||||
-(id)initWithDelegate:(id)delegate;
|
||||
{
|
||||
_delegate = delegate;
|
||||
[self startWatchingAppSwitching];
|
||||
singleton = self;
|
||||
_mediaKeyAppList = [NSMutableArray new];
|
||||
return self;
|
||||
}
|
||||
-(void)dealloc;
|
||||
{
|
||||
[self stopWatchingMediaKeys];
|
||||
[self stopWatchingAppSwitching];
|
||||
[_mediaKeyAppList release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
-(void)startWatchingAppSwitching;
|
||||
{
|
||||
// Listen to "app switched" event, so that we don't intercept media keys if we
|
||||
// weren't the last "media key listening" app to be active
|
||||
EventTypeSpec eventType = { kEventClassApplication, kEventAppFrontSwitched };
|
||||
OSStatus err = InstallApplicationEventHandler(NewEventHandlerUPP(appSwitched), 1, &eventType, self, &_app_switching_ref);
|
||||
assert(err == noErr);
|
||||
|
||||
eventType.eventKind = kEventAppTerminated;
|
||||
err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, self, &_app_terminating_ref);
|
||||
assert(err == noErr);
|
||||
}
|
||||
-(void)stopWatchingAppSwitching;
|
||||
{
|
||||
if(!_app_switching_ref) return;
|
||||
RemoveEventHandler(_app_switching_ref);
|
||||
_app_switching_ref = NULL;
|
||||
}
|
||||
|
||||
-(void)startWatchingMediaKeys;{
|
||||
[self setShouldInterceptMediaKeyEvents:YES];
|
||||
|
||||
// Add an event tap to intercept the system defined media key events
|
||||
_eventPort = CGEventTapCreate(kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionDefault,
|
||||
CGEventMaskBit(NX_SYSDEFINED),
|
||||
tapEventCallback,
|
||||
self);
|
||||
assert(_eventPort != NULL);
|
||||
|
||||
_eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0);
|
||||
assert(_eventPortSource != NULL);
|
||||
|
||||
// Let's do this in a separate thread so that a slow app doesn't lag the event tap
|
||||
[NSThread detachNewThreadSelector:@selector(eventTapThread) toTarget:self withObject:nil];
|
||||
}
|
||||
-(void)stopWatchingMediaKeys;
|
||||
{
|
||||
// TODO<nevyn>: Shut down thread, remove event tap port and source
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
+(BOOL)usesGlobalMediaKeyTap
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot
|
||||
return NO;
|
||||
#else
|
||||
// XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy.
|
||||
return floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
||||
{
|
||||
return [NSArray arrayWithObjects:
|
||||
[[NSBundle mainBundle] bundleIdentifier], // your app
|
||||
@"com.spotify.client",
|
||||
@"com.apple.iTunes",
|
||||
@"com.apple.QuickTimePlayerX",
|
||||
@"com.apple.quicktimeplayer",
|
||||
@"com.apple.iWork.Keynote",
|
||||
@"com.apple.iPhoto",
|
||||
@"org.videolan.vlc",
|
||||
@"com.apple.Aperture",
|
||||
@"com.plexsquared.Plex",
|
||||
@"com.soundcloud.desktop",
|
||||
@"com.macromedia.fireworks", // the tap messes up their mouse input
|
||||
nil
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
||||
{
|
||||
BOOL shouldIntercept = NO;
|
||||
@synchronized(self) {
|
||||
shouldIntercept = _shouldInterceptMediaKeyEvents;
|
||||
}
|
||||
return shouldIntercept;
|
||||
}
|
||||
|
||||
-(void)pauseTapOnTapThread:(BOOL)yeahno;
|
||||
{
|
||||
CGEventTapEnable(self->_eventPort, yeahno);
|
||||
}
|
||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
||||
{
|
||||
BOOL oldSetting;
|
||||
@synchronized(self) {
|
||||
oldSetting = _shouldInterceptMediaKeyEvents;
|
||||
_shouldInterceptMediaKeyEvents = newSetting;
|
||||
}
|
||||
if(_tapThreadRL && oldSetting != newSetting) {
|
||||
id grab = [self grab];
|
||||
[grab pauseTapOnTapThread:newSetting];
|
||||
NSTimer *timer = [NSTimer timerWithTimeInterval:0 invocation:[grab invocation] repeats:NO];
|
||||
CFRunLoopAddTimer(_tapThreadRL, (CFRunLoopTimerRef)timer, kCFRunLoopCommonModes);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark
|
||||
#pragma mark -
|
||||
#pragma mark Event tap callbacks
|
||||
|
||||
// Note: method called on background thread
|
||||
|
||||
static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
||||
{
|
||||
SPMediaKeyTap *self = refcon;
|
||||
|
||||
if(type == kCGEventTapDisabledByTimeout) {
|
||||
NSLog(@"Media key event tap was disabled by timeout");
|
||||
CGEventTapEnable(self->_eventPort, TRUE);
|
||||
return event;
|
||||
} else if(type == kCGEventTapDisabledByUserInput) {
|
||||
// Was disabled manually by -[pauseTapOnTapThread]
|
||||
return event;
|
||||
}
|
||||
NSEvent *nsEvent = nil;
|
||||
@try {
|
||||
nsEvent = [NSEvent eventWithCGEvent:event];
|
||||
}
|
||||
@catch (NSException * e) {
|
||||
NSLog(@"Strange CGEventType: %d: %@", type, e);
|
||||
assert(0);
|
||||
return event;
|
||||
}
|
||||
|
||||
if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys)
|
||||
return event;
|
||||
|
||||
int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16);
|
||||
if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND)
|
||||
return event;
|
||||
|
||||
if (![self shouldInterceptMediaKeyEvents])
|
||||
return event;
|
||||
|
||||
[nsEvent retain]; // matched in handleAndReleaseMediaKeyEvent:
|
||||
[self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
||||
{
|
||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||
CGEventRef ret = tapEventCallback2(proxy, type, event, refcon);
|
||||
[pool drain];
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// event will have been retained in the other thread
|
||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event {
|
||||
[event autorelease];
|
||||
|
||||
[_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
|
||||
}
|
||||
|
||||
|
||||
-(void)eventTapThread;
|
||||
{
|
||||
_tapThreadRL = CFRunLoopGetCurrent();
|
||||
CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes);
|
||||
CFRunLoopRun();
|
||||
}
|
||||
|
||||
#pragma mark Task switching callbacks
|
||||
|
||||
NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys";
|
||||
|
||||
|
||||
-(void)mediaKeyAppListChanged;
|
||||
{
|
||||
if([_mediaKeyAppList count] == 0) return;
|
||||
|
||||
/*NSLog(@"--");
|
||||
int i = 0;
|
||||
for (NSValue *psnv in _mediaKeyAppList) {
|
||||
ProcessSerialNumber psn; [psnv getValue:&psn];
|
||||
NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary(
|
||||
&psn,
|
||||
kProcessDictionaryIncludeAllInformationMask
|
||||
) autorelease];
|
||||
NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
||||
NSLog(@"%d: %@", i++, bundleIdentifier);
|
||||
}*/
|
||||
|
||||
ProcessSerialNumber mySerial, topSerial;
|
||||
GetCurrentProcess(&mySerial);
|
||||
[[_mediaKeyAppList objectAtIndex:0] getValue:&topSerial];
|
||||
|
||||
Boolean same;
|
||||
OSErr err = SameProcess(&mySerial, &topSerial, &same);
|
||||
[self setShouldInterceptMediaKeyEvents:(err == noErr && same)];
|
||||
|
||||
}
|
||||
-(void)appIsNowFrontmost:(ProcessSerialNumber)psn;
|
||||
{
|
||||
NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)];
|
||||
|
||||
NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary(
|
||||
&psn,
|
||||
kProcessDictionaryIncludeAllInformationMask
|
||||
) autorelease];
|
||||
NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
||||
|
||||
NSArray *whitelistIdentifiers = [[NSUserDefaults standardUserDefaults] arrayForKey:kMediaKeyUsingBundleIdentifiersDefaultsKey];
|
||||
if(![whitelistIdentifiers containsObject:bundleIdentifier]) return;
|
||||
|
||||
[_mediaKeyAppList removeObject:psnv];
|
||||
[_mediaKeyAppList insertObject:psnv atIndex:0];
|
||||
[self mediaKeyAppListChanged];
|
||||
}
|
||||
-(void)appTerminated:(ProcessSerialNumber)psn;
|
||||
{
|
||||
NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)];
|
||||
[_mediaKeyAppList removeObject:psnv];
|
||||
[self mediaKeyAppListChanged];
|
||||
}
|
||||
|
||||
static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData)
|
||||
{
|
||||
SPMediaKeyTap *self = (id)userData;
|
||||
|
||||
ProcessSerialNumber newSerial;
|
||||
GetFrontProcess(&newSerial);
|
||||
|
||||
[self appIsNowFrontmost:newSerial];
|
||||
|
||||
return CallNextEventHandler(nextHandler, evt);
|
||||
}
|
||||
|
||||
static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData)
|
||||
{
|
||||
SPMediaKeyTap *self = (id)userData;
|
||||
|
||||
ProcessSerialNumber deadPSN;
|
||||
|
||||
GetEventParameter(
|
||||
evt,
|
||||
kEventParamProcessID,
|
||||
typeProcessSerialNumber,
|
||||
NULL,
|
||||
sizeof(deadPSN),
|
||||
NULL,
|
||||
&deadPSN
|
||||
);
|
||||
|
||||
|
||||
[self appTerminated:deadPSN];
|
||||
return CallNextEventHandler(nextHandler, evt);
|
||||
}
|
||||
|
||||
@end
|
25
thirdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m
vendored
Normal file
25
thirdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
||||
{
|
||||
assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
|
||||
|
||||
int keyCode = (([event data1] & 0xFFFF0000) >> 16);
|
||||
int keyFlags = ([event data1] & 0x0000FFFF);
|
||||
int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
|
||||
int keyRepeat = (keyFlags & 0x1);
|
||||
|
||||
if (keyState == 1 && windowController != NULL) {
|
||||
|
||||
|
||||
switch (keyCode) {
|
||||
|
||||
case NX_KEYTYPE_PLAY:
|
||||
... return;
|
||||
|
||||
case NX_KEYTYPE_FAST:
|
||||
... return;
|
||||
|
||||
case NX_KEYTYPE_REWIND:
|
||||
... return;
|
||||
}
|
||||
}
|
||||
}
|
2
thirdparty/jreen
vendored
2
thirdparty/jreen
vendored
Submodule thirdparty/jreen updated: a231a2b386...8f995f2466
Reference in New Issue
Block a user