2018-02-27 18:06:05 +01:00
/*
* Strawberry Music Player
* This file was part of Clementine .
* Copyright 2010 , David Sansome < me @ davidsansome . com >
2023-05-14 11:34:55 +02:00
* Copyright 2018 - 2023 , Jonas Kvinge < jonas @ jkvinge . net >
2018-02-27 18:06:05 +01:00
*
* Strawberry 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 3 of the License , or
* ( at your option ) any later version .
*
* Strawberry 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 Strawberry . If not , see < http : //www.gnu.org/licenses/>.
2018-08-09 18:39:44 +02:00
*
2018-02-27 18:06:05 +01:00
*/
# include "config.h"
2020-07-19 22:43:58 +02:00
# include <functional>
2018-05-01 00:41:33 +02:00
# include <algorithm>
# include <iterator>
2018-02-27 18:06:05 +01:00
# include <limits>
2018-05-01 00:41:33 +02:00
# include <QtGlobal>
2020-07-18 16:28:39 +02:00
# include <QtConcurrent>
2020-07-19 19:07:12 +02:00
# include <QFuture>
2021-01-30 21:53:53 +01:00
# include <QFutureWatcher>
2018-05-01 00:41:33 +02:00
# include <QObject>
# include <QWidget>
# include <QDialog>
# include <QItemSelectionModel>
# include <QAbstractItemModel>
# include <QDir>
# include <QAction>
# include <QDateTime>
# include <QList>
2021-02-27 01:02:37 +01:00
# include <QMap>
2018-05-01 00:41:33 +02:00
# include <QVariant>
# include <QString>
# include <QStringBuilder>
# include <QUrl>
# include <QPixmap>
# include <QPalette>
# include <QColor>
# include <QFont>
2018-02-27 18:06:05 +01:00
# include <QLabel>
2018-05-01 00:41:33 +02:00
# include <QLineEdit>
# include <QListWidget>
# include <QLocale>
2018-02-27 18:06:05 +01:00
# include <QMenu>
# include <QMessageBox>
# include <QShortcut>
2018-05-01 00:41:33 +02:00
# include <QSize>
# include <QSpinBox>
2020-09-23 00:52:41 +02:00
# include <QCheckBox>
2018-05-01 00:41:33 +02:00
# include <QSplitter>
# include <QTabWidget>
# include <QTextEdit>
# include <QPlainTextEdit>
# include <QKeySequence>
# include <QDialogButtonBox>
# include <QPushButton>
# include <QAbstractButton>
# include <QtEvents>
# include <QSettings>
2018-02-27 18:06:05 +01:00
# include "core/application.h"
2018-05-01 00:41:33 +02:00
# include "core/iconloader.h"
2018-02-27 18:06:05 +01:00
# include "core/logging.h"
# include "core/tagreaderclient.h"
2024-04-11 02:56:01 +02:00
# include "core/settings.h"
2022-12-28 03:12:00 +01:00
# include "utilities/strutils.h"
# include "utilities/timeutils.h"
# include "utilities/imageutils.h"
2023-03-18 20:03:07 +01:00
# include "utilities/coverutils.h"
# include "utilities/coveroptions.h"
2018-05-01 00:41:33 +02:00
# include "widgets/busyindicator.h"
# include "widgets/lineedit.h"
2018-02-27 18:06:05 +01:00
# include "collection/collectionbackend.h"
2018-05-01 00:41:33 +02:00
# include "playlist/playlist.h"
2018-02-27 18:06:05 +01:00
# include "playlist/playlistdelegates.h"
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2018-07-16 07:23:37 +02:00
# include "musicbrainz / tagfetcher.h"
2021-02-26 21:03:51 +01:00
# include "trackselectiondialog.h"
2018-07-16 07:23:37 +02:00
# endif
2024-03-06 23:16:29 +01:00
# include "lyrics/lyricsfetcher.h"
2018-05-01 00:41:33 +02:00
# include "covermanager/albumcoverchoicecontroller.h"
2018-02-27 18:06:05 +01:00
# include "covermanager/albumcoverloader.h"
2020-04-20 18:03:18 +02:00
# include "covermanager/albumcoverloaderoptions.h"
# include "covermanager/albumcoverloaderresult.h"
2018-02-27 18:06:05 +01:00
# include "covermanager/coverproviders.h"
2021-02-26 21:03:51 +01:00
# include "covermanager/currentalbumcoverloader.h"
# include "covermanager/albumcoverimageresult.h"
2018-05-01 00:41:33 +02:00
# include "edittagdialog.h"
# include "ui_edittagdialog.h"
2020-02-09 02:29:35 +01:00
# include "tagreadermessages.pb.h"
2018-02-27 18:06:05 +01:00
2024-04-11 02:56:01 +02:00
namespace {
constexpr char kTagsDifferentHintText [ ] = QT_TR_NOOP ( " (different across multiple songs) " ) ;
constexpr char kArtDifferentHintText [ ] = QT_TR_NOOP ( " Different art across multiple songs. " ) ;
constexpr char kSettingsGroup [ ] = " EditTagDialog " ;
constexpr int kSmallImageSize = 128 ;
} // namespace
2018-02-27 18:06:05 +01:00
EditTagDialog : : EditTagDialog ( Application * app , QWidget * parent )
: QDialog ( parent ) ,
ui_ ( new Ui_EditTagDialog ) ,
app_ ( app ) ,
album_cover_choice_controller_ ( new AlbumCoverChoiceController ( this ) ) ,
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2023-04-21 20:20:53 +02:00
tag_fetcher_ ( new TagFetcher ( app - > network ( ) , this ) ) ,
2021-02-26 21:03:51 +01:00
results_dialog_ ( new TrackSelectionDialog ( this ) ) ,
2018-02-27 18:06:05 +01:00
# endif
2024-03-06 23:16:29 +01:00
lyrics_fetcher_ ( new LyricsFetcher ( app - > lyrics_providers ( ) , this ) ) ,
2023-05-14 11:34:55 +02:00
image_no_cover_thumbnail_ ( ImageUtils : : GenerateNoCoverImage ( QSize ( 128 , 128 ) , devicePixelRatioF ( ) ) ) ,
2021-02-26 21:03:51 +01:00
loading_ ( false ) ,
ignore_edits_ ( false ) ,
summary_cover_art_id_ ( - 1 ) ,
tags_cover_art_id_ ( - 1 ) ,
2018-02-27 18:06:05 +01:00
cover_art_is_set_ ( false ) ,
2024-03-06 23:16:29 +01:00
save_tag_pending_ ( 0 ) ,
lyrics_id_ ( - 1 ) {
2018-02-27 18:06:05 +01:00
2023-07-21 05:55:24 +02:00
QObject : : connect ( & * app_ - > album_cover_loader ( ) , & AlbumCoverLoader : : AlbumCoverLoaded , this , & EditTagDialog : : AlbumCoverLoaded ) ;
2018-02-27 18:06:05 +01:00
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2021-01-26 16:48:04 +01:00
QObject : : connect ( tag_fetcher_ , & TagFetcher : : ResultAvailable , results_dialog_ , & TrackSelectionDialog : : FetchTagFinished , Qt : : QueuedConnection ) ;
QObject : : connect ( tag_fetcher_ , & TagFetcher : : Progress , results_dialog_ , & TrackSelectionDialog : : FetchTagProgress ) ;
QObject : : connect ( results_dialog_ , & TrackSelectionDialog : : SongChosen , this , & EditTagDialog : : FetchTagSongChosen ) ;
QObject : : connect ( results_dialog_ , & TrackSelectionDialog : : finished , tag_fetcher_ , & TagFetcher : : Cancel ) ;
2018-02-27 18:06:05 +01:00
# endif
2024-03-06 23:16:29 +01:00
QObject : : connect ( lyrics_fetcher_ , & LyricsFetcher : : LyricsFetched , this , & EditTagDialog : : UpdateLyrics ) ;
2018-02-27 18:06:05 +01:00
2019-07-07 21:14:24 +02:00
album_cover_choice_controller_ - > Init ( app_ ) ;
2018-02-27 18:06:05 +01:00
ui_ - > setupUi ( this ) ;
ui_ - > splitter - > setSizes ( QList < int > ( ) < < 200 < < width ( ) - 200 ) ;
ui_ - > loading_label - > hide ( ) ;
2021-02-26 21:03:51 +01:00
ui_ - > label_lyrics - > hide ( ) ;
2018-02-27 18:06:05 +01:00
2024-04-11 02:56:01 +02:00
ui_ - > fetch_tag - > setIcon ( QPixmap : : fromImage ( QImage ( QStringLiteral ( " :/pictures/musicbrainz.png " ) ) ) ) ;
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2018-07-16 07:23:37 +02:00
ui_ - > fetch_tag - > setEnabled ( true ) ;
# else
ui_ - > fetch_tag - > setEnabled ( false ) ;
# endif
2018-02-27 18:06:05 +01:00
2019-02-20 21:27:53 +01:00
// An editable field is one that has a label as a buddy.
// The label is important because it gets turned bold when the field is changed.
2018-02-27 18:06:05 +01:00
for ( QLabel * label : findChildren < QLabel * > ( ) ) {
QWidget * widget = label - > buddy ( ) ;
if ( widget ) {
// Store information about the field
2021-06-20 19:04:08 +02:00
fields_ < < FieldData ( label , widget , widget - > objectName ( ) ) ; // clazy:exclude=reserve-candidates
2018-02-27 18:06:05 +01:00
// Connect the edited signal
2021-01-26 16:48:04 +01:00
if ( LineEdit * lineedit = qobject_cast < LineEdit * > ( widget ) ) {
QObject : : connect ( lineedit , & LineEdit : : textChanged , this , & EditTagDialog : : FieldValueEdited ) ;
QObject : : connect ( lineedit , & LineEdit : : Reset , this , & EditTagDialog : : ResetField ) ;
2018-02-27 18:06:05 +01:00
}
2021-01-26 16:48:04 +01:00
else if ( TextEdit * textedit = qobject_cast < TextEdit * > ( widget ) ) {
QObject : : connect ( textedit , & TextEdit : : textChanged , this , & EditTagDialog : : FieldValueEdited ) ;
QObject : : connect ( textedit , & TextEdit : : Reset , this , & EditTagDialog : : ResetField ) ;
2018-02-27 18:06:05 +01:00
}
2021-01-26 16:48:04 +01:00
else if ( SpinBox * spinbox = qobject_cast < SpinBox * > ( widget ) ) {
QObject : : connect ( spinbox , QOverload < int > : : of ( & SpinBox : : valueChanged ) , this , & EditTagDialog : : FieldValueEdited ) ;
QObject : : connect ( spinbox , & SpinBox : : Reset , this , & EditTagDialog : : ResetField ) ;
2018-02-27 18:06:05 +01:00
}
2021-01-26 16:48:04 +01:00
else if ( CheckBox * checkbox = qobject_cast < CheckBox * > ( widget ) ) {
QObject : : connect ( checkbox , & QCheckBox : : stateChanged , this , & EditTagDialog : : FieldValueEdited ) ;
QObject : : connect ( checkbox , & CheckBox : : Reset , this , & EditTagDialog : : ResetField ) ;
2020-09-23 00:52:41 +02:00
}
2021-10-24 16:08:17 +02:00
else if ( RatingBox * ratingbox = qobject_cast < RatingBox * > ( widget ) ) {
QObject : : connect ( ratingbox , & RatingWidget : : RatingChanged , this , & EditTagDialog : : FieldValueEdited ) ;
}
2018-02-27 18:06:05 +01:00
}
}
// Set the colour of all the labels on the summary page
const bool light = palette ( ) . color ( QPalette : : Base ) . value ( ) > 128 ;
const QColor color = palette ( ) . color ( QPalette : : WindowText ) ;
QPalette summary_label_palette ( palette ( ) ) ;
summary_label_palette . setColor ( QPalette : : WindowText , light ? color . lighter ( 150 ) : color . darker ( 150 ) ) ;
2021-02-26 21:03:51 +01:00
for ( QLabel * label : ui_ - > tab_summary - > findChildren < QLabel * > ( ) ) {
2018-02-27 18:06:05 +01:00
if ( label - > property ( " field_label " ) . toBool ( ) ) {
label - > setPalette ( summary_label_palette ) ;
}
}
2021-01-26 16:48:04 +01:00
QObject : : connect ( ui_ - > song_list - > selectionModel ( ) , & QItemSelectionModel : : selectionChanged , this , & EditTagDialog : : SelectionChanged ) ;
QObject : : connect ( ui_ - > button_box , & QDialogButtonBox : : clicked , this , & EditTagDialog : : ButtonClicked ) ;
2023-03-18 20:03:07 +01:00
QObject : : connect ( ui_ - > playcount_reset , & QPushButton : : clicked , this , & EditTagDialog : : ResetPlayStatistics ) ;
2021-10-24 16:08:17 +02:00
QObject : : connect ( ui_ - > rating , & RatingWidget : : RatingChanged , this , & EditTagDialog : : SongRated ) ;
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2021-01-26 16:48:04 +01:00
QObject : : connect ( ui_ - > fetch_tag , & QPushButton : : clicked , this , & EditTagDialog : : FetchTag ) ;
2018-07-16 07:23:37 +02:00
# endif
2024-03-06 23:16:29 +01:00
QObject : : connect ( ui_ - > fetch_lyrics , & QPushButton : : clicked , this , & EditTagDialog : : FetchLyrics ) ;
2018-02-27 18:06:05 +01:00
// Set up the album cover menu
cover_menu_ = new QMenu ( this ) ;
QList < QAction * > actions = album_cover_choice_controller_ - > GetAllActions ( ) ;
2021-09-19 19:31:34 +02:00
QObject : : connect ( album_cover_choice_controller_ , & AlbumCoverChoiceController : : Error , this , & EditTagDialog : : Error ) ;
2021-01-26 16:48:04 +01:00
QObject : : connect ( album_cover_choice_controller_ - > cover_from_file_action ( ) , & QAction : : triggered , this , & EditTagDialog : : LoadCoverFromFile ) ;
QObject : : connect ( album_cover_choice_controller_ - > cover_to_file_action ( ) , & QAction : : triggered , this , & EditTagDialog : : SaveCoverToFile ) ;
QObject : : connect ( album_cover_choice_controller_ - > cover_from_url_action ( ) , & QAction : : triggered , this , & EditTagDialog : : LoadCoverFromURL ) ;
QObject : : connect ( album_cover_choice_controller_ - > search_for_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : SearchForCover ) ;
QObject : : connect ( album_cover_choice_controller_ - > unset_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : UnsetCover ) ;
2021-02-26 21:03:51 +01:00
QObject : : connect ( album_cover_choice_controller_ - > clear_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : ClearCover ) ;
QObject : : connect ( album_cover_choice_controller_ - > delete_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : DeleteCover ) ;
2021-01-26 16:48:04 +01:00
QObject : : connect ( album_cover_choice_controller_ - > show_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : ShowCover ) ;
2021-02-27 01:32:45 +01:00
QObject : : connect ( ui_ - > checkbox_embedded_cover , & QCheckBox : : toggled , album_cover_choice_controller_ , & AlbumCoverChoiceController : : set_save_embedded_cover_override ) ;
2018-02-27 18:06:05 +01:00
cover_menu_ - > addActions ( actions ) ;
2021-02-26 21:03:51 +01:00
ui_ - > tags_art_button - > setMenu ( cover_menu_ ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
ui_ - > tags_art - > installEventFilter ( this ) ;
ui_ - > tags_art - > setAcceptDrops ( true ) ;
2018-02-27 18:06:05 +01:00
2021-03-07 23:23:58 +01:00
ui_ - > summary_art - > installEventFilter ( this ) ;
2018-02-27 18:06:05 +01:00
// Add the next/previous buttons
2024-04-09 23:20:26 +02:00
previous_button_ = new QPushButton ( IconLoader : : Load ( QStringLiteral ( " go-previous " ) ) , tr ( " Previous " ) , this ) ;
next_button_ = new QPushButton ( IconLoader : : Load ( QStringLiteral ( " go-next " ) ) , tr ( " Next " ) , this ) ;
2018-02-27 18:06:05 +01:00
ui_ - > button_box - > addButton ( previous_button_ , QDialogButtonBox : : ResetRole ) ;
ui_ - > button_box - > addButton ( next_button_ , QDialogButtonBox : : ResetRole ) ;
2021-01-26 16:48:04 +01:00
QObject : : connect ( previous_button_ , & QPushButton : : clicked , this , & EditTagDialog : : PreviousSong ) ;
QObject : : connect ( next_button_ , & QPushButton : : clicked , this , & EditTagDialog : : NextSong ) ;
2018-02-27 18:06:05 +01:00
// Set some shortcuts for the buttons
new QShortcut ( QKeySequence : : Back , previous_button_ , SLOT ( click ( ) ) ) ;
new QShortcut ( QKeySequence : : Forward , next_button_ , SLOT ( click ( ) ) ) ;
new QShortcut ( QKeySequence : : MoveToPreviousPage , previous_button_ , SLOT ( click ( ) ) ) ;
new QShortcut ( QKeySequence : : MoveToNextPage , next_button_ , SLOT ( click ( ) ) ) ;
// Show the shortcuts as tooltips
2024-04-09 23:20:26 +02:00
previous_button_ - > setToolTip ( QStringLiteral ( " %1 (%2 / %3) " ) . arg (
2018-02-27 18:06:05 +01:00
previous_button_ - > text ( ) ,
QKeySequence ( QKeySequence : : Back ) . toString ( QKeySequence : : NativeText ) ,
QKeySequence ( QKeySequence : : MoveToPreviousPage ) . toString ( QKeySequence : : NativeText ) ) ) ;
2024-04-09 23:20:26 +02:00
next_button_ - > setToolTip ( QStringLiteral ( " %1 (%2 / %3) " ) . arg (
2018-02-27 18:06:05 +01:00
next_button_ - > text ( ) ,
QKeySequence ( QKeySequence : : Forward ) . toString ( QKeySequence : : NativeText ) ,
QKeySequence ( QKeySequence : : MoveToNextPage ) . toString ( QKeySequence : : NativeText ) ) ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Artist , ui_ - > artist ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Album , ui_ - > album ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_AlbumArtist , ui_ - > albumartist ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Genre , ui_ - > genre ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Composer , ui_ - > composer ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Performer , ui_ - > performer ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Grouping , ui_ - > grouping ) ;
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
}
EditTagDialog : : ~ EditTagDialog ( ) {
delete ui_ ;
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : showEvent ( QShowEvent * e ) {
if ( ! e - > spontaneous ( ) ) {
// Set the dialog's height to the smallest possible
resize ( width ( ) , sizeHint ( ) . height ( ) ) ;
// Restore the tab that was current last time.
2024-04-11 02:56:01 +02:00
Settings s ;
2021-02-26 21:03:51 +01:00
s . beginGroup ( kSettingsGroup ) ;
if ( s . contains ( " geometry " ) ) {
restoreGeometry ( s . value ( " geometry " ) . toByteArray ( ) ) ;
}
ui_ - > tab_widget - > setCurrentIndex ( s . value ( " current_tab " ) . toInt ( ) ) ;
s . endGroup ( ) ;
album_cover_choice_controller_ - > ReloadSettings ( ) ;
2023-05-14 11:34:55 +02:00
cover_types_ = AlbumCoverLoaderOptions : : LoadTypes ( ) ;
2021-02-26 21:03:51 +01:00
}
QDialog : : showEvent ( e ) ;
}
void EditTagDialog : : hideEvent ( QHideEvent * e ) {
// Save the current tab
2024-04-11 02:56:01 +02:00
Settings s ;
2021-02-26 21:03:51 +01:00
s . beginGroup ( kSettingsGroup ) ;
s . setValue ( " geometry " , saveGeometry ( ) ) ;
s . setValue ( " current_tab " , ui_ - > tab_widget - > currentIndex ( ) ) ;
s . endGroup ( ) ;
QDialog : : hideEvent ( e ) ;
}
void EditTagDialog : : accept ( ) {
// Show the loading indicator
2024-04-11 02:56:01 +02:00
if ( ! SetLoading ( tr ( " Saving tracks " ) + QStringLiteral ( " ... " ) ) ) return ;
2021-02-26 21:03:51 +01:00
SaveData ( ) ;
}
bool EditTagDialog : : eventFilter ( QObject * o , QEvent * e ) {
if ( o = = ui_ - > tags_art ) {
switch ( e - > type ( ) ) {
2021-03-07 23:23:58 +01:00
case QEvent : : MouseButtonRelease : {
QMouseEvent * mouse_event = static_cast < QMouseEvent * > ( e ) ;
if ( mouse_event & & mouse_event - > button ( ) = = Qt : : RightButton ) {
2021-02-26 21:03:51 +01:00
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
2021-03-07 23:23:58 +01:00
cover_menu_ - > popup ( mouse_event - > globalPosition ( ) . toPoint ( ) ) ;
2021-02-26 21:03:51 +01:00
# else
2021-03-07 23:26:08 +01:00
cover_menu_ - > popup ( mouse_event - > globalPos ( ) ) ;
2021-02-26 21:03:51 +01:00
# endif
2021-03-07 23:23:58 +01:00
}
break ;
}
case QEvent : : MouseButtonDblClick :
ShowCover ( ) ;
2021-02-26 21:03:51 +01:00
break ;
2023-05-14 11:34:55 +02:00
case QEvent : : DragEnter : {
2021-02-26 21:03:51 +01:00
QDragEnterEvent * event = static_cast < QDragEnterEvent * > ( e ) ;
if ( AlbumCoverChoiceController : : CanAcceptDrag ( event ) ) {
event - > acceptProposedAction ( ) ;
}
break ;
}
2023-05-14 11:34:55 +02:00
case QEvent : : Drop : {
2021-02-26 21:03:51 +01:00
const QDropEvent * event = static_cast < QDropEvent * > ( e ) ;
if ( event - > mimeData ( ) - > hasImage ( ) ) {
QImage image = qvariant_cast < QImage > ( event - > mimeData ( ) - > imageData ( ) ) ;
if ( ! image . isNull ( ) ) {
2023-04-09 22:26:17 +02:00
UpdateCover ( UpdateCoverAction : : New , AlbumCoverImageResult ( image ) ) ;
2021-02-26 21:03:51 +01:00
}
}
break ;
}
default :
break ;
}
}
2021-03-07 23:23:58 +01:00
if ( o = = ui_ - > summary_art ) {
switch ( e - > type ( ) ) {
case QEvent : : MouseButtonDblClick :
ShowCover ( ) ;
break ;
default :
break ;
}
}
2021-06-20 19:04:08 +02:00
return QDialog : : eventFilter ( o , e ) ;
2021-02-26 21:03:51 +01:00
}
2018-02-27 18:06:05 +01:00
bool EditTagDialog : : SetLoading ( const QString & message ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
const bool loading = ! message . isEmpty ( ) ;
if ( loading = = loading_ ) return false ;
loading_ = loading ;
ui_ - > button_box - > setEnabled ( ! loading ) ;
ui_ - > tab_widget - > setEnabled ( ! loading ) ;
ui_ - > song_list - > setEnabled ( ! loading ) ;
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2018-02-27 18:06:05 +01:00
ui_ - > fetch_tag - > setEnabled ( ! loading ) ;
2018-07-16 07:23:37 +02:00
# endif
2018-02-27 18:06:05 +01:00
ui_ - > loading_label - > setVisible ( loading ) ;
ui_ - > loading_label - > set_text ( message ) ;
2021-02-26 21:03:51 +01:00
2018-02-27 18:06:05 +01:00
return true ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
2021-06-22 13:41:38 +02:00
QList < EditTagDialog : : Data > EditTagDialog : : LoadData ( const SongList & songs ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
QList < Data > ret ;
for ( const Song & song : songs ) {
if ( song . IsEditable ( ) ) {
// Try reloading the tags from file
Song copy ( song ) ;
TagReaderClient : : Instance ( ) - > ReadFileBlocking ( copy . url ( ) . toLocalFile ( ) , & copy ) ;
if ( copy . is_valid ( ) ) {
2022-06-05 04:59:50 +02:00
copy . MergeUserSetData ( song , false , false ) ;
2018-02-27 18:06:05 +01:00
ret < < Data ( copy ) ;
}
}
}
return ret ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
2023-02-18 14:09:27 +01:00
void EditTagDialog : : SetSongs ( const SongList & s , const PlaylistItemPtrList & items ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
// Show the loading indicator
2024-04-11 02:56:01 +02:00
if ( ! SetLoading ( tr ( " Loading tracks " ) + QStringLiteral ( " ... " ) ) ) return ;
2018-02-27 18:06:05 +01:00
data_ . clear ( ) ;
playlist_items_ = items ;
ui_ - > song_list - > clear ( ) ;
2021-02-26 21:03:51 +01:00
collection_songs_ . clear ( ) ;
2018-02-27 18:06:05 +01:00
// Reload tags in the background
2021-06-22 13:41:38 +02:00
QFuture < QList < Data > > future = QtConcurrent : : run ( & EditTagDialog : : LoadData , s ) ;
2021-01-30 21:53:53 +01:00
QFutureWatcher < QList < Data > > * watcher = new QFutureWatcher < QList < Data > > ( ) ;
QObject : : connect ( watcher , & QFutureWatcher < QList < Data > > : : finished , this , & EditTagDialog : : SetSongsFinished ) ;
2021-06-16 00:30:21 +02:00
watcher - > setFuture ( future ) ;
2019-02-20 21:27:53 +01:00
2018-02-27 18:06:05 +01:00
}
2021-01-30 21:53:53 +01:00
void EditTagDialog : : SetSongsFinished ( ) {
2018-02-27 18:06:05 +01:00
2021-01-30 21:53:53 +01:00
QFutureWatcher < QList < Data > > * watcher = static_cast < QFutureWatcher < QList < Data > > * > ( sender ( ) ) ;
QList < Data > result_data = watcher - > result ( ) ;
watcher - > deleteLater ( ) ;
2021-02-26 21:03:51 +01:00
if ( ! SetLoading ( QString ( ) ) ) return ;
2021-01-30 21:53:53 +01:00
data_ = result_data ;
2018-02-27 18:06:05 +01:00
if ( data_ . count ( ) = = 0 ) {
// If there were no valid songs, disable everything
ui_ - > song_list - > setEnabled ( false ) ;
ui_ - > tab_widget - > setEnabled ( false ) ;
// Show a summary with empty information
2023-05-14 11:34:55 +02:00
UpdateSummaryTab ( Song ( ) ) ;
2021-02-26 21:03:51 +01:00
ui_ - > tab_widget - > setCurrentWidget ( ui_ - > tab_summary ) ;
2018-02-27 18:06:05 +01:00
SetSongListVisibility ( false ) ;
return ;
}
// Add the filenames to the list
2020-04-23 21:08:28 +02:00
for ( const Data & tag_data : data_ ) {
ui_ - > song_list - > addItem ( tag_data . current_ . basefilename ( ) ) ;
2018-02-27 18:06:05 +01:00
}
// Select all
ui_ - > song_list - > setCurrentRow ( 0 ) ;
ui_ - > song_list - > selectAll ( ) ;
// Hide the list if there's only one song in it
SetSongListVisibility ( data_ . count ( ) ! = 1 ) ;
2019-02-20 21:27:53 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : SetSongListVisibility ( bool visible ) {
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
ui_ - > song_list - > setVisible ( visible ) ;
previous_button_ - > setEnabled ( visible ) ;
next_button_ - > setEnabled ( visible ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
QVariant EditTagDialog : : Data : : value ( const Song & song , const QString & id ) {
2018-10-02 00:38:52 +02:00
2024-04-11 02:56:01 +02:00
if ( id = = QStringLiteral ( " title " ) ) return song . title ( ) ;
if ( id = = QStringLiteral ( " artist " ) ) return song . artist ( ) ;
if ( id = = QStringLiteral ( " album " ) ) return song . album ( ) ;
if ( id = = QStringLiteral ( " albumartist " ) ) return song . albumartist ( ) ;
if ( id = = QStringLiteral ( " composer " ) ) return song . composer ( ) ;
if ( id = = QStringLiteral ( " performer " ) ) return song . performer ( ) ;
if ( id = = QStringLiteral ( " grouping " ) ) return song . grouping ( ) ;
if ( id = = QStringLiteral ( " genre " ) ) return song . genre ( ) ;
if ( id = = QStringLiteral ( " comment " ) ) return song . comment ( ) ;
if ( id = = QStringLiteral ( " lyrics " ) ) return song . lyrics ( ) ;
if ( id = = QStringLiteral ( " track " ) ) return song . track ( ) ;
if ( id = = QStringLiteral ( " disc " ) ) return song . disc ( ) ;
if ( id = = QStringLiteral ( " year " ) ) return song . year ( ) ;
if ( id = = QStringLiteral ( " compilation " ) ) return song . compilation ( ) ;
if ( id = = QStringLiteral ( " rating " ) ) { return song . rating ( ) ; }
2018-02-27 18:06:05 +01:00
qLog ( Warning ) < < " Unknown ID " < < id ;
return QVariant ( ) ;
}
void EditTagDialog : : Data : : set_value ( const QString & id , const QVariant & value ) {
2024-04-11 02:56:01 +02:00
if ( id = = QStringLiteral ( " title " ) ) current_ . set_title ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " artist " ) ) current_ . set_artist ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " album " ) ) current_ . set_album ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " albumartist " ) ) current_ . set_albumartist ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " composer " ) ) current_ . set_composer ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " performer " ) ) current_ . set_performer ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " grouping " ) ) current_ . set_grouping ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " genre " ) ) current_ . set_genre ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " comment " ) ) current_ . set_comment ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " lyrics " ) ) current_ . set_lyrics ( value . toString ( ) ) ;
else if ( id = = QStringLiteral ( " track " ) ) current_ . set_track ( value . toInt ( ) ) ;
else if ( id = = QStringLiteral ( " disc " ) ) current_ . set_disc ( value . toInt ( ) ) ;
else if ( id = = QStringLiteral ( " year " ) ) current_ . set_year ( value . toInt ( ) ) ;
else if ( id = = QStringLiteral ( " compilation " ) ) current_ . set_compilation ( value . toBool ( ) ) ;
else if ( id = = QStringLiteral ( " rating " ) ) { current_ . set_rating ( value . toFloat ( ) ) ; }
2018-02-27 18:06:05 +01:00
else qLog ( Warning ) < < " Unknown ID " < < id ;
}
bool EditTagDialog : : DoesValueVary ( const QModelIndexList & sel , const QString & id ) const {
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
QVariant value = data_ [ sel . first ( ) . row ( ) ] . current_value ( id ) ;
for ( int i = 1 ; i < sel . count ( ) ; + + i ) {
if ( value ! = data_ [ sel [ i ] . row ( ) ] . current_value ( id ) ) return true ;
}
return false ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
bool EditTagDialog : : IsValueModified ( const QModelIndexList & sel , const QString & id ) const {
2021-07-11 09:49:38 +02:00
return std : : any_of ( sel . begin ( ) , sel . end ( ) , [ this , id ] ( const QModelIndex & i ) { return data_ [ i . row ( ) ] . original_value ( id ) ! = data_ [ i . row ( ) ] . current_value ( id ) ; } ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : InitFieldValue ( const FieldData & field , const QModelIndexList & sel ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
const bool varies = DoesValueVary ( sel , field . id_ ) ;
if ( ExtendedEditor * editor = dynamic_cast < ExtendedEditor * > ( field . editor_ ) ) {
editor - > clear ( ) ;
editor - > clear_hint ( ) ;
if ( varies ) {
2021-02-26 21:03:51 +01:00
editor - > set_hint ( tr ( kTagsDifferentHintText ) ) ;
2020-09-23 00:52:41 +02:00
editor - > set_partially ( ) ;
2018-02-27 18:06:05 +01:00
}
else {
2020-09-23 00:52:41 +02:00
editor - > set_value ( data_ [ sel [ 0 ] . row ( ) ] . current_value ( field . id_ ) ) ;
2018-02-27 18:06:05 +01:00
}
}
2021-03-21 04:47:11 +01:00
else if ( field . editor_ ) {
2020-09-23 00:52:41 +02:00
qLog ( Error ) < < " Missing editor for " < < field . editor_ - > objectName ( ) ;
}
2018-02-27 18:06:05 +01:00
UpdateModifiedField ( field , sel ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : UpdateFieldValue ( const FieldData & field , const QModelIndexList & sel ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
// Get the value from the field
QVariant value ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
if ( ExtendedEditor * editor = dynamic_cast < ExtendedEditor * > ( field . editor_ ) ) {
2021-06-25 18:19:37 +02:00
value = editor - > value ( ) ;
2020-09-23 00:52:41 +02:00
}
2021-03-21 04:47:11 +01:00
else if ( field . editor_ ) {
2020-09-23 00:52:41 +02:00
qLog ( Error ) < < " Missing editor for " < < field . editor_ - > objectName ( ) ;
2018-02-27 18:06:05 +01:00
}
// Did we get it?
if ( ! value . isValid ( ) ) {
return ;
}
// Set it in each selected song
for ( const QModelIndex & i : sel ) {
data_ [ i . row ( ) ] . set_value ( field . id_ , value ) ;
}
UpdateModifiedField ( field , sel ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : UpdateModifiedField ( const FieldData & field , const QModelIndexList & sel ) {
const bool modified = IsValueModified ( sel , field . id_ ) ;
// Update the boldness
QFont new_font ( font ( ) ) ;
new_font . setBold ( modified ) ;
field . label_ - > setFont ( new_font ) ;
2021-03-21 04:47:11 +01:00
if ( field . editor_ ) field . editor_ - > setFont ( new_font ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : ResetFieldValue ( const FieldData & field , const QModelIndexList & sel ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
// Reset each selected song
for ( const QModelIndex & i : sel ) {
2020-04-23 21:08:28 +02:00
Data & tag_data = data_ [ i . row ( ) ] ;
tag_data . set_value ( field . id_ , tag_data . original_value ( field . id_ ) ) ;
2018-02-27 18:06:05 +01:00
}
// Reset the field
InitFieldValue ( field , sel ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : SelectionChanged ( ) {
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
const QModelIndexList indexes = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
if ( indexes . isEmpty ( ) ) return ;
2018-02-27 18:06:05 +01:00
// Set the editable fields
2021-02-26 21:03:51 +01:00
UpdateUI ( indexes ) ;
2018-02-27 18:06:05 +01:00
2024-03-06 23:16:29 +01:00
lyrics_id_ = - 1 ;
2018-02-27 18:06:05 +01:00
// If we're editing multiple songs then we have to disable certain tabs
2021-02-26 21:03:51 +01:00
const bool multiple = indexes . count ( ) > 1 ;
ui_ - > tab_widget - > setTabEnabled ( ui_ - > tab_widget - > indexOf ( ui_ - > tab_summary ) , ! multiple ) ;
ui_ - > tab_widget - > setTabEnabled ( ui_ - > tab_widget - > indexOf ( ui_ - > tab_lyrics ) , ! multiple ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
if ( multiple ) {
2023-05-14 11:34:55 +02:00
UpdateSummaryTab ( Song ( ) ) ;
2021-02-26 21:03:51 +01:00
UpdateStatisticsTab ( Song ( ) ) ;
}
else {
2023-05-14 11:34:55 +02:00
UpdateSummaryTab ( data_ [ indexes . first ( ) . row ( ) ] . original_ ) ;
2021-02-26 21:03:51 +01:00
UpdateStatisticsTab ( data_ [ indexes . first ( ) . row ( ) ] . original_ ) ;
}
const Song & first_song = data_ [ indexes . first ( ) . row ( ) ] . original_ ;
UpdateCoverAction first_cover_action = data_ [ indexes . first ( ) . row ( ) ] . cover_action_ ;
bool art_different = false ;
bool action_different = false ;
2022-11-23 22:06:45 +01:00
bool albumartist_enabled = false ;
bool composer_enabled = false ;
bool performer_enabled = false ;
bool grouping_enabled = false ;
bool genre_enabled = false ;
bool compilation_enabled = false ;
bool rating_enabled = false ;
bool comment_enabled = false ;
bool lyrics_enabled = false ;
2021-02-26 21:03:51 +01:00
for ( const QModelIndex & idx : indexes ) {
2023-02-18 14:09:27 +01:00
if ( data_ [ idx . row ( ) ] . cover_action_ = = UpdateCoverAction : : None ) {
2023-04-09 22:26:17 +02:00
data_ [ idx . row ( ) ] . cover_result_ = AlbumCoverImageResult ( ) ;
2021-02-26 21:03:51 +01:00
}
const Song & song = data_ [ idx . row ( ) ] . original_ ;
2023-04-09 22:26:17 +02:00
if ( data_ [ idx . row ( ) ] . cover_action_ ! = first_cover_action | | ( first_cover_action ! = UpdateCoverAction : : None & & data_ [ idx . row ( ) ] . cover_result_ . image_data ! = data_ [ indexes . first ( ) . row ( ) ] . cover_result_ . image_data ) ) {
2021-02-27 00:58:43 +01:00
action_different = true ;
}
2021-02-26 21:03:51 +01:00
if ( data_ [ idx . row ( ) ] . cover_action_ ! = first_cover_action | |
song . art_manual ( ) ! = first_song . art_manual ( ) | |
2023-05-14 11:34:55 +02:00
song . art_embedded ( ) ! = first_song . art_embedded ( ) | |
song . art_automatic ( ) ! = first_song . art_automatic ( ) | |
( song . art_embedded ( ) & & first_song . art_embedded ( ) & & ( first_song . effective_albumartist ( ) ! = song . effective_albumartist ( ) | | first_song . album ( ) ! = song . album ( ) ) )
2021-02-26 21:03:51 +01:00
) {
art_different = true ;
2022-11-23 22:06:45 +01:00
}
if ( song . albumartist_supported ( ) ) {
albumartist_enabled = true ;
}
if ( song . composer_supported ( ) ) {
composer_enabled = true ;
}
if ( song . performer_supported ( ) ) {
performer_enabled = true ;
}
if ( song . grouping_supported ( ) ) {
grouping_enabled = true ;
}
if ( song . genre_supported ( ) ) {
genre_enabled = true ;
}
if ( song . compilation_supported ( ) ) {
compilation_enabled = true ;
}
if ( song . rating_supported ( ) ) {
rating_enabled = true ;
}
if ( song . comment_supported ( ) ) {
comment_enabled = true ;
}
if ( song . lyrics_supported ( ) ) {
lyrics_enabled = true ;
2021-02-26 21:03:51 +01:00
}
2018-02-27 18:06:05 +01:00
}
2018-03-10 13:02:56 +01:00
2021-02-26 21:03:51 +01:00
QString summary ;
if ( indexes . count ( ) = = 1 ) {
2024-04-11 02:56:01 +02:00
summary + = QStringLiteral ( " <p><b> " ) + first_song . PrettyTitleWithArtist ( ) . toHtmlEscaped ( ) + QStringLiteral ( " </b></p> " ) ;
2021-02-26 21:03:51 +01:00
}
else {
2024-04-09 23:20:26 +02:00
summary + = QLatin1String ( " <p><b> " ) ;
2021-02-26 21:03:51 +01:00
summary + = tr ( " %1 songs selected. " ) . arg ( indexes . count ( ) ) ;
2024-04-09 23:20:26 +02:00
summary + = QLatin1String ( " </b></p> " ) ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
ui_ - > tags_summary - > setText ( summary ) ;
2021-02-26 21:03:51 +01:00
2021-03-07 01:49:38 +01:00
const bool enable_change_art = first_song . is_collection_song ( ) ;
ui_ - > tags_art_button - > setEnabled ( enable_change_art ) ;
2023-02-18 14:09:27 +01:00
if ( ( art_different & & first_cover_action ! = UpdateCoverAction : : New ) | | action_different ) {
2021-07-11 09:49:38 +02:00
tags_cover_art_id_ = - 1 ; // Cancels any pending art load.
2021-02-26 21:03:51 +01:00
ui_ - > tags_art - > clear ( ) ;
2024-04-11 02:56:01 +02:00
ui_ - > tags_art - > setText ( QLatin1String ( kArtDifferentHintText ) ) ;
2021-02-26 21:03:51 +01:00
album_cover_choice_controller_ - > show_cover_action ( ) - > setEnabled ( false ) ;
album_cover_choice_controller_ - > cover_to_file_action ( ) - > setEnabled ( false ) ;
2021-03-07 01:49:38 +01:00
album_cover_choice_controller_ - > cover_from_file_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > cover_from_url_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > search_for_cover_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > unset_cover_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > clear_cover_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > delete_cover_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > search_for_cover_action ( ) - > setEnabled ( enable_change_art ) ;
2021-02-26 21:03:51 +01:00
}
else {
ui_ - > tags_art - > clear ( ) ;
2023-05-14 11:34:55 +02:00
album_cover_choice_controller_ - > show_cover_action ( ) - > setEnabled ( first_song . has_valid_art ( ) & & ! first_song . art_unset ( ) ) ;
album_cover_choice_controller_ - > cover_to_file_action ( ) - > setEnabled ( first_song . has_valid_art ( ) & & ! first_song . art_unset ( ) ) ;
2021-03-07 01:49:38 +01:00
album_cover_choice_controller_ - > cover_from_file_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > cover_from_url_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > search_for_cover_action ( ) - > setEnabled ( app_ - > cover_providers ( ) - > HasAnyProviders ( ) & & enable_change_art ) ;
2023-05-14 11:34:55 +02:00
album_cover_choice_controller_ - > unset_cover_action ( ) - > setEnabled ( enable_change_art & & ! first_song . art_unset ( ) ) ;
2023-06-15 20:34:03 +02:00
album_cover_choice_controller_ - > clear_cover_action ( ) - > setEnabled ( enable_change_art & & ( ! first_song . art_manual ( ) . isEmpty ( ) | | first_song . art_unset ( ) ) ) ;
2023-05-14 11:34:55 +02:00
album_cover_choice_controller_ - > delete_cover_action ( ) - > setEnabled ( enable_change_art & & ( first_song . art_embedded ( ) | | ! first_song . art_automatic ( ) . isEmpty ( ) | | ! first_song . art_manual ( ) . isEmpty ( ) ) ) ;
AlbumCoverLoaderOptions cover_options ( AlbumCoverLoaderOptions : : Option : : RawImageData | AlbumCoverLoaderOptions : : Option : : OriginalImage | AlbumCoverLoaderOptions : : Option : : ScaledImage | AlbumCoverLoaderOptions : : Option : : PadScaledImage ) ;
cover_options . types = cover_types_ ;
cover_options . desired_scaled_size = QSize ( kSmallImageSize , kSmallImageSize ) ;
cover_options . device_pixel_ratio = devicePixelRatioF ( ) ;
2023-02-18 14:09:27 +01:00
if ( data_ [ indexes . first ( ) . row ( ) ] . cover_action_ = = UpdateCoverAction : : None ) {
2023-05-14 11:34:55 +02:00
tags_cover_art_id_ = app_ - > album_cover_loader ( ) - > LoadImageAsync ( cover_options , first_song ) ;
2021-02-26 21:03:51 +01:00
}
else {
2023-05-14 11:34:55 +02:00
tags_cover_art_id_ = app_ - > album_cover_loader ( ) - > LoadImageAsync ( cover_options , data_ [ indexes . first ( ) . row ( ) ] . cover_result_ ) ;
2021-02-26 21:03:51 +01:00
}
}
2023-05-14 11:34:55 +02:00
const bool embedded_cover = ( first_song . save_embedded_cover_supported ( ) & & ( first_song . art_embedded ( ) | | album_cover_choice_controller_ - > get_collection_save_album_cover_type ( ) = = CoverOptions : : CoverType : : Embedded ) ) ;
2021-02-26 21:03:51 +01:00
ui_ - > checkbox_embedded_cover - > setChecked ( embedded_cover ) ;
album_cover_choice_controller_ - > set_save_embedded_cover_override ( embedded_cover ) ;
2022-11-23 22:06:45 +01:00
ui_ - > albumartist - > setEnabled ( albumartist_enabled ) ;
ui_ - > composer - > setEnabled ( composer_enabled ) ;
ui_ - > performer - > setEnabled ( performer_enabled ) ;
ui_ - > grouping - > setEnabled ( grouping_enabled ) ;
ui_ - > genre - > setEnabled ( genre_enabled ) ;
ui_ - > compilation - > setEnabled ( compilation_enabled ) ;
ui_ - > rating - > setEnabled ( rating_enabled ) ;
ui_ - > comment - > setEnabled ( comment_enabled ) ;
ui_ - > lyrics - > setEnabled ( lyrics_enabled ) ;
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : UpdateUI ( const QModelIndexList & indexes ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
ignore_edits_ = true ;
for ( const FieldData & field : fields_ ) {
2021-02-26 21:03:51 +01:00
InitFieldValue ( field , indexes ) ;
2018-02-27 18:06:05 +01:00
}
ignore_edits_ = false ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : SetText ( QLabel * label , const int value , const QString & suffix , const QString & def ) {
2024-04-11 02:56:01 +02:00
label - > setText ( value < = 0 ? def : ( QString : : number ( value ) + QLatin1Char ( ' ' ) + suffix ) ) ;
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : SetDate ( QLabel * label , const uint time ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( time = = std : : numeric_limits < uint > : : max ( ) ) { // -1
label - > setText ( QObject : : tr ( " Unknown " ) ) ;
}
else {
2020-07-18 04:09:36 +02:00
label - > setText ( QDateTime : : fromSecsSinceEpoch ( time ) . toString ( QLocale : : system ( ) . dateTimeFormat ( QLocale : : LongFormat ) ) ) ;
2018-02-27 18:06:05 +01:00
}
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
2023-05-14 11:34:55 +02:00
void EditTagDialog : : UpdateSummaryTab ( const Song & song ) {
2018-02-27 18:06:05 +01:00
2023-05-14 11:34:55 +02:00
AlbumCoverLoaderOptions cover_options ( AlbumCoverLoaderOptions : : Option : : ScaledImage | AlbumCoverLoaderOptions : : Option : : PadScaledImage ) ;
cover_options . types = cover_types_ ;
cover_options . desired_scaled_size = QSize ( kSmallImageSize , kSmallImageSize ) ;
cover_options . device_pixel_ratio = devicePixelRatioF ( ) ;
summary_cover_art_id_ = app_ - > album_cover_loader ( ) - > LoadImageAsync ( cover_options , song ) ;
2018-02-27 18:06:05 +01:00
2024-04-11 02:56:01 +02:00
ui_ - > summary - > setText ( QStringLiteral ( " <p><b> " ) + song . PrettyTitleWithArtist ( ) . toHtmlEscaped ( ) + QStringLiteral ( " </b></p> " ) ) ;
2018-02-27 18:06:05 +01:00
ui_ - > length - > setText ( Utilities : : PrettyTimeNanosec ( song . length_nanosec ( ) ) ) ;
2024-04-09 23:20:26 +02:00
SetText ( ui_ - > samplerate , song . samplerate ( ) , QStringLiteral ( " Hz " ) ) ;
SetText ( ui_ - > bitdepth , song . bitdepth ( ) , QStringLiteral ( " Bit " ) ) ;
2018-02-27 18:06:05 +01:00
SetText ( ui_ - > bitrate , song . bitrate ( ) , tr ( " kbps " ) ) ;
2023-06-27 03:56:35 +02:00
ui_ - > ebur128_integrated_loudness - > setText ( song . Ebur128LoudnessLUFSToText ( ) ) ;
ui_ - > ebur128_loudness_range - > setText ( song . Ebur128LoudnessRangeLUToText ( ) ) ;
2018-02-27 18:06:05 +01:00
SetDate ( ui_ - > mtime , song . mtime ( ) ) ;
SetDate ( ui_ - > ctime , song . ctime ( ) ) ;
if ( song . filesize ( ) = = - 1 ) {
ui_ - > filesize - > setText ( tr ( " Unknown " ) ) ;
}
else {
ui_ - > filesize - > setText ( Utilities : : PrettySize ( song . filesize ( ) ) ) ;
}
ui_ - > filetype - > setText ( song . TextForFiletype ( ) ) ;
2021-02-26 21:03:51 +01:00
if ( song . url ( ) . isLocalFile ( ) ) {
ui_ - > filename - > setText ( song . url ( ) . fileName ( ) ) ;
ui_ - > path - > setText ( QFileInfo ( QDir : : toNativeSeparators ( song . url ( ) . toLocalFile ( ) ) ) . path ( ) ) ;
}
else {
2018-02-27 18:06:05 +01:00
ui_ - > filename - > setText ( song . url ( ) . toString ( ) ) ;
2021-02-26 21:03:51 +01:00
ui_ - > path - > clear ( ) ;
}
2018-02-27 18:06:05 +01:00
2023-05-14 11:34:55 +02:00
ui_ - > art_embedded - > setText ( song . art_embedded ( ) ? tr ( " Yes " ) : tr ( " No " ) ) ;
2021-02-26 21:03:51 +01:00
if ( song . art_manual ( ) . isEmpty ( ) ) {
ui_ - > art_manual - > setText ( tr ( " None " ) ) ;
}
else {
ui_ - > art_manual - > setText ( song . art_manual ( ) . toString ( ) ) ;
}
if ( song . art_automatic ( ) . isEmpty ( ) ) {
ui_ - > art_automatic - > setText ( tr ( " None " ) ) ;
}
else {
ui_ - > art_automatic - > setText ( song . art_automatic ( ) . toString ( ) ) ;
}
2023-05-14 11:34:55 +02:00
ui_ - > art_unset - > setText ( song . art_unset ( ) ? tr ( " Yes " ) : tr ( " No " ) ) ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
QString EditTagDialog : : GetArtSummary ( const Song & song , const AlbumCoverLoaderResult : : Type cover_type ) {
2021-02-26 21:03:51 +01:00
QString summary ;
2023-05-14 11:34:55 +02:00
switch ( cover_type ) {
case AlbumCoverLoaderResult : : Type : : None :
break ;
case AlbumCoverLoaderResult : : Type : : Unset :
summary = tr ( " Cover is unset. " ) . toHtmlEscaped ( ) ;
break ;
case AlbumCoverLoaderResult : : Type : : Embedded :
summary = tr ( " Cover from embedded image. " ) ;
break ;
case AlbumCoverLoaderResult : : Type : : Manual :
summary = tr ( " Cover from %1 " ) . arg ( song . art_manual ( ) . toString ( ) ) . toHtmlEscaped ( ) ;
break ;
case AlbumCoverLoaderResult : : Type : : Automatic :
summary = tr ( " Cover from %1 " ) . arg ( song . art_automatic ( ) . toString ( ) ) . toHtmlEscaped ( ) ;
break ;
}
if ( summary . isEmpty ( ) ) {
2021-02-26 21:03:51 +01:00
summary = tr ( " Cover art not set " ) . toHtmlEscaped ( ) ;
}
2021-03-07 01:49:38 +01:00
if ( ! song . is_collection_song ( ) ) {
2024-04-09 23:20:26 +02:00
if ( ! summary . isEmpty ( ) ) summary + = QLatin1String ( " <br /> " ) ;
2021-03-07 01:49:38 +01:00
summary = tr ( " Album cover editing is only available for collection songs. " ) ;
}
2021-02-26 21:03:51 +01:00
return summary ;
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
}
2023-05-14 11:34:55 +02:00
QString EditTagDialog : : GetArtSummary ( const UpdateCoverAction cover_action ) {
switch ( cover_action ) {
case UpdateCoverAction : : Clear :
return tr ( " Cover changed: Will be cleared when saved. " ) . toHtmlEscaped ( ) ;
case UpdateCoverAction : : Unset :
return tr ( " Cover changed: Will be unset when saved. " ) . toHtmlEscaped ( ) ;
case UpdateCoverAction : : Delete :
return tr ( " Cover changed: Will be deleted when saved. " ) . toHtmlEscaped ( ) ;
case UpdateCoverAction : : New :
return tr ( " Cover changed: Will set new when saved. " ) . toHtmlEscaped ( ) ;
case UpdateCoverAction : : None :
break ;
}
return QString ( ) ;
}
2018-02-27 18:06:05 +01:00
void EditTagDialog : : UpdateStatisticsTab ( const Song & song ) {
2018-10-02 00:38:52 +02:00
2021-11-09 19:34:20 +01:00
ui_ - > playcount - > setText ( QString : : number ( song . playcount ( ) ) ) ;
ui_ - > skipcount - > setText ( QString : : number ( song . skipcount ( ) ) ) ;
2020-07-18 04:09:36 +02:00
ui_ - > lastplayed - > setText ( song . lastplayed ( ) < = 0 ? tr ( " Never " ) : QDateTime : : fromSecsSinceEpoch ( song . lastplayed ( ) ) . toString ( QLocale : : system ( ) . dateTimeFormat ( QLocale : : LongFormat ) ) ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
2023-04-09 22:26:17 +02:00
void EditTagDialog : : AlbumCoverLoaded ( const quint64 id , const AlbumCoverLoaderResult & result ) {
2019-09-15 20:27:32 +02:00
2023-05-14 11:34:55 +02:00
if ( id = = summary_cover_art_id_ ) {
if ( result . success & & ! result . image_scaled . isNull ( ) & & result . type ! = AlbumCoverLoaderResult : : Type : : Unset ) {
ui_ - > summary_art - > setPixmap ( QPixmap : : fromImage ( result . image_scaled ) ) ;
2021-02-26 21:03:51 +01:00
}
else {
2023-05-14 11:34:55 +02:00
ui_ - > summary_art - > setPixmap ( QPixmap : : fromImage ( image_no_cover_thumbnail_ ) ) ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
if ( ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . count ( ) > 0 ) {
const QModelIndex idx = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . first ( ) ;
QString summary = ui_ - > summary - > toPlainText ( ) ;
2024-04-09 23:20:26 +02:00
summary + = QLatin1String ( " <br /> " ) ;
summary + = QLatin1String ( " <br /> " ) ;
2023-05-14 11:34:55 +02:00
summary + = GetArtSummary ( data_ [ idx . row ( ) ] . current_ , result . type ) ;
ui_ - > summary - > setText ( summary ) ;
}
summary_cover_art_id_ = - 1 ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
else if ( id = = tags_cover_art_id_ ) {
if ( result . success & & ! result . image_scaled . isNull ( ) & & result . type ! = AlbumCoverLoaderResult : : Type : : Unset ) {
ui_ - > tags_art - > setPixmap ( QPixmap : : fromImage ( result . image_scaled ) ) ;
2021-02-26 21:03:51 +01:00
}
else {
2023-05-14 11:34:55 +02:00
ui_ - > tags_art - > setPixmap ( QPixmap : : fromImage ( image_no_cover_thumbnail_ ) ) ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
Song first_song ;
2023-06-06 23:17:29 +02:00
UpdateCoverAction cover_action = UpdateCoverAction : : None ;
2023-05-14 11:34:55 +02:00
for ( const QModelIndex & idx : ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ) {
data_ [ idx . row ( ) ] . cover_result_ = result . album_cover ;
if ( ! first_song . is_valid ( ) ) {
first_song = data_ [ idx . row ( ) ] . current_ ;
cover_action = data_ [ idx . row ( ) ] . cover_action_ ;
}
}
bool enable_change_art = false ;
if ( first_song . is_valid ( ) ) {
QString summary = ui_ - > tags_summary - > toPlainText ( ) ;
2024-04-09 23:20:26 +02:00
summary + = QLatin1String ( " <br /> " ) ;
summary + = QLatin1String ( " <br /> " ) ;
2023-05-14 11:34:55 +02:00
if ( cover_action = = UpdateCoverAction : : None ) {
summary + = GetArtSummary ( first_song , result . type ) ;
}
else {
summary + = GetArtSummary ( cover_action ) ;
}
ui_ - > tags_summary - > setText ( summary ) ;
enable_change_art = first_song . is_collection_song ( ) & & ! first_song . effective_albumartist ( ) . isEmpty ( ) & & ! first_song . album ( ) . isEmpty ( ) ;
}
tags_cover_art_id_ = - 1 ;
album_cover_choice_controller_ - > show_cover_action ( ) - > setEnabled ( result . success & & result . type ! = AlbumCoverLoaderResult : : Type : : Unset ) ;
album_cover_choice_controller_ - > cover_to_file_action ( ) - > setEnabled ( result . success & & result . type ! = AlbumCoverLoaderResult : : Type : : Unset ) ;
album_cover_choice_controller_ - > delete_cover_action ( ) - > setEnabled ( enable_change_art & & result . success & & result . type ! = AlbumCoverLoaderResult : : Type : : Unset ) ;
2018-02-27 18:06:05 +01:00
}
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : FieldValueEdited ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( ignore_edits_ ) return ;
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
2021-08-23 21:21:08 +02:00
if ( sel . isEmpty ( ) ) {
2018-02-27 18:06:05 +01:00
return ;
2021-08-23 21:21:08 +02:00
}
2018-02-27 18:06:05 +01:00
QWidget * w = qobject_cast < QWidget * > ( sender ( ) ) ;
// Find the field
for ( const FieldData & field : fields_ ) {
if ( field . editor_ = = w ) {
UpdateFieldValue ( field , sel ) ;
return ;
}
}
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : ResetField ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
2021-08-23 21:21:08 +02:00
if ( sel . isEmpty ( ) ) {
2018-02-27 18:06:05 +01:00
return ;
2021-08-23 21:21:08 +02:00
}
2018-02-27 18:06:05 +01:00
QWidget * w = qobject_cast < QWidget * > ( sender ( ) ) ;
// Find the field
for ( const FieldData & field : fields_ ) {
if ( field . editor_ = = w ) {
ignore_edits_ = true ;
ResetFieldValue ( field , sel ) ;
ignore_edits_ = false ;
return ;
}
}
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
Song * EditTagDialog : : GetFirstSelected ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
if ( sel . isEmpty ( ) ) return nullptr ;
2021-02-26 21:03:51 +01:00
return & data_ [ sel . first ( ) . row ( ) ] . current_ ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : LoadCoverFromFile ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
Song * song = GetFirstSelected ( ) ;
if ( ! song ) return ;
2023-04-09 22:26:17 +02:00
const AlbumCoverImageResult result = album_cover_choice_controller_ - > LoadImageFromFile ( song ) ;
if ( result . is_valid ( ) ) UpdateCover ( UpdateCoverAction : : New , result ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : SaveCoverToFile ( ) {
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
if ( ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . isEmpty ( ) ) return ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
const Data & first_data = data_ [ ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . first ( ) . row ( ) ] ;
album_cover_choice_controller_ - > SaveCoverToFileManual ( first_data . current_ , first_data . cover_result_ ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : LoadCoverFromURL ( ) {
2021-02-26 21:03:51 +01:00
if ( ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . isEmpty ( ) ) return ;
2018-02-27 18:06:05 +01:00
2023-04-09 22:26:17 +02:00
const AlbumCoverImageResult result = album_cover_choice_controller_ - > LoadImageFromURL ( ) ;
if ( result . is_valid ( ) ) UpdateCover ( UpdateCoverAction : : New , result ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : SearchForCover ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
Song * song = GetFirstSelected ( ) ;
if ( ! song ) return ;
2023-04-09 22:26:17 +02:00
const AlbumCoverImageResult result = album_cover_choice_controller_ - > SearchForImage ( song ) ;
if ( result . is_valid ( ) ) UpdateCover ( UpdateCoverAction : : New , result ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : UnsetCover ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
Song * song = GetFirstSelected ( ) ;
if ( ! song ) return ;
2023-05-14 11:34:55 +02:00
song - > set_art_embedded ( false ) ;
song - > clear_art_automatic ( ) ;
song - > clear_art_manual ( ) ;
song - > set_art_unset ( true ) ;
2018-02-27 18:06:05 +01:00
2023-02-18 14:09:27 +01:00
UpdateCover ( UpdateCoverAction : : Unset ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : ClearCover ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
Song * song = GetFirstSelected ( ) ;
2021-02-26 21:03:51 +01:00
if ( ! song ) return ;
2023-05-14 11:34:55 +02:00
song - > set_art_embedded ( false ) ;
2021-02-26 21:03:51 +01:00
song - > clear_art_automatic ( ) ;
song - > clear_art_manual ( ) ;
2023-05-14 11:34:55 +02:00
song - > set_art_unset ( false ) ;
2018-02-27 18:06:05 +01:00
2023-02-18 14:09:27 +01:00
UpdateCover ( UpdateCoverAction : : Clear ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : DeleteCover ( ) {
2018-10-02 00:38:52 +02:00
2023-02-18 14:09:27 +01:00
UpdateCover ( UpdateCoverAction : : Delete ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
}
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
void EditTagDialog : : ShowCover ( ) {
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
if ( ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . isEmpty ( ) ) return ;
const Data & first_data = data_ [ ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . first ( ) . row ( ) ] ;
2023-04-09 22:26:17 +02:00
album_cover_choice_controller_ - > ShowCover ( first_data . current_ , first_data . cover_result_ . image ) ;
2020-10-19 21:05:59 +02:00
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
void EditTagDialog : : UpdateCover ( const UpdateCoverAction cover_action , const AlbumCoverImageResult & cover_result ) {
2021-02-26 21:03:51 +01:00
const QModelIndexList indexes = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
if ( indexes . isEmpty ( ) ) return ;
2020-10-19 21:05:59 +02:00
2021-02-26 21:03:51 +01:00
QString artist = data_ [ indexes . first ( ) . row ( ) ] . current_ . effective_albumartist ( ) ;
QString album = data_ [ indexes . first ( ) . row ( ) ] . current_ . album ( ) ;
for ( const QModelIndex & idx : indexes ) {
2023-05-14 11:34:55 +02:00
data_ [ idx . row ( ) ] . cover_action_ = cover_action ;
data_ [ idx . row ( ) ] . cover_result_ = cover_result ;
if ( cover_action = = UpdateCoverAction : : New ) {
2021-02-26 21:03:51 +01:00
data_ [ idx . row ( ) ] . current_ . clear_art_manual ( ) ;
2023-05-14 11:34:55 +02:00
data_ [ idx . row ( ) ] . current_ . set_art_unset ( false ) ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
else if ( cover_action = = UpdateCoverAction : : Unset ) {
data_ [ idx . row ( ) ] . current_ . set_art_unset ( true ) ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
else if ( cover_action = = UpdateCoverAction : : Clear | | cover_action = = UpdateCoverAction : : Delete ) {
data_ [ idx . row ( ) ] . current_ . set_art_embedded ( false ) ;
2021-02-26 21:03:51 +01:00
data_ [ idx . row ( ) ] . current_ . clear_art_manual ( ) ;
data_ [ idx . row ( ) ] . current_ . clear_art_automatic ( ) ;
2023-05-14 11:34:55 +02:00
data_ [ idx . row ( ) ] . current_ . set_art_unset ( false ) ;
2021-02-26 21:03:51 +01:00
}
if ( artist ! = data_ [ idx . row ( ) ] . current_ . effective_albumartist ( ) | | album ! = data_ [ idx . row ( ) ] . current_ . effective_albumartist ( ) ) {
artist . clear ( ) ;
album . clear ( ) ;
}
2018-02-27 18:06:05 +01:00
}
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
// Now check if we have any other songs cached that share that artist and album (and would therefore be changed as well)
if ( ! artist . isEmpty ( ) & & ! album . isEmpty ( ) ) {
for ( int i = 0 ; i < data_ . count ( ) ; + + i ) {
if ( data_ [ i ] . current_ . effective_albumartist ( ) = = artist & & data_ [ i ] . current_ . album ( ) = = album ) {
2023-05-14 11:34:55 +02:00
data_ [ i ] . cover_action_ = cover_action ;
data_ [ i ] . cover_result_ = cover_result ;
if ( cover_action = = UpdateCoverAction : : New ) {
2021-02-26 21:03:51 +01:00
data_ [ i ] . current_ . clear_art_manual ( ) ;
2023-05-14 11:34:55 +02:00
data_ [ i ] . current_ . set_art_unset ( false ) ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
else if ( cover_action = = UpdateCoverAction : : Unset ) {
data_ [ i ] . current_ . set_art_unset ( true ) ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
else if ( cover_action = = UpdateCoverAction : : Clear | | cover_action = = UpdateCoverAction : : Delete ) {
data_ [ i ] . current_ . set_art_embedded ( false ) ;
2021-02-26 21:03:51 +01:00
data_ [ i ] . current_ . clear_art_manual ( ) ;
data_ [ i ] . current_ . clear_art_automatic ( ) ;
2023-05-14 11:34:55 +02:00
data_ [ i ] . current_ . set_art_unset ( false ) ;
2021-02-26 21:03:51 +01:00
}
}
}
}
2023-05-14 11:34:55 +02:00
UpdateSummaryTab ( data_ [ indexes . first ( ) . row ( ) ] . current_ ) ;
2021-02-26 21:03:51 +01:00
SelectionChanged ( ) ;
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : NextSong ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( ui_ - > song_list - > count ( ) = = 0 ) {
return ;
}
int row = ( ui_ - > song_list - > currentRow ( ) + 1 ) % ui_ - > song_list - > count ( ) ;
ui_ - > song_list - > setCurrentRow ( row ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : PreviousSong ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( ui_ - > song_list - > count ( ) = = 0 ) {
return ;
}
int row = ( ui_ - > song_list - > currentRow ( ) - 1 + ui_ - > song_list - > count ( ) ) % ui_ - > song_list - > count ( ) ;
ui_ - > song_list - > setCurrentRow ( row ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : ButtonClicked ( QAbstractButton * button ) {
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
if ( button = = ui_ - > button_box - > button ( QDialogButtonBox : : Discard ) ) {
reject ( ) ;
}
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : SaveData ( ) {
2018-10-02 00:38:52 +02:00
2021-02-27 00:58:43 +01:00
QMap < QString , QUrl > cover_urls ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
for ( int i = 0 ; i < data_ . count ( ) ; + + i ) {
Data & ref = data_ [ i ] ;
2018-02-27 18:06:05 +01:00
2021-03-07 07:01:22 +01:00
QString embedded_cover_from_file ;
2022-08-28 02:44:37 +02:00
// If embedded album cover is selected, and it isn't saved to the tags, then save it even if no action was done.
2023-05-14 11:34:55 +02:00
if ( ui_ - > checkbox_embedded_cover - > isChecked ( ) & & ref . cover_action_ = = UpdateCoverAction : : None & & ! ref . original_ . art_embedded ( ) & & ref . original_ . save_embedded_cover_supported ( ) ) {
if ( ref . original_ . art_manual_is_valid ( ) ) {
2023-02-18 14:09:27 +01:00
ref . cover_action_ = UpdateCoverAction : : New ;
2021-03-07 07:01:22 +01:00
embedded_cover_from_file = ref . original_ . art_manual ( ) . toLocalFile ( ) ;
}
2023-05-14 11:34:55 +02:00
else if ( ref . original_ . art_automatic_is_valid ( ) ) {
2023-02-18 14:09:27 +01:00
ref . cover_action_ = UpdateCoverAction : : New ;
2021-03-07 07:01:22 +01:00
embedded_cover_from_file = ref . original_ . art_automatic ( ) . toLocalFile ( ) ;
}
2021-02-26 21:03:51 +01:00
}
2018-02-27 18:06:05 +01:00
2023-03-18 20:03:07 +01:00
const bool save_tags = ! ref . current_ . IsMetadataEqual ( ref . original_ ) ;
const bool save_rating = ! ref . current_ . IsRatingEqual ( ref . original_ ) ;
const bool save_playcount = ref . current_ . playcount ( ) = = 0 & & ref . current_ . skipcount ( ) = = 0 & & ref . current_ . lastplayed ( ) = = - 1 & & ! ref . current_ . IsPlayStatisticsEqual ( ref . original_ ) ;
const bool save_embedded_cover = ref . cover_action_ ! = UpdateCoverAction : : None & & ui_ - > checkbox_embedded_cover - > isChecked ( ) & & ref . original_ . save_embedded_cover_supported ( ) ;
2023-02-18 14:09:27 +01:00
if ( ref . cover_action_ ! = UpdateCoverAction : : None ) {
2021-02-26 21:03:51 +01:00
switch ( ref . cover_action_ ) {
2023-02-18 14:09:27 +01:00
case UpdateCoverAction : : None :
2021-02-27 02:12:26 +01:00
break ;
2023-02-18 14:09:27 +01:00
case UpdateCoverAction : : New : {
2021-02-27 02:12:26 +01:00
if ( ( ! ref . current_ . effective_albumartist ( ) . isEmpty ( ) & & ! ref . current_ . album ( ) . isEmpty ( ) ) & &
( ! ui_ - > checkbox_embedded_cover - > isChecked ( ) | | ! ref . original_ . save_embedded_cover_supported ( ) ) ) {
QUrl cover_url ;
2023-04-09 22:26:17 +02:00
if ( ! ref . cover_result_ . cover_url . isEmpty ( ) & & ref . cover_result_ . cover_url . isLocalFile ( ) & & QFile : : exists ( ref . cover_result_ . cover_url . toLocalFile ( ) ) ) {
cover_url = ref . cover_result_ . cover_url ;
2021-02-27 02:12:26 +01:00
}
else {
2024-04-11 02:56:01 +02:00
QString cover_hash = QString : : fromLatin1 ( CoverUtils : : Sha1CoverHash ( ref . current_ . effective_albumartist ( ) , ref . current_ . album ( ) ) . toHex ( ) ) ;
2021-03-07 02:36:50 +01:00
if ( cover_urls . contains ( cover_hash ) ) {
cover_url = cover_urls [ cover_hash ] ;
}
else {
cover_url = album_cover_choice_controller_ - > SaveCoverToFileAutomatic ( & ref . current_ , ref . cover_result_ ) ;
2023-05-14 11:34:55 +02:00
if ( cover_url . isValid ( ) ) {
cover_urls . insert ( cover_hash , cover_url ) ;
}
2021-03-07 02:36:50 +01:00
}
2021-02-26 21:03:51 +01:00
}
2021-02-27 02:12:26 +01:00
ref . current_ . set_art_manual ( cover_url ) ;
2023-05-14 11:34:55 +02:00
ref . current_ . set_art_unset ( false ) ;
2021-02-26 21:03:51 +01:00
}
2021-02-27 02:12:26 +01:00
break ;
}
2023-02-18 14:09:27 +01:00
case UpdateCoverAction : : Unset :
2023-05-14 11:34:55 +02:00
ref . current_ . set_art_embedded ( false ) ;
ref . current_ . clear_art_manual ( ) ;
ref . current_ . clear_art_automatic ( ) ;
ref . current_ . set_art_unset ( true ) ;
2021-02-27 02:12:26 +01:00
break ;
2023-02-18 14:09:27 +01:00
case UpdateCoverAction : : Clear :
2023-05-14 11:34:55 +02:00
ref . current_ . set_art_embedded ( false ) ;
2021-02-27 02:12:26 +01:00
ref . current_ . clear_art_manual ( ) ;
2023-05-14 11:34:55 +02:00
ref . current_ . clear_art_automatic ( ) ;
ref . current_ . set_art_unset ( false ) ;
2021-02-27 02:12:26 +01:00
break ;
2023-02-18 14:09:27 +01:00
case UpdateCoverAction : : Delete : {
2023-05-14 11:34:55 +02:00
ref . current_ . set_art_embedded ( false ) ;
2021-02-27 02:12:26 +01:00
if ( ! ref . original_ . art_automatic ( ) . isEmpty ( ) ) {
2023-05-14 11:34:55 +02:00
if ( ref . original_ . art_automatic_is_valid ( ) ) {
const QString art_automatic = ref . original_ . art_automatic ( ) . toLocalFile ( ) ;
2021-02-27 02:12:26 +01:00
if ( QFile : : exists ( art_automatic ) ) {
QFile : : remove ( art_automatic ) ;
}
2021-02-26 21:03:51 +01:00
}
2021-02-27 02:12:26 +01:00
ref . current_ . clear_art_automatic ( ) ;
}
2023-05-14 11:34:55 +02:00
if ( ! ref . original_ . art_manual ( ) . isEmpty ( ) ) {
if ( ref . original_ . art_manual_is_valid ( ) ) {
const QString art_manual = ref . original_ . art_manual ( ) . toLocalFile ( ) ;
2021-02-27 02:12:26 +01:00
if ( QFile : : exists ( art_manual ) ) {
QFile : : remove ( art_manual ) ;
}
2021-02-26 21:03:51 +01:00
}
2021-02-27 02:12:26 +01:00
ref . current_ . clear_art_manual ( ) ;
2021-02-26 21:03:51 +01:00
}
2023-05-14 11:34:55 +02:00
ref . current_ . set_art_unset ( false ) ;
2021-02-27 02:12:26 +01:00
break ;
}
2021-02-26 21:03:51 +01:00
}
2023-03-18 20:03:07 +01:00
}
if ( save_tags | | save_playcount | | save_rating | | save_embedded_cover ) {
2023-03-25 18:29:12 +01:00
// Not to confuse the collection model.
if ( ref . current_ . track ( ) < = 0 ) { ref . current_ . set_track ( - 1 ) ; }
if ( ref . current_ . disc ( ) < = 0 ) { ref . current_ . set_disc ( - 1 ) ; }
if ( ref . current_ . year ( ) < = 0 ) { ref . current_ . set_year ( - 1 ) ; }
if ( ref . current_ . originalyear ( ) < = 0 ) { ref . current_ . set_originalyear ( - 1 ) ; }
if ( ref . current_ . lastplayed ( ) < = 0 ) { ref . current_ . set_lastplayed ( - 1 ) ; }
2023-03-18 20:03:07 +01:00
+ + save_tag_pending_ ;
TagReaderClient : : SaveCoverOptions savecover_options ;
if ( save_embedded_cover & & ref . cover_action_ = = UpdateCoverAction : : New ) {
2023-04-09 22:26:17 +02:00
if ( ! ref . cover_result_ . image . isNull ( ) ) {
2023-05-14 11:34:55 +02:00
savecover_options . mime_type = ref . cover_result_ . mime_type ;
2021-03-07 07:01:22 +01:00
}
2023-03-18 20:03:07 +01:00
else if ( ! embedded_cover_from_file . isEmpty ( ) ) {
savecover_options . cover_filename = embedded_cover_from_file ;
2018-02-27 18:06:05 +01:00
}
2023-05-14 11:34:55 +02:00
savecover_options . cover_data = ref . cover_result_ . image_data ;
}
TagReaderClient : : SaveTypes save_types ;
if ( save_tags ) {
save_types | = TagReaderClient : : SaveType : : Tags ;
}
if ( save_playcount ) {
save_types | = TagReaderClient : : SaveType : : PlayCount ;
2018-02-27 18:06:05 +01:00
}
2023-05-14 11:34:55 +02:00
if ( save_rating ) {
save_types | = TagReaderClient : : SaveType : : Rating ;
}
if ( save_embedded_cover ) {
save_types | = TagReaderClient : : SaveType : : Cover ;
}
TagReaderReply * reply = TagReaderClient : : Instance ( ) - > SaveFile ( ref . current_ . url ( ) . toLocalFile ( ) , ref . current_ , save_types , savecover_options ) ;
2023-03-18 20:03:07 +01:00
QObject : : connect ( reply , & TagReaderReply : : Finished , this , [ this , reply , ref ] ( ) { SongSaveTagsComplete ( reply , ref . current_ . url ( ) . toLocalFile ( ) , ref . current_ , ref . cover_action_ ) ; } , Qt : : QueuedConnection ) ;
}
// If the cover was changed, but no tags written, make sure to update the collection.
else if ( ref . cover_action_ ! = UpdateCoverAction : : None & & ! ref . current_ . effective_albumartist ( ) . isEmpty ( ) & & ! ref . current_ . album ( ) . isEmpty ( ) ) {
if ( ref . current_ . is_collection_song ( ) ) {
collection_songs_ . insert ( ref . current_ . id ( ) , ref . current_ ) ;
}
if ( ref . current_ = = app_ - > current_albumcover_loader ( ) - > last_song ( ) ) {
app_ - > current_albumcover_loader ( ) - > LoadAlbumCover ( ref . current_ ) ;
2018-02-27 18:06:05 +01:00
}
}
2023-03-18 20:03:07 +01:00
2018-02-27 18:06:05 +01:00
}
2020-09-23 00:52:41 +02:00
2023-03-18 20:03:07 +01:00
if ( save_tag_pending_ < = 0 ) SaveDataFinished ( ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
}
2018-10-02 00:38:52 +02:00
2023-03-18 20:03:07 +01:00
void EditTagDialog : : SaveDataFinished ( ) {
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
if ( ! collection_songs_ . isEmpty ( ) ) {
app_ - > collection_backend ( ) - > AddOrUpdateSongsAsync ( collection_songs_ . values ( ) ) ;
collection_songs_ . clear ( ) ;
2020-11-22 03:37:15 +01:00
}
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
if ( ! SetLoading ( QString ( ) ) ) return ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
QDialog : : accept ( ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
2023-03-18 20:03:07 +01:00
void EditTagDialog : : ResetPlayStatistics ( ) {
2018-10-02 00:38:52 +02:00
2023-02-10 22:51:48 +01:00
const QModelIndexList idx_list = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
if ( idx_list . isEmpty ( ) ) return ;
2021-02-26 21:03:51 +01:00
2023-02-10 22:51:48 +01:00
Song * song = & data_ [ idx_list . first ( ) . row ( ) ] . current_ ;
if ( ! song - > is_valid ( ) ) return ;
2018-02-27 18:06:05 +01:00
2023-03-18 20:03:07 +01:00
if ( QMessageBox : : question ( this , tr ( " Reset song play statistics " ) , tr ( " Are you sure you want to reset this song's play statistics? " ) , QMessageBox : : Reset , QMessageBox : : Cancel ) ! = QMessageBox : : Reset ) {
2018-02-27 18:06:05 +01:00
return ;
}
song - > set_playcount ( 0 ) ;
song - > set_skipcount ( 0 ) ;
song - > set_lastplayed ( - 1 ) ;
UpdateStatisticsTab ( * song ) ;
2020-08-10 21:27:27 +02:00
2018-02-27 18:06:05 +01:00
}
2021-10-24 16:08:17 +02:00
void EditTagDialog : : SongRated ( const float rating ) {
const QModelIndexList indexes = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
if ( indexes . isEmpty ( ) ) return ;
for ( const QModelIndex & idx : indexes ) {
2023-02-10 22:51:48 +01:00
if ( ! data_ [ idx . row ( ) ] . current_ . is_valid ( ) ) continue ;
2021-10-24 16:08:17 +02:00
data_ [ idx . row ( ) ] . current_ . set_rating ( rating ) ;
}
}
2018-02-27 18:06:05 +01:00
void EditTagDialog : : FetchTag ( ) {
2018-10-02 00:38:52 +02:00
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2019-10-03 23:29:52 +02:00
2018-02-27 18:06:05 +01:00
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
SongList songs ;
2021-01-26 16:48:04 +01:00
for ( const QModelIndex & idx : sel ) {
Song song = data_ [ idx . row ( ) ] . original_ ;
2018-02-27 18:06:05 +01:00
if ( ! song . is_valid ( ) ) {
continue ;
}
songs < < song ;
}
if ( songs . isEmpty ( ) ) return ;
results_dialog_ - > Init ( songs ) ;
tag_fetcher_ - > StartFetch ( songs ) ;
results_dialog_ - > show ( ) ;
2018-03-10 13:02:56 +01:00
2019-10-03 23:29:52 +02:00
# endif
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : FetchTagSongChosen ( const Song & original_song , const Song & new_metadata ) {
2018-10-02 00:38:52 +02:00
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2019-10-03 23:29:52 +02:00
2018-02-27 18:06:05 +01:00
const QString filename = original_song . url ( ) . toLocalFile ( ) ;
// Find the song with this filename
2019-02-20 21:27:53 +01:00
auto data_it = std : : find_if ( data_ . begin ( ) , data_ . end ( ) , [ & filename ] ( const Data & d ) {
return d . original_ . url ( ) . toLocalFile ( ) = = filename ;
} ) ;
2018-02-27 18:06:05 +01:00
if ( data_it = = data_ . end ( ) ) {
qLog ( Warning ) < < " Could not find song to filename: " < < filename ;
return ;
}
// Update song data
data_it - > current_ . set_title ( new_metadata . title ( ) ) ;
data_it - > current_ . set_artist ( new_metadata . artist ( ) ) ;
data_it - > current_ . set_album ( new_metadata . album ( ) ) ;
data_it - > current_ . set_track ( new_metadata . track ( ) ) ;
data_it - > current_ . set_year ( new_metadata . year ( ) ) ;
// Is it currently being displayed in the UI?
if ( ui_ - > song_list - > currentRow ( ) = = std : : distance ( data_ . begin ( ) , data_it ) ) {
2022-08-28 02:44:37 +02:00
// Yes! Additionally, update UI
2018-02-27 18:06:05 +01:00
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
UpdateUI ( sel ) ;
}
2019-02-20 21:27:53 +01:00
2019-10-03 23:29:52 +02:00
# else
Q_UNUSED ( original_song )
Q_UNUSED ( new_metadata )
2018-02-27 18:06:05 +01:00
# endif
2019-02-20 21:27:53 +01:00
2019-10-03 23:29:52 +02:00
}
2024-03-06 23:16:29 +01:00
void EditTagDialog : : FetchLyrics ( ) {
if ( ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . isEmpty ( ) ) return ;
const Song & song = data_ [ ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . first ( ) . row ( ) ] . current_ ;
lyrics_fetcher_ - > Clear ( ) ;
ui_ - > lyrics - > setPlainText ( tr ( " loading... " ) ) ;
lyrics_id_ = static_cast < qint64 > ( lyrics_fetcher_ - > Search ( song . effective_albumartist ( ) , song . artist ( ) , song . album ( ) , song . title ( ) ) ) ;
}
void EditTagDialog : : UpdateLyrics ( const quint64 id , const QString & provider , const QString & lyrics ) {
Q_UNUSED ( provider ) ;
if ( static_cast < qint64 > ( id ) ! = lyrics_id_ ) return ;
lyrics_id_ = - 1 ;
if ( lyrics . isEmpty ( ) ) {
ui_ - > lyrics - > setPlainText ( tr ( " Not found. " ) ) ;
}
else {
ui_ - > lyrics - > setPlainText ( lyrics ) ;
}
}
2023-03-18 20:03:07 +01:00
void EditTagDialog : : SongSaveTagsComplete ( TagReaderReply * reply , const QString & filename , Song song , const UpdateCoverAction cover_action ) {
2019-02-20 21:27:53 +01:00
2021-02-26 21:03:51 +01:00
- - save_tag_pending_ ;
2023-03-18 20:03:07 +01:00
const bool success = reply - > message ( ) . save_file_response ( ) . success ( ) ;
reply - > deleteLater ( ) ;
if ( success ) {
if ( song . is_collection_song ( ) ) {
if ( collection_songs_ . contains ( song . id ( ) ) ) {
Song old_song = collection_songs_ . take ( song . id ( ) ) ;
song . set_art_automatic ( old_song . art_automatic ( ) ) ;
song . set_art_manual ( old_song . art_manual ( ) ) ;
}
switch ( cover_action ) {
case UpdateCoverAction : : None :
break ;
case UpdateCoverAction : : New :
song . clear_art_manual ( ) ;
2023-05-14 11:34:55 +02:00
song . set_art_embedded ( true ) ;
2023-03-18 20:03:07 +01:00
break ;
case UpdateCoverAction : : Clear :
case UpdateCoverAction : : Delete :
2023-05-14 11:34:55 +02:00
song . set_art_embedded ( false ) ;
2023-03-18 20:03:07 +01:00
break ;
case UpdateCoverAction : : Unset :
2023-05-14 11:34:55 +02:00
song . set_art_embedded ( false ) ;
song . set_art_unset ( true ) ;
2023-03-18 20:03:07 +01:00
break ;
}
collection_songs_ . insert ( song . id ( ) , song ) ;
2021-02-26 21:03:51 +01:00
}
2023-03-18 20:03:07 +01:00
if ( cover_action ! = UpdateCoverAction : : None & & song = = app_ - > current_albumcover_loader ( ) - > last_song ( ) ) {
app_ - > current_albumcover_loader ( ) - > LoadAlbumCover ( song ) ;
2021-02-26 21:03:51 +01:00
}
}
2023-03-18 20:03:07 +01:00
else {
emit Error ( tr ( " An error occurred writing metadata to '%1' " ) . arg ( filename ) ) ;
2019-02-20 21:27:53 +01:00
}
2023-03-18 20:03:07 +01:00
if ( save_tag_pending_ < = 0 ) SaveDataFinished ( ) ;
2019-06-22 08:36:02 +02:00
2019-02-20 21:27:53 +01:00
}