// (c) 2004 Mark Kretschmann <markey@web.de>
// (c) 2004 Christian Muehlhaeuser <chris@chris.de>
// (c) 2005 GÁbor Lehel <illissius@gmail.com>
// (c) 2005 Alexandre Pereira de Oliveira <aleprj@gmail.com>
// (c) 2005 Christan Baumgart <christianbaumgart@web.de>
// (c) 2006 Joe Rabinoff <bobqwatson@yahoo.com>
// See COPYING file for licensing information.
# include <config.h>
# include "amarok.h"
# include "amarokconfig.h"
# include "browserbar.h"
# include "browserToolBar.h"
# include "clicklineedit.h"
# include "collectionbrowser.h"
# include "collectiondb.h"
# include "covermanager.h"
# include "debug.h"
# include "deletedialog.h"
# include "directorylist.h"
# include "editfilterdialog.h"
# include "k3bexporter.h"
# include "mediabrowser.h"
# include "metabundle.h"
# include "mountpointmanager.h"
# include "organizecollectiondialog.h"
# include "playlist.h" //insertMedia()
# include "playlistbrowser.h"
# include "starmanager.h"
# include "statusbar.h"
# include "tagdialog.h"
# include "threadmanager.h"
# include "qstringx.h"
# include <taglib/tfile.h> //TagLib::File::isWritable
# include <unistd.h> //CollectionView ctor
# include <tqapplication.h>
# include <tqcstring.h>
# include <tqdragobject.h>
# include <tqlayout.h> //infobox
# include <tqmap.h>
# include <tqpainter.h>
# include <tqpixmap.h>
# include <tqptrlist.h>
# include <tqpushbutton.h>
# include <tqsimplerichtext.h>
# include <tqtimer.h>
# include <tqtooltip.h> //TQToolTip::add()
# include <tqheader.h>
# include <tqregexp.h>
# include <tdeactioncollection.h>
# include <kapplication.h> //kapp
# include <tdeconfig.h>
# include <kcombobox.h>
# include <kcursor.h>
# include <kdialogbase.h>
# include <kglobal.h>
# include <kiconloader.h> //renderView()
# include <klocale.h>
# include <kmessagebox.h>
# include <tdepopupmenu.h>
# include <tdetoolbarbutton.h> //ctor
# include <kurldrag.h> //dragObject()
# include <tdeio/job.h>
# include <kpushbutton.h>
extern " C "
{
# if TDE_VERSION < TDE_MAKE_VERSION(3,3,91)
# include <X11/Xlib.h> //ControlMask in contentsDragMoveEvent()
# endif
}
using namespace CollectionBrowserIds ;
namespace Amarok { extern TDEConfig * config ( const TQString & ) ; }
class CoverFetcher ;
CollectionBrowser * CollectionBrowser : : s_instance = 0 ;
CollectionBrowser : : CollectionBrowser ( const char * name )
: TQVBox ( 0 , name )
, m_cat1Menu ( new TDEPopupMenu ( this ) )
, m_cat2Menu ( new TDEPopupMenu ( this ) )
, m_cat3Menu ( new TDEPopupMenu ( this ) )
, m_timer ( new TQTimer ( this ) )
, m_returnPressed ( false )
{
s_instance = this ;
setSpacing ( 4 ) ;
m_toolbar = new Browser : : ToolBar ( this ) ;
{ //<Search LineEdit>
TDEToolBarButton * button ;
TDEToolBar * searchToolBar = new Browser : : ToolBar ( this ) ;
button = new TDEToolBarButton ( " locationbar_erase " , 0 , searchToolBar ) ;
m_searchEdit = new ClickLineEdit ( i18n ( " Enter search terms here " ) , searchToolBar ) ;
m_searchEdit - > installEventFilter ( this ) ;
KPushButton * filterButton = new KPushButton ( " ... " , searchToolBar , " filter " ) ;
searchToolBar - > setStretchableWidget ( m_searchEdit ) ;
m_searchEdit - > setFrame ( TQFrame : : Sunken ) ;
connect ( button , TQT_SIGNAL ( clicked ( ) ) , TQT_SLOT ( slotClearFilter ( ) ) ) ;
connect ( filterButton , TQT_SIGNAL ( clicked ( ) ) , TQT_SLOT ( slotEditFilter ( ) ) ) ;
TQToolTip : : add ( button , i18n ( " Clear search field " ) ) ;
TQToolTip : : add ( m_searchEdit , i18n ( " Enter space-separated terms to search in the collection " ) ) ;
TQToolTip : : add ( filterButton , i18n ( " Click to edit collection filter " ) ) ;
} //</Search LineEdit>
// We put a little toolbar for the forward/back buttons for iPod
// navigation to the right of m_timeFilter. This toolbar is
// hidden when not in iPod browsing mode; it is shown and hidden
// in CollectionView::setViewMode(). m_ipodHbox holds m_timeFilter
// and m_ipodToolbar
m_ipodHbox = new TQHBox ( this ) ;
m_ipodHbox - > setSpacing ( 7 ) ; // looks better
m_timeFilter = new KComboBox ( m_ipodHbox , " timeFilter " ) ;
m_ipodHbox - > setStretchFactor ( m_timeFilter , 1 ) ;
// Allow the combobox to shrink so the iPod buttons are still visible
m_timeFilter - > setSizePolicy ( TQSizePolicy : : Expanding , TQSizePolicy : : Fixed ) ;
m_timeFilter - > insertItem ( i18n ( " Entire Collection " ) ) ;
m_timeFilter - > insertItem ( i18n ( " Added Today " ) ) ;
m_timeFilter - > insertItem ( i18n ( " Added Within One Week " ) ) ;
m_timeFilter - > insertItem ( i18n ( " Added Within One Month " ) ) ;
m_timeFilter - > insertItem ( i18n ( " Added Within Three Months " ) ) ;
m_timeFilter - > insertItem ( i18n ( " Added Within One Year " ) ) ;
// m_ipodToolbar just holds the forward and back buttons, which are
// plugged below
m_ipodToolbar = new Browser : : ToolBar ( m_ipodHbox ) ;
m_ipodHbox - > setStretchFactor ( m_ipodToolbar , 0 ) ;
m_ipodToolbar - > setIconText ( TDEToolBar : : IconOnly , false ) ;
TDEActionCollection * ac = new TDEActionCollection ( this ) ;
m_view = new CollectionView ( this ) ;
m_view - > installEventFilter ( this ) ;
m_configureAction = new TDEAction ( i18n ( " Configure Folders " ) , Amarok : : icon ( " configure " ) , 0 , TQT_TQOBJECT ( this ) , TQT_SLOT ( setupDirs ( ) ) , ac , " Configure " ) ;
m_treeViewAction = new TDERadioAction ( i18n ( " Tree View " ) , " view_tree " , 0 , TQT_TQOBJECT ( m_view ) , TQT_SLOT ( setTreeMode ( ) ) , ac , " Tree View " ) ;
m_flatViewAction = new TDERadioAction ( i18n ( " Flat View " ) , " view_detailed " , 0 , TQT_TQOBJECT ( m_view ) , TQT_SLOT ( setFlatMode ( ) ) , ac , " Flat View " ) ;
m_ipodViewAction = new TDERadioAction ( i18n ( " iPod View " ) , Amarok : : icon ( " device " ) , 0 , TQT_TQOBJECT ( m_view ) , TQT_SLOT ( setIpodMode ( ) ) , ac , " iPod View " ) ;
m_treeViewAction - > setExclusiveGroup ( " view mode " ) ;
m_flatViewAction - > setExclusiveGroup ( " view mode " ) ;
m_ipodViewAction - > setExclusiveGroup ( " view mode " ) ;
switch ( m_view - > m_viewMode )
{
case CollectionView : : modeTreeView :
m_treeViewAction - > setChecked ( true ) ;
break ;
case CollectionView : : modeFlatView :
m_flatViewAction - > setChecked ( true ) ;
break ;
case CollectionView : : modeIpodView :
m_ipodViewAction - > setChecked ( true ) ;
break ;
}
m_showDividerAction = new TDEToggleAction ( i18n ( " Show Divider " ) , " leftjust " , 0 , TQT_TQOBJECT ( this ) , TQT_SLOT ( toggleDivider ( ) ) , ac , " Show Divider " ) ;
m_showDividerAction - > setChecked ( m_view - > m_showDivider ) ;
// m_ipodIncrement and m_ipodDecrement are the actions that
// correspond to moving forward / backward in the iPod collection
// browser window; see the "For iPod-style navigation" comments below.
m_ipodDecrement = new TDEAction ( i18n ( " Browse backward " ) ,
TQIconSet ( m_view - > ipodDecrementIcon ( ) , TQIconSet : : Small ) ,
0 , TQT_TQOBJECT ( m_view ) , TQT_SLOT ( decrementDepth ( ) ) , ac ,
" iPod Decrement " ) ;
m_ipodIncrement = new TDEAction ( i18n ( " Browse forward " ) ,
TQIconSet ( m_view - > ipodIncrementIcon ( ) , TQIconSet : : Small ) ,
0 , TQT_TQOBJECT ( m_view ) , TQT_SLOT ( incrementDepth ( ) ) , ac ,
" iPod Increment " ) ;
m_ipodDecrement - > plug ( m_ipodToolbar ) ;
m_ipodIncrement - > plug ( m_ipodToolbar ) ;
// Show / hide m_ipodToolbar based on the view mode
ipodToolbar ( m_view - > m_viewMode = = CollectionView : : modeIpodView ) ;
m_tagfilterMenuButton = new TDEActionMenu ( i18n ( " Group By " ) , " filter " , ac ) ;
m_tagfilterMenuButton - > setDelayed ( false ) ;
// FIXME: either both or nothing
//m_tagfilterMenuButton->setEnabled( m_view->m_viewMode == CollectionView::modeTreeView );
//connect ( m_treeViewAction, TQT_SIGNAL ( toggled(bool) ), m_tagfilterMenuButton, TQT_SLOT( setEnabled (bool) ) );
layoutToolbar ( ) ;
m_categoryMenu = m_tagfilterMenuButton - > popupMenu ( ) ;
m_categoryMenu - > insertItem ( i18n ( " Artist " ) , m_view , TQT_SLOT ( presetMenu ( int ) ) , 0 , IdArtist ) ;
m_categoryMenu - > insertItem ( i18n ( " Artist / Album " ) , m_view , TQT_SLOT ( presetMenu ( int ) ) , 0 , IdArtistAlbum ) ;
m_categoryMenu - > insertItem ( i18n ( " Artist " ) + " / " + i18n ( " Year " ) + i18n ( " - " ) + i18n ( " Album " ) , m_view , TQT_SLOT ( presetMenu ( int ) ) , 0 , IdArtistVisYearAlbum ) ;
m_categoryMenu - > insertItem ( i18n ( " Album " ) , m_view , TQT_SLOT ( presetMenu ( int ) ) , 0 , IdAlbum ) ;
m_categoryMenu - > insertItem ( i18n ( " Genre / Artist " ) , m_view , TQT_SLOT ( presetMenu ( int ) ) , 0 , IdGenreArtist ) ;
m_categoryMenu - > insertItem ( i18n ( " Genre / Artist / Album " ) , m_view , TQT_SLOT ( presetMenu ( int ) ) , 0 , IdGenreArtistAlbum ) ;
m_categoryMenu - > insertSeparator ( ) ;
m_categoryMenu - > insertItem ( i18n ( " &First Level " ) , m_cat1Menu ) ;
m_categoryMenu - > insertItem ( i18n ( " &Second Level " ) , m_cat2Menu ) ;
m_categoryMenu - > insertItem ( i18n ( " &Third Level " ) , m_cat3Menu ) ;
m_cat1Menu - > insertItem ( i18n ( " &Album " ) , m_view , TQT_SLOT ( cat1Menu ( int ) ) , 0 , IdAlbum ) ;
m_cat1Menu - > insertItem ( i18n ( " (Y&ear) - Album " ) , m_view , TQT_SLOT ( cat1Menu ( int ) ) , 0 , IdVisYearAlbum ) ;
m_cat1Menu - > insertItem ( i18n ( " A&rtist " ) , m_view , TQT_SLOT ( cat1Menu ( int ) ) , 0 , IdArtist ) ;
m_cat1Menu - > insertItem ( i18n ( " &Composer " ) , m_view , TQT_SLOT ( cat1Menu ( int ) ) , 0 , IdComposer ) ;
m_cat1Menu - > insertItem ( i18n ( " &Genre " ) , m_view , TQT_SLOT ( cat1Menu ( int ) ) , 0 , IdGenre ) ;
m_cat1Menu - > insertItem ( i18n ( " &Year " ) , m_view , TQT_SLOT ( cat1Menu ( int ) ) , 0 , IdYear ) ;
m_cat1Menu - > insertItem ( i18n ( " &Label " ) , m_view , TQT_SLOT ( cat1Menu ( int ) ) , 0 , IdLabel ) ;
m_cat2Menu - > insertItem ( i18n ( " &None " ) , m_view , TQT_SLOT ( cat2Menu ( int ) ) , 0 , IdNone ) ;
m_cat2Menu - > insertSeparator ( ) ;
m_cat2Menu - > insertItem ( i18n ( " &Album " ) , m_view , TQT_SLOT ( cat2Menu ( int ) ) , 0 , IdAlbum ) ;
m_cat2Menu - > insertItem ( i18n ( " (Y&ear) - Album " ) , m_view , TQT_SLOT ( cat2Menu ( int ) ) , 0 , IdVisYearAlbum ) ;
m_cat2Menu - > insertItem ( i18n ( " A&rtist " ) , m_view , TQT_SLOT ( cat2Menu ( int ) ) , 0 , IdArtist ) ;
m_cat2Menu - > insertItem ( i18n ( " &Composer " ) , m_view , TQT_SLOT ( cat2Menu ( int ) ) , 0 , IdComposer ) ;
m_cat2Menu - > insertItem ( i18n ( " &Genre " ) , m_view , TQT_SLOT ( cat2Menu ( int ) ) , 0 , IdGenre ) ;
m_cat2Menu - > insertItem ( i18n ( " &Year " ) , m_view , TQT_SLOT ( cat2Menu ( int ) ) , 0 , IdYear ) ;
m_cat2Menu - > insertItem ( i18n ( " &Label " ) , m_view , TQT_SLOT ( cat2Menu ( int ) ) , 0 , IdLabel ) ;
m_cat3Menu - > insertItem ( i18n ( " &None " ) , m_view , TQT_SLOT ( cat3Menu ( int ) ) , 0 , IdNone ) ;
m_cat3Menu - > insertSeparator ( ) ;
m_cat3Menu - > insertItem ( i18n ( " A&lbum " ) , m_view , TQT_SLOT ( cat3Menu ( int ) ) , 0 , IdAlbum ) ;
m_cat3Menu - > insertItem ( i18n ( " (Y&ear) - Album " ) , m_view , TQT_SLOT ( cat3Menu ( int ) ) , 0 , IdVisYearAlbum ) ;
m_cat3Menu - > insertItem ( i18n ( " A&rtist " ) , m_view , TQT_SLOT ( cat3Menu ( int ) ) , 0 , IdArtist ) ;
m_cat3Menu - > insertItem ( i18n ( " &Composer " ) , m_view , TQT_SLOT ( cat3Menu ( int ) ) , 0 , IdComposer ) ;
m_cat3Menu - > insertItem ( i18n ( " &Genre " ) , m_view , TQT_SLOT ( cat3Menu ( int ) ) , 0 , IdGenre ) ;
m_cat3Menu - > insertItem ( i18n ( " &Year " ) , m_view , TQT_SLOT ( cat3Menu ( int ) ) , 0 , IdYear ) ;
m_cat3Menu - > insertItem ( i18n ( " &Label " ) , m_view , TQT_SLOT ( cat3Menu ( int ) ) , 0 , IdLabel ) ;
m_view - > cat1Menu ( m_view - > m_cat1 , false ) ;
m_view - > cat2Menu ( m_view - > m_cat2 , false ) ;
m_view - > cat3Menu ( m_view - > m_cat3 , false ) ;
m_view - > setViewMode ( m_view - > m_viewMode ) ;
connect ( m_timer , TQT_SIGNAL ( timeout ( ) ) , TQT_SLOT ( slotSetFilter ( ) ) ) ;
connect ( m_searchEdit , TQT_SIGNAL ( textChanged ( const TQString & ) ) , TQT_SLOT ( slotSetFilterTimeout ( ) ) ) ;
connect ( m_timeFilter , TQT_SIGNAL ( activated ( int ) ) , TQT_SLOT ( slotSetFilter ( ) ) ) ;
setFocusProxy ( m_view ) ; //default object to get focus
}
void
CollectionBrowser : : slotClearFilter ( ) //SLOT
{
m_searchEdit - > clear ( ) ;
kapp - > processEvents ( ) ; //Let the search bar redraw fully.
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( slotSetFilter ( ) ) ) ; //Filter instantly
TQTimer : : singleShot ( 0 , m_view , TQT_SLOT ( slotEnsureSelectedItemVisible ( ) ) ) ;
}
void
CollectionBrowser : : slotSetFilterTimeout ( ) //SLOT
{
m_returnPressed = false ;
m_timer - > start ( 280 , true ) ; //stops the timer for us first
}
void
CollectionBrowser : : slotSetFilter ( ) //SLOT
{
m_timer - > stop ( ) ;
m_view - > m_dirty = true ;
m_view - > setFilter ( m_searchEdit - > text ( ) ) ;
m_view - > setTimeFilter ( m_timeFilter - > currentItem ( ) ) ;
m_view - > renderView ( ) ;
if ( m_returnPressed )
appendSearchResults ( ) ;
m_returnPressed = false ;
}
void
CollectionBrowser : : slotSetFilter ( const TQString & filter ) //SLOT
{
m_searchEdit - > setText ( filter ) ;
kapp - > processEvents ( ) ; //Let the search bar redraw fully.
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( slotSetFilter ( ) ) ) ; //Filter instantly
TQTimer : : singleShot ( 0 , m_view , TQT_SLOT ( slotEnsureSelectedItemVisible ( ) ) ) ;
}
void
CollectionBrowser : : slotEditFilter ( ) //SLOT
{
EditFilterDialog * cod = new EditFilterDialog ( this , false , m_searchEdit - > text ( ) ) ;
connect ( cod , TQT_SIGNAL ( filterChanged ( const TQString & ) ) , TQT_SLOT ( slotSetFilter ( const TQString & ) ) ) ;
if ( cod - > exec ( ) )
m_searchEdit - > setText ( cod - > filter ( ) ) ;
delete cod ;
}
void
CollectionBrowser : : setupDirs ( ) //SLOT
{
m_view - > setupDirs ( ) ;
}
void
CollectionBrowser : : toggleDivider ( ) //SLOT
{
m_view - > setShowDivider ( m_showDividerAction - > isChecked ( ) ) ;
}
void
CollectionBrowser : : appendSearchResults ( )
{
//If we are not filtering, or the search string has changed recently, do nothing
if ( m_searchEdit - > text ( ) . stripWhiteSpace ( ) . isEmpty ( ) | | m_timer - > isActive ( ) )
return ;
m_view - > selectAll ( ) ;
Playlist : : instance ( ) - > insertMedia ( m_view - > listSelected ( ) , Playlist : : Unique | Playlist : : Append ) ;
m_view - > clearSelection ( ) ;
slotClearFilter ( ) ;
}
bool
CollectionBrowser : : eventFilter ( TQObject * o , TQEvent * e )
{
switch ( e - > type ( ) )
{
case 6 /*TQEvent::KeyPress*/ :
//there are a few keypresses that we intercept
# define e TQT_TQKEYEVENT(e)
if ( TQT_BASE_OBJECT ( o ) = = TQT_BASE_OBJECT ( m_searchEdit ) ) //the search lineedit
{
switch ( e - > key ( ) )
{
case Key_Up :
case Key_Down :
case Key_PageDown :
case Key_PageUp :
m_view - > setFocus ( ) ;
TQApplication : : sendEvent ( m_view , e ) ;
return true ;
case Key_Escape :
slotClearFilter ( ) ;
return true ;
case Key_Return :
case Key_Enter :
if ( m_timer - > isActive ( ) )
{
//Immediately filter and add results
m_timer - > stop ( ) ;
m_returnPressed = true ;
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( slotSetFilter ( ) ) ) ;
}
else
{
//Add current results
appendSearchResults ( ) ;
}
return true ;
default :
return false ;
}
}
// (Joe Rabinoff) the code that was here which dealt with wrapping
// the selection around when Key_Up or Key_Down was pressed was
// moved to CollectionView::keyPressEvent(). That code also
// skips dividers.
if ( ( e - > key ( ) > = Key_0 & & e - > key ( ) < = Key_Z ) | | e - > key ( ) = = Key_Backspace | | e - > key ( ) = = Key_Escape )
{
m_searchEdit - > setFocus ( ) ;
TQApplication : : sendEvent ( m_searchEdit , e ) ;
return true ;
}
# undef e
break ;
default :
break ;
}
return TQVBox : : eventFilter ( o , e ) ;
}
void
CollectionBrowser : : layoutToolbar ( )
{
if ( ! m_toolbar ) return ;
m_toolbar - > clear ( ) ;
m_toolbar - > setIconText ( TDEToolBar : : IconTextRight , false ) ;
m_tagfilterMenuButton - > plug ( m_toolbar ) ;
m_toolbar - > setIconText ( TDEToolBar : : IconOnly , false ) ;
m_toolbar - > insertLineSeparator ( ) ;
m_treeViewAction - > plug ( m_toolbar ) ;
m_flatViewAction - > plug ( m_toolbar ) ;
m_ipodViewAction - > plug ( m_toolbar ) ;
m_toolbar - > insertLineSeparator ( ) ;
m_showDividerAction - > plug ( m_toolbar ) ;
m_configureAction - > plug ( m_toolbar ) ;
//This would break things if the toolbar is too big, see bug #121915
//setMinimumWidth( m_toolbar->sizeHint().width() + 2 ); //set a reasonable minWidth
}
// (De)activate the iPod toolbar when switching into and out of
// iPod browsing mode
void
CollectionBrowser : : ipodToolbar ( bool activate )
{
if ( activate )
m_ipodToolbar - > show ( ) ;
else
m_ipodToolbar - > hide ( ) ;
}
//////////////////////////////////////////////////////////////////////////////////////////
// CLASS CollectionView
//////////////////////////////////////////////////////////////////////////////////////////
CollectionView * CollectionView : : m_instance = 0 ;
CollectionView : : CollectionView ( CollectionBrowser * parent )
: TDEListView ( parent )
, m_parent ( parent )
, m_timeFilter ( 0 )
, m_currentDepth ( 0 )
, m_ipodIncremented ( 1 )
, m_dirty ( true )
, m_organizingFileCancelled ( false )
{
DEBUG_FUNC_INFO
m_instance = this ;
setSelectionMode ( TQListView : : Extended ) ;
setItemsMovable ( false ) ;
setSorting ( 0 ) ;
setShowSortIndicator ( true ) ;
setAcceptDrops ( true ) ;
setAllColumnsShowFocus ( true ) ;
//<READ CONFIG>
TDEConfig * config = Amarok : : config ( " Collection Browser " ) ;
m_cat1 = config - > readNumEntry ( " Category1 " , IdArtist ) ;
m_cat2 = config - > readNumEntry ( " Category2 " , IdAlbum ) ;
m_cat3 = config - > readNumEntry ( " Category3 " , IdNone ) ;
# define saneCat(x) (x==IdAlbum||x==IdArtist||x==IdComposer||x==IdGenre||x==IdYear \
| | x = = IdNone \
| | x = = IdArtistAlbum | | x = = IdGenreArtist | | x = = IdGenreArtistAlbum | | x = = IdVisYearAlbum | | x = = IdArtistVisYearAlbum )
if ( ! saneCat ( m_cat1 ) )
{
m_cat1 = IdArtist ;
m_cat2 = IdAlbum ;
m_cat2 = IdNone ;
}
if ( ! saneCat ( m_cat2 ) | | ! saneCat ( m_cat3 ) )
{
m_cat2 = m_cat3 = IdNone ;
}
# undef saneCat
m_viewMode = config - > readNumEntry ( " ViewMode " , modeTreeView ) ;
m_showDivider = config - > readBoolEntry ( " ShowDivider " , true ) ;
updateTrackDepth ( ) ;
m_flatColumnWidths . clear ( ) ;
TQStringList flatWidths = config - > readListEntry ( " FlatColumnWidths " ) ;
for ( TQStringList : : iterator it = flatWidths . begin ( ) ;
it ! = flatWidths . end ( ) ;
it + + )
m_flatColumnWidths . push_back ( ( * it ) . toInt ( ) ) ;
//</READ CONFIG>
TDEActionCollection * ac = new TDEActionCollection ( this ) ;
KStdAction : : selectAll ( TQT_TQOBJECT ( this ) , TQT_SLOT ( selectAll ( ) ) , ac , " collectionview_select_all " ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( scanStarted ( ) ) ,
this , TQT_SLOT ( scanStarted ( ) ) ) ;
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( scanDone ( bool ) ) ,
this , TQT_SLOT ( scanDone ( bool ) ) ) ;
connect ( BrowserBar : : instance ( ) , TQT_SIGNAL ( browserActivated ( int ) ) ,
this , TQT_SLOT ( renderView ( ) ) ) ; // renderView() checks if current tab is this
connect ( CollectionDB : : instance ( ) , TQT_SIGNAL ( ratingChanged ( const TQString & , int ) ) ,
this , TQT_SLOT ( ratingChanged ( const TQString & , int ) ) ) ;
connect ( this , TQT_SIGNAL ( expanded ( TQListViewItem * ) ) ,
this , TQT_SLOT ( slotExpand ( TQListViewItem * ) ) ) ;
connect ( this , TQT_SIGNAL ( collapsed ( TQListViewItem * ) ) ,
this , TQT_SLOT ( slotCollapse ( TQListViewItem * ) ) ) ;
connect ( this , TQT_SIGNAL ( returnPressed ( TQListViewItem * ) ) ,
this , TQT_SLOT ( invokeItem ( TQListViewItem * ) ) ) ;
connect ( this , TQT_SIGNAL ( doubleClicked ( TQListViewItem * , const TQPoint & , int ) ) ,
this , TQT_SLOT ( invokeItem ( TQListViewItem * , const TQPoint & , int ) ) ) ;
connect ( this , TQT_SIGNAL ( clicked ( TQListViewItem * , const TQPoint & , int ) ) ,
this , TQT_SLOT ( ipodItemClicked ( TQListViewItem * , const TQPoint & , int ) ) ) ;
connect ( this , TQT_SIGNAL ( contextMenuRequested ( TQListViewItem * , const TQPoint & , int ) ) ,
this , TQT_SLOT ( rmbPressed ( TQListViewItem * , const TQPoint & , int ) ) ) ;
connect ( header ( ) , TQT_SIGNAL ( sizeChange ( int , int , int ) ) ,
this , TQT_SLOT ( triggerUpdate ( ) ) ) ;
connect ( MountPointManager : : instance ( ) , TQT_SIGNAL ( mediumConnected ( int ) ) ,
this , TQT_SLOT ( databaseChanged ( ) ) ) ;
connect ( MountPointManager : : instance ( ) , TQT_SIGNAL ( mediumRemoved ( int ) ) ,
this , TQT_SLOT ( databaseChanged ( ) ) ) ;
}
CollectionView : : ~ CollectionView ( ) {
DEBUG_FUNC_INFO
TDEConfig * const config = Amarok : : config ( " Collection Browser " ) ;
config - > writeEntry ( " Category1 " , m_cat1 ) ;
config - > writeEntry ( " Category2 " , m_cat2 ) ;
config - > writeEntry ( " Category3 " , m_cat3 ) ;
config - > writeEntry ( " ViewMode " , m_viewMode ) ;
config - > writeEntry ( " ShowDivider " , m_showDivider ) ;
TQStringList flatWidths ;
for ( TQValueList < int > : : iterator it = m_flatColumnWidths . begin ( ) ;
it ! = m_flatColumnWidths . end ( ) ;
it + + )
flatWidths . push_back ( TQString : : number ( ( * it ) ) ) ;
config - > writeEntry ( " FlatColumnWidths " , flatWidths ) ;
}
void
CollectionView : : setShowDivider ( bool show )
{
if ( show ! = m_showDivider ) {
m_showDivider = show ;
renderView ( true ) ;
}
}
// Reimplemented for iPod-style navigation, and to skip dividers
// Specifically, this method traps the Key_Up/Down/Left/Right events.
// When Up or Down is pressed, it skips dividers and wraps around when
// necessary. When Left or Right is pressed and we are viewing in
// iPod mode, the iPod "move forward / backward" actions are activated.
void
CollectionView : : keyPressEvent ( TQKeyEvent * e )
{
typedef TQListViewItemIterator It ;
// Reimplement up and down to skip dividers and to loop around.
// Some of this code used to be in CollectionBrowser::eventFilter.
// This rewritten code is more faithful to the ordinary moving
// behavior, even when looping around. (For instance, it behaves
// correctly if control-up is pressed at the top of the screen.)
// It sends fake keypress events to the parent instead of programatically
// selecting items.
if ( ( e - > key ( ) = = Key_Up | | e - > key ( ) = = Key_Down ) & & currentItem ( ) )
{
// Handle both up and down at once to avoid code duplication (it's
// a delicate piece of logic, and was hard to get right)
TQListViewItem * cur = currentItem ( ) ;
# define nextItem (e->key() == Key_Up ? cur->itemAbove() : cur->itemBelow())
bool wraparound = true ;
// First skip any dividers directly above / below
do
{
TDEListView : : keyPressEvent ( e ) ;
if ( currentItem ( ) = = cur ) // Prevent infinite loops
{
if ( nextItem ! = 0 )
wraparound = false ;
break ;
}
cur = currentItem ( ) ;
if ( cur & & dynamic_cast < DividerItem * > ( cur ) = = 0 )
wraparound = false ; // Found an item above / below
} while ( cur ! = NULL
& & dynamic_cast < DividerItem * > ( cur ) ! = 0
& & nextItem ! = 0 ) ;
if ( cur = = 0 ) return ; // Shouldn't happen
// Wrap around if necessary, by sending a Key_Home/Key_End event.
if ( wraparound )
{
TQKeyEvent e2 ( e - > type ( ) ,
( e - > key ( ) = = Key_Up ? Key_End : Key_Home ) ,
0 , e - > state ( ) ,
TQString ( ) , e - > isAutoRepeat ( ) , e - > count ( ) ) ;
TQApplication : : sendEvent ( this , & e2 ) ;
cur = currentItem ( ) ;
// The first item may also be a divider, so keep moving
// until it's not
while ( cur ! = 0
& & dynamic_cast < DividerItem * > ( cur ) ! = 0
& & nextItem ! = 0 )
{
TDEListView : : keyPressEvent ( e ) ;
if ( currentItem ( ) = = cur ) // Prevent infinite loops
break ;
cur = currentItem ( ) ;
}
}
# undef nextItem
}
// When Right/Left is pressed in iPod view mode, activate the iPod
// "move forward/backward" action.
else if ( ( e - > key ( ) = = Key_Left | | e - > key ( ) = = Key_Right )
& & m_viewMode = = modeIpodView )
{
if ( e - > key ( ) = = Key_Right )
m_parent - > m_ipodIncrement - > activate ( ) ;
else if ( e - > key ( ) = = Key_Left )
m_parent - > m_ipodDecrement - > activate ( ) ;
}
else // we don't want the event
TDEListView : : keyPressEvent ( e ) ;
}
//////////////////////////////////////////////////////////////////////////////////////////
// public slots
//////////////////////////////////////////////////////////////////////////////////////////
void
CollectionView : : renderView ( bool force /* = false */ ) //SLOT
{
SHOULD_BE_GUI
if ( ! force & & ! m_dirty )
return ;
if ( BrowserBar : : instance ( ) - > currentBrowser ( ) ! = m_parent )
{
// the collectionbrowser is intensive for sql, so we only renderView() if the tab
// is currently active. else, wait until user focuses it.
// debug() << "current browser is not collection, aborting renderView()" << endl;
m_dirty = true ;
return ;
}
m_dirty = false ;
// Don't cache / restore view if we're in ipod mode and we've
// just incremented or decremented, since we'll run selectIpodItems()
// below anyway.
if ( childCount ( ) & &
! ( m_viewMode = = modeIpodView & & m_ipodIncremented > 0 ) )
cacheView ( ) ;
//clear();
safeClear ( ) ;
if ( m_viewMode = = modeFlatView )
{
renderFlatModeView ( force ) ;
}
if ( m_viewMode = = modeIpodView )
{
renderIpodModeView ( force ) ;
}
if ( m_viewMode = = modeTreeView )
{
renderTreeModeView ( force ) ;
}
// Don't cache or restore view when we're just going to run
// selectIpodItems() below anyway.
if ( ! ( m_viewMode = = modeIpodView & & m_ipodIncremented > 0 ) )
restoreView ( ) ;
else
selectIpodItems ( ) ;
}
//////////////////////////////////////////////////////////////////////////////////////////
// private slots
//////////////////////////////////////////////////////////////////////////////////////////
void
CollectionView : : setupDirs ( ) //SLOT
{
KDialogBase dialog ( this , 0 , false ) ;
kapp - > setTopWidget ( & dialog ) ;
dialog . setCaption ( kapp - > makeStdCaption ( i18n ( " Configure Collection " ) ) ) ;
CollectionSetup * setup = new CollectionSetup ( & dialog ) ;
dialog . setMainWidget ( setup ) ;
dialog . showButtonApply ( false ) ;
dialog . adjustSize ( ) ;
// Make the dialog a bit bigger, default is too small to be useful
dialog . resize ( dialog . width ( ) + 50 , dialog . height ( ) + 150 ) ;
if ( dialog . exec ( ) ! = TQDialog : : Rejected )
{
const bool rescan = ( MountPointManager : : instance ( ) - > collectionFolders ( ) ! = setup - > dirs ( ) ) ;
setup - > writeConfig ( ) ;
if ( rescan )
CollectionDB : : instance ( ) - > startScan ( ) ;
}
}
void
CollectionView : : scanStarted ( ) // SLOT
{
Amarok : : actionCollection ( ) - > action ( " update_collection " ) - > setEnabled ( false ) ;
}
void
CollectionView : : scanDone ( bool changed ) //SLOT
{
if ( changed )
{
renderView ( true ) ;
}
Amarok : : actionCollection ( ) - > action ( " update_collection " ) - > setEnabled ( true ) ;
}
void
CollectionView : : slotEnsureSelectedItemVisible ( ) //SLOT
{
//Scroll to make sure the first selected item is visible
//Find the first selected item
TQListViewItem * r = 0 ;
for ( TQListViewItem * i = firstChild ( ) ; i & & ! r ; i = i - > nextSibling ( ) )
{
if ( i - > isSelected ( ) )
r = i ;
for ( TQListViewItem * j = i - > firstChild ( ) ; j & & ! r ; j = j - > nextSibling ( ) )
{
if ( j - > isSelected ( ) )
r = j ;
for ( TQListViewItem * k = j - > firstChild ( ) ; k & & ! r ; k = k - > nextSibling ( ) )
{
if ( k - > isSelected ( ) )
r = k ;
}
}
}
if ( r )
{
//We've found the selected item. Now let's refocus on it.
//An elaborate agorithm to try to make as much as possible of the vicinity visible
//It looks better if things end up consistently in one place.
//So, scroll to the end so that we come at items from the bottom.
if ( lastChild ( ) )
ensureItemVisible ( lastChild ( ) ) ;
//Create a reverse list of parents, grandparents etc.
//Later we try to make the grandparents in view, then their children etc.
//This means that the selected item has the most priority as it is done last.
TQValueStack < TQListViewItem * > parents ;
while ( r )
{
parents . push ( r ) ;
r = r - > parent ( ) ;
}
while ( ! parents . isEmpty ( ) )
{
//We would prefer the next item to be visible.
if ( parents . top ( ) - > nextSibling ( ) )
ensureItemVisible ( parents . top ( ) - > nextSibling ( ) ) ;
//It's even more important the actual item is visible than the next one.
ensureItemVisible ( parents . top ( ) ) ;
parents . pop ( ) ;
}
}
}
void
CollectionView : : slotExpand ( TQListViewItem * item ) //SLOT
{
if ( ! item | | ! item - > isExpandable ( ) ) return ;
int category = 0 ;
TQStringList values ;
QueryBuilder qb ;
bool c = false ;
bool SortbyTrackFirst = false ;
//Sort by track number first if album is in one of the categories, otherwise by track name first
if ( m_cat1 = = IdAlbum | |
m_cat2 = = IdAlbum | |
m_cat3 = = IdAlbum )
SortbyTrackFirst = true ;
// initialization for year - album mode
TQString tmptext ;
int VisYearAlbum = - 1 ;
int VisLabel = - 1 ;
int q_cat1 = m_cat1 ;
int q_cat2 = m_cat2 ;
int q_cat3 = m_cat3 ;
if ( m_cat1 = = IdVisYearAlbum | |
m_cat2 = = IdVisYearAlbum | |
m_cat3 = = IdVisYearAlbum )
{
SortbyTrackFirst = true ;
if ( m_cat1 = = IdVisYearAlbum )
{
VisYearAlbum = 1 ;
q_cat1 = IdAlbum ;
}
if ( m_cat2 = = IdVisYearAlbum )
{
VisYearAlbum = 2 ;
q_cat2 = IdAlbum ;
}
if ( m_cat3 = = IdVisYearAlbum )
{
VisYearAlbum = 3 ;
q_cat3 = IdAlbum ;
}
}
if ( m_cat1 = = IdLabel | |
m_cat2 = = IdLabel | |
m_cat3 = = IdLabel )
{
if ( m_cat1 = = IdLabel )
VisLabel = 1 ;
if ( m_cat2 = = IdLabel )
VisLabel = 2 ;
if ( m_cat3 = = IdLabel )
VisLabel = 3 ;
}
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate , TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) - translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
TQString itemText ;
bool isUnknown ;
if ( dynamic_cast < CollectionItem * > ( item ) )
{
itemText = static_cast < CollectionItem * > ( item ) - > getSQLText ( 0 ) ;
}
else
{
debug ( ) < < " slotExpand in CollectionView of a non-CollectionItem " < < endl ;
itemText = item - > text ( 0 ) ;
}
switch ( item - > depth ( ) )
{
case 0 :
tmptext = itemText ;
isUnknown = tmptext . isEmpty ( ) ;
if ( ! static_cast < CollectionItem * > ( item ) - > isSampler ( ) )
{
if ( m_cat1 = = IdArtist )
qb . setOptions ( QueryBuilder : : optNoCompilations ) ;
if ( VisYearAlbum = = 1 )
{
tmptext = item - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( isUnknown )
tmptext = " " ;
}
qb . addMatch ( q_cat1 , tmptext , false , true ) ;
}
else
{
qb . setOptions ( QueryBuilder : : optOnlyCompilations ) ;
c = true ;
}
if ( m_cat2 = = QueryBuilder : : tabSong )
{
qb . addReturnValue ( q_cat2 , QueryBuilder : : valTitle , true ) ;
qb . addReturnValue ( q_cat2 , QueryBuilder : : valURL ) ;
if ( c ) qb . addReturnValue ( QueryBuilder : : tabArtist , QueryBuilder : : valName , true ) ;
if ( SortbyTrackFirst ) {
qb . sortBy ( q_cat2 , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( q_cat2 , QueryBuilder : : valTrack ) ;
}
if ( c ) qb . sortBy ( QueryBuilder : : tabArtist , QueryBuilder : : valName ) ;
qb . sortBy ( q_cat2 , QueryBuilder : : valTitle ) ;
if ( ! SortbyTrackFirst ) {
qb . sortBy ( q_cat2 , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( q_cat2 , QueryBuilder : : valTrack ) ;
}
qb . sortBy ( q_cat2 , QueryBuilder : : valURL ) ;
}
else
{
c = false ;
qb . addReturnValue ( q_cat2 , QueryBuilder : : valName , true ) ;
if ( VisYearAlbum = = 2 )
{
qb . addReturnValue ( QueryBuilder : : tabYear , QueryBuilder : : valName , true ) ;
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
}
qb . sortBy ( q_cat2 , QueryBuilder : : valName ) ;
}
category = m_cat2 ;
break ;
case 1 :
tmptext = dynamic_cast < CollectionItem * > ( item - > parent ( ) ) ?
static_cast < CollectionItem * > ( item - > parent ( ) ) - > getSQLText ( 0 ) :
item - > parent ( ) - > text ( 0 ) ;
isUnknown = tmptext . isEmpty ( ) ;
if ( ! static_cast < CollectionItem * > ( item - > parent ( ) ) - > isSampler ( ) )
{
if ( m_cat1 = = IdArtist )
qb . setOptions ( QueryBuilder : : optNoCompilations ) ;
if ( VisYearAlbum = = 1 )
{
tmptext = item - > parent ( ) - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( isUnknown )
tmptext = " " ;
}
qb . addMatch ( q_cat1 , tmptext , false , true ) ;
}
else
{
qb . setOptions ( QueryBuilder : : optOnlyCompilations ) ;
c = true ;
}
tmptext = itemText ;
isUnknown = tmptext . isEmpty ( ) ;
if ( VisYearAlbum = = 2 )
{
tmptext = item - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( isUnknown )
tmptext = " " ;
}
qb . addMatch ( q_cat2 , tmptext , false , true ) ;
if ( m_cat3 = = QueryBuilder : : tabSong )
{
qb . addReturnValue ( q_cat3 , QueryBuilder : : valTitle , true ) ;
qb . addReturnValue ( q_cat3 , QueryBuilder : : valURL ) ;
if ( c ) qb . addReturnValue ( QueryBuilder : : tabArtist , QueryBuilder : : valName , true ) ;
if ( SortbyTrackFirst ) {
qb . sortBy ( q_cat3 , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( q_cat3 , QueryBuilder : : valTrack ) ;
}
if ( c ) qb . sortBy ( QueryBuilder : : tabArtist , QueryBuilder : : valName ) ;
qb . sortBy ( q_cat3 , QueryBuilder : : valTitle ) ;
if ( ! SortbyTrackFirst ) {
qb . sortBy ( q_cat3 , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( q_cat3 , QueryBuilder : : valTrack ) ;
}
qb . sortBy ( q_cat3 , QueryBuilder : : valURL ) ;
}
else
{
c = false ;
qb . addReturnValue ( q_cat3 , QueryBuilder : : valName , true ) ;
if ( VisYearAlbum = = 3 )
{
qb . addReturnValue ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
}
qb . sortBy ( q_cat3 , QueryBuilder : : valName ) ;
}
category = m_cat3 ;
break ;
case 2 :
tmptext = dynamic_cast < CollectionItem * > ( item - > parent ( ) - > parent ( ) ) ?
static_cast < CollectionItem * > ( item - > parent ( ) - > parent ( ) ) - > getSQLText ( 0 ) :
item - > parent ( ) - > parent ( ) - > text ( 0 ) ;
isUnknown = tmptext . isEmpty ( ) ;
if ( ! static_cast < CollectionItem * > ( item - > parent ( ) - > parent ( ) ) - > isSampler ( ) )
{
if ( m_cat1 = = IdArtist )
qb . setOptions ( QueryBuilder : : optNoCompilations ) ;
if ( VisYearAlbum = = 1 )
{
tmptext = item - > parent ( ) - > parent ( ) - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( isUnknown )
tmptext = " " ;
}
qb . addMatch ( q_cat1 , tmptext , false , true ) ;
}
else
{
qb . setOptions ( QueryBuilder : : optOnlyCompilations ) ;
c = true ;
}
tmptext = dynamic_cast < CollectionItem * > ( item - > parent ( ) ) ?
static_cast < CollectionItem * > ( item - > parent ( ) ) - > getSQLText ( 0 ) :
item - > parent ( ) - > text ( 0 ) ;
isUnknown = tmptext . isEmpty ( ) ;
if ( VisYearAlbum = = 2 )
{
tmptext = item - > parent ( ) - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( isUnknown )
tmptext = " " ;
}
qb . addMatch ( q_cat2 , tmptext , false , true ) ;
tmptext = itemText ;
isUnknown = tmptext . isEmpty ( ) ;
if ( VisYearAlbum = = 3 )
{
tmptext = item - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( isUnknown )
tmptext = " " ;
}
qb . addMatch ( q_cat3 , tmptext , false , true ) ;
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valTitle , true ) ;
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
if ( c )
qb . addReturnValue ( QueryBuilder : : tabArtist , QueryBuilder : : valName , true ) ;
if ( SortbyTrackFirst ) {
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTrack ) ;
}
if ( c ) qb . sortBy ( QueryBuilder : : tabArtist , QueryBuilder : : valName ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTitle ) ;
if ( ! SortbyTrackFirst ) {
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTrack ) ;
}
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
category = IdNone ;
break ;
}
qb . setGoogleFilter ( q_cat1 | q_cat2 | q_cat3 | QueryBuilder : : tabSong , m_filter ) ;
qb . setOptions ( QueryBuilder : : optRemoveDuplicates ) ;
values = qb . run ( ) ;
uint countReturnValues = qb . countReturnValues ( ) ;
TQPixmap pixmap ;
bool expandable = category ! = IdNone ;
if ( expandable )
pixmap = iconForCategory ( category ) ;
//this check avoid possible problems on database errors. FIXME: Should we add some real error handling here,
//like calling a collection update or something?
if ( values . isEmpty ( ) ) { return ; }
for ( int i = values . count ( ) - countReturnValues ; i > = 0 ; i - = countReturnValues )
{
TQString text ;
bool unknown = false ;
if ( category = = IdVisYearAlbum )
text + = ( values [ i + 1 ] . isEmpty ( ) ? " ? " : values [ i + 1 ] ) + i18n ( " - " ) ;
//show "artist - title" for compilations
if ( c )
{
if ( values [ i + 2 ] . stripWhiteSpace ( ) . isEmpty ( ) )
{
text + = i18n ( " Unknown " ) + i18n ( " - " ) ;
unknown = true ;
}
else
text = values [ i + 2 ] + i18n ( " - " ) ;
}
if ( values [ i ] . stripWhiteSpace ( ) . isEmpty ( ) )
{
if ( category = = IdLabel )
text + = i18n ( " No Label " ) ;
else
text + = i18n ( " Unknown " ) ;
unknown = true ;
}
else
text + = values [ i ] ;
CollectionItem * child = new CollectionItem ( item , category , unknown ) ;
child - > setDragEnabled ( true ) ;
child - > setDropEnabled ( false ) ;
child - > setText ( 0 , text ) ;
if ( expandable )
child - > setPixmap ( 0 , pixmap ) ;
else
child - > setUrl ( values [ i + 1 ] ) ;
child - > setExpandable ( expandable ) ;
}
//Display the album cover for the parent item now it is expanded
if ( dynamic_cast < CollectionItem * > ( item ) )
{
CollectionItem * i = static_cast < CollectionItem * > ( item ) ;
if ( i - > m_cat = = IdAlbum | | i - > m_cat = = IdVisYearAlbum )
i - > setPixmap ( 0 , TQPixmap ( ) ) ; //The pixmap given is unimportant. The cover is used.
}
}
void
CollectionView : : slotCollapse ( TQListViewItem * item ) //SLOT
{
//On collapse, go back from showing the cover to showing the icon for albums
if ( dynamic_cast < CollectionItem * > ( item ) )
{
CollectionItem * i = static_cast < CollectionItem * > ( item ) ;
if ( i - > m_cat = = IdAlbum | | i - > m_cat = = IdVisYearAlbum )
i - > setPixmap ( 0 , iconForCategory ( i - > m_cat ) ) ;
}
TQListViewItem * child = item - > firstChild ( ) ;
TQListViewItem * childTmp ;
//delete all children
while ( child )
{
childTmp = child ;
child = child - > nextSibling ( ) ;
delete childTmp ;
}
}
void
CollectionView : : ratingChanged ( const TQString & , int )
{
m_dirty = true ;
TQTimer : : singleShot ( 0 , CollectionView : : instance ( ) , TQT_SLOT ( renderView ( ) ) ) ;
}
void
CollectionView : : presetMenu ( int id ) //SLOT
{
switch ( id )
{
case IdArtist :
cat1Menu ( IdArtist , false ) ;
cat2Menu ( IdNone , false ) ;
cat3Menu ( IdNone , false ) ;
break ;
case IdAlbum :
cat1Menu ( IdAlbum , false ) ;
cat2Menu ( IdNone , false ) ;
cat3Menu ( IdNone , false ) ;
break ;
case IdArtistAlbum :
cat1Menu ( IdArtist , false ) ;
cat2Menu ( IdAlbum , false ) ;
cat3Menu ( IdNone , false ) ;
break ;
case IdArtistVisYearAlbum :
cat1Menu ( IdArtist , false ) ;
cat2Menu ( IdVisYearAlbum , false ) ;
cat3Menu ( IdNone , false ) ;
break ;
case IdGenreArtist :
cat1Menu ( IdGenre , false ) ;
cat2Menu ( IdArtist , false ) ;
cat3Menu ( IdNone , false ) ;
break ;
case IdGenreArtistAlbum :
cat1Menu ( IdGenre , false ) ;
cat2Menu ( IdArtist , false ) ;
cat3Menu ( IdAlbum , false ) ;
break ;
}
renderView ( true ) ;
}
void
CollectionView : : cat1Menu ( int id , bool rerender ) //SLOT
{
m_parent - > m_cat1Menu - > setItemChecked ( m_cat1 , false ) ; //uncheck old item
m_parent - > m_cat2Menu - > setItemEnabled ( m_cat1 , true ) ; //enable old items
m_parent - > m_cat3Menu - > setItemEnabled ( m_cat1 , true ) ;
m_cat1 = id ;
updateColumnHeader ( ) ;
resetIpodDepth ( ) ;
m_parent - > m_cat1Menu - > setItemChecked ( m_cat1 , true ) ;
//prevent choosing the same category in both menus
m_parent - > m_cat2Menu - > setItemEnabled ( id , false ) ;
m_parent - > m_cat3Menu - > setItemEnabled ( id , false ) ;
//if this item is checked in second menu, uncheck it
if ( m_parent - > m_cat2Menu - > isItemChecked ( id ) ) {
m_parent - > m_cat2Menu - > setItemChecked ( id , false ) ;
m_parent - > m_cat2Menu - > setItemChecked ( IdNone , true ) ;
m_cat2 = IdNone ;
enableCat3Menu ( false ) ;
}
//if this item is checked in third menu, uncheck it
if ( m_parent - > m_cat3Menu - > isItemChecked ( id ) ) {
m_parent - > m_cat3Menu - > setItemChecked ( id , false ) ;
m_parent - > m_cat3Menu - > setItemChecked ( IdNone , true ) ;
m_cat3 = IdNone ;
}
updateTrackDepth ( ) ;
if ( rerender )
{
renderView ( true ) ;
}
}
void
CollectionView : : cat2Menu ( int id , bool rerender ) //SLOT
{
m_parent - > m_cat2Menu - > setItemChecked ( m_cat2 , false ) ; //uncheck old item
m_parent - > m_cat3Menu - > setItemEnabled ( m_cat3 , true ) ; //enable old item
m_cat2 = id ;
m_parent - > m_cat2Menu - > setItemChecked ( m_cat2 , true ) ;
updateColumnHeader ( ) ;
resetIpodDepth ( ) ;
enableCat3Menu ( id ! = IdNone ) ;
//prevent choosing the same category in both menus
m_parent - > m_cat3Menu - > setItemEnabled ( m_cat1 , false ) ;
if ( id ! = IdNone )
m_parent - > m_cat3Menu - > setItemEnabled ( id , false ) ;
//if this item is checked in third menu, uncheck it
if ( m_parent - > m_cat3Menu - > isItemChecked ( id ) ) {
m_parent - > m_cat3Menu - > setItemChecked ( id , false ) ;
enableCat3Menu ( false ) ;
}
updateTrackDepth ( ) ;
if ( rerender )
{
renderView ( true ) ;
}
}
void
CollectionView : : cat3Menu ( int id , bool rerender ) //SLOT
{
m_parent - > m_cat3Menu - > setItemChecked ( m_cat3 , false ) ; //uncheck old item
m_cat3 = id ;
m_parent - > m_cat3Menu - > setItemChecked ( m_cat3 , true ) ;
updateColumnHeader ( ) ;
resetIpodDepth ( ) ;
updateTrackDepth ( ) ;
if ( rerender )
{
renderView ( true ) ;
}
}
void
CollectionView : : enableCat3Menu ( bool enable )
{
m_parent - > m_cat3Menu - > setItemEnabled ( IdAlbum , enable ) ;
m_parent - > m_cat3Menu - > setItemEnabled ( IdVisYearAlbum , enable ) ;
m_parent - > m_cat3Menu - > setItemEnabled ( IdArtist , enable ) ;
m_parent - > m_cat3Menu - > setItemEnabled ( IdComposer , enable ) ;
m_parent - > m_cat3Menu - > setItemEnabled ( IdGenre , enable ) ;
m_parent - > m_cat3Menu - > setItemEnabled ( IdYear , enable ) ;
m_parent - > m_cat3Menu - > setItemEnabled ( IdLabel , enable ) ;
if ( ! enable ) {
m_parent - > m_cat3Menu - > setItemChecked ( m_cat3 , false ) ;
m_parent - > m_cat3Menu - > setItemChecked ( IdNone , true ) ;
m_cat3 = IdNone ;
}
updateTrackDepth ( ) ;
}
void
CollectionView : : invokeItem ( TQListViewItem * i , const TQPoint & point , int column ) //SLOT
{
if ( column = = - 1 )
return ;
TQPoint p = mapFromGlobal ( point ) ;
if ( p . x ( ) > header ( ) - > sectionPos ( header ( ) - > mapToIndex ( 0 ) ) + treeStepSize ( ) * ( i - > depth ( ) + ( rootIsDecorated ( ) ? 1 : 0 ) ) + itemMargin ( )
| | p . x ( ) < header ( ) - > sectionPos ( header ( ) - > mapToIndex ( 0 ) ) )
invokeItem ( i ) ;
}
void
CollectionView : : invokeItem ( TQListViewItem * item ) //SLOT
{
if ( ! item | | dynamic_cast < DividerItem * > ( item ) )
return ;
item - > setSelected ( true ) ;
setCurrentItem ( item ) ;
//append and prevent doubles in playlist
if ( item - > isExpandable ( ) | | m_viewMode = = modeIpodView )
Playlist : : instance ( ) - > insertMedia ( listSelected ( ) , Playlist : : DefaultOptions ) ;
else
Playlist : : instance ( ) - > insertMedia ( static_cast < CollectionItem * > ( item ) - > url ( ) , Playlist : : DefaultOptions ) ;
}
// This slot is here to handle clicks on the right-arrow buttons
// in iPod browsing mode
void
CollectionView : : ipodItemClicked ( TQListViewItem * item , const TQPoint & , int c )
{
if ( item = = 0 | | c = = 0 )
return ;
if ( m_viewMode ! = modeIpodView )
return ;
// The TQt manual says NOT to delete items from within this slot
TQTimer : : singleShot ( 0 , m_parent - > m_ipodIncrement , TQT_SLOT ( activate ( ) ) ) ;
}
void
CollectionView : : rmbPressed ( TQListViewItem * item , const TQPoint & point , int ) //SLOT
{
if ( dynamic_cast < DividerItem * > ( item ) ) return ;
int artistLevel = - 1 ;
if ( item ) {
TDEPopupMenu menu ( this ) ;
int cat = 0 ;
if ( m_viewMode = = modeTreeView ) {
switch ( item - > depth ( ) )
{
case 0 :
cat = m_cat1 ;
break ;
case 1 :
if ( m_cat1 = = IdArtist )
artistLevel = 0 ;
cat = m_cat2 ;
break ;
case 2 :
if ( m_cat1 = = IdArtist )
artistLevel = 0 ;
else if ( m_cat2 = = IdArtist )
artistLevel = 1 ;
cat = m_cat3 ;
break ;
}
}
else if ( m_viewMode = = modeIpodView ) {
int catArr [ 3 ] = { m_cat1 , m_cat2 , m_cat3 } ;
if ( m_currentDepth < trackDepth ( ) )
cat = catArr [ m_currentDepth ] ;
}
enum Actions { APPEND , QUEUE , MAKE , SAVE , MEDIA_DEVICE , BURN_ARTIST ,
BURN_COMPOSER , BURN_ALBUM , BURN_CD , FETCH , INFO ,
COMPILATION_SET , COMPILATION_UNSET , ORGANIZE , DELETE , TRASH ,
FILE_MENU } ;
TQString trueItemText = getTrueItemText ( cat , item ) ;
KURL : : List selection = listSelected ( ) ;
TQStringList siblingSelection = listSelectedSiblingsOf ( cat , item ) ;
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " files " ) ) , i18n ( " &Load " ) , MAKE ) ;
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " add_playlist " ) ) , i18n ( " &Append to Playlist " ) , APPEND ) ;
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " queue_track " ) ) , selection . count ( ) = = 1 ? i18n ( " &Queue Track " )
: i18n ( " &Queue Tracks " ) , QUEUE ) ;
if ( selection . count ( ) > 1 | | item - > isExpandable ( ) )
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " save " ) ) , i18n ( " &Save as Playlist... " ) , SAVE ) ;
menu . insertSeparator ( ) ;
if ( MediaBrowser : : isAvailable ( ) )
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " device " ) ) , i18n ( " &Transfer to Media Device " ) , MEDIA_DEVICE ) ;
if ( cat = = IdArtist )
{
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " burn " ) ) , i18n ( " &Burn All Tracks by This Artist " ) , BURN_ARTIST ) ;
menu . setItemEnabled ( BURN_ARTIST , K3bExporter : : isAvailable ( ) & & siblingSelection . count ( ) = = 1 ) ;
}
else if ( cat = = IdComposer )
{
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " burn " ) ) , i18n ( " &Burn All Tracks by This Composer " ) , BURN_COMPOSER ) ;
menu . setItemEnabled ( BURN_COMPOSER , K3bExporter : : isAvailable ( ) & & siblingSelection . count ( ) = = 1 ) ;
}
else if ( ( cat = = IdAlbum | | cat = = IdVisYearAlbum ) )
{
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " burn " ) ) , i18n ( " &Burn This Album " ) , BURN_ALBUM ) ;
menu . setItemEnabled ( BURN_ALBUM , K3bExporter : : isAvailable ( ) & & siblingSelection . count ( ) = = 1 ) ;
}
// !item->isExpandable() in tree mode corresponds to
// showing tracks in iPod mode
else if ( ! item - > isExpandable ( ) & &
( m_viewMode ! = modeIpodView | | m_currentDepth = = trackDepth ( ) ) )
{
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " burn " ) ) , i18n ( " B&urn to CD " ) , BURN_CD ) ;
menu . setItemEnabled ( BURN_CD , K3bExporter : : isAvailable ( ) ) ;
}
menu . insertSeparator ( ) ;
TDEPopupMenu fileMenu ;
fileMenu . insertItem ( SmallIconSet ( " filesaveas " ) , i18n ( " &Organize File... " , " &Organize %n Files... " , selection . count ( ) ) , ORGANIZE ) ;
fileMenu . insertItem ( SmallIconSet ( Amarok : : icon ( " remove " ) ) , i18n ( " &Delete File... " , " &Delete %n Files... " , selection . count ( ) ) , DELETE ) ;
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " files " ) ) , i18n ( " Manage &Files " ) , & fileMenu , FILE_MENU ) ;
if ( ( cat = = IdAlbum | | cat = = IdVisYearAlbum ) & & siblingSelection . count ( ) = = 1 ) // cover fetch isn't multiselection capable
{
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " download " ) ) , i18n ( " &Fetch Cover From amazon.%1 " ) . arg ( CoverManager : : amazonTld ( ) ) , this , TQT_SLOT ( fetchCover ( ) ) , 0 , FETCH ) ;
# ifndef AMAZON_SUPPORT
menu . setItemEnabled ( FETCH , false ) ;
# endif
if ( trueItemText . isEmpty ( ) ) // disable cover fetch for unknown albums
menu . setItemEnabled ( FETCH , false ) ;
}
if ( ( ( cat = = IdAlbum | | cat = = IdVisYearAlbum ) & & siblingSelection . count ( ) > 0 ) //album
| | ( ! item - > isExpandable ( ) & & m_viewMode = = modeTreeView ) ) //or track
{
menu . insertSeparator ( ) ;
menu . insertItem ( SmallIconSet ( " ok " ) , i18n ( " Show under &Various Artists " ) , COMPILATION_SET ) ;
menu . insertItem ( SmallIconSet ( " cancel " ) , i18n ( " &Do not Show under Various Artists " ) , COMPILATION_UNSET ) ;
}
menu . insertSeparator ( ) ;
menu . insertItem ( SmallIconSet ( Amarok : : icon ( " info " ) )
, i18n ( " Edit Track &Information... " , " Edit &Information for %n Tracks... " , selection . count ( ) )
, this , TQT_SLOT ( showTrackInfo ( ) ) , 0 , INFO ) ;
switch ( menu . exec ( point ) )
{
case APPEND :
Playlist : : instance ( ) - > insertMedia ( selection , Playlist : : Append ) ;
break ;
case MAKE :
Playlist : : instance ( ) - > insertMedia ( selection , Playlist : : Replace ) ;
break ;
case SAVE :
playlistFromURLs ( selection ) ;
break ;
case QUEUE :
Playlist : : instance ( ) - > insertMedia ( selection , Playlist : : Queue ) ;
break ;
case MEDIA_DEVICE :
MediaBrowser : : queue ( ) - > addURLs ( selection ) ;
break ;
case BURN_COMPOSER :
K3bExporter : : instance ( ) - > exportComposer ( trueItemText ) ;
break ;
case BURN_ARTIST :
K3bExporter : : instance ( ) - > exportArtist ( trueItemText ) ;
break ;
case BURN_ALBUM :
if ( artistLevel = = - 1 | | static_cast < CollectionItem * > ( item ) - > isSampler ( ) )
{
K3bExporter : : instance ( ) - > exportAlbum ( trueItemText ) ;
}
else
{
TQString artist ;
if ( item - > depth ( ) - artistLevel = = 1 )
artist = item - > parent ( ) - > text ( 0 ) ;
else if ( item - > depth ( ) - artistLevel = = 2 )
artist = item - > parent ( ) - > parent ( ) - > text ( 0 ) ;
else if ( item - > depth ( ) - artistLevel = = 3 )
artist = item - > parent ( ) - > parent ( ) - > parent ( ) - > text ( 0 ) ;
K3bExporter : : instance ( ) - > exportAlbum ( artist , trueItemText ) ;
}
break ;
case BURN_CD :
K3bExporter : : instance ( ) - > exportTracks ( selection ) ;
break ;
case COMPILATION_SET :
setCompilation ( selection , true ) ;
break ;
case COMPILATION_UNSET :
setCompilation ( selection , false ) ;
break ;
case ORGANIZE :
organizeFiles ( selection , i18n ( " Organize Collection Files " ) , false /* do not add to collection, just move */ ) ;
break ;
case DELETE :
if ( DeleteDialog : : showTrashDialog ( this , selection ) )
{
CollectionDB : : instance ( ) - > removeSongs ( selection ) ;
foreachType ( KURL : : List , selection )
CollectionDB : : instance ( ) - > emitFileDeleted ( ( * it ) . path ( ) ) ;
}
m_dirty = true ;
TQTimer : : singleShot ( 0 , CollectionView : : instance ( ) , TQT_SLOT ( renderView ( ) ) ) ;
break ;
}
}
}
void
CollectionView : : setViewMode ( int mode , bool rerender /*=true*/ )
{
if ( m_viewMode = = modeFlatView )
{
m_flatColumnWidths . clear ( ) ;
for ( int c = 0 ; c < columns ( ) ; + + c )
m_flatColumnWidths . push_back ( columnWidth ( c ) ) ;
}
m_viewMode = mode ;
clear ( ) ;
updateColumnHeader ( ) ;
if ( m_viewMode = = modeIpodView )
{
# if TDE_VERSION >= TDE_MAKE_VERSION(3,4,0)
setShadeSortColumn ( false ) ;
# endif
m_parent - > m_ipodDecrement - > setEnabled ( m_currentDepth > 0 ) ;
m_parent - > ipodToolbar ( true ) ;
}
else
{
# if TDE_VERSION >= TDE_MAKE_VERSION(3,4,0)
setShadeSortColumn ( true ) ;
# endif
m_parent - > ipodToolbar ( false ) ;
}
if ( rerender )
{
// Pretend we just incremented the view depth so that
// renderView() will call selectIpodItems()
if ( m_viewMode = = modeIpodView )
m_ipodIncremented = 1 ;
renderView ( true ) ;
}
}
void
CollectionItem : : setPixmap ( int column , const TQPixmap & pix )
{
//Don't show the cover if the album isn't expanded (for speed)
if ( ! isOpen ( ) )
{
TQListViewItem : : setPixmap ( column , pix ) ;
return ;
}
//Generate Album name
TQString album ( text ( 0 ) ) , artist ;
if ( m_cat = = IdVisYearAlbum )
{
TQString pointlessString ;
CollectionView : : yearAlbumCalc ( pointlessString , album ) ;
}
else if ( m_cat ! = IdAlbum )
{
TQListViewItem : : setPixmap ( column , pix ) ;
return ;
}
//Now m_cat is either IdAlbum or IdVisYearAlbum, and so this is an album as required.
//Now work out the artist
CollectionItem * p = this ;
while ( p - > parent ( ) & & dynamic_cast < CollectionItem * > ( p - > parent ( ) ) )
{
p = static_cast < CollectionItem * > ( p - > parent ( ) ) ;
if ( IdArtist = = p - > m_cat )
{
artist = p - > text ( 0 ) ;
break ;
}
}
if ( artist . isNull ( ) )
{
//Try to guess artist - this will only happen if you don't have an Artist category
//above the Album category in the tree
QueryBuilder qb ;
qb . addReturnValue ( QueryBuilder : : tabArtist , QueryBuilder : : valName ) ;
qb . addMatch ( QueryBuilder : : tabAlbum , QueryBuilder : : valName , album ) ;
TQStringList values ( qb . run ( ) ) ;
if ( ! values . isEmpty ( ) )
artist = values [ 0 ] ;
else
{
//Don't bother trying to create a shadow because it won't work anyway. The
//nocover image has intial transparency, so adding the shadow doesn't work.
TQListViewItem : : setPixmap ( column , TQPixmap ( CollectionDB : : instance ( ) - > notAvailCover ( false , 50 ) ) ) ;
return ;
}
}
TQListViewItem : : setPixmap ( column , TQPixmap ( CollectionDB : : instance ( ) - > albumImage ( artist , album , true , 50 ) ) ) ;
}
void
CollectionView : : fetchCover ( ) //SLOT
{
# ifdef AMAZON_SUPPORT
CollectionItem * item = static_cast < CollectionItem * > ( currentItem ( ) ) ;
if ( ! item ) return ;
int cat = 0 ;
switch ( item - > depth ( ) )
{
case 0 :
cat = m_cat1 ;
break ;
case 1 :
cat = m_cat2 ;
break ;
case 2 :
cat = m_cat3 ;
break ;
}
TQString album = item - > text ( 0 ) ;
if ( cat = = IdVisYearAlbum )
{
// we can't use findRev since an album may have " - " within it.
TQString sep = i18n ( " - " ) ;
album = album . right ( album . length ( ) - sep . length ( ) - album . find ( sep ) ) ;
}
// find the first artist's name
TQStringList values =
CollectionDB : : instance ( ) - > query ( TQString (
" SELECT DISTINCT artist.name FROM artist, album, tags "
" WHERE artist.id = tags.artist AND tags.album = album.id "
" AND album.name = '%1'; " )
. arg ( CollectionDB : : instance ( ) - > escapeString ( album ) ) ) ;
if ( ! values . isEmpty ( ) )
CollectionDB : : instance ( ) - > fetchCover ( this , values [ 0 ] , album , false , static_cast < TQListViewItem * > ( item ) ) ;
# endif
}
void
CollectionView : : showTrackInfo ( ) //SLOT
{
DEBUG_BLOCK
KURL : : List urls = listSelected ( ) ;
int selectedTracksNumber = urls . count ( ) ;
//If we have only one, call the full dialog. Otherwise, the multiple tracks one.
if ( selectedTracksNumber = = 1 )
{
TagDialog * dialog = new TagDialog ( urls . first ( ) , instance ( ) ) ;
dialog - > show ( ) ;
}
else if ( selectedTracksNumber )
{
TagDialog * dialog = new TagDialog ( urls , instance ( ) ) ;
dialog - > show ( ) ;
}
}
bool
CollectionView : : isOrganizingFiles ( ) const
{
return m_organizeURLs . count ( ) > 0 ;
}
void CollectionView : : cancelOrganizingFiles ( )
{
// Set the indicator
m_organizingFileCancelled = true ;
// Cancel the current underlying CollectionDB::instance()->moveFile operation
CollectionDB : : instance ( ) - > cancelMovingFileJob ( ) ;
}
void
CollectionView : : organizeFiles ( const KURL : : List & urls , const TQString & caption , bool copy ) //SLOT
{
if ( m_organizingFileCancelled )
{
TQString shortMsg = i18n ( " Cannot start organize operation until jobs are aborted. " ) ;
Amarok : : StatusBar : : instance ( ) - > shortMessage ( shortMsg , KDE : : StatusBar : : Sorry ) ;
return ;
}
if ( m_organizeURLs . count ( ) )
{
if ( copy ! = m_organizeCopyMode )
{
TQString shortMsg = i18n ( " Cannot start organize operation of different kind while another is in progress. " ) ;
Amarok : : StatusBar : : instance ( ) - > shortMessage ( shortMsg , KDE : : StatusBar : : Sorry ) ;
return ;
}
else
{
m_organizeURLs + = Amarok : : recursiveUrlExpand ( urls ) ;
Amarok : : StatusBar : : instance ( ) - > incrementProgressTotalSteps ( TQT_TQOBJECT ( this ) , urls . count ( ) ) ;
return ;
}
}
TQStringList folders = MountPointManager : : instance ( ) - > collectionFolders ( ) ;
if ( folders . isEmpty ( ) )
{
TQString longMsg = i18n ( " You need to configure at least one folder for your collection for organizing your files. " ) ;
Amarok : : StatusBar : : instance ( ) - > longMessage ( longMsg , KDE : : StatusBar : : Sorry ) ;
return ;
}
OrganizeCollectionDialogBase base ( m_parent , " OrganizeFiles " , true , caption ,
KDialogBase : : Ok | KDialogBase : : Cancel | KDialogBase : : Details ) ;
TQVBox * page = base . makeVBoxMainWidget ( ) ;
OrganizeCollectionDialog dialog ( page ) ;
dialog . folderCombo - > insertStringList ( folders , 0 ) ;
dialog . folderCombo - > setCurrentItem ( AmarokConfig : : organizeDirectory ( ) ) ;
dialog . overwriteCheck - > setChecked ( AmarokConfig : : overwriteFiles ( ) ) ;
dialog . filetypeCheck - > setChecked ( AmarokConfig : : groupByFiletype ( ) ) ;
dialog . initialCheck - > setChecked ( AmarokConfig : : groupArtists ( ) ) ;
dialog . spaceCheck - > setChecked ( AmarokConfig : : replaceSpace ( ) ) ;
dialog . coverCheck - > setChecked ( AmarokConfig : : coverIcons ( ) ) ;
dialog . ignoreTheCheck - > setChecked ( AmarokConfig : : ignoreThe ( ) ) ;
dialog . vfatCheck - > setChecked ( AmarokConfig : : vfatCompatible ( ) ) ;
dialog . asciiCheck - > setChecked ( AmarokConfig : : asciiOnly ( ) ) ;
dialog . customschemeCheck - > setChecked ( AmarokConfig : : useCustomScheme ( ) ) ;
dialog . formatEdit - > setText ( AmarokConfig : : customScheme ( ) ) ;
dialog . regexpEdit - > setText ( AmarokConfig : : replacementRegexp ( ) ) ;
dialog . replaceEdit - > setText ( AmarokConfig : : replacementString ( ) ) ;
connect ( & base , TQT_SIGNAL ( detailsClicked ( ) ) , & dialog , TQT_SLOT ( slotDetails ( ) ) ) ;
if ( dialog . customschemeCheck - > isChecked ( ) )
{
base . setDetails ( true ) ;
}
else
{
dialog . slotDetails ( ) ;
}
KURL : : List previewURLs = Amarok : : recursiveUrlExpand ( urls . first ( ) , 1 ) ;
if ( previewURLs . count ( ) )
{
dialog . setPreviewBundle ( MetaBundle ( previewURLs . first ( ) ) ) ;
dialog . update ( 0 ) ;
}
base . setInitialSize ( TQSize ( 450 , 350 ) ) ;
if ( base . exec ( ) = = KDialogBase : : Accepted )
{
AmarokConfig : : setOrganizeDirectory ( dialog . folderCombo - > currentItem ( ) ) ;
AmarokConfig : : setOverwriteFiles ( dialog . overwriteCheck - > isChecked ( ) ) ;
AmarokConfig : : setGroupByFiletype ( dialog . filetypeCheck - > isChecked ( ) ) ;
AmarokConfig : : setGroupArtists ( dialog . initialCheck - > isChecked ( ) ) ;
AmarokConfig : : setIgnoreThe ( dialog . ignoreTheCheck - > isChecked ( ) ) ;
AmarokConfig : : setReplaceSpace ( dialog . spaceCheck - > isChecked ( ) ) ;
AmarokConfig : : setCoverIcons ( dialog . coverCheck - > isChecked ( ) ) ;
AmarokConfig : : setVfatCompatible ( dialog . vfatCheck - > isChecked ( ) ) ;
AmarokConfig : : setAsciiOnly ( dialog . asciiCheck - > isChecked ( ) ) ;
AmarokConfig : : setUseCustomScheme ( dialog . customschemeCheck - > isChecked ( ) ) ;
AmarokConfig : : setCustomScheme ( dialog . formatEdit - > text ( ) ) ;
AmarokConfig : : setReplacementRegexp ( dialog . regexpEdit - > text ( ) ) ;
AmarokConfig : : setReplacementString ( dialog . replaceEdit - > text ( ) ) ;
KURL : : List skipped ;
m_organizeURLs = Amarok : : recursiveUrlExpand ( urls ) ;
m_organizeCopyMode = copy ;
CollectionDB : : instance ( ) - > createTables ( true ) ; // create temp tables
Amarok : : StatusBar : : instance ( ) - > newProgressOperation ( TQT_TQOBJECT ( this ) )
. setDescription ( caption )
. setAbortSlot ( TQT_TQOBJECT ( this ) , TQT_SLOT ( cancelOrganizingFiles ( ) ) )
. setTotalSteps ( m_organizeURLs . count ( ) ) ;
while ( ! m_organizeURLs . empty ( ) & & ! m_organizingFileCancelled )
{
KURL & src = m_organizeURLs . first ( ) ;
if ( ! CollectionDB : : instance ( ) - > organizeFile ( src , dialog , copy ) )
{
skipped + = src ;
}
m_organizeURLs . pop_front ( ) ;
Amarok : : StatusBar : : instance ( ) - > incrementProgress ( TQT_TQOBJECT ( this ) ) ;
if ( m_organizingFileCancelled ) m_organizeURLs . clear ( ) ;
}
CollectionDB : : instance ( ) - > sanitizeCompilations ( ) ; //queryBuilder doesn't handle unknownCompilations
CollectionDB : : instance ( ) - > copyTempTables ( ) ; // copy temp table contents to permanent tables
CollectionDB : : instance ( ) - > dropTables ( true ) ; // and drop them
// and now do an incremental scan since this was disabled while organizing files
TQTimer : : singleShot ( 0 , CollectionDB : : instance ( ) , TQT_SLOT ( scanMonitor ( ) ) ) ;
if ( ! m_organizingFileCancelled & & skipped . count ( ) > 0 )
{
TQString longMsg = i18n ( " The following file could not be organized: " ,
" The following %n files could not be organized: " , skipped . count ( ) ) ;
bool first = true ;
for ( KURL : : List : : iterator it = skipped . begin ( ) ;
it ! = skipped . end ( ) ;
it + + )
{
if ( ! first )
longMsg + = i18n ( " , " ) ;
else
first = false ;
longMsg + = ( * it ) . path ( ) ;
}
longMsg + = i18n ( " . " ) ;
TQString shortMsg = i18n ( " Sorry, one file could not be organized. " ,
" Sorry, %n files could not be organized. " , skipped . count ( ) ) ;
Amarok : : StatusBar : : instance ( ) - > shortLongMessage ( shortMsg , longMsg , KDE : : StatusBar : : Sorry ) ;
}
else if ( m_organizingFileCancelled )
{
Amarok : : StatusBar : : instance ( ) - > shortMessage ( i18n ( " Aborting jobs... " ) ) ;
m_organizingFileCancelled = false ;
}
m_dirty = true ;
TQTimer : : singleShot ( 0 , CollectionView : : instance ( ) , TQT_SLOT ( renderView ( ) ) ) ;
Amarok : : StatusBar : : instance ( ) - > endProgressOperation ( TQT_TQOBJECT ( this ) ) ;
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// private
//////////////////////////////////////////////////////////////////////////////////////////
void
CollectionView : : contentsDragEnterEvent ( TQDragEnterEvent * e )
{
e - > accept ( e - > source ( ) ! = viewport ( ) & & e - > source ( ) ! = this & & KURLDrag : : canDecode ( e ) ) ;
}
void
CollectionView : : contentsDragMoveEvent ( TQDragMoveEvent * e )
{
e - > accept ( e - > source ( ) ! = viewport ( ) & & e - > source ( ) ! = this & & KURLDrag : : canDecode ( e ) ) ;
}
void
CollectionView : : contentsDropEvent ( TQDropEvent * e )
{
KURL : : List list ;
if ( KURLDrag : : decode ( e , list ) )
{
KURL : : List expandedList ;
int dropped = 0 ;
int invalid = 0 ;
for ( KURL : : List : : iterator it = list . begin ( ) ;
it ! = list . end ( ) ;
+ + it )
{
if ( ( * it ) . isLocalFile ( ) & & TQFileInfo ( ( * it ) . path ( ) ) . isDir ( ) )
expandedList + = Amarok : : recursiveUrlExpand ( * it ) ;
else
expandedList + = * it ;
}
KURL : : List cleanList ;
for ( KURL : : List : : iterator it = expandedList . begin ( ) ;
it ! = expandedList . end ( ) ;
+ + it )
{
TQString proto = ( * it ) . protocol ( ) ;
if ( ! MetaBundle : : isKioUrl ( * it ) )
invalid + + ;
else if ( ( * it ) . isLocalFile ( ) & & CollectionDB : : instance ( ) - > isFileInCollection ( ( * it ) . path ( ) ) )
dropped + + ;
else
cleanList + = * it ;
}
TQString msg ;
if ( dropped > 0 )
msg + = i18n ( " One file already in collection " ,
" %n files already in collection " , dropped ) ;
if ( invalid > 0 )
if ( msg . isEmpty ( ) )
msg + = i18n ( " One dropped file is invalid " ,
" %n dropped files are invalid " , invalid ) ;
else
msg + = i18n ( " , one dropped file is invalid " ,
" , %n dropped files are invalid " , invalid ) ;
if ( ! msg . isEmpty ( ) )
Amarok : : StatusBar : : instance ( ) - > shortMessage ( msg ) ;
if ( cleanList . count ( ) > 0 )
organizeFiles ( list , i18n ( " Copy Files To Collection " ) , true /* copy */ ) ;
}
}
void
CollectionView : : dropProxyEvent ( TQDropEvent * e )
{
contentsDropEvent ( e ) ;
}
void
CollectionView : : safeClear ( )
{
bool block = signalsBlocked ( ) ;
blockSignals ( true ) ;
clearSelection ( ) ;
TQMap < TQListViewItem * , CoverFetcher * > * itemCoverMap = CollectionDB : : instance ( ) - > getItemCoverMap ( ) ;
TQMutex * itemCoverMapMutex = CollectionDB : : instance ( ) - > getItemCoverMapMutex ( ) ;
TQListViewItem * c = firstChild ( ) ;
TQListViewItem * n ;
itemCoverMapMutex - > lock ( ) ;
while ( c ) {
if ( itemCoverMap - > contains ( c ) )
itemCoverMap - > erase ( c ) ;
n = c - > nextSibling ( ) ;
delete c ;
c = n ;
}
itemCoverMapMutex - > unlock ( ) ;
blockSignals ( block ) ;
triggerUpdate ( ) ;
}
void
CollectionView : : updateColumnHeader ( )
{
// remove all columns
for ( int i = columns ( ) - 1 ; i > = 0 ; - - i )
removeColumn ( i ) ;
if ( m_viewMode = = modeFlatView )
{
setResizeMode ( TQListView : : NoColumn ) ;
if ( m_flatColumnWidths . size ( ) = = 0 )
{
addColumn ( captionForTag ( Title ) ) ;
# define includesArtist(cat) (((cat)&IdArtist) \
| | ( ( cat ) & IdArtistAlbum ) \
| | ( ( cat ) & IdGenreArtist ) \
| | ( ( cat ) & IdGenreArtistAlbum ) \
| | ( ( cat ) & IdArtistVisYearAlbum ) )
if ( includesArtist ( m_cat1 ) | | includesArtist ( m_cat2 ) | | includesArtist ( m_cat3 ) )
addColumn ( captionForTag ( Artist ) ) ;
else
addColumn ( captionForTag ( Artist ) , 0 ) ;
# undef includesArtist
if ( m_cat1 & IdComposer | | m_cat2 & IdComposer | | m_cat3 & IdComposer )
addColumn ( captionForTag ( Composer ) ) ;
else
addColumn ( captionForTag ( Composer ) , 0 ) ;
# define includesAlbum(cat) (((cat)&IdAlbum) \
| | ( ( cat ) & IdArtistAlbum ) \
| | ( ( cat ) & IdGenreArtistAlbum ) \
| | ( ( cat ) & IdVisYearAlbum ) \
| | ( ( cat ) & IdArtistVisYearAlbum ) )
if ( includesAlbum ( m_cat1 ) | | includesAlbum ( m_cat2 ) | | includesAlbum ( m_cat3 ) )
addColumn ( captionForTag ( Album ) ) ;
else
addColumn ( captionForTag ( Album ) , 0 ) ;
# undef includesAlbum
# define includesGenre(cat) (((cat)&IdGenre) \
| | ( ( cat ) & IdGenreArtist ) \
| | ( ( cat ) & IdGenreArtistAlbum ) )
if ( includesGenre ( m_cat1 ) | | includesGenre ( m_cat2 ) | | includesGenre ( m_cat3 ) )
addColumn ( captionForTag ( Genre ) ) ;
else
addColumn ( captionForTag ( Genre ) , 0 ) ;
# undef includesGenre
addColumn ( captionForTag ( Length ) , 0 ) ;
addColumn ( captionForTag ( DiscNumber ) , 0 ) ;
addColumn ( captionForTag ( Track ) , 0 ) ;
# define includesYear(cat) (((cat)&IdYear) \
| | ( ( cat ) & IdVisYearAlbum ) \
| | ( ( cat ) & IdArtistVisYearAlbum ) )
if ( includesYear ( m_cat1 ) | | includesYear ( m_cat2 ) | | includesYear ( m_cat3 ) )
addColumn ( captionForTag ( Year ) ) ;
else
addColumn ( captionForTag ( Year ) , 0 ) ;
# undef includesYear
addColumn ( captionForTag ( Comment ) , 0 ) ;
addColumn ( captionForTag ( Playcount ) , 0 ) ;
addColumn ( captionForTag ( Score ) , 0 ) ;
addColumn ( captionForTag ( Rating ) , 0 ) ;
addColumn ( captionForTag ( Filename ) , 0 ) ;
addColumn ( captionForTag ( Firstplay ) , 0 ) ;
addColumn ( captionForTag ( Lastplay ) , 0 ) ;
addColumn ( captionForTag ( Modified ) , 0 ) ;
addColumn ( captionForTag ( Bitrate ) , 0 ) ;
addColumn ( captionForTag ( Filesize ) , 0 ) ;
addColumn ( captionForTag ( BPM ) , 0 ) ;
}
else
{
for ( uint tag = 0 ; tag < NUM_TAGS ; + + tag ) {
if ( tag < m_flatColumnWidths . size ( ) )
addColumn ( captionForTag ( static_cast < Tag > ( tag ) ) , m_flatColumnWidths [ tag ] ) ;
else
addColumn ( captionForTag ( static_cast < Tag > ( tag ) ) , 0 ) ;
}
}
setColumnAlignment ( Track , TQt : : AlignCenter ) ;
setColumnAlignment ( DiscNumber , TQt : : AlignCenter ) ;
setColumnAlignment ( Length , TQt : : AlignRight ) ;
setColumnAlignment ( Bitrate , TQt : : AlignCenter ) ;
setColumnAlignment ( Score , TQt : : AlignCenter ) ;
setColumnAlignment ( Playcount , TQt : : AlignCenter ) ;
setColumnAlignment ( BPM , TQt : : AlignRight ) ;
//TQListView allows invisible columns to be resized, so we disable resizing for them
for ( int i = 0 ; i < columns ( ) ; + + i ) {
setColumnWidthMode ( i , TQListView : : Manual ) ;
if ( columnWidth ( i ) = = 0 )
header ( ) - > setResizeEnabled ( false , i ) ;
}
setRootIsDecorated ( false ) ;
}
else if ( m_viewMode = = modeTreeView )
{
setResizeMode ( TQListView : : LastColumn ) ;
TQString caption = captionForCategory ( m_cat1 ) ;
int catArr [ 2 ] = { m_cat2 , m_cat3 } ;
for ( int i = 0 ; i < 2 ; i + + ) {
if ( catArr [ i ] ! = IdNone ) {
caption + = " / " + captionForCategory ( catArr [ i ] ) ;
}
}
addColumn ( caption ) ;
setRootIsDecorated ( true ) ;
}
// The iPod columns
// When not in track mode, there are two columns: the first
// contains the text + pixmap, and the second just has the
// right-arrow pixmap. In any case we're not in tree mode.
else if ( m_viewMode = = modeIpodView )
{
TQString caption ;
if ( m_currentDepth = = trackDepth ( ) )
caption = i18n ( " Tracks " ) ;
else
{
int catArr [ 3 ] = { m_cat1 , m_cat2 , m_cat3 } ;
caption = captionForCategory ( catArr [ m_currentDepth ] ) ;
}
addColumn ( caption ) ;
if ( m_currentDepth ! = trackDepth ( ) )
{
TQPixmap pixmap = ipodDecrementIcon ( ) ;
// This column is for the "->" buttons. The width is just
// a guess, and will be changed once an item is added.
addColumn ( " " , pixmap . width ( ) + 10 ) ;
}
setRootIsDecorated ( false ) ;
// Neither column should automatically stretch, since this tends
// to add a scrollbar when it's not needed, and anyway we manually
// stretch it in viewportResizeEvent()
header ( ) - > setStretchEnabled ( false , 0 ) ;
if ( m_currentDepth ! = trackDepth ( ) )
header ( ) - > setStretchEnabled ( false , 1 ) ;
}
//manage column widths
TQResizeEvent rev ( size ( ) , TQSize ( ) ) ;
viewportResizeEvent ( & rev ) ;
m_parent - > m_categoryMenu - > setItemChecked ( IdArtist , m_cat1 = = IdArtist & & m_cat2 = = IdNone ) ;
m_parent - > m_categoryMenu - > setItemChecked ( IdAlbum , m_cat1 = = IdAlbum & & m_cat2 = = IdNone ) ;
m_parent - > m_categoryMenu - > setItemChecked ( IdArtistAlbum , m_cat1 = = IdArtist & & m_cat2 = = IdAlbum & & m_cat3 = = IdNone ) ;
m_parent - > m_categoryMenu - > setItemChecked ( IdArtistVisYearAlbum , m_cat1 = = IdArtist & & m_cat2 = = IdVisYearAlbum & & m_cat3 = = IdNone ) ;
m_parent - > m_categoryMenu - > setItemChecked ( IdGenreArtist , m_cat1 = = IdGenre & & m_cat2 = = IdArtist & & m_cat3 = = IdNone ) ;
m_parent - > m_categoryMenu - > setItemChecked ( IdGenreArtistAlbum , m_cat1 = = IdGenre & & m_cat2 = = IdArtist & & m_cat3 = = IdAlbum ) ;
}
void
CollectionView : : startDrag ( )
{
KURL : : List urls = listSelected ( ) ;
KURLDrag * d = new KURLDrag ( urls , this ) ;
d - > setPixmap ( CollectionDB : : createDragPixmap ( urls ) ,
TQPoint ( CollectionDB : : DRAGPIXMAP_OFFSET_X ,
CollectionDB : : DRAGPIXMAP_OFFSET_Y ) ) ;
d - > dragCopy ( ) ;
}
TQString
CollectionView : : getTrueItemText ( int cat , TQListViewItem * item ) const
{
//Work out the true name of the album ( where Unknown is "" ) , and the
TQString trueItemText ;
if ( item = = 0 )
{
warning ( ) < < " getTrueItemText() called for empty CollectionItem " < < endl ;
return TQString ( ) ;
}
if ( dynamic_cast < CollectionItem * > ( item ) )
{
CollectionItem * collectItem = static_cast < CollectionItem * > ( item ) ;
trueItemText = collectItem - > getSQLText ( 0 ) ;
if ( cat = = IdVisYearAlbum & & ! collectItem - > isUnknown ( ) )
trueItemText = trueItemText . right ( trueItemText . length ( ) - trueItemText . find ( i18n ( " - " ) ) - i18n ( " - " ) . length ( ) ) ;
}
else
{
trueItemText = item - > text ( 0 ) ;
warning ( ) < < " getTrueItemText() called for non-CollectionItem with text ' " < < trueItemText < < ' \' ' < < endl ;
}
return trueItemText ;
}
TQStringList
CollectionView : : listSelectedSiblingsOf ( int cat , TQListViewItem * item )
{
// notice that using the nextSibling()-axis does not work in this case as this
// would only select items below the specified item.
TQStringList list ;
TQString trueItemText ;
int depth = item - > depth ( ) ;
// go to top most item
while ( item & & item - > itemAbove ( ) )
{
item = item - > itemAbove ( ) ;
//debug() << "walked up to item: " << getTrueItemText( cat, item ) << endl;
}
// walk down to get all selected items in same depth
while ( item )
{
if ( item - > isSelected ( ) & & item - > depth ( ) = = depth )
{
trueItemText = getTrueItemText ( cat , item ) ;
//debug() << "selected item: " << trueItemText << endl;
list < < trueItemText ;
}
item = item - > itemBelow ( ) ;
}
return list ;
}
KURL : : List
CollectionView : : listSelected ( )
{
//Here we determine the URLs of all selected items. We use two passes, one for the parent items,
//and another one for the children.
KURL : : List list ;
TQListViewItem * item ;
TQStringList values ;
QueryBuilder qb ;
// initialization for year - album mode
TQString tmptext ;
bool unknownText ;
int VisYearAlbum = - 1 ;
int q_cat1 = m_cat1 ;
int q_cat2 = m_cat2 ;
int q_cat3 = m_cat3 ;
if ( m_cat1 = = IdVisYearAlbum | | m_cat2 = = IdVisYearAlbum | | m_cat3 = = IdVisYearAlbum )
{
if ( m_cat1 = = IdVisYearAlbum )
{
VisYearAlbum = 1 ;
q_cat1 = IdAlbum ;
}
if ( m_cat2 = = IdVisYearAlbum )
{
VisYearAlbum = 2 ;
q_cat2 = IdAlbum ;
}
if ( m_cat3 = = IdVisYearAlbum )
{
VisYearAlbum = 3 ;
q_cat3 = IdAlbum ;
}
}
if ( m_viewMode = = modeFlatView )
{
for ( item = firstChild ( ) ; item ; item = item - > nextSibling ( ) )
if ( item - > isSelected ( ) )
list < < static_cast < CollectionItem * > ( item ) - > url ( ) ;
return list ;
}
// The iPod selection code is written to resemble the tree mode
// selection logic, as well as what happens on an actual iPod. If
// we're in track mode, just return the list of tracks selected.
// Otherwise select all children of all currently selected items,
// sorting first by m_cat1, then m_cat2, then m_cat3. Sort by
// track first if one of the categories is Id(VisYear)Album.
// There is a difficulty with compilation albums -- if we're
// sorting by track first then we want to group compilation albums
// separately, and not with the individual artists, even if that's
// one of the categories (e.g. if the user just adds one
// compilation album, we can't sort by artist first). In other
// words, when one of the categories is Id(VisYear)Album,
// compilation albums should behave as if the artist is Various
// Artists, for sorting purposes. This is handled by making two
// separate queries, the first for the compilations, and the
// second for everything else.
// Note that if "All" is currently selected then the other
// selections are ignored.
if ( m_viewMode = = modeIpodView )
{
// If we're already displaying tracks, just return the selected ones
if ( m_currentDepth = = trackDepth ( ) )
{
TQPtrList < TQListViewItem > selected = selectedItems ( ) ;
TQPtrList < TQListViewItem > : : iterator it = selected . begin ( ) ;
while ( it ! = selected . end ( ) )
{
if ( dynamic_cast < CollectionItem * > ( * it ) ! = 0 )
list < < dynamic_cast < CollectionItem * > ( * it ) - > url ( ) ;
+ + it ;
}
return list ;
}
// We're not displaying tracks.
QueryBuilder qb ;
// Do a "fake" depth incrementation to figure out the
// correct filters at the current depth
incrementDepth ( false ) ;
// Copy the filter list before calling decrementDepth() below
TQStringList filters [ 3 ] ;
for ( int i = 0 ; i < m_currentDepth ; + + i )
filters [ i ] = m_ipodFilters [ i ] ;
TQStringList filterYear = m_ipodFilterYear ;
// Undo the fake depth incrementation
decrementDepth ( false ) ;
int catArr [ 3 ] = { m_cat1 , m_cat2 , m_cat3 } ;
int tables = 0 ;
bool sortByTrackFirst = false ;
for ( int i = 0 ; i < trackDepth ( ) ; + + i )
tables | = ( catArr [ i ] = = IdVisYearAlbum
? IdAlbum
: catArr [ i ] ) ;
// Figure out if the results will be sorted by track first
// (i.e., if one of the filters is by album). If so, we need
// to search compilations first.
if ( tables & IdAlbum )
sortByTrackFirst = true ;
if ( sortByTrackFirst )
{
// Build the query, recursively sorted. First get compilation
// albums so they'll be first, not sorted by artist
buildIpodQuery ( qb , trackDepth ( ) , filters , filterYear , true , true ) ;
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate , TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) - translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
qb . setOptions ( QueryBuilder : : optOnlyCompilations ) ;
qb . setGoogleFilter ( tables | QueryBuilder : : tabSong , m_filter ) ;
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
values = qb . run ( ) ;
}
// Now build the non-compilation album query
qb . clear ( ) ;
buildIpodQuery ( qb , trackDepth ( ) , filters , filterYear , true , false ) ;
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate , TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) - translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
if ( sortByTrackFirst )
qb . setOptions ( QueryBuilder : : optNoCompilations ) ;
qb . setGoogleFilter ( tables | QueryBuilder : : tabSong , m_filter ) ;
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
values + = qb . run ( ) ;
int total = values . count ( ) / qb . countReturnValues ( ) ;
// This shouldn't happen
if ( total = = 0 )
return list ;
TQStringList : : Iterator it = values . begin ( ) ;
KURL tmp ;
while ( it ! = values . end ( ) )
{
tmp . setPath ( ( * ( it + + ) ) ) ;
list < < tmp ;
}
return list ;
}
//first pass: parents
for ( item = firstChild ( ) ; item ; item = item - > nextSibling ( ) )
if ( item - > isSelected ( ) )
{
const bool sampler = static_cast < CollectionItem * > ( item ) - > isSampler ( ) ;
qb . clear ( ) ;
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate , TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) - translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
tmptext = static_cast < CollectionItem * > ( item ) - > getSQLText ( 0 ) ;
unknownText = tmptext . isEmpty ( ) ;
if ( ! sampler )
{
if ( q_cat1 = = IdArtist )
qb . setOptions ( QueryBuilder : : optNoCompilations ) ;
if ( VisYearAlbum = = 1 )
{
tmptext = item - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( unknownText )
tmptext = " " ;
}
qb . addMatch ( q_cat1 , tmptext , false , true ) ;
}
else
qb . setOptions ( QueryBuilder : : optOnlyCompilations ) ;
qb . setGoogleFilter ( q_cat1 | q_cat2 | q_cat3 | QueryBuilder : : tabSong , m_filter ) ;
if ( VisYearAlbum = = 1 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
if ( ! sampler ) qb . sortBy ( q_cat1 , QueryBuilder : : valName ) ;
if ( VisYearAlbum = = 2 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
if ( q_cat2 ! = QueryBuilder : : tabSong )
qb . sortBy ( q_cat2 , QueryBuilder : : valName ) ;
if ( VisYearAlbum = = 3 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
if ( q_cat3 ! = QueryBuilder : : tabSong )
qb . sortBy ( q_cat3 , QueryBuilder : : valName ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTrack ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
qb . setOptions ( QueryBuilder : : optRemoveDuplicates ) ;
values = qb . run ( ) ;
for ( uint i = 0 ; i < values . count ( ) ; i + + )
{
KURL tmp ;
tmp . setPath ( values [ i ] ) ;
list < < tmp ;
}
}
//second pass: category 1
if ( m_cat2 = = IdNone )
{
for ( item = firstChild ( ) ; item ; item = item - > nextSibling ( ) )
for ( TQListViewItem * child = item - > firstChild ( ) ; child ; child = child - > nextSibling ( ) )
if ( child - > isSelected ( ) & & ! child - > parent ( ) - > isSelected ( ) )
list < < static_cast < CollectionItem * > ( child ) - > url ( ) ;
}
else {
for ( item = firstChild ( ) ; item ; item = item - > nextSibling ( ) )
for ( TQListViewItem * child = item - > firstChild ( ) ; child ; child = child - > nextSibling ( ) )
if ( child - > isSelected ( ) & & ! child - > parent ( ) - > isSelected ( ) )
{
const bool sampler = static_cast < CollectionItem * > ( item ) - > isSampler ( ) ;
qb . clear ( ) ;
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate , TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) - translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
if ( ! sampler )
{
if ( q_cat1 = = IdArtist )
qb . setOptions ( QueryBuilder : : optNoCompilations ) ;
tmptext = static_cast < CollectionItem * > ( item ) - > getSQLText ( 0 ) ;
unknownText = tmptext . isEmpty ( ) ;
if ( VisYearAlbum = = 1 )
{
tmptext = item - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( unknownText )
tmptext = " " ;
}
qb . addMatch ( q_cat1 , tmptext , false , true ) ;
}
else
qb . setOptions ( QueryBuilder : : optOnlyCompilations ) ;
tmptext = static_cast < CollectionItem * > ( child ) - > getSQLText ( 0 ) ;
unknownText = tmptext . isEmpty ( ) ;
if ( VisYearAlbum = = 2 )
{
tmptext = child - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( unknownText )
tmptext = " " ;
}
qb . addMatch ( q_cat2 , tmptext , false , true ) ;
qb . setGoogleFilter ( q_cat1 | q_cat2 | q_cat3 | QueryBuilder : : tabSong , m_filter ) ;
if ( VisYearAlbum = = 1 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
if ( ! sampler )
qb . sortBy ( q_cat1 , QueryBuilder : : valName ) ;
if ( VisYearAlbum = = 2 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
qb . sortBy ( q_cat2 , QueryBuilder : : valName ) ;
if ( VisYearAlbum = = 3 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
if ( q_cat3 ! = QueryBuilder : : tabSong )
qb . sortBy ( q_cat3 , QueryBuilder : : valName ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTrack ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
qb . setOptions ( QueryBuilder : : optRemoveDuplicates ) ;
values = qb . run ( ) ;
for ( uint i = 0 ; i < values . count ( ) ; i + + )
{
KURL tmp ;
tmp . setPath ( values [ i ] ) ;
list < < tmp ;
}
}
}
//third pass: category 2
for ( item = firstChild ( ) ; item ; item = item - > nextSibling ( ) )
for ( TQListViewItem * child = item - > firstChild ( ) ; child ; child = child - > nextSibling ( ) )
for ( TQListViewItem * grandChild = child - > firstChild ( ) ; grandChild ; grandChild = grandChild - > nextSibling ( ) )
if ( grandChild - > isSelected ( ) & & ! grandChild - > isExpandable ( ) & & ! child - > parent ( ) - > isSelected ( ) & & ! child - > isSelected ( ) )
list < < static_cast < CollectionItem * > ( grandChild ) - > url ( ) ;
//category 3
if ( m_cat3 = = IdNone )
{
for ( item = firstChild ( ) ; item ; item = item - > nextSibling ( ) )
for ( TQListViewItem * child = item - > firstChild ( ) ; child ; child = child - > nextSibling ( ) )
for ( TQListViewItem * grandChild = child - > firstChild ( ) ; grandChild ; grandChild = grandChild - > nextSibling ( ) )
for ( TQListViewItem * grandChild2 = grandChild - > firstChild ( ) ; grandChild2 ; grandChild2 = grandChild2 - > nextSibling ( ) )
if ( grandChild2 - > isSelected ( ) & & ! child - > parent ( ) - > isSelected ( ) & & ! child - > isSelected ( ) & & ! grandChild - > isSelected ( ) )
list < < static_cast < CollectionItem * > ( grandChild2 ) - > url ( ) ;
}
else {
for ( item = firstChild ( ) ; item ; item = item - > nextSibling ( ) )
for ( TQListViewItem * child = item - > firstChild ( ) ; child ; child = child - > nextSibling ( ) )
for ( TQListViewItem * grandChild = child - > firstChild ( ) ; grandChild ; grandChild = grandChild - > nextSibling ( ) )
if ( grandChild - > isSelected ( ) & & ! grandChild - > parent ( ) - > isSelected ( ) )
{
const bool sampler = static_cast < CollectionItem * > ( item ) - > isSampler ( ) ;
qb . clear ( ) ;
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate , TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) - translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
if ( ! sampler )
{
if ( q_cat1 = = IdArtist )
qb . setOptions ( QueryBuilder : : optNoCompilations ) ;
tmptext = static_cast < CollectionItem * > ( item ) - > getSQLText ( 0 ) ;
unknownText = tmptext . isEmpty ( ) ;
if ( VisYearAlbum = = 1 )
{
tmptext = item - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( unknownText )
tmptext = " " ;
}
qb . addMatch ( q_cat1 , tmptext , false , true ) ;
}
else
qb . setOptions ( QueryBuilder : : optOnlyCompilations ) ;
tmptext = static_cast < CollectionItem * > ( child ) - > getSQLText ( 0 ) ;
unknownText = tmptext . isEmpty ( ) ;
if ( VisYearAlbum = = 2 )
{
tmptext = child - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( unknownText )
tmptext = " " ;
}
qb . addMatch ( q_cat2 , tmptext , false , true ) ;
tmptext = static_cast < CollectionItem * > ( grandChild ) - > getSQLText ( 0 ) ;
unknownText = tmptext . isEmpty ( ) ;
if ( VisYearAlbum = = 3 )
{
tmptext = grandChild - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
qb . addMatch ( QueryBuilder : : tabYear , year , false , true ) ;
if ( unknownText )
tmptext = " " ;
}
qb . addMatch ( q_cat3 , tmptext , false , true ) ;
qb . setGoogleFilter ( q_cat1 | q_cat2 | q_cat3 | QueryBuilder : : tabSong , m_filter ) ;
if ( VisYearAlbum = = 1 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
if ( ! sampler )
qb . sortBy ( q_cat1 , QueryBuilder : : valName ) ;
if ( VisYearAlbum = = 2 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
qb . sortBy ( q_cat2 , QueryBuilder : : valName ) ;
if ( VisYearAlbum = = 3 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
qb . sortBy ( q_cat3 , QueryBuilder : : valName ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTrack ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
qb . setOptions ( QueryBuilder : : optRemoveDuplicates ) ;
values = qb . run ( ) ;
for ( uint i = 0 ; i < values . count ( ) ; i + + )
{
KURL tmp ;
tmp . setPath ( values [ i ] ) ;
list < < tmp ;
}
}
}
//category 3
for ( item = firstChild ( ) ; item ; item = item - > nextSibling ( ) )
for ( TQListViewItem * child = item - > firstChild ( ) ; child ; child = child - > nextSibling ( ) )
for ( TQListViewItem * grandChild = child - > firstChild ( ) ; grandChild ; grandChild = grandChild - > nextSibling ( ) )
for ( TQListViewItem * grandChild2 = grandChild - > firstChild ( ) ; grandChild2 ; grandChild2 = grandChild2 - > nextSibling ( ) )
if ( grandChild2 - > isSelected ( ) & & ! child - > parent ( ) - > isSelected ( ) & & ! child - > isSelected ( ) & & ! grandChild - > isSelected ( ) )
list < < static_cast < CollectionItem * > ( grandChild2 ) - > url ( ) ;
return list ;
}
void
CollectionView : : playlistFromURLs ( const KURL : : List & urls )
{
TQString suggestion ;
typedef TQListViewItemIterator It ;
It it ( this , It : : Visible | It : : Selected ) ;
if ( ( * it ) & & ! ( * ( + + it ) ) )
suggestion = ( * It ( this , It : : Visible | It : : Selected ) ) - > text ( 0 ) ;
else
suggestion = i18n ( " Untitled " ) ;
const TQString path = PlaylistDialog : : getSaveFileName ( suggestion ) ;
if ( path . isEmpty ( ) )
return ;
CollectionDB * db = CollectionDB : : instance ( ) ;
TQValueList < TQString > titles ;
TQValueList < int > lengths ;
for ( KURL : : List : : ConstIterator it = urls . constBegin ( ) , end = urls . constEnd ( ) ; it ! = end ; + + it )
{
int deviceid = MountPointManager : : instance ( ) - > getIdForUrl ( * it ) ;
KURL rpath ;
MountPointManager : : instance ( ) - > getRelativePath ( deviceid , * it , rpath ) ;
const TQString query = TQString ( " SELECT title, length FROM tags WHERE url = '%1' AND deviceid = %2; " )
. arg ( db - > escapeString ( rpath . path ( ) ) ) . arg ( deviceid ) ;
debug ( ) < < " media id: " < < deviceid < < " rpath: " < < rpath . path ( ) < < endl ;
TQStringList result = db - > query ( query ) ;
titles < < result [ 0 ] ;
lengths < < result [ 1 ] . toInt ( ) ;
}
if ( PlaylistBrowser : : savePlaylist ( path , urls , titles , lengths ) )
PlaylistWindow : : self ( ) - > showBrowser ( " PlaylistBrowser " ) ;
}
TQPixmap
CollectionView : : iconForCategory ( const int cat ) const
{
TQString icon ;
switch ( cat )
{
case IdAlbum :
icon = " cdrom_unmount " ;
break ;
case IdVisYearAlbum :
icon = " cdrom_unmount " ;
break ;
case IdArtist :
icon = " personal " ;
break ;
case IdComposer :
icon = " personal " ;
break ;
case IdGenre :
icon = " kfm " ;
break ;
case IdYear :
icon = " history " ;
break ;
case IdLabel :
icon = " kfm " ;
break ;
}
return TDEGlobal : : iconLoader ( ) - > loadIcon ( icon , TDEIcon : : Toolbar , TDEIcon : : SizeSmall ) ;
}
TQString
CollectionView : : captionForCategory ( const int cat ) const
{
switch ( cat )
{
case IdAlbum :
return i18n ( " Album " ) ;
break ;
case IdVisYearAlbum :
return i18n ( " Year " ) + i18n ( " - " ) + i18n ( " Album " ) ;
break ;
case IdArtist :
return i18n ( " Artist " ) ;
break ;
case IdComposer :
return i18n ( " Composer " ) ;
break ;
case IdGenre :
return i18n ( " Genre " ) ;
break ;
case IdYear :
return i18n ( " Year " ) ;
break ;
case IdLabel :
return i18n ( " Label " ) ;
break ;
}
return TQString ( ) ;
}
TQString
CollectionView : : captionForTag ( const Tag tag ) const
{
TQString caption ;
switch ( tag )
{
case Artist : caption = i18n ( " Artist " ) ; break ;
case Album : caption = i18n ( " Album " ) ; break ;
case Genre : caption = i18n ( " Genre " ) ; break ;
case Title : caption = i18n ( " Title " ) ; break ;
case Length : caption = i18n ( " Length " ) ; break ;
case DiscNumber : caption = i18n ( " Disc Number " ) ; break ;
case Track : caption = i18n ( " Track " ) ; break ;
case Year : caption = i18n ( " Year " ) ; break ;
case Comment : caption = i18n ( " Comment " ) ; break ;
case Composer : caption = i18n ( " Composer " ) ; break ;
case Playcount : caption = i18n ( " Playcount " ) ; break ;
case Score : caption = i18n ( " Score " ) ; break ;
case Rating : caption = i18n ( " Rating " ) ; break ;
case Filename : caption = i18n ( " Filename " ) ; break ;
case Firstplay : caption = i18n ( " First Play " ) ; break ;
case Lastplay : caption = i18n ( " Last Play " ) ; break ;
case Modified : caption = i18n ( " Modified Date " ) ; break ;
case Bitrate : caption = i18n ( " Bitrate " ) ; break ;
case Filesize : caption = i18n ( " File Size " ) ; break ;
case BPM : caption = i18n ( " BPM " ) ; break ;
default : break ;
}
return caption ;
}
//////////////////////////////
// For iPod-style navigation
//////////////////////////////
/*
* Overview
* - - - - - - - -
*
* The iPod - style navigation is a ( 1 , 2 , or ) 3 - tier filtering process
* ( depending on the current " Group By " setting ) . For concreteness
* let ' s say the user is grouping by Genre , Artist , Album . The first
* screen he is presented with is a list of the genres , along with an
* " All genres " option ( unless there is only one genre ) . He selects
* one or more genres , and clicks the right arrow in the toolbar . He
* is then presented with a list of albums whose genre matches one of
* the genres he has just chosen , unless he has chosen " All genres " ,
* in which case all albums are shown . This is repeated until he gets
* to an actual track list . If the user clicks the left arrow , he is
* taken back to the previous screen , with his previous selection
* still intact .
*
*
* Interface
* - - - - - - - - -
*
* The two main actions the user can perform are " browse forward " and
* " browse backward " , otherwise known as increment and decrement the
* browse depth . There is a small toolbar with these two buttons
* located to the right of the time filter combobox , which is enabled
* in iPod view mode . If the user is not viewing tracks , there is
* also a small browse forward button to the right of each entry on
* his screen . If the user is viewing tracks , the browse forward
* action adds the currently selected tracks to the playlist .
* Pressing the right or left keys is an alternate way of browsing
* forward or backward . At any point the user may drag and drop , press
* return , or double - click to add the current selection to the playlist ;
* the logic for this is explained in a comment in listSelected ( ) . If
* divider mode is on , dividers are added as expected when not in track
* mode .
*
*
* Related methods
* - - - - - - - - - - - - - - -
*
* CollectionBrowser : : ipodToolbar ( )
* - - ( de ) activate the toolbar with the browse buttons
* CollectionView : : keyPressEvent ( )
* - - handle the Left and Right keys , as well as the Up and Down keys
* to allow for wrapping around and skipping dividers
* CollectionView : : renderView ( )
* - - logic for applying the current filter and adding the matching
* items to the screen
* CollectionView : : ipodItemClicked ( )
* - - slot for activating the browse forward action when the right
* arrow on a listview item is clicked
* CollectionView : : setViewMode ( )
* - - enable / disable iPod toolbar
* CollectionView : : updateColumnHeader ( )
* CollectionView : : listSelected ( )
* - - apply current filters and current selection to get a track
* list , and order it correctly
* CollectionView : : allForCategory ( )
* - - return the text for the " All " item in the current category
* CollectionView : : incrementDepth ( )
* - - save the current selection away as a filter , and remember it
* in case the user browses back so we can reselect
* CollectionView : : decrementDepth ( )
* - - delete the current filter and reselect the saved selection
* CollectionView : : resetIpodDepth ( )
* - - reset the iPod view mode to the first screen
* CollectionView : : buildIpodQuery ( )
* - - adds query and sort criteria to a QueryBuilder , based on the
* saved filters
* CollectionView : : selectIpodItems ( )
* - - if we ' ve just browsed forward , select the All item ( or the
* unique item if there ' s only one ) . If we ' ve just browsed back ,
* reselect the saved selection .
* CollectionView : : ipodIncrementIcon ( ) , CollectionView : : ipodDecrementIcon ( )
* - - returns a TQPixmap of the small version of the right , resp . left
* arrow buttons
* CollectionView : : viewportResizeEvent ( )
* CollectionItem : : compare ( )
* - - in iPod mode the " All " item goes first
*
*/
// Returns the text for the "All" filter option for the given category
// and the number of items in that category.
TQString
CollectionView : : allForCategory ( const int cat , const int num ) const
{
switch ( cat )
{
// The singular forms shouldn't get used
case IdAlbum :
case IdVisYearAlbum :
return i18n ( " Album " , " All %n Albums " , num ) ;
break ;
case IdArtist :
return i18n ( " Artist " , " All %n Artists " , num ) ;
break ;
case IdComposer :
return i18n ( " Composer " , " All %n Composers " , num ) ;
break ;
case IdGenre :
return i18n ( " Genre " , " All %n Genres " , num ) ;
break ;
case IdYear :
return i18n ( " Year " , " All %n Years " , num ) ;
break ;
case IdLabel :
return i18n ( " Label " , " All %n Labels " , num ) ;
break ;
}
return TQString ( ) ;
}
// This slot is called when the "browse right" action is activated,
// and also in listSelected(). It is responsible for saving the
// current selection in two ways: first, it saves the selection as
// a list of query match criteria in m_ipodFilters and m_ipodFilterYear,
// and second, it saves the currently selected items, the current item,
// and the screen position in m_ipodSelected, m_ipodCurrent, and
// m_ipodTopItem.
// The reason there's a separate m_ipodFilterYear which is not an
// array of lists, is because if one of the categories is IdVisYearAlbum,
// we want to save the album filter separately from the year filter;
// since there's only one category that is IdVisYearAlbum, we don't need
// an array of years.
// The main reason the cache data (m_ipodSelected, etc.) is separate
// from the filter data (m_ipodFilters, etc.), apart from the fact it's
// easier to code this way, is that the filter data must be deleted
// immediately upon browsing left, whereas the cache data must not.
// If we're in track mode, this just inserts the currently selected
// tracks into the playlist.
void
CollectionView : : incrementDepth ( bool rerender /*= true*/ )
{
if ( m_viewMode ! = modeIpodView )
return ;
if ( selectedItems ( ) . count ( ) = = 0 )
return ;
// Track mode?
if ( m_currentDepth = = trackDepth ( ) )
{
Playlist : : instance ( ) - > insertMedia ( listSelected ( ) , Playlist : : Unique | Playlist : : Append ) ;
return ;
}
m_parent - > m_ipodDecrement - > setEnabled ( true ) ;
// We're not in track mode
int catArr [ 3 ] = { m_cat1 , m_cat2 , m_cat3 } ;
int cat = catArr [ m_currentDepth ] ;
// Clear filters and cache data at this level
m_ipodFilters [ m_currentDepth ] . clear ( ) ;
if ( cat = = IdVisYearAlbum )
m_ipodFilterYear . clear ( ) ;
m_ipodSelected [ m_currentDepth ] . clear ( ) ;
m_ipodCurrent [ m_currentDepth ] = TQString ( ) ;
m_ipodTopItem [ m_currentDepth ] = TQString ( ) ;
// Save the current item
if ( currentItem ( ) )
m_ipodCurrent [ m_currentDepth ] = currentItem ( ) - > text ( 0 ) ;
//cache viewport's top item
TQListViewItem * item = itemAt ( TQPoint ( 0 , 0 ) ) ;
if ( item )
m_ipodTopItem [ m_currentDepth ] = item - > text ( 0 ) ;
// Figure out the next filter, and save the current selection
TQPtrList < TQListViewItem > selected = selectedItems ( ) ;
TQPtrList < TQListViewItem > : : iterator it = selected . begin ( ) ;
while ( it ! = selected . end ( ) )
{
CollectionItem * item = dynamic_cast < CollectionItem * > ( * it ) ;
+ + it ;
if ( item = = 0 )
continue ;
// No filter if "All" is selected
if ( item - > isSampler ( ) )
{
m_ipodFilters [ m_currentDepth ] . clear ( ) ;
if ( cat = = IdVisYearAlbum )
m_ipodFilterYear . clear ( ) ;
// If "All" is selected then don't bother saving this
// selection, since All will then be reselected automatically
// in selectIpodItems()
m_ipodSelected [ m_currentDepth ] . clear ( ) ;
m_ipodCurrent [ m_currentDepth ] = TQString ( ) ;
break ;
}
if ( cat = = IdVisYearAlbum )
{
TQString tmptext = item - > text ( 0 ) ;
TQString year = tmptext . left ( tmptext . find ( i18n ( " - " ) ) ) ;
yearAlbumCalc ( year , tmptext ) ;
if ( ! item - > isUnknown ( ) )
m_ipodFilters [ m_currentDepth ] < < tmptext ;
else
m_ipodFilters [ m_currentDepth ] < < " " ;
m_ipodFilterYear < < year ; // May be ""
}
else
m_ipodFilters [ m_currentDepth ] < < item - > getSQLText ( 0 ) ;
// Save the selection
m_ipodSelected [ m_currentDepth ] < < item - > text ( 0 ) ;
}
m_currentDepth + + ;
if ( rerender )
{
updateColumnHeader ( ) ;
m_ipodIncremented = 1 ;
renderView ( true ) ;
}
}
// This slot is basically responsible for undoing whatever
// incrementDepth did. Namely, it deletes the filter at
// the previous level; selectIpodItems() will then be called
// from renderView() to reselect the remembered selection.
void
CollectionView : : decrementDepth ( bool rerender /*= true*/ )
{
if ( m_viewMode ! = modeIpodView )
return ;
if ( m_currentDepth < = 0 )
return ;
m_currentDepth - - ;
m_parent - > m_ipodDecrement - > setEnabled ( m_currentDepth > 0 ) ;
m_ipodFilters [ m_currentDepth ] . clear ( ) ;
int catArr [ 3 ] = { m_cat1 , m_cat2 , m_cat3 } ;
int cat = catArr [ m_currentDepth ] ;
if ( cat = = IdVisYearAlbum )
m_ipodFilterYear . clear ( ) ;
// Clear the selection on higher levels
for ( int i = m_currentDepth + 1 ; i < 3 ; + + i )
{
m_ipodSelected [ i ] . clear ( ) ;
m_ipodCurrent [ i ] = TQString ( ) ;
m_ipodTopItem [ i ] = TQString ( ) ;
}
if ( rerender )
{
m_ipodIncremented = 2 ;
updateColumnHeader ( ) ;
renderView ( true ) ;
}
}
// This resets the ipod view mode to the first screen.
// Call updateColumnHeader() as well whenever you run this
void
CollectionView : : resetIpodDepth ( void )
{
m_currentDepth = 0 ;
m_ipodFilterYear . clear ( ) ;
m_ipodFilters [ 0 ] . clear ( ) ;
m_ipodFilters [ 1 ] . clear ( ) ;
m_ipodFilters [ 2 ] . clear ( ) ;
m_ipodIncremented = 1 ;
m_parent - > m_ipodDecrement - > setEnabled ( false ) ;
}
// This method is the querying workhorse for the iPod browsing code.
// It is used both to populate the content browser (in renderView())
// and to generate track lists (in listSelected()). This method only
// runs qb.addMatch() and qb.sortBy() (as well as one qb.addFilter()
// below); the caller should run qb.setGoogleFilter(),
// qb.addReturnValue(), etc.
// The sorting is as follows: if recursiveSort is true (for
// listSelected()), then it sorts first by m_cat1, then by m_cat2,
// then m_cat3, then track; if recursiveSort is false (for
// renderView()), it only sorts by the category at m_currentDepth.
// Tracks are sorted by track number first if either of the two
// following conditions hold:
// (i) recursiveSort is true and one of the categories is (Year +) Album
// (ii) recursiveSort is false and only one album is selected
// This most closely mimics the behavior of the list view, as well as
// an actual iPod.
// The compilationsOnly argument does *not* set the onlyCompilations
// option (setting options is up to the caller); all it does is disable
// sorting by artist if recursiveSort is on. This is because when doing
// a compilations-only search, all tracks should behave as if the artist
// were Various Artists for sorting purposes.
void
CollectionView : : buildIpodQuery ( QueryBuilder & qb , int depth , TQStringList filters [ 3 ] ,
TQStringList filterYear , bool recursiveSort /*= false*/ , bool compilationsOnly /*= false*/ )
{
int catArr [ 3 ] = { m_cat1 , m_cat2 , m_cat3 } ;
int q_cat ;
bool stillFiltering = ( depth < trackDepth ( ) ) ;
bool SortbyTrackFirst = false ;
// First apply the filters from previous screens
for ( int i = 0 ; i < depth ; + + i )
{
q_cat = catArr [ i ] ;
if ( q_cat = = IdVisYearAlbum )
{
q_cat = IdAlbum ;
if ( filters [ i ] . count ( ) > 0 )
{
// This is very annoying -- we have to do an OR of queries
// of the form (album = ? AND year = ??)
TQStringList : : iterator album = filters [ i ] . begin ( ) ;
TQStringList : : iterator year = filterYear . begin ( ) ;
qb . beginOR ( ) ;
while ( album ! = filters [ i ] . end ( ) )
{
qb . beginAND ( ) ;
qb . addMatch ( QueryBuilder : : tabAlbum , * album , false , true ) ;
qb . addMatch ( QueryBuilder : : tabYear , * year , false , true ) ;
qb . endAND ( ) ;
+ + album ;
+ + year ;
}
qb . endOR ( ) ;
}
if ( recursiveSort )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
}
else
{
if ( filters [ i ] . count ( ) > 0 )
qb . addMatches ( q_cat , filters [ i ] , false , true ) ;
}
// Don't sort by artist if we're getting compilations
if ( recursiveSort
& & ! ( compilationsOnly & & q_cat = = IdArtist ) )
qb . sortBy ( q_cat , QueryBuilder : : valName ) ;
// Sort by track first subject to the conditions described above
if ( q_cat = = IdAlbum & &
( filters [ i ] . count ( ) = = 1 | | recursiveSort ) )
SortbyTrackFirst = true ;
}
// Now add the non-recursive sort-by
if ( stillFiltering ) // Are we showing a category?
{
q_cat = catArr [ depth ] ;
if ( q_cat = = IdVisYearAlbum )
{
q_cat = IdAlbum ;
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
}
qb . sortBy ( q_cat , QueryBuilder : : valName ) ;
// ensure we don't get empty genres/albums/etc due to tag changes
qb . addFilter ( QueryBuilder : : tabSong , TQString ( ) ) ;
}
// ... or are we showing tracks?
else
{
if ( SortbyTrackFirst ) {
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTrack ) ;
}
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTitle ) ;
if ( ! SortbyTrackFirst ) {
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valDiscNumber ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTrack ) ;
}
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
}
}
// This method is responsible for either selecting the "All" item
// if the iPod depth has been incremented, or selecting the previously
// remembered items if the depth has been decremented, depending on
// m_ipodIncremented, m_ipodSelected, m_ipodCurrent, and m_ipodTopItem.
// Note that if the previously selected items have disappeared (due to
// a GoogleFilter being applied, e.g.) then we select the "All" item by
// default.
// Note that if there is only one item in the list then there is no
// All option, so we select the unique item.
void
CollectionView : : selectIpodItems ( void )
{
if ( m_viewMode ! = modeIpodView | |
m_ipodIncremented = = 0 )
{
m_ipodIncremented = 0 ;
return ;
}
// If we've just decremented the iPod view depth then remember
// the selection and the current item last time we incremented.
// Note that a filter or something may have happened between then
// and now, so we should allow for those items no longer being
// here (in which case we pass through to the code below)
if ( m_ipodIncremented = = 2 )
{
// Pedantry -- presumably we're not at track depth!
if ( m_currentDepth = = trackDepth ( ) )
{
m_ipodIncremented = 0 ;
return ;
}
// If there's no selection or the selected items have
// disappeared, pass through to the code below
if ( m_ipodSelected [ m_currentDepth ] . count ( ) = = 0 )
m_ipodIncremented = 1 ;
else
{
TDEListView : : selectAll ( false ) ;
int selected = 0 ;
TQStringList : : iterator it = m_ipodSelected [ m_currentDepth ] . begin ( ) ;
while ( it ! = m_ipodSelected [ m_currentDepth ] . end ( ) )
{
TQListViewItem * item = findItem ( * it , 0 ) ;
+ + it ;
if ( ! item )
continue ;
selected + + ;
// If the saved currentItem has disappeared, it's more
// intuitive if the last selected item is current.
setCurrentItem ( item ) ;
item - > setSelected ( true ) ;
setSelectionAnchor ( item ) ;
}
// Pass through to below
if ( selected = = 0 )
m_ipodIncremented = 1 ;
else
{
// Remember the current item and scroll position
if ( ! m_ipodTopItem [ m_currentDepth ] . isEmpty ( ) & &
! m_ipodTopItem [ m_currentDepth ] . isNull ( ) )
{
//scroll to previous viewport top item
TQListViewItem * item = findItem ( m_ipodTopItem [ m_currentDepth ] , 0 ) ;
if ( item )
setContentsPos ( 0 , itemPos ( item ) ) ;
}
if ( ! m_ipodCurrent [ m_currentDepth ] . isEmpty ( ) & &
! m_ipodCurrent [ m_currentDepth ] . isNull ( ) )
{
TQListViewItem * item = findItem ( m_ipodCurrent [ m_currentDepth ] , 0 ) ;
if ( item )
setCurrentItem ( item ) ;
}
}
}
}
// If we've just incremented the iPod view depth (or are displaying
// the iPod window for the first time) then automatically select the
// All option (or the only element of the list) for keyboard
// navigation
if ( m_ipodIncremented = = 1 )
{
TDEListView : : selectAll ( false ) ;
TQListViewItem * item = firstChild ( ) ;
// There will be a divider in the first slot if there is only
// one item in the list and m_showDivider is on
while ( item & & dynamic_cast < DividerItem * > ( item ) )
item = item - > itemBelow ( ) ;
if ( item )
{
setCurrentItem ( item ) ;
item - > setSelected ( true ) ;
setSelectionAnchor ( item ) ;
setContentsPos ( 0 , itemPos ( item ) ) ;
}
}
m_ipodIncremented = 0 ;
}
// Convenience methods for returning the correct (small version of)
// the browse forward / backward buttons
TQPixmap
CollectionView : : ipodIncrementIcon ( void )
{
return SmallIcon ( Amarok : : icon ( " fastforward " ) ) ;
}
TQPixmap
CollectionView : : ipodDecrementIcon ( void )
{
return SmallIcon ( Amarok : : icon ( " rewind " ) ) ;
}
void
CollectionView : : setCompilation ( const KURL : : List & urls , bool compilation )
{
//visual feedback
TQApplication : : setOverrideCursor ( KCursor : : waitCursor ( ) ) ;
//Set it in the DB. We don't need to update the view now as we do it at the end.
CollectionDB : : instance ( ) - > setCompilation ( urls , compilation , false ) ;
foreachType ( KURL : : List , urls ) {
if ( ! TagLib : : File : : isWritable ( TQFile : : encodeName ( ( * it ) . path ( ) ) ) )
continue ;
MetaBundle mb ( * it ) ;
mb . setCompilation ( compilation ? MetaBundle : : CompilationYes : MetaBundle : : CompilationNo ) ;
if ( mb . save ( ) ) {
mb . updateFilesize ( ) ;
//update the collection db, since filesize might have changed
CollectionDB : : instance ( ) - > updateTags ( mb . url ( ) . path ( ) , mb , false ) ;
}
}
//visual feedback
TQApplication : : restoreOverrideCursor ( ) ;
if ( ! urls . isEmpty ( ) ) renderView ( true ) ;
}
void
CollectionView : : cacheView ( )
{
//free cache
m_cacheOpenItemPaths . clear ( ) ;
//Store the current item
m_cacheCurrentItem = makeStructuredNameList ( currentItem ( ) ) ;
//cache expanded/open items
if ( m_viewMode = = modeTreeView ) {
TQListViewItemIterator it ( this ) ;
while ( it . current ( ) ) {
TQListViewItem * item = it . current ( ) ;
if ( item - > isOpen ( ) ) {
//construct path to item
TQStringList itemPath ;
for ( const TQListViewItem * i = item ; i ; i = i - > parent ( ) )
itemPath . prepend ( i - > text ( 0 ) ) ;
m_cacheOpenItemPaths . append ( itemPath ) ;
}
+ + it ;
}
}
//cache viewport's top item
m_cacheViewportTopItem = makeStructuredNameList ( itemAt ( TQPoint ( 0 , 0 ) ) ) ;
}
void
CollectionView : : restoreView ( )
{
//expand cached items
if ( m_viewMode = = modeTreeView ) {
TQValueList < TQStringList > : : const_iterator it ;
for ( it = m_cacheOpenItemPaths . begin ( ) ; it ! = m_cacheOpenItemPaths . end ( ) ; + + it ) {
TQListViewItem * item = findItem ( ( * it ) [ 0 ] , 0 ) ;
if ( item )
item - > setOpen ( true ) ;
for ( uint i = 1 ; i < ( * it ) . count ( ) & & item ; + + i ) {
item = item - > firstChild ( ) ;
while ( item ) {
if ( item - > text ( 0 ) = = ( * it ) [ i ] )
item - > setOpen ( true ) ;
item = item - > nextSibling ( ) ;
}
}
}
}
//scroll to previous viewport top item
TQListViewItem * item = findFromStructuredNameList ( m_cacheViewportTopItem ) ;
if ( item )
setContentsPos ( 0 , itemPos ( item ) ) ;
//Restore a selected item (all levels of the tree stored to fully specify which item)
item = findFromStructuredNameList ( m_cacheCurrentItem ) ;
if ( item )
{
setCurrentItem ( item ) ;
item - > setSelected ( true ) ;
// More intuitive if shift-click selects from current selection
setSelectionAnchor ( item ) ;
}
//free cache
m_cacheOpenItemPaths . clear ( ) ;
m_cacheViewportTopItem = TQStringList ( ) ;
m_cacheCurrentItem = TQStringList ( ) ;
}
TQStringList
CollectionView : : makeStructuredNameList ( TQListViewItem * item ) const
{
TQStringList nameList ;
for ( TQListViewItem * current = item ; current ; current = current - > parent ( ) )
nameList . push_front ( current - > text ( 0 ) ) ;
return nameList ;
}
TQListViewItem *
CollectionView : : findFromStructuredNameList ( const TQStringList & nameList ) const
{
TQListViewItem * item ( firstChild ( ) ) ;
bool firstTime = true ;
foreach ( nameList )
{
if ( ! firstTime )
item = item - > firstChild ( ) ;
else
firstTime = false ;
while ( item & & item - > text ( 0 ) ! = * it )
item = item - > nextSibling ( ) ;
if ( ! item )
{
debug ( ) < < " Could not find expected element to select: " < < nameList < < endl ;
break ;
}
}
return nameList . isEmpty ( ) ? 0 : item ;
}
// Small function aimed to convert Eagles, The -> The Eagles (and back again)
// TODO Internationlise
void
CollectionView : : manipulateThe ( TQString & str , bool reverse )
{
if ( reverse )
{
TQString begin = str . left ( 3 ) ;
str = TQString ( str . append ( " , %1 " ) ) . arg ( begin ) ;
str = str . mid ( 4 ) ;
return ;
}
if ( ! endsInThe ( str ) )
return ;
TQString end = str . right ( 3 ) ;
str = TQString ( str . prepend ( " %1 " ) ) . arg ( end ) ;
uint newLen = str . length ( ) - end . length ( ) - 2 ;
str . truncate ( newLen ) ;
}
bool
CollectionView : : endsInThe ( const TQString & text )
{
return text . endsWith ( " , the " , false ) ;
}
// avoid code duplication
void
CollectionView : : yearAlbumCalc ( TQString & year , TQString & text )
{
if ( year = = " \ ? " )
year = " " ;
text = text . right ( text . length ( ) -
text . find ( i18n ( " - " ) ) -
i18n ( " - " ) . length ( ) ) ;
}
void
CollectionView : : viewportPaintEvent ( TQPaintEvent * e )
{
TDEListView : : viewportPaintEvent ( e ) ;
// Superimpose bubble help for Flat-View mode:
if ( m_viewMode = = modeFlatView & & childCount ( ) = = 0 )
{
TQPainter p ( viewport ( ) ) ;
TQSimpleRichText t ( i18n (
" <div align=center> "
" <h3>Flat-View Mode</h3> "
" To enable the Flat-View mode, please enter search terms in the search line above. "
" </div> " ) , TQApplication : : font ( ) ) ;
t . setWidth ( width ( ) - 50 ) ;
const uint w = t . width ( ) + 20 ;
const uint h = t . height ( ) + 20 ;
p . setBrush ( colorGroup ( ) . background ( ) ) ;
p . drawRoundRect ( 15 , 15 , w , h , ( 8 * 200 ) / w , ( 8 * 200 ) / h ) ;
t . draw ( & p , 20 , 20 , TQRect ( ) , colorGroup ( ) ) ;
}
}
void
CollectionView : : updateTrackDepth ( ) {
bool m3 = ( m_cat3 = = IdNone ) ;
bool m2 = ( m_cat2 = = IdNone ) ;
bool m1 = ( m_cat1 = = IdNone ) ;
if ( m3 | | m2 | | m1 ) {
//The wanted depth, is the lowest IdNone
if ( m3 )
m_trackDepth = 2 ;
if ( m2 )
m_trackDepth = 1 ;
if ( m1 )
m_trackDepth = 0 ;
}
else // If there's no IdNone, then it's 3
m_trackDepth = 3 ;
}
void
CollectionView : : viewportResizeEvent ( TQResizeEvent * e )
{
if ( m_viewMode ! = modeIpodView )
{
header ( ) - > blockSignals ( true ) ;
const double width = e - > size ( ) . width ( ) ;
int visibleColumns = 0 ;
for ( int i = 0 ; i < columns ( ) ; + + i )
if ( columnWidth ( i ) ! = 0 )
visibleColumns + + ;
int correct = e - > size ( ) . width ( ) - ( e - > size ( ) . width ( ) / visibleColumns ) * visibleColumns ;
if ( m_viewMode = = modeFlatView )
m_flatColumnWidths . clear ( ) ;
if ( visibleColumns ! = 0 ) {
for ( int c = 0 ; c < columns ( ) ; + + c ) {
int w = columnWidth ( c ) ? static_cast < int > ( width / visibleColumns ) : 0 ;
if ( w > 0 )
{
w + = correct ;
correct = 0 ;
setColumnWidth ( c , w ) ;
}
if ( m_viewMode = = modeFlatView )
m_flatColumnWidths . push_back ( w ) ;
}
}
header ( ) - > blockSignals ( false ) ;
}
// iPod-mode header adjustment code
else
{
// Don't use header()->adjustHeaderSize(), since that doesn't
// do a very good job. Instead we treat the browse-forward-button
// column as rigid, and stretch the text column to exactly fit
// the width.
int width = visibleWidth ( ) ;
int col1width = 0 ;
// No column 1 for tracks
if ( m_currentDepth ! = trackDepth ( ) )
col1width = columnWidth ( 1 ) ;
setColumnWidth ( 0 , width - col1width ) ;
}
// Needed for correct redraw of bubble help
triggerUpdate ( ) ;
}
bool
CollectionView : : eventFilter ( TQObject * o , TQEvent * e )
{
if ( TQT_BASE_OBJECT ( o ) = = TQT_BASE_OBJECT ( header ( ) )
& & e - > type ( ) = = TQEvent : : MouseButtonPress
& & TQT_TQMOUSEEVENT ( e ) - > button ( ) = = Qt : : RightButton
& & m_viewMode = = modeFlatView )
{
TDEPopupMenu popup ;
popup . setCheckable ( true ) ;
popup . insertTitle ( i18n ( " Flat View Columns " ) , /*id*/ - 1 , /*index*/ 1 ) ;
for ( int i = 0 ; i < columns ( ) ; + + i )
{
popup . insertItem ( captionForTag ( static_cast < Tag > ( i ) ) , i ) ;
popup . setItemChecked ( i , ( columnWidth ( i ) ! = 0 ) ) ;
}
//title column should always be shown
popup . setItemEnabled ( Title , false ) ;
popup . setItemVisible ( Score , AmarokConfig : : useScores ( ) ) ;
popup . setItemVisible ( Rating , AmarokConfig : : useRatings ( ) ) ;
const int returnID = popup . exec ( TQT_TQMOUSEEVENT ( e ) - > globalPos ( ) ) ;
if ( returnID ! = - 1 )
{
if ( columnWidth ( returnID ) = = 0 ) {
adjustColumn ( returnID ) ; // show column
header ( ) - > setResizeEnabled ( true , returnID ) ;
renderView ( true ) ;
}
else {
setColumnWidth ( returnID , 0 ) ; // hide column
header ( ) - > setResizeEnabled ( false , returnID ) ;
}
//manage column widths
TQResizeEvent rev ( size ( ) , TQSize ( ) ) ;
viewportResizeEvent ( & rev ) ;
}
m_flatColumnWidths . clear ( ) ;
for ( int c = 0 ; c < columns ( ) ; + + c )
m_flatColumnWidths . push_back ( columnWidth ( c ) ) ;
return true ;
}
return TDEListView : : eventFilter ( o , e ) ;
}
uint CollectionView : : translateTimeFilter ( uint filterMode )
{
uint filterSecs = 0 ;
switch ( filterMode )
{
case 1 :
// added today
filterSecs = 60 * 60 * 24 ;
break ;
case 2 :
// added within one week
filterSecs = 60 * 60 * 24 * 7 ;
break ;
case 3 :
// added within one month
filterSecs = 60 * 60 * 24 * 30 ;
break ;
case 4 :
// added within three months
filterSecs = 60 * 60 * 24 * 91 ;
break ;
case 5 :
// added within one year
filterSecs = 60 * 60 * 24 * 365 ;
break ;
}
return filterSecs ;
}
void
CollectionView : : renderFlatModeView ( bool /*=false*/ )
{
TQStringList values ;
QueryBuilder qb ;
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate , TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) - translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
if ( translateTimeFilter ( timeFilter ( ) ) < = 0
& & ( m_filter . length ( ) < 3 | | ( ! m_filter . contains ( " " ) & & m_filter . endsWith ( " : " ) ) ) )
{
// Redraw bubble help
triggerUpdate ( ) ;
return ;
}
TQValueList < Tag > visibleColumns ;
for ( int c = 0 ; c < columns ( ) ; + + c )
if ( columnWidth ( c ) ! = 0 )
{
visibleColumns . append ( static_cast < Tag > ( c ) ) ;
}
//always fetch URL
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
//device automatically added
int filterTables = 0 ;
for ( TQValueList < Tag > : : ConstIterator it = visibleColumns . constBegin ( ) ; it ! = visibleColumns . constEnd ( ) ; + + it )
{
switch ( * it )
{
case Artist :
qb . addReturnValue ( QueryBuilder : : tabArtist , QueryBuilder : : valName , true ) ;
filterTables | = QueryBuilder : : tabArtist ;
break ;
case Composer :
qb . addReturnValue ( QueryBuilder : : tabComposer , QueryBuilder : : valName , true ) ;
filterTables | = QueryBuilder : : tabComposer ;
break ;
case Album :
qb . addReturnValue ( QueryBuilder : : tabAlbum , QueryBuilder : : valName , true ) ;
filterTables | = QueryBuilder : : tabAlbum ;
break ;
case Genre :
qb . addReturnValue ( QueryBuilder : : tabGenre , QueryBuilder : : valName , true ) ;
filterTables | = QueryBuilder : : tabGenre ;
break ;
case Title :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valTitle , true ) ;
filterTables | = QueryBuilder : : tabSong ;
break ;
case Length :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valLength ) ;
filterTables | = QueryBuilder : : tabSong ;
break ;
case DiscNumber :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valDiscNumber ) ;
filterTables | = QueryBuilder : : tabSong ;
break ;
case Track :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valTrack ) ;
filterTables | = QueryBuilder : : tabSong ;
break ;
case Year :
qb . addReturnValue ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
filterTables | = QueryBuilder : : tabYear ;
break ;
case Comment :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valComment ) ;
filterTables | = QueryBuilder : : tabSong ;
break ;
case Playcount :
qb . addReturnValue ( QueryBuilder : : tabStats , QueryBuilder : : valPlayCounter ) ;
break ;
case Score :
qb . addReturnValue ( QueryBuilder : : tabStats , QueryBuilder : : valScore ) ;
break ;
case Rating :
qb . addReturnValue ( QueryBuilder : : tabStats , QueryBuilder : : valRating ) ;
break ;
case Filename :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valRelativePath ) ;
break ;
case Firstplay :
qb . addReturnValue ( QueryBuilder : : tabStats , QueryBuilder : : valCreateDate ) ;
break ;
case Lastplay :
qb . addReturnValue ( QueryBuilder : : tabStats , QueryBuilder : : valAccessDate ) ;
break ;
case Modified :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate ) ;
break ;
case Bitrate :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valBitrate ) ;
break ;
case Filesize :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valFilesize ) ;
break ;
case BPM :
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valBPM ) ;
filterTables | = QueryBuilder : : tabSong ;
break ;
default :
qb . addReturnValue ( QueryBuilder : : tabDummy , QueryBuilder : : valDummy ) ;
break ;
}
}
qb . setGoogleFilter ( filterTables , m_filter ) ;
qb . sortBy ( QueryBuilder : : tabSong , QueryBuilder : : valTitle ) ;
qb . setOptions ( QueryBuilder : : optRemoveDuplicates ) ;
//we leftjoin the query so it can return mysql NULL cells, i.e. for score and playcount
//this is an ugly hack - should be integrated in querybuilder itself instead.
TQString leftQuery = qb . query ( ) ;
leftQuery . replace ( " INNER JOIN " , " LEFT JOIN " ) ;
values = CollectionDB : : instance ( ) - > query ( leftQuery ) ;
//construct items
TQStringList : : ConstIterator it = values . constBegin ( ) ;
TQStringList : : ConstIterator end = values . constEnd ( ) ;
while ( it ! = end )
{
CollectionItem * item = new CollectionItem ( this ) ;
item - > setDragEnabled ( true ) ;
item - > setDropEnabled ( false ) ;
TQString rpath = * it ;
item - > setUrl ( MountPointManager : : instance ( ) - > getAbsolutePath ( ( * + + it ) . toInt ( ) , rpath ) ) ;
+ + it ;
for ( TQValueList < Tag > : : ConstIterator it_c = visibleColumns . constBegin ( ) ; it_c ! = visibleColumns . constEnd ( ) ; + + it_c )
{
switch ( * it_c )
{
case Length :
item - > setText ( * it_c , MetaBundle : : prettyLength ( ( * it ) . toInt ( ) , false ) ) ;
break ;
case Bitrate :
item - > setText ( * it_c , MetaBundle : : prettyBitrate ( ( * it ) . toInt ( ) ) ) ;
break ;
case Firstplay :
case Lastplay :
case Modified :
{
TQDateTime time = TQDateTime ( ) ;
time . setTime_t ( ( * it ) . toUInt ( ) ) ;
item - > setText ( * it_c , time . date ( ) . toString ( Qt : : LocalDate ) ) ;
break ;
}
case Playcount :
case Score :
item - > setText ( * it_c , ( * it ) . isNull ( ) ? " 0 " : ( * it ) ) ;
break ;
case Rating :
item - > setText ( * it_c , ( * it ) . isNull ( ) ? " 0 " : ( * it ) ) ;
break ;
case Filename :
item - > setText ( * it_c , KURL : : fromPathOrURL ( ( * it ) . right ( ( * it ) . length ( ) - 1 ) ) . filename ( ) ) ;
break ;
case Filesize :
item - > setText ( * it_c , MetaBundle : : prettyFilesize ( ( * it ) . toInt ( ) ) ) ;
break ;
default :
item - > setText ( * it_c , ( * it ) ) ;
break ;
}
+ + it ;
}
}
}
void
CollectionView : : renderTreeModeView ( bool /*=false*/ )
{
TQStringList values ;
QueryBuilder qb ;
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate , TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) - translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
setSorting ( 0 ) ;
int VisYearAlbum = - 1 ;
int VisLabel = - 1 ;
int q_cat1 = m_cat1 ;
int q_cat2 = m_cat2 ;
int q_cat3 = m_cat3 ;
if ( m_cat1 = = IdVisYearAlbum | |
m_cat2 = = IdVisYearAlbum | |
m_cat3 = = IdVisYearAlbum )
{
if ( m_cat1 = = IdVisYearAlbum )
{
VisYearAlbum = 1 ;
q_cat1 = IdAlbum ;
}
if ( m_cat2 = = IdVisYearAlbum )
{
VisYearAlbum = 2 ;
q_cat2 = IdAlbum ;
}
if ( m_cat3 = = IdVisYearAlbum )
{
VisYearAlbum = 3 ;
q_cat3 = IdAlbum ;
}
}
if ( m_cat1 = = IdLabel | |
m_cat2 = = IdLabel | |
m_cat3 = = IdLabel )
{
if ( m_cat1 = = IdLabel )
VisLabel = 1 ;
else if ( m_cat2 = = IdLabel )
VisLabel = 2 ;
else
VisLabel = 3 ;
}
TQPixmap pixmap = iconForCategory ( m_cat1 ) ;
qb . addReturnValue ( q_cat1 , QueryBuilder : : valName , true ) ;
if ( VisYearAlbum = = 1 )
qb . addReturnValue ( QueryBuilder : : tabYear , QueryBuilder : : valName , true ) ;
qb . setGoogleFilter ( q_cat1 | q_cat2 | q_cat3 | QueryBuilder : : tabSong , m_filter ) ;
if ( VisYearAlbum = = 1 )
qb . sortBy ( QueryBuilder : : tabYear , QueryBuilder : : valName ) ;
qb . sortBy ( q_cat1 , QueryBuilder : : valName ) ;
qb . setOptions ( QueryBuilder : : optRemoveDuplicates ) ;
if ( q_cat1 = = QueryBuilder : : tabArtist )
qb . setOptions ( QueryBuilder : : optNoCompilations ) ;
// ensure we don't get empty genres/albums/etc due to tag changes
qb . addFilter ( QueryBuilder : : tabSong , TQString ( ) ) ;
values = qb . run ( ) ;
//add items to the view
uint dividerCount = 0 ;
if ( values . count ( ) )
{
//keep track of headers already added
TQMap < TQString , bool > containedDivider ;
for ( TQStringList : : Iterator it = values . fromLast ( ) , begin = values . begin ( ) ; true ; - - it )
{
bool unknown = false ;
//For Year-Album
if ( VisYearAlbum = = 1 )
{
( * it ) = ( * it ) . isEmpty ( ) ? " ? " : ( * it ) + i18n ( " - " ) ;
TQStringList : : Iterator album = it ;
- - album ;
if ( ( * album ) . isEmpty ( ) )
{
unknown = true ;
( * it ) + = i18n ( " Unknown " ) ;
}
else
( * it ) + = * album ;
}
if ( ( * it ) . stripWhiteSpace ( ) . isEmpty ( ) )
{
if ( VisLabel = = 1 )
( * it ) = i18n ( " No Label " ) ;
else
( * it ) = i18n ( " Unknown " ) ;
unknown = true ;
}
else if ( m_showDivider )
{
//Dividers for "The Who" should be created as "W", not "T", because
//that's how we sort it
TQString actualStr = * it ;
if ( m_cat1 = = IdArtist & & actualStr . startsWith ( " the " , false ) )
manipulateThe ( actualStr , true ) ;
TQString headerStr = DividerItem : : createGroup ( actualStr , m_cat1 ) ;
if ( ! containedDivider [ headerStr ] & & ! headerStr . isEmpty ( ) )
{
containedDivider [ headerStr ] = true ;
( void ) new DividerItem ( this , headerStr , m_cat1 /*, m_sortYearsInverted*/ ) ;
dividerCount + + ;
}
}
CollectionItem * item = new CollectionItem ( this , m_cat1 , unknown ) ;
item - > setExpandable ( true ) ;
item - > setDragEnabled ( true ) ;
item - > setDropEnabled ( false ) ;
item - > setText ( 0 , * it ) ;
item - > setPixmap ( 0 , pixmap ) ;
//The structure of the return is different if Year - Album is format
if ( VisYearAlbum = = 1 )
- - it ;
if ( it = = begin )
break ;
}
}
//check if we need to add a Various Artists node
if ( q_cat1 = = QueryBuilder : : tabArtist )
{
qb . clear ( ) ;
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate ,
TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) -
translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
qb . addReturnValue ( q_cat1 , QueryBuilder : : valName , true ) ;
qb . setGoogleFilter ( q_cat1 | q_cat2 | q_cat3 | QueryBuilder : : tabSong , m_filter ) ;
qb . setOptions ( QueryBuilder : : optOnlyCompilations | QueryBuilder : : optRemoveDuplicates ) ;
qb . setLimit ( 0 , 1 ) ;
values = qb . run ( ) ;
if ( values . count ( ) )
{
// TDEListViewItem* x = new DividerItem(this, i18n( "Various" ), m_cat1);
// x->setExpandable(false);
// x->setDropEnabled( false );
// x->setSelectable(false);
CollectionItem * item = new CollectionItem ( this , m_cat1 , false , true ) ;
item - > setExpandable ( true ) ;
item - > setDragEnabled ( true ) ;
item - > setDropEnabled ( false ) ;
item - > setText ( 0 , i18n ( " Various Artists " ) ) ;
item - > setPixmap ( 0 , pixmap ) ;
}
}
//Algorithm to expand some items after a search in a pretty/useful way:
//Aim to expand all items with one child, but with a maximum limit to how many items
//should be shown in the listview ( maxRows) after which we give up. If an item has
//more than one child and we haven't reached the limit, we may end up expanding it
//later.
TQValueList < TQListViewItem * > couldOpen ;
int totalCount = childCount ( ) - dividerCount ;
const int maxRows = 20 ; //This seems like a fair limit for a 1024x768 screen
if ( totalCount < maxRows )
{
//Generate initial list of top list items to look at
for ( TQListViewItem * top = firstChild ( ) ; top ; top = top - > nextSibling ( ) )
{
if ( ! dynamic_cast < CollectionItem * > ( top ) )
continue ;
couldOpen . push_back ( top ) ;
}
//Expand suggested items and expand or enqueue their children until we run out of
//rows or have expanded everything
for ( TQValueList < TQListViewItem * > : : iterator it = couldOpen . begin ( ) ; it ! = couldOpen . end ( ) & & totalCount < maxRows ; + + it )
{
if ( ! ( * it ) - > isOpen ( ) )
( * it ) - > setOpen ( true ) ;
totalCount + = ( * it ) - > childCount ( ) ;
if ( ( * it ) - > firstChild ( ) - > isExpandable ( ) ) //Check we have not reached the bottom
{
for ( TQListViewItem * j = ( * it ) - > firstChild ( ) ; j & & totalCount < maxRows ; j = j - > nextSibling ( ) )
{
j - > setOpen ( true ) ;
if ( j - > childCount ( ) > 1 ) //More than one child - maybe later
{
j - > setOpen ( false ) ;
couldOpen . push_back ( j ) ;
}
else
{
//Prioritize expanding its children - add it immediately next
TQValueList < TQListViewItem * > : : iterator next = it ;
+ + next ;
couldOpen . insert ( next , j ) ;
+ + totalCount ;
}
}
}
}
}
//If the tree has only one branch, at least at the top, make the lowest child
//which has no siblings (other branches) become selected.
//Rationale: If you search for something and then clear the search bar, this way it
//will stay in view, assuming you only had one real result.
if ( childCount ( ) - dividerCount = = 1 )
{
TQListViewItem * item = firstChild ( ) ;
if ( dynamic_cast < DividerItem * > ( item ) ) //Skip a divider, if present
item = item - > nextSibling ( ) ;
for ( ; item ; item = item - > firstChild ( ) )
if ( ! item - > isOpen ( ) | | item - > childCount ( ) > 1 )
break ;
if ( item )
{
setCurrentItem ( item ) ;
item - > setSelected ( true ) ;
setSelectionAnchor ( item ) ;
}
}
removeDuplicatedHeaders ( ) ;
}
void
CollectionView : : removeDuplicatedHeaders ( )
{
/* Following code depends on the order! */
sort ( ) ;
TQValueList < DividerItem * > toDelete ;
DividerItem * current = 0 , * last = 0 ;
bool empty ;
TQListViewItem * item ;
/* If we have two consecutive headers, one of them is useless, and should be removed */
for ( item = firstChild ( ) , empty = false ; item ; item = item - > nextSibling ( ) )
{
if ( ( current = dynamic_cast < DividerItem * > ( item ) ) )
{
if ( empty )
{
if ( ! current - > text ( 0 ) . at ( 0 ) . isLetterOrNumber ( )
| | ( last - > text ( 0 ) . at ( 0 ) . isLetterOrNumber ( )
& & current - > text ( 0 ) . at ( 0 ) . unicode ( ) > last - > text ( 0 ) . at ( 0 ) . unicode ( ) ) )
toDelete + = current ;
else
{
toDelete + = last ;
last = current ;
}
}
else
last = current ;
empty = true ;
}
else
empty = false ;
}
for ( TQValueList < DividerItem * > : : iterator it = toDelete . begin ( ) ; it ! = toDelete . end ( ) ; + + it )
delete * it ;
}
// MODE IPODVIEW This is the heart of the iPod view mode code. It
// applies the current filters (as defined by previous "move
// forward" actions). If we're not viewing tracks (stillFiltering
// == true), then display the results in the standard order, with
// dividers if applicable, with an "All" (i.e., no filter) item if
// there is more than one result, and with "Unknown" items if
// there are any. Note that the "All" item is a CollectionItem
// with the Sampler bit set, since it behaves similar to the
// Various Artists node.
// If we are viewing tracks (stillFiltering ==
// false), then just apply all of the filters and show the
// selected tracks. The track ordering is similar to in list view
// mode; see the comments in buildIpodQuery() for details.
void
CollectionView : : renderIpodModeView ( bool /*=false*/ )
{
TQStringList values ;
QueryBuilder qb ;
if ( translateTimeFilter ( timeFilter ( ) ) > 0 )
qb . addFilter ( QueryBuilder : : tabSong , QueryBuilder : : valCreateDate , TQString ( ) . setNum ( TQDateTime : : currentDateTime ( ) . toTime_t ( ) - translateTimeFilter ( timeFilter ( ) ) ) , QueryBuilder : : modeGreater ) ;
int catArr [ 3 ] = { m_cat1 , m_cat2 , m_cat3 } ;
// stillFiltering is true when we're not viewing tracks
bool stillFiltering = ( m_currentDepth < trackDepth ( ) ) ;
// q_cat is the "query category" -- it's undefined if
// stillFiltering is true; otherwise it's the category
// at the current iPod viewing depth (m_currentDepth), unless
// that category is IdVisYearAlbum, in which case it's IdAlbum.
int q_cat = ( stillFiltering ? catArr [ m_currentDepth ] : catArr [ 0 ] ) ;
// m_cat is the current actual category -- it agrees with q_cat
// if and only if m_cat != IdVisYearAlbum
int m_cat = q_cat ;
// This is set to true if the current category is IdVisYearAlbum
// It is only used when stillFiltering == true.
bool VisYearAlbum = false ;
//This is set to true if the current category is IdLabel
bool VisLabel = false ;
if ( q_cat = = IdVisYearAlbum & & stillFiltering )
{
VisYearAlbum = true ;
q_cat = IdAlbum ;
}
if ( q_cat = = IdLabel & & stillFiltering )
VisLabel = true ;
// If we're viewing tracks, we don't want them to be sorted
// alphabetically, since we take great pains in
// buildIpodQuery() to have them returned to us from the
// database in the correct order.
setSorting ( stillFiltering ? 0 : - 1 ) ;
// Do the grunt work of building the query (this method is also
// called by listSelected() )
buildIpodQuery ( qb , m_currentDepth , m_ipodFilters , m_ipodFilterYear ) ;
if ( stillFiltering )
qb . setOptions ( QueryBuilder : : optRemoveDuplicates ) ;
int tables = 0 ;
for ( int i = 0 ; i < trackDepth ( ) ; + + i )
tables | = ( catArr [ i ] = = IdVisYearAlbum
? IdAlbum
: catArr [ i ] ) ;
qb . setGoogleFilter ( tables | QueryBuilder : : tabSong , m_filter ) ;
// Return values
if ( stillFiltering )
{
qb . addReturnValue ( q_cat , QueryBuilder : : valName , true ) ;
if ( VisYearAlbum )
qb . addReturnValue ( QueryBuilder : : tabYear , QueryBuilder : : valName , true ) ;
}
else
{
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valURL ) ;
qb . addReturnValue ( QueryBuilder : : tabSong , QueryBuilder : : valTitle ) ;
}
values = qb . run ( ) ;
int total = values . count ( ) / qb . countReturnValues ( ) ;
// This can happen -- with the filter it might be empty
if ( total = = 0 )
return ;
// We want to load the pixmap only once if we're still filtering
// Otherwise just load a dummy pixmap
TQPixmap pixmap = iconForCategory ( q_cat ) ;
TQPixmap incPixmap = ipodIncrementIcon ( ) ;
int width = incPixmap . width ( ) + 10 ; // Set the column width below
// Keep track of the dividers we've created.
TQMap < TQString , bool > containedDivider ;
TQStringList : : Iterator itStep = values . end ( ) ;
TQStringList : : Iterator begin = values . begin ( ) ;
itStep - = qb . countReturnValues ( ) ;
// It's an awkward business stepping through a list backward
// when the elements are in tuples, going forward.
// This loop breaks at the bottom -- don't put a continue in here!
while ( 1 )
{
CollectionItem * item ;
TQStringList : : Iterator it = itStep ;
// Add non-track items
if ( stillFiltering )
{
bool unknown = false ;
if ( ( * it ) . isEmpty ( ) )
{
unknown = true ;
if ( VisLabel )
* it = i18n ( " No Label " ) ;
else
* it = i18n ( " Unknown " ) ;
}
item = new CollectionItem ( this , m_cat , unknown ) ;
if ( VisYearAlbum )
{
TQString album = * it ;
TQString year = * ( + + it ) ;
if ( year . isEmpty ( ) )
year = " ? " ;
item - > setText ( 0 , year + i18n ( " - " ) + album ) ;
}
else
item - > setText ( 0 , * it ) ;
item - > setPixmap ( 0 , pixmap ) ;
item - > setPixmap ( 1 , incPixmap ) ;
item - > setText ( 1 , " " ) ;
// Calculate width
width = item - > width ( fontMetrics ( ) , this , 1 ) ;
// Only do dividers if we're not showing tracks since
// dividers don't really make sense in a track-only view
if ( ! unknown & & m_showDivider )
{
//Dividers for "The Who" should be created as "W", not "T",
//because that's how we sort it
TQString actualStr = item - > text ( 0 ) ;
if ( m_cat = = IdArtist & &
actualStr . startsWith ( " the " , false ) )
manipulateThe ( actualStr , true ) ;
TQString headerStr = DividerItem : : createGroup ( actualStr , m_cat ) ;
if ( ! containedDivider [ headerStr ] & & ! headerStr . isEmpty ( ) )
{
containedDivider [ headerStr ] = true ;
( void ) new DividerItem ( this , headerStr , m_cat ) ;
}
}
}
else // Add tracks
{
item = new CollectionItem ( this ) ;
item - > setUrl ( * it ) ;
item - > setText ( 0 , * ( + + it ) ) ;
}
item - > setDragEnabled ( true ) ;
item - > setDropEnabled ( false ) ;
if ( itStep = = begin )
break ;
itStep - = qb . countReturnValues ( ) ;
}
// Add the "All" filter if necessary
if ( stillFiltering )
{
if ( total > 1 )
{
// The "All" filter has much the same behavior as the
// "Various Artists" item, so we use the isSampler bit
CollectionItem * item = new CollectionItem ( this , m_cat , false , true ) ;
item - > setDragEnabled ( true ) ;
item - > setDropEnabled ( false ) ;
item - > setPixmap ( 0 , pixmap ) ;
item - > setText ( 0 , allForCategory ( q_cat , total ) ) ;
item - > setPixmap ( 1 , incPixmap ) ;
item - > setText ( 1 , " " ) ;
sort ( ) ;
}
setColumnWidth ( 1 , width ) ;
TQResizeEvent rev ( size ( ) , TQSize ( ) ) ;
viewportResizeEvent ( & rev ) ;
}
removeDuplicatedHeaders ( ) ;
}
//////////////////////////////////////////////////////////////////////////////////////////
// CLASS CollectionItem
//////////////////////////////////////////////////////////////////////////////////////////
void
CollectionItem : : paintCell ( TQPainter * painter , const TQColorGroup & cg ,
int column , int width , int align )
{
if ( static_cast < CollectionView : : Tag > ( column ) = = CollectionView : : Rating )
{
TQPixmap buf ( width , height ( ) ) ;
TQPainter p ( & buf , true ) ;
const TQColorGroup _cg = listView ( ) - > palette ( ) . active ( ) ;
TQColor bg = isSelected ( ) ? _cg . highlight ( )
: isAlternate ( ) ? listView ( ) - > alternateBackground ( )
: listView ( ) - > viewport ( ) - > backgroundColor ( ) ;
# if KDE_IS_VERSION( 3, 3, 91 )
if ( listView ( ) - > shadeSortColumn ( ) & & ! isSelected ( ) & & listView ( ) - > columnSorted ( ) = = column )
{
/* from tdelistview.cpp
Copyright ( C ) 2000 Reginald Stadlbauer < reggie @ kde . org >
Copyright ( C ) 2000 , 2003 Charles Samuels < charles @ kde . org >
Copyright ( C ) 2000 Peter Putzer */
if ( bg = = TQt : : black )
bg = TQColor ( 55 , 55 , 55 ) ; // dark gray
else
{
int h , s , v ;
bg . hsv ( & h , & s , & v ) ;
if ( v > 175 )
bg = bg . dark ( 104 ) ;
else
bg = bg . light ( 120 ) ;
}
}
# endif
buf . fill ( bg ) ;
int rating = text ( column ) . toInt ( ) ;
int i = 1 , x = 1 ;
const int y = height ( ) / 2 - StarManager : : instance ( ) - > getGreyStar ( ) - > height ( ) / 2 ;
bool half = rating % 2 ;
for ( ; i < = rating / 2 ; + + i )
{
bitBlt ( p . device ( ) , x , y , StarManager : : instance ( ) - > getStar ( half ? rating / 2 + 1 : rating / 2 ) ) ;
x + = StarManager : : instance ( ) - > getGreyStar ( ) - > width ( ) + listView ( ) - > itemMargin ( ) ;
}
if ( half )
{
bitBlt ( p . device ( ) , x , y , StarManager : : instance ( ) - > getHalfStar ( rating / 2 + 1 ) ) ;
x + = StarManager : : instance ( ) - > getGreyStar ( ) - > width ( ) + listView ( ) - > itemMargin ( ) ;
}
p . end ( ) ;
painter - > drawPixmap ( 0 , 0 , buf ) ;
}
else
{
TDEListViewItem : : paintCell ( painter , cg , column , width , align ) ;
}
}
int
CollectionItem : : compare ( TQListViewItem * i , int col , bool ascending ) const
{
TQString a = text ( col ) ;
TQString b = i - > text ( col ) ;
int ia , ib ;
//Special cases go first to take priority
// Sampler is the first one in iPod view
CollectionView * view = static_cast < CollectionView * > ( listView ( ) ) ;
if ( view - > viewMode ( ) = = CollectionView : : modeIpodView )
{
if ( m_isSampler )
return - 1 ;
if ( dynamic_cast < CollectionItem * > ( i ) & & static_cast < CollectionItem * > ( i ) - > m_isSampler )
return 1 ;
}
else if ( view - > viewMode ( ) = = CollectionView : : modeFlatView )
{
ia = ib = 0 ;
// correctly treat numeric values
switch ( col )
{
case CollectionView : : Track :
case CollectionView : : DiscNumber :
case CollectionView : : Bitrate :
case CollectionView : : Score :
case CollectionView : : Rating :
case CollectionView : : Playcount :
case CollectionView : : BPM :
ia = a . toInt ( ) ;
ib = b . toInt ( ) ;
break ;
case CollectionView : : Length :
ia = a . section ( ' : ' , 0 , 0 ) . toInt ( ) * 60 + a . section ( ' : ' , 1 , 1 ) . toInt ( ) ;
ib = b . section ( ' : ' , 0 , 0 ) . toInt ( ) * 60 + b . section ( ' : ' , 1 , 1 ) . toInt ( ) ;
break ;
}
if ( ia | | ib )
{
if ( ia < ib )
return 1 ;
if ( ia > ib )
return - 1 ;
return 0 ;
}
}
// Unknown is always the first one unless we're doing iPod view, but if the two items to be compared are Unknown,
// then compare the normal way
if ( ! ( m_isUnknown & & dynamic_cast < CollectionItem * > ( i ) & & static_cast < CollectionItem * > ( i ) - > m_isUnknown ) )
{
if ( m_isUnknown )
return - 1 ;
if ( dynamic_cast < CollectionItem * > ( i ) & & static_cast < CollectionItem * > ( i ) - > m_isUnknown )
return 1 ;
}
// Various Artists is always after unknown
if ( m_isSampler )
return - 1 ;
if ( dynamic_cast < CollectionItem * > ( i ) & & static_cast < CollectionItem * > ( i ) - > m_isSampler )
return 1 ;
//Group heading should go above the items in that group
if ( dynamic_cast < DividerItem * > ( i ) & & DividerItem : : shareTheSameGroup ( a , b , m_cat ) ) {
return ascending = = false ? - 1 : 1 ;
}
switch ( m_cat ) {
case IdVisYearAlbum :
a = a . left ( a . find ( i18n ( " - " ) ) ) ;
b = b . left ( b . find ( i18n ( " - " ) ) ) ;
// "?" are the last ones
if ( a = = " ? " )
return 1 ;
if ( b = = " ? " )
return - 1 ;
//fall through
case IdYear :
ia = a . toInt ( ) ;
ib = b . toInt ( ) ;
if ( ia = = ib )
return TQString : : localeAwareCompare ( text ( col ) . lower ( ) , i - > text ( col ) . lower ( ) ) ;
if ( ia < ib )
return 1 ;
else
return - 1 ;
//For artists, we sort by ignoring "The" eg "The Who" sorts as if it were "Who"
case IdArtist :
if ( a . startsWith ( " the " , false ) )
CollectionView : : manipulateThe ( a , true ) ;
if ( b . startsWith ( " the " , false ) )
CollectionView : : manipulateThe ( b , true ) ;
break ;
}
// Need to make single letter artist names sort lower than acented divider items
// (e.g. The artist "A" should sort below the divider "Ä") so the divider colapsing
// code works correctly. Making the artist a two letter word makes localeAwareCompare
// give the result we want. See BR 126545.
if ( a . length ( ) = = 1 & & dynamic_cast < DividerItem * > ( i ) )
a + = a ;
// No special case, then fall on default
return TQString : : localeAwareCompare ( a . lower ( ) , b . lower ( ) ) ;
}
void
CollectionItem : : sortChildItems ( int column , bool ascending ) {
CollectionView * view = static_cast < CollectionView * > ( listView ( ) ) ;
// Sort only if it's not the tracks
if ( depth ( ) + 1 < view - > trackDepth ( ) )
TQListViewItem : : sortChildItems ( column , ascending ) ;
}
//
// DividerItem
DividerItem : : DividerItem ( TQListView * parent , TQString txt , int cat /*, bool sortYearsInverted*/ )
: TDEListViewItem ( parent ) , m_blockText ( false ) , m_text ( txt ) , m_cat ( cat ) /*, m_sortYearsInverted(sortYearsInverted)*/
{
setExpandable ( false ) ;
setDropEnabled ( false ) ;
setSelectable ( false ) ;
}
void
DividerItem : : paintCell ( TQPainter * p , const TQColorGroup & cg ,
int column , int width , int align )
{
p - > save ( ) ;
// be sure, that TDEListViewItem::paintCell() does not draw its text
setBlockText ( true ) ;
TDEListViewItem : : paintCell ( p , cg , column , width , align ) ;
setBlockText ( false ) ;
//use bold font for the divider
TQFont f = p - > font ( ) ;
f . setBold ( true ) ;
p - > setFont ( f ) ;
// draw the text offset a little bit
if ( column = = 0 ) // For iPod viewing mode
{
TQFontMetrics fm ( p - > fontMetrics ( ) ) ;
int x = ! TQApplication : : reverseLayout ( ) ? 25 : width - 25 ;
int y = fm . ascent ( ) + ( height ( ) - fm . height ( ) ) / 2 ;
p - > drawText ( x , y , m_text ) ;
}
//draw the baseline
p - > setPen ( TQPen ( TQt : : gray , 2 ) ) ;
p - > drawLine ( 0 , height ( ) - 2 , width , height ( ) - 2 ) ;
p - > restore ( ) ;
}
void
DividerItem : : paintFocus ( TQPainter * /*p*/ , const TQColorGroup & /*cg*/ , const TQRect & /*r*/ )
{
//not implemented, we don't to show focus
}
//to draw the text on my own I have to be able to block the text, otherwise I could
// not use TQListViewItem::paintCell() to draw the basic cell
TQString
DividerItem : : text ( int column ) const
{
if ( column = = 0 ) {
return m_blockText ? " " : m_text ;
}
return TDEListViewItem : : text ( column ) ;
}
int
DividerItem : : compare ( TQListViewItem * i , int col , bool ascending ) const
{
if ( ! i ) {
return TQString : : localeAwareCompare ( text ( col ) . lower ( ) , TQString ( " " ) ) ;
}
if ( dynamic_cast < CollectionItem * > ( i ) ) {
return - 1 * i - > compare ( const_cast < DividerItem * > ( this ) , col , ascending ) ;
}
if ( m_cat = = IdYear | |
m_cat = = IdVisYearAlbum )
{
bool ok_a , ok_b ;
int ia = text ( col ) . toInt ( & ok_a ) ;
int ib = i - > text ( col ) . toInt ( & ok_b ) ;
if ( ok_a & & ok_b )
{
if ( ia = = ib ) return 0 ;
else if ( ia < ib ) return 1 ;
else return - 1 ;
}
}
return TQString : : localeAwareCompare ( text ( col ) . lower ( ) , i - > text ( col ) . lower ( ) ) ;
}
TQString
DividerItem : : createGroup ( const TQString & src , int cat )
{
TQString ret ;
switch ( cat ) {
case IdVisYearAlbum : {
ret = src . left ( src . find ( " - " ) ) ;
break ;
}
case IdYear : {
ret = src ;
if ( ret . length ( ) = = 2 | | ret . length ( ) = = 4 ) {
ret = ret . left ( ret . length ( ) - 1 ) + ' 0 ' ;
}
break ;
}
default :
ret = src . stripWhiteSpace ( ) ;
// (Joe Rabinoff) deleted. The bug this fixes is as follows.
// If the category is Album and the album name is "Heroes" (by
// David Bowie -- the quotes are part of the title), it gets
// put under the H group, but then gets sorted under '"'.
// What I've done is the wrong fix -- albums should be sorted
// by their first alphanumeric character.
/*
while ( ! ret . isEmpty ( ) & & ! ret . at ( 0 ) . isLetterOrNumber ( ) ) {
ret = ret . right ( ret . length ( ) - 1 ) ;
}
*/
if ( ! ret . isEmpty ( ) & & ret . at ( 0 ) . isLetterOrNumber ( ) )
ret = ret . left ( 1 ) . upper ( ) ;
else
return " " ;
/*else
ret = i18n ( " Others " ) ; */
/* By returning an empty string, no header is created. */
if ( TQChar ( ret . at ( 0 ) ) . isDigit ( ) ) {
ret = " 0-9 " ;
}
}
return ret ;
}
bool
DividerItem : : shareTheSameGroup ( const TQString & itemStr , const TQString & divStr , int cat )
{
bool inGroup = false ;
TQString tmp = itemStr . stripWhiteSpace ( ) ;
switch ( cat ) {
case IdVisYearAlbum : {
TQString sa = itemStr . left ( itemStr . find ( i18n ( " - " ) ) ) ;
TQString sb = divStr . left ( divStr . find ( i18n ( " - " ) ) ) ;
if ( sa = = sb ) {
inGroup = true ;
}
break ;
}
case IdYear : {
int ia = itemStr . toInt ( ) ;
int ib = divStr . toInt ( ) ;
// they share one group if:
// o they are < 100 (short years '98')
// o they are > 1000 (long years '1998')
// o their 'century' is the same
// o are the same
if ( ( ( ia < 100 | | ia > 1000 ) & & ia / 10 = = ib / 10 ) | |
( ia = = ib ) ) {
inGroup = true ;
}
break ;
}
case IdArtist :
//"The Who" should count as being in "W" and not "T"
if ( tmp . startsWith ( " the " , false ) )
CollectionView : : manipulateThe ( tmp , true ) ;
//Fall through
default :
if ( ! tmp . isEmpty ( ) ) {
if ( divStr = = " 0-9 " & & TQChar ( tmp . at ( 0 ) ) . isDigit ( ) ) {
inGroup = true ;
}
else if ( tmp . startsWith ( divStr , 0 ) ) {
inGroup = true ;
}
}
}
return inGroup ;
}
# include "collectionbrowser.moc"