Warning, /utilities/krecorder/src/contents/ui/RecordingListPage.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  * SPDX-FileCopyrightText: 2020 Jonah BrĂ¼chert <jbb@kaidan.im>
0003  * SPDX-FileCopyrightText: 2020-2022 Devin Lin <espidev@gmail.com>
0004  *
0005  * SPDX-License-Identifier: GPL-3.0-or-later
0006  */
0007 
0008 import QtCore
0009 import QtQuick
0010 import QtQuick.Controls as Controls
0011 import QtQuick.Layouts
0012 import QtQuick.Dialogs
0013 
0014 import org.kde.kirigami as Kirigami
0015 
0016 import KRecorder
0017 
0018 import "components"
0019 
0020 Kirigami.ScrollablePage {
0021     id: root
0022     title: i18n("Recordings")
0023 
0024     property Recording currentRecordingToEdit
0025     property bool editMode
0026     
0027     onEditModeChanged: {
0028         editAction.checked = editMode;
0029     }
0030     
0031     implicitWidth: applicationWindow().isWidescreen ? Kirigami.Units.gridUnit * 8 : applicationWindow().width
0032     
0033     actions: [
0034         Kirigami.Action {
0035             id: editAction
0036             icon.name: "edit-entry"
0037             text: i18n("Edit")
0038             onTriggered: root.editMode = !root.editMode
0039             checkable: true
0040             visible: listView.count > 0
0041         },
0042         Kirigami.Action {
0043             visible: !applicationWindow().isWidescreen
0044             icon.name: "settings-configure"
0045             text: i18n("Settings")
0046             onTriggered: applicationWindow().openSettings();
0047         },
0048         Kirigami.Action {
0049             visible: applicationWindow().isWidescreen
0050             icon.name: "microphone-sensitivity-high"
0051             text: i18n("Record")
0052             onTriggered: applicationWindow().openRecordScreen()
0053         }
0054     ]
0055     
0056     function editRecordingDialog(recording) {
0057         editDialogName.text = recording.fileName;
0058         editDialogLocation.text = recording.filePath;
0059         currentRecordingToEdit = recording;
0060         editNameDialog.open();
0061     }
0062     
0063     function removeRecordingDialog(recording, index) {
0064         deleteDialog.toDelete = recording;
0065         deleteDialog.toDeleteIndex = index;
0066         deleteDialog.open();
0067     }
0068     
0069     ListView {
0070         id: listView
0071         model: RecordingModel
0072         
0073         // show animation
0074         property real yTranslate: 0
0075         transform: Translate { y: listView.yTranslate }
0076         NumberAnimation on opacity {
0077             from: 0
0078             to: 1
0079             duration: Kirigami.Units.longDuration * 2
0080             easing.type: Easing.InOutQuad
0081             running: true
0082         }
0083         NumberAnimation {
0084             from: Kirigami.Units.gridUnit * 3
0085             to: 0
0086             duration: Kirigami.Units.longDuration * 3
0087             easing.type: Easing.OutQuint
0088             property: "yTranslate"
0089             target: listView
0090             running: true
0091         }
0092         
0093         // prevent default highlight
0094         currentIndex: -1
0095 
0096         add: Transition {
0097             NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: Kirigami.Units.shortDuration }
0098         }
0099         remove: Transition {
0100             NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: Kirigami.Units.shortDuration }
0101         }
0102         displaced: Transition {
0103             NumberAnimation { properties: "x,y"; duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad}
0104         }
0105         
0106         Kirigami.PlaceholderMessage {
0107             anchors.centerIn: parent
0108             anchors.left: parent.left
0109             anchors.right: parent.right
0110             anchors.margins: Kirigami.Units.largeSpacing
0111             
0112             icon.name: applicationWindow().isWidescreen ? "format-list-unordered" : "microphone-sensitivity-high"
0113             text: i18n("No recordings")
0114             visible: parent.count === 0
0115         }
0116         
0117         // record button
0118         FloatingActionButton {
0119             visible: !applicationWindow().isWidescreen
0120             icon.name: 'microphone-sensitivity-high'
0121             onClicked: applicationWindow().openRecordScreen()
0122         }
0123         
0124         delegate: RecordingListDelegate {
0125             recording: model.recording
0126             width: listView.width
0127             editMode: root.editMode
0128             showSeparator: index != listView.count - 1
0129             
0130             onLongPressed: root.editMode = !root.editMode
0131             onEditRequested: root.editRecordingDialog(model.recording)
0132             onDeleteRequested: root.removeRecordingDialog(model.recording, index)
0133             onContextMenuRequested: {
0134                 contextMenu.recording = model.recording;
0135                 contextMenu.index = index;
0136                 contextMenu.popup(this)
0137             }
0138             onExportRequested: saveFileDialog.openForRecording(model.recording)
0139         }
0140         
0141         FileDialog {
0142             id: saveFileDialog
0143             fileMode: FileDialog.SaveFile
0144             currentFolder: StandardPaths.writableLocation(StandardPaths.MusicLocation)
0145                 
0146             property Recording recording
0147             
0148             function openForRecording(recording) {
0149                 title = i18n("Select a location to save recording %1", recording.fileName);
0150                 defaultSuffix = recording.fileExtension;
0151                 nameFilters = [`${recording.fileExtension} files (*.${recording.fileExtension})`];
0152                 saveFileDialog.recording = recording;
0153                 open();
0154             }
0155             
0156             onAccepted: {
0157                 let prefixLessUrl = decodeURIComponent(fileUrl.toString().substring("file://".length));
0158                 recording.createCopyOfFile(prefixLessUrl);
0159                 applicationWindow().showPassiveNotification(i18n("Saved recording to %1", prefixLessUrl), "short");
0160             }
0161         }
0162         
0163         Controls.Menu {
0164             id: contextMenu
0165             modal: true
0166             Controls.Overlay.modal: MouseArea {}
0167             
0168             property Recording recording
0169             property int index
0170     
0171             Controls.MenuItem {
0172                 text: i18n("Export to location")
0173                 icon.name: "document-save"
0174                 onTriggered: saveFileDialog.openForRecording(contextMenu.recording)
0175             }
0176 
0177             Controls.MenuItem {
0178                 text: i18n("Edit")
0179                 icon.name: "edit-entry"
0180                 onTriggered: {
0181                     openDialogTimer.run = () => root.editRecordingDialog(contextMenu.recording);
0182                     openDialogTimer.restart();
0183                 }
0184             }
0185 
0186             Controls.MenuItem {
0187                 text: i18n("Delete")
0188                 icon.name: "delete"
0189                 onTriggered: {
0190                     openDialogTimer.run = () => root.removeRecordingDialog(contextMenu.recording, contextMenu.index);
0191                     openDialogTimer.restart();
0192                 }
0193             }
0194         }
0195         
0196         // HACK: for some reason the dialog might close immediately if triggered from the context menu
0197         // open the dialog a little later to workaround this
0198         Timer {
0199             id: openDialogTimer
0200             interval: 50
0201             property var run: () => {}
0202             onTriggered: run()
0203         }
0204         
0205         Kirigami.PromptDialog {
0206             id: deleteDialog
0207             standardButtons: Kirigami.Dialog.NoButton
0208             
0209             property Recording toDelete: null
0210             property int toDeleteIndex: 0
0211             
0212             title: i18n("Delete %1", deleteDialog.toDelete ? deleteDialog.toDelete.fileName : "")
0213             subtitle: i18n("Are you sure you want to delete the recording %1?<br/>It will be <b>permanently lost</b> forever!", deleteDialog.toDelete ? deleteDialog.toDelete.fileName : "")
0214             
0215             customFooterActions: [
0216                 Kirigami.Action {
0217                     text: i18nc("@action:button", "Delete")
0218                     icon.name: "delete"
0219                     onTriggered: {
0220                         if (applicationWindow().currentRecording && deleteDialog.toDelete.filePath == applicationWindow().currentRecording.filePath) {
0221                             applicationWindow().switchToRecording(null);
0222                         }
0223                         RecordingModel.deleteRecording(deleteDialog.toDeleteIndex);
0224                         deleteDialog.close();
0225                     }
0226                 },
0227                 Kirigami.Action {
0228                     text: i18nc("@action:button", "Cancel")
0229                     icon.name: "dialog-cancel"
0230                     onTriggered: {
0231                         deleteDialog.close();
0232                     }
0233                 }
0234             ]
0235         }
0236         
0237         Kirigami.Dialog {
0238             id: editNameDialog
0239             
0240             title: i18n("Rename %1", editDialogName.text)
0241             standardButtons: Kirigami.Dialog.Cancel | Kirigami.Dialog.Apply
0242             
0243             padding: Kirigami.Units.largeSpacing
0244             bottomPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
0245             preferredWidth: Kirigami.Units.gridUnit * 20
0246             
0247             onApplied: {
0248                  currentRecordingToEdit.fileName = editDialogName.text;
0249                  editNameDialog.close();
0250             }
0251             
0252             Kirigami.FormLayout {
0253                 Controls.TextField {
0254                     id: editDialogName
0255                     Kirigami.FormData.label: i18n("Name:")
0256                     Layout.fillWidth: true
0257                 }
0258                 
0259                 Controls.Label {
0260                     id: editDialogLocation 
0261                     Kirigami.FormData.label: i18n("Location:")
0262                     Layout.fillWidth: true
0263                     wrapMode: Text.Wrap
0264                 }
0265             }
0266         }
0267     }
0268 }