You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4718 lines
166 KiB
4718 lines
166 KiB
// (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 <tdeapplication.h> //kapp |
|
#include <tdeconfig.h> |
|
#include <kcombobox.h> |
|
#include <kcursor.h> |
|
#include <kdialogbase.h> |
|
#include <tdeglobal.h> |
|
#include <kiconloader.h> //renderView() |
|
#include <tdelocale.h> |
|
#include <tdemessagebox.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" ), "format-justify-left", 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( "document-save-as" ), 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 |