|
|
|
/***************************************************************************
|
|
|
|
* copyright : (C) 2005-2006 Seb Ruiz <me@sebruiz.net> *
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include "amarok.h" //foreach macro
|
|
|
|
#include "browserToolBar.h" //search toolbar
|
|
|
|
#include "clicklineedit.h"
|
|
|
|
#include "collectiondb.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "playlist.h"
|
|
|
|
#include "statistics.h"
|
|
|
|
#include "tagdialog.h" //showContextMenu()
|
|
|
|
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <kdeversion.h> //KDE_VERSION ifndefs. Remove this once we reach a kde 4 dep
|
|
|
|
#include <kiconloader.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kmultipledrag.h> //startDrag()
|
|
|
|
#include <kpopupmenu.h>
|
|
|
|
#include <kstringhandler.h> //paintCell
|
|
|
|
#include <ktoolbarbutton.h> //ctor
|
|
|
|
#include <kurldrag.h> //startDrag()
|
|
|
|
#include <kwin.h>
|
|
|
|
|
|
|
|
#include <tqcolor.h>
|
|
|
|
#include <tqdatetime.h>
|
|
|
|
#include <tqheader.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqpen.h>
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqsimplerichtext.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
#include <tqtooltip.h>
|
|
|
|
#include <tqvbox.h>
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// CLASS Statistics
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
Statistics *Statistics::s_instance = 0;
|
|
|
|
|
|
|
|
Statistics::Statistics( TQWidget *parent, const char *name )
|
|
|
|
: KDialogBase( KDialogBase::Swallow, 0, parent, name, false, 0, Close )
|
|
|
|
, m_timer( new TQTimer( this ) )
|
|
|
|
{
|
|
|
|
s_instance = this;
|
|
|
|
|
|
|
|
// Gives the window a small title bar, and skips a taskbar entry
|
|
|
|
KWin::setType( winId(), NET::Utility );
|
|
|
|
KWin::setState( winId(), NET::SkipTaskbar );
|
|
|
|
|
|
|
|
kapp->setTopWidget( this );
|
|
|
|
setCaption( kapp->makeStdCaption( i18n("Collection Statistics") ) );
|
|
|
|
setInitialSize( TQSize( 400, 550 ) );
|
|
|
|
|
|
|
|
TQVBox *mainBox = new TQVBox( this );
|
|
|
|
setMainWidget( mainBox );
|
|
|
|
|
|
|
|
TQVBox *box = new TQVBox( mainWidget() );
|
|
|
|
box->setSpacing( 5 );
|
|
|
|
|
|
|
|
{ //<Search LineEdit>
|
|
|
|
KToolBar *bar = new Browser::ToolBar( box );
|
|
|
|
bar->setIconSize( 22, false ); //looks more sensible
|
|
|
|
bar->setFlat( true ); //removes the ugly frame
|
|
|
|
bar->setMovingEnabled( false ); //removes the ugly frame
|
|
|
|
|
|
|
|
TQWidget *button = new KToolBarButton( "locationbar_erase", 1, bar );
|
|
|
|
m_lineEdit = new ClickLineEdit( i18n( "Enter search terms here" ), bar );
|
|
|
|
|
|
|
|
bar->setStretchableWidget( m_lineEdit );
|
|
|
|
m_lineEdit->setFrame( TQFrame::Sunken );
|
|
|
|
m_lineEdit->installEventFilter( this ); //we intercept keyEvents
|
|
|
|
|
|
|
|
connect( button, TQT_SIGNAL( clicked() ) , m_lineEdit , TQT_SLOT( clear() ) );
|
|
|
|
connect( m_timer, TQT_SIGNAL( timeout() ) , TQT_SLOT( slotSetFilter() ) );
|
|
|
|
connect( m_lineEdit, TQT_SIGNAL( textChanged( const TQString& ) ), TQT_SLOT( slotSetFilterTimeout() ) );
|
|
|
|
connect( m_lineEdit, TQT_SIGNAL( returnPressed() ) , TQT_SLOT( slotSetFilter() ) );
|
|
|
|
|
|
|
|
TQToolTip::add( button, i18n( "Clear search field" ) );
|
|
|
|
} //</Search LineEdit>
|
|
|
|
|
|
|
|
m_listView = new StatisticsList( box );
|
|
|
|
}
|
|
|
|
|
|
|
|
Statistics::~Statistics()
|
|
|
|
{
|
|
|
|
s_instance = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Statistics::slotSetFilterTimeout() //SLOT
|
|
|
|
{
|
|
|
|
m_timer->start( 280, true ); //stops the timer for us first
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Statistics::slotSetFilter() //SLOT
|
|
|
|
{
|
|
|
|
m_timer->stop();
|
|
|
|
m_listView->setFilter( m_lineEdit->text() );
|
|
|
|
if( m_listView->childCount() > 1 )
|
|
|
|
m_listView->renderView();
|
|
|
|
else
|
|
|
|
m_listView->refreshView();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// CLASS StatisticsList
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
StatisticsList::StatisticsList( TQWidget *parent, const char *name )
|
|
|
|
: KListView( parent, name )
|
|
|
|
, m_currentItem( 0 )
|
|
|
|
, m_expanded( false )
|
|
|
|
{
|
|
|
|
header()->hide();
|
|
|
|
|
|
|
|
addColumn( i18n("Name") );
|
|
|
|
setResizeMode( TQListView::LastColumn );
|
|
|
|
setSelectionMode( TQListView::Extended );
|
|
|
|
setSorting( -1 );
|
|
|
|
|
|
|
|
setAcceptDrops( false );
|
|
|
|
setDragEnabled( true );
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL( onItem( TQListViewItem*) ), TQT_SLOT( startHover( TQListViewItem* ) ) );
|
|
|
|
connect( this, TQT_SIGNAL( onViewport() ), TQT_SLOT( clearHover() ) );
|
|
|
|
connect( this, TQT_SIGNAL( clicked( TQListViewItem*) ), TQT_SLOT( itemClicked( TQListViewItem* ) ) );
|
|
|
|
connect( this, TQT_SIGNAL( contextMenuRequested( TQListViewItem *, const TQPoint &, int ) ),
|
|
|
|
this, TQT_SLOT( showContextMenu( TQListViewItem *, const TQPoint &, int ) ) );
|
|
|
|
|
|
|
|
if( CollectionDB::instance()->isEmpty() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
renderView();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsList::startDrag()
|
|
|
|
{
|
|
|
|
// there is only one item ever selected in this tool. maybe this needs to change
|
|
|
|
|
|
|
|
DEBUG_FUNC_INFO
|
|
|
|
|
|
|
|
KURL::List list;
|
|
|
|
KMultipleDrag *drag = new KMultipleDrag( this );
|
|
|
|
|
|
|
|
TQListViewItemIterator it( this, TQListViewItemIterator::Selected );
|
|
|
|
|
|
|
|
StatisticsDetailedItem *item = dynamic_cast<StatisticsDetailedItem*>(*it);
|
|
|
|
|
|
|
|
if ( !item )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( item->itemType() == StatisticsDetailedItem::TRACK )
|
|
|
|
{
|
|
|
|
list += KURL::fromPathOrURL( item->url() );
|
|
|
|
drag->addDragObject( new KURLDrag( list, viewport() ) );
|
|
|
|
drag->setPixmap( CollectionDB::createDragPixmap(list),
|
|
|
|
TQPoint( CollectionDB::DRAGPIXMAP_OFFSET_X,
|
|
|
|
CollectionDB::DRAGPIXMAP_OFFSET_Y ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TQTextDrag *textdrag = new TQTextDrag( '\n' + item->getSQL(), 0 );
|
|
|
|
textdrag->setSubtype( "amarok-sql" );
|
|
|
|
drag->addDragObject( textdrag );
|
|
|
|
drag->setPixmap( CollectionDB::createDragPixmapFromSQL( item->getSQL() ),
|
|
|
|
TQPoint( CollectionDB::DRAGPIXMAP_OFFSET_X,
|
|
|
|
CollectionDB::DRAGPIXMAP_OFFSET_Y ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
clearSelection();
|
|
|
|
drag->dragCopy();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsList::refreshView()
|
|
|
|
{
|
|
|
|
if( m_expanded )
|
|
|
|
{
|
|
|
|
if( !firstChild() )
|
|
|
|
{
|
|
|
|
error() << "Statistics: uh oh, no first child!" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
while( firstChild()->firstChild() )
|
|
|
|
delete firstChild()->firstChild();
|
|
|
|
|
|
|
|
expandInformation( static_cast<StatisticsItem*>(firstChild()), true /*refresh*/ );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
renderView();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsList::renderView()
|
|
|
|
{
|
|
|
|
m_expanded = false;
|
|
|
|
|
|
|
|
//ensure cleanliness - this function is not just called from the ctor, but also when returning to the initial display
|
|
|
|
while( firstChild() )
|
|
|
|
delete firstChild();
|
|
|
|
m_currentItem = 0;
|
|
|
|
|
|
|
|
QueryBuilder qb;
|
|
|
|
TQStringList a;
|
|
|
|
|
|
|
|
qb.clear();
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcCount, QueryBuilder::tabSong, QueryBuilder::valURL );
|
|
|
|
qb.setOptions( QueryBuilder::optRemoveDuplicates );
|
|
|
|
a = qb.run();
|
|
|
|
|
|
|
|
m_trackItem = new StatisticsItem( i18n("Favorite Tracks"), this, 0 );
|
|
|
|
m_trackItem->setSubtext( i18n("%n track", "%n tracks", a[0].toInt()) );
|
|
|
|
|
|
|
|
qb.clear();
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcSum, QueryBuilder::tabStats, QueryBuilder::valPlayCounter );
|
|
|
|
a = qb.run();
|
|
|
|
|
|
|
|
m_mostplayedItem = new StatisticsItem( i18n("Most Played Tracks"), this, m_trackItem );
|
|
|
|
m_mostplayedItem->setSubtext( i18n("%n play", "%n plays", a[0].toInt()) );
|
|
|
|
|
|
|
|
qb.clear();
|
|
|
|
//qb.addReturnFunctionValue( QueryBuilder::funcCount, QueryBuilder::tabArtist, QueryBuilder::valID );
|
|
|
|
//qb.setOptions( QueryBuilder::optRemoveDuplicates );
|
|
|
|
//a = qb.run();
|
|
|
|
qb.setOptions( QueryBuilder::optRemoveDuplicates );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valArtistID );
|
|
|
|
//I can't get the correct value w/o using a subquery, and querybuilder doesn't support those
|
|
|
|
a = TQString::number( qb.run().count() );
|
|
|
|
|
|
|
|
m_artistItem = new StatisticsItem( i18n("Favorite Artists"), this, m_mostplayedItem );
|
|
|
|
m_artistItem->setSubtext( i18n("%n artist", "%n artists", a[0].toInt()) );
|
|
|
|
|
|
|
|
qb.clear();
|
|
|
|
//qb.addReturnFunctionValue( QueryBuilder::funcCount, QueryBuilder::tabAlbum, QueryBuilder::valID );
|
|
|
|
//qb.setOptions( QueryBuilder::optRemoveDuplicates );
|
|
|
|
//a = qb.run();
|
|
|
|
qb.setOptions( QueryBuilder::optRemoveDuplicates );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valAlbumID );
|
|
|
|
//I can't get the correct value w/o using a subquery, and querybuilder doesn't support those
|
|
|
|
a = TQString::number( qb.run().count() );
|
|
|
|
|
|
|
|
m_albumItem = new StatisticsItem( i18n("Favorite Albums"), this, m_artistItem );
|
|
|
|
m_albumItem->setSubtext( i18n("%n album", "%n albums", a[0].toInt()) );
|
|
|
|
|
|
|
|
qb.clear();
|
|
|
|
//qb.addReturnFunctionValue( QueryBuilder::funcCount, QueryBuilder::tabGenre, QueryBuilder::valID );
|
|
|
|
//qb.setOptions( QueryBuilder::optRemoveDuplicates );
|
|
|
|
//a = qb.run();
|
|
|
|
qb.setOptions( QueryBuilder::optRemoveDuplicates );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valGenreID );
|
|
|
|
//I can't get the correct value w/o using a subquery, and querybuilder doesn't support those
|
|
|
|
a = TQString::number( qb.run().count() );
|
|
|
|
|
|
|
|
m_genreItem = new StatisticsItem( i18n("Favorite Genres"), this, m_albumItem );
|
|
|
|
m_genreItem->setSubtext( i18n("%n genre", "%n genres", a[0].toInt()) );
|
|
|
|
|
|
|
|
qb.clear();
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcMin, QueryBuilder::tabStats, QueryBuilder::valCreateDate );
|
|
|
|
qb.setOptions( QueryBuilder::optRemoveDuplicates );
|
|
|
|
a = qb.run();
|
|
|
|
TQDateTime firstPlay = TQDateTime::currentDateTime();
|
|
|
|
if ( a[0].toUInt() )
|
|
|
|
firstPlay.setTime_t( a[0].toUInt() );
|
|
|
|
|
|
|
|
m_newestItem = new StatisticsItem( i18n("Newest Items"), this, m_genreItem );
|
|
|
|
m_newestItem->setSubtext( i18n("First played %1").arg( Amarok::verboseTimeSince( firstPlay ) ) );
|
|
|
|
|
|
|
|
m_trackItem ->setIcon( Amarok::icon("track") );
|
|
|
|
m_mostplayedItem->setIcon( Amarok::icon("mostplayed") );
|
|
|
|
m_artistItem ->setIcon( Amarok::icon("artist") );
|
|
|
|
m_albumItem ->setIcon( Amarok::icon("album") );
|
|
|
|
m_genreItem ->setIcon( Amarok::icon("favourite_genres") );
|
|
|
|
m_newestItem ->setIcon( Amarok::icon("clock") );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsList::itemClicked( TQListViewItem *item ) //SLOT
|
|
|
|
{
|
|
|
|
if( !item )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( item->depth() != 0 ) //not very flexible, *shrug*
|
|
|
|
return;
|
|
|
|
|
|
|
|
#define item static_cast<StatisticsItem*>(item)
|
|
|
|
|
|
|
|
if( item->isExpanded() )
|
|
|
|
{
|
|
|
|
renderView();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
expandInformation( item );
|
|
|
|
item->setOpen( true );
|
|
|
|
|
|
|
|
#undef item
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsList::expandInformation( StatisticsItem *item, bool refresh )
|
|
|
|
{
|
|
|
|
m_expanded = true;
|
|
|
|
KLocale *locale = new KLocale( "locale" );
|
|
|
|
|
|
|
|
QueryBuilder qb;
|
|
|
|
|
|
|
|
StatisticsDetailedItem *m_last = 0;
|
|
|
|
uint c = 1;
|
|
|
|
|
|
|
|
if( item == m_trackItem )
|
|
|
|
{
|
|
|
|
if( !refresh ) {
|
|
|
|
delete m_newestItem;
|
|
|
|
delete m_genreItem;
|
|
|
|
delete m_albumItem;
|
|
|
|
delete m_artistItem;
|
|
|
|
delete m_mostplayedItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valScore );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valRating );
|
|
|
|
qb.addNumericFilter( QueryBuilder::tabStats, QueryBuilder::valForFavoriteSorting(), "0", QueryBuilder::modeGreater );
|
|
|
|
qb.setGoogleFilter( QueryBuilder::tabSong | QueryBuilder::tabArtist, m_filter );
|
|
|
|
qb.sortByFavorite();
|
|
|
|
qb.setLimit( 0, 50 );
|
|
|
|
TQStringList fave = qb.run();
|
|
|
|
|
|
|
|
for( uint i=0; i < fave.count(); i += qb.countReturnValues() )
|
|
|
|
{
|
|
|
|
TQString name = i18n("%1. %2 - %3").arg( TQString::number(c),
|
|
|
|
fave[i].isEmpty() ? i18n( "Unknown" ) : fave[i],
|
|
|
|
fave[i+1].isEmpty() ? i18n( "Unknown" ) : fave[i+1]);
|
|
|
|
TQString score = locale->formatNumber( fave[i+3].toDouble(), 0 );
|
|
|
|
TQString rating = locale->formatNumber( fave[i+4].toDouble() / 2.0, 1 );
|
|
|
|
m_last = new StatisticsDetailedItem( name, subText( score, rating ), item, m_last );
|
|
|
|
m_last->setItemType( StatisticsDetailedItem::TRACK );
|
|
|
|
m_last->setUrl( fave[i+2] );
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( item == m_mostplayedItem )
|
|
|
|
{
|
|
|
|
if( !refresh ) {
|
|
|
|
delete m_newestItem;
|
|
|
|
delete m_genreItem;
|
|
|
|
delete m_albumItem;
|
|
|
|
delete m_artistItem;
|
|
|
|
delete m_trackItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valPlayCounter );
|
|
|
|
qb.addNumericFilter( QueryBuilder::tabStats, QueryBuilder::valPlayCounter, "0", QueryBuilder::modeGreater );
|
|
|
|
qb.setGoogleFilter( QueryBuilder::tabSong | QueryBuilder::tabArtist, m_filter );
|
|
|
|
qb.sortBy( QueryBuilder::tabStats, QueryBuilder::valPlayCounter, true );
|
|
|
|
qb.setLimit( 0, 50 );
|
|
|
|
TQStringList fave = qb.run();
|
|
|
|
|
|
|
|
for( uint i=0; i < fave.count(); i += qb.countReturnValues() )
|
|
|
|
{
|
|
|
|
TQString name = i18n("%1. %2 - %3").arg( TQString::number(c),
|
|
|
|
fave[i].isEmpty() ? i18n( "Unknown" ) : fave[i],
|
|
|
|
fave[i+1].isEmpty() ? i18n( "Unknown" ) : fave[i+1]);
|
|
|
|
double plays = fave[i+3].toDouble();
|
|
|
|
TQString subtext = i18n("%1: %2").arg( i18n( "Playcount" ) ).arg( plays );
|
|
|
|
m_last = new StatisticsDetailedItem( name, subtext, item, m_last );
|
|
|
|
m_last->setItemType( StatisticsDetailedItem::TRACK );
|
|
|
|
m_last->setUrl( fave[i+2] );
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( item == m_artistItem )
|
|
|
|
{
|
|
|
|
if( !refresh ) {
|
|
|
|
delete m_newestItem;
|
|
|
|
delete m_genreItem;
|
|
|
|
delete m_albumItem;
|
|
|
|
delete m_mostplayedItem;
|
|
|
|
delete m_trackItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName );
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcAvg, QueryBuilder::tabStats, QueryBuilder::valScore );
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcAvg, QueryBuilder::tabStats, QueryBuilder::valRating );
|
|
|
|
qb.sortByFavoriteAvg();
|
|
|
|
// only artists with more than 3 tracks
|
|
|
|
qb.having( QueryBuilder::tabArtist, QueryBuilder::valID, QueryBuilder::funcCount, QueryBuilder::modeGreater, "3" );
|
|
|
|
qb.setGoogleFilter( QueryBuilder::tabArtist, m_filter );
|
|
|
|
qb.groupBy( QueryBuilder::tabArtist, QueryBuilder::valName);
|
|
|
|
qb.setLimit( 0, 50 );
|
|
|
|
TQStringList fave = qb.run();
|
|
|
|
|
|
|
|
for( uint i=0; i < fave.count(); i += qb.countReturnValues() )
|
|
|
|
{
|
|
|
|
TQString name = i18n("%1. %2").arg( TQString::number(c),
|
|
|
|
fave[i].isEmpty() ? i18n( "Unknown" ) : fave[i] );
|
|
|
|
TQString score = locale->formatNumber( fave[i+1].toDouble(), 2 );
|
|
|
|
TQString rating = locale->formatNumber( fave[i+2].toDouble() / 2.0, 2 );
|
|
|
|
m_last = new StatisticsDetailedItem( name, subText( score, rating ), item, m_last );
|
|
|
|
m_last->setItemType( StatisticsDetailedItem::ARTIST );
|
|
|
|
TQString url = TQString("%1").arg( fave[i] );
|
|
|
|
m_last->setUrl( url );
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( item == m_albumItem )
|
|
|
|
{
|
|
|
|
if( !refresh ) {
|
|
|
|
delete m_newestItem;
|
|
|
|
delete m_genreItem;
|
|
|
|
delete m_artistItem;
|
|
|
|
delete m_mostplayedItem;
|
|
|
|
delete m_trackItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valID );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valID );
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcAvg, QueryBuilder::tabStats, QueryBuilder::valScore );
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcAvg, QueryBuilder::tabStats, QueryBuilder::valRating );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valIsCompilation );
|
|
|
|
// only albums with more than 3 tracks
|
|
|
|
qb.having( QueryBuilder::tabAlbum, QueryBuilder::valID, QueryBuilder::funcCount, QueryBuilder::modeGreater, "3" );
|
|
|
|
qb.setOptions( QueryBuilder::optNoCompilations ); // samplers __need__ to be handled differently
|
|
|
|
qb.setGoogleFilter( QueryBuilder::tabAlbum | QueryBuilder::tabArtist, m_filter );
|
|
|
|
qb.sortByFavoriteAvg();
|
|
|
|
qb.excludeMatch( QueryBuilder::tabAlbum, i18n( "Unknown" ) );
|
|
|
|
qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valID );
|
|
|
|
qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valName );
|
|
|
|
qb.groupBy( QueryBuilder::tabArtist, QueryBuilder::valID );
|
|
|
|
qb.groupBy( QueryBuilder::tabArtist, QueryBuilder::valName );
|
|
|
|
qb.groupBy( QueryBuilder::tabSong, QueryBuilder::valIsCompilation );
|
|
|
|
|
|
|
|
qb.setLimit( 0, 50 );
|
|
|
|
TQStringList fave = qb.run();
|
|
|
|
|
|
|
|
const TQString trueValue = CollectionDB::instance()->boolT();
|
|
|
|
for( uint i=0; i < fave.count(); i += qb.countReturnValues() )
|
|
|
|
{
|
|
|
|
const bool isSampler = (fave[i+6] == trueValue);
|
|
|
|
TQString name = i18n("%1. %2 - %3").arg( TQString::number(c),
|
|
|
|
fave[i].isEmpty() ? i18n( "Unknown" ) : fave[i],
|
|
|
|
isSampler ? i18n( "Various Artists" ) :
|
|
|
|
( fave[i+1].isEmpty() ? i18n( "Unknown" ) : fave[i+1] ) );
|
|
|
|
TQString score = locale->formatNumber( fave[i+4].toDouble(), 2 );
|
|
|
|
TQString rating = locale->formatNumber( fave[i+5].toDouble() / 2.0, 2 );
|
|
|
|
|
|
|
|
m_last = new StatisticsDetailedItem( name, subText( score, rating ), item, m_last );
|
|
|
|
m_last->setItemType( StatisticsDetailedItem::ALBUM );
|
|
|
|
TQString url = TQString("%1 @@@ %2").arg( isSampler ? "0" : fave[i+2], fave[i+3] );
|
|
|
|
m_last->setUrl( url );
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( item == m_genreItem )
|
|
|
|
{
|
|
|
|
if( !refresh ) {
|
|
|
|
delete m_newestItem;
|
|
|
|
delete m_albumItem;
|
|
|
|
delete m_artistItem;
|
|
|
|
delete m_mostplayedItem;
|
|
|
|
delete m_trackItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
qb.addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valName );
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcAvg, QueryBuilder::tabStats, QueryBuilder::valScore );
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcAvg, QueryBuilder::tabStats, QueryBuilder::valRating );
|
|
|
|
// only genres with more than 3 tracks
|
|
|
|
qb.having( QueryBuilder::tabGenre, QueryBuilder::valID, QueryBuilder::funcCount, QueryBuilder::modeGreater, "3" );
|
|
|
|
// only genres which have been played/rated
|
|
|
|
qb.setGoogleFilter( QueryBuilder::tabGenre, m_filter );
|
|
|
|
qb.sortByFavoriteAvg();
|
|
|
|
qb.groupBy( QueryBuilder::tabGenre, QueryBuilder::valName);
|
|
|
|
qb.setLimit( 0, 50 );
|
|
|
|
TQStringList fave = qb.run();
|
|
|
|
|
|
|
|
for( uint i=0; i < fave.count(); i += qb.countReturnValues() )
|
|
|
|
{
|
|
|
|
TQString name = i18n("%1. %2").arg( TQString::number(c),
|
|
|
|
fave[i].isEmpty() ? i18n( "Unknown" ) : fave[i] );
|
|
|
|
TQString score = locale->formatNumber( fave[i+1].toDouble(), 2 );
|
|
|
|
TQString rating = locale->formatNumber( fave[i+2].toDouble() / 2.0, 2 );
|
|
|
|
|
|
|
|
m_last = new StatisticsDetailedItem( name, subText( score, rating ), item, m_last );
|
|
|
|
m_last->setItemType( StatisticsDetailedItem::GENRE );
|
|
|
|
TQString url = TQString("%1").arg( fave[i] );
|
|
|
|
m_last->setUrl( url );
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( item == m_newestItem )
|
|
|
|
{
|
|
|
|
if( !refresh ) {
|
|
|
|
delete m_genreItem;
|
|
|
|
delete m_albumItem;
|
|
|
|
delete m_artistItem;
|
|
|
|
delete m_mostplayedItem;
|
|
|
|
delete m_trackItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valID );
|
|
|
|
qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valID );
|
|
|
|
qb.addReturnFunctionValue( QueryBuilder::funcMax, QueryBuilder::tabSong, QueryBuilder::valCreateDate );
|
|
|
|
qb.sortByFunction( QueryBuilder::funcMax, QueryBuilder::tabSong, QueryBuilder::valCreateDate, true );
|
|
|
|
qb.excludeMatch( QueryBuilder::tabAlbum, i18n( "Unknown" ) );
|
|
|
|
qb.setGoogleFilter( QueryBuilder::tabAlbum | QueryBuilder::tabArtist, m_filter );
|
|
|
|
qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valName);
|
|
|
|
qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valID);
|
|
|
|
qb.groupBy( QueryBuilder::tabArtist, QueryBuilder::valName);
|
|
|
|
qb.groupBy( QueryBuilder::tabArtist, QueryBuilder::valID);
|
|
|
|
qb.setOptions( QueryBuilder::optNoCompilations ); // samplers __need__ to be handled differently
|
|
|
|
qb.setLimit( 0, 50 );
|
|
|
|
TQStringList newest = qb.run();
|
|
|
|
|
|
|
|
for( uint i=0; i < newest.count(); i += qb.countReturnValues() )
|
|
|
|
{
|
|
|
|
TQString name = i18n("%1. %2 - %3").arg( TQString::number(c),
|
|
|
|
newest[i].isEmpty() ? i18n( "Unknown" ) : newest[i],
|
|
|
|
newest[i+1].isEmpty() ? i18n( "Unknown" ) : newest[i+1] );
|
|
|
|
TQDateTime added = TQDateTime();
|
|
|
|
added.setTime_t( newest[i+4].toUInt() );
|
|
|
|
TQString subtext = i18n("Added: %1").arg( Amarok::verboseTimeSince( added ) );
|
|
|
|
m_last = new StatisticsDetailedItem( name, subtext, item, m_last );
|
|
|
|
m_last->setItemType( StatisticsDetailedItem::HISTORY );
|
|
|
|
TQString url = TQString("%1 @@@ %2").arg( newest[i+2] ).arg( newest[i+3] );
|
|
|
|
m_last->setUrl( url );
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
item->setExpanded( true );
|
|
|
|
repaintItem( item ); // Better than ::repaint(), flickers less
|
|
|
|
delete locale;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString StatisticsList::subText( const TQString &score, const TQString &rating ) //static
|
|
|
|
{
|
|
|
|
if( AmarokConfig::useScores() && AmarokConfig::useRatings() )
|
|
|
|
return i18n( "Score: %1 Rating: %2" ).arg( score ).arg( rating );
|
|
|
|
else if( AmarokConfig::useScores() )
|
|
|
|
return i18n( "Score: %1" ).arg( score );
|
|
|
|
else if( AmarokConfig::useRatings() )
|
|
|
|
return i18n( "Rating: %1" ).arg( rating );
|
|
|
|
else
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsList::startHover( TQListViewItem *item ) //SLOT
|
|
|
|
{
|
|
|
|
if( m_currentItem && item != m_currentItem )
|
|
|
|
static_cast<StatisticsItem*>(m_currentItem)->leaveHover();
|
|
|
|
|
|
|
|
if( item->depth() != 0 )
|
|
|
|
{
|
|
|
|
m_currentItem = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static_cast<StatisticsItem*>(item)->enterHover();
|
|
|
|
m_currentItem = item;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsList::clearHover() //SLOT
|
|
|
|
{
|
|
|
|
if( m_currentItem )
|
|
|
|
static_cast<StatisticsItem*>(m_currentItem)->leaveHover();
|
|
|
|
|
|
|
|
m_currentItem = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsList::viewportPaintEvent( TQPaintEvent *e )
|
|
|
|
{
|
|
|
|
if( e ) KListView::viewportPaintEvent( e );
|
|
|
|
|
|
|
|
if( CollectionDB::instance()->isEmpty() && e )
|
|
|
|
{
|
|
|
|
TQPainter p( viewport() );
|
|
|
|
TQString minimumText(i18n(
|
|
|
|
"<div align=center>"
|
|
|
|
"<h3>Statistics</h3>"
|
|
|
|
"You need a collection to use statistics! "
|
|
|
|
"Create a collection and then start playing "
|
|
|
|
"tracks to accumulate data on your play habits!"
|
|
|
|
"</div>" ) );
|
|
|
|
TQSimpleRichText t( minimumText, TQApplication::font() );
|
|
|
|
|
|
|
|
if ( t.width()+30 >= viewport()->width() || t.height()+30 >= viewport()->height() )
|
|
|
|
//too big, giving up
|
|
|
|
return;
|
|
|
|
|
|
|
|
const uint w = t.width();
|
|
|
|
const uint h = t.height();
|
|
|
|
const uint x = (viewport()->width() - w - 30) / 2 ;
|
|
|
|
const uint y = (viewport()->height() - h - 30) / 2 ;
|
|
|
|
|
|
|
|
p.setBrush( colorGroup().background() );
|
|
|
|
p.drawRoundRect( x, y, w+30, h+30, (8*200)/w, (8*200)/h );
|
|
|
|
t.draw( &p, x+15, y+15, TQRect(), colorGroup() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsList::showContextMenu( TQListViewItem *item, const TQPoint &p, int ) //SLOT
|
|
|
|
{
|
|
|
|
if( !item || item->rtti() == StatisticsItem::RTTI ) return;
|
|
|
|
|
|
|
|
#define item static_cast<StatisticsDetailedItem*>(item)
|
|
|
|
|
|
|
|
bool hasSQL = !( item->itemType() == StatisticsDetailedItem::TRACK ); //track is url
|
|
|
|
|
|
|
|
KPopupMenu menu( this );
|
|
|
|
enum Actions { APPEND, QUEUE, INFO };
|
|
|
|
|
|
|
|
menu.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ), APPEND );
|
|
|
|
menu.insertItem( SmallIconSet( Amarok::icon( "queue_track" ) ), i18n( "&Queue Track" ), QUEUE );
|
|
|
|
|
|
|
|
menu.insertSeparator();
|
|
|
|
|
|
|
|
menu.insertItem( SmallIconSet( Amarok::icon( "info" ) ), i18n( "Edit Track &Information..." ), INFO );
|
|
|
|
|
|
|
|
switch( menu.exec( p ) )
|
|
|
|
{
|
|
|
|
case APPEND:
|
|
|
|
hasSQL ?
|
|
|
|
Playlist::instance()->insertMediaSql( item->getSQL() ):
|
|
|
|
Playlist::instance()->insertMedia( KURL::fromPathOrURL( item->url() ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case QUEUE:
|
|
|
|
hasSQL ?
|
|
|
|
Playlist::instance()->insertMediaSql( item->getSQL(), Playlist::Queue ):
|
|
|
|
Playlist::instance()->insertMedia( KURL::fromPathOrURL( item->url() ), Playlist::Queue );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case INFO:
|
|
|
|
if( hasSQL )
|
|
|
|
{
|
|
|
|
TagDialog* dialog = new TagDialog( item->getURLs(), Statistics::instance() );
|
|
|
|
dialog->show();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TagDialog* dialog = new TagDialog( KURL::fromPathOrURL( item->url() ), Statistics::instance() );
|
|
|
|
dialog->show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#undef item
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// CLASS StatisticsItem
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
StatisticsItem::StatisticsItem( TQString text, StatisticsList *parent, KListViewItem *after, const char *name )
|
|
|
|
: KListViewItem( static_cast<KListView*>(parent), after, name )
|
|
|
|
, m_animTimer( new TQTimer( this ) )
|
|
|
|
, m_animCount( 0 )
|
|
|
|
, m_isActive( false )
|
|
|
|
, m_isExpanded( false )
|
|
|
|
{
|
|
|
|
setDragEnabled( false );
|
|
|
|
setDropEnabled( false );
|
|
|
|
setSelectable( false );
|
|
|
|
|
|
|
|
setText( 0, text );
|
|
|
|
|
|
|
|
connect( m_animTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotAnimTimer() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsItem::setIcon( const TQString &icon )
|
|
|
|
{
|
|
|
|
TQString path = kapp->iconLoader()->iconPath( icon, -KIcon::SizeHuge );
|
|
|
|
path.replace( "32x32", "48x48" ); //HACK: KIconLoader only returns 32x32 max. Why?
|
|
|
|
|
|
|
|
// debug() << "ICONPATH: " << path << endl;
|
|
|
|
|
|
|
|
setPixmap( 0, path );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsItem::enterHover()
|
|
|
|
{
|
|
|
|
m_animEnter = true;
|
|
|
|
m_animCount = 0;
|
|
|
|
m_isActive = true;
|
|
|
|
m_animTimer->start( ANIM_INTERVAL );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsItem::leaveHover()
|
|
|
|
{
|
|
|
|
// This can happen if you enter and leave the tab quickly
|
|
|
|
if( m_animCount == 0 )
|
|
|
|
m_animCount = 1;
|
|
|
|
|
|
|
|
m_animEnter = false;
|
|
|
|
m_isActive = true;
|
|
|
|
m_animTimer->start( ANIM_INTERVAL );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsItem::slotAnimTimer()
|
|
|
|
{
|
|
|
|
if( m_animEnter )
|
|
|
|
{
|
|
|
|
m_animCount += 1;
|
|
|
|
listView()->repaintItem( this ); // Better than ::repaint(), flickers less
|
|
|
|
|
|
|
|
if( m_animCount >= ANIM_MAX )
|
|
|
|
m_animTimer->stop();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_animCount -= 1;
|
|
|
|
listView()->repaintItem( this );
|
|
|
|
if( m_animCount <= 0 )
|
|
|
|
{
|
|
|
|
m_animTimer->stop();
|
|
|
|
m_isActive = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsItem::paintCell( TQPainter *p, const TQColorGroup &cg, int column, int width, int align )
|
|
|
|
{
|
|
|
|
TQColor fillColor, textColor;
|
|
|
|
|
|
|
|
if( m_isActive ) //glowing animation
|
|
|
|
{
|
|
|
|
fillColor = blendColors( cg.background(), cg.highlight(), static_cast<int>( m_animCount * 3.5 ) );
|
|
|
|
textColor = blendColors( cg.text(), cg.highlightedText(), static_cast<int>( m_animCount * 4.5 ) );
|
|
|
|
}
|
|
|
|
else //alternate colours
|
|
|
|
{
|
|
|
|
#if KDE_VERSION < KDE_MAKE_VERSION(3,3,91)
|
|
|
|
fillColor = isSelected() ? cg.highlight() : backgroundColor();
|
|
|
|
#else
|
|
|
|
fillColor = isSelected() ? cg.highlight() : backgroundColor(0);
|
|
|
|
#endif
|
|
|
|
textColor = isSelected() ? cg.highlightedText() : cg.text();
|
|
|
|
}
|
|
|
|
|
|
|
|
//flicker-free drawing
|
|
|
|
static TQPixmap buffer;
|
|
|
|
|
|
|
|
buffer.resize( width, height() );
|
|
|
|
|
|
|
|
if( buffer.isNull() )
|
|
|
|
{
|
|
|
|
KListViewItem::paintCell( p, cg, column, width, align );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer.fill( fillColor );
|
|
|
|
|
|
|
|
TQPainter pBuf( &buffer, true );
|
|
|
|
|
|
|
|
KListView *lv = static_cast<KListView *>( listView() );
|
|
|
|
|
|
|
|
TQFont font( p->font() );
|
|
|
|
font.setBold( true );
|
|
|
|
TQFontMetrics fm( p->fontMetrics() );
|
|
|
|
|
|
|
|
int textHeight = height();
|
|
|
|
int text_x = 0;
|
|
|
|
|
|
|
|
pBuf.setPen( textColor );
|
|
|
|
|
|
|
|
if( pixmap( column ) )
|
|
|
|
{
|
|
|
|
int y = (textHeight - pixmap(column)->height())/2;
|
|
|
|
pBuf.drawPixmap( 0, y, *pixmap(column) );
|
|
|
|
text_x += pixmap(column)->width() + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
pBuf.setFont( font );
|
|
|
|
TQFontMetrics fmName( font );
|
|
|
|
|
|
|
|
TQString name = text(column);
|
|
|
|
if( fmName.width( name ) + text_x + lv->itemMargin()*2 > width )
|
|
|
|
{
|
|
|
|
const int _width = width - text_x - lv->itemMargin()*2;
|
|
|
|
name = KStringHandler::rPixelSqueeze( name, pBuf.fontMetrics(), _width );
|
|
|
|
}
|
|
|
|
|
|
|
|
pBuf.drawText( text_x, 0, width, textHeight, AlignVCenter, name );
|
|
|
|
|
|
|
|
if( !m_subText.isEmpty() )
|
|
|
|
{
|
|
|
|
font.setBold( false );
|
|
|
|
pBuf.setFont( font );
|
|
|
|
|
|
|
|
pBuf.drawText( text_x, fmName.height() + 1, width, textHeight, AlignVCenter, m_subText );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( m_isExpanded )
|
|
|
|
{
|
|
|
|
TQPen pen( cg.highlight(), 1 );
|
|
|
|
pBuf.setPen( pen );
|
|
|
|
int y = textHeight - 1;
|
|
|
|
pBuf.drawLine( 0, y, width, y );
|
|
|
|
}
|
|
|
|
|
|
|
|
pBuf.end();
|
|
|
|
p->drawPixmap( 0, 0, buffer );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQColor
|
|
|
|
StatisticsItem::blendColors( const TQColor& color1, const TQColor& color2, int percent )
|
|
|
|
{
|
|
|
|
const float factor1 = ( 100 - ( float ) percent ) / 100;
|
|
|
|
const float factor2 = ( float ) percent / 100;
|
|
|
|
|
|
|
|
const int r = static_cast<int>( color1.red() * factor1 + color2.red() * factor2 );
|
|
|
|
const int g = static_cast<int>( color1.green() * factor1 + color2.green() * factor2 );
|
|
|
|
const int b = static_cast<int>( color1.blue() * factor1 + color2.blue() * factor2 );
|
|
|
|
|
|
|
|
TQColor result;
|
|
|
|
result.setRgb( r, g, b );
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// CLASS StatisticsDetailedItem
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
StatisticsDetailedItem::StatisticsDetailedItem( const TQString &text, const TQString &subtext, StatisticsItem *parent,
|
|
|
|
StatisticsDetailedItem *after, const char *name )
|
|
|
|
: KListViewItem( parent, after, name )
|
|
|
|
, m_type( NONE )
|
|
|
|
, m_subText( subtext )
|
|
|
|
{
|
|
|
|
setDragEnabled( true );
|
|
|
|
setDropEnabled( false );
|
|
|
|
setSelectable( true );
|
|
|
|
|
|
|
|
setText( 0, text );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsDetailedItem::paintCell( TQPainter *p, const TQColorGroup &cg, int column, int width, int align )
|
|
|
|
{
|
|
|
|
bool showDetails = !m_subText.isEmpty();
|
|
|
|
|
|
|
|
//flicker-free drawing
|
|
|
|
static TQPixmap buffer;
|
|
|
|
buffer.resize( width, height() );
|
|
|
|
|
|
|
|
if( buffer.isNull() )
|
|
|
|
{
|
|
|
|
KListViewItem::paintCell( p, cg, column, width, align );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPainter pBuf( &buffer, true );
|
|
|
|
// use alternate background
|
|
|
|
#if KDE_VERSION < KDE_MAKE_VERSION(3,3,91)
|
|
|
|
pBuf.fillRect( buffer.rect(), isSelected() ? cg.highlight() : backgroundColor() );
|
|
|
|
#else
|
|
|
|
pBuf.fillRect( buffer.rect(), isSelected() ? cg.highlight() : backgroundColor(0) );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
KListView *lv = static_cast<KListView *>( listView() );
|
|
|
|
|
|
|
|
TQFont font( p->font() );
|
|
|
|
TQFontMetrics fm( p->fontMetrics() );
|
|
|
|
|
|
|
|
int text_x = 0;
|
|
|
|
int textHeight;
|
|
|
|
|
|
|
|
if( showDetails )
|
|
|
|
textHeight = fm.lineSpacing() + lv->itemMargin() + 1;
|
|
|
|
else
|
|
|
|
textHeight = height();
|
|
|
|
|
|
|
|
pBuf.setPen( isSelected() ? cg.highlightedText() : cg.text() );
|
|
|
|
|
|
|
|
if( pixmap( column ) )
|
|
|
|
{
|
|
|
|
int y = (textHeight - pixmap(column)->height())/2;
|
|
|
|
if( showDetails ) y++;
|
|
|
|
pBuf.drawPixmap( text_x, y, *pixmap(column) );
|
|
|
|
text_x += pixmap(column)->width() + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
pBuf.setFont( font );
|
|
|
|
TQFontMetrics fmName( font );
|
|
|
|
|
|
|
|
TQString name = text(column);
|
|
|
|
const int _width = width - text_x - lv->itemMargin()*2;
|
|
|
|
if( fmName.width( name ) > _width )
|
|
|
|
{
|
|
|
|
name = KStringHandler::rPixelSqueeze( name, pBuf.fontMetrics(), _width );
|
|
|
|
}
|
|
|
|
|
|
|
|
pBuf.drawText( text_x, 0, width, textHeight, AlignVCenter, name );
|
|
|
|
|
|
|
|
if( showDetails )
|
|
|
|
{
|
|
|
|
const TQColorGroup _cg = listView()->palette().disabled();
|
|
|
|
text_x = lv->treeStepSize() + 3;
|
|
|
|
font.setItalic( true );
|
|
|
|
pBuf.setPen( isSelected() ? _cg.highlightedText() : TQColor(_cg.text().dark()) );
|
|
|
|
pBuf.drawText( text_x, textHeight, width, fm.lineSpacing(), AlignVCenter, m_subText );
|
|
|
|
}
|
|
|
|
|
|
|
|
pBuf.end();
|
|
|
|
p->drawPixmap( 0, 0, buffer );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StatisticsDetailedItem::setup()
|
|
|
|
{
|
|
|
|
TQFontMetrics fm( listView()->font() );
|
|
|
|
int margin = listView()->itemMargin()*2;
|
|
|
|
int h = fm.lineSpacing();
|
|
|
|
if ( h % 2 > 0 )
|
|
|
|
h++;
|
|
|
|
if( !m_subText.isEmpty() )
|
|
|
|
setHeight( h + fm.lineSpacing() + margin );
|
|
|
|
else
|
|
|
|
setHeight( h + margin );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString
|
|
|
|
StatisticsDetailedItem::getSQL()
|
|
|
|
{
|
|
|
|
QueryBuilder qb;
|
|
|
|
TQString query = TQString();
|
|
|
|
TQString artist, album, track; // track is unused here
|
|
|
|
Amarok::albumArtistTrackFromUrl( url(), artist, album, track );
|
|
|
|
|
|
|
|
if( itemType() == StatisticsDetailedItem::ALBUM || itemType() == StatisticsDetailedItem::HISTORY )
|
|
|
|
{
|
|
|
|
qb.initSQLDrag();
|
|
|
|
if ( artist != "0" )
|
|
|
|
qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valArtistID, artist );
|
|
|
|
qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valAlbumID, album );
|
|
|
|
qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber );
|
|
|
|
qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack );
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( itemType() == StatisticsDetailedItem::ARTIST )
|
|
|
|
{
|
|
|
|
const uint artist_id = CollectionDB::instance()->artistID( url() );
|
|
|
|
|
|
|
|
qb.initSQLDrag();
|
|
|
|
qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valArtistID, TQString::number( artist_id ) );
|
|
|
|
qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName );
|
|
|
|
qb.sortBy( QueryBuilder::tabAlbum, QueryBuilder::valName );
|
|
|
|
qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber );
|
|
|
|
qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack );
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( itemType() == StatisticsDetailedItem::GENRE )
|
|
|
|
{
|
|
|
|
const uint genre_id = CollectionDB::instance()->genreID( url() );
|
|
|
|
|
|
|
|
qb.initSQLDrag();
|
|
|
|
qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valGenreID, TQString::number( genre_id ) );
|
|
|
|
qb.sortBy( QueryBuilder::tabArtist, QueryBuilder::valName );
|
|
|
|
qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName );
|
|
|
|
qb.sortBy( QueryBuilder::tabAlbum, QueryBuilder::valName );
|
|
|
|
qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber );
|
|
|
|
qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack );
|
|
|
|
}
|
|
|
|
debug() << "DetailedStatisticsItem: query is: " << qb.query() << endl;
|
|
|
|
|
|
|
|
return qb.query();
|
|
|
|
}
|
|
|
|
|
|
|
|
KURL::List
|
|
|
|
StatisticsDetailedItem::getURLs()
|
|
|
|
{
|
|
|
|
if( itemType() == StatisticsDetailedItem::TRACK )
|
|
|
|
return KURL::List( KURL::fromPathOrURL(url()) );
|
|
|
|
|
|
|
|
QueryBuilder qb;
|
|
|
|
TQString query = TQString();
|
|
|
|
TQString artist, album, track; // track is unused here
|
|
|
|
Amarok::albumArtistTrackFromUrl( m_url, artist, album, track );
|
|
|
|
|
|
|
|
qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL );
|
|
|
|
|
|
|
|
if( itemType() == StatisticsDetailedItem::ALBUM || itemType() == StatisticsDetailedItem::HISTORY )
|
|
|
|
{
|
|
|
|
if ( artist != "0" )
|
|
|
|
qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valArtistID, artist );
|
|
|
|
qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valAlbumID, album );
|
|
|
|
qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack );
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( itemType() == StatisticsDetailedItem::ARTIST )
|
|
|
|
{
|
|
|
|
const uint artist_id = CollectionDB::instance()->artistID( url() );
|
|
|
|
qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valArtistID, TQString::number( artist_id ) );
|
|
|
|
qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack );
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( itemType() == StatisticsDetailedItem::GENRE )
|
|
|
|
{
|
|
|
|
const uint genre_id = CollectionDB::instance()->genreID( url() );
|
|
|
|
qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valGenreID, TQString::number( genre_id ) );
|
|
|
|
qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQStringList values = qb.run();
|
|
|
|
KURL::List urls;
|
|
|
|
foreach( values )
|
|
|
|
urls += KURL::fromPathOrURL( *it );
|
|
|
|
return urls;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "statistics.moc"
|
|
|
|
|