1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-20 07:49:42 +01:00

Merge branch 'master' into playlistinterface_ptr

Conflicts:
	src/libtomahawk/playlistinterface.h
This commit is contained in:
Jeff Mitchell 2011-12-21 14:08:57 -05:00
commit 44cbcd434e
33 changed files with 880 additions and 402 deletions

2
README

@ -39,7 +39,7 @@ Dependencies
The following dependencies are optional, but recommended:
Attica 0.2.0 - ftp://ftp.kde.org/pub/kde/stable/attica/
jreen (Git) - https://github.com/euroelessar/jreen
Jreen 1.0.1 - https://github.com/euroelessar/jreen
QTweetLib 0.3.0 - https://github.com/minimoog/QTweetLib
Third party libraries that we ship with our source:

Binary file not shown.

Before

(image error) Size: 2.5 KiB

After

(image error) Size: 10 KiB

@ -2,7 +2,7 @@
// if run in phantomjs add fake Tomahawk environment
if(window.Tomahawk === undefined)
{
alert("PHANTOMJS ENVIRONMENT");
// alert("PHANTOMJS ENVIRONMENT");
var Tomahawk = {
fakeEnv: function()
{

@ -60,11 +60,13 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
sourcetree/sourcedelegate.cpp
sourcetree/animationhelper.cpp
sourcetree/items/sourcetreeitem.cpp
sourcetree/items/collectionitem.cpp
sourcetree/items/sourceitem.cpp
sourcetree/items/playlistitems.cpp
sourcetree/items/categoryitems.cpp
sourcetree/items/genericpageitems.cpp
sourcetree/items/temporarypageitem.cpp
sourcetree/items/groupitem.cpp
sourcetree/items/historyitem.cpp
breakpad/BreakPad.cpp
@ -112,11 +114,13 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui}
sourcetree/sourcedelegate.h
sourcetree/animationhelper.h
sourcetree/items/sourcetreeitem.h
sourcetree/items/collectionitem.h
sourcetree/items/sourceitem.h
sourcetree/items/playlistitems.h
sourcetree/items/categoryitems.h
sourcetree/items/genericpageitems.h
sourcetree/items/temporarypageitem.h
sourcetree/items/groupitem.h
sourcetree/items/historyitem.h
tomahawktrayicon.h
audiocontrols.h

@ -76,9 +76,9 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
typedef QPair<int, float> scorepair_t;
// STEP 1
QList< QPair<int, float> > artists = lib->searchTable( "artist", m_query->artist(), 10 );
QList< QPair<int, float> > tracks = lib->searchTable( "track", m_query->track(), 10 );
QList< QPair<int, float> > albums = lib->searchTable( "album", m_query->album(), 10 );
QList< QPair<int, float> > artists = lib->searchTable( "artist", m_query->artist() );
QList< QPair<int, float> > tracks = lib->searchTable( "track", m_query->track() );
QList< QPair<int, float> > albums = lib->searchTable( "album", m_query->album() );
if ( artists.length() == 0 || tracks.length() == 0 )
{

@ -385,7 +385,7 @@ DatabaseImpl::searchTable( const QString& table, const QString& name, uint limit
return resultslist;
QList< QPair<int, float> > resultscapped;
for ( unsigned int i = 0; i < limit && i < resultsmap.count(); i++ )
for ( int i = 0; i < (int)limit && i < resultsmap.count(); i++ )
{
resultscapped << resultslist.at( i );
}

@ -378,7 +378,7 @@ Servent::readyRead()
}
// they connected to us and want something we are offering
if( conntype == "accept-offer" || "push-offer" )
if ( conntype == "accept-offer" || conntype == "push-offer" )
{
sock->_msg.clear();
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << key << nodeid << "socket peer address = " << sock->peerAddress() << "socket peer name = " << sock->peerName();

@ -53,6 +53,7 @@ public:
Q_INVOKABLE void addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName );
QSharedPointer<QIODevice> customIODeviceFactory( const Tomahawk::result_ptr& result );
public slots:
QByteArray readRaw( const QString& fileName );
QString readBase64( const QString& fileName );
@ -66,7 +67,6 @@ public slots:
void addTrackResults( const QVariantMap& results );
private:
QString m_scriptPath, m_urlCallback;
QVariantMap m_resolverConfig;

@ -77,8 +77,6 @@ private slots:
void resolvingFinished( bool hasResults );
private:
SourcePlaylistInterface();
Tomahawk::source_ptr m_source;
Tomahawk::result_ptr m_currentItem;
bool m_gotNextItem;

@ -34,14 +34,12 @@ ProxyStyle::drawPrimitive( PrimitiveElement pe, const QStyleOption* opt, QPainte
{
if ( pe == PE_IndicatorBranch )
{
if ( opt->state & QStyle::State_Children )
if ( opt->state & QStyle::State_Children && !w->property( "flattenBranches" ).toBool() )
{
QRect r = opt->rect;
int hd = ( opt->rect.height() - ARROW_HEIGHT ) / 2;
int wd = ( opt->rect.width() - ARROW_WIDTH ) / 2;
r.adjust( wd, hd, 0, 0 );
QRect r = opt->rect.adjusted( wd, hd, 0, 0 );
QPointF pointsOpened[3] = { QPointF( r.x(), r.y() ), QPointF( r.x() + ARROW_WIDTH, r.y() ), QPointF( r.x() + ARROW_WIDTH / 2, r.y() + ARROW_HEIGHT ) };
QPointF pointsClosed[3] = { QPointF( r.x(), r.y() ), QPointF( r.x() + ARROW_WIDTH, r.y() + ARROW_HEIGHT / 2 ), QPointF( r.x(), r.y() + ARROW_HEIGHT ) };

@ -40,6 +40,7 @@
#include "sourcelist.h"
#include "tomahawksettings.h"
#include "customplaylistview.h"
#include "dynamic/widgets/DynamicWidget.h"
#include "widgets/welcomewidget.h"
@ -71,6 +72,7 @@ ViewManager::ViewManager( QObject* parent )
, m_widget( new QWidget() )
, m_welcomeWidget( new WelcomeWidget() )
, m_whatsHotWidget( new WhatsHotWidget() )
, m_topLovedWidget( 0 )
, m_currentMode( PlaylistInterface::Tree )
{
s_instance = this;
@ -418,6 +420,15 @@ ViewManager::showWhatsHotPage()
}
Tomahawk::ViewPage*
ViewManager::showTopLovedPage()
{
if ( !m_topLovedWidget )
m_topLovedWidget = new CustomPlaylistView( CustomPlaylistView::AllLovedTracks, source_ptr(), m_widget );
return show( m_topLovedWidget );
}
void
ViewManager::setTableMode()

@ -90,6 +90,7 @@ public:
Tomahawk::ViewPage* welcomeWidget() const { return m_welcomeWidget; }
Tomahawk::ViewPage* whatsHotWidget() const { return m_whatsHotWidget; }
Tomahawk::ViewPage* topLovedWidget() const { return m_topLovedWidget; }
ArtistView* superCollectionView() const { return m_superCollectionView; }
/// Get the view page for the given item. Not pretty...
@ -133,6 +134,7 @@ public slots:
Tomahawk::ViewPage* showSuperCollection();
Tomahawk::ViewPage* showWelcomePage();
Tomahawk::ViewPage* showWhatsHotPage();
Tomahawk::ViewPage* showTopLovedPage();
void showCurrentTrack();
// Returns the shown viewpage
@ -194,6 +196,7 @@ private:
QueueView* m_queue;
WelcomeWidget* m_welcomeWidget;
WhatsHotWidget* m_whatsHotWidget;
Tomahawk::ViewPage* m_topLovedWidget;
QList< Tomahawk::collection_ptr > m_superCollections;

@ -144,7 +144,7 @@ CheckDirModel::getCheck( const QModelIndex& index )
CheckDirTree::CheckDirTree( QWidget* parent )
: QTreeView( parent )
{
m_dirModel.setFilter( QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks );
m_dirModel.setFilter( QDir::Dirs | QDir::NoDotAndDotDot );
m_dirModel.setRootPath( "/" );
m_dirModel.setNameFilters( QStringList() << "[^\\.]*" );

@ -365,6 +365,8 @@ CategoryItem::CategoryItem( SourcesModel* model, SourceTreeItem* parent, Sources
m_addItem = new CategoryAddItem( model, this, m_category );
}
// endRowsAdded();
connect( this, SIGNAL( toggleExpandRequest( SourceTreeItem* ) ), model, SLOT( itemToggleExpandRequest( SourceTreeItem* ) ) );
}
@ -405,8 +407,5 @@ CategoryItem::peerSortValue() const
void
CategoryItem::activate()
{
if( m_category == SourcesModel::StationsCategory ) {
// TODO activate stations page
}
emit toggleExpandRequest( this );
}

@ -75,6 +75,9 @@ public:
SourcesModel::CategoryType categoryType() { return m_category; }
signals:
void toggleExpandRequest( SourceTreeItem* );
private:
SourcesModel::CategoryType m_category;
CategoryAddItem* m_addItem;

@ -0,0 +1,55 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.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 "groupitem.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include "viewmanager.h"
#include "audio/audioengine.h"
using namespace Tomahawk;
GroupItem::GroupItem( SourcesModel* model, SourceTreeItem* parent, const QString& text, int peerSortValue )
: SourceTreeItem( model, parent, SourcesModel::Group )
, m_text( text )
, m_peerSortValue( peerSortValue )
{
connect( this, SIGNAL( toggleExpandRequest( SourceTreeItem* ) ), model, SLOT( itemToggleExpandRequest( SourceTreeItem* ) ) );
}
GroupItem::~GroupItem()
{
}
void
GroupItem::activate()
{
emit toggleExpandRequest( this );
}
QString
GroupItem::text() const
{
return m_text;
}

@ -0,0 +1,53 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.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/>.
*/
#ifndef GROUP_ITEM_H
#define GROUP_ITEM_H
#include "sourcetreeitem.h"
#include "boost/function.hpp"
#include "boost/bind.hpp"
// generic item that has some name, some text, and calls a certain slot when activated. badabing!
class GroupItem : public SourceTreeItem
{
Q_OBJECT
public:
// takes 2 function pointers: show: called when wanting to show the desired view page. get: called to get the view page from ViewManager if it exists
GroupItem( SourcesModel* model, SourceTreeItem* parent, const QString& text, int peerSortValue = 0 );
virtual ~GroupItem();
virtual QString text() const;
virtual void activate();
virtual bool willAcceptDrag( const QMimeData* data ) const { Q_UNUSED( data ); return false; }
virtual QIcon icon() const { return QIcon(); }
virtual int peerSortValue() const { return m_peerSortValue; }
virtual bool isBeingPlayed() const { return false; }
signals:
void activated();
void toggleExpandRequest( SourceTreeItem* );
private:
QString m_text;
int m_peerSortValue;
};
#endif

@ -0,0 +1,96 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.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 "historyitem.h"
#include "viewmanager.h"
#include "genericpageitems.h"
#include "utils/tomahawkutilsgui.h"
#include "utils/logger.h"
#include "playlist/customplaylistview.h"
#include "temporarypageitem.h"
#include "sourcelist.h"
using namespace Tomahawk;
HistoryItem::HistoryItem( SourcesModel* model, SourceTreeItem* parent, const QString& text, int peerSortValue )
: GroupItem( model, parent, text, peerSortValue )
{
connect( this, SIGNAL( toggleExpandRequest( SourceTreeItem* ) ), model, SLOT( itemToggleExpandRequest( SourceTreeItem* ) ) );
connect( ViewManager::instance(), SIGNAL( tempPageActivated( Tomahawk::ViewPage* ) ), SLOT( tempPageActivated( Tomahawk::ViewPage* ) ) );
}
HistoryItem::~HistoryItem()
{
}
void
HistoryItem::activate()
{
emit toggleExpandRequest( this );
}
void
HistoryItem::tempPageActivated( Tomahawk::ViewPage* v )
{
const int idx = children().count();
int latest = idx;
if ( idx )
{
latest = children().last()->IDValue();
foreach ( TemporaryPageItem* page, m_tempItems )
{
if ( page->page() == v )
{
emit selectRequest( page );
return;
}
}
}
// Only keep 5 temporary pages at once
while ( m_tempItems.size() > 4 )
{
TemporaryPageItem* item = m_tempItems.takeFirst();
QTimer::singleShot( 0, item, SLOT( removeFromList() ) );
}
emit beginRowsAdded( idx, idx );
TemporaryPageItem* tempPage = new TemporaryPageItem( model(), this, v, latest + 1 );
connect( tempPage, SIGNAL( removed() ), this, SLOT( temporaryPageDestroyed() ) );
m_tempItems << tempPage;
endRowsAdded();
emit selectRequest( tempPage );
}
void
HistoryItem::temporaryPageDestroyed()
{
TemporaryPageItem* tempPage = qobject_cast< TemporaryPageItem* >( sender() );
Q_ASSERT( tempPage );
m_tempItems.removeAll( tempPage );
}

@ -0,0 +1,57 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.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/>.
*/
#ifndef HISTORY_ITEM_H
#define HISTORY_ITEM_H
#include "groupitem.h"
class TemporaryPageItem;
class GenericPageItem;
class CategoryItem;
namespace Tomahawk
{
class ViewPage;
}
class HistoryItem : public GroupItem
{
Q_OBJECT
public:
HistoryItem( SourcesModel* model, SourceTreeItem* parent, const QString& text, int peerSortValue = 0 );
virtual ~HistoryItem();
virtual void activate();
signals:
void activated();
void toggleExpandRequest( SourceTreeItem* );
private slots:
void tempPageActivated( Tomahawk::ViewPage* );
void temporaryPageDestroyed();
private:
QList< TemporaryPageItem* > m_tempItems;
private:
};
#endif

@ -25,7 +25,7 @@
#include "viewmanager.h"
#include "playlist/dynamic/GeneratorInterface.h"
#include "categoryitems.h"
#include "collectionitem.h"
#include "sourceitem.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include "dropjob.h"
@ -337,7 +337,7 @@ DynamicPlaylistItem::checkReparentHackNeeded( const DynamicPlaylistRevision& rev
CategoryItem* from = cat;
CategoryItem* to = 0;
if( cat->categoryType() == SourcesModel::PlaylistsCategory && revision.mode == OnDemand ) { // WRONG
CollectionItem* col = qobject_cast< CollectionItem* >( cat->parent() );
SourceItem* col = qobject_cast< SourceItem* >( cat->parent() );
to = col->stationsCategory();
if( !to ) { // you have got to be fucking kidding me
int fme = col->children().count();
@ -348,7 +348,7 @@ DynamicPlaylistItem::checkReparentHackNeeded( const DynamicPlaylistRevision& rev
col->setStationsCategory( to );
}
} else if( cat->categoryType() == SourcesModel::StationsCategory && revision.mode == Static ) { // WRONG
CollectionItem* col = qobject_cast< CollectionItem* >( cat->parent() );
SourceItem* col = qobject_cast< SourceItem* >( cat->parent() );
to = col->playlistsCategory();
// qDebug() << "TRYING TO HACK TO:" << to;
if( !to ) { // you have got to be fucking kidding me

@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "collectionitem.h"
#include "sourceitem.h"
#include "categoryitems.h"
#include "playlistitems.h"
@ -28,14 +28,13 @@
#include "widgets/SocialPlaylistWidget.h"
#include "playlist/customplaylistview.h"
#include "source.h"
#include "temporarypageitem.h"
#include <sourcelist.h>
#include "sourcelist.h"
/// CollectionItem
/// SourceItem
using namespace Tomahawk;
CollectionItem::CollectionItem( SourcesModel* mdl, SourceTreeItem* parent, const Tomahawk::source_ptr& source )
SourceItem::SourceItem( SourcesModel* mdl, SourceTreeItem* parent, const Tomahawk::source_ptr& source )
: SourceTreeItem( mdl, parent, SourcesModel::Collection )
, m_source( source )
, m_playlists( 0 )
@ -43,51 +42,31 @@ CollectionItem::CollectionItem( SourcesModel* mdl, SourceTreeItem* parent, cons
, m_latchedOn( false )
, m_sourceInfoItem( 0 )
, m_coolPlaylistsItem( 0 )
, m_lovedTracksItem()
, m_collectionPage( 0 )
, m_sourceInfoPage( 0 )
, m_coolPlaylistsPage( 0 )
, m_lovedTracksPage( 0 )
, m_whatsHotPage( 0 )
{
m_lovedTracksItem = new GenericPageItem( model(), this, ( m_source.isNull() ? tr( "Top Loved Tracks" ) : tr( "Loved Tracks" ) ), QIcon( RESPATH "images/loved_playlist.png" ),
boost::bind( &CollectionItem::lovedTracksClicked, this ),
boost::bind( &CollectionItem::getLovedTracksPage, this ) );
m_lovedTracksItem->setSortValue( -250 );
if ( m_source.isNull() )
{
// super collection
connect( ViewManager::instance(), SIGNAL( tempPageActivated( Tomahawk::ViewPage*) ), this, SLOT( tempPageActivated( Tomahawk::ViewPage* ) ) );
// add misc children of root node
GenericPageItem* recent = new GenericPageItem( model(), this, tr( "Dashboard" ), QIcon( RESPATH "images/dashboard.png" ),
boost::bind( &ViewManager::showWelcomePage, ViewManager::instance() ),
boost::bind( &ViewManager::welcomeWidget, ViewManager::instance() )
);
recent->setSortValue( -300 );
GenericPageItem* hot = new GenericPageItem( model(), this, tr( "Charts" ), QIcon( RESPATH "images/charts.png" ),
boost::bind( &ViewManager::showWhatsHotPage, ViewManager::instance() ),
boost::bind( &ViewManager::whatsHotWidget, ViewManager::instance() )
);
hot->setSortValue( -300 );
// TODO finish implementing and making pretty
// m_coolPlaylistsItem = new GenericPageItem( model(), this, tr( "Cool Stuff" ), QIcon( RESPATH "images/new-additions.png" ),
// boost::bind( &CollectionItem::coolPlaylistsClicked, this ),
// boost::bind( &CollectionItem::getCoolPlaylistsPage, this )
// );
// m_coolPlaylistsItem->setSortValue( 200 );
m_superCol = TomahawkUtils::createAvatarFrame( QPixmap( RESPATH "images/supercollection.png" ) );
return;
}
m_lovedTracksItem = new GenericPageItem( model(), this, tr( "Loved Tracks" ), QIcon( RESPATH "images/loved_playlist.png" ),
boost::bind( &SourceItem::lovedTracksClicked, this ),
boost::bind( &SourceItem::getLovedTracksPage, this ) );
m_lovedTracksItem->setSortValue( -250 );
m_collectionItem = new GenericPageItem( model(), this, tr( "Collection" ), QIcon( RESPATH "images/drop-song.png" ), //FIXME different icon
boost::bind( &SourceItem::collectionClicked, this ),
boost::bind( &SourceItem::getCollectionPage, this ) );
m_collectionItem->setSortValue( -350 );
m_sourceInfoItem = new GenericPageItem( model(), this, tr( "New Additions" ), QIcon( RESPATH "images/new-additions.png" ),
boost::bind( &CollectionItem::sourceInfoClicked, this ),
boost::bind( &CollectionItem::getSourceInfoPage, this ) );
boost::bind( &SourceItem::sourceInfoClicked, this ),
boost::bind( &SourceItem::getSourceInfoPage, this ) );
m_sourceInfoItem->setSortValue( -300 );
// create category items if there are playlists to show, or stations to show
@ -114,14 +93,14 @@ CollectionItem::CollectionItem( SourcesModel* mdl, SourceTreeItem* parent, cons
// load auto playlists and stations!
connect( source.data(), SIGNAL( stats( QVariantMap ) ), this, SIGNAL( updated() ) );
connect( source.data(), SIGNAL( syncedWithDatabase() ), this, SIGNAL( updated() ) );
connect( source.data(), SIGNAL( playbackStarted( Tomahawk::query_ptr ) ), this, SIGNAL( updated() ) );
connect( source.data(), SIGNAL( stateChanged() ), this, SIGNAL( updated() ) );
connect( source.data(), SIGNAL( offline() ), this, SIGNAL( updated() ) );
connect( source.data(), SIGNAL( online() ), this, SIGNAL( updated() ) );
connect( SourceList::instance(), SIGNAL( sourceLatchedOn( Tomahawk::source_ptr, Tomahawk::source_ptr ) ), this, SLOT( latchedOn( Tomahawk::source_ptr, Tomahawk::source_ptr ) ) );
connect( SourceList::instance(), SIGNAL( sourceLatchedOff( Tomahawk::source_ptr, Tomahawk::source_ptr ) ), this, SLOT( latchedOff( Tomahawk::source_ptr, Tomahawk::source_ptr ) ) );
connect( source.data(), SIGNAL( stats( QVariantMap ) ), SIGNAL( updated() ) );
connect( source.data(), SIGNAL( syncedWithDatabase() ), SIGNAL( updated() ) );
connect( source.data(), SIGNAL( playbackStarted( Tomahawk::query_ptr ) ), SIGNAL( updated() ) );
connect( source.data(), SIGNAL( stateChanged() ), SIGNAL( updated() ) );
connect( source.data(), SIGNAL( offline() ), SIGNAL( updated() ) );
connect( source.data(), SIGNAL( online() ), SIGNAL( updated() ) );
connect( SourceList::instance(), SIGNAL( sourceLatchedOn( Tomahawk::source_ptr, Tomahawk::source_ptr ) ), SLOT( latchedOn( Tomahawk::source_ptr, Tomahawk::source_ptr ) ) );
connect( SourceList::instance(), SIGNAL( sourceLatchedOff( Tomahawk::source_ptr, Tomahawk::source_ptr ) ), SLOT( latchedOff( Tomahawk::source_ptr, Tomahawk::source_ptr ) ) );
connect( source->collection().data(), SIGNAL( playlistsAdded( QList<Tomahawk::playlist_ptr> ) ),
SLOT( onPlaylistsAdded( QList<Tomahawk::playlist_ptr> ) ), Qt::QueuedConnection );
@ -131,30 +110,30 @@ CollectionItem::CollectionItem( SourcesModel* mdl, SourceTreeItem* parent, cons
SLOT( onStationsAdded( QList<Tomahawk::dynplaylist_ptr> ) ), Qt::QueuedConnection );
if ( m_source->isLocal() )
QTimer::singleShot(0, this, SLOT(requestExpanding()));
QTimer::singleShot( 0, this, SLOT( requestExpanding() ) );
}
Tomahawk::source_ptr
CollectionItem::source() const
SourceItem::source() const
{
return m_source;
}
QString
CollectionItem::text() const
SourceItem::text() const
{
return m_source.isNull() ? tr( "Super Collection" ) : m_source->friendlyName();
}
int
CollectionItem::IDValue() const
SourceItem::IDValue() const
{
if( m_source.isNull() )
if ( m_source.isNull() )
return -1;
if( m_source->isLocal() )
if ( m_source->isLocal() )
return 0;
return m_source->id();
@ -162,46 +141,46 @@ CollectionItem::IDValue() const
int
CollectionItem::peerSortValue() const
SourceItem::peerSortValue() const
{
if( m_source.isNull() )
if ( m_source.isNull() || m_source->isLocal() )
return -1;
if( m_source->isLocal() )
return 0;
return 1;
}
void
CollectionItem::activate()
SourceItem::activate()
{
ViewPage* p = 0;
if ( source().isNull() )
p = ViewManager::instance()->showSuperCollection();
else
p = ViewManager::instance()->show( source()->collection() );
emit toggleExpandRequest( this );
// p = ViewManager::instance()->show( source()->collection() );
model()->linkSourceItemToPage( this, p );
}
QIcon
CollectionItem::icon() const
SourceItem::icon() const
{
if ( m_source.isNull() )
return m_superCol;
else
{
if( m_source->avatar().isNull() )
if ( m_source->avatar().isNull() )
return m_defaultAvatar;
else
return m_source->avatar( Source::FancyStyle );
}
}
bool
CollectionItem::localLatchedOn() const
SourceItem::localLatchedOn() const
{
// Don't show a listen icon if this is the local collection and we are latched on to someone who went offline
// we are technically still latched on (if they come back online we'll be still listening along) but it's not visible
@ -216,7 +195,7 @@ CollectionItem::localLatchedOn() const
void
CollectionItem::latchedOff( const source_ptr& from, const source_ptr& to )
SourceItem::latchedOff( const source_ptr& from, const source_ptr& to )
{
if ( from->isLocal() && ( m_source == to || m_source == from ) )
{
@ -226,8 +205,9 @@ CollectionItem::latchedOff( const source_ptr& from, const source_ptr& to )
}
}
void
CollectionItem::latchedOn( const source_ptr& from, const source_ptr& to )
SourceItem::latchedOn( const source_ptr& from, const source_ptr& to )
{
if ( from->isLocal() && ( m_source == to || m_source == from ) )
{
@ -239,29 +219,32 @@ CollectionItem::latchedOn( const source_ptr& from, const source_ptr& to )
void
CollectionItem::playlistsAddedInternal( SourceTreeItem* parent, const QList< dynplaylist_ptr >& playlists )
SourceItem::playlistsAddedInternal( SourceTreeItem* parent, const QList< dynplaylist_ptr >& playlists )
{
QList< SourceTreeItem* > items;
int addOffset = playlists.first()->author()->isLocal() ? 1 : 0;
int from = parent->children().count() - addOffset;
parent->beginRowsAdded( from, from + playlists.count() - 1 );
foreach( const dynplaylist_ptr& p, playlists )
foreach ( const dynplaylist_ptr& p, playlists )
{
DynamicPlaylistItem* plItem = new DynamicPlaylistItem( model(), parent, p, parent->children().count() - addOffset );
// qDebug() << "Dynamic Playlist added:" << p->title() << p->creator() << p->info();
p->loadRevision();
items << plItem;
if( p->mode() == Static ) {
if( m_source->isLocal() )
if ( p->mode() == Static )
{
if ( m_source->isLocal() )
connect( p.data(), SIGNAL( aboutToBeDeleted( Tomahawk::dynplaylist_ptr ) ),
SLOT( onAutoPlaylistDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
else
connect( p.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ),
SLOT( onAutoPlaylistDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
} else {
if( m_source->isLocal() )
}
else
{
if ( m_source->isLocal() )
connect( p.data(), SIGNAL( aboutToBeDeleted( Tomahawk::dynplaylist_ptr ) ),
SLOT( onStationDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
else
@ -275,7 +258,7 @@ CollectionItem::playlistsAddedInternal( SourceTreeItem* parent, const QList< dyn
template< typename T >
void
CollectionItem::playlistDeletedInternal( SourceTreeItem* parent, const T& p )
SourceItem::playlistDeletedInternal( SourceTreeItem* parent, const T& p )
{
Q_ASSERT( parent ); // How can we delete playlists if we have none?
int curCount = parent->children().count();
@ -314,7 +297,7 @@ CollectionItem::playlistDeletedInternal( SourceTreeItem* parent, const T& p )
void
CollectionItem::onPlaylistsAdded( const QList< playlist_ptr >& playlists )
SourceItem::onPlaylistsAdded( const QList< playlist_ptr >& playlists )
{
// qDebug() << Q_FUNC_INFO << m_source->friendlyName() << playlists.count();
@ -355,14 +338,14 @@ CollectionItem::onPlaylistsAdded( const QList< playlist_ptr >& playlists )
void
CollectionItem::onPlaylistDeleted( const playlist_ptr& playlist )
SourceItem::onPlaylistDeleted( const playlist_ptr& playlist )
{
playlistDeletedInternal( m_playlists, playlist );
}
void
CollectionItem::onAutoPlaylistsAdded( const QList< dynplaylist_ptr >& playlists )
SourceItem::onAutoPlaylistsAdded( const QList< dynplaylist_ptr >& playlists )
{
if( playlists.isEmpty() )
return;
@ -381,7 +364,7 @@ CollectionItem::onAutoPlaylistsAdded( const QList< dynplaylist_ptr >& playlists
void
CollectionItem::onAutoPlaylistDeleted( const dynplaylist_ptr& playlist )
SourceItem::onAutoPlaylistDeleted( const dynplaylist_ptr& playlist )
{
if( !m_playlists )
qDebug() << "NO playlist category item for a deleting playlist..";
@ -391,7 +374,7 @@ CollectionItem::onAutoPlaylistDeleted( const dynplaylist_ptr& playlist )
void
CollectionItem::onStationsAdded( const QList< dynplaylist_ptr >& stations )
SourceItem::onStationsAdded( const QList< dynplaylist_ptr >& stations )
{
if( stations.isEmpty() )
return;
@ -410,59 +393,21 @@ CollectionItem::onStationsAdded( const QList< dynplaylist_ptr >& stations )
void
CollectionItem::onStationDeleted( const dynplaylist_ptr& station )
SourceItem::onStationDeleted( const dynplaylist_ptr& station )
{
playlistDeletedInternal( m_stations, station );
}
void
CollectionItem::requestExpanding()
SourceItem::requestExpanding()
{
emit expandRequest(this);
}
void
CollectionItem::tempPageActivated( Tomahawk::ViewPage* v )
{
const int idx = children().count();
const int latest = children().last()->IDValue();
foreach ( TemporaryPageItem* page, m_tempItems )
{
if ( page->page() == v )
{
emit selectRequest( page );
return;
}
}
// Only keep 5 temporary pages at once
while ( m_tempItems.size() > 4 )
{
TemporaryPageItem* item = m_tempItems.takeFirst();
QTimer::singleShot( 0, item, SLOT( removeFromList() ) );
}
emit beginRowsAdded( idx, idx );
TemporaryPageItem* tempPage = new TemporaryPageItem( model(), this, v, latest + 1 );
connect( tempPage, SIGNAL( removed() ), this, SLOT( temporaryPageDestroyed() ) );
m_tempItems << tempPage;
endRowsAdded();
emit selectRequest( tempPage );
}
void
CollectionItem::temporaryPageDestroyed()
{
TemporaryPageItem* tempPage = qobject_cast< TemporaryPageItem* >( sender() );
Q_ASSERT( tempPage );
m_tempItems.removeAll( tempPage );
emit expandRequest( this );
}
ViewPage*
CollectionItem::sourceInfoClicked()
SourceItem::sourceInfoClicked()
{
if( m_source.isNull() )
return 0;
@ -473,19 +418,37 @@ CollectionItem::sourceInfoClicked()
ViewPage*
CollectionItem::getSourceInfoPage() const
SourceItem::getSourceInfoPage() const
{
return m_sourceInfoPage;
}
ViewPage*
CollectionItem::coolPlaylistsClicked()
SourceItem::collectionClicked()
{
if( !m_source.isNull() )
if( m_source.isNull() )
return 0;
if( !m_coolPlaylistsPage )
m_collectionPage = ViewManager::instance()->show( m_source->collection() );
return m_collectionPage;
}
ViewPage*
SourceItem::getCollectionPage() const
{
return m_collectionPage;;
}
ViewPage*
SourceItem::coolPlaylistsClicked()
{
if ( !m_source.isNull() )
return 0;
if ( !m_coolPlaylistsPage )
m_coolPlaylistsPage = new SocialPlaylistWidget( ViewManager::instance()->widget() );
ViewManager::instance()->show( m_coolPlaylistsPage );
@ -494,16 +457,16 @@ CollectionItem::coolPlaylistsClicked()
ViewPage*
CollectionItem::getCoolPlaylistsPage() const
SourceItem::getCoolPlaylistsPage() const
{
return m_coolPlaylistsPage;
}
ViewPage*
CollectionItem::lovedTracksClicked()
SourceItem::lovedTracksClicked()
{
if( !m_lovedTracksPage )
if ( !m_lovedTracksPage )
m_lovedTracksPage = new CustomPlaylistView( m_source.isNull() ? CustomPlaylistView::AllLovedTracks : CustomPlaylistView::SourceLovedTracks, m_source, ViewManager::instance()->widget() );
ViewManager::instance()->show( m_lovedTracksPage );
@ -512,7 +475,7 @@ CollectionItem::lovedTracksClicked()
ViewPage*
CollectionItem::getLovedTracksPage() const
SourceItem::getLovedTracksPage() const
{
return m_lovedTracksPage;
}

@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef COLLECTION_ITEM_H
#define COLLECTION_ITEM_H
#ifndef SOURCE_ITEM_H
#define SOURCE_ITEM_H
#include "sourcetreeitem.h"
@ -30,11 +30,11 @@ namespace Tomahawk
class ViewPage;
}
class CollectionItem : public SourceTreeItem
class SourceItem : public SourceTreeItem
{
Q_OBJECT
public:
CollectionItem( SourcesModel* model, SourceTreeItem* parent, const Tomahawk::source_ptr& source );
SourceItem( SourcesModel* model, SourceTreeItem* parent, const Tomahawk::source_ptr& source );
virtual QString text() const;
virtual void activate();
@ -64,12 +64,12 @@ private slots:
void requestExpanding();
void tempPageActivated( Tomahawk::ViewPage* );
void temporaryPageDestroyed();
Tomahawk::ViewPage* sourceInfoClicked();
Tomahawk::ViewPage* getSourceInfoPage() const;
Tomahawk::ViewPage* collectionClicked();
Tomahawk::ViewPage* getCollectionPage() const;
Tomahawk::ViewPage* coolPlaylistsClicked();
Tomahawk::ViewPage* getCoolPlaylistsPage() const;
@ -89,11 +89,12 @@ private:
bool m_latchedOn;
Tomahawk::source_ptr m_latchedOnTo;
QList< TemporaryPageItem* > m_tempItems;
GenericPageItem* m_collectionItem;
GenericPageItem* m_sourceInfoItem;
GenericPageItem* m_coolPlaylistsItem;
GenericPageItem* m_lovedTracksItem;
Tomahawk::ViewPage* m_collectionPage;
Tomahawk::ViewPage* m_sourceInfoPage;
Tomahawk::ViewPage* m_coolPlaylistsPage;
Tomahawk::ViewPage* m_lovedTracksPage;

@ -29,18 +29,20 @@ SourceTreeItem::SourceTreeItem( SourcesModel* model, SourceTreeItem* parent, Sou
, m_parent( parent )
, m_model( model )
{
connect( this, SIGNAL( beginChildRowsAdded( int,int ) ), m_model, SLOT( onItemRowsAddedBegin( int,int ) ) );
connect( this, SIGNAL( beginChildRowsRemoved( int,int ) ), m_model, SLOT( onItemRowsRemovedBegin( int,int ) ) );
connect( this, SIGNAL( beginChildRowsAdded( int, int ) ), m_model, SLOT( onItemRowsAddedBegin( int, int ) ) );
connect( this, SIGNAL( beginChildRowsRemoved( int, int ) ), m_model, SLOT( onItemRowsRemovedBegin( int, int ) ) );
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* ) ) );
connect( this, SIGNAL( expandRequest( SourceTreeItem* ) ), m_model, SLOT( itemExpandRequest( SourceTreeItem* ) ) );
if( !m_parent )
connect( this, SIGNAL( toggleExpandRequest( SourceTreeItem* ) ), m_model, SLOT( itemToggleExpandRequest( SourceTreeItem* ) ) );
if ( !m_parent )
return;
// caller must call begin/endInsertRows
if( index < 0 )
if ( index < 0 )
m_parent->appendChild( this );
else
m_parent->insertChild( index, this );

@ -25,6 +25,7 @@
#include "sourcesmodel.h"
class QMimeData;
class SourceTreeItem : public QObject
{
Q_OBJECT
@ -80,6 +81,7 @@ signals:
void updated();
void selectRequest( SourceTreeItem* );
void expandRequest( SourceTreeItem* );
void toggleExpandRequest( SourceTreeItem* );
void beginChildRowsAdded( int fromRow, int toRow );
void childRowsAdded();
@ -90,6 +92,7 @@ signals:
protected:
void setRowType( SourcesModel::RowType t ) { m_type = t; }
void setParentItem( SourceTreeItem* item ) { m_parent = item; }
private:
SourcesModel::RowType m_type;

@ -21,7 +21,7 @@
#include "sourcedelegate.h"
#include "items/sourcetreeitem.h"
#include "items/collectionitem.h"
#include "items/sourceitem.h"
#include "items/playlistitems.h"
#include "items/categoryitems.h"
#include "items/temporarypageitem.h"
@ -37,7 +37,7 @@
#include <audio/audioengine.h>
#include <actioncollection.h>
#define TREEVIEW_INDENT_ADD -7
#define TREEVIEW_INDENT_ADD 12
SourceDelegate::SourceDelegate( QAbstractItemView* parent )
@ -81,17 +81,21 @@ QSize
SourceDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
SourceTreeItem *item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >();
SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() );
if ( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() == SourcesModel::Collection )
if ( type == SourcesModel::Collection )
{
return QSize( option.rect.width(), 44 );
return QSize( option.rect.width(), 40 );
}
else if ( type == SourcesModel::Divider )
{
return QSize( option.rect.width(), 6 );
}
else if ( m_expandedMap.contains( index ) )
{
if ( !m_expandedMap.value( index )->initialized() )
{
int dropTypes = dropTypeCount( item );
qDebug() << "droptypecount is " << dropTypes;
QSize originalSize = QStyledItemDelegate::sizeHint( option, index );
QSize targetSize = originalSize + QSize( 0, dropTypes == 0 ? 0 : 56 );
m_expandedMap.value( index )->initialize( originalSize, targetSize, 300 );
@ -105,166 +109,286 @@ SourceDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex&
}
void
SourceDelegate::paintDecorations( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() );
SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >();
// Paint the speaker icon next to the currently-playing playlist
const bool playable = ( type == SourcesModel::StaticPlaylist ||
type == SourcesModel::AutomaticPlaylist ||
type == SourcesModel::Station ||
type == SourcesModel::TemporaryPage ||
type == SourcesModel::GenericPage );
const bool playing = ( AudioEngine::instance()->isPlaying() || AudioEngine::instance()->isPaused() );
if ( playable && playing && item->isBeingPlayed() )
{
const int iconW = option.rect.height() - 4;
QRect iconRect = QRect( option.rect.x() - iconW - 4, option.rect.y() + 2, iconW, iconW );
QPixmap speaker = option.state & QStyle::State_Selected ? m_nowPlayingSpeaker : m_nowPlayingSpeakerDark;
speaker = speaker.scaledToHeight( iconW, Qt::SmoothTransformation );
painter->drawPixmap( iconRect, speaker );
}
}
void
SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QFont normal = painter->font();
QFont bold = painter->font();
bold.setBold( true );
SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >();
SourceItem* colItem = qobject_cast< SourceItem* >( item );
Q_ASSERT( colItem );
bool status = !( !colItem || colItem->source().isNull() || !colItem->source()->isOnline() );
QString tracks;
QString name = index.data().toString();
int figWidth = 0;
if ( status && colItem && !colItem->source().isNull() )
{
tracks = QString::number( colItem->source()->trackCount() );
figWidth = painter->fontMetrics().width( tracks );
name = colItem->source()->friendlyName();
}
QRect iconRect = option.rect.adjusted( 4, 6, -option.rect.width() + option.rect.height() - 12 + 4, -6 );
QPixmap avatar = colItem->icon().pixmap( iconRect.size() );
painter->drawPixmap( iconRect, avatar.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ) );
if ( ( option.state & QStyle::State_Selected ) == QStyle::State_Selected )
{
painter->setPen( option.palette.color( QPalette::HighlightedText ) );
}
QRect textRect = option.rect.adjusted( iconRect.width() + 8, 6, -figWidth - 24, 0 );
if ( status || colItem->source().isNull() )
painter->setFont( bold );
QString text = painter->fontMetrics().elidedText( name, Qt::ElideRight, textRect.width() );
painter->drawText( textRect, text );
bool isPlaying = false;
QString desc = status ? colItem->source()->textStatus() : tr( "Offline" );
if ( colItem->source().isNull() )
desc = tr( "All available tracks" );
if ( status && desc.isEmpty() && !colItem->source()->currentTrack().isNull() )
{
desc = colItem->source()->currentTrack()->artist() + " - " + colItem->source()->currentTrack()->track();
isPlaying = true;
}
if ( desc.isEmpty() )
desc = tr( "Online" );
textRect = option.rect.adjusted( iconRect.width() + 8, painter->fontMetrics().height() + 9, -figWidth - 24, -6 );
painter->setFont( normal );
bool privacyOn = TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::FullyPrivate;
if ( !colItem->source().isNull() && colItem->source()->isLocal() && privacyOn )
{
QRect pmRect = textRect;
pmRect.setTop( pmRect.bottom() - painter->fontMetrics().height() + 3 );
pmRect.setRight( pmRect.left() + pmRect.height() );
ActionCollection::instance()->getAction( "togglePrivacy" )->icon().paint( painter, pmRect );
textRect.adjust( pmRect.width() + 3, 0, 0, 0 );
}
if ( isPlaying || ( !colItem->source().isNull() && colItem->source()->isLocal() ) )
{
// Show a listen icon
QPixmap pm;
if ( index.data( SourcesModel::LatchedOnRole ).toBool() )
{
// Currently listening along
pm = m_headphonesOn;
}
else if ( !colItem->source()->isLocal() )
{
pm = m_headphonesOff;
}
if ( !pm.isNull() )
{
QRect pmRect = textRect;
pmRect.setTop( pmRect.bottom() - painter->fontMetrics().height() + 3 );
pmRect.setRight( pmRect.left() + pmRect.height() );
painter->drawPixmap( pmRect, pm.scaledToHeight( pmRect.height(), Qt::SmoothTransformation ) );
textRect.adjust( pmRect.width() + 3, 0, 0, 0 );
}
}
text = painter->fontMetrics().elidedText( desc, Qt::ElideRight, textRect.width() );
QTextOption to( Qt::AlignBottom );
painter->drawText( textRect, text, to );
if ( status )
{
painter->setRenderHint( QPainter::Antialiasing );
QRect figRect = option.rect.adjusted( option.rect.width() - figWidth - 8, 0, -13, -option.rect.height() + 16 );
int hd = ( option.rect.height() - figRect.height() ) / 2;
figRect.adjust( 0, hd, 0, hd );
#ifdef Q_WS_WIN
figRect.adjust( -3, 0, 3, 0 );
#endif
painter->setFont( bold );
QColor figColor( 167, 183, 211 );
painter->setPen( figColor );
painter->setBrush( figColor );
TomahawkUtils::drawBackgroundAndNumbers( painter, tracks, figRect );
}
}
void
SourceDelegate::paintCategory( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QTextOption to( Qt::AlignVCenter );
painter->setPen( option.palette.color( QPalette::Base ) );
painter->setBrush( option.palette.color( QPalette::Base ) );
painter->drawRect( option.rect );
painter->setRenderHint( QPainter::Antialiasing );
painter->setPen( Qt::white );
painter->drawText( option.rect.translated( 4, 1 ), index.data().toString().toUpper(), to );
painter->setPen( QColor( 99, 113, 128 ) );
painter->drawText( option.rect.translated( 4, 0 ), index.data().toString().toUpper(), to );
if ( option.state & QStyle::State_MouseOver )
{
QString text = tr( "Show" );
if ( option.state & QStyle::State_Open )
text = tr( "Hide" );
QFont font = painter->font();
font.setPixelSize( 11 );
font.setBold( true );
painter->setFont( font );
QTextOption to( Qt::AlignVCenter | Qt::AlignRight );
// draw close icon
painter->setPen( Qt::white );
painter->drawText( option.rect.translated( -4, 1 ), text, to );
painter->setPen( QColor( 99, 113, 128 ) );
painter->drawText( option.rect.translated( -4, 0 ), text, to );
}
}
void
SourceDelegate::paintGroup( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QFont font = painter->font();
font.setPixelSize( 12 );
font.setBold( true );
painter->setFont( font );
QTextOption to( Qt::AlignVCenter );
painter->setPen( option.palette.color( QPalette::Base ) );
painter->setBrush( option.palette.color( QPalette::Base ) );
painter->drawRect( option.rect );
painter->setRenderHint( QPainter::Antialiasing );
painter->setPen( Qt::white );
painter->drawText( option.rect.translated( 4, 1 ), index.data().toString().toUpper(), to );
painter->setPen( QColor( 99, 113, 128 ) );
painter->drawText( option.rect.translated( 4, 0 ), index.data().toString().toUpper(), to );
if ( option.state & QStyle::State_MouseOver )
{
QString text = tr( "Show" );
if ( option.state & QStyle::State_Open )
text = tr( "Hide" );
font.setPixelSize( font.pixelSize() - 1 );
painter->setFont( font );
QTextOption to( Qt::AlignVCenter | Qt::AlignRight );
// draw close icon
painter->setPen( Qt::white );
painter->drawText( option.rect.translated( -4, 1 ), text, to );
painter->setPen( QColor( 99, 113, 128 ) );
painter->drawText( option.rect.translated( -4, 0 ), text, to );
}
}
void
SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QStyleOptionViewItem o = option;
QStyleOptionViewItemV4 o3 = option;
#ifdef Q_WS_MAC
QFont savedFont = painter->font();
QFont smaller = savedFont;
smaller.setPointSize( smaller.pointSize() - 2 );
painter->setFont( smaller );
o.font = smaller;
#endif
painter->save();
QFont font = painter->font();
font.setPixelSize( 11 );
painter->setFont( font );
o.font = font;
o3.font = font;
SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() );
SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >();
Q_ASSERT( item );
if ( ( option.state & QStyle::State_Enabled ) == QStyle::State_Enabled )
{
o.state = QStyle::State_Enabled;
if ( ( option.state & QStyle::State_MouseOver ) == QStyle::State_MouseOver )
{
o.state |= QStyle::State_MouseOver;
o3.state |= QStyle::State_MouseOver;
}
if ( ( option.state & QStyle::State_Open ) == QStyle::State_Open )
{
o.state |= QStyle::State_Open;
}
if ( ( option.state & QStyle::State_Selected ) == QStyle::State_Selected )
{
o3.state |= QStyle::State_Selected;
if ( type != SourcesModel::Collection )
o3.state |= QStyle::State_Selected;
else
o3.state &= ~QStyle::State_Selected;
o.palette.setColor( QPalette::Base, QColor( 0, 0, 0, 0 ) );
o.palette.setColor( QPalette::Text, o.palette.color( QPalette::HighlightedText ) );
o3.palette.setColor( QPalette::Text, o.palette.color( QPalette::HighlightedText ) );
}
}
SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() );
SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >();
Q_ASSERT( item );
if ( type != SourcesModel::Collection && type != SourcesModel::Category )
o3.rect.setX( 0 );
QApplication::style()->drawControl( QStyle::CE_ItemViewItem, &o3, painter );
// Paint the speaker icon next to the currently-playing playlist
const bool playable = ( type == SourcesModel::StaticPlaylist ||
type == SourcesModel::AutomaticPlaylist ||
type == SourcesModel::Station ||
type == SourcesModel::TemporaryPage ||
type == SourcesModel::GenericPage );
const bool playing = ( AudioEngine::instance()->isPlaying() || AudioEngine::instance()->isPaused() );
if ( playable && playing && item->isBeingPlayed() )
// shrink the indentations
{
const int iconW = o3.rect.height() - 4;
QRect iconRect = QRect( option.rect.x() - iconW - 4, option.rect.y() + 2, iconW, iconW );
QPixmap speaker = o3.state & QStyle::State_Selected ? m_nowPlayingSpeaker : m_nowPlayingSpeakerDark;
speaker = speaker.scaledToHeight( iconW, Qt::SmoothTransformation );
painter->drawPixmap( iconRect, speaker );
int indentMult = 0;
QModelIndex counter = index;
while ( counter.parent().isValid() )
{
indentMult++;
counter = counter.parent();
}
int indentDelta = o.rect.x() - m_parent->viewport()->x();
o.rect.setX( o.rect.x() - indentDelta + indentMult * TREEVIEW_INDENT_ADD );
o3.rect.setX( 0 );
}
if ( type != SourcesModel::Group && type != SourcesModel::Category && type != SourcesModel::Divider )
QApplication::style()->drawControl( QStyle::CE_ItemViewItem, &o3, painter );
paintDecorations( painter, o3, index );
if ( type == SourcesModel::Collection )
{
painter->save();
QFont normal = painter->font();
QFont bold = painter->font();
bold.setBold( true );
CollectionItem* colItem = qobject_cast< CollectionItem* >( item );
Q_ASSERT( colItem );
bool status = !( !colItem || colItem->source().isNull() || !colItem->source()->isOnline() );
QString tracks;
QString name = index.data().toString();
int figWidth = 0;
if ( status && colItem && !colItem->source().isNull() )
{
tracks = QString::number( colItem->source()->trackCount() );
figWidth = painter->fontMetrics().width( tracks );
name = colItem->source()->friendlyName();
}
QRect iconRect = option.rect.adjusted( 4, 6, -option.rect.width() + option.rect.height() - 12 + 4, -6 );
QPixmap avatar = colItem->icon().pixmap( iconRect.size() );
painter->drawPixmap( iconRect, avatar.scaledToHeight( iconRect.height(), Qt::SmoothTransformation ) );
if ( ( option.state & QStyle::State_Selected ) == QStyle::State_Selected )
{
painter->setPen( o.palette.color( QPalette::HighlightedText ) );
}
QRect textRect = option.rect.adjusted( iconRect.width() + 8, 6, -figWidth - 24, 0 );
if ( status || colItem->source().isNull() )
painter->setFont( bold );
QString text = painter->fontMetrics().elidedText( name, Qt::ElideRight, textRect.width() );
painter->drawText( textRect, text );
bool isPlaying = false;
QString desc = status ? colItem->source()->textStatus() : tr( "Offline" );
if ( colItem->source().isNull() )
desc = tr( "All available tracks" );
if ( status && desc.isEmpty() && !colItem->source()->currentTrack().isNull() )
{
desc = colItem->source()->currentTrack()->artist() + " - " + colItem->source()->currentTrack()->track();
isPlaying = true;
}
if ( desc.isEmpty() )
desc = tr( "Online" );
textRect = option.rect.adjusted( iconRect.width() + 8, painter->fontMetrics().height() + 6, -figWidth - 24, -4 );
painter->setFont( normal );
bool privacyOn = TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::FullyPrivate;
if ( !colItem->source().isNull() && colItem->source()->isLocal() && privacyOn )
{
QRect pmRect = textRect;
pmRect.setTop( pmRect.bottom() - painter->fontMetrics().height() + 3 );
pmRect.setRight( pmRect.left() + pmRect.height() );
ActionCollection::instance()->getAction( "togglePrivacy" )->icon().paint( painter, pmRect );
textRect.adjust( pmRect.width() + 3, 0, 0, 0 );
}
if ( isPlaying || ( !colItem->source().isNull() && colItem->source()->isLocal() ) )
{
// Show a listen icon
QPixmap pm;
if ( index.data( SourcesModel::LatchedOnRole ).toBool() )
{
// Currently listening along
pm = m_headphonesOn;
} else if ( !colItem->source()->isLocal() ) {
pm = m_headphonesOff;
}
if ( !pm.isNull() )
{
QRect pmRect = textRect;
pmRect.setTop( pmRect.bottom() - painter->fontMetrics().height() + 3 );
pmRect.setRight( pmRect.left() + pmRect.height() );
// tDebug() << "DOING HEADPHONES RECT:" << pmRect;
painter->drawPixmap( pmRect, pm.scaledToHeight( pmRect.height(), Qt::SmoothTransformation ) );
textRect.adjust( pmRect.width() + 3, 0, 0, 0 );
}
}
text = painter->fontMetrics().elidedText( desc, Qt::ElideRight, textRect.width() );
QTextOption to( Qt::AlignBottom );
painter->drawText( textRect, text, to );
if ( status )
{
painter->setRenderHint( QPainter::Antialiasing );
QRect figRect = o.rect.adjusted( o.rect.width() - figWidth - 8, 0, -13, -o.rect.height() + 16 );
int hd = ( option.rect.height() - figRect.height() ) / 2;
figRect.adjust( 0, hd, 0, hd );
#ifdef Q_WS_WIN
figRect.adjust( -3, 0, 3, 0 );
#endif
painter->setFont( bold );
QColor figColor( 167, 183, 211 );
painter->setPen( figColor );
painter->setBrush( figColor );
TomahawkUtils::drawBackgroundAndNumbers( painter, tracks, figRect );
}
painter->restore();
paintCollection( painter, o, index );
}
else if ( ( type == SourcesModel::StaticPlaylist || type == SourcesModel::CategoryAdd ) &&
m_expandedMap.contains( index ) && m_expandedMap.value( index )->partlyExpanded() && dropTypeCount( item ) > 0 )
@ -275,8 +399,6 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co
o.rect.adjust( 0, 0, 0, - option.rect.height() + m_expandedMap.value( index )->originalSize().height() );
QStyledItemDelegate::paint( painter, o, index );
painter->save();
// Get whole rect for the menu
QRect itemsRect = option.rect.adjusted( -option.rect.x(), m_expandedMap.value( index )->originalSize().height(), 0, 0 );
QPoint cursorPos = m_parent->mapFromGlobal( QCursor::pos() );
@ -287,9 +409,6 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co
{
m_gradient = QLinearGradient( itemsRect.topLeft(), itemsRect.bottomLeft() );
m_gradient.setColorAt( 0.0, Qt::white );
// m_gradient.setColorAt( 0.8, QColor( 0xd6, 0xd6, 0xd6 ) ); // light grey
// m_gradient.setColorAt( 1.0, QColor( 0xf4, 0x17, 0x05 ) ); // dark red
// m_gradient.setColorAt( 1.0, QColor( 0xb1, 0xb1, 0xb1 ) ); // not so light but still not dark grey
m_gradient.setColorAt( 0.9, QColor( 0x88, 0x88, 0x88 ) );
m_gradient.setColorAt( 1.0, QColor( 0x99, 0x99, 0x99 ) ); // dark grey
}
@ -352,11 +471,30 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co
count++;
}
}
else if ( type == SourcesModel::Group )
{
paintGroup( painter, o3, index );
}
else if ( type == SourcesModel::Category )
{
paintCategory( painter, o, index );
}
else if ( type == SourcesModel::Divider )
{
QRect middle = o.rect.adjusted( 0, 2, 0, -2 );
painter->setRenderHint( QPainter::Antialiasing, false );
painter->restore();
QColor bgcolor = o3.palette.color( QPalette::Base );
painter->setPen( bgcolor.darker( 120 ) );
painter->drawLine( middle.topLeft(), middle.topRight() );
painter->setPen( bgcolor.lighter( 120 ) );
painter->drawLine( middle.bottomLeft(), middle.bottomRight() );
}
else
{
o.state &= ~QStyle::State_MouseOver;
QStyledItemDelegate::paint( painter, o, index );
if ( type == SourcesModel::TemporaryPage )
@ -376,29 +514,9 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co
painter->drawPixmap( r, p );
}
}
/*QStyleOptionViewItemV4 opt = o;
initStyleOption( &opt, index );
// shrink the indentations. count how indented this item is and remove it
int indentMult = 0;
QModelIndex counter = index;
while ( counter.parent().isValid() )
{
indentMult++;
counter = counter.parent();
}
int realX = opt.rect.x() + indentMult * TREEVIEW_INDENT_ADD;
opt.rect.setX( realX );
const QWidget *widget = opt.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawControl( QStyle::CE_ItemViewItem, &opt, painter, widget ); */
}
#ifdef Q_WS_MAC
painter->setFont( savedFont );
#endif
painter->restore();
}
@ -415,7 +533,7 @@ SourceDelegate::updateEditorGeometry( QWidget* editor, const QStyleOptionViewIte
bool
SourceDelegate::editorEvent ( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
SourceDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
{
if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonPress )
{
@ -447,7 +565,7 @@ SourceDelegate::editorEvent ( QEvent* event, QAbstractItemModel* model, const QS
}
else if ( type == SourcesModel::Collection )
{
CollectionItem* colItem = qobject_cast< CollectionItem* >( index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >() );
SourceItem* colItem = qobject_cast< SourceItem* >( index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >() );
Q_ASSERT( colItem );
if ( !colItem->source().isNull() && !colItem->source()->currentTrack().isNull() && !colItem->source()->isLocal() )
@ -460,7 +578,6 @@ SourceDelegate::editorEvent ( QEvent* event, QAbstractItemModel* model, const QS
const int height = fm.height() + 3;
QRect headphonesRect( option.rect.height() + 10, o.rect.bottom() - height, height, height );
// tDebug() << "CHECKING CLICK RECT:" << headphonesRect;
if ( headphonesRect.contains( ev->pos() ) )
{
if ( event->type() == QEvent::MouseButtonRelease )

@ -56,6 +56,12 @@ private slots:
void animationFinished( const QModelIndex& );
private:
void paintDecorations( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
void paintCollection( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
void paintCategory( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
void paintGroup( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
QAbstractItemView* m_parent;
mutable int m_iconHeight;
QModelIndex m_dropHoverIndex;

@ -25,8 +25,10 @@
#include <QSize>
#include "sourcetree/items/sourcetreeitem.h"
#include "sourcetree/items/collectionitem.h"
#include "sourcetree/items/sourceitem.h"
#include "sourcetree/items/groupitem.h"
#include "sourcetree/items/genericpageitems.h"
#include "sourcetree/items/historyitem.h"
#include "sourcelist.h"
#include "playlist.h"
#include "collection.h"
@ -50,6 +52,7 @@ SourcesModel::SourcesModel( QObject* parent )
{
m_rootItem = new SourceTreeItem( this, 0, Invalid );
appendGroups();
appendItem( source_ptr() );
onSourcesAdded( SourceList::instance()->sources() );
@ -71,6 +74,9 @@ SourcesModel::rowTypeToString( RowType type )
{
switch ( type )
{
case Group:
return tr( "Group" );
case Collection:
return tr( "Collection" );
@ -92,35 +98,35 @@ SourcesModel::rowTypeToString( RowType type )
QVariant
SourcesModel::data( const QModelIndex& index, int role ) const
{
if( !index.isValid() )
if ( !index.isValid() )
return QVariant();
switch( role )
switch ( role )
{
case Qt::SizeHintRole:
return QSize( 0, 18 );
case SourceTreeItemRole:
return QVariant::fromValue< SourceTreeItem* >( itemFromIndex( index ) );
case SourceTreeItemTypeRole:
return itemFromIndex( index )->type();
case Qt::DisplayRole:
case Qt::EditRole:
return itemFromIndex( index )->text();
case Qt::DecorationRole:
return itemFromIndex( index )->icon();
case SourcesModel::SortRole:
return itemFromIndex( index )->peerSortValue();
case SourcesModel::IDRole:
return itemFromIndex( index )->IDValue();
case SourcesModel::LatchedOnRole:
{
if ( itemFromIndex( index )->type() == Collection )
case Qt::SizeHintRole:
return QSize( 0, 18 );
case SourceTreeItemRole:
return QVariant::fromValue< SourceTreeItem* >( itemFromIndex( index ) );
case SourceTreeItemTypeRole:
return itemFromIndex( index )->type();
case Qt::DisplayRole:
case Qt::EditRole:
return itemFromIndex( index )->text();
case Qt::DecorationRole:
return itemFromIndex( index )->icon();
case SourcesModel::SortRole:
return itemFromIndex( index )->peerSortValue();
case SourcesModel::IDRole:
return itemFromIndex( index )->IDValue();
case SourcesModel::LatchedOnRole:
{
CollectionItem* cItem = qobject_cast< CollectionItem* >( itemFromIndex( index ) );
return cItem->localLatchedOn();
if ( itemFromIndex( index )->type() == Collection )
{
SourceItem* cItem = qobject_cast< SourceItem* >( itemFromIndex( index ) );
return cItem->localLatchedOn();
}
return false;
}
return false;
}
}
return QVariant();
}
@ -236,21 +242,63 @@ SourcesModel::supportedDropActions() const
Qt::ItemFlags
SourcesModel::flags( const QModelIndex& index ) const
{
if ( index.isValid() ) {
if ( index.isValid() )
{
return itemFromIndex( index )->flags();
} else {
return 0;
}
else
return 0;
}
void
SourcesModel::appendGroups()
{
beginInsertRows( QModelIndex(), rowCount(), rowCount() + 2 );
SourceTreeItem* divider = new SourceTreeItem( this, m_rootItem, SourcesModel::Divider, 0 );
HistoryItem* history = new HistoryItem( this, m_rootItem, tr( "History" ), 5 );
GroupItem* browse = new GroupItem( this, m_rootItem, tr( "Browse" ), 10 );
// super collection
GenericPageItem* loved = new GenericPageItem( this, browse, tr( "Top Loved Tracks" ), QIcon( RESPATH "images/loved_playlist.png" ),
boost::bind( &ViewManager::showTopLovedPage, ViewManager::instance() ),
boost::bind( &ViewManager::topLovedWidget, ViewManager::instance() ) );
loved->setSortValue( -250 );
// add misc children of root node
GenericPageItem* recent = new GenericPageItem( this, browse, tr( "Dashboard" ), QIcon( RESPATH "images/dashboard.png" ),
boost::bind( &ViewManager::showWelcomePage, ViewManager::instance() ),
boost::bind( &ViewManager::welcomeWidget, ViewManager::instance() ) );
recent->setSortValue( -300 );
GenericPageItem* hot = new GenericPageItem( this, browse, tr( "Charts" ), QIcon( RESPATH "images/charts.png" ),
boost::bind( &ViewManager::showWhatsHotPage, ViewManager::instance() ),
boost::bind( &ViewManager::whatsHotWidget, ViewManager::instance() ) );
hot->setSortValue( -300 );
m_collectionsGroup = new GroupItem( this, m_rootItem, tr( "Friends" ), 15 );
endInsertRows();
}
void
SourcesModel::appendItem( const Tomahawk::source_ptr& source )
{
beginInsertRows( QModelIndex(), rowCount(), rowCount() );
// append to end
new CollectionItem( this, m_rootItem, source );
SourceTreeItem* parent;
if ( !source.isNull() && source->isLocal() )
{
parent = m_rootItem;
}
else
{
parent = m_collectionsGroup;
}
QModelIndex idx = indexFromItem( parent );
beginInsertRows( idx, rowCount( idx ), rowCount( idx ) );
new SourceItem( this, parent, source );
endInsertRows();
}
@ -265,7 +313,7 @@ SourcesModel::removeItem( const Tomahawk::source_ptr& source )
for ( int row = 0; row < rows; row++ )
{
QModelIndex idx = index( row, 0, QModelIndex() );
CollectionItem* item = static_cast< CollectionItem* >( idx.internalPointer() );
SourceItem* item = static_cast< SourceItem* >( idx.internalPointer() );
if ( item && item->source() == source )
{
// qDebug() << "Found removed source item:" << item->source()->userName();
@ -348,12 +396,12 @@ SourcesModel::viewPageActivated( Tomahawk::ViewPage* page )
m_sourcesWithViewPageItems[ s ] = collectionOfPlaylist;
tDebug() << "Emitting dataChanged for offline source:" << idx << idx.isValid() << collectionOfPlaylist << collectionOfPlaylist->text();
emit dataChanged( idx, idx );
}
}
}
}
SourceTreeItem*
SourcesModel::activatePlaylistPage( ViewPage* p, SourceTreeItem* i )
{
@ -425,7 +473,6 @@ SourcesModel::itemUpdated()
void
SourcesModel::onItemRowsAddedBegin( int first, int last )
{
Q_ASSERT( qobject_cast< SourceTreeItem* >( sender() ) );
SourceTreeItem* item = qobject_cast< SourceTreeItem* >( sender() );
@ -486,6 +533,7 @@ SourcesModel::linkSourceItemToPage( SourceTreeItem* item, ViewPage* p )
m_viewPageDelayedCacheItem = 0;
}
void
SourcesModel::onWidgetDestroyed( QWidget* w )
{
@ -576,9 +624,17 @@ SourcesModel::itemSelectRequest( SourceTreeItem* item )
emit selectRequest( QPersistentModelIndex( indexFromItem( item ) ) );
}
void
SourcesModel::itemExpandRequest( SourceTreeItem *item )
{
qDebug() << "expanding source" << indexFromItem( item ) << item;
emit expandRequest( QPersistentModelIndex( indexFromItem( item ) ) );
}
void
SourcesModel::itemToggleExpandRequest( SourceTreeItem *item )
{
emit toggleExpandRequest( QPersistentModelIndex( indexFromItem( item ) ) );
}

@ -1,19 +1,21 @@
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.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/>.
*/
#ifndef SOURCESMODEL_H
@ -28,6 +30,7 @@
class QMimeData;
class SourceTreeItem;
class GroupItem;
namespace Tomahawk {
class Source;
@ -41,8 +44,10 @@ class SourcesModel : public QAbstractItemModel
public:
enum RowType {
Invalid = -1,
Divider = 9,
Collection = 0,
Group = 8,
Category = 1,
CategoryAdd = 2,
@ -65,7 +70,7 @@ public:
SourceTreeItemTypeRole = Qt::UserRole + 11,
SortRole = Qt::UserRole + 12,
IDRole = Qt::UserRole + 13,
LatchedOnRole = Qt::UserRole + 14
LatchedOnRole = Qt::UserRole + 14
};
SourcesModel( QObject* parent = 0 );
@ -87,6 +92,8 @@ public:
virtual Qt::DropActions supportedDropActions() const;
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
void appendGroups();
void appendItem( const Tomahawk::source_ptr& source );
bool removeItem( const Tomahawk::source_ptr& source );
@ -110,10 +117,12 @@ public slots:
void itemSelectRequest( SourceTreeItem* item );
void itemExpandRequest( SourceTreeItem* item );
void itemToggleExpandRequest( SourceTreeItem* item );
signals:
void selectRequest( const QPersistentModelIndex& idx );
void expandRequest( const QPersistentModelIndex& idx );
void toggleExpandRequest( const QPersistentModelIndex& idx );
private slots:
void onSourcesAdded( const QList<Tomahawk::source_ptr>& sources );
@ -121,12 +130,14 @@ private slots:
void onSourceRemoved( const Tomahawk::source_ptr& source );
void onWidgetDestroyed( QWidget* w );
private:
SourceTreeItem* itemFromIndex( const QModelIndex& idx ) const;
int rowForItem( SourceTreeItem* item ) const;
SourceTreeItem* activatePlaylistPage( Tomahawk::ViewPage* p, SourceTreeItem* i );
SourceTreeItem* m_rootItem;
GroupItem* m_collectionsGroup;
QList< Tomahawk::source_ptr > m_sourcesWithViewPage;
QHash< Tomahawk::source_ptr, SourceTreeItem* > m_sourcesWithViewPageItems;

@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.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
@ -22,7 +23,7 @@
#include "sourcelist.h"
#include "sourcesmodel.h"
#include "sourcetree/items/collectionitem.h"
#include "sourcetree/items/sourceitem.h"
#include "utils/logger.h"
@ -37,6 +38,11 @@ SourcesProxyModel::SourcesProxyModel( SourcesModel* model, QObject* parent )
setSourceModel( model );
connect( model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onModelChanged() ) );
connect( model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), SLOT( onModelChanged() ) );
if ( model && model->metaObject()->indexOfSignal( "toggleExpandRequest(QPersistentModelIndex)" ) > -1 )
connect( model, SIGNAL( toggleExpandRequest( QPersistentModelIndex ) ), this, SLOT( toggleExpandRequested( QPersistentModelIndex ) ), Qt::QueuedConnection );
if ( model && model->metaObject()->indexOfSignal( "expandRequest(QPersistentModelIndex)" ) > -1 )
connect( model, SIGNAL( expandRequest( QPersistentModelIndex ) ), this, SLOT( expandRequested( QPersistentModelIndex ) ), Qt::QueuedConnection );
if ( model && model->metaObject()->indexOfSignal( "selectRequest(QPersistentModelIndex)" ) > -1 )
@ -55,10 +61,16 @@ SourcesProxyModel::showOfflineSources( bool offlineSourcesShown )
bool
SourcesProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const
{
// always filter empty top-level groups
SourceTreeItem* item = m_model->data( sourceModel()->index( sourceRow, 0, sourceParent ), SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >();
if ( item && item->type() != SourcesModel::Divider && item->parent()->parent() == 0 && !item->children().count() )
return false;
if ( !m_filtered )
return true;
CollectionItem* sti = qobject_cast< CollectionItem* >( m_model->data( sourceModel()->index( sourceRow, 0, sourceParent ), SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >() );
SourceItem* sti = qobject_cast< SourceItem* >( item );
if ( sti )
{
if ( sti->source().isNull() || sti->source()->isOnline() )
@ -68,6 +80,7 @@ SourcesProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourcePar
else
return false;
}
// accept rows that aren't sources
return true;
}
@ -76,7 +89,6 @@ SourcesProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourcePar
void
SourcesProxyModel::selectRequested( const QPersistentModelIndex& idx )
{
qDebug() << "selectRequested for idx" << idx << idx.data(Qt::DisplayRole).toString() << mapFromSource( idx ) << mapFromSource( idx ).data(Qt::DisplayRole).toString();
emit selectRequest( QPersistentModelIndex( mapFromSource( idx ) ) );
}
@ -84,11 +96,17 @@ SourcesProxyModel::selectRequested( const QPersistentModelIndex& idx )
void
SourcesProxyModel::expandRequested( const QPersistentModelIndex& idx )
{
qDebug() << "emitting expand for idx" << idx << idx.data(Qt::DisplayRole).toString() << mapFromSource( idx ) << mapFromSource( idx ).data(Qt::DisplayRole).toString();
emit expandRequest( QPersistentModelIndex( mapFromSource( idx ) ) );
}
void
SourcesProxyModel::toggleExpandRequested( const QPersistentModelIndex& idx )
{
emit toggleExpandRequest( QPersistentModelIndex( mapFromSource( idx ) ) );
}
bool
SourcesProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const
{
@ -103,3 +121,10 @@ SourcesProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right )
else
return QString::localeAwareCompare( lefts, rights ) < 0;
}
void
SourcesProxyModel::onModelChanged()
{
invalidateFilter();
}

@ -35,15 +35,20 @@ public slots:
void selectRequested( const QPersistentModelIndex& );
void expandRequested( const QPersistentModelIndex& );
void toggleExpandRequested( const QPersistentModelIndex& );
signals:
void selectRequest( const QPersistentModelIndex& idx );
void expandRequest( const QPersistentModelIndex& idx );
void toggleExpandRequest( const QPersistentModelIndex& idx );
protected:
bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const;
bool lessThan( const QModelIndex& left, const QModelIndex& right ) const;
private slots:
void onModelChanged();
private:
SourcesModel* m_model;

@ -35,7 +35,7 @@
#include "sourcelist.h"
#include "sourcedelegate.h"
#include "sourcetree/items/playlistitems.h"
#include "sourcetree/items/collectionitem.h"
#include "sourcetree/items/sourceitem.h"
#include "audio/audioengine.h"
#include "sourceplaylistinterface.h"
#include "tomahawksettings.h"
@ -56,6 +56,8 @@ SourceTreeView::SourceTreeView( QWidget* parent )
, m_latchManager( new LatchManager( this ) )
, m_dragging( false )
{
setProperty( "flattenBranches", QVariant( true ) );
setFrameShape( QFrame::NoFrame );
setAttribute( Qt::WA_MacShowFocusRect, 0 );
setContentsMargins( 0, 0, 0, 0 );
@ -71,11 +73,12 @@ SourceTreeView::SourceTreeView( QWidget* parent )
setDropIndicatorShown( false );
setAllColumnsShowFocus( true );
setUniformRowHeights( false );
setIndentation( 14 );
setIndentation( 0 );
setSortingEnabled( true );
sortByColumn( 0, Qt::AscendingOrder );
setVerticalScrollMode( QTreeView::ScrollPerPixel );
setMouseTracking( true );
// TODO animation conflicts with the expanding-playlists-when-collection-is-null
// so investigate
// setAnimated( true );
@ -93,6 +96,7 @@ SourceTreeView::SourceTreeView( QWidget* parent )
m_proxyModel = new SourcesProxyModel( m_model, this );
connect( m_proxyModel, SIGNAL( selectRequest( QPersistentModelIndex ) ), this, SLOT( selectRequest( QPersistentModelIndex ) ) );
connect( m_proxyModel, SIGNAL( expandRequest( QPersistentModelIndex ) ), this, SLOT( expandRequest( QPersistentModelIndex ) ) );
connect( m_proxyModel, SIGNAL( toggleExpandRequest( QPersistentModelIndex ) ), this, SLOT( toggleExpandRequest( QPersistentModelIndex ) ) );
setModel( m_proxyModel );
@ -152,7 +156,7 @@ SourceTreeView::setupMenus()
if ( type == SourcesModel::Collection )
{
CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex );
SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex );
source_ptr source = item->source();
if ( !source.isNull() )
{
@ -198,7 +202,7 @@ SourceTreeView::setupMenus()
connect( deletePlaylistAction, SIGNAL( triggered() ), SLOT( deletePlaylist() ) );
connect( copyPlaylistAction, SIGNAL( triggered() ), SLOT( copyPlaylistLink() ) );
connect( addToLocalAction, SIGNAL( triggered() ), SLOT( addToLocal() ) );
connect( latchOnAction, SIGNAL( triggered() ), SLOT( latchOnOrCatchUp() ), Qt::QueuedConnection );
connect( latchOnAction, SIGNAL( triggered() ), SLOT( latchOnOrCatchUp() ), Qt::QueuedConnection );
}
@ -235,7 +239,6 @@ SourceTreeView::onItemExpanded( const QModelIndex& idx )
void
SourceTreeView::selectRequest( const QPersistentModelIndex& idx )
{
qDebug() << "Select request for:" << idx << idx.data().toString() << selectionModel()->selectedIndexes().contains( idx );
if ( !selectionModel()->selectedIndexes().contains( idx ) )
{
scrollTo( idx, QTreeView::EnsureVisible );
@ -247,11 +250,20 @@ SourceTreeView::selectRequest( const QPersistentModelIndex& idx )
void
SourceTreeView::expandRequest( const QPersistentModelIndex &idx )
{
qDebug() << "Expanding idx" << idx << idx.data( Qt::DisplayRole ).toString();
expand( idx );
}
void
SourceTreeView::toggleExpandRequest( const QPersistentModelIndex &idx )
{
if ( isExpanded( idx ) )
collapse( idx );
else
expand( idx );
}
void
SourceTreeView::loadPlaylist()
{
@ -359,7 +371,7 @@ SourceTreeView::latchOnOrCatchUp()
if( type != SourcesModel::Collection )
return;
CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex );
SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex );
source_ptr source = item->source();
latchOnOrCatchUp( source );
@ -378,7 +390,7 @@ SourceTreeView::latchOff()
if( type != SourcesModel::Collection )
return;
const CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex );
const SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex );
const source_ptr source = item->source();
latchOff( source );
@ -436,7 +448,7 @@ SourceTreeView::onCustomContextMenu( const QPoint& pos )
}
else if ( model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ) == SourcesModel::Collection )
{
CollectionItem* item = itemFromIndex< CollectionItem >( m_contextMenuIndex );
SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex );
if ( !item->source().isNull() && !item->source()->isLocal() )
m_latchMenu.exec( mapToGlobal( pos ) );
else if ( !item->source().isNull() )

@ -64,6 +64,7 @@ private slots:
void onItemActivated( const QModelIndex& index );
void selectRequest( const QPersistentModelIndex& idx );
void expandRequest( const QPersistentModelIndex& idx );
void toggleExpandRequest( const QPersistentModelIndex& idx );
void loadPlaylist();
void deletePlaylist( const QModelIndex& = QModelIndex() );
@ -74,7 +75,7 @@ private slots:
void latchOff();
void latchOnOrCatchUp( const Tomahawk::source_ptr& source );
void latchOff( const Tomahawk::source_ptr& source );
void onCustomContextMenu( const QPoint& pos );
protected:

@ -226,7 +226,6 @@ TomahawkApp::init()
#ifndef ENABLE_HEADLESS
EchonestGenerator::setupCatalogs();
if ( !m_headless )
{
tDebug() << "Init MainWindow.";
@ -282,7 +281,7 @@ TomahawkApp::init()
GlobalActionManager::instance();
// check if our spotify playlist api server is up and running, and enable spotify playlist drops if so
QNetworkReply* r = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( SPOTIFY_PLAYLIST_API_URL "/playlist/test" ) ) );
QNetworkReply* r = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( SPOTIFY_PLAYLIST_API_URL "/pong" ) ) );
connect( r, SIGNAL( finished() ), this, SLOT( spotifyApiCheckFinished() ) );
#endif
@ -541,10 +540,10 @@ TomahawkApp::spotifyApiCheckFinished()
QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
Q_ASSERT( reply );
if ( reply->error() == QNetworkReply::ContentNotFoundError )
DropJob::setCanParseSpotifyPlaylists( true );
else
if ( reply->error() )
DropJob::setCanParseSpotifyPlaylists( false );
else
DropJob::setCanParseSpotifyPlaylists( true );
#endif
}