Warning, /multimedia/kdenlive/src/timeline2/view/qml/Track.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2013-2016 Meltytech LLC
0003     SPDX-FileCopyrightText: 2013-2016 Dan Dennedy <dan@dennedy.org>
0004 
0005     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 import QtQuick 2.15
0009 import QtQml.Models 2.15
0010 import com.enums 1.0
0011 
0012 Item{
0013     id: trackRoot
0014     property alias trackModel: trackModel.model
0015     property alias rootIndex : trackModel.rootIndex
0016     property bool isAudio
0017     property bool isLocked: false
0018     property int trackInternalId : -42
0019     property int trackThumbsFormat
0020     property int itemType: 0
0021     property var effectZones
0022     opacity: model.disabled ? 0.4 : 1
0023 
0024     function clipAt(index) {
0025         return repeater.itemAt(index)
0026     }
0027 
0028     function isClip(type) {
0029         return type != ProducerType.Composition && type != ProducerType.Track;
0030     }
0031 
0032     width: clipRow.width
0033 
0034     DelegateModel {
0035         id: trackModel
0036         delegate: Item {
0037             property var itemModel : model
0038             property bool clipItem: isClip(model.clipType)
0039             function calculateZIndex() {
0040                 // Z order indicates the items that will be drawn on top.
0041                 if (model.clipType == ProducerType.Composition) {
0042                     // Compositions should be top, then clips
0043                     return 50000;
0044                 }
0045 
0046                 if (model.mixDuration > 0) {
0047                     // Clips with mix should be ordered related to their position so that the right clip of a clip mix is always on top (the mix UI is drawn over the right clip)
0048                     return Math.round(model.start / 25) + 1;
0049                 }
0050 
0051                 if (root.activeTool === ProjectTool.SlipTool && model.selected) {
0052                     return model.item === timeline.trimmingMainClip ? 2 : 1;
0053                 }
0054 
0055                 if (root.activeTool === ProjectTool.RippleTool && model.item === timeline.trimmingMainClip) {
0056                     return 1;
0057                 }
0058                 return 0;
0059             }
0060             z: calculateZIndex()
0061             Loader {
0062                 id: loader
0063                 Binding {
0064                     target: loader.item
0065                     property: "speed"
0066                     value: model.speed
0067                     when: loader.status == Loader.Ready && loader.item && clipItem
0068                 }
0069                 Binding {
0070                     target: loader.item
0071                     property: "timeScale"
0072                     value: root.timeScale
0073                     when: loader.status == Loader.Ready && loader.item
0074                 }
0075                 Binding {
0076                     target: loader.item
0077                     property: "fakeTid"
0078                     value: model.fakeTrackId
0079                     when: loader.status == Loader.Ready && loader.item && clipItem
0080                 }
0081                 Binding {
0082                     target: loader.item
0083                     property: "tagColor"
0084                     value: model.tag
0085                     when: loader.status == Loader.Ready && loader.item && clipItem
0086                 }
0087                 Binding {
0088                     target: loader.item
0089                     property: "fakePosition"
0090                     value: model.fakePosition
0091                     when: loader.status == Loader.Ready && loader.item && clipItem
0092                 }
0093                 Binding {
0094                     target: loader.item
0095                     property: "mixDuration"
0096                     value: model.mixDuration
0097                     when: loader.status == Loader.Ready && loader.item && clipItem
0098                 }
0099                 Binding {
0100                     target: loader.item
0101                     property: "mixCut"
0102                     value: model.mixCut
0103                     when: loader.status == Loader.Ready && loader.item && clipItem
0104                 }
0105                 Binding {
0106                     target: loader.item
0107                     property: "selected"
0108                     value: model.selected
0109                     when: loader.status == Loader.Ready && loader.item
0110                 }
0111                 Binding {
0112                     target: loader.item
0113                     property: "mltService"
0114                     value: model.mlt_service
0115                     when: loader.status == Loader.Ready && loader.item
0116                 }
0117                 Binding {
0118                     target: loader.item
0119                     property: "modelStart"
0120                     value: model.start
0121                     when: loader.status == Loader.Ready && loader.item
0122                 }
0123                 Binding {
0124                     target: loader.item
0125                     property: "fadeIn"
0126                     value: model.fadeIn
0127                     when: loader.status == Loader.Ready && clipItem
0128                 }
0129                 Binding {
0130                     target: loader.item
0131                     property: "positionOffset"
0132                     value: model.positionOffset
0133                     when: loader.status == Loader.Ready && clipItem
0134                 }
0135                 Binding {
0136                     target: loader.item
0137                     property: "effectNames"
0138                     value: model.effectNames
0139                     when: loader.status == Loader.Ready && clipItem
0140                 }
0141                 Binding {
0142                     target: loader.item
0143                     property: "isStackEnabled"
0144                     value: model.isStackEnabled
0145                     when: loader.status == Loader.Ready && clipItem
0146                 }
0147                 Binding {
0148                     target: loader.item
0149                     property: "clipStatus"
0150                     value: model.clipStatus
0151                     when: loader.status == Loader.Ready && clipItem
0152                 }
0153                 Binding {
0154                     target: loader.item
0155                     property: "fadeOut"
0156                     value: model.fadeOut
0157                     when: loader.status == Loader.Ready && clipItem
0158                 }
0159                 Binding {
0160                     target: loader.item
0161                     property: "showKeyframes"
0162                     value: model.showKeyframes
0163                     when: loader.status == Loader.Ready && loader.item
0164                 }
0165                 Binding {
0166                     target: loader.item
0167                     property: "isGrabbed"
0168                     value: model.isGrabbed
0169                     when: loader.status == Loader.Ready && loader.item
0170                 }
0171                 Binding {
0172                     target: loader.item
0173                     property: "keyframeModel"
0174                     value: model.keyframeModel
0175                     when: loader.status == Loader.Ready && loader.item
0176                 }
0177                 Binding {
0178                     target: loader.item
0179                     property: "aTrack"
0180                     value: model.a_track
0181                     when: loader.status == Loader.Ready && model.clipType == ProducerType.Composition
0182                 }
0183                 Binding {
0184                     target: loader.item
0185                     property: "trackHeight"
0186                     value: root.trackHeight
0187                     when: loader.status == Loader.Ready && model.clipType == ProducerType.Composition
0188                 }
0189                 Binding {
0190                     target: loader.item
0191                     property: "clipDuration"
0192                     value: model.duration
0193                     when: loader.status == Loader.Ready && loader.item
0194                 }
0195                 Binding {
0196                     target: loader.item
0197                     property: "inPoint"
0198                     value: model.in
0199                     when: loader.status == Loader.Ready && loader.item
0200                 }
0201                 Binding {
0202                     target: loader.item
0203                     property: "outPoint"
0204                     value: model.out
0205                     when: loader.status == Loader.Ready && loader.item
0206                 }
0207                 Binding {
0208                     target: loader.item
0209                     property: "grouped"
0210                     value: model.grouped
0211                     when: loader.status == Loader.Ready && loader.item
0212                 }
0213                 Binding {
0214                     target: loader.item
0215                     property: "clipName"
0216                     value: model.name
0217                     when: loader.status == Loader.Ready && loader.item
0218                 }
0219                 Binding {
0220                     target: loader.item
0221                     property: "clipResource"
0222                     value: model.resource
0223                     when: loader.status == Loader.Ready && clipItem
0224                 }
0225                 Binding {
0226                     target: loader.item
0227                     property: "clipState"
0228                     value: model.clipState
0229                     when: loader.status == Loader.Ready && clipItem
0230                 }
0231                 Binding {
0232                     target: loader.item
0233                     property: "maxDuration"
0234                     value: model.maxDuration
0235                     when: loader.status == Loader.Ready && clipItem
0236                 }
0237                 Binding {
0238                     target: loader.item
0239                     property: "clipThumbId"
0240                     value: model.clipThumbId
0241                     when: loader.status == Loader.Ready && clipItem
0242                 }
0243                 Binding {
0244                     target: loader.item
0245                     property: "forceReloadAudioThumb"
0246                     value: model.reloadAudioThumb
0247                     when: loader.status == Loader.Ready && clipItem
0248                 }
0249                 Binding {
0250                     target: loader.item
0251                     property: "binId"
0252                     value: model.binId
0253                     when: loader.status == Loader.Ready && clipItem
0254                 }
0255                 Binding {
0256                     target: loader.item
0257                     property: "timeremap"
0258                     value: model.timeremap
0259                     when: loader.status == Loader.Ready && clipItem
0260                 }
0261                 sourceComponent: {
0262                     if (clipItem) {
0263                         return clipDelegate
0264                     } else if (model.clipType == ProducerType.Composition) {
0265                         return compositionDelegate
0266                     } else {
0267                         // Track
0268                         return undefined
0269                     }
0270                 }
0271                 onLoaded: {
0272                     item.clipId= model.item
0273                     item.parentTrack = trackRoot
0274                     if (clipItem) {
0275                         console.log('loaded clip: ', model.start, ', ID: ', model.item, ', index: ', trackRoot.DelegateModel.itemsIndex,', TYPE:', model.clipType)
0276                         item.isAudio= model.audio
0277                         item.markers= model.markers
0278                         item.hasAudio = model.hasAudio
0279                         item.canBeAudio = model.canBeAudio
0280                         item.canBeVideo = model.canBeVideo
0281                         item.itemType = model.clipType
0282                         item.audioChannels = model.audioChannels
0283                         item.audioStream = model.audioStream
0284                         item.multiStream = model.multiStream
0285                         item.aStreamIndex = model.audioStreamIndex
0286                         console.log('loaded clip with Astream: ', model.audioStream)                       
0287                     } else if (model.clipType == ProducerType.Composition) {
0288                         console.log('loaded composition: ', model.start, ', ID: ', model.item, ', index: ', trackRoot.DelegateModel.itemsIndex)
0289                         //item.aTrack = model.a_track
0290                     } else {
0291                         console.log('loaded unwanted element: ', model.item, ', index: ', trackRoot.DelegateModel.itemsIndex)
0292                     }
0293                     item.trackId = model.trackId
0294                     //item.selected= trackRoot.selection.indexOf(item.clipId) != -1
0295                     //console.log(width, height);
0296                 }
0297             }
0298         }
0299     }
0300 
0301     Item {
0302         id: clipRow
0303         height: trackRoot.height
0304         Repeater { id: repeater; model: trackModel }
0305     }
0306 
0307     Component {
0308         id: clipDelegate
0309         Clip {
0310             height: trackRoot.height
0311             onInitGroupTrim: clipId => {
0312                 // We are resizing a group, remember coordinates of all elements
0313                 root.groupTrimData = controller.getGroupData(clipId)
0314             }
0315             onTrimmingIn: (clip, newDuration, shiftTrim, controlTrim) => {
0316                 if (root.activeTool === ProjectTool.SelectTool && controlTrim) {
0317                     newDuration = controller.requestItemSpeedChange(clip.clipId, newDuration, false, root.snapping)
0318                     if (!speedController.visible) {
0319                         // Store original speed
0320                         speedController.originalSpeed = clip.speed
0321                     }
0322                     clip.x += clip.width - (newDuration * root.timeScale)
0323                     clip.width = newDuration * root.timeScale
0324                     speedController.x = clip.x + clip.border.width
0325                     speedController.width = Math.max(0, clip.width - 2 * clip.border.width)
0326                     speedController.lastValidDuration = newDuration
0327                     clip.speed = clip.originalDuration * speedController.originalSpeed / newDuration
0328                     speedController.visible = true
0329                     var delta = newDuration - clip.originalDuration
0330                     var s = timeline.simplifiedTC(Math.abs(delta))
0331                     s = '%1:%2, %3:%4'.arg(i18n("Speed"))
0332                         .arg(clip.speed)
0333                         .arg(i18n("Duration"))
0334                         .arg(timeline.simplifiedTC(newDuration))
0335                     timeline.showToolTip(s)
0336                     return
0337                 }
0338                 var new_duration = 0;
0339                 if (root.activeTool === ProjectTool.RippleTool) {
0340                     console.log("In: Request for " + newDuration)
0341                     new_duration = timeline.requestItemRippleResize(clip.clipId, newDuration, false, false, root.snapping, shiftTrim)
0342                     timeline.requestStartTrimmingMode(clip.clipId, false, false);
0343                     timeline.ripplePosChanged(new_duration, false);
0344                 } else {
0345                     new_duration = controller.requestItemResize(clip.clipId, newDuration, false, false, root.snapping, shiftTrim)
0346                 }
0347 
0348                 if (new_duration > 0) {
0349                     clip.lastValidDuration = new_duration
0350                     clip.originalX = clip.draggedX
0351                     // Show amount trimmed as a time in a "bubble" help.
0352                     var delta = new_duration - clip.originalDuration
0353                     var s = timeline.simplifiedTC(Math.abs(delta))
0354                     s = '%1%2, %3:%4'.arg((delta <= 0)? '+' : '-')
0355                         .arg(s)
0356                         .arg(i18n("In"))
0357                         .arg(timeline.simplifiedTC(clip.inPoint))
0358                     timeline.showToolTip(s)
0359                     //bubbleHelp.show(clip.x - 20, trackRoot.y + trackRoot.height, s)
0360                 }
0361             }
0362             onTrimmedIn: (clip, shiftTrim, controlTrim) => {
0363                 //bubbleHelp.hide()
0364                 timeline.showToolTip();
0365                 if (shiftTrim || (root.groupTrimData == undefined/*TODO > */ || root.activeTool === ProjectTool.RippleTool /* < TODO*/) || controlTrim) {
0366                     // We only resize one element
0367                     if (root.activeTool === ProjectTool.RippleTool) {
0368                         timeline.requestItemRippleResize(clip.clipId, clip.originalDuration, false, false, 0, shiftTrim)
0369                     } else {
0370                         controller.requestItemResize(clip.clipId, clip.originalDuration, false, false, 0, shiftTrim)
0371                     }
0372 
0373                     if (root.activeTool === ProjectTool.SelectTool && controlTrim) {
0374                         // Update speed
0375                         speedController.visible = false
0376                         controller.requestClipResizeAndTimeWarp(clip.clipId, speedController.lastValidDuration, false, root.snapping, shiftTrim, clip.originalDuration * speedController.originalSpeed / speedController.lastValidDuration)
0377                         speedController.originalSpeed = 1
0378                     } else {
0379                         if (root.activeTool === ProjectTool.RippleTool) {
0380                             timeline.requestItemRippleResize(clip.clipId, clip.lastValidDuration, false, true, 0, shiftTrim)
0381                             timeline.requestEndTrimmingMode();
0382                         } else {
0383                             controller.requestItemResize(clip.clipId, clip.lastValidDuration, false, true, 0, shiftTrim)
0384                         }
0385                     }
0386                 } else {
0387                     var updatedGroupData = controller.getGroupData(clip.clipId)
0388                     controller.processGroupResize(root.groupTrimData, updatedGroupData, false)
0389                 }
0390                 root.groupTrimData = undefined
0391             }
0392             onTrimmingOut: (clip, newDuration, shiftTrim, controlTrim) => {
0393                 if (root.activeTool === ProjectTool.SelectTool && controlTrim) {
0394                     if (!speedController.visible) {
0395                         // Store original speed
0396                         speedController.originalSpeed = clip.speed
0397                     }
0398                     speedController.x = clip.x + clip.border.width
0399                     newDuration = controller.requestItemSpeedChange(clip.clipId, newDuration, true, root.snapping)
0400                     clip.width = newDuration * root.timeScale
0401                     speedController.width = Math.max(0, clip.width - 2 * clip.border.width)
0402                     speedController.lastValidDuration = newDuration
0403                     clip.speed = clip.originalDuration * speedController.originalSpeed / newDuration
0404                     speedController.visible = true
0405                     var s = '%1:%2\%, %3:%4'.arg(i18n("Speed"))
0406                         .arg(Math.round(clip.speed*100))
0407                         .arg(i18n("Duration"))
0408                         .arg(timeline.simplifiedTC(newDuration))
0409                     timeline.showToolTip(s)
0410                     return
0411                 }
0412                 var new_duration = 0;
0413                 if (root.activeTool === ProjectTool.RippleTool) {
0414                     console.log("Out: Request for " + newDuration)
0415                     new_duration = timeline.requestItemRippleResize(clip.clipId, newDuration, true, false, root.snapping, shiftTrim)
0416                     timeline.requestStartTrimmingMode(clip.clipId, false, true);
0417                     timeline.ripplePosChanged(new_duration, true);
0418                 } else {
0419                     new_duration = controller.requestItemResize(clip.clipId, newDuration, true, false, root.snapping, shiftTrim)
0420                 }
0421                 if (new_duration > 0) {
0422                     clip.lastValidDuration = new_duration
0423                     // Show amount trimmed as a time in a "bubble" help.
0424                     var delta = clip.originalDuration - new_duration
0425                     var s = timeline.simplifiedTC(Math.abs(delta))
0426                     s = '%1%2, %3:%4'.arg((delta <= 0)? '+' : '-')
0427                         .arg(s)
0428                         .arg(i18n("Duration"))
0429                         .arg(timeline.simplifiedTC(new_duration))
0430                     timeline.showToolTip(s);
0431                     //bubbleHelp.show(clip.x + clip.width - 20, trackRoot.y + trackRoot.height, s)
0432                 }
0433             }
0434             onTrimmedOut: (clip, shiftTrim, controlTrim) => {
0435                 timeline.showToolTip();
0436                 //bubbleHelp.hide()
0437                 if (shiftTrim || (root.groupTrimData == undefined/*TODO > */ || root.activeTool === ProjectTool.RippleTool /* < TODO*/) || controlTrim) {
0438                     if (root.activeTool === ProjectTool.RippleTool) {
0439                         timeline.requestItemRippleResize(clip.clipId, clip.originalDuration, true, false, 0, shiftTrim)
0440                     } else {
0441                         controller.requestItemResize(clip.clipId, clip.originalDuration, true, false, 0, shiftTrim)
0442                     }
0443 
0444                     if (root.activeTool === ProjectTool.SelectTool && controlTrim) {
0445                         speedController.visible = false
0446                         // Update speed
0447                         controller.requestClipResizeAndTimeWarp(clip.clipId, speedController.lastValidDuration, true, root.snapping, shiftTrim, clip.originalDuration * speedController.originalSpeed / speedController.lastValidDuration)
0448                         speedController.originalSpeed = 1
0449                     } else {
0450                         if (root.activeTool === ProjectTool.RippleTool) {
0451                             timeline.requestItemRippleResize(clip.clipId, clip.lastValidDuration, true, true, 0, shiftTrim)
0452                             timeline.requestEndTrimmingMode();
0453                         } else {
0454                             controller.requestItemResize(clip.clipId, clip.lastValidDuration, true, true, 0, shiftTrim)
0455                         }
0456                     }
0457                 } else {
0458                     var updatedGroupData = controller.getGroupData(clip.clipId)
0459                     controller.processGroupResize(root.groupTrimData, updatedGroupData, true)
0460                 }
0461                 root.groupTrimData = undefined
0462             }
0463         }
0464     }
0465     Component {
0466         id: compositionDelegate
0467         Composition {
0468             displayHeight: Math.max(trackRoot.height / 2, trackRoot.height - (root.baseUnit * 2))
0469             opacity: 0.8
0470             selected: root.timelineSelection.indexOf(clipId) != -1
0471             onTrimmingIn: (clip, newDuration) => {
0472                 var new_duration = controller.requestItemResize(clip.clipId, newDuration, false, false, root.snapping)
0473                 if (new_duration > 0) {
0474                     clip.lastValidDuration = newDuration
0475                     clip.originalX = clip.draggedX
0476                     // Show amount trimmed as a time in a "bubble" help.
0477                     var delta = clip.originalDuration - new_duration
0478                     var s = timeline.simplifiedTC(Math.abs(delta))
0479                     s = i18n("%1%2, Duration = %3", ((delta <= 0)? '+' : '-')
0480                         , s, timeline.simplifiedTC(new_duration))
0481                     timeline.showToolTip(s)
0482                 }
0483             }
0484             onTrimmedIn: clip => {
0485                 timeline.showToolTip()
0486                 //bubbleHelp.hide()
0487                 controller.requestItemResize(clip.clipId, clip.originalDuration, false, false, root.snapping)
0488                 controller.requestItemResize(clip.clipId, clip.lastValidDuration, false, true, root.snapping)
0489             }
0490             onTrimmingOut: (clip, newDuration) => {
0491                 var new_duration = controller.requestItemResize(clip.clipId, newDuration, true, false, root.snapping)
0492                 if (new_duration > 0) {
0493                     clip.lastValidDuration = newDuration
0494                     // Show amount trimmed as a time in a "bubble" help.
0495                     var delta = clip.originalDuration - new_duration
0496                     var s = timeline.simplifiedTC(Math.abs(delta))
0497                     s = i18n("%1%2, Duration = %3", ((delta <= 0)? '+' : '-')
0498                         , s, timeline.simplifiedTC(new_duration))
0499                     timeline.showToolTip(s)
0500                 }
0501             }
0502             onTrimmedOut: clip => {
0503                 timeline.showToolTip()
0504                 //bubbleHelp.hide()
0505                 controller.requestItemResize(clip.clipId, clip.originalDuration, true, false, root.snapping)
0506                 controller.requestItemResize(clip.clipId, clip.lastValidDuration, true, true, root.snapping)
0507             }
0508         }
0509     }
0510     Rectangle {
0511         id: speedController
0512         anchors.bottom: parent.bottom
0513         color: activePalette.highlight //'#cccc0000'
0514         visible: false
0515         clip: true
0516         height: root.baseUnit * 1.5
0517         property int lastValidDuration: 0
0518         property real originalSpeed: 1
0519         Text {
0520             id: speedLabel
0521             text: i18n("Adjusting speed")
0522             font: miniFont
0523             anchors.fill: parent
0524             verticalAlignment: Text.AlignVCenter
0525             horizontalAlignment: Text.AlignHCenter
0526             color: activePalette.highlightedText
0527         }
0528         transitions: [ Transition {
0529             NumberAnimation { property: "opacity"; duration: 300}
0530         } ]
0531     }
0532     Repeater {
0533         model: effectZones
0534         Rectangle {
0535             x: effectZones[index].x * timeline.scaleFactor
0536             height: 2
0537             width: (effectZones[index].y - effectZones[index].x) * timeline.scaleFactor
0538             color: 'blueviolet'
0539             opacity: 1
0540             anchors.top: parent.top
0541         }
0542     }
0543 }