1 // (c) 2004 Mark Kretschmann <markey@web.de>
2 // (c) 2004 Pierpaolo Di Panfilo <pippo_dp@libero.it>
3 // (c) 2005-2006 Alexandre Pereira de Oliveira <aleprj@gmail.com>
4 // See COPYING file for licensing information.
9 #include "amarokconfig.h"
10 #include "CoverFetcher.h"
12 #include "querybuilder.h"
13 #include "ContextStatusBar.h" //for status messages
14 #include "TagGuesser.h"
15 #include "trackpickerdialog.h"
16 #include "ui_tagguesserconfigdialog.h"
18 #include <KApplication>
23 #include <KIconLoader>
25 #include <KMessageBox>
28 #include <KStandardDirs>
39 #include <QPushButton>
42 #include <tfile.h> //TagLib::File::isWritable
44 class TagDialogWriter
: public ThreadManager::Job
47 TagDialogWriter( const QMap
<QString
, MetaBundle
> tagsToChange
);
52 QList
<MetaBundle
> m_tags
;
56 QStringList m_failedURLs
;
59 TagDialog::TagDialog( const KUrl
& url
, QWidget
* parent
)
60 : TagDialogBase( parent
)
61 , m_bundle( url
, true )
69 TagDialog::TagDialog( const KUrl::List list
, QWidget
* parent
)
70 : TagDialogBase( parent
)
80 // TagDialog::TagDialog( const MetaBundle& mb, PlaylistItem* item, QWidget* parent )
81 // : TagDialogBase( parent )
83 // , m_playlistItem( item )
84 // , m_currentCover( 0 )
89 TagDialog::TagDialog( const Meta::TrackList
&tracks
, QWidget
*parent
)
90 :TagDialogBase( parent
)
98 TagDialog::TagDialog( Meta::TrackPtr track
, QWidget
*parent
)
99 :TagDialogBase( parent
)
102 , m_currentTrack( track
)
104 m_tracks
.append( track
);
109 TagDialog::~TagDialog()
113 Amarok::config( "TagDialog" ).writeEntry( "CurrentTab", kTabWidget
->currentIndex() );
118 TagDialog::setTab( int id
)
120 kTabWidget
->setCurrentIndex( id
);
124 ////////////////////////////////////////////////////////////////////////////////
126 ////////////////////////////////////////////////////////////////////////////////
129 TagDialog::cancelPressed() //SLOT
131 QApplication::restoreOverrideCursor(); // restore the cursor before closing the dialog
137 TagDialog::accept() //SLOT
139 pushButton_ok
->setEnabled( false ); //visual feedback
147 TagDialog::openPressed() //SLOT
149 Amarok::invokeBrowser( m_path
);
154 TagDialog::previousTrack()
157 // if( m_playlistItem )
159 // if( !m_playlistItem->itemAbove() ) return;
163 // m_playlistItem = static_cast<PlaylistItem *>( m_playlistItem->itemAbove() );
165 // loadTags( m_playlistItem->url() );
169 storeTags( *m_currentURL
);
171 if( m_currentURL
!= m_urlList
.begin() )
173 loadTags( *m_currentURL
);
181 TagDialog::nextTrack()
184 // if( m_playlistItem )
186 // if( !m_playlistItem->itemBelow() ) return;
190 // m_playlistItem = static_cast<PlaylistItem *>( m_playlistItem->itemBelow() );
192 // loadTags( m_playlistItem->url() );
196 storeTags( *m_currentURL
);
198 KUrl::List::iterator next
= m_currentURL
;
200 if( next
!= m_urlList
.end() )
202 loadTags( *m_currentURL
);
209 TagDialog::perTrack()
211 m_perTrack
= !m_perTrack
;
214 // just switched to per track mode
216 setSingleTrackMode();
217 loadTags( *m_currentURL
);
222 storeTags( *m_currentURL
);
223 setMultipleTracksMode();
224 readMultipleTracks();
232 TagDialog::enableItems()
234 checkBox_perTrack
->setChecked( m_perTrack
);
235 pushButton_previous
->setEnabled( m_perTrack
&& m_currentURL
!= m_urlList
.begin() );
236 KUrl::List::ConstIterator next
= m_currentURL
;
238 pushButton_next
->setEnabled( m_perTrack
&& next
!= m_urlList
.end());
239 if( m_urlList
.count() == 1 )
241 checkBox_perTrack
->setEnabled( false );
245 checkBox_perTrack
->setEnabled( true );
251 TagDialog::checkModified() //SLOT
253 pushButton_ok
->setEnabled( hasChanged() || storedTags
.count() > 0 || storedScores
.count() > 0
254 || storedLyrics
.count() > 0 || storedRatings
.count() > 0 || newLabels
.count() > 0 );
258 TagDialog::loadCover( const QString
&artist
, const QString
&album
)
260 if ( m_bundle
.artist() != artist
|| m_bundle
.album()!=album
)
263 // draw the album cover on the dialog
264 QString cover
= CollectionDB::instance()->albumImage( m_bundle
);
266 if( m_currentCover
!= cover
)
268 pixmap_cover
->setPixmap( QPixmap( cover
, "PNG" ) );
269 m_currentCover
= cover
;
271 pixmap_cover
->setInformation( m_bundle
.artist(), m_bundle
.album() );
272 const int s
= AmarokConfig::coverPreviewSize();
273 pixmap_cover
->setMinimumSize( s
, s
);
274 pixmap_cover
->setMaximumSize( s
, s
);
279 TagDialog::setFileNameSchemes() //SLOT
281 KDialog
*kDialog
= new KDialog(this);
282 Ui::TagGuesserConfigDialog
* dialog
= new Ui::TagGuesserConfigDialog();
283 dialog
->setupUi(kDialog
);
290 TagDialog::guessFromFilename() //SLOT
294 TagGuesser
guesser( m_bundle
.url().path() );
295 if( !guesser
.title().isNull() )
296 kLineEdit_title
->setText( guesser
.title() );
298 if( !guesser
.artist().isNull() )
300 cur
= kComboBox_artist
->currentIndex();
301 kComboBox_artist
->setItemText( cur
, guesser
.artist() );
304 if( !guesser
.album().isNull() )
306 cur
= kComboBox_album
->currentIndex();
307 kComboBox_album
->setItemText( cur
, guesser
.album() );
310 if( !guesser
.track().isNull() )
311 qSpinBox_track
->setValue( guesser
.track().toInt() );
312 if( !guesser
.comment().isNull() )
313 kTextEdit_comment
->setText( guesser
.comment() );
314 if( !guesser
.year().isNull() )
315 qSpinBox_year
->setValue( guesser
.year().toInt() );
317 if( !guesser
.composer().isNull() )
319 cur
= kComboBox_composer
->currentIndex();
320 kComboBox_composer
->setItemText( cur
, guesser
.composer() );
323 if( !guesser
.genre().isNull() )
325 cur
= kComboBox_genre
->currentIndex();
326 kComboBox_genre
->setItemText( cur
, guesser
.genre() );
331 TagDialog::musicbrainzQuery() //SLOT
336 m_mbTrack
= m_bundle
.url();
337 KTRMLookup
* ktrm
= new KTRMLookup( m_mbTrack
.path(), true );
338 connect( ktrm
, SIGNAL( sigResult( KTRMResultList
, QString
) ), SLOT( queryDone( KTRMResultList
, QString
) ) );
339 connect( pushButton_cancel
, SIGNAL( clicked() ), ktrm
, SLOT( deleteLater() ) );
341 pushButton_musicbrainz
->setEnabled( false );
342 pushButton_musicbrainz
->setText( i18n( "Generating audio fingerprint..." ) );
343 QApplication::setOverrideCursor( Qt::BusyCursor
);
348 TagDialog::queryDone( KTRMResultList results
, QString error
) //SLOT
352 if ( !error
.isEmpty() ) {
353 KMessageBox::sorry( this, i18n( "Tunepimp (MusicBrainz tagging library) returned the following error: \"%1\".", error
) );
356 if ( !results
.isEmpty() )
358 TrackPickerDialog
* t
= new TrackPickerDialog( m_mbTrack
.fileName(), results
, this );
360 connect( t
, SIGNAL( finished() ), SLOT( resetMusicbrainz() ) ); // clear m_mbTrack
363 KMessageBox::sorry( this, i18n( "The track was not found in the MusicBrainz database." ) );
364 resetMusicbrainz(); // clear m_mbTrack
368 QApplication::restoreOverrideCursor();
369 pushButton_musicbrainz
->setEnabled( true );
370 pushButton_musicbrainz
->setText( m_buttonMbText
);
378 TagDialog::fillSelected( KTRMResult selected
) //SLOT
384 if ( m_bundle
.url() == m_mbTrack
) {
385 if ( !selected
.title().isEmpty() ) kLineEdit_title
->setText( selected
.title() );
386 if ( !selected
.artist().isEmpty() ) kComboBox_artist
->setCurrentText( selected
.artist() );
387 if ( !selected
.album().isEmpty() ) kComboBox_album
->setCurrentText( selected
.album() );
388 if ( selected
.track() != 0 ) qSpinBox_track
->setValue( selected
.track() );
389 if ( selected
.year() != 0 ) qSpinBox_year
->setValue( selected
.year() );
392 mb
.setPath( m_mbTrack
.path() );
393 if ( !selected
.title().isEmpty() ) mb
.setTitle( selected
.title() );
394 if ( !selected
.artist().isEmpty() ) mb
.setArtist( selected
.artist() );
395 if ( !selected
.album().isEmpty() ) mb
.setAlbum( selected
.album() );
396 if ( selected
.track() != 0 ) mb
.setTrack( selected
.track() );
397 if ( selected
.year() != 0 ) mb
.setYear( selected
.year() );
399 storedTags
.replace( m_mbTrack
.path(), mb
);
406 void TagDialog::resetMusicbrainz() //SLOT
413 ////////////////////////////////////////////////////////////////////////////////
415 ////////////////////////////////////////////////////////////////////////////////
417 void TagDialog::init()
422 // delete itself when closing
423 setAttribute( Qt::WA_DeleteOnClose
);
425 KConfigGroup config
= Amarok::config( "TagDialog" );
427 kTabWidget
->addTab( summaryTab
, i18n( "Summary" ) );
428 kTabWidget
->addTab( tagsTab
, i18n( "Tags" ) );
429 kTabWidget
->addTab( lyricsTab
, i18n( "Lyrics" ) );
430 kTabWidget
->addTab( statisticsTab
, i18n( "Statistics" ) );
431 kTabWidget
->addTab( labelsTab
, i18n( "Labels" ) );
432 kTabWidget
->setCurrentIndex( config
.readEntry( "CurrentTab", 0 ) );
434 int items
= kComboBox_artist
->count();
435 const QStringList artists
= CollectionDB::instance()->artistList();
436 kComboBox_artist
->insertItems( items
, artists
);
437 kComboBox_artist
->completionObject()->insertItems( artists
);
438 kComboBox_artist
->completionObject()->setIgnoreCase( true );
439 kComboBox_artist
->setCompletionMode( KGlobalSettings::CompletionPopup
);
441 items
= kComboBox_album
->count();
442 const QStringList albums
= CollectionDB::instance()->albumList();
443 kComboBox_album
->insertItems( items
, albums
);
444 kComboBox_album
->completionObject()->insertItems( albums
);
445 kComboBox_album
->completionObject()->setIgnoreCase( true );
446 kComboBox_album
->setCompletionMode( KGlobalSettings::CompletionPopup
);
448 items
= kComboBox_artist
->count();
449 const QStringList composers
= CollectionDB::instance()->composerList();
450 kComboBox_composer
->insertItems( items
, composers
);
451 kComboBox_composer
->completionObject()->insertItems( composers
);
452 kComboBox_composer
->completionObject()->setIgnoreCase( true );
453 kComboBox_composer
->setCompletionMode( KGlobalSettings::CompletionPopup
);
455 items
= kComboBox_artist
->count();
456 kComboBox_rating
->insertItems( items
, MetaBundle::ratingList() );
458 // const QStringList genres = MetaBundle::genreList();
459 items
= kComboBox_artist
->count();
460 const QStringList genres
= CollectionDB::instance()->genreList();
461 kComboBox_genre
->insertItems( items
, genres
);
462 kComboBox_genre
->completionObject()->insertItems( genres
);
463 kComboBox_genre
->completionObject()->setIgnoreCase( true );
465 const QStringList labels
= CollectionDB::instance()->labelList();
466 //TODO: figure out a way to add auto-completion support to kTestEdit_selectedLabels
468 m_labelCloud
= new KHTMLPart( labels_favouriteLabelsFrame
);
469 //m_labelCloud = new HTMLView( labels_favouriteLabelsFrame );
470 //m_labelCloud->view()->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored, false );
471 QSizePolicy
policy(QSizePolicy::Ignored
, QSizePolicy::Ignored
);
472 m_labelCloud
->view()->setSizePolicy(policy
);
474 //m_labelCloud->view()->setVScrollBarMode( Q3ScrollView::AlwaysOff );
475 //m_labelCloud->view()->setHScrollBarMode( Q3ScrollView::AlwaysOff );
477 new QVBoxLayout( labels_favouriteLabelsFrame
);
478 labels_favouriteLabelsFrame
->layout()->addWidget( m_labelCloud
->view() );
479 const QStringList favoriteLabels
= CollectionDB::instance()->favoriteLabels();
480 QString html
= generateHTML( favoriteLabels
);
481 m_labelCloud
->write( html
);
482 connect( m_labelCloud
->browserExtension(), SIGNAL( openUrlRequest( const KUrl
&, const KParts::OpenUrlArguments
&, const KParts::BrowserArguments
& ) ),
483 this, SLOT( openUrlRequest( const KUrl
& ) ) );
485 // looks better to have a blank label than 0, we can't do this in
486 // the UI file due to bug in Designer
487 qSpinBox_track
->setSpecialValueText( " " );
488 qSpinBox_year
->setSpecialValueText( " " );
489 qSpinBox_score
->setSpecialValueText( " " );
490 qSpinBox_discNumber
->setSpecialValueText( " " );
492 if( !AmarokConfig::useRatings() )
494 kComboBox_rating
->hide();
497 if( !AmarokConfig::useScores() )
499 qSpinBox_score
->hide();
503 //HACK due to deficiency in Qt that will be addressed in version 4
504 // QSpinBox doesn't emit valueChanged if you edit the value with
505 // the lineEdit until you change the keyboard focus
506 connect( qSpinBox_year
, SIGNAL(valueChanged( int )), SLOT(checkModified()) );
507 connect( qSpinBox_track
, SIGNAL(valueChanged( int )), SLOT(checkModified()) );
508 connect( qSpinBox_score
, SIGNAL(valueChanged( int )), SLOT(checkModified()) );
509 connect( qSpinBox_discNumber
, SIGNAL(valueChanged( int )), SLOT(checkModified()) );
511 // Connects for modification check
512 connect( kLineEdit_title
, SIGNAL(textChanged( const QString
& )), SLOT(checkModified()) );
513 connect( kComboBox_composer
,SIGNAL(activated( int )), SLOT(checkModified()) );
514 connect( kComboBox_composer
,SIGNAL(textChanged( const QString
& )), SLOT(checkModified()) );
515 connect( kComboBox_artist
, SIGNAL(activated( int )), SLOT(checkModified()) );
516 connect( kComboBox_artist
, SIGNAL(textChanged( const QString
& )), SLOT(checkModified()) );
517 connect( kComboBox_album
, SIGNAL(activated( int )), SLOT(checkModified()) );
518 connect( kComboBox_album
, SIGNAL(textChanged( const QString
& )), SLOT(checkModified()) );
519 connect( kComboBox_genre
, SIGNAL(activated( int )), SLOT(checkModified()) );
520 connect( kComboBox_genre
, SIGNAL(textChanged( const QString
& )), SLOT(checkModified()) );
521 connect( kComboBox_rating
, SIGNAL(activated( int )), SLOT(checkModified()) );
522 connect( kComboBox_rating
, SIGNAL(textChanged( const QString
& )), SLOT(checkModified()) );
523 connect( qSpinBox_track
, SIGNAL(valueChanged( int )), SLOT(checkModified()) );
524 connect( qSpinBox_year
, SIGNAL(valueChanged( int )), SLOT(checkModified()) );
525 connect( qSpinBox_score
, SIGNAL(valueChanged( int )), SLOT(checkModified()) );
526 connect( kTextEdit_comment
, SIGNAL(textChanged()), SLOT(checkModified()) );
527 connect( kTextEdit_lyrics
, SIGNAL(textChanged()), SLOT(checkModified()) );
528 connect( kTextEdit_selectedLabels
, SIGNAL(textChanged()), SLOT(checkModified()) );
530 // Remember original button text
531 m_buttonMbText
= pushButton_musicbrainz
->text();
533 connect( pushButton_cancel
, SIGNAL(clicked()), SLOT(cancelPressed()) );
534 connect( pushButton_ok
, SIGNAL(clicked()), SLOT(accept()) );
535 connect( pushButton_open
, SIGNAL(clicked()), SLOT(openPressed()) );
536 connect( pushButton_previous
, SIGNAL(clicked()), SLOT(previousTrack()) );
537 connect( pushButton_next
, SIGNAL(clicked()), SLOT(nextTrack()) );
538 connect( checkBox_perTrack
, SIGNAL(clicked()), SLOT(perTrack()) );
540 // set an icon for the open-in-konqui button
541 pushButton_open
->setIcon( KIcon( Amarok::icon( "files" ) ) );
543 //Update lyrics on Context Browser
544 // connect( this, SIGNAL(lyricsChanged( const QString& )), ContextBrowser::instance(), SLOT( lyricsChanged( const QString& ) ) );
547 connect( CollectionDB::instance(), SIGNAL( coverFetched( const QString
&, const QString
& ) ),
548 this, SLOT( loadCover( const QString
&, const QString
& ) ) );
549 connect( CollectionDB::instance(), SIGNAL( coverChanged( const QString
&, const QString
& ) ),
550 this, SLOT( loadCover( const QString
&, const QString
& ) ) );
555 connect( pushButton_musicbrainz
, SIGNAL(clicked()), SLOT(musicbrainzQuery()) );
557 pushButton_musicbrainz
->setToolTip( i18n("Please install MusicBrainz to enable this functionality") );
560 connect( pushButton_guessTags
, SIGNAL(clicked()), SLOT( guessFromFilename() ) );
561 connect( pushButton_setFilenameSchemes
, SIGNAL(clicked()), SLOT( setFileNameSchemes() ) );
563 if( m_tracks
.count() ) { //editing multiple tracks
565 setMultipleTracksMode();
566 readMultipleTracks();
568 checkBox_perTrack
->setChecked( m_perTrack
);
569 if( m_urlList
.count() == 1 )
571 checkBox_perTrack
->setEnabled( false );
572 pushButton_previous
->setEnabled( false );
573 pushButton_next
->setEnabled( false );
577 checkBox_perTrack
->setEnabled( true );
578 pushButton_previous
->setEnabled( m_perTrack
);
579 pushButton_next
->setEnabled( m_perTrack
);
585 checkBox_perTrack
->hide();
587 if( !m_playlistItem
) {
588 //We have already loaded the metadata (from the file) in the constructor
589 pushButton_previous
->hide();
590 pushButton_next
->hide();
595 //Reload the metadata from the file, to be sure it's accurate
596 // loadTags( m_playlistItem->url() );
599 loadLyrics( m_bundle
.url() );
600 loadLabels( m_bundle
.url() );
605 // make it as small as possible
606 resize( sizeHint().width(), minimumSize().height() );
611 inline const QString
TagDialog::unknownSafe( QString s
) {
612 return ( s
.isNull() || s
.isEmpty() || s
== "?" || s
== "-" )
617 const QStringList
TagDialog::statisticsData() {
619 QStringList data
, values
;
620 const uint artist_id
= CollectionDB::instance()->artistID( m_bundle
.artist() );
621 const uint album_id
= CollectionDB::instance()->albumID ( m_bundle
.album() );
625 if ( !m_bundle
.artist().isEmpty() ) {
626 // tracks by this artist
628 qb
.addReturnFunctionValue( QueryBuilder::funcCount
, QueryBuilder::tabSong
, QueryBuilder::valTitle
);
629 qb
.addMatch( QueryBuilder::tabSong
, QueryBuilder::valArtistID
, QString::number( artist_id
) );
631 data
+= i18n( "Tracks by this Artist" );
635 // albums by this artist
637 qb
.addReturnFunctionValue( QueryBuilder::funcCount
, QueryBuilder::tabAlbum
, QueryBuilder::valID
);
638 qb
.addMatch( QueryBuilder::tabSong
, QueryBuilder::valArtistID
, QString::number( artist_id
) );
639 qb
.groupBy( QueryBuilder::tabSong
, QueryBuilder::valAlbumID
);
640 qb
.excludeMatch( QueryBuilder::tabAlbum
, i18n( "Unknown" ) );
641 qb
.setOptions( QueryBuilder::optNoCompilations
);
643 data
+= i18n( "Albums by this Artist" );
644 data
+= QString::number( values
.count() );
647 // Favorite track by this artist
649 qb
.addReturnValue( QueryBuilder::tabSong
, QueryBuilder::valTitle
);
650 qb
.addReturnValue( QueryBuilder::tabStats
, QueryBuilder::valScore
);
651 qb
.addMatch( QueryBuilder::tabSong
, QueryBuilder::valArtistID
, QString::number( artist_id
) );
655 data
+= i18n( "Favorite by this Artist" );
656 data
+= values
.isEmpty() ? QString() : values
[0];
658 if ( !m_bundle
.album().isEmpty() ) {
659 // Favorite track on this album
661 qb
.addReturnValue( QueryBuilder::tabSong
, QueryBuilder::valTitle
);
662 qb
.addReturnValue( QueryBuilder::tabStats
, QueryBuilder::valScore
);
663 qb
.addMatch( QueryBuilder::tabSong
, QueryBuilder::valAlbumID
, QString::number( album_id
) );
667 data
+= i18n( "Favorite on this Album" );
668 data
+= values
.isEmpty() ? QString() : values
[0];
672 const QString sArtists
= CollectionDB::instance()->similarArtists( m_bundle
.artist(), 4 ).join(", ");
673 if ( !sArtists
.isEmpty() ) {
674 data
+= i18n( "Related Artists" );
681 void TagDialog::readTags()
683 bool local
= m_currentTrack
->playableUrl().isLocalFile();
685 setWindowTitle( KDialog::makeStandardCaption( i18n("Track Information: %1 by %2",
686 m_currentTrack
->name(), m_currentTrack
->artist() ? m_currentTrack
->artist()->name() : QString() ) ) );
689 if ( m_currentTrack
->album()->name().isEmpty() ) {
690 if( !m_currentTrack
->name().isEmpty() ) {
691 if( !m_currentTrack
->artist()->name().isEmpty() )
692 niceTitle
= i18n( "<b>%1</b> by <b>%2</b>", m_currentTrack
->name(), m_currentTrack
->artist()->name() );
694 niceTitle
= QString( "<b>%1</b>").arg( m_currentTrack
->name() );
696 else niceTitle
= m_currentTrack
->prettyName();
699 niceTitle
= i18n( "<b>%1</b> by <b>%2</b> on <b>%3</b>" ,
700 m_currentTrack
->name(), m_currentTrack
->artist()->name(), m_currentTrack
->album()->name() );
702 trackArtistAlbumLabel
->setText( niceTitle
);
703 trackArtistAlbumLabel2
->setText( niceTitle
);
705 kLineEdit_title
->setText( m_currentTrack
->name() );
706 kComboBox_artist
->setItemText( kComboBox_artist
->currentIndex(), m_currentTrack
->artist()->name() );
707 kComboBox_album
->setItemText( kComboBox_album
->currentIndex(), m_currentTrack
->album()->name() );
708 kComboBox_genre
->setItemText( kComboBox_genre
->currentIndex(), m_currentTrack
->genre()->name() );
709 kComboBox_rating
->setCurrentIndex( m_currentTrack
->rating() ? m_currentTrack
->rating() - 1 : 0 );
710 qSpinBox_track
->setValue( m_currentTrack
->trackNumber() );
711 kComboBox_composer
->setItemText( kComboBox_composer
->currentIndex(), m_currentTrack
->composer()->name() );
712 qSpinBox_year
->setValue( m_currentTrack
->year()->name().toInt() );
713 qSpinBox_score
->setValue( static_cast<int>(m_currentTrack
->score()) );
714 qSpinBox_discNumber
->setValue( m_currentTrack
->discNumber() );
715 kTextEdit_comment
->setText( m_currentTrack
->comment() );
718 //bool extended = m_bundle.hasExtendedMetaInformation();
719 //qSpinBox_discNumber->setEnabled( extended );
720 //kComboBox_composer->setEnabled( extended );
723 QString summaryText
, statisticsText
;
724 const QString body2cols
= "<tr><td><nobr>%1</nobr></td><td><b>%2</b></td></tr>";
725 const QString body1col
= "<tr><td colspan=2>%1</td></td></tr>";
726 const QString emptyLine
= "<tr><td colspan=2></td></tr>";
728 summaryText
= "<table width=100%><tr><td width=50%><table>";
729 summaryText
+= body2cols
.arg( i18n("Length:"), unknownSafe( m_bundle
.prettyLength() ) );
730 summaryText
+= body2cols
.arg( i18n("Bitrate:"), unknownSafe( m_bundle
.prettyBitrate() ) );
731 summaryText
+= body2cols
.arg( i18n("Samplerate:"), unknownSafe( m_bundle
.prettySampleRate() ) );
732 summaryText
+= body2cols
.arg( i18n("Size:"), unknownSafe( m_bundle
.prettyFilesize() ) );
733 summaryText
+= body2cols
.arg( i18n("Format:"), unknownSafe( m_currentTrack
->type() ) );
735 summaryText
+= "</table></td><td width=50%><table>";
736 if( AmarokConfig::useScores() )
737 summaryText
+= body2cols
.arg( i18n("Score:"), QString::number( static_cast<int>( m_currentTrack
->score() ) ) );
738 if( AmarokConfig::useRatings() )
739 summaryText
+= body2cols
.arg( i18n("Rating:"), m_bundle
.prettyRating() );
741 summaryText
+= body2cols
.arg( i18n("Playcount:"), QString::number( m_currentTrack
->playCount() ) );
742 QDate firstPlayed
= QDateTime::fromTime_t( m_currentTrack
->firstPlayed() ).date();
743 QDate lastPlayed
= QDateTime::fromTime_t( m_currentTrack
->lastPlayed() ).date();
744 summaryText
+= body2cols
.arg( i18n("First Played:"),
745 m_currentTrack
->playCount() ? KGlobal::locale()->formatDate( firstPlayed
, KLocale::ShortDate
) : i18n("Never") );
746 summaryText
+= body2cols
.arg( i18nc("a single item (singular)", "Last Played:"),
747 m_currentTrack
->playCount() ? KGlobal::locale()->formatDate( lastPlayed
, KLocale::ShortDate
) : i18n("Never") );
749 summaryText
+= "</table></td></tr></table>";
750 summaryLabel
->setText( summaryText
);
752 statisticsText
= "<table>";
754 QStringList sData
= statisticsData();
755 for ( int i
= 0; i
< sData
.count(); i
+=2 ) {
756 statisticsText
+= body2cols
.arg( sData
[i
], sData
[i
+1] );
759 statisticsText
+= "</table>";
761 statisticsLabel
->setText( statisticsText
);
763 kLineEdit_location
->setText( local
? m_currentTrack
->playableUrl().path() : m_currentTrack
->playableUrl().url() );
766 kTextEdit_lyrics
->setText( m_lyrics
);
768 pixmap_cover
->setPixmap( m_currentTrack
->album()->image( AmarokConfig::coverPreviewSize(), false ) );
769 pixmap_cover
->setInformation( m_currentTrack
->artist()->name(), m_currentTrack
->album()->name() );
770 const int s
= AmarokConfig::coverPreviewSize();
771 pixmap_cover
->setMinimumSize( s
, s
);
772 pixmap_cover
->setMaximumSize( s
, s
);
775 // enable only for local files
776 kLineEdit_title
->setReadOnly( !local
);
777 kComboBox_artist
->setEnabled( local
);
778 kComboBox_album
->setEnabled( local
);
779 kComboBox_genre
->setEnabled( local
);
780 kComboBox_rating
->setEnabled( local
);
781 qSpinBox_track
->setEnabled( local
);
782 qSpinBox_year
->setEnabled( local
);
783 qSpinBox_score
->setEnabled( local
);
784 kTextEdit_comment
->setEnabled( local
);
785 kTextEdit_selectedLabels
->setEnabled( local
);
786 m_labelCloud
->view()->setEnabled( local
);
790 pushButton_musicbrainz
->show();
791 pushButton_guessTags
->show();
792 pushButton_setFilenameSchemes
->show();
796 pushButton_musicbrainz
->hide();
797 pushButton_guessTags
->hide();
798 pushButton_setFilenameSchemes
->hide();
801 // If it's a local file, write the directory to m_path, else disable the "open in konqui" button
803 m_path
= m_currentTrack
->playableUrl().directory();
805 pushButton_open
->setEnabled( false );
807 pushButton_ok
->setEnabled( storedTags
.count() > 0 || storedScores
.count() > 0
808 || storedLyrics
.count() > 0 || storedRatings
.count() > 0
809 || newLabels
.count() > 0 );
812 // Don't enable button if a query is in progress already (or if the file isn't local)
813 pushButton_musicbrainz
->setEnabled( m_bundle
.url().isLocalFile() && m_mbTrack
.isEmpty() );
815 pushButton_musicbrainz
->setEnabled( false );
819 // if( m_playlistItem ) {
820 // pushButton_previous->setEnabled( m_playlistItem->itemAbove() );
821 // pushButton_next->setEnabled( m_playlistItem->itemBelow() );
827 TagDialog::setMultipleTracksMode()
830 kTabWidget
->setTabEnabled( kTabWidget
->indexOf(summaryTab
), false );
831 kTabWidget
->setTabEnabled( kTabWidget
->indexOf(lyricsTab
), false );
833 kComboBox_artist
->setItemText( kComboBox_artist
->currentIndex(), "" );
834 kComboBox_album
->setItemText( kComboBox_album
->currentIndex(), "" );
835 kComboBox_genre
->setItemText( kComboBox_genre
->currentIndex(), "" );
836 kComboBox_composer
->setItemText( kComboBox_composer
->currentIndex(), "" );
837 kLineEdit_title
->setText( "" );
838 kTextEdit_comment
->setText( "" );
839 qSpinBox_track
->setValue( qSpinBox_track
->minimum() );
840 qSpinBox_discNumber
->setValue( qSpinBox_discNumber
->minimum() );
841 qSpinBox_year
->setValue( qSpinBox_year
->minimum() );
843 qSpinBox_score
->setValue( qSpinBox_score
->minimum() );
844 kComboBox_rating
->setCurrentItem( 0 );
846 kLineEdit_title
->setEnabled( false );
847 qSpinBox_track
->setEnabled( false );
849 pushButton_musicbrainz
->hide();
850 pushButton_guessTags
->hide();
851 pushButton_setFilenameSchemes
->hide();
853 locationLabel
->hide();
854 kLineEdit_location
->hide();
855 pushButton_open
->hide();
856 pixmap_cover
->hide();
860 TagDialog::setSingleTrackMode()
863 kTabWidget
->setTabEnabled( kTabWidget
->indexOf(summaryTab
), true );
864 kTabWidget
->setTabEnabled( kTabWidget
->indexOf(lyricsTab
), true );
866 kLineEdit_title
->setEnabled( true );
867 qSpinBox_track
->setEnabled( true );
869 pushButton_musicbrainz
->show();
870 pushButton_guessTags
->show();
871 pushButton_setFilenameSchemes
->show();
873 locationLabel
->show();
874 kLineEdit_location
->show();
875 pushButton_open
->show();
876 pixmap_cover
->show();
881 TagDialog::readMultipleTracks()
884 setWindowTitle( KDialog::makeStandardCaption( i18np("1 Track", "Information for %1 Tracks", m_urlList
.count()) ) );
886 //Check which fields are the same for all selected tracks
887 const KUrl::List::ConstIterator end
= m_urlList
.end();
888 KUrl::List::ConstIterator it
= m_urlList
.begin();
890 m_bundle
= MetaBundle();
892 MetaBundle first
= bundleForURL( *it
);
894 bool artist
=true, album
=true, genre
=true, comment
=true, year
=true,
895 score
=true, rating
=true, composer
=true, discNumber
=true;
896 int songCount
=0, ratingCount
=0, ratingSum
=0, scoreCount
=0;
897 float scoreSum
= 0.f
;
898 for ( ; it
!= end
; ++it
) {
899 MetaBundle mb
= bundleForURL( *it
);
903 ratingSum
+=mb
.rating();
905 if ( mb
.score() > 0.f
) {
907 scoreSum
+=mb
.score();
910 if( !mb
.url().isLocalFile() ) {
911 // If we have a non local file, don't even lose more time comparing
912 artist
= album
= genre
= comment
= year
= false;
913 score
= rating
= composer
= discNumber
= false;
916 if ( artist
&& mb
.artist()!=first
.artist() )
918 if ( album
&& mb
.album()!=first
.album() )
920 if ( genre
&& mb
.genre()!=first
.genre() )
922 if ( comment
&& mb
.comment()!=first
.comment() )
924 if ( year
&& mb
.year()!=first
.year() )
926 if ( composer
&& mb
.composer()!=first
.composer() )
928 if ( discNumber
&& mb
.discNumber()!=first
.discNumber() )
930 if ( score
&& mb
.score()!=first
.score() )
932 if ( rating
&& mb
.rating()!=first
.rating() )
935 // Set them in the dialog and in m_bundle ( so we don't break hasChanged() )
938 cur_item
= kComboBox_artist
->currentIndex();
939 m_bundle
.setArtist( first
.artist() );
940 kComboBox_artist
->setItemText( cur_item
, first
.artist() );
943 cur_item
= kComboBox_album
->currentIndex();
944 m_bundle
.setAlbum( first
.album() );
945 kComboBox_album
->setItemText( cur_item
, first
.album() );
948 cur_item
= kComboBox_genre
->currentIndex();
949 m_bundle
.setGenre( first
.genre() );
950 kComboBox_genre
->setItemText( cur_item
, first
.genre() );
953 m_bundle
.setComment( first
.comment() );
954 kTextEdit_comment
->setText( first
.comment() );
957 cur_item
= kComboBox_composer
->currentIndex();
958 m_bundle
.setComposer( first
.composer() );
959 kComboBox_composer
->setItemText( cur_item
, first
.composer() );
962 m_bundle
.setYear( first
.year() );
963 qSpinBox_year
->setValue( first
.year() );
966 m_bundle
.setDiscNumber( first
.discNumber() );
967 qSpinBox_discNumber
->setValue( first
.discNumber() );
970 m_bundle
.setScore( first
.score() );
971 qSpinBox_score
->setValue( static_cast<int>( first
.score() ) );
974 m_bundle
.setRating( first
.rating() );
975 kComboBox_rating
->setCurrentIndex( first
.rating() ? first
.rating() - 1 : 0 );
978 m_currentURL
= m_urlList
.begin();
980 trackArtistAlbumLabel2
->setText( i18np( "Editing 1 file", "Editing %1 files", songCount
) );
982 const QString body
= "<tr><td><nobr>%1:</nobr></td><td><b>%2</b></td></tr>";
983 QString statisticsText
= "<table>";
985 if( AmarokConfig::useRatings() ) {
986 statisticsText
+= body
.arg( i18n( "Rated Songs:" ) , QString::number( ratingCount
) );
988 statisticsText
+= body
.arg( i18n( "Average Rating:" ) , QString::number( (float)ratingSum
/ (float)ratingCount
/2.0, 'f', 1 ) );
991 if( AmarokConfig::useRatings() ) {
992 statisticsText
+= body
.arg( i18n( "Scored Songs:" ) , QString::number( scoreCount
) );
994 statisticsText
+= body
.arg( i18n( "Average Score:" ) , QString::number( scoreSum
/ scoreCount
, 'f', 1 ) );
998 statisticsText
+= "</table>";
1000 statisticsLabel
->setText( statisticsText
);
1002 QStringList commonLabels
= getCommonLabels();
1004 oldForeach ( commonLabels
)
1006 if ( !text
.isEmpty() )
1007 text
.append( ", " );
1010 kTextEdit_selectedLabels
->setText( text
);
1011 m_commaSeparatedLabels
= text
;
1013 // This will reset a wrongly enabled Ok button
1018 TagDialog::getCommonLabels()
1021 QMap
<QString
, int> counterMap
;
1022 const KUrl::List::ConstIterator end
= m_urlList
.end();
1023 KUrl::List::ConstIterator iter
= m_urlList
.begin();
1024 for(; iter
!= end
; ++iter
)
1026 QStringList labels
= labelsForURL( *iter
);
1027 oldForeach( labels
)
1029 if ( counterMap
.contains( *it
) )
1030 counterMap
[ *it
] = counterMap
[ *it
] +1;
1032 counterMap
[ *it
] = 1;
1035 int n
= m_urlList
.count();
1037 QMap
<QString
, int>::ConstIterator
counterEnd( counterMap
.end() );
1038 for(QMap
<QString
, int>::ConstIterator it
= counterMap
.begin(); it
!= counterEnd
; ++it
)
1040 if ( it
.value() == n
)
1041 result
.append( it
.key() );
1047 equalString( const QString
&a
, const QString
&b
)
1049 return (a
.isEmpty() && b
.isEmpty()) ? true : a
== b
;
1053 TagDialog::hasChanged()
1059 TagDialog::changes()
1061 int result
=TagDialog::NOCHANGE
;
1062 bool modified
= false;
1063 modified
|= !equalString( kComboBox_artist
->lineEdit()->text(), m_bundle
.artist() );
1064 modified
|= !equalString( kComboBox_album
->lineEdit()->text(), m_bundle
.album() );
1065 modified
|= !equalString( kComboBox_genre
->lineEdit()->text(), m_bundle
.genre() );
1066 modified
|= qSpinBox_year
->value() != m_bundle
.year();
1067 modified
|= qSpinBox_discNumber
->value() != m_bundle
.discNumber();
1068 modified
|= !equalString( kComboBox_composer
->lineEdit()->text(), m_bundle
.composer() );
1070 modified
|= !equalString( kTextEdit_comment
->toPlainText(), m_bundle
.comment() );
1072 if (!m_urlList
.count() || m_perTrack
) { //ignore these on MultipleTracksMode
1073 modified
|= !equalString( kLineEdit_title
->text(), m_bundle
.title() );
1074 modified
|= qSpinBox_track
->value() != m_bundle
.track();
1077 result
|= TagDialog::TAGSCHANGED
;
1079 if (qSpinBox_score
->value() != m_bundle
.score())
1080 result
|= TagDialog::SCORECHANGED
;
1081 if (kComboBox_rating
->currentIndex() != ( m_bundle
.rating() ? m_bundle
.rating() - 1 : 0 ) )
1082 result
|= TagDialog::RATINGCHANGED
;
1084 if (!m_urlList
.count() || m_perTrack
) { //ignore these on MultipleTracksMode
1085 if ( !equalString( kTextEdit_lyrics
->toPlainText(), m_lyrics
) )
1086 result
|= TagDialog::LYRICSCHANGED
;
1089 if ( !equalString( kTextEdit_selectedLabels
->toPlainText(), m_commaSeparatedLabels
) )
1090 result
|= TagDialog::LABELSCHANGED
;
1096 TagDialog::storeTags()
1098 storeTags( m_bundle
.url() );
1102 TagDialog::storeTags( const KUrl
&kurl
)
1104 int result
= changes();
1105 QString url
= kurl
.path();
1106 if( result
& TagDialog::TAGSCHANGED
) {
1107 MetaBundle
mb( m_bundle
);
1109 mb
.setTitle( kLineEdit_title
->text() );
1110 mb
.setComposer( kComboBox_composer
->currentText() );
1111 mb
.setArtist( kComboBox_artist
->currentText() );
1112 mb
.setAlbum( kComboBox_album
->currentText() );
1113 mb
.setComment( kTextEdit_comment
->toPlainText() );
1114 mb
.setGenre( kComboBox_genre
->currentText() );
1115 mb
.setTrack( qSpinBox_track
->value() );
1116 mb
.setYear( qSpinBox_year
->value() );
1117 mb
.setDiscNumber( qSpinBox_discNumber
->value() );
1118 mb
.setLength( m_bundle
.length() );
1119 mb
.setBitrate( m_bundle
.bitrate() );
1120 mb
.setSampleRate( m_bundle
.sampleRate() );
1121 storedTags
.remove( url
);
1122 storedTags
.insert( url
, mb
);
1124 if( result
& TagDialog::SCORECHANGED
) {
1125 storedScores
.remove( url
);
1126 storedScores
.insert( url
, qSpinBox_score
->value() );
1129 if( result
& TagDialog::RATINGCHANGED
) {
1130 storedRatings
.remove( url
);
1131 storedRatings
.insert( url
, kComboBox_rating
->currentIndex() ? kComboBox_rating
->currentIndex() + 1 : 0 );
1134 if( result
& TagDialog::LYRICSCHANGED
) {
1135 if ( kTextEdit_lyrics
->toPlainText().isEmpty() ) {
1136 storedLyrics
.remove( url
);
1137 storedLyrics
.insert( url
, QString() );
1141 QDomElement e
= doc
.createElement( "lyrics" );
1142 e
.setAttribute( "artist", kComboBox_artist
->currentText() );
1143 e
.setAttribute( "title", kLineEdit_title
->text() );
1144 QDomText t
= doc
.createTextNode( kTextEdit_lyrics
->toPlainText() );
1146 doc
.appendChild( e
);
1147 storedLyrics
.remove( url
);
1148 storedLyrics
.insert( url
, doc
.toString() );
1151 if( result
& TagDialog::LABELSCHANGED
) {
1152 generateDeltaForLabelList( labelListFromText( kTextEdit_selectedLabels
->toPlainText() ) );
1153 QStringList tmpLabels
;
1154 if ( newLabels
.find( url
) != newLabels
.end() )
1155 tmpLabels
= newLabels
[ url
];
1157 tmpLabels
= originalLabels
[ url
];
1159 oldForeach( m_removedLabels
)
1161 tmpLabels
.removeAt( tmpLabels
.indexOf(*it
) );
1163 oldForeach( m_addedLabels
)
1165 // this just feels dirty...
1166 if( tmpLabels
.indexOf( *it
) == tmpLabels
.indexOf( *tmpLabels
.end() ) )
1167 tmpLabels
.append( *it
);
1169 newLabels
.remove( url
);
1170 newLabels
.insert( url
, tmpLabels
);
1175 TagDialog::storeTags( const KUrl
&url
, int changes
, const MetaBundle
&mb
)
1177 if ( changes
& TagDialog::TAGSCHANGED
) {
1178 storedTags
.remove( url
.path() );
1179 storedTags
.insert( url
.path(), mb
);
1181 if ( changes
& TagDialog::SCORECHANGED
) {
1182 storedScores
.remove( url
.path() );
1183 storedScores
.insert( url
.path(), mb
.score() );
1185 if ( changes
& TagDialog::RATINGCHANGED
) {
1186 storedRatings
.remove( url
.path() );
1187 storedRatings
.insert( url
.path(), mb
.rating() );
1192 TagDialog::storeLabels( const KUrl
&url
, const QStringList
&labels
)
1194 newLabels
.remove( url
.path() );
1195 newLabels
.insert( url
.path(), labels
);
1200 TagDialog::loadTags( const KUrl
&url
)
1202 m_bundle
= bundleForURL( url
);
1208 TagDialog::loadLyrics( const KUrl
&url
)
1210 QString xml
= lyricsForURL(url
.path() );
1213 if( doc
.setContent( xml
) )
1214 m_lyrics
= doc
.documentElement().text();
1220 TagDialog::loadLabels( const KUrl
&url
)
1223 m_labels
= labelsForURL( url
);
1224 originalLabels
[ url
.path() ] = m_labels
;
1226 oldForeach( m_labels
)
1228 if ( !text
.isEmpty() )
1229 text
.append( ", " );
1232 kTextEdit_selectedLabels
->setText( text
);
1233 m_commaSeparatedLabels
= text
;
1237 TagDialog::bundleForURL( const KUrl
&url
)
1239 if( storedTags
.find( url
.path() ) != storedTags
.end() )
1240 return storedTags
[ url
.path() ];
1242 return MetaBundle( url
, url
.isLocalFile() );
1246 TagDialog::scoreForURL( const KUrl
&url
)
1248 if( storedScores
.find( url
.path() ) != storedScores
.end() )
1249 return storedScores
[ url
.path() ];
1251 return CollectionDB::instance()->getSongPercentage( url
.path() );
1255 TagDialog::ratingForURL( const KUrl
&url
)
1257 if( storedRatings
.find( url
.path() ) != storedRatings
.end() )
1258 return storedRatings
[ url
.path() ];
1260 return CollectionDB::instance()->getSongRating( url
.path() );
1264 TagDialog::lyricsForURL( const KUrl
&url
)
1266 if( storedLyrics
.find( url
.path() ) != storedLyrics
.end() )
1267 return storedLyrics
[ url
.path() ];
1269 return CollectionDB::instance()->getLyrics( url
.path() );
1273 TagDialog::labelsForURL( const KUrl
&url
)
1275 if( newLabels
.find( url
.path() ) != newLabels
.end() )
1276 return newLabels
[ url
.path() ];
1277 if( originalLabels
.find( url
.path() ) != originalLabels
.end() )
1278 return originalLabels
[ url
.path() ];
1279 QStringList tmp
= CollectionDB::instance()->getLabels( url
.path(), CollectionDB::typeUser
);
1280 originalLabels
[ url
.path() ] = tmp
;
1285 TagDialog::saveTags()
1296 QMap
<QString
, float>::ConstIterator
endScore( storedScores
.end() );
1297 for(QMap
<QString
, float>::ConstIterator it
= storedScores
.begin(); it
!= endScore
; ++it
) {
1298 CollectionDB::instance()->setSongPercentage( it
.key(), it
.value() );
1300 QMap
<QString
, int>::ConstIterator
endRating( storedRatings
.end() );
1301 for(QMap
<QString
, int>::ConstIterator it
= storedRatings
.begin(); it
!= endRating
; ++it
) {
1302 CollectionDB::instance()->setSongRating( it
.key(), it
.value() );
1304 QMap
<QString
, QString
>::ConstIterator
endLyrics( storedLyrics
.end() );
1305 for(QMap
<QString
, QString
>::ConstIterator it
= storedLyrics
.begin(); it
!= endLyrics
; ++it
) {
1306 CollectionDB::instance()->setLyrics( it
.key(), it
.value(),
1307 CollectionDB::instance()->uniqueIdFromUrl( KUrl( it
.key() ) ) );
1308 emit
lyricsChanged( it
.key() );
1310 QMap
<QString
, QStringList
>::ConstIterator
endLabels( newLabels
.end() );
1311 for(QMap
<QString
, QStringList
>::ConstIterator it
= newLabels
.begin(); it
!= endLabels
; ++it
) {
1312 CollectionDB::instance()->setLabels( it
.key(), it
.value(),
1313 CollectionDB::instance()->uniqueIdFromUrl( KUrl( it
.key() ) ), CollectionDB::typeUser
);
1315 CollectionDB::instance()->cleanLabels();
1317 ThreadManager::instance()->queueJob( new TagDialogWriter( storedTags
) );
1322 TagDialog::applyToAllTracks()
1324 generateDeltaForLabelList( labelListFromText( kTextEdit_selectedLabels
->toPlainText() ) );
1326 const KUrl::List::ConstIterator end
= m_urlList
.end();
1327 for ( KUrl::List::ConstIterator it
= m_urlList
.begin(); it
!= end
; ++it
) {
1329 /* we have to update the values if they changed, so:
1330 1) !kLineEdit_field->text().isEmpty() && kLineEdit_field->text() != mb.field
1331 i.e.: The user wrote something on the field, and it's different from
1332 what we have in the tag.
1333 2) !m_bundle.field().isEmpty() && kLineEdit_field->text().isEmpty()
1334 i.e.: The user was shown some value for the field (it was the same
1335 for all selected tracks), and he deliberately emptied it.
1336 TODO: All this mess is because the dialog uses "" to represent what the user
1337 doesn't want to change, maybe we can think of something better?
1340 MetaBundle mb
= bundleForURL( *it
);
1343 if( !kComboBox_artist
->currentText().isEmpty() && kComboBox_artist
->currentText() != mb
.artist() ||
1344 kComboBox_artist
->currentText().isEmpty() && !m_bundle
.artist().isEmpty() ) {
1345 mb
.setArtist( kComboBox_artist
->currentText() );
1346 changed
|= TagDialog::TAGSCHANGED
;
1349 if( !kComboBox_album
->currentText().isEmpty() && kComboBox_album
->currentText() != mb
.album() ||
1350 kComboBox_album
->currentText().isEmpty() && !m_bundle
.album().isEmpty() ) {
1351 mb
.setAlbum( kComboBox_album
->currentText() );
1352 changed
|= TagDialog::TAGSCHANGED
;
1354 if( !kComboBox_genre
->currentText().isEmpty() && kComboBox_genre
->currentText() != mb
.genre() ||
1355 kComboBox_genre
->currentText().isEmpty() && !m_bundle
.genre().isEmpty() ) {
1356 mb
.setGenre( kComboBox_genre
->currentText() );
1357 changed
|= TagDialog::TAGSCHANGED
;
1359 if( !kTextEdit_comment
->toPlainText().isEmpty() && kTextEdit_comment
->toPlainText() != mb
.comment() ||
1360 kTextEdit_comment
->toPlainText().isEmpty() && !m_bundle
.comment().isEmpty() ) {
1361 mb
.setComment( kTextEdit_comment
->toPlainText() );
1362 changed
|= TagDialog::TAGSCHANGED
;
1364 if( !kComboBox_composer
->currentText().isEmpty() && kComboBox_composer
->currentText() != mb
.composer() ||
1365 kComboBox_composer
->currentText().isEmpty() && !m_bundle
.composer().isEmpty() ) {
1366 mb
.setComposer( kComboBox_composer
->currentText() );
1367 changed
|= TagDialog::TAGSCHANGED
;
1370 if( qSpinBox_year
->value() && qSpinBox_year
->value() != mb
.year() ||
1371 !qSpinBox_year
->value() && m_bundle
.year() ) {
1372 mb
.setYear( qSpinBox_year
->value() );
1373 changed
|= TagDialog::TAGSCHANGED
;
1375 if( qSpinBox_discNumber
->value() && qSpinBox_discNumber
->value() != mb
.discNumber() ||
1376 !qSpinBox_discNumber
->value() && m_bundle
.discNumber() ) {
1377 mb
.setDiscNumber( qSpinBox_discNumber
->value() );
1378 changed
|= TagDialog::TAGSCHANGED
;
1381 if( qSpinBox_score
->value() && qSpinBox_score
->value() != mb
.score() ||
1382 !qSpinBox_score
->value() && m_bundle
.score() )
1384 mb
.setScore( qSpinBox_score
->value() );
1385 changed
|= TagDialog::SCORECHANGED
;
1388 if( kComboBox_rating
->currentIndex() && kComboBox_rating
->currentIndex() != m_bundle
.rating() - 1 ||
1389 !kComboBox_rating
->currentIndex() && m_bundle
.rating() )
1391 mb
.setRating( kComboBox_rating
->currentIndex() ? kComboBox_rating
->currentIndex() + 1 : 0 );
1392 changed
|= TagDialog::RATINGCHANGED
;
1394 storeTags( *it
, changed
, mb
);
1396 QStringList tmpLabels
= labelsForURL( *it
);
1398 for( QStringList::Iterator iter
= m_removedLabels
.begin(); iter
!= m_removedLabels
.end(); ++iter
)
1400 tmpLabels
.erase( iter
);
1402 for( QStringList::Iterator iter
= m_addedLabels
.begin(); iter
!= m_addedLabels
.end(); ++iter
)
1404 if( tmpLabels
.indexOf( *iter
) == tmpLabels
.indexOf( *tmpLabels
.end() ) )
1405 tmpLabels
.append( *iter
);
1407 storeLabels( *it
, tmpLabels
);
1412 TagDialog::labelListFromText( const QString
&text
)
1414 QStringList tmp
= text
.split(',', QString::SkipEmptyParts
);
1415 //insert each string into a map to remove duplicates
1416 QMap
<QString
, int> map
;
1419 QString tmpString
= (*it
).trimmed();
1420 if ( !tmpString
.isEmpty() ) {
1421 map
.remove( tmpString
);
1422 map
.insert( tmpString
, 0 );
1426 QMap
<QString
, int>::ConstIterator
endMap( map
.end() );
1427 for(QMap
<QString
, int>::ConstIterator it
= map
.begin(); it
!= endMap
; ++it
) {
1428 result
.append( it
.key() );
1434 TagDialog::generateDeltaForLabelList( const QStringList
&list
)
1436 m_addedLabels
.clear();
1437 m_removedLabels
.clear();
1440 if ( !m_labels
.contains( *it
) )
1441 m_addedLabels
.append( *it
);
1443 oldForeach( m_labels
)
1445 if ( !list
.contains( *it
) )
1446 m_removedLabels
.append( *it
);
1452 TagDialog::generateHTML( const QStringList
&labels
)
1454 //the first column of each row is the label name, the second the number of assigned songs
1455 //loop through it to find the highest number of songs, can be removed if somebody figures out a better sql query
1456 QMap
<QString
, QPair
<QString
, int> > mapping
;
1457 QStringList sortedLabels
;
1459 oldForeach( labels
)
1461 QString label
= *it
;
1462 sortedLabels
<< label
.toLower();
1464 int value
= ( *it
).toInt();
1467 mapping
[label
.toLower()] = QPair
<QString
, int>( label
, value
);
1469 sortedLabels
.sort();
1470 QString html
= "<html><body>";
1471 oldForeach( sortedLabels
)
1474 //generate a number in the range 1..10 based on how much the label is used
1475 int labelUse
= ( mapping
[key
].second
* 10 ) / max
;
1476 if ( labelUse
== 0 )
1478 html
.append( QString( "<span class='label size%1'><a href=\"label:%2\">%3</a></span> " )
1479 .arg( QString::number( labelUse
), mapping
[key
].first
, mapping
[key
].first
) );
1481 html
.append( "</html></body>" );
1482 debug() << "Dumping HTML for label cloud: " << html
;
1487 TagDialog::openUrlRequest(const KUrl
&url
) //SLOT
1490 if ( url
.protocol() == "label" )
1492 QString text
= kTextEdit_selectedLabels
->toPlainText();
1493 QStringList currentLabels
= labelListFromText( text
);
1494 if ( currentLabels
.contains( url
.path() ) )
1496 if ( !text
.isEmpty() )
1497 text
.append( ", " );
1498 text
.append( url
.path() );
1499 kTextEdit_selectedLabels
->setText( text
);
1504 TagDialog::writeTag( MetaBundle
&mb
, bool updateCB
)
1506 QByteArray path
= QFile::encodeName( mb
.url().path() );
1507 if ( !TagLib::File::isWritable( path
) ) {
1508 Amarok::ContextStatusBar::instance()->longMessage( i18n(
1509 "The file %1 is not writable.", mb
.url().fileName() ), KDE::StatusBar::Error
);
1514 QApplication::setOverrideCursor( Qt::WaitCursor
);
1516 bool result
= mb
.save();
1517 mb
.updateFilesize();
1520 //update the collection db
1521 CollectionDB::instance()->updateTags( mb
.url().path(), mb
, updateCB
);
1523 QApplication::restoreOverrideCursor();
1528 TagDialogWriter::TagDialogWriter( const QMap
<QString
, MetaBundle
> tagsToChange
)
1529 : ThreadManager::Job( "TagDialogWriter" ),
1530 m_successCount ( 0 ),
1533 QApplication::setOverrideCursor( Qt::WaitCursor
);
1534 QMap
<QString
, MetaBundle
>::ConstIterator end
= tagsToChange
.end();
1535 for(QMap
<QString
, MetaBundle
>::ConstIterator it
= tagsToChange
.begin(); it
!= end
; ++it
) {
1536 MetaBundle mb
= it
.value();
1542 TagDialogWriter::doJob()
1544 for( int i
= 0, size
=m_tags
.size(); i
<size
; ++i
) {
1545 QByteArray path
= QFile::encodeName( m_tags
[i
].url().path() );
1546 if ( !TagLib::File::isWritable( path
) ) {
1547 Amarok::ContextStatusBar::instance()->longMessageThreadSafe( i18n(
1548 "The file %1 is not writable.", m_tags
[i
].url().fileName() ), KDE::StatusBar::Error
);
1553 bool result
= m_tags
[i
].save();
1554 m_tags
[i
].updateFilesize();
1560 m_failedURLs
+= m_tags
[i
].prettyUrl();
1562 m_failed
+= !result
;
1568 TagDialogWriter::completeJob()
1570 for( int i
= 0, size
=m_tags
.size(); i
<size
; ++i
) {
1571 if ( !m_failed
[i
] ) {
1572 CollectionDB::instance()->updateTags( m_tags
[i
].url().path(), m_tags
[i
], false /* don't update browsers*/ );
1573 // PORT 2.0 Playlist::instance()->updateMetaData( m_tags[i] );
1576 QApplication::restoreOverrideCursor();
1577 //PORT 2.0 if ( m_successCount )
1578 //PORT 2.0 CollectionView::instance()->databaseChanged();
1580 Amarok::ContextStatusBar::instance()->longMessage( i18n(
1581 "Sorry, the tag for the following files could not be changed:\n", m_failedURLs
.join( ";\n" ) ), KDE::StatusBar::Error
);
1585 #include "tagdialog.moc"