diff --git a/src/collection/sqlcollection/OrganizeCollectionDialog.cpp b/src/collection/sqlcollection/OrganizeCollectionDialog.cpp index eca02ea3d8..fead785959 100644 --- a/src/collection/sqlcollection/OrganizeCollectionDialog.cpp +++ b/src/collection/sqlcollection/OrganizeCollectionDialog.cpp @@ -1,514 +1,526 @@ /****************************************************************************** * Copyright (c) 2008 Bonne Eggleston * * 2008 Teo Mrnjavac * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ******************************************************************************/ #ifndef AMAROK_ORGANIZECOLLECTIONDIALOG_UI_H #define AMAROK_ORGANIZECOLLECTIONDIALOG_UI_H #include "OrganizeCollectionDialog.h" #include "Amarok.h" #include "Debug.h" #include "amarokconfig.h" #include "file/File.h" #include "MountPointManager.h" #include "QueryMaker.h" #include "QStringx.h" #include "SqlQueryMaker.h" #include "ui_OrganizeCollectionDialogBase.h" #include OrganizeCollectionDialog::OrganizeCollectionDialog( QueryMaker *qm, QWidget *parent, const char *name, bool modal, const QString &caption, QFlags buttonMask ) { SqlQueryMaker *sqlqm = static_cast( qm ); sqlqm->setQueryType( QueryMaker::Track ); sqlqm->setBlocking( true ); sqlqm->run(); Meta::TrackList tracks; foreach( const QString &collectionId, sqlqm->collectionIds() ) { tracks << sqlqm->tracks( collectionId ); } // FIXME: someone has to delete it. should it really be done here? delete qm; OrganizeCollectionDialog( tracks, parent, name, modal, caption, buttonMask ); } OrganizeCollectionDialog::OrganizeCollectionDialog( const Meta::TrackList &tracks, QWidget *parent, const char *name, bool modal, const QString &caption, QFlags buttonMask ) : KDialog( parent ) , ui( new Ui::OrganizeCollectionDialogBase ) , m_detailed( true ) { Q_UNUSED( name ) setCaption( caption ); setModal( modal ); setButtons( buttonMask ); showButtonSeparator( true ); m_previewTrack = 0; if ( tracks.size() > 0 ) { m_previewTrack = tracks[0]; m_allTracks = tracks; } KVBox *mainVBox = new KVBox( this ); setMainWidget( mainVBox ); QWidget *mainContainer = new QWidget( mainVBox ); ui->setupUi( mainContainer ); m_filenameLayoutDialog = new FilenameLayoutDialog( mainContainer, 1 ); //", 1" means isOrganizeCollection ==> doesn't show Options frame m_filenameLayoutDialog->hide(); ui->verticalLayout->insertWidget( 3, m_filenameLayoutDialog ); ui->ignoreTheCheck->show(); const QStringList folders = MountPointManager::instance()->collectionFolders(); ui->folderCombo->insertItems( 0, folders ); ui->folderCombo->setCurrentIndex( AmarokConfig::organizeDirectory() ); ui->overwriteCheck->setChecked( AmarokConfig::overwriteFiles() ); ui->filetypeCheck->setChecked( AmarokConfig::groupByFiletype() ); ui->initialCheck->setChecked( AmarokConfig::groupArtists() ); ui->spaceCheck->setChecked( AmarokConfig::replaceSpace() ); ui->ignoreTheCheck->setChecked( AmarokConfig::ignoreThe() ); ui->vfatCheck->setChecked( AmarokConfig::vfatCompatible() ); ui->asciiCheck->setChecked( AmarokConfig::asciiOnly() ); ui->customschemeCheck->setChecked( AmarokConfig::useCustomScheme() ); ui->regexpEdit->setText( AmarokConfig::replacementRegexp() ); ui->replaceEdit->setText( AmarokConfig::replacementString() ); connect( this, SIGNAL( updatePreview( QString ) ), ui->previewText, SLOT( setText( QString ) ) ); connect( ui->filetypeCheck , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) ); connect( ui->initialCheck , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) ); connect( ui->ignoreTheCheck, SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) ); connect( ui->spaceCheck , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) ); connect( ui->asciiCheck , SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) ); connect( ui->customschemeCheck, SIGNAL(toggled(bool)), SLOT(slotUpdatePreview()) ); connect( ui->regexpEdit , SIGNAL(textChanged(QString)), SLOT(slotUpdatePreview()) ); connect( ui->replaceEdit , SIGNAL(textChanged(QString)), SLOT(slotUpdatePreview()) ); connect( m_filenameLayoutDialog, SIGNAL( schemeChanged() ), this, SLOT( slotUpdatePreview() ) ); connect( ui->customschemeCheck, SIGNAL( toggled( bool ) ), this, SLOT( toggleCustomScheme( bool ) ) ); connect( this , SIGNAL( accepted() ), SLOT( slotDialogAccepted() ) ); connect( ui->folderCombo, SIGNAL( currentIndexChanged( const QString & ) ), this, SLOT( slotUpdatePreview() ) ); - + connect( ui->folderCombo, SIGNAL( currentIndexChanged( const QString & ) ), + this, SLOT( slotEnableOk( const QString & ) ) ); + toggleCustomScheme( ui->customschemeCheck->isChecked() ); + slotEnableOk( ui->folderCombo->currentText() ); init(); } OrganizeCollectionDialog::~OrganizeCollectionDialog() { DEBUG_BLOCK AmarokConfig::setOrganizeDirectory( ui->folderCombo->currentIndex() ); delete ui; } QMap OrganizeCollectionDialog::getDestinations() { QString format = buildFormatString(); QMap destinations; foreach( const Meta::TrackPtr &track, m_allTracks ) { destinations.insert( track, buildDestination( format, track ) ); } return destinations; } bool OrganizeCollectionDialog::overwriteDestinations() const { return ui->overwriteCheck->isChecked(); } QString OrganizeCollectionDialog::buildDestination( const QString &format, const Meta::TrackPtr &track ) const { //TODO: handle if track==NULL to avoid bug 169684 //This could maybe happen with an empty collection, when the TrackList is empty and then m_previewTrack is null. //FIXME: 169684 bool isCompilation = track->album() && track->album()->isCompilation(); QMap args; QString artist = track->artist()->name(); QString albumartist; if( isCompilation ) albumartist = i18n( "Various Artists" ); else { if( track->album() && track->album()->albumArtist() ) albumartist = track->album()->albumArtist()->name(); else albumartist = artist; } args["theartist"] = cleanPath( artist ); args["thealbumartist"] = cleanPath( albumartist ); if( ui->ignoreTheCheck->isChecked() && artist.startsWith( "The " ) ) Amarok::manipulateThe( artist, true ); artist = cleanPath( artist ); if( ui->ignoreTheCheck->isChecked() && albumartist.startsWith( "The " ) ) Amarok::manipulateThe( albumartist, true ); albumartist = cleanPath( albumartist ); //these additional columns from MetaBundle were used before but haven't //been ported yet. Do they need to be? //Bpm,Directory,Bitrate,SampleRate,Mood args["folder"] = ui->folderCombo->currentText(); args["title"] = cleanPath( track->prettyName() ); args["composer"] = cleanPath( track->composer()->prettyName() ); args["year"] = cleanPath( track->year()->prettyName() ); args["album"] = cleanPath( track->album()->prettyName() ); if( track->discNumber() ) args["discnumber"] = QString::number( track->discNumber() ); args["genre"] = cleanPath( track->genre()->prettyName() ); args["comment"] = cleanPath( track->comment() ); args["artist"] = artist; args["albumartist"] = albumartist; args["initial"] = albumartist.mid( 0, 1 ).toUpper(); //artists starting with The are already handled above args["filetype"] = track->type(); args["rating"] = track->rating(); args["filesize"] = track->filesize(); args["length"] = track->length(); if ( track->trackNumber() ) { QString trackNum = QString("%1").arg( track->trackNumber(), 2, 10, QChar('0') ); args["track"] = trackNum; } Amarok::QStringx formatx( format ); QString result = formatx.namedOptArgs( args ); if( !result.startsWith( '/' ) ) result.prepend( "/" ); return result.replace( QRegExp( "/\\.*" ), "/" ); } QString OrganizeCollectionDialog::buildFormatTip() const { //FIXME: This is directly copied from mediadevice/generic/genericmediadeviceconfigdialog.ui.h QMap args; args["albumartist"] = i18n( "%1 or %2", QLatin1String("Album Artist, The") , QLatin1String("The Album Artist") ); args["thealbumartist"] = "The Album Artist"; args["theartist"] = "The Artist"; args["artist"] = i18n( "%1 or %2", QLatin1String("Artist, The") , QLatin1String("The Artist") ); args["initial"] = i18n( "Artist's Initial" ); args["filetype"] = i18n( "File Extension of Source" ); args["track"] = i18n( "Track Number" ); QString tooltip = i18n( "

Custom Format String

" ); tooltip += i18n( "You can use the following tokens:" ); tooltip += "
    "; for( QMap::iterator it = args.begin(); it != args.end(); ++it ) tooltip += QString( "
  • %1 - %2" ).arg( it.value(), '%' + it.key() ); tooltip += "
"; tooltip += i18n( "If you surround sections of text that contain a token with curly-braces, " "that section will be hidden if the token is empty." ); return tooltip; } QString OrganizeCollectionDialog::buildFormatString() const { //TODO: this is where I need to query m_filenameLayoutDialog if( ui->customschemeCheck->isChecked() ) return "%folder/" + m_filenameLayoutDialog->getParsableScheme(); QString format = "%folder/"; if( ui->filetypeCheck->isChecked() ) format += "%filetype/"; if( ui->initialCheck->isChecked() ) format += "%initial/"; format += "%albumartist/"; if( ui->spaceCheck->isChecked() ) //replace spaces with underscores { format += "%album{_(Disc_%discnumber)}/"; format += "{%track_-_}%title.%filetype"; } else { format += "%album{ (Disc %discnumber)}/"; format += "{%track - }%title.%filetype"; } format = QDir::fromNativeSeparators( format ); //fromNativeSeparators handles \\ under windows return format; } void OrganizeCollectionDialog::preview( const QString &format ) { emit updatePreview( buildDestination( format, m_previewTrack ) ); } QString OrganizeCollectionDialog::cleanPath( const QString &component ) const { QString result = component; if( ui->asciiCheck->isChecked() ) { result = Amarok::cleanPath( result ); result = Amarok::asciiPath( result ); } if( !ui->regexpEdit->text().isEmpty() ) result.replace( QRegExp( ui->regexpEdit->text() ), ui->replaceEdit->text() ); result.simplified(); if( ui->spaceCheck->isChecked() ) result.replace( QRegExp( "\\s" ), "_" ); if( ui->vfatCheck->isChecked() ) result = Amarok::vfatPath( result ); result.replace( '/', '-' ); return result; } void OrganizeCollectionDialog::update( int dummy ) //why the dummy? { Q_UNUSED( dummy ); if( ui->customschemeCheck->isChecked() ) emit updatePreview( buildDestination( "%folder/" + m_filenameLayoutDialog->getParsableScheme(), m_previewTrack ) ); else emit updatePreview( buildDestination( buildFormatString(), m_previewTrack ) ); } void OrganizeCollectionDialog::update( const QString & dummy ) { Q_UNUSED( dummy ); update( 0 ); } void OrganizeCollectionDialog::init() { slotUpdatePreview(); } void OrganizeCollectionDialog::slotUpdatePreview() { preview( buildFormatString() ); } void OrganizeCollectionDialog::slotDialogAccepted() { AmarokConfig::setOrganizeDirectory( ui->folderCombo->currentIndex() ); AmarokConfig::setGroupByFiletype( ui->filetypeCheck->isChecked() ); AmarokConfig::setGroupArtists( ui->initialCheck->isChecked() ); AmarokConfig::setIgnoreThe( ui->ignoreTheCheck->isChecked() ); AmarokConfig::setReplaceSpace( ui->spaceCheck->isChecked() ); AmarokConfig::setVfatCompatible( ui->vfatCheck->isChecked() ); AmarokConfig::setAsciiOnly( ui->asciiCheck->isChecked() ); AmarokConfig::setUseCustomScheme( ui->customschemeCheck->isChecked() ); AmarokConfig::setReplacementRegexp( ui->regexpEdit->text() ); AmarokConfig::setReplacementString( ui->replaceEdit->text() ); } //Hides and shows the right elements in the interface in the right order to keep the layout sane void OrganizeCollectionDialog::toggleCustomScheme( bool state ) //SLOT { if( state ) { ui->initialCheck->hide(); ui->filetypeCheck->hide(); m_filenameLayoutDialog->setVisible( true ); } else { m_filenameLayoutDialog->hide(); ui->initialCheck->show(); ui->filetypeCheck->show(); } } +//The Ok button should be disabled when there's no collection root selected. +void +OrganizeCollectionDialog::slotEnableOk( const QString & currentCollectionRoot ) +{ + if( currentCollectionRoot == 0 ) + enableButtonOk( false ); + else + enableButtonOk( true ); +} /* Code to port * 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 QString &caption, bool copy ) //SLOT { if( m_organizingFileCancelled ) { QString shortMsg = i18n( "Cannot start organize operation until jobs are aborted." ); The::statusBar()->shortMessage( shortMsg, StatusBar::Sorry ); return; } if( m_organizeURLs.count() ) { if( copy != m_organizeCopyMode ) { QString shortMsg = i18n( "Cannot start organize operation of different kind while another is in progress." ); The::statusBar()->shortMessage( shortMsg, StatusBar::Sorry ); return; } else { m_organizeURLs += Amarok::recursiveUrlExpand( urls ); The::statusBar()->incrementProgressTotalSteps( this, urls.count() ); return; } } QStringList folders = MountPointManager::instance()->collectionFolders(); if( folders.isEmpty() ) { QString longMsg = i18n( "You need to configure at least one folder for your collection for organizing your files." ); The::statusBar()->longMessage( longMsg, StatusBar::Sorry ); return; } KURL::List previewURLs = Amarok::recursiveUrlExpand( urls.first(), 1 ); if( previewURLs.count() ) { dialog.setPreviewBundle( MetaBundle( previewURLs.first() ) ); dialog.update( 0 ); } base.setInitialSize( QSize( 450, 350 ) ); if( base.exec() == KDialogBase::Accepted ) { AmarokConfig::setOrganizeDirectory( dialog.folderCombo->currentIndex() ); 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::setVfatCompatible( dialog.vfatCheck->isChecked() ); AmarokConfig::setAsciiOnly( dialog.asciiCheck->isChecked() ); AmarokConfig::setUseCustomScheme( dialog.customschemeCheck->isChecked() ); 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 The::statusBar()->newProgressOperation( this ) .setDescription( caption ) .setAbortSlot( this, 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(); The::statusBar()->incrementProgress( 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 QTimer::singleShot( 0, CollectionDB::instance(), SLOT( scanMonitor() ) ); if( !m_organizingFileCancelled && skipped.count() > 0 ) { QString longMsg = i18n( "The following file could not be organized: ", "The following %1 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( "." ); QString shortMsg = i18n( "Sorry, one file could not be organized.", "Sorry, %1 files could not be organized.", skipped.count() ); The::statusBar()->shortLongMessage( shortMsg, longMsg, StatusBar::Sorry ); } else if ( m_organizingFileCancelled ) { The::statusBar()->shortMessage( i18n( "Aborting jobs..." ) ); m_organizingFileCancelled = false; } m_dirty = true; QTimer::singleShot( 0, CollectionView::instance(), SLOT( renderView() ) ); The::statusBar()->endProgressOperation( this ); } } */ #endif //AMAROK_ORGANIZECOLLECTIONDIALOG_UI_H diff --git a/src/collection/sqlcollection/OrganizeCollectionDialog.h b/src/collection/sqlcollection/OrganizeCollectionDialog.h index 46e5aaa1c4..1fd887373e 100644 --- a/src/collection/sqlcollection/OrganizeCollectionDialog.h +++ b/src/collection/sqlcollection/OrganizeCollectionDialog.h @@ -1,82 +1,83 @@ /****************************************************************************** * Copyright (c) 2006 Mike Diehl * * 2008 Teo Mrnjavac * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ******************************************************************************/ #ifndef AMAROK_ORGANIZECOLLECTIONDIALOG_H #define AMAROK_ORGANIZECOLLECTIONDIALOG_H #include "meta/Meta.h" #include "dialogs/FilenameLayoutDialog.h" #include "widgets/FilenameLayoutWidget.h" #include "widgets/TokenListWidget.h" #include #include #include namespace Ui { class OrganizeCollectionDialogBase; } class OrganizeCollectionDialog : public KDialog { Q_OBJECT public: explicit OrganizeCollectionDialog( QueryMaker *qm, QWidget *parent=0, const char *name=0, bool modal=true, const QString &caption=QString(), QFlags buttonMask=Ok|Cancel ); explicit OrganizeCollectionDialog( const Meta::TrackList &tracks, QWidget *parent=0, const char *name=0, bool modal=true, const QString &caption=QString(), QFlags buttonMask=Ok|Cancel ); ~OrganizeCollectionDialog(); QMap getDestinations(); bool overwriteDestinations() const; signals: void updatePreview(QString); public slots: void slotUpdatePreview(); void slotDialogAccepted(); private: QString buildDestination( const QString &format, const Meta::TrackPtr &track ) const; QString cleanPath( const QString &component ) const; QString buildFormatTip() const; QString buildFormatString() const; void toggleDetails(); void preview( const QString &format ); void update( int dummy ); void update( const QString & dummy ); void init(); Ui::OrganizeCollectionDialogBase *ui; FilenameLayoutDialog *m_filenameLayoutDialog; Meta::TrackPtr m_previewTrack; bool m_detailed; Meta::TrackList m_allTracks; private slots: void toggleCustomScheme( bool state ); + void slotEnableOk( const QString & currentCollectionRoot ); }; #endif //AMAROK_ORGANIZECOLLECTIONDIALOG_H