diff --git a/src/models/editabletrackmetadatamodel.cpp b/src/models/editabletrackmetadatamodel.cpp index f82bd721..fba7a4b9 100644 --- a/src/models/editabletrackmetadatamodel.cpp +++ b/src/models/editabletrackmetadatamodel.cpp @@ -1,100 +1,112 @@ /* * Copyright 2020 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "editabletrackmetadatamodel.h" +#include + #include EditableTrackMetadataModel::EditableTrackMetadataModel(QObject *parent) : TrackMetadataModel(parent) { } bool EditableTrackMetadataModel::setData(const QModelIndex &index, const QVariant &value, int role) { auto result = TrackMetadataModel::setData(index, value, role); if (result) { if (!mIsDirty) { mIsDirty = true; Q_EMIT isDirtyChanged(); } validData(); } return result; } void EditableTrackMetadataModel::saveData() { mIsDirty = false; Q_EMIT isDirtyChanged(); QString imageUrl = dataFromType(DataTypes::ImageUrlRole).toString(); if (!imageUrl.isEmpty() && !imageUrl.startsWith(QStringLiteral("http://")) && !imageUrl.startsWith(QStringLiteral("https://")) && !imageUrl.startsWith(QStringLiteral("file://"))) { auto newTrackData = allTrackData(); newTrackData[DataTypes::ImageUrlRole] = QStringLiteral("file:/").append(imageUrl); Q_EMIT saveRadioData(newTrackData); } else { Q_EMIT saveRadioData(allTrackData()); } } void EditableTrackMetadataModel::filterDataFromTrackData() { TrackMetadataModel::filterDataFromTrackData(); validData(); } void EditableTrackMetadataModel::fillLyricsDataFromTrack() { TrackMetadataModel::fillLyricsDataFromTrack(); validData(); } void EditableTrackMetadataModel::validData() { bool newValidState = true; const auto &resourceData = dataFromType(TrackDataType::key_type::ResourceRole); if (resourceData.canConvert()) { const auto resourceUrl = resourceData.toUrl(); newValidState = !resourceUrl.scheme().isEmpty() && resourceUrl.isValid() && !resourceUrl.isRelative(); } else { newValidState = false; } + if (!newValidState) { + mErrorMessage = i18nc("Error message when track URL is invalid", "Invalid URL."); + Q_EMIT errorMessageChanged(); + } + if (newValidState) { const auto &titleData = dataFromType(TrackDataType::key_type::TitleRole); newValidState = newValidState && !titleData.toString().isEmpty(); + + if (!newValidState) { + mErrorMessage = i18nc("Error message when track title is empty", "Empty title."); + Q_EMIT errorMessageChanged(); + } } if (mIsDataValid != newValidState) { mIsDataValid = newValidState; Q_EMIT isDataValidChanged(); } } #include "moc_editabletrackmetadatamodel.cpp" diff --git a/src/models/editabletrackmetadatamodel.h b/src/models/editabletrackmetadatamodel.h index 3c8277e0..32edc904 100644 --- a/src/models/editabletrackmetadatamodel.h +++ b/src/models/editabletrackmetadatamodel.h @@ -1,79 +1,92 @@ /* * Copyright 2020 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef EDITABLETRACKMETADATAMODEL_H #define EDITABLETRACKMETADATAMODEL_H #include "elisaLib_export.h" #include "trackmetadatamodel.h" class ELISALIB_EXPORT EditableTrackMetadataModel : public TrackMetadataModel { Q_OBJECT Q_PROPERTY(bool isDataValid READ isDataValid NOTIFY isDataValidChanged) + Q_PROPERTY(QString errorMessage + READ errorMessage + NOTIFY errorMessageChanged) + Q_PROPERTY(bool isDirty READ isDirty NOTIFY isDirtyChanged) public: explicit EditableTrackMetadataModel(QObject *parent = nullptr); bool isDataValid() const { return mIsDataValid; } bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool isDirty() const { return mIsDirty; } + QString errorMessage() const + { + return mErrorMessage; + } + Q_SIGNALS: void isDataValidChanged(); void isDirtyChanged(); + void errorMessageChanged(); + public Q_SLOTS: void saveData(); protected: void filterDataFromTrackData() override; void fillLyricsDataFromTrack() override; private: void validData(); bool mIsDataValid = false; bool mIsDirty = false; + + QString mErrorMessage; }; #endif // EDITABLETRACKMETADATAMODEL_H diff --git a/src/qml/MediaTrackMetadataView.qml b/src/qml/MediaTrackMetadataView.qml index f84535d8..d2ac69ee 100644 --- a/src/qml/MediaTrackMetadataView.qml +++ b/src/qml/MediaTrackMetadataView.qml @@ -1,270 +1,270 @@ /* * Copyright 2017 Alexander Stippich * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Window 2.2 import QtQml.Models 2.2 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.5 as Kirigami import org.kde.elisa 1.0 Window { id: trackMetadata property var modelType property url fileName property bool editableMetadata property bool isCreation: false property alias showImage: metadataImage.visible property alias showTrackFileName: fileNameRow.visible property alias showDeleteButton: deleteButtonBox.visible property alias showApplyButton: applyButton.visible property double widthIndex: 2.8 signal rejected() LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true title: isCreation ? i18nc("Window title for track metadata", "Create a Radio") : i18nc("Window title for track metadata", "View Details") EditableTrackMetadataModel { id: realModel manager: elisa.musicManager } modality: Qt.NonModal flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint color: myPalette.window minimumHeight: elisaTheme.coverImageSize * 1.8 minimumWidth: elisaTheme.coverImageSize * trackMetadata.widthIndex ColumnLayout { anchors.fill: parent anchors.margins: elisaTheme.layoutVerticalMargin spacing: elisaTheme.layoutVerticalMargin RowLayout { id: metadataView Layout.fillHeight: true Layout.fillWidth: true spacing: 0 Image { id: metadataImage source: realModel.coverUrl sourceSize.width: elisaTheme.coverImageSize sourceSize.height: elisaTheme.coverImageSize fillMode: Image.PreserveAspectFit Layout.alignment: Qt.AlignTop | Qt.AlignHCenter Layout.preferredHeight: elisaTheme.coverImageSize Layout.preferredWidth: elisaTheme.coverImageSize Layout.minimumHeight: elisaTheme.coverImageSize Layout.minimumWidth: elisaTheme.coverImageSize Layout.maximumHeight: elisaTheme.coverImageSize Layout.maximumWidth: elisaTheme.coverImageSize onStatusChanged: { if (metadataImage.status === Image.Error) { source = Qt.resolvedUrl(elisaTheme.defaultAlbumImage) } } } ListView { id: trackData Layout.fillWidth: true Layout.fillHeight: true Layout.leftMargin: 2 * elisaTheme.layoutHorizontalMargin focus: true ScrollBar.vertical: ScrollBar { id: scrollBar } boundsBehavior: Flickable.StopAtBounds clip: true ScrollHelper { id: scrollHelper flickable: trackData anchors.fill: trackData } model: realModel Component { id: metaDataDelegate MetaDataDelegate { width: scrollBar.visible ? (!LayoutMirroring.enabled ? trackData.width - scrollBar.width : trackData.width) : trackData.width } } Component { id: editableMetaDataDelegate EditableMetaDataDelegate { width: scrollBar.visible ? (!LayoutMirroring.enabled ? trackData.width - scrollBar.width : trackData.width) : trackData.width } } delegate: editableMetadata ? editableMetaDataDelegate: metaDataDelegate } } RowLayout { id: fileNameRow Layout.alignment: Qt.AlignLeft | Qt.AlignBottom Layout.topMargin: elisaTheme.layoutVerticalMargin Layout.bottomMargin: elisaTheme.layoutVerticalMargin spacing: elisaTheme.layoutHorizontalMargin Image { Layout.preferredWidth: fileNameLabel.height Layout.preferredHeight: fileNameLabel.height sourceSize.width: fileNameLabel.height sourceSize.height: fileNameLabel.height source: elisaTheme.folderIcon } LabelWithToolTip { id: fileNameLabel Layout.fillWidth: true text: realModel.fileUrl elide: Text.ElideRight } } Kirigami.InlineMessage { id: formInvalidNotification - text: i18n("Data are not valid. Radio cannot be created or modified.") + text: i18nc("Form validation error message for track data", "Data are not valid. %1", realModel.errorMessage) type: Kirigami.MessageType.Error showCloseButton: false visible: !realModel.isDataValid Layout.topMargin: 5 Layout.fillWidth: true Layout.rightMargin: elisaTheme.layoutHorizontalMargin Layout.leftMargin: elisaTheme.layoutHorizontalMargin } RowLayout { spacing: elisaTheme.layoutVerticalMargin DialogButtonBox { id: deleteButtonBox Layout.minimumHeight: implicitHeight alignment: Qt.AlignLeft Button { id: deleteButton text: i18n("Delete") DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole onClicked: { elisa.musicManager.deleteElementById(modelType, realModel.databaseId) trackMetadata.close() } } } DialogButtonBox { id: buttons Layout.fillWidth: true Layout.minimumHeight: implicitHeight alignment: Qt.AlignRight Button { id: applyButton enabled: realModel.isDataValid && realModel.isDirty text: i18n("Apply") DialogButtonBox.buttonRole: DialogButtonBox.ApplyRole onClicked: { realModel.saveData() if (!deleteButtonBox.visible && editableMetadata) { deleteButtonBox.visible = true } } } Button { text: i18n("Close") DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole onClicked: trackMetadata.close() } } } } Connections { target: elisa onMusicManagerChanged: { if (isCreation) { realModel.initializeForNewRadio() } else { realModel.initializeByUrl(modelType, fileName) } } } Connections { target: realModel onCoverUrlChanged: { metadataImage.source = realModel.coverUrl } } Component.onCompleted: { if (elisa.musicManager) { if (isCreation) { realModel.initializeForNewRadio() } else { realModel.initializeByUrl(modelType, fileName) } } } }