Warning, /multimedia/kdenlive/src/timeline2/view/qml/Clip.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 QtQuick.Controls 2.15
0010 import Kdenlive.Controls 1.0
0011 import QtQuick.Window 2.15
0012 import QtQml.Models 2.15
0013 import QtQml 2.15
0014 
0015 import 'Timeline.js' as Logic
0016 import com.enums 1.0
0017 
0018 Rectangle {
0019     id: clipRoot
0020     property real timeScale: 1
0021     property string clipName: ''
0022     property string tagColor: ''
0023     property string clipResource: ''
0024     property string mltService: ''
0025     property string effectNames
0026     property bool isStackEnabled
0027     property int modelStart
0028     property int mixDuration: 0
0029     property int mixCut: 0
0030     property int inPoint: 0
0031     property int outPoint: 0
0032     property int clipDuration: 0
0033     property int maxDuration: 0
0034     property bool isAudio: false
0035     property bool timeremap: false
0036     property int audioChannels
0037     property int audioStream: -1
0038     property bool multiStream: false
0039     property int aStreamIndex: 0
0040     property bool showKeyframes: false
0041     property bool isGrabbed: false
0042     property bool grouped: false
0043     property var markers
0044     property var keyframeModel
0045     property int clipState: 0
0046     property int clipStatus: 0
0047     property int itemType: 0
0048     property int fadeIn: 0
0049     property int fadeOut: 0
0050     property int binId: 0
0051     property int positionOffset: 0
0052     property var parentTrack
0053     property int trackIndex //Index in track repeater
0054     property int clipId: -1     //Id of the clip in the model
0055     property int trackId: -1 // Id of the parent track in the model
0056     property int fakeTid: -1
0057     property int fakePosition: 0
0058     property int originalTrackId: -1
0059     property int originalX: x
0060     property int originalDuration: clipDuration
0061     property int lastValidDuration: clipDuration
0062     property int draggedX: x
0063     property double xIntegerOffset: 0
0064     property bool selected: false
0065     property bool isLocked: parentTrack && parentTrack.isLocked === true
0066     property bool hasAudio
0067     property bool canBeAudio
0068     property bool canBeVideo
0069     property double speed: 1.0
0070     property color borderColor: "#000000"
0071     property string clipThumbId
0072     property bool forceReloadAudioThumb
0073     property bool isComposition: false
0074     property int slipOffset: boundValue(outPoint - maxDuration + 1, trimmingOffset, inPoint)
0075     property int scrollStart: scrollView.contentX - (clipRoot.modelStart * root.timeScale)
0076     visible: scrollView.width + clipRoot.scrollStart >= 0 && clipRoot.scrollStart < clipRoot.width
0077     property bool hideClipViews: !visible || clipRoot.width < root.minClipWidthForViews
0078     property int mouseXPos: mouseArea.mouseX
0079     width : Math.round(clipDuration * timeScale)
0080     opacity: dragProxyArea.drag.active && dragProxy.draggedItem == clipId ? 0.8 : 1.0
0081     enabled: !clipDropArea.containsDrag
0082 
0083     signal trimmingIn(var clip, real newDuration, bool shiftTrim, bool controlTrim)
0084     signal trimmedIn(var clip, bool shiftTrim, bool controlTrim)
0085     signal initGroupTrim(int clipId)
0086     signal trimmingOut(var clip, real newDuration, bool shiftTrim, bool controlTrim)
0087     signal trimmedOut(var clip, bool shiftTrim, bool controlTrim)
0088 
0089     onVisibleChanged: {
0090         if (clipRoot.visible) {
0091             updateLabelOffset()
0092         }
0093     }
0094 
0095     onScrollStartChanged: {
0096         if (!clipRoot.visible) {
0097             return
0098         }
0099         updateLabelOffset()
0100         if (isAudio && thumbsLoader.item) {
0101             thumbsLoader.item.reload(1)
0102         }
0103         if (!clipRoot.hideClipViews && clipRoot.width > scrollView.width) {
0104             if (effectRow.item && effectRow.item.kfrCanvas) {
0105                 effectRow.item.kfrCanvas.requestPaint()
0106             }
0107         }
0108     }
0109 
0110     onIsGrabbedChanged: {
0111         if (clipRoot.isGrabbed) {
0112             grabItem()
0113         } else {
0114             timeline.showToolTip()
0115             mouseArea.focus = false
0116         }
0117     }
0118 
0119     function itemHeight() {
0120         return clipRoot.height
0121     }
0122 
0123 
0124     function boundValue(min, val, max) {
0125         return Math.max(min, Math.min(val, max))
0126     }
0127 
0128     function grabItem() {
0129         clipRoot.forceActiveFocus()
0130         mouseArea.focus = true
0131     }
0132 
0133     function resetSelection() {
0134         if (effectRow.visible) {
0135             effectRow.item.resetSelection()
0136         }
0137     }
0138 
0139     function clearAndMove(offset) {
0140         controller.requestClearSelection()
0141         controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true)
0142         controller.requestAddToSelection(clipRoot.clipId)
0143     }
0144 
0145     function doesContainMouse(pnt) {
0146         return pnt.x > 0.5 && pnt.x < clipRoot.width
0147     }
0148 
0149     onClipResourceChanged: {
0150         if (itemType === ProducerType.Color) {
0151             color: Qt.darker(getColor(), 1.5)
0152         }
0153     }
0154 
0155     onClipDurationChanged: {
0156         width = clipDuration * timeScale
0157         if (parentTrack && parentTrack.isAudio && thumbsLoader.item) {
0158             // Duration changed, we may need a different number of repeaters
0159             thumbsLoader.item.reload(1)
0160         }
0161     }
0162 
0163     onModelStartChanged: {
0164         x = modelStart * timeScale
0165         xIntegerOffset = Math.ceil(x) - x
0166     }
0167 
0168     onFakePositionChanged: {
0169         x = fakePosition * timeScale
0170         xIntegerOffset = Math.ceil(x) - x
0171     }
0172     onFakeTidChanged: {
0173         if (clipRoot.fakeTid > -1 && parentTrack) {
0174             if (clipRoot.parent != dragContainer) {
0175                 var pos = clipRoot.mapToGlobal(clipRoot.x, clipRoot.y);
0176                 clipRoot.parent = dragContainer
0177                 pos = clipRoot.mapFromGlobal(pos.x, pos.y)
0178                 clipRoot.x = pos.x
0179                 clipRoot.y = pos.y
0180             }
0181             clipRoot.y = Logic.getTrackById(clipRoot.fakeTid).y
0182             clipRoot.height = Logic.getTrackById(clipRoot.fakeTid).height
0183         } else {
0184             clipRoot.height = Qt.binding(function () {
0185                 return parentTrack.height
0186             })
0187         }
0188     }
0189 
0190     onForceReloadAudioThumbChanged: {
0191         // TODO: find a way to force reload of clip thumbs
0192         if (!isAudio) {
0193             return;
0194         }
0195         if (thumbsLoader.item) {
0196             thumbsLoader.item.reload(0)
0197         }
0198     }
0199 
0200     onTimeScaleChanged: {
0201         x = modelStart * clipRoot.timeScale;
0202         xIntegerOffset = Math.ceil(x) - x
0203         width = clipDuration * clipRoot.timeScale;
0204         if (clipRoot.visible) {
0205             if (!clipRoot.hideClipViews) {
0206                 if (effectRow.item && effectRow.item.kfrCanvas) {
0207                     effectRow.item.kfrCanvas.requestPaint()
0208                 }
0209             }
0210         }
0211     }
0212     
0213     function updateLabelOffset()
0214     {
0215         nameContainer.anchors.leftMargin = clipRoot.scrollStart > 0 ? (mixContainer.width + labelRect.width > clipRoot.width ? mixContainer.width : Math.max(clipRoot.scrollStart, mixContainer.width + mixBackground.border.width)) : mixContainer.width + mixBackground.border.width
0216     }
0217 
0218     /*border.color: (clipStatus === ClipStatus.StatusMissing || ClipStatus === ClipStatus.StatusWaiting || clipStatus === ClipStatus.StatusDeleting) ? "#ff0000" : selected ? root.selectionColor : grouped ? root.groupColor : borderColor
0219     border.width: isGrabbed ? 8 : 2*/
0220 
0221     function updateDrag() {
0222         var itemPos = mapToItem(tracksContainerArea, 0, 0, clipRoot.width, clipRoot.height)
0223         initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
0224     }
0225     
0226     function showClipInfo() {
0227         var text = i18n("%1 (%2-%3), Position: %4, Duration: %5".arg(clipRoot.clipName)
0228                         .arg(timeline.simplifiedTC(clipRoot.inPoint))
0229                         .arg(timeline.simplifiedTC(clipRoot.outPoint))
0230                         .arg(timeline.simplifiedTC(clipRoot.modelStart))
0231                         .arg(timeline.simplifiedTC(clipRoot.clipDuration)))
0232         timeline.showToolTip(text)
0233     }
0234 
0235     function getColor() {
0236         if (clipRoot.clipState === ClipState.Disabled) {
0237             return '#888'
0238         }
0239         if (clipRoot.tagColor) {
0240             return clipRoot.tagColor
0241         }
0242         if (itemType === ProducerType.Text) {
0243             return titleColor
0244         }
0245         if (itemType === ProducerType.Image) {
0246             return imageColor
0247         }
0248         if (itemType === ProducerType.SlideShow) {
0249             return slideshowColor
0250         }
0251         if (itemType === ProducerType.Color) {
0252             var color = clipResource.substring(clipResource.length - 9)
0253             if (color[0] === '#') {
0254                 return color
0255             }
0256             return '#' + color.substring(color.length - 8, color.length - 2)
0257         }
0258         return isAudio? root.audioColor : root.videoColor
0259     }
0260 
0261 /*    function reparent(track) {
0262         console.log('TrackId: ',trackId)
0263         parent = track
0264         height = track.height
0265         parentTrack = track
0266         trackId = parentTrack.trackId
0267         console.log('Reparenting clip to Track: ', trackId)
0268         //generateWaveform()
0269     }
0270 */
0271     property bool noThumbs: (isAudio || itemType === ProducerType.Color || mltService === '')
0272     property string baseThumbPath: noThumbs ? '' : 'image://thumbnail/' + clipThumbId
0273 
0274     DropArea { //Drop area for clips
0275         anchors.fill: clipRoot
0276         keys: 'kdenlive/effect'
0277         property string dropData
0278         property string dropSource
0279         property int dropRow: -1
0280         onEntered: drag => {
0281             dropData = drag.getDataAsString('kdenlive/effect')
0282             dropSource = drag.getDataAsString('kdenlive/effectsource')
0283             updateDrag()
0284         }
0285         onDropped: drag => {
0286             console.log("Add effect: ", dropData)
0287             if (dropSource == '') {
0288                 // drop from effects list
0289                 controller.addClipEffect(clipRoot.clipId, dropData)
0290                 if (proxy.seekOnDrop() && (proxy.position < clipRoot.modelStart || proxy.position > clipRoot.modelStart + clipRoot.clipDuration)) {
0291                     // If timeline cursor is not inside clip, seek to drop position
0292                     proxy.position = clipRoot.modelStart + drag.x / timeScale
0293                 }
0294             } else {
0295                 controller.copyClipEffect(clipRoot.clipId, dropSource)
0296             }
0297             dropSource = ''
0298             dropRow = -1
0299             drag.acceptProposedAction
0300             root.regainFocus(mapToItem(root, drag.x, drag.y))
0301             //console.log('KFR VIEW VISIBLE: ', effectRow.visible, ', SOURCE: ', effectRow.source, '\n HIDEVIEW:', clipRoot.hideClipViews<<', UNDEFINED: ', (clipRoot.keyframeModel == undefined))
0302         }
0303         onExited: {
0304             root.endDrag()
0305         }
0306     }
0307     MouseArea {
0308         id: mouseArea
0309         enabled: root.activeTool === ProjectTool.SelectTool || root.activeTool === ProjectTool.SlipTool || root.activeTool === ProjectTool.RippleTool
0310         anchors.fill: clipRoot
0311         acceptedButtons: Qt.RightButton
0312         hoverEnabled: root.activeTool === ProjectTool.SelectTool || root.activeTool === ProjectTool.RippleTool
0313         cursorShape: (trimInMouseArea.drag.active || trimOutMouseArea.drag.active)? Qt.SizeHorCursor : dragProxyArea.cursorShape
0314         property bool shiftSlip: false
0315         property bool controlSlip: false
0316         onPressed: mouse => {
0317             root.autoScrolling = false
0318             root.mainItemId = clipRoot.clipId
0319             if (mouse.button == Qt.RightButton) {
0320                 if (timeline.selection.indexOf(clipRoot.clipId) === -1) {
0321                     controller.requestAddToSelection(clipRoot.clipId, true)
0322                 }
0323                 root.clickFrame = Math.round(mouse.x / timeline.scaleFactor)
0324                 root.showClipMenu(clipRoot.clipId)
0325                 root.autoScrolling = timeline.autoScroll
0326             }
0327         }
0328         onReleased: {
0329             root.autoScrolling = timeline.autoScroll
0330         }
0331         Keys.onShortcutOverride: event => {event.accepted = clipRoot.isGrabbed && (event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Up || event.key === Qt.Key_Down || event.key === Qt.Key_Escape)}
0332         Keys.onLeftPressed: event => {
0333             var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
0334             while((clipRoot.modelStart >= offset) && !controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true)) {
0335                 offset++;
0336             }
0337             timeline.showToolTip(i18n("Position: %1", timeline.simplifiedTC(clipRoot.modelStart)));
0338         }
0339         Keys.onRightPressed: event => {
0340             var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
0341             while(!controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart + offset, true, true, true)) {
0342                 offset++;
0343             }
0344             timeline.showToolTip(i18n("Position: %1", timeline.simplifiedTC(clipRoot.modelStart)));
0345         }
0346         Keys.onUpPressed: {
0347             var nextTrack = controller.getNextTrackId(clipRoot.trackId);
0348             while(!controller.requestClipMove(clipRoot.clipId, nextTrack, clipRoot.modelStart, true, true, true) && nextTrack !== controller.getNextTrackId(nextTrack)) {
0349                 nextTrack = controller.getNextTrackId(nextTrack);
0350             }
0351         }
0352         Keys.onDownPressed: {
0353             var previousTrack = controller.getPreviousTrackId(clipRoot.trackId);
0354             while(!controller.requestClipMove(clipRoot.clipId, previousTrack, clipRoot.modelStart, true, true, true) && previousTrack !== controller.getPreviousTrackId(previousTrack)) {
0355                 previousTrack = controller.getPreviousTrackId(previousTrack);
0356             }
0357         }
0358         Keys.onEscapePressed: {
0359             timeline.grabCurrent()
0360             //focus = false
0361         }
0362         onPositionChanged: mouse => {
0363             var mapped = parentTrack.mapFromItem(clipRoot, mouse.x, mouse.y).x
0364             root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
0365         }
0366         onEntered: {
0367             if (clipRoot.clipId > -1) {
0368                 var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
0369                 initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
0370             }
0371             showClipInfo()
0372         }
0373 
0374         onExited: {
0375             if (pressed) {
0376                 root.endDrag()
0377                 if (!trimInMouseArea.containsMouse && !trimOutMouseArea.containsMouse && !compInArea.containsMouse && !compOutArea.containsMouse) {
0378                     timeline.showToolTip()
0379                 }
0380             }
0381         }
0382         onWheel: wheel => zoomByWheel(wheel)
0383 
0384         Loader {
0385             // Thumbs container
0386             id: thumbsLoader
0387             anchors.fill: parent
0388             anchors.leftMargin: parentTrack.isAudio ? xIntegerOffset : itemBorder.border.width + mixContainer.width
0389             anchors.rightMargin: parentTrack.isAudio ? clipRoot.width - Math.floor(clipRoot.width) : itemBorder.border.width
0390             anchors.topMargin: itemBorder.border.width
0391             anchors.bottomMargin: itemBorder.border.width
0392             //clip: true
0393             asynchronous: true
0394             visible: status == Loader.Ready
0395             source: (clipRoot.hideClipViews || clipRoot.itemType == 0 || clipRoot.itemType === ProducerType.Color) ? "" : parentTrack.isAudio ? (timeline.showAudioThumbnails ? "ClipAudioThumbs.qml" : "") : timeline.showThumbnails ? "ClipThumbs.qml" : ""
0396             onStatusChanged: {
0397                 if (!parentTrack.isAudio && thumbsLoader.item) {
0398                     thumbsLoader.item.initialSpeed = clipRoot.speed
0399                 }
0400             }
0401         }
0402 
0403         Rectangle {
0404             // Border rectangle
0405             color: 'transparent'
0406             id: itemBorder
0407             anchors.fill: parent
0408             border.color: (clipStatus === ClipStatus.StatusMissing || ClipStatus === ClipStatus.StatusWaiting || clipStatus === ClipStatus.StatusDeleting) ? "#ff0000" : clipRoot.selected ? root.selectionColor : grouped ? root.groupColor : borderColor
0409             border.width: isGrabbed ? 8 : 2
0410         }
0411 
0412         Item {
0413             // Clipping container
0414             id: container
0415             anchors.fill: parent
0416             anchors.margins: itemBorder.border.width
0417             //clip: true
0418             property bool showDetails: (!clipRoot.selected || !effectRow.visible) && container.height > 2.2 * labelRect.height
0419             property bool handleVisible: clipRoot.width - width > 3 * root.baseUnit / 2 || width > 3 * root.baseUnit / 2
0420             
0421             Item {
0422                 // Mix indicator
0423                 id: mixContainer
0424                 anchors.left: parent.left
0425                 anchors.top: parent.top
0426                 anchors.bottom: parent.bottom
0427                 width: clipRoot.mixDuration * root.timeScale
0428                 onWidthChanged: {
0429                     if (clipRoot.visible) {
0430                         updateLabelOffset()
0431                     }
0432                 }
0433                 
0434                 Rectangle {
0435                     id: mixBackground
0436                     property double mixPos: mixBackground.width - clipRoot.mixCut * clipRoot.timeScale
0437                     property bool mixSelected: root.selectedMix == clipRoot.clipId
0438                     anchors.top: parent.top
0439                     anchors.bottom: parent.bottom
0440                     anchors.left: parent.left
0441                     anchors.right: parent.right
0442                     visible: clipRoot.mixDuration > 0
0443                     color: mixSelected ? root.selectionColor : "mediumpurple"
0444                     Loader {
0445                         id: shapeLoader
0446                         source: clipRoot.mixDuration > 0 ? "MixShape.qml" : ""
0447                         property bool valid: item !== null
0448                     }
0449 
0450                     opacity: mixArea.containsMouse || trimInMixArea.pressed || trimInMixArea.containsMouse || mixSelected ? 1 : 0.7
0451                     border.color: mixSelected ? root.selectionColor : "transparent"
0452                     border.width: clipRoot.mixDuration > 0 ? 2 : 0
0453                     MouseArea {
0454                         // Mix click mouse area
0455                         id: mixArea
0456                         anchors.fill: parent
0457                         hoverEnabled: true
0458                         cursorShape: Qt.PointingHandCursor
0459                         acceptedButtons: Qt.RightButton | Qt.LeftButton
0460                         enabled: container.handleVisible && width > root.baseUnit * 0.8
0461                         onPressed: mouse => {
0462                             controller.requestMixSelection(clipRoot.clipId);
0463                             root.autoScrolling = false
0464                             if (mouse.button == Qt.RightButton) {
0465                                 root.clickFrame = Math.round(mouse.x / timeline.scaleFactor)
0466                                 root.showMixMenu(clipRoot.clipId)
0467                                 root.autoScrolling = timeline.autoScroll
0468                             }
0469                         }
0470                         onEntered: {
0471                             var text = i18n("Mix duration: %1, Cut at: %2".arg(timeline.simplifiedTC(clipRoot.mixDuration))
0472                             .arg(timeline.simplifiedTC(clipRoot.mixDuration - clipRoot.mixCut)))
0473                             timeline.showToolTip(text)
0474                         }
0475                     }
0476                     Rectangle {
0477                         id: mixCutPos
0478                         anchors.right: parent.right
0479                         anchors.rightMargin: clipRoot.mixCut * clipRoot.timeScale
0480                         anchors.top: parent.top
0481                         anchors.bottom: parent.bottom
0482                         width: 2
0483                         color: "navy"
0484                     }
0485                     MouseArea {
0486                         // Right mix resize handle
0487                         id: trimInMixArea
0488                         anchors.left: parent.left
0489                         anchors.leftMargin: clipRoot.mixDuration * clipRoot.timeScale
0490                         height: parent.height
0491                         width: root.baseUnit / 2
0492                         visible: root.activeTool === ProjectTool.SelectTool
0493                         property int previousMix
0494                         enabled: !isLocked && mixArea.enabled && (pressed || container.handleVisible)
0495                         hoverEnabled: true
0496                         drag.target: trimInMixArea
0497                         drag.axis: Drag.XAxis
0498                         drag.smoothed: false
0499                         drag.maximumX: clipRoot.width
0500                         drag.minimumX: (clipRoot.mixDuration - clipRoot.mixCut) * clipRoot.timeScale
0501                         property bool sizeChanged: false
0502                         cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor)
0503                         onPressed: {
0504                             root.trimInProgress = true;
0505                             previousMix = clipRoot.mixDuration
0506                             root.autoScrolling = false
0507                             mixOut.color = 'red'
0508                             anchors.left = undefined
0509                             parent.anchors.right = undefined
0510                             mixCutPos.anchors.right = undefined
0511                         }
0512                         onReleased: mouse => {
0513                             root.autoScrolling = timeline.autoScroll
0514                             if (sizeChanged) {
0515                                 controller.resizeStartMix(clipRoot.clipId, Math.round(Math.max(0, x) / clipRoot.timeScale), mouse.modifiers & Qt.ShiftModifier)
0516                                 sizeChanged = false
0517                             }
0518                             anchors.left = parent.left
0519                             parent.anchors.right = mixContainer.right
0520                             mixBackground.anchors.bottom = mixContainer.bottom
0521                             mixOut.color = itemBorder.border.color
0522                             mixCutPos.anchors.right = mixCutPos.parent.right
0523                             root.trimInProgress = false;
0524                         }
0525                         onPositionChanged: mouse => {
0526                             if (mouse.buttons === Qt.LeftButton) {
0527                                 var currentFrame = Math.round(x / clipRoot.timeScale)
0528                                 if (currentFrame != previousMix) {
0529                                     parent.width = currentFrame * clipRoot.timeScale
0530                                     sizeChanged = true
0531                                     if (currentFrame > previousMix) {
0532                                         timeline.showToolTip(i18n("+%1, Mix duration: %2", timeline.simplifiedTC(currentFrame - previousMix), timeline.simplifiedTC(currentFrame)))
0533                                     } else {
0534                                         timeline.showToolTip(i18n("-%1, Mix duration: %2", timeline.simplifiedTC(previousMix - currentFrame), timeline.simplifiedTC(currentFrame)))
0535                                     }
0536                                 } else {
0537                                     timeline.showToolTip(i18n("Mix duration: %1", timeline.simplifiedTC(currentFrame)))
0538                                 }
0539                                 if (x < mixCutPos.x) {
0540                                     // This will delete the mix
0541                                     mixBackground.anchors.bottom = mixContainer.top
0542                                 } else {
0543                                     mixBackground.anchors.bottom = mixContainer.bottom
0544                                 }
0545                             }
0546                         }
0547                         onEntered: {
0548                             if (!pressed) {
0549                                 mixOut.color = 'red'
0550                                 timeline.showToolTip(i18n("Mix duration: %1", timeline.simplifiedTC(clipRoot.mixDuration)))
0551                             }
0552                         }
0553                         onExited: {
0554                             if (!pressed) {
0555                                 mixOut.color = itemBorder.border.color
0556                                 if (!mouseArea.containsMouse) {
0557                                     timeline.showToolTip()
0558                                 } else {
0559                                     clipRoot.showClipInfo()
0560                                 }
0561                             }
0562                         }
0563                         Rectangle {
0564                             id: mixOut
0565                             width: itemBorder.border.width
0566                             height: mixContainer.height
0567                             color: itemBorder.border.color
0568                             Drag.active: trimInMixArea.drag.active
0569                             Drag.proposedAction: Qt.MoveAction
0570                             visible: trimInMixArea.pressed || (root.activeTool === ProjectTool.SelectTool && !mouseArea.drag.active && parent.enabled)
0571                         }
0572                     }
0573                 }
0574                 
0575             }
0576 
0577             Repeater {
0578                 // Clip markers
0579                 model: markers
0580                 delegate:
0581                 Item {
0582                     visible: markerBase.x >= 0 && markerBase.x < clipRoot.width
0583                     Rectangle {
0584                         id: markerBase
0585                         width: 1
0586                         height: container.height
0587                         x: clipRoot.speed < 0
0588                            ? (clipRoot.maxDuration - clipRoot.inPoint) * clipRoot.timeScale + (Math.round(model.frame / clipRoot.speed)) * clipRoot.timeScale - itemBorder.border.width
0589                            : (Math.round(model.frame / clipRoot.speed) - clipRoot.inPoint) * clipRoot.timeScale - itemBorder.border.width;
0590                         color: model.color
0591                         ToolTip.visible: markerArea.containsMouse
0592                         ToolTip.text: textMetrics.text
0593                         ToolTip.delay: 1000
0594                         ToolTip.timeout: 5000
0595                     }
0596                     Rectangle {
0597                         visible: mlabel.visible
0598                         opacity: 0.7
0599                         x: markerBase.x
0600                         radius: 2
0601                         width: mlabel.width + 4
0602                         height: mlabel.height
0603                         y: mlabel.y
0604                         color: model.color
0605                         MouseArea {
0606                             z: 10
0607                             id: markerArea
0608                             anchors.fill: parent
0609                             acceptedButtons: Qt.LeftButton
0610                             cursorShape: Qt.PointingHandCursor
0611                             hoverEnabled: true
0612                             onDoubleClicked: timeline.editMarker(clipRoot.clipId, model.frame)
0613                             onClicked: proxy.position = clipRoot.modelStart + (clipRoot.speed < 0
0614                                                                                ? (clipRoot.maxDuration - clipRoot.inPoint) * clipRoot.timeScale + (Math.round(model.frame / clipRoot.speed))
0615                                                                                : (Math.round(model.frame / clipRoot.speed) - clipRoot.inPoint))
0616                         }
0617                     }
0618                     TextMetrics {
0619                         id: textMetrics
0620                         font: miniFont
0621                         text: model.comment
0622                         elide: clipRoot.timeScale > 1 ? Text.ElideNone : Text.ElideRight
0623                         elideWidth: root.maxLabelWidth
0624                     }
0625                     Text {
0626                         id: mlabel
0627                         visible: timeline.showMarkers && textMetrics.elideWidth > root.baseUnit && height < container.height && (markerBase.x > mlabel.width || container.height > 2 * height)
0628                         text: textMetrics.elidedText
0629                         font: miniFont
0630                         x: markerBase.x + 1
0631                         y: Math.min(label.height, container.height - height)
0632                         color: 'white'
0633                     }
0634                 }
0635             }
0636 
0637             MouseArea {
0638                 // Left resize handle
0639                 id: trimInMouseArea
0640                 x: -itemBorder.border.width
0641                 height: parent.height
0642                 width: root.baseUnit / 2
0643                 visible: enabled && (root.activeTool === ProjectTool.SelectTool || (root.activeTool === ProjectTool.RippleTool && clipRoot.mixDuration <= 0 && !controller.hasClipEndMix(clipRoot.clipId)))
0644                 enabled: !isLocked && (pressed || (container.handleVisible && (mixArea.enabled || clipRoot.mixDuration == 0))) && clipRoot.clipId == dragProxy.draggedItem
0645                 hoverEnabled: true
0646                 drag.target: trimInMouseArea
0647                 drag.axis: Drag.XAxis
0648                 drag.smoothed: false
0649                 property bool shiftTrim: false
0650                 property bool controlTrim: false
0651                 property bool sizeChanged: false
0652                 cursorShape: (enabled && (containsMouse || pressed) ? Qt.SizeHorCursor : Qt.OpenHandCursor)
0653                 onPressed: mouse => {
0654                     root.autoScrolling = false
0655                     root.trimInProgress = true;
0656                     clipRoot.originalX = clipRoot.x
0657                     clipRoot.originalDuration = clipDuration
0658                     shiftTrim = mouse.modifiers & Qt.ShiftModifier
0659                     controlTrim = mouse.modifiers & Qt.ControlModifier && itemType != ProducerType.Color && itemType != ProducerType.Timeline && itemType != ProducerType.Playlist && itemType != ProducerType.Image
0660                     if (!shiftTrim && (clipRoot.grouped || controller.hasMultipleSelection())) {
0661                         clipRoot.initGroupTrim(clipRoot.clipId)
0662                     }
0663                     if (root.activeTool === ProjectTool.RippleTool) {
0664                         timeline.requestStartTrimmingMode(clipRoot.clipId, false, false);
0665                     }
0666                     trimIn.opacity = 0
0667                 }
0668                 onReleased: {
0669                     root.autoScrolling = timeline.autoScroll
0670                     x = -itemBorder.border.width
0671                     if (sizeChanged) {
0672                         clipRoot.trimmedIn(clipRoot, shiftTrim, controlTrim)
0673                         sizeChanged = false
0674                         if (!controlTrim && root.activeTool !== ProjectTool.RippleTool) {
0675                             updateDrag()
0676                         } else {
0677                             root.endDrag()
0678                         }
0679                     } else {
0680                         if (root.activeTool === ProjectTool.RippleTool) {
0681                             timeline.requestEndTrimmingMode();
0682                         }
0683                         root.groupTrimData = undefined
0684                     }
0685                     root.trimInProgress = false;
0686                 }
0687                 onDoubleClicked: {
0688                     if (clipRoot.mixDuration == 0) {
0689                         timeline.mixClip(clipRoot.clipId, -1)
0690                     }
0691                 }
0692                 onPositionChanged: mouse => {
0693                     if (mouse.buttons === Qt.LeftButton) {
0694                         var currentFrame = Math.round((clipRoot.x + (x + itemBorder.border.width)) / clipRoot.timeScale)
0695                         var currentClipPos = clipRoot.modelStart
0696                         var delta = currentFrame - currentClipPos
0697                         if (delta !== 0) {
0698                             if (delta > 0 && (clipRoot.mixDuration > 0 && clipRoot.mixDuration - clipRoot.mixCut - delta < (clipRoot.mixCut == 0 ? 1 : 0))) {
0699                                 if (clipRoot.mixCut == 0 && clipRoot.mixDuration > 1) {
0700                                     delta = clipRoot.mixDuration - clipRoot.mixCut - 1
0701                                 } else if (clipRoot.mixCut > 0 && clipRoot.mixDuration > clipRoot.mixCut) {
0702                                     delta = clipRoot.mixDuration - clipRoot.mixCut
0703                                 } else {
0704                                     return
0705                                 }
0706                             }
0707                             var newDuration = 0;
0708                             if (root.activeTool === ProjectTool.RippleTool) {
0709                                 newDuration = clipRoot.originalDuration - delta
0710                             } else {
0711                                 if (maxDuration > 0 && delta < -inPoint && !(mouse.modifiers & Qt.ControlModifier)) {
0712                                     delta = -inPoint
0713                                 }
0714                                 newDuration = clipDuration - delta
0715                             }
0716                             sizeChanged = true
0717                             clipRoot.trimmingIn(clipRoot, newDuration, shiftTrim, controlTrim)
0718                         }
0719                     }
0720                 }
0721                 onEntered: {
0722                     if (!pressed && !root.isDragging()) {
0723                         trimIn.opacity = 1
0724                         var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
0725                         initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
0726                         var s = i18n("In:%1, Position:%2", timeline.simplifiedTC(clipRoot.inPoint),timeline.simplifiedTC(clipRoot.modelStart))
0727                         timeline.showToolTip(s)
0728                         if (clipRoot.mixDuration == 0) {
0729                             timeline.showKeyBinding(i18n("<b>Ctrl drag</b> to change speed, <b>Double click</b> to mix with adjacent clip"))
0730                         } else {
0731                             timeline.showKeyBinding(i18n("<b>Drag</b> to change mix duration"))
0732                         }
0733                     }
0734                 }
0735                 onExited: {
0736                     trimIn.opacity = 0
0737                     if (!pressed) {
0738                         if (!mouseArea.containsMouse) {
0739                             timeline.showToolTip()
0740                         } else {
0741                             clipRoot.showClipInfo()
0742                         }
0743                         if (!fadeInMouseArea.containsMouse) {
0744                             timeline.showKeyBinding()
0745                         }
0746                     }
0747                 }
0748                 Rectangle {
0749                     id: trimIn
0750                     anchors.left: parent.left
0751                     width: itemBorder.border.width
0752                     height: parent.height
0753                     color: 'lawngreen'
0754                     opacity: 0
0755                     Drag.active: trimInMouseArea.drag.active
0756                     Drag.proposedAction: Qt.MoveAction
0757                     visible: trimInMouseArea.pressed || ((root.activeTool === ProjectTool.SelectTool || (root.activeTool === ProjectTool.RippleTool && clipRoot.mixDuration <= 0)) && !mouseArea.drag.active && parent.enabled)
0758 
0759                     /*ToolTip {
0760                         visible: trimInMouseArea.containsMouse && !trimInMouseArea.pressed
0761                         delay: 1000
0762                         timeout: 5000
0763                         background: Rectangle {
0764                             color: activePalette.alternateBase
0765                             border.color: activePalette.light
0766                         }
0767                         contentItem: Label {
0768                             color: activePalette.text
0769                             font: miniFont
0770                             text: i18n("In:%1\nPosition:%2", timeline.simplifiedTC(clipRoot.inPoint),timeline.simplifiedTC(clipRoot.modelStart))
0771                         }
0772                     }*/
0773                 }
0774             }
0775 
0776             MouseArea {
0777                 // Right resize handle
0778                 id: trimOutMouseArea
0779                 anchors.right: parent.right
0780                 anchors.rightMargin: -itemBorder.border.width
0781                 anchors.top: parent.top
0782                 height: parent.height
0783                 width: root.baseUnit / 2
0784                 hoverEnabled: true
0785                 visible: enabled && (root.activeTool === ProjectTool.SelectTool || (root.activeTool === ProjectTool.RippleTool && clipRoot.mixDuration <= 0))
0786                 enabled: !isLocked && (pressed || container.handleVisible) && clipRoot.clipId == dragProxy.draggedItem
0787                 property bool shiftTrim: false
0788                 property bool controlTrim: false
0789                 property bool sizeChanged: false
0790                 cursorShape: (enabled && (containsMouse || pressed) ? Qt.SizeHorCursor : Qt.OpenHandCursor)
0791                 drag.target: trimOutMouseArea
0792                 drag.axis: Drag.XAxis
0793                 drag.smoothed: false
0794 
0795                 onPressed: mouse => {
0796                     root.autoScrolling = false
0797                     root.trimInProgress = true;
0798                     clipRoot.originalDuration = clipDuration
0799                     anchors.right = undefined
0800                     shiftTrim = mouse.modifiers & Qt.ShiftModifier
0801                     controlTrim = mouse.modifiers & Qt.ControlModifier && itemType != ProducerType.Color && itemType != ProducerType.Timeline && itemType != ProducerType.Playlist && itemType != ProducerType.Image
0802                     if (!shiftTrim && (clipRoot.grouped || controller.hasMultipleSelection())) {
0803                         clipRoot.initGroupTrim(clipRoot.clipId)
0804                     }
0805                     if (root.activeTool === ProjectTool.RippleTool) {
0806                         timeline.requestStartTrimmingMode(clipRoot.clipId, false, true);
0807                     }
0808 
0809                     trimOut.opacity = 0
0810                 }
0811                 onReleased: {
0812                     root.autoScrolling = timeline.autoScroll
0813                     anchors.right = parent.right
0814                     if (sizeChanged) {
0815                         clipRoot.trimmedOut(clipRoot, shiftTrim, controlTrim)
0816                         sizeChanged = false
0817                         if (!controlTrim && root.activeTool !== ProjectTool.RippleTool) {
0818                             updateDrag()
0819                         } else {
0820                             root.endDrag()
0821                         }
0822                     } else {
0823                         if (root.activeTool === ProjectTool.RippleTool) {
0824                             timeline.requestEndTrimmingMode();
0825                         }
0826                         root.groupTrimData = undefined
0827                     }
0828                     root.trimInProgress = false;
0829                 }
0830                 onDoubleClicked: {
0831                     timeline.mixClip(clipRoot.clipId, 1)
0832                 }
0833                 onPositionChanged: mouse => {
0834                     if (mouse.buttons === Qt.LeftButton) {
0835                         var newDuration = Math.round((x + width + itemBorder.border.width) / clipRoot.timeScale)
0836                         if (maxDuration > 0 && (newDuration > maxDuration - inPoint) && !(mouse.modifiers & Qt.ControlModifier)) {
0837                             newDuration = maxDuration - inPoint
0838                         }
0839                         if (newDuration != clipDuration) {
0840                             sizeChanged = true
0841                             clipRoot.trimmingOut(clipRoot, newDuration, shiftTrim, controlTrim)
0842                         }
0843                     }
0844                 }
0845                 onEntered: {
0846                     if (!pressed && !root.isDragging()) {
0847                         trimOut.opacity = 1
0848                         var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
0849                         initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
0850                         var s = i18n("Out:%1, Position:%2", timeline.simplifiedTC(clipRoot.outPoint),timeline.simplifiedTC(clipRoot.modelStart + clipRoot.clipDuration))
0851                         timeline.showToolTip(s)
0852                         if (!fadeOutMouseArea.containsMouse) {
0853                             timeline.showKeyBinding(i18n("<b>Ctrl drag</b> to change speed, <b>Double click</b> to mix with adjacent clip"))
0854                         }
0855                     }
0856                 }
0857                 onExited: {
0858                     trimOut.opacity = 0
0859                     if (!pressed) {
0860                          if (!mouseArea.containsMouse) {
0861                             timeline.showToolTip()
0862                          } else {
0863                              var text = i18n("%1 (%2-%3), Position: %4, Duration: %5".arg(clipRoot.clipName)
0864                         .arg(timeline.simplifiedTC(clipRoot.inPoint))
0865                         .arg(timeline.simplifiedTC(clipRoot.outPoint))
0866                         .arg(timeline.simplifiedTC(clipRoot.modelStart))
0867                         .arg(timeline.simplifiedTC(clipRoot.clipDuration)))
0868                              timeline.showToolTip(text)
0869                          }
0870                          if (!fadeOutMouseArea.containsMouse) {
0871                              timeline.showKeyBinding()
0872                          }
0873                     }
0874                 }
0875                 /*ToolTip {
0876                     visible: trimOutMouseArea.containsMouse && !trimOutMouseArea.pressed
0877                     delay: 1000
0878                     timeout: 5000
0879                     background: Rectangle {
0880                         color: activePalette.alternateBase
0881                         border.color: activePalette.light
0882                     }
0883                     contentItem: Label {
0884                         color: activePalette.text
0885                         font: miniFont
0886                         text: i18n("Out: ") + timeline.simplifiedTC(clipRoot.outPoint)
0887                     }
0888                 }*/
0889                 Rectangle {
0890                     id: trimOut
0891                     anchors.right: parent.right
0892                     width: itemBorder.border.width
0893                     height: parent.height
0894                     color: 'red'
0895                     opacity: 0
0896                     Drag.active: trimOutMouseArea.drag.active
0897                     Drag.proposedAction: Qt.MoveAction
0898                     visible: trimOutMouseArea.pressed || ((root.activeTool === ProjectTool.SelectTool || (root.activeTool === ProjectTool.RippleTool && clipRoot.mixDuration <= 0)) && !mouseArea.drag.active && parent.enabled)
0899                 }
0900             }
0901 
0902             TimelineTriangle {
0903                 // Green fade in triangle
0904                 id: fadeInTriangle
0905                 fillColor: 'green'
0906                 width: Math.min(clipRoot.fadeIn * clipRoot.timeScale, container.width)
0907                 height: parent.height
0908                 anchors.left: parent.left
0909                 anchors.top: parent.top
0910                 opacity: 0.4
0911             }
0912 
0913             TimelineTriangle {
0914                 // Red fade out triangle
0915                 id: fadeOutCanvas
0916                 fillColor: 'red'
0917                 width: Math.min(clipRoot.fadeOut * clipRoot.timeScale, container.width)
0918                 height: parent.height
0919                 anchors.right: parent.right
0920                 anchors.top: parent.top
0921                 opacity: 0.4
0922                 transform: Scale { xScale: -1; origin.x: fadeOutCanvas.width / 2}
0923             }
0924 
0925             Item {
0926                 // Clipping container for clip names
0927                 id: nameContainer
0928                 anchors.fill: parent
0929                 anchors.leftMargin: clipRoot.scrollStart > 0 ? (mixContainer.width + labelRect.width > clipRoot.width ? mixContainer.width : Math.max(clipRoot.scrollStart, mixContainer.width + mixBackground.border.width)) : mixContainer.width + mixBackground.border.width
0930                 clip: true
0931                 Rectangle {
0932                     // Debug: Clip Id background
0933                     id: debugCidRect
0934                     color: 'magenta'
0935                     width: debugCid.width + (2 * itemBorder.border.width)
0936                     height: debugCid.height
0937                     visible: root.debugmode
0938                     anchors.left: parent.left
0939                     anchors.leftMargin: clipRoot.timeremap ? debugCidRect.height : 0
0940                     Text {
0941                         // Clip ID text
0942                         id: debugCid
0943                         text: clipRoot.clipId
0944                         font: miniFont
0945                         anchors {
0946                             left: debugCidRect.left
0947                             leftMargin: itemBorder.border.width
0948                         }
0949                         color: 'white'
0950                     }
0951                 }
0952                 Rectangle {
0953                     // Clip name background
0954                     id: labelRect
0955                     color: clipRoot.selected ? 'darkred' : '#66000000'
0956                     width: label.width + (2 * itemBorder.border.width)
0957                     height: label.height
0958                     visible: clipRoot.width > root.baseUnit
0959                     anchors.left: debugCidRect.visible ? debugCidRect.right : parent.left
0960                     anchors.leftMargin: clipRoot.timeremap ? labelRect.height : 0
0961                     Text {
0962                         // Clip name text
0963                         id: label
0964                         property string clipNameString: (clipRoot.isAudio && clipRoot.multiStream) ? ((clipRoot.audioStream > 10000 ? 'Merged' : clipRoot.aStreamIndex) + '|' + clipName ) : clipName
0965                         text: (clipRoot.speed != 1.0 ? ('[' + Math.round(clipRoot.speed*100) + '%] ') : '') + clipNameString
0966                         font: miniFont
0967                         anchors {
0968                             left: labelRect.left
0969                             leftMargin: itemBorder.border.width
0970                         }
0971                         color: 'white'
0972                         //style: Text.Outline
0973                         //styleColor: 'black'
0974                     }
0975                 }
0976 
0977                 Rectangle {
0978                     // Offset info
0979                     id: offsetRect
0980                     color: 'darkgreen'
0981                     width: offsetLabel.width + radius
0982                     height: offsetLabel.height
0983                     radius: height/3
0984                     x: labelRect.width + 4
0985                     y: 2
0986                     visible: labelRect.visible && positionOffset != 0
0987                     MouseArea {
0988                         id: offsetArea
0989                         hoverEnabled: true
0990                         cursorShape: Qt.PointingHandCursor
0991                         anchors.fill: parent
0992                         onClicked: {
0993                             clearAndMove(positionOffset)
0994                         }
0995                         onEntered: {
0996                             var text = positionOffset < 0 ? i18n("Offset: -%1", timeline.simplifiedTC(-positionOffset)) : i18n("Offset: %1", timeline.simplifiedTC(positionOffset))
0997                             timeline.showToolTip(text)
0998                         }
0999                         onExited: {
1000                             timeline.showToolTip()
1001                         }
1002                         Text {
1003                             id: offsetLabel
1004                             text: positionOffset
1005                             font: miniFont
1006                             anchors {
1007                                 horizontalCenter: parent.horizontalCenter
1008                                 topMargin: 1
1009                                 leftMargin: 1
1010                             }
1011                             color: 'white'
1012                             style: Text.Outline
1013                             styleColor: 'black'
1014                         }
1015                     }
1016                 }
1017 
1018                 Rectangle {
1019                     // effect names background
1020                     id: effectsRect
1021                     color: '#555555'
1022                     width: effectLabel.width + effectsToggle.width + 4
1023                     height: effectLabel.height
1024                     anchors.top: labelRect.bottom
1025                     anchors.left: labelRect.left
1026                     visible: labelRect.visible && clipRoot.effectNames != '' && container.showDetails
1027                     Rectangle {
1028                         // effects toggle button background
1029                         id: effectsToggle
1030                         color: clipRoot.isStackEnabled ? '#fdbc4b' : 'black'
1031                         visible: clipRoot.width > 2.5 * effectLabel.height
1032                         width: visible ? effectsRect.height : 0
1033                         height: effectsRect.height
1034                         ToolButton {
1035                             id: effectButton
1036                             height: effectsRect.height
1037                             width: effectsRect.height
1038                             onClicked: {
1039                                 timeline.setEffectsEnabled(clipRoot.clipId, !clipRoot.isStackEnabled)
1040                             }
1041 
1042                             icon {
1043                                 name: 'tools-wizard'
1044                                 color: clipRoot.isStackEnabled ? 'black' : 'white'
1045                                 height: effectLabel.height
1046                                 width: effectLabel.height
1047                             }
1048                             anchors {
1049                                 top: parent.top
1050                                 left: parent.left
1051                                 leftMargin: 1
1052                             }
1053                         }
1054                     }
1055                     Text {
1056                         // Effect names text
1057                         id: effectLabel
1058                         text: clipRoot.effectNames
1059                         font {
1060                             family: miniFont.family
1061                             pointSize: miniFont.pointSize
1062                             strikeout: !clipRoot.isStackEnabled
1063                         }
1064                         visible: effectsRect.visible
1065                         anchors {
1066                             top: effectsToggle.top
1067                             left: effectsToggle.right
1068                             leftMargin: 2
1069                             rightMargin: 2
1070                             // + ((isAudio || !settings.timelineShowThumbnails) ? 0 : inThumbnail.width) + 1
1071                         }
1072                         color: 'white'
1073                         //style: Text.Outline
1074                         styleColor: 'black'
1075                     }
1076                }
1077                Rectangle{
1078                     //proxy 
1079                     id: proxyRect
1080                     color: '#fdbc4b'
1081                     width: labelRect.height
1082                     height: labelRect.height
1083                     anchors.top: labelRect.top
1084                     anchors.left: labelRect.right
1085                     visible: !clipRoot.isAudio && clipRoot.clipStatus === ClipStatus.StatusProxy || clipRoot.clipStatus === ClipStatus.StatusProxyOnly
1086                     Text {
1087                         // Proxy P
1088                         id: proxyLabel
1089                         text: "P"
1090                         font.pointSize: root.fontUnit +1
1091                         visible: proxyRect.visible
1092                         anchors {
1093                             top: proxyRect.top
1094                             left: proxyRect.left
1095                             leftMargin: (labelRect.height-proxyLabel.width)/2
1096                             topMargin: (labelRect.height-proxyLabel.height)/2
1097                         }
1098                         color: 'black'
1099                         styleColor: 'black'
1100                     }
1101                 }
1102                 Rectangle{
1103                     //remap
1104                     id:remapRect
1105                     color: '#cc0033'
1106                     width: labelRect.height
1107                     height: labelRect.height
1108                     anchors.top: labelRect.top
1109                     anchors.left: nameContainer.left
1110                     visible: clipRoot.timeremap
1111                     Text {
1112                         // Remap R
1113                         id: remapLabel
1114                         text: "R"
1115                         font.pointSize: root.fontUnit +1
1116                         visible: remapRect.visible
1117                         anchors {
1118                             top: remapRect.top
1119                             left: remapRect.left
1120                             leftMargin: (labelRect.height-proxyLabel.width)/2
1121                             topMargin: (labelRect.height-proxyLabel.height)/2
1122                         }
1123                         color: 'white'
1124                         styleColor: 'white'
1125                     }
1126                 }
1127             }
1128 
1129             Loader {
1130                 // keyframes container
1131                 id: effectRow
1132                 clip: true
1133                 anchors.fill: parent
1134                 asynchronous: true
1135                 visible: status == Loader.Ready && clipRoot.showKeyframes && clipRoot.keyframeModel && clipRoot.width > 2 * root.baseUnit
1136                 source: clipRoot.hideClipViews || clipRoot.keyframeModel == undefined ? "" : "KeyframeView.qml"
1137                 Binding {
1138                     target: effectRow.item
1139                     property: "kfrModel"
1140                     value: clipRoot.hideClipViews ? undefined : clipRoot.keyframeModel
1141                     when: effectRow.status == Loader.Ready && effectRow.item
1142                     restoreMode: Binding.RestoreBindingOrValue
1143                 }
1144                 Binding {
1145                     target: effectRow.item
1146                     property: "selected"
1147                     value: clipRoot.selected
1148                     when: effectRow.status == Loader.Ready && effectRow.item
1149                     restoreMode: Binding.RestoreBindingOrValue
1150                 }
1151                 Binding {
1152                     target: effectRow.item
1153                     property: "inPoint"
1154                     value: clipRoot.inPoint
1155                     when: effectRow.status == Loader.Ready && effectRow.item
1156                     restoreMode: Binding.RestoreBindingOrValue
1157                 }
1158                 Binding {
1159                     target: effectRow.item
1160                     property: "outPoint"
1161                     value: clipRoot.outPoint
1162                     when: effectRow.status == Loader.Ready && effectRow.item
1163                     restoreMode: Binding.RestoreBindingOrValue
1164                 }
1165                 Binding {
1166                     target: effectRow.item
1167                     property: "modelStart"
1168                     value: clipRoot.modelStart
1169                     when: effectRow.status == Loader.Ready && effectRow.item
1170                     restoreMode: Binding.RestoreBindingOrValue
1171                 }
1172                 Binding {
1173                     target: effectRow.item
1174                     property: "scrollStart"
1175                     value: clipRoot.scrollStart
1176                     when: effectRow.status == Loader.Ready && effectRow.item
1177                     restoreMode: Binding.RestoreBindingOrValue
1178                 }
1179                 Binding {
1180                     target: effectRow.item
1181                     property: "clipId"
1182                     value: clipRoot.clipId
1183                     when: effectRow.status == Loader.Ready && effectRow.item
1184                     restoreMode: Binding.RestoreBindingOrValue
1185                 }
1186             }
1187             Connections {
1188                 target: effectRow.item
1189                 function onSeek(position) { proxy.position = position }
1190             }
1191         }
1192 
1193         states: [
1194             State {
1195                 name: 'locked'
1196                 when: isLocked
1197                 PropertyChanges {
1198                     target: clipRoot
1199                     color: root.lockedColor
1200                     opacity: 0.8
1201                     z: 0
1202                 }
1203             },
1204             State {
1205                 name: 'normal'
1206                 when: clipRoot.selected === false
1207                 PropertyChanges {
1208                     target: clipRoot
1209                     color: Qt.darker(getColor(), 1.5)
1210                     z: 0
1211                 }
1212             },
1213             State {
1214                 name: 'selectedClip'
1215                 when: clipRoot.selected === true
1216                 PropertyChanges {
1217                     target: clipRoot
1218                     color: getColor()
1219                     z: 3
1220                 }
1221             }
1222         ]
1223 
1224         MouseArea {
1225             // Add start composition area
1226             id: compInArea
1227             anchors.left: parent.left
1228             anchors.bottom: parent.bottom
1229             width: Math.min(root.baseUnit, container.height / 3)
1230             height: width
1231             hoverEnabled: true
1232             cursorShape: Qt.PointingHandCursor
1233             visible: !clipRoot.isAudio
1234             enabled: !clipRoot.isAudio && dragProxy.draggedItem === clipRoot.clipId && compositionIn.visible
1235             onPressed: {
1236                 root.mainItemId = -1
1237                 timeline.addCompositionToClip('', clipRoot.clipId, 0)
1238             }
1239             onEntered: {
1240                 timeline.showKeyBinding(i18n("<b>Click</b> to add composition"))
1241             }
1242             onExited: {
1243                 timeline.showKeyBinding()
1244             }
1245             Rectangle {
1246                 // Start composition box
1247                 id: compositionIn
1248                 anchors.bottom: parent.bottom
1249                 anchors.left: parent.left
1250                 width: compInArea.containsMouse ? parent.width : 5
1251                 height: width
1252                 radius: width / 2
1253                 visible: clipRoot.width > 4 * parent.width && mouseArea.containsMouse && !dragProxyArea.pressed
1254                 color: Qt.darker('mediumpurple')
1255                 border.width: 3
1256                 border.color: 'mediumpurple'
1257                 Behavior on width { NumberAnimation { duration: 100 } }
1258             }
1259         }
1260 
1261         MouseArea {
1262             // Add end composition area
1263             id: compOutArea
1264             anchors.right: parent.right
1265             anchors.bottom: parent.bottom
1266             width: Math.min(root.baseUnit, container.height / 3)
1267             height: width
1268             hoverEnabled: true
1269             cursorShape: Qt.PointingHandCursor
1270             enabled: !clipRoot.isAudio && dragProxy.draggedItem === clipRoot.clipId && compositionOut.visible
1271             visible: !clipRoot.isAudio
1272             onPressed: {
1273                 root.mainItemId = -1
1274                 timeline.addCompositionToClip('', clipRoot.clipId, clipRoot.clipDuration - 1)
1275             }
1276             onEntered: {
1277                 timeline.showKeyBinding(i18n("<b>Click</b> to add composition"))
1278             }
1279             onExited: {
1280                 timeline.showKeyBinding()
1281             }
1282             Rectangle {
1283                 // End composition box
1284                 id: compositionOut
1285                 anchors.bottom: parent.bottom
1286                 anchors.right: parent.right
1287                 width: compOutArea.containsMouse ? parent.height : 5
1288                 height: width
1289                 radius: width / 2
1290                 visible: clipRoot.width > 4 * parent.width && mouseArea.containsMouse && !dragProxyArea.pressed
1291                 color: Qt.darker('mediumpurple')
1292                 border.width: 3
1293                 border.color: 'mediumpurple'
1294                 Behavior on width { NumberAnimation { duration: 100 } }
1295             }
1296         }
1297 
1298         MouseArea {
1299             // Fade out drag zone
1300             id: fadeOutMouseArea
1301             anchors.right: parent.right
1302             anchors.rightMargin: clipRoot.fadeOut <= 0 ? 0 : fadeOutCanvas.width - width / 2
1303             anchors.top: parent.top
1304             width: Math.min(root.baseUnit, container.height / 3)
1305             height: width
1306             hoverEnabled: true
1307             cursorShape: Qt.PointingHandCursor
1308             drag.target: fadeOutMouseArea
1309             drag.axis: Drag.XAxis
1310             drag.minimumX: - Math.ceil(width / 2)
1311             drag.maximumX: container.width + Math.ceil(width / 4)
1312             visible: container.handleVisible && mouseArea.containsMouse && !dragProxyArea.pressed
1313             property int startFadeOut
1314             property int lastDuration: -1
1315             property int startMousePos
1316             property bool dragStarted: false
1317             property string fadeString: timeline.simplifiedTC(clipRoot.fadeOut)
1318             drag.smoothed: false
1319             onClicked: {
1320                 if (clipRoot.fadeOut == 0) {
1321                     timeline.adjustFade(clipRoot.clipId, 'fadeout', 0, -2)
1322                 }
1323             }
1324             onPressed: {
1325                 root.autoScrolling = false
1326                 startFadeOut = clipRoot.fadeOut
1327                 dragStarted = startFadeOut > 0
1328                 startMousePos = mouse.x
1329                 anchors.right = undefined
1330                 fadeOutCanvas.opacity = 0.6
1331             }
1332             onReleased: {
1333                 fadeOutCanvas.opacity = 0.4
1334                 root.autoScrolling = timeline.autoScroll
1335                 anchors.right = parent.right
1336                 var duration = clipRoot.fadeOut
1337                 timeline.adjustFade(clipRoot.clipId, 'fadeout', duration, startFadeOut)
1338                 //bubbleHelp.hide()
1339                 timeline.showToolTip()
1340             }
1341             onPositionChanged: mouse => {
1342                 if (mouse.buttons === Qt.LeftButton) {
1343                     if (!dragStarted && startMousePos - mouse.x < 3) {
1344                         return
1345                     }
1346                     dragStarted = true
1347                     var delta = clipRoot.clipDuration - Math.floor((x + width / 2 - itemBorder.border.width)/ clipRoot.timeScale)
1348                     var duration = Math.max(0, delta)
1349                     duration = Math.min(duration, clipRoot.clipDuration)
1350                     if (lastDuration != duration) {
1351                         lastDuration = duration
1352                         timeline.adjustFade(clipRoot.clipId, 'fadeout', duration, -1)
1353                         // Show fade duration as time in a "bubble" help.
1354                         timeline.showToolTip(i18n("Fade out: %1", fadeString))
1355                     }
1356                 }
1357             }
1358             onEntered: {
1359                 if (!pressed) {
1360                     if (clipRoot.fadeOut > 0) {
1361                         timeline.showToolTip(i18n("Fade out: %1", fadeString))
1362                     } else {
1363                         clipRoot.showClipInfo()
1364                     }
1365                     timeline.showKeyBinding(i18n("<b>Drag</b> to adjust fade, <b>Click</b> to add default duration fade"))
1366                 }
1367             }
1368             onExited: {
1369                 if (!pressed) {
1370                     timeline.showKeyBinding()
1371                     if (mouseArea.containsMouse) {
1372                         clipRoot.showClipInfo()
1373                     } else {
1374                         timeline.showToolTip()
1375                     }
1376                 }
1377             }
1378             Rectangle {
1379                 id: fadeOutControl
1380                 anchors.top: parent.top
1381                 anchors.right: clipRoot.fadeOut > 0 ? undefined : parent.right
1382                 anchors.horizontalCenter: clipRoot.fadeOut > 0 ? parent.horizontalCenter : undefined
1383                 width: fadeOutMouseArea.containsMouse || Drag.active ? parent.width : parent.width / 3
1384                 height: width
1385                 radius: width / 2
1386                 color: 'darkred'
1387                 border.width: 3
1388                 border.color: 'red'
1389                 enabled: !isLocked && !dragProxy.isComposition
1390                 Drag.active: fadeOutMouseArea.drag.active
1391                 Behavior on width { NumberAnimation { duration: 100 } }
1392                 Rectangle {
1393                     id: fadeOutMarker
1394                     anchors.horizontalCenter: parent.horizontalCenter
1395                     anchors.top: parent.top
1396                     color: 'red'
1397                     height: container.height
1398                     width: 1
1399                     visible : clipRoot.fadeOut > 0 && (fadeOutMouseArea.containsMouse || fadeOutMouseArea.drag.active)
1400                 }
1401             }
1402             ToolTip.visible: (containsMouse || pressed || drag.active)
1403             ToolTip.delay: (pressed || drag.active) ? 0 : 1000
1404             ToolTip.text: fadeString
1405         }
1406 
1407         MouseArea {
1408             // Fade in drag zone
1409             id: fadeInMouseArea
1410             anchors.left: container.left
1411             anchors.leftMargin: clipRoot.fadeIn <= 0 ? 0 : (fadeInTriangle.width - width / 3)
1412             anchors.top: parent.top
1413             width: Math.min(root.baseUnit, container.height / 3)
1414             height: width
1415             hoverEnabled: true
1416             cursorShape: Qt.PointingHandCursor
1417             drag.target: fadeInMouseArea
1418             drag.minimumX: - Math.ceil(width / 2)
1419             drag.maximumX: container.width - width / 2
1420             drag.axis: Drag.XAxis
1421             drag.smoothed: false
1422             property int startFadeIn
1423             property int startMousePos
1424             property bool dragStarted: false
1425             property string fadeString: timeline.simplifiedTC(clipRoot.fadeIn)
1426             visible: container.handleVisible && mouseArea.containsMouse && !dragProxyArea.pressed
1427             onClicked: {
1428                 if (clipRoot.fadeIn == 0) {
1429                     timeline.adjustFade(clipRoot.clipId, 'fadein', 0, -2)
1430                 }
1431             }
1432             onPressed: mouse => {
1433                 root.autoScrolling = false
1434                 startFadeIn = clipRoot.fadeIn
1435                 dragStarted = startFadeIn > 0
1436                 startMousePos = mouse.x
1437                 anchors.left = undefined
1438                 fadeInTriangle.opacity = 0.6
1439                 // parentTrack.clipSelected(clipRoot, parentTrack) TODO
1440             }
1441             onReleased: {
1442                 root.autoScrolling = timeline.autoScroll
1443                 fadeInTriangle.opacity = 0.4
1444                 timeline.adjustFade(clipRoot.clipId, 'fadein', clipRoot.fadeIn, startFadeIn)
1445                 //bubbleHelp.hide()
1446                 timeline.showToolTip()
1447                 anchors.left = container.left
1448             }
1449             onPositionChanged: mouse => {
1450                 if (mouse.buttons === Qt.LeftButton) {
1451                     if (!dragStarted && mouse.x - startMousePos < 3) {
1452                         return
1453                     }
1454                     dragStarted = true
1455                     var delta = Math.round((x + width / 2) / clipRoot.timeScale)
1456                     var duration = Math.max(0, delta)
1457                     duration = Math.min(duration, clipRoot.clipDuration - 1)
1458                     if (duration != clipRoot.fadeIn) {
1459                         timeline.adjustFade(clipRoot.clipId, 'fadein', duration, -1)
1460                         // Show fade duration as time in a "bubble" help.
1461                         timeline.showToolTip(i18n("Fade in: %1", fadeString))
1462                     }
1463                 }
1464             }
1465             onEntered: {
1466                 if (!pressed) {
1467                     if (clipRoot.fadeIn > 0) {
1468                         timeline.showToolTip(i18n("Fade in: %1", fadeString))
1469                     } else {
1470                         clipRoot.showClipInfo()
1471                     }
1472                     timeline.showKeyBinding(i18n("<b>Drag</b> to adjust fade, <b>Click</b> to add default duration fade"))
1473                 }
1474             }
1475             onExited: {
1476                 if (!pressed) {
1477                     timeline.showKeyBinding()
1478                     if (mouseArea.containsMouse) {
1479                         clipRoot.showClipInfo()
1480                     } else {
1481                         timeline.showToolTip()
1482                     }
1483                 }
1484             }
1485             Rectangle {
1486                 id: fadeInControl
1487                 anchors.top: parent.top
1488                 anchors.left: clipRoot.fadeIn > 0 ? undefined : parent.left
1489                 anchors.horizontalCenter: clipRoot.fadeIn > 0 ? parent.horizontalCenter : undefined
1490                 width: fadeInMouseArea.containsMouse || Drag.active ? parent.width : parent.width / 3
1491                 height: width
1492                 radius: width / 2
1493                 color: 'green'
1494                 border.width: 3
1495                 border.color: '#FF66FFFF'
1496                 enabled: !isLocked && !dragProxy.isComposition
1497                 Drag.active: fadeInMouseArea.drag.active
1498                 Behavior on width { NumberAnimation { duration: 100 } }
1499                 Rectangle {
1500                     id: fadeInMarker
1501                     anchors.horizontalCenter: parent.horizontalCenter
1502                     anchors.top: parent.top
1503                     color: '#FF66FFFF'
1504                     height: container.height
1505                     width: 1
1506                     visible : clipRoot.fadeIn > 0 && (fadeInMouseArea.containsMouse || fadeInMouseArea.drag.active)
1507                 }
1508             }
1509             ToolTip.visible: (containsMouse || pressed || drag.active)
1510             ToolTip.delay: (pressed || drag.active) ? 0 : 1000
1511             ToolTip.text: fadeString
1512         }
1513 
1514         Rectangle {
1515             id: currentRegion
1516             color: slipControler.color
1517             anchors {
1518                 right: container.right
1519                 left: container.left
1520                 top: slipControler.top
1521             }
1522             height: container.height / 2
1523             opacity: 0.7
1524             visible: slipControler.visible
1525         }
1526         Item {
1527             id: slipControler
1528             property color color: timeline.trimmingMainClip === clipId ? root.selectionColor : activePalette.highlight
1529             anchors.bottom: container.bottom
1530             height: container.height
1531             width: clipRoot.maxDuration * clipRoot.timeScale
1532             x: - (clipRoot.inPoint - slipOffset) * clipRoot.timeScale
1533             visible: root.activeTool === ProjectTool.SlipTool && clipRoot.selected && clipRoot.maxDuration > 0 // don't show for endless clips
1534             property int inPoint: clipRoot.inPoint
1535             property int outPoint: clipRoot.outPoint
1536             Rectangle {
1537                 id: slipBackground
1538                 anchors.fill: parent
1539                 color: parent.color
1540                 border.width: 2
1541                 border.color: activePalette.highlightedText
1542                 opacity: 0.3
1543             }
1544             Rectangle {
1545                 id: currentRegionMoved
1546                 color: parent.color
1547                 x: slipBackground.x + slipControler.inPoint * clipRoot.timeScale + itemBorder.border.width
1548                 anchors.bottom: parent.bottom
1549                 height: parent.height / 2
1550                 width: container.width
1551                 opacity: 0.7
1552             }
1553             Text {
1554                 id: slipLable
1555                 text: i18n("Slip Clip")
1556                 font: miniFont
1557                 anchors.fill: parent
1558                 verticalAlignment: Text.AlignVCenter
1559                 horizontalAlignment: Text.AlignHCenter
1560                 color: activePalette.highlightedText
1561                 opacity: 1
1562             }
1563         }
1564     }
1565 }