Clip.qml 51.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
 * Copyright (c) 2013-2016 Meltytech, LLC
 * Author: Dan Dennedy <dan@dennedy.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

19
20
import QtQuick 2.11
import QtQuick.Controls 2.4
21
import Kdenlive.Controls 1.0
22
import QtQml.Models 2.11
23
import QtQuick.Window 2.2
24
import 'Timeline.js' as Logic
25
import com.enums 1.0
26
27
28

Rectangle {
    id: clipRoot
29
    property real timeScale: 1
30
    property string clipName: ''
31
    property string tagColor: ''
32
33
    property string clipResource: ''
    property string mltService: ''
34
    property string effectNames
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
35
    property int modelStart
36
    property int mixDuration: 0
37
    property int mixCut: 0
38
    property real scrollX: 0
39
40
41
    property int inPoint: 0
    property int outPoint: 0
    property int clipDuration: 0
42
    property int maxDuration: 0
43
    property bool isAudio: false
44
    property int audioChannels
45
    property int audioStream: -1
46
    property bool multiStream: false
47
    property int aStreamIndex: 0
48
    property bool showKeyframes: false
49
    property bool isGrabbed: false
50
    property bool grouped: false
51
    property var markers
52
    property var keyframeModel
53
    property int clipState: 0
54
55
    property int clipStatus: 0
    property int itemType: 0
56
57
    property int fadeIn: 0
    property int fadeOut: 0
58
    property int binId: 0
59
    property int positionOffset: 0
60
    property var parentTrack
61
62
    property int trackIndex //Index in track repeater
    property int clipId     //Id of the clip in the model
63
64
65
    property int trackId: -1 // Id of the parent track in the model
    property int fakeTid: -1
    property int fakePosition: 0
66
    property int originalTrackId: -1
67
    property int originalX: x
68
69
    property int originalDuration: clipDuration
    property int lastValidDuration: clipDuration
70
    property int draggedX: x
71
    property bool selected: false
72
    property bool isLocked: parentTrack && parentTrack.isLocked == true
73
    property bool hasAudio
74
75
    property bool canBeAudio
    property bool canBeVideo
76
    property double speed: 1.0
77
    property color borderColor: 'black'
78
    property bool forceReloadThumb
79
    property bool isComposition: false
80
    property bool hideClipViews: false
81
    property int scrollStart: scrollView.contentX - (clipRoot.modelStart * timeline.scaleFactor)
82
    property int mouseXPos: mouseArea.mouseX
83
    width : clipDuration * timeScale
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
84
    opacity: dragProxyArea.drag.active && dragProxy.draggedItem == clipId ? 0.8 : 1.0
85

86
87
    signal trimmingIn(var clip, real newDuration, var mouse, bool shiftTrim, bool controlTrim)
    signal trimmedIn(var clip, bool shiftTrim, bool controlTrim)
88
    signal initGroupTrim(var clip)
89
90
    signal trimmingOut(var clip, real newDuration, var mouse, bool shiftTrim, bool controlTrim)
    signal trimmedOut(var clip, bool shiftTrim, bool controlTrim)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
91

92
    onScrollStartChanged: {
93
        clipRoot.hideClipViews = scrollStart > (clipDuration * timeline.scaleFactor) || scrollStart + scrollView.width < 0
94
    }
95

96
97
    onIsGrabbedChanged: {
        if (clipRoot.isGrabbed) {
98
            grabItem()
99
        } else {
100
            timeline.showToolTip()
101
            mouseArea.focus = false
102
103
104
        }
    }

105
106
107
108
109
    function grabItem() {
        clipRoot.forceActiveFocus()
        mouseArea.focus = true
    }

110
111
    function clearAndMove(offset) {
        controller.requestClearSelection()
112
113
        controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true)
        controller.requestAddToSelection(clipRoot.clipId)
114
115
    }

116
    onClipResourceChanged: {
117
        if (itemType == ProducerType.Color) {
118
            color: Qt.darker(getColor(), 1.5)
119
120
        }
    }
121

122
    onKeyframeModelChanged: {
123
        if (effectRow.keyframecanvas) {
124
            console.log('keyframe model changed............')
125
126
            effectRow.keyframecanvas.requestPaint()
        }
127
128
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
129
    onClipDurationChanged: {
130
        width = clipDuration * timeScale
131
132
        if (parentTrack && parentTrack.isAudio && thumbsLoader.item) {
            // Duration changed, we may need a different number of repeaters
133
            thumbsLoader.item.reload(1)
134
        }
135
    }
136

137
138
139
    onModelStartChanged: {
        x = modelStart * timeScale;
    }
140

141
142
143
144
145
    onFakePositionChanged: {
        x = fakePosition * timeScale;
    }
    onFakeTidChanged: {
        if (clipRoot.fakeTid > -1 && parentTrack) {
146
147
148
149
150
151
152
            if (clipRoot.parent != dragContainer) {
                var pos = clipRoot.mapToGlobal(clipRoot.x, clipRoot.y);
                clipRoot.parent = dragContainer
                pos = clipRoot.mapFromGlobal(pos.x, pos.y)
                clipRoot.x = pos.x
                clipRoot.y = pos.y
            }
153
            clipRoot.y = Logic.getTrackById(clipRoot.fakeTid).y
154
            clipRoot.height = Logic.getTrackById(clipRoot.fakeTid).height
155
        }
156
157
    }

158
    onForceReloadThumbChanged: {
159
        // TODO: find a way to force reload of clip thumbs
160
        if (thumbsLoader.item) {
161
            thumbsLoader.item.reload(0)
162
        }
163
164
    }

165
    onTimeScaleChanged: {
166
        x = modelStart * timeScale;
167
        width = clipDuration * timeScale;
168
        updateLabelOffset()
169
170
    }
    onScrollXChanged: {
171
172
173
174
175
        updateLabelOffset()
    }
    
    function updateLabelOffset()
    {
176
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : 0
177
178
    }

179
    border.color: (clipStatus == ClipStatus.StatusMissing || ClipStatus == ClipStatus.StatusWaiting || clipStatus == ClipStatus.StatusDeleting) ? "#ff0000" : selected ? root.selectionColor : grouped ? root.groupColor : borderColor
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
180
    border.width: isGrabbed ? 8 : 2
181

182
183
184
185
186
    function updateDrag() {
        var itemPos = mapToItem(tracksContainerArea, 0, 0, clipRoot.width, clipRoot.height)
        initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
    }

187
    function getColor() {
188
189
        if (clipRoot.clipState == ClipState.Disabled) {
            return '#888'
190
        }
191
192
193
        if (clipRoot.tagColor) {
            return clipRoot.tagColor
        }
194
195
196
        if (itemType == ProducerType.Text) {
            return titleColor
        }
197
        if (itemType == ProducerType.Image) {
198
199
            return imageColor
        }
200
201
202
        if (itemType == ProducerType.SlideShow) {
            return slideshowColor
        }
203
        if (itemType == ProducerType.Color) {
204
205
206
            var color = clipResource.substring(clipResource.length - 9)
            if (color[0] == '#') {
                return color
207
            }
208
            return '#' + color.substring(color.length - 8, color.length - 2)
209
        }
210
        return isAudio? root.audioColor : root.videoColor
211
212
    }

213
/*    function reparent(track) {
214
        console.log('TrackId: ',trackId)
215
216
        parent = track
        height = track.height
217
        parentTrack = track
218
        trackId = parentTrack.trackId
219
        console.log('Reparenting clip to Track: ', trackId)
220
        //generateWaveform()
221
    }
222
*/
223
    property bool noThumbs: (isAudio || itemType == ProducerType.Color || mltService === '')
224
    property string baseThumbPath: noThumbs ? '' : 'image://thumbnail/' + binId + '/' + documentId + '/#'
225

226
    DropArea { //Drop area for clips
227
228
229
        anchors.fill: clipRoot
        keys: 'kdenlive/effect'
        property string dropData
230
        property string dropSource
231
        property int dropRow: -1
232
233
        onEntered: {
            dropData = drag.getDataAsString('kdenlive/effect')
234
            dropSource = drag.getDataAsString('kdenlive/effectsource')
235
236
237
        }
        onDropped: {
            console.log("Add effect: ", dropData)
238
            if (dropSource == '') {
239
240
241
                // drop from effects list
                controller.addClipEffect(clipRoot.clipId, dropData);
            } else {
242
                controller.copyClipEffect(clipRoot.clipId, dropSource);
243
            }
244
            dropSource = ''
245
            dropRow = -1
246
            drag.acceptProposedAction
247
248
        }
    }
249
250
    MouseArea {
        id: mouseArea
251
        enabled: root.activeTool === 0
252
        anchors.fill: clipRoot
253
        acceptedButtons: Qt.RightButton
254
        hoverEnabled: root.activeTool === 0
255
        cursorShape: (trimInMouseArea.drag.active || trimOutMouseArea.drag.active)? Qt.SizeHorCursor : dragProxyArea.cursorShape
256
        onPressed: {
257
            root.autoScrolling = false
258
            root.mainItemId = clipRoot.clipId
259
            if (mouse.button == Qt.RightButton) {
260
                if (timeline.selection.indexOf(clipRoot.clipId) == -1) {
261
                    controller.requestAddToSelection(clipRoot.clipId, true)
262
                }
263
                root.mainFrame = Math.round(mouse.x / timeline.scaleFactor)
264
                root.showClipMenu(clipRoot.clipId)
265
                root.autoScrolling = timeline.autoScroll
266
            }
267
        }
268
269
270
        onReleased: {
            root.autoScrolling = timeline.autoScroll
        }
271
        Keys.onShortcutOverride: 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)
272
        Keys.onLeftPressed: {
273
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
274
275
276
            if (controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true)) {
                timeline.showToolTip(i18n("Position: %1", timeline.simplifiedTC(clipRoot.modelStart)));
            }
277
278
        }
        Keys.onRightPressed: {
279
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
280
281
282
            if (controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart + offset, true, true, true)) {
                timeline.showToolTip(i18n("Position: %1", timeline.simplifiedTC(clipRoot.modelStart)));
            }
283
        }
284
285
286
287
288
289
        Keys.onUpPressed: {
            controller.requestClipMove(clipRoot.clipId, controller.getNextTrackId(clipRoot.trackId), clipRoot.modelStart, true, true, true);
        }
        Keys.onDownPressed: {
            controller.requestClipMove(clipRoot.clipId, controller.getPreviousTrackId(clipRoot.trackId), clipRoot.modelStart, true, true, true);
        }
290
291
        Keys.onEscapePressed: {
            timeline.grabCurrent()
292
            //focus = false
293
        }
294
        onPositionChanged: {
295
296
            var mapped = parentTrack.mapFromItem(clipRoot, mouse.x, mouse.y).x
            root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
297
        }
298
299
300
        onEntered: {
            var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
            initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
301
302
303
304
305
306
            var text = i18n("%1 (%2-%3), Position: %4, Duration: %5".arg(clipRoot.clipName)
                        .arg(timeline.simplifiedTC(clipRoot.inPoint))
                        .arg(timeline.simplifiedTC(clipRoot.outPoint))
                        .arg(timeline.simplifiedTC(clipRoot.modelStart))
                        .arg(timeline.simplifiedTC(clipRoot.clipDuration)))
            timeline.showToolTip(text)
307
        }
308

309
310
        onExited: {
            endDrag()
311
            timeline.showToolTip()
312
        }
313
        onWheel: zoomByWheel(wheel)
314

315
        Loader {
316
            // Thumbs container
317
            id: thumbsLoader
318
            anchors.fill: parent
319
            anchors.leftMargin: parentTrack.isAudio ? 0 : clipRoot.border.width + mixContainer.width
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
320
            anchors.rightMargin: parentTrack.isAudio ? 0 : clipRoot.border.width
321
322
323
            anchors.topMargin: clipRoot.border.width
            anchors.bottomMargin: clipRoot.border.width
            clip: true
324
325
            asynchronous: true
            visible: status == Loader.Ready
326
            source: clipRoot.hideClipViews || clipRoot.itemType == 0 || clipRoot.itemType === ProducerType.Color ? "" : parentTrack.isAudio ? (timeline.showAudioThumbnails ? "ClipAudioThumbs.qml" : "") : timeline.showThumbnails ? "ClipThumbs.qml" : ""
327
        }
328

329
330
331
332
        Item {
            // Clipping container
            id: container
            anchors.fill: parent
333
334
            anchors.margins: clipRoot.border.width
            //clip: true
335
            property bool showDetails: (!clipRoot.selected || !effectRow.visible) && container.height > 2.2 * labelRect.height
336
            
337
            Item {
338
                // Mix indicator
339
                id: mixContainer
340
341
342
                anchors.left: parent.left
                anchors.top: parent.top
                anchors.bottom: parent.bottom
343
344
345
                width: clipRoot.mixDuration * clipRoot.timeScale
                
                Rectangle {
346
                    id: mixBackground
347
                    property double mixPos: mixBackground.width - clipRoot.mixCut * clipRoot.timeScale
348
349
350
351
352
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    anchors.left: parent.left
                    anchors.right: parent.right
                    visible: clipRoot.mixDuration > 0
353
                    color: "mediumpurple"
354
355
                    Loader {
                        id: shapeLoader
356
                        source: clipRoot.mixDuration > 0 ? "MixShape.qml" : ""
357
                        property bool valid: item !== null
358
                    }
359
360

                    opacity: mixArea.containsMouse || trimInMixArea.pressed || trimInMixArea.containsMouse || root.selectedMix == clipRoot.clipId ? 1 : 0.7
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
                    border.color: root.selectedMix == clipRoot.clipId ? root.selectionColor : "transparent"
                    border.width: 2
                    MouseArea {
                        // Mix click mouse area
                        id: mixArea
                        anchors.fill: parent
                        hoverEnabled: true
                        cursorShape: Qt.PointingHandCursor
                        onPressed: {
                            controller.requestMixSelection(clipRoot.clipId);
                        }
                    }
                    Rectangle {
                        id: mixCutPos
                        anchors.right: parent.right
                        anchors.rightMargin: clipRoot.mixCut * clipRoot.timeScale
                        anchors.top: parent.top
                        anchors.bottom: parent.bottom
                        width: 2
380
                        color: "navy"
381
                    }
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
                    MouseArea {
                        // Left resize handle
                        id: trimInMixArea
                        anchors.left: parent.left
                        anchors.leftMargin: clipRoot.mixDuration * clipRoot.timeScale
                        height: parent.height
                        width: root.baseUnit / 2
                        visible: root.activeTool === 0
                        property int previousMix
                        enabled: !isLocked && (pressed || clipRoot.width > 3 * width)
                        hoverEnabled: true
                        drag.target: trimInMixArea
                        drag.axis: Drag.XAxis
                        drag.smoothed: false
                        property bool sizeChanged: false
                        cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
                        onPressed: {
                            previousMix = clipRoot.mixDuration
                            root.autoScrolling = false
401
                            mixOut.color = 'darkorchid'
402
403
                            anchors.left = undefined
                            parent.anchors.right = undefined
404
                            mixCutPos.anchors.right = undefined
405
406
                        }
                        onReleased: {
407
                            controller.resizeStartMix(clipRoot.clipId, Math.round(Math.max(0, x) / clipRoot.timeScale), mouse.modifiers & Qt.ShiftModifier)
408
409
410
411
412
413
                            root.autoScrolling = timeline.autoScroll
                            if (sizeChanged) {
                                sizeChanged = false
                            }
                            anchors.left = parent.left
                            parent.anchors.right = mixContainer.right
414
                            mixBackground.anchors.bottom = mixContainer.bottom
415
                            mixOut.color = clipRoot.border.color
416
                            mixCutPos.anchors.right = mixCutPos.parent.right
417
418
419
420
421
422
423
424
425
426
                        }
                        onPositionChanged: {
                            if (mouse.buttons === Qt.LeftButton) {
                                var currentFrame = Math.round(x / clipRoot.timeScale)
                                if (currentFrame != previousMix) {
                                    parent.width = currentFrame * clipRoot.timeScale
                                    sizeChanged = true
                                    //TODO: resize mix's other clip
                                    //clipRoot.trimmingIn(clipRoot, newDuration, mouse, shiftTrim, controlTrim)
                                }
427
428
429
430
431
432
                                if (x < mixCutPos.x) {
                                    // This will delete the mix
                                    mixBackground.anchors.bottom = mixContainer.top
                                } else {
                                    mixBackground.anchors.bottom = mixContainer.bottom
                                }
433
434
435
436
                            }
                        }
                        onEntered: {
                            if (!pressed) {
437
                                mixOut.color = 'darkorchid'
438
                                timeline.showToolTip(i18n("Mix:%1", timeline.simplifiedTC(clipRoot.mixDuration)))
439
440
441
                            }
                        }
                        onExited: {
442
                            if (!pressed) {
443
                                mixOut.color = clipRoot.border.color
444
                                timeline.showToolTip()
445
                            }
446
447
448
449
                        }
                        Rectangle {
                            id: mixOut
                            width: clipRoot.border.width
450
                            height: mixContainer.height
451
                            color: clipRoot.border.color
452
453
454
455
                            Drag.active: trimInMixArea.drag.active
                            Drag.proposedAction: Qt.MoveAction
                            visible: trimInMixArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && parent.enabled)
                        }
456
457
                    }
                }
458
                
459
            }
460
461

            Repeater {
462
                // Clip markers
463
464
465
466
                model: markers
                delegate:
                Item {
                    anchors.fill: parent
467
                    visible: markerBase.x >= 0 && markerBase.x < clipRoot.width
468
469
470
471
                    Rectangle {
                        id: markerBase
                        width: 1
                        height: parent.height
472
                        x: clipRoot.speed < 0 ? (clipRoot.maxDuration - clipRoot.inPoint) * timeScale + (Math.round(model.frame / clipRoot.speed)) * timeScale - clipRoot.border.width : (Math.round(model.frame / clipRoot.speed) - clipRoot.inPoint) * timeScale - clipRoot.border.width;
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
                        color: model.color
                    }
                    Rectangle {
                        visible: mlabel.visible
                        opacity: 0.7
                        x: markerBase.x
                        radius: 2
                        width: mlabel.width + 4
                        height: mlabel.height
                        anchors {
                            bottom: parent.verticalCenter
                        }
                        color: model.color
                        MouseArea {
                            z: 10
                            anchors.fill: parent
                            acceptedButtons: Qt.LeftButton
                            cursorShape: Qt.PointingHandCursor
                            hoverEnabled: true
492
                            onDoubleClicked: timeline.editMarker(clipRoot.clipId, model.frame)
493
                            onClicked: proxy.position = clipRoot.modelStart + (clipRoot.speed < 0 ? (clipRoot.maxDuration - clipRoot.inPoint) * timeScale + (Math.round(model.frame / clipRoot.speed)) : (Math.round(model.frame / clipRoot.speed) - clipRoot.inPoint))
494
495
                        }
                    }
496
497
498
499
500
501
502
                    TextMetrics {
                        id: textMetrics
                        font: miniFont
                        text: model.comment
                        elide: root.timeScale > 1 ? Text.ElideNone : Text.ElideRight
                        elideWidth: root.maxLabelWidth
                    }
503
504
                    Text {
                        id: mlabel
505
506
                        visible: timeline.showMarkers && textMetrics.elideWidth > root.baseUnit
                        text: textMetrics.elidedText
507
                        font: miniFont
508
509
510
511
512
513
514
515
                        x: markerBase.x
                        anchors {
                            bottom: parent.verticalCenter
                            topMargin: 2
                            leftMargin: 2
                        }
                        color: 'white'
                    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
516
                }
517
518
            }

519
520
521
            MouseArea {
                // Left resize handle
                id: trimInMouseArea
522
                x: -clipRoot.border.width
523
524
                height: parent.height
                width: root.baseUnit / 2
525
                visible: root.activeTool === 0
526
                enabled: !isLocked && (pressed || clipRoot.width > 3 * width)
527
528
529
530
531
532
533
                hoverEnabled: true
                drag.target: trimInMouseArea
                drag.axis: Drag.XAxis
                drag.smoothed: false
                property bool shiftTrim: false
                property bool controlTrim: false
                property bool sizeChanged: false
534
                cursorShape: (containsMouse || pressed ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
535
536
537
538
539
540
541
542
543
544
545
546
547
                onPressed: {
                    root.autoScrolling = false
                    clipRoot.originalX = clipRoot.x
                    clipRoot.originalDuration = clipDuration
                    shiftTrim = mouse.modifiers & Qt.ShiftModifier
                    controlTrim = mouse.modifiers & Qt.ControlModifier
                    if (!shiftTrim && clipRoot.grouped) {
                        clipRoot.initGroupTrim(clipRoot)
                    }
                    trimIn.opacity = 0
                }
                onReleased: {
                    root.autoScrolling = timeline.autoScroll
548
                    x = -clipRoot.border.width
549
550
551
                    if (sizeChanged) {
                        clipRoot.trimmedIn(clipRoot, shiftTrim, controlTrim)
                        sizeChanged = false
552
553
554
555
556
                        if (!controlTrim) {
                            updateDrag()
                        } else {
                            endDrag()
                        }
557
558
                    }
                }
559
560
561
                onDoubleClicked: {
                    timeline.mixClip(clipRoot.clipId, -1)
                }
562
563
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
564
565
566
                        var currentFrame = Math.round((clipRoot.x + (x + clipRoot.border.width)) / timeScale)
                        var currentClipPos = clipRoot.modelStart
                        var delta = currentFrame - currentClipPos
567
                        if (delta !== 0) {
568
                            if (maxDuration > 0 && delta < -inPoint && !(mouse.modifiers & Qt.ControlModifier)) {
569
570
571
572
573
574
575
576
577
578
579
                                delta = -inPoint
                            }
                            var newDuration =  clipDuration - delta
                            sizeChanged = true
                            clipRoot.trimmingIn(clipRoot, newDuration, mouse, shiftTrim, controlTrim)
                        }
                    }
                }
                onEntered: {
                    if (!pressed) {
                        trimIn.opacity = 1
580
581
                        var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
                        initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
582
583
                        var s = i18n("In:%1, Position:%2", timeline.simplifiedTC(clipRoot.inPoint),timeline.simplifiedTC(clipRoot.modelStart))
                        timeline.showToolTip(s)
584
585
586
587
                    }
                }
                onExited: {
                    trimIn.opacity = 0
588
589
590
591
592
593
594
595
596
597
598
599
                    if (!pressed) {
                        if (!mouseArea.containsMouse) {
                            timeline.showToolTip()
                        } else {
                            var text = i18n("%1 (%2-%3), Position: %4, Duration: %5".arg(clipRoot.clipName)
                        .arg(timeline.simplifiedTC(clipRoot.inPoint))
                        .arg(timeline.simplifiedTC(clipRoot.outPoint))
                        .arg(timeline.simplifiedTC(clipRoot.modelStart))
                        .arg(timeline.simplifiedTC(clipRoot.clipDuration)))
                             timeline.showToolTip(text)
                        }
                    }
600
601
602
603
604
605
606
607
608
609
                }
                Rectangle {
                    id: trimIn
                    anchors.left: parent.left
                    width: clipRoot.border.width
                    height: parent.height
                    color: 'lawngreen'
                    opacity: 0
                    Drag.active: trimInMouseArea.drag.active
                    Drag.proposedAction: Qt.MoveAction
610
                    visible: trimInMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && parent.enabled)
611

612
                    /*ToolTip {
613
614
615
616
617
618
619
620
621
                        visible: trimInMouseArea.containsMouse && !trimInMouseArea.pressed
                        delay: 1000
                        timeout: 5000
                        background: Rectangle {
                            color: activePalette.alternateBase
                            border.color: activePalette.light
                        }
                        contentItem: Label {
                            color: activePalette.text
622
                            font: miniFont
623
624
                            text: i18n("In:%1\nPosition:%2", timeline.simplifiedTC(clipRoot.inPoint),timeline.simplifiedTC(clipRoot.modelStart))
                        }
625
                    }*/
626
627
628
629
630
631
632
633
634
635
636
637
                }
            }

            MouseArea {
                // Right resize handle
                id: trimOutMouseArea
                anchors.right: parent.right
                anchors.rightMargin: -clipRoot.border.width
                anchors.top: parent.top
                height: parent.height
                width: root.baseUnit / 2
                hoverEnabled: true
638
                visible: root.activeTool === 0
639
                enabled: !isLocked && (pressed || clipRoot.width > 3 * width)
640
641
642
                property bool shiftTrim: false
                property bool controlTrim: false
                property bool sizeChanged: false
643
                cursorShape: (containsMouse || pressed ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
                drag.target: trimOutMouseArea
                drag.axis: Drag.XAxis
                drag.smoothed: false

                onPressed: {
                    root.autoScrolling = false
                    clipRoot.originalDuration = clipDuration
                    anchors.right = undefined
                    shiftTrim = mouse.modifiers & Qt.ShiftModifier
                    controlTrim = mouse.modifiers & Qt.ControlModifier
                    if (!shiftTrim && clipRoot.grouped) {
                        clipRoot.initGroupTrim(clipRoot)
                    }
                    trimOut.opacity = 0
                }
                onReleased: {
                    root.autoScrolling = timeline.autoScroll
                    anchors.right = parent.right
                    if (sizeChanged) {
                        clipRoot.trimmedOut(clipRoot, shiftTrim, controlTrim)
                        sizeChanged = false
665
666
667
668
669
                        if (!controlTrim) {
                            updateDrag()
                        } else {
                            endDrag()
                        }
670
671
                    }
                }
672
673
674
                onDoubleClicked: {
                    timeline.mixClip(clipRoot.clipId, 1)
                }
675
676
677
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
                        var newDuration = Math.round((x + width) / timeScale)
678
                        if (maxDuration > 0 && (newDuration > maxDuration - inPoint) && !(mouse.modifiers & Qt.ControlModifier)) {
679
680
681
682
683
684
685
686
687
688
689
                            newDuration = maxDuration - inPoint
                        }
                        if (newDuration != clipDuration) {
                            sizeChanged = true
                            clipRoot.trimmingOut(clipRoot, newDuration, mouse, shiftTrim, controlTrim)
                        }
                    }
                }
                onEntered: {
                    if (!pressed) {
                        trimOut.opacity = 1
690
691
                        var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
                        initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
692
693
                        var s = i18n("Out:%1, Position:%2", timeline.simplifiedTC(clipRoot.outPoint),timeline.simplifiedTC(clipRoot.modelStart + clipRoot.clipDuration))
                        timeline.showToolTip(s)
694
695
                    }
                }
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
                onExited: {
                    trimOut.opacity = 0
                    if (!pressed) {
                         if (!mouseArea.containsMouse) {
                            timeline.showToolTip()
                         } else {
                             var text = i18n("%1 (%2-%3), Position: %4, Duration: %5".arg(clipRoot.clipName)
                        .arg(timeline.simplifiedTC(clipRoot.inPoint))
                        .arg(timeline.simplifiedTC(clipRoot.outPoint))
                        .arg(timeline.simplifiedTC(clipRoot.modelStart))
                        .arg(timeline.simplifiedTC(clipRoot.clipDuration)))
                             timeline.showToolTip(text)
                         }
                    }
                }
                /*ToolTip {
712
713
714
715
716
717
718
719
720
                    visible: trimOutMouseArea.containsMouse && !trimOutMouseArea.pressed
                    delay: 1000
                    timeout: 5000
                    background: Rectangle {
                        color: activePalette.alternateBase
                        border.color: activePalette.light
                    }
                    contentItem: Label {
                        color: activePalette.text
721
                        font: miniFont
722
723
                        text: i18n("Out: ") + timeline.simplifiedTC(clipRoot.outPoint)
                    }
724
                }*/
725
726
727
728
729
730
731
732
733
                Rectangle {
                    id: trimOut
                    anchors.right: parent.right
                    width: clipRoot.border.width
                    height: parent.height
                    color: 'red'
                    opacity: 0
                    Drag.active: trimOutMouseArea.drag.active
                    Drag.proposedAction: Qt.MoveAction
734
                    visible: trimOutMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && parent.enabled)
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
                }
            }

            TimelineTriangle {
                // Green fade in triangle
                id: fadeInTriangle
                fillColor: 'green'
                width: Math.min(clipRoot.fadeIn * timeScale, container.width)
                height: parent.height
                anchors.left: parent.left
                anchors.top: parent.top
                opacity: 0.4
            }

            TimelineTriangle {
                // Red fade out triangle
                id: fadeOutCanvas
                fillColor: 'red'
                width: Math.min(clipRoot.fadeOut * timeScale, container.width)
                height: parent.height
                anchors.right: parent.right
                anchors.top: parent.top
                opacity: 0.4
                transform: Scale { xScale: -1; origin.x: fadeOutCanvas.width / 2}
            }

            Item {
                // Clipping container for clip names
                anchors.fill: parent
764
                anchors.leftMargin: mixContainer.width
765
766
767
768
769
                clip: true
                Rectangle {
                    // Clip name background
                    id: labelRect
                    color: clipRoot.selected ? 'darkred' : '#66000000'
770
                    width: label.width + (2 * clipRoot.border.width)
771
772
773
774
775
                    height: label.height
                    visible: clipRoot.width > width / 2
                    Text {
                        // Clip name text
                        id: label
776
                        property string clipNameString: (clipRoot.isAudio && clipRoot.multiStream) ? ((clipRoot.audioStream > 10000 ? 'Merged' : clipRoot.aStreamIndex) + '|' + clipName ) : clipName
777
                        text: (clipRoot.speed != 1.0 ? ('[' + Math.round(clipRoot.speed*100) + '%] ') : '') + clipNameString
778
                        font: miniFont
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
                        anchors {
                            top: labelRect.top
                            left: labelRect.left
                            leftMargin: clipRoot.border.width
                        }
                        color: 'white'
                        //style: Text.Outline
                        //styleColor: 'black'
                    }
                }

                Rectangle {
                    // Offset info
                    id: offsetRect
                    color: 'darkgreen'
                    width: offsetLabel.width + radius
                    height: offsetLabel.height
                    radius: height/3
                    x: labelRect.width + 4
                    y: 2
                    visible: labelRect.visible && positionOffset != 0
                    MouseArea {
                        id: offsetArea
                        hoverEnabled: true
                        cursorShape: Qt.PointingHandCursor
                        anchors.fill: parent
                        onClicked: {
                            clearAndMove(positionOffset)
                        }
808
809
810
811
812
813
                        onEntered: {
                            var text = positionOffset < 0 ? i18n("Offset: -%1", timeline.simplifiedTC(-positionOffset)) : i18n("Offset: %1", timeline.simplifiedTC(positionOffset))
                            timeline.showToolTip(text)
                        }
                        onExited: {
                            timeline.showToolTip()
814
815
816
817
                        }
                        Text {
                            id: offsetLabel
                            text: positionOffset
818
                            font: miniFont
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
                            anchors {
                                horizontalCenter: parent.horizontalCenter
                                topMargin: 1
                                leftMargin: 1
                            }
                            color: 'white'
                            style: Text.Outline
                            styleColor: 'black'
                        }
                    }
                }

                Rectangle {
                    // effect names background
                    id: effectsRect
                    color: '#555555'
                    width: effectLabel.width + 2
                    height: effectLabel.height
                    x: labelRect.x
                    anchors.top: labelRect.bottom
                    visible: labelRect.visible && clipRoot.effectNames != '' && container.showDetails
                    Text {
                        // Effect names text
                        id: effectLabel
                        text: clipRoot.effectNames
844
                        font: miniFont
845
846
847
848
849
850
851
852
853
854
855
                        visible: effectsRect.visible
                        anchors {
                            top: effectsRect.top
                            left: effectsRect.left
                            leftMargin: 1
                            // + ((isAudio || !settings.timelineShowThumbnails) ? 0 : inThumbnail.width) + 1
                        }
                        color: 'white'
                        //style: Text.Outline
                        styleColor: 'black'
                    }
856
               }
857
858
859
               Rectangle{
                    //proxy 
                    id:proxyRect
860
                    color: '#fdbc4b'
861
862
863
864
865
                    width: labelRect.height
                    height: labelRect.height
                    x: labelRect.x
                    anchors.top: labelRect.top
                    anchors.left: labelRect.right
866
                    visible: !clipRoot.isAudio && clipRoot.clipStatus == ClipStatus.StatusProxy || clipRoot.clipStatus == ClipStatus.StatusProxyOnly
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
                    Text {
                        // Proxy P
                        id: proxyLabel
                        text: "P"
                        font.pointSize: root.fontUnit +1
                        visible: proxyRect.visible
                        anchors {
                            top: proxyRect.top
                            left: proxyRect.left
                            leftMargin: (labelRect.height-proxyLabel.width)/2
                            topMargin: (labelRect.height-proxyLabel.height)/2
                        }
                        color: 'black'
                        styleColor: 'black'
                    }
                }
883
            }
884
885
886
887
888
889
890
891
892
893
894
895

            KeyframeView {
                id: effectRow
                clip: true
                anchors.fill: parent
                visible: clipRoot.showKeyframes && clipRoot.keyframeModel
                selected: clipRoot.selected
                inPoint: clipRoot.inPoint
                outPoint: clipRoot.outPoint
                masterObject: clipRoot
                kfrModel: clipRoot.hideClipViews ? 0 : clipRoot.keyframeModel
            }
896
        }
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928

        states: [
            State {
                name: 'locked'
                when: isLocked
                PropertyChanges {
                    target: clipRoot
                    color: root.lockedColor
                    opacity: 0.8
                    z: 0
                }
            },
            State {
                name: 'normal'
                when: clipRoot.selected === false
                PropertyChanges {
                    target: clipRoot
                    color: Qt.darker(getColor(), 1.5)
                    z: 0
                }
            },
            State {
                name: 'selected'
                when: clipRoot.selected === true
                PropertyChanges {
                    target: clipRoot
                    color: getColor()
                    z: 3
                }
            }
        ]

929
930
931
        MouseArea {
            // Add start composition area
            id: compInArea
932
933
            anchors.left: parent.left
            anchors.bottom: parent.bottom
934
            width: Math.min(root.baseUnit, container.height / 3)
935
            height: width
936
937
938
939
940
941
942
943
944
945
946
947
948
949
            hoverEnabled: true
            cursorShape: Qt.PointingHandCursor
            visible: !clipRoot.isAudio
            enabled: !clipRoot.isAudio && dragProxy.draggedItem === clipRoot.clipId && compositionIn.visible
            onPressed: {
                timeline.addCompositionToClip('', clipRoot.clipId, 0)
            }
            ToolTip {
                visible: compInArea.containsMouse && !dragProxyArea.pressed
                delay: 1000
                timeout: 5000
                background: Rectangle {
                    color: activePalette.alternateBase
                    border.color: activePalette.light
950
                }
951
952
                contentItem: Label {
                    color: activePalette.text
953
                    font: miniFont
954
                    text: i18n("Click to add composition")
955
956
                }
            }
957
958
959
960
961
            Rectangle {
                // Start composition box
                id: compositionIn
                anchors.bottom: parent.bottom
                anchors.left: parent.left
962
                width: compInArea.containsMouse ? parent.width : 5
963
964
965
966
967
968
969
970
                height: width
                radius: width / 2
                visible: clipRoot.width > 4 * parent.width && mouseArea.containsMouse && !dragProxyArea.pressed
                color: Qt.darker('mediumpurple')
                border.width: 3
                border.color: 'mediumpurple'
                Behavior on width { NumberAnimation { duration: 100 } }
            }
971
        }
972
973
974
975

        MouseArea {
            // Add end composition area
            id: compOutArea
976
977
            anchors.right: parent.right
            anchors.bottom: parent.bottom
978
            width: Math.min(root.baseUnit, container.height / 3)
979
            height: width
980
981
982
983
984
985
            hoverEnabled: true
            cursorShape: Qt.PointingHandCursor
            enabled: !clipRoot.isAudio && dragProxy.draggedItem === clipRoot.clipId && compositionOut.visible
            visible: !clipRoot.isAudio
            onPressed: {
                timeline.addCompositionToClip('', clipRoot.clipId, clipRoot.clipDuration - 1)
986
            }
987
            ToolTip {
988
                visible: compOutArea.containsMouse && !dragProxyArea.pressed
989
990
991
992
993
994
995
996
                delay: 1000
                timeout: 5000
                background: Rectangle {
                    color: activePalette.alternateBase
                    border.color: activePalette.light
                }
                contentItem: Label {
                    color: activePalette.text
997
                    font: miniFont
998
                    text: i18n("Click to add composition")
999
1000
                }
            }
1001
1002
1003
1004
1005
            Rectangle {
                // End composition box
                id: compositionOut
                anchors.bottom: parent.bottom
                anchors.right: parent.right
1006
                width: compOutArea.containsMouse ? parent.height : 5
1007
1008
1009
1010
1011
1012
1013
                height: width
                radius: width / 2
                visible: clipRoot.width > 4 * parent.width && mouseArea.containsMouse && !dragProxyArea.pressed
                color: Qt.darker('mediumpurple')
                border.width: 3
                border.color: 'mediumpurple'
                Behavior on width { NumberAnimation { duration: 100 } }
Jean-Baptiste Mardelle's avatar