Clip.qml 34.6 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
import QtQuick 2.6
20
import QtQuick.Controls 2.2
21
import Kdenlive.Controls 1.0
22
23
import QtQml.Models 2.2
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.0
30
31
32
    property string clipName: ''
    property string clipResource: ''
    property string mltService: ''
33
    property string effectNames
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
34
    property int modelStart
35
    property real scrollX: 0
36
37
38
39
    property int inPoint: 0
    property int outPoint: 0
    property int clipDuration: 0
    property bool isAudio: false
40
    property int audioChannels
41
    property bool showKeyframes: false
42
    property bool isGrabbed: false
43
    property bool grouped: false
44
    property var markers
45
    property var keyframeModel
46
47
    property int clipStatus: 0
    property int itemType: 0
48
49
    property int fadeIn: 0
    property int fadeOut: 0
50
    property int binId: 0
51
    property int positionOffset: 0
52
    property var parentTrack
53
54
    property int trackIndex //Index in track repeater
    property int clipId     //Id of the clip in the model
55
56
57
    property int trackId: -1 // Id of the parent track in the model
    property int fakeTid: -1
    property int fakePosition: 0
58
    property int originalTrackId: -1
59
    property int originalX: x
60
61
    property int originalDuration: clipDuration
    property int lastValidDuration: clipDuration
62
    property int draggedX: x
63
    property bool selected: false
64
    property bool isLocked: parentTrack && parentTrack.isLocked == true
65
    property bool hasAudio
66
67
    property bool canBeAudio
    property bool canBeVideo
68
    property string hash: 'ccc' //TODO
69
    property double speed: 1.0
70
    property color borderColor: 'black'
71
    property bool forceReloadThumb
72
    property bool isComposition: false
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
73
    width : clipDuration * timeScale;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
74
    opacity: dragProxyArea.drag.active && dragProxy.draggedItem == clipId ? 0.8 : 1.0
75

76
77
78
79
    signal trimmingIn(var clip, real newDuration, var mouse, bool shiftTrim)
    signal trimmedIn(var clip, bool shiftTrim)
    signal trimmingOut(var clip, real newDuration, var mouse, bool shiftTrim)
    signal trimmedOut(var clip, bool shiftTrim)
80

81
82
83
84
85
86
87
    onIsGrabbedChanged: {
        if (clipRoot.isGrabbed) {
            clipRoot.forceActiveFocus();
            mouseArea.focus = true
        }
    }

88
89
    function clearAndMove(offset) {
        controller.requestClearSelection()
90
91
        controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true)
        controller.requestAddToSelection(clipRoot.clipId)
92
93
    }

94
    onInPointChanged: {
95
        if (parentTrack && parentTrack.isAudio && thumbsLoader.item) {
96
            thumbsLoader.item.reload()
97
        }
98
99
    }

100
    onClipResourceChanged: {
101
        if (itemType == ProducerType.Color) {
102
            color: Qt.darker(getColor(), 1.5)
103
104
        }
    }
105
    ToolTip {
106
        visible: mouseArea.containsMouse && !dragProxyArea.pressed
107
108
109
110
111
112
113
114
115
        font.pixelSize: root.baseUnit
        delay: 1000
        timeout: 5000
        background: Rectangle {
            color: activePalette.alternateBase
            border.color: activePalette.light
        }
        contentItem: Label {
            color: activePalette.text
116
            text: clipRoot.clipName + ' (' + timeline.timecode(clipRoot.inPoint) + '-' + timeline.timecode(clipRoot.outPoint) + ')'
117
118
119
        }
    }

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

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
127
128
    onClipDurationChanged: {
        width = clipDuration * timeScale;
129
    }
130

131
132
133
    onModelStartChanged: {
        x = modelStart * timeScale;
    }
134
135
136
137
138
    onFakePositionChanged: {
        x = fakePosition * timeScale;
    }
    onFakeTidChanged: {
        if (clipRoot.fakeTid > -1 && parentTrack) {
139
140
141
142
143
144
145
            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
            }
146
147
            clipRoot.y = Logic.getTrackById(clipRoot.fakeTid).y
        }
148
149
    }

150
    onForceReloadThumbChanged: {
151
        // TODO: find a way to force reload of clip thumbs
152
153
154
        if (thumbsLoader.item) {
            thumbsLoader.item.reload()
        }
155
156
    }

157
    onTimeScaleChanged: {
158
        x = modelStart * timeScale;
159
        width = clipDuration * timeScale;
160
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : 0
161
        if (parentTrack && parentTrack.isAudio) {
162
            thumbsLoader.item.reload();
163
        }
164
165
166
    }
    onScrollXChanged: {
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : 0
167
168
    }

Vincent Pinon's avatar
Vincent Pinon committed
169
    border.color: selected ? root.selectionColor : grouped ? root.groupColor : borderColor
170
    border.width: isGrabbed ? 8 : 1.5
171

172
173
174
175
176
    function updateDrag() {
        var itemPos = mapToItem(tracksContainerArea, 0, 0, clipRoot.width, clipRoot.height)
        initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
    }

177
    function getColor() {
178
179
180
        if (clipStatus == ClipState.Disabled) {
            return 'grey'
        }
181
        if (itemType == ProducerType.Color) {
182
183
184
            var color = clipResource.substring(clipResource.length - 9)
            if (color[0] == '#') {
                return color
185
            }
186
            return '#' + color.substring(color.length - 8, color.length - 2)
187
        }
188
        return isAudio? root.audioColor : root.videoColor
189
190
    }

191
/*    function reparent(track) {
192
        console.log('TrackId: ',trackId)
193
194
        parent = track
        height = track.height
195
        parentTrack = track
196
        trackId = parentTrack.trackId
197
        console.log('Reparenting clip to Track: ', trackId)
198
        //generateWaveform()
199
    }
200
*/
201
202
    property bool variableThumbs: (isAudio || itemType == ProducerType.Color || mltService === '')
    property bool isImage: itemType == ProducerType.Image
203
204
205
    property string baseThumbPath: variableThumbs ? '' : 'image://thumbnail/' + binId + '/' + (isImage ? '#0' : '#')
    property string inThumbPath: (variableThumbs || isImage ) ? baseThumbPath : baseThumbPath + Math.floor(inPoint * speed)
    property string outThumbPath: (variableThumbs || isImage ) ? baseThumbPath : baseThumbPath + Math.floor(outPoint * speed)
206

207
    DropArea { //Drop area for clips
208
209
210
        anchors.fill: clipRoot
        keys: 'kdenlive/effect'
        property string dropData
211
        property string dropSource
212
        property int dropRow: -1
213
214
        onEntered: {
            dropData = drag.getDataAsString('kdenlive/effect')
215
            dropSource = drag.getDataAsString('kdenlive/effectsource')
216
217
218
        }
        onDropped: {
            console.log("Add effect: ", dropData)
219
            if (dropSource == '') {
220
221
222
                // drop from effects list
                controller.addClipEffect(clipRoot.clipId, dropData);
            } else {
223
                controller.copyClipEffect(clipRoot.clipId, dropSource);
224
            }
225
            dropSource = ''
226
            dropRow = -1
227
            drag.acceptProposedAction
228
229
        }
    }
230
231
    MouseArea {
        id: mouseArea
232
        enabled: root.activeTool === 0
233
        anchors.fill: clipRoot
234
        acceptedButtons: Qt.RightButton
235
        hoverEnabled: root.activeTool === 0
236
        cursorShape: dragProxyArea.drag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor
237
238
        onPressed: {
            root.stopScrolling = true
239
            if (mouse.button == Qt.RightButton) {
240
                if (timeline.selection.indexOf(clipRoot.clipId) == -1) {
241
                    controller.requestAddToSelection(clipRoot.clipId, true)
242
243
244
                }
                clipMenu.clipId = clipRoot.clipId
                clipMenu.clipStatus = clipRoot.clipStatus
245
                clipMenu.clipFrame = Math.round(mouse.x / timeline.scaleFactor)
246
247
248
249
250
                clipMenu.grouped = clipRoot.grouped
                clipMenu.trackId = clipRoot.trackId
                clipMenu.canBeAudio = clipRoot.canBeAudio
                clipMenu.canBeVideo = clipRoot.canBeVideo
                clipMenu.popup()
251
            }
252
        }
253
        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)
254
255
256
257
258
259
        Keys.onLeftPressed: {
            controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - 1, true, true, true);
        }
        Keys.onRightPressed: {
            controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart + 1, true, true, true);
        }
260
261
262
263
264
265
        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);
        }
266
        onPositionChanged: {
267
268
            var mapped = parentTrack.mapFromItem(clipRoot, mouse.x, mouse.y).x
            root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
269
        }
270
271
272
273
        onEntered: {
            var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
            initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
        }
274

275
276
        onExited: {
            endDrag()
277
        }
278
        onWheel: zoomByWheel(wheel)
279

280
281
        Item {
            // Thumbs container
282
            anchors.fill: parent
283
284
285
286
287
288
289
290
291
292
293
294
295
296
            anchors.leftMargin: 0
            anchors.rightMargin: 0
            anchors.topMargin: clipRoot.border.width
            anchors.bottomMargin: clipRoot.border.width
            clip: true
            Loader {
                id: thumbsLoader
                asynchronous: true
                visible: status == Loader.Ready
                anchors.fill: parent
                source: parentTrack.isAudio ? (timeline.showAudioThumbnails ? "ClipAudioThumbs.qml" : "") : itemType == ProducerType.Color ? "" : timeline.showThumbnails ? "ClipThumbs.qml" : ""
                onLoaded: {
                    item.reload()
                }
297
            }
298
        }
299

300
301
302
303
304
305
        Item {
            // Clipping container
            id: container
            anchors.fill: parent
            anchors.margins: 1.5
            clip: true
306

307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
            Rectangle {
                // text background
                id: labelRect
                color: clipRoot.selected ? 'darkred' : '#66000000'
                width: label.width + 2
                height: label.height
                visible: clipRoot.width > width / 2
                Text {
                    id: label
                    text: clipName + (clipRoot.speed != 1.0 ? ' [' + Math.round(clipRoot.speed*100) + '%]': '')
                    font.pixelSize: root.baseUnit * 1.2
                    anchors {
                        top: labelRect.top
                        left: labelRect.left
                        topMargin: 1
                        leftMargin: 1
                    }
                    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
                visible: labelRect.visible && positionOffset != 0
                MouseArea {
                    id: offsetArea
                    hoverEnabled: true
                    cursorShape: Qt.PointingHandCursor
                    anchors.fill: parent
                    onClicked: {
                        clearAndMove(positionOffset)
                    }
                    ToolTip {
                        visible: offsetArea.containsMouse
                        font.pixelSize: root.baseUnit
                        delay: 1000
                        timeout: 5000
                        background: Rectangle {
                            color: activePalette.alternateBase
                            border.color: activePalette.light
                        }
                        contentItem: Label {
                            color: activePalette.text
                            text: i18n('Offset') + (positionOffset < 0 ? ( ': -' + timeline.timecode(-positionOffset)) : ': ' + timeline.timecode(positionOffset))
                        }
                    }
                    Text {
                        id: offsetLabel
                        text: positionOffset
                        font.pixelSize: root.baseUnit * 1.2
                        anchors {
                            horizontalCenter: parent.horizontalCenter
                            topMargin: 1
                            leftMargin: 1
                        }
                        color: 'white'
                        style: Text.Outline
                        styleColor: 'black'
                    }
                }
            }
            Rectangle {
                // effects
                id: effectsRect
                color: '#555555'
                width: effectLabel.width + 2
                height: effectLabel.height
                x: labelRect.x
                anchors.top: labelRect.bottom
                visible: labelRect.visible && clipRoot.effectNames != ''
                Text {
                    id: effectLabel
                    text: clipRoot.effectNames
                    font.pixelSize: root.baseUnit * 1.2
                    anchors {
                        top: effectsRect.top
                        left: effectsRect.left
                        topMargin: 1
                        leftMargin: 1
                        // + ((isAudio || !settings.timelineShowThumbnails) ? 0 : inThumbnail.width) + 1
                    }
                    color: 'white'
                    //style: Text.Outline
                    styleColor: 'black'
                }
            }

            Repeater {
                model: markers
                delegate:
                Item {
                    anchors.fill: parent
                    Rectangle {
                        id: markerBase
                        width: 1
                        height: parent.height
                        x: (model.frame - clipRoot.inPoint) * timeScale;
                        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
                            onDoubleClicked: timeline.editMarker(clipRoot.clipId, clipRoot.modelStart + model.frame - clipRoot.inPoint)
                            onClicked: timeline.position = (clipRoot.x + markerBase.x) / timeline.scaleFactor
                        }
                    }
                    Text {
                        id: mlabel
                        visible: timeline.showMarkers && parent.width > width * 1.5
                        text: model.comment
                        font.pixelSize: root.baseUnit
                        x: markerBase.x
                        anchors {
                            bottom: parent.verticalCenter
                            topMargin: 2
                            leftMargin: 2
                        }
                        color: 'white'
                    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
447
                }
448
449
450
451
452
453
454
455
456
457
            }

            KeyframeView {
                id: effectRow
                visible: clipRoot.showKeyframes && clipRoot.keyframeModel
                selected: clipRoot.selected
                inPoint: clipRoot.inPoint
                outPoint: clipRoot.outPoint
                masterObject: clipRoot
                kfrModel: clipRoot.keyframeModel
458
459
            }
        }
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491

        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
                }
            }
        ]

492
        Rectangle {
493
494
495
496
497
498
499
500
501
502
503
504
505
506
            id: compositionIn
            anchors.left: parent.left
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 2
            anchors.leftMargin: 4
            width: root.baseUnit * 1.2
            height: width
            radius: 2
            color: Qt.darker('mediumpurple')
            border.width: 2
            border.color: 'green'
            opacity: 0
            enabled: !clipRoot.isAudio && dragProxy.draggedItem == clipRoot.clipId
            visible: clipRoot.width > 4 * width
507
            MouseArea {
508
509
                id: compInArea
                anchors.fill: parent
510
511
                hoverEnabled: true
                cursorShape: Qt.PointingHandCursor
512
513
514
515
516
517
518
519
520
521
522
523
                onEntered: parent.opacity = 0.7
                onExited: {
                    if (!pressed) {
                    parent.opacity = 0
                    }
                }
                onPressed: {
                    timeline.addCompositionToClip('', clipRoot.clipId, 0)
                    endDrag()
                }
                onReleased: {
                    parent.opacity = 0
524
525
                }
                ToolTip {
526
                    visible: compInArea.containsMouse && !dragProxyArea.pressed
527
528
529
530
531
532
533
534
535
                    font.pixelSize: root.baseUnit
                    delay: 1000
                    timeout: 5000
                    background: Rectangle {
                        color: activePalette.alternateBase
                        border.color: activePalette.light
                    }
                    contentItem: Label {
                        color: activePalette.text
536
                        text: i18n('Click to add composition')
537
538
539
540
                    }
                }
            }
        }
541
        Rectangle {
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
            id: compositionOut
            anchors.right: parent.right
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 2
            anchors.rightMargin: 4
            width: root.baseUnit * 1.2
            height: width
            radius: 2
            color: Qt.darker('mediumpurple')
            border.width: 2
            border.color: 'green'
            opacity: 0
            enabled: !clipRoot.isAudio  && dragProxy.draggedItem == clipRoot.clipId
            visible: clipRoot.width > 4 * width
            MouseArea {
                id: compOutArea
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
558
                anchors.fill: parent
559
560
561
562
                hoverEnabled: true
                cursorShape: Qt.PointingHandCursor
                onEntered: {
                    parent.opacity = 0.7
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
563
                }
564
565
566
                onExited: {
                    if (!pressed) {
                        parent.opacity = 0
567
                    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
568
                }
569
570
571
572
573
574
575
576
577
                onPressed: {
                    timeline.addCompositionToClip('', clipRoot.clipId, clipRoot.clipDuration - 1)
                    endDrag()
                }
                onReleased: {
                    parent.opacity = 0
                }
                ToolTip {
                    visible: compOutArea.containsMouse && !dragProxyArea.pressed
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
578
                    font.pixelSize: root.baseUnit
579
580
581
582
583
584
585
586
587
                    delay: 1000
                    timeout: 5000
                    background: Rectangle {
                        color: activePalette.alternateBase
                        border.color: activePalette.light
                    }
                    contentItem: Label {
                        color: activePalette.text
                        text: i18n('Click to add composition')
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
588
589
                    }
                }
590
591
            }
        }
592

593

594
595
596
597
598
599
600
601
602
        TimelineTriangle {
            id: fadeInTriangle
            fillColor: 'green'
            width: Math.min(clipRoot.fadeIn * timeScale, clipRoot.width)
            height: clipRoot.height - clipRoot.border.width * 2
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.margins: clipRoot.border.width
            opacity: 0.3
603
        }
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
        Rectangle {
            id: fadeInControl
            anchors.left: fadeInTriangle.width > radius? undefined : fadeInTriangle.left
            anchors.horizontalCenter: fadeInTriangle.width > radius? fadeInTriangle.right : undefined
            anchors.top: fadeInTriangle.top
            anchors.topMargin: -10
            width: root.baseUnit * 2
            height: width
            radius: width / 2
            color: '#FF66FFFF'
            border.width: 2
            border.color: 'green'
            enabled: !isLocked && !dragProxy.isComposition
            opacity: 0
            visible : clipRoot.width > 3 * width
            Drag.active: fadeInMouseArea.drag.active
            MouseArea {
                id: fadeInMouseArea
                anchors.fill: parent
                hoverEnabled: true
                cursorShape: Qt.PointingHandCursor
                drag.target: parent
                drag.minimumX: -root.baseUnit
                drag.maximumX: container.width
                drag.axis: Drag.XAxis
                drag.smoothed: false
                property int startX
                property int startFadeIn
                onEntered: parent.opacity = 0.7
                onExited: {
                    if (!pressed) {
                    parent.opacity = 0
                    }
637
                }
638
639
640
641
642
643
644
645
646
                onPressed: {
                    root.stopScrolling = true
                    startX = Math.round(parent.x / timeScale)
                    startFadeIn = clipRoot.fadeIn
                    parent.anchors.left = undefined
                    parent.anchors.horizontalCenter = undefined
                    parent.opacity = 1
                    fadeInTriangle.opacity = 0.5
                    // parentTrack.clipSelected(clipRoot, parentTrack) TODO
647
                }
648
649
650
                onReleased: {
                    root.stopScrolling = false
                    fadeInTriangle.opacity = 0.3
651
                    parent.opacity = 0
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
                    if (fadeInTriangle.width > parent.radius)
                        parent.anchors.horizontalCenter = fadeInTriangle.right
                    else
                        parent.anchors.left = fadeInTriangle.left
                    console.log('released fade: ', clipRoot.fadeIn)
                    timeline.adjustFade(clipRoot.clipId, 'fadein', clipRoot.fadeIn, startFadeIn)
                    bubbleHelp.hide()
                }
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
                        var delta = Math.round(parent.x / timeScale) - startX
                        var duration = Math.max(0, startFadeIn + delta)
                        duration = Math.min(duration, clipRoot.clipDuration)
                        if (duration != clipRoot.fadeIn) {
                            timeline.adjustFade(clipRoot.clipId, 'fadein', duration, -1)
                            // Show fade duration as time in a "bubble" help.
                            var s = timeline.timecode(Math.max(duration, 0))
                            bubbleHelp.show(clipRoot.x, parentTrack.y + clipRoot.height, s)
                        }
                    }
672
                }
673
            }
674
675
676
677
678
679
680
681
            SequentialAnimation on scale {
                loops: Animation.Infinite
                running: fadeInMouseArea.containsMouse && !fadeInMouseArea.pressed
                NumberAnimation {
                    from: 1.0
                    to: 0.7
                    duration: 250
                    easing.type: Easing.InOutQuad
682
                }
683
684
685
686
687
                NumberAnimation {
                    from: 0.7
                    to: 1.0
                    duration: 250
                    easing.type: Easing.InOutQuad
688
689
690
691
                }
            }
        }

692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
        TimelineTriangle {
            id: fadeOutCanvas
            fillColor: 'red'
            width: Math.min(clipRoot.fadeOut * timeScale, clipRoot.width)
            height: clipRoot.height - clipRoot.border.width * 2
            anchors.right: parent.right
            anchors.top: parent.top
            anchors.margins: clipRoot.border.width
            opacity: 0.3
            transform: Scale { xScale: -1; origin.x: fadeOutCanvas.width / 2}
        }
        Rectangle {
            id: fadeOutControl
            anchors.right: fadeOutCanvas.width > radius? undefined : fadeOutCanvas.right
            anchors.horizontalCenter: fadeOutCanvas.width > radius? fadeOutCanvas.left : undefined
            anchors.top: fadeOutCanvas.top
            anchors.topMargin: -10
            width: root.baseUnit * 2
            height: width
            radius: width / 2
            color: '#66FFFFFF'
            border.width: 2
            border.color: 'red'
            opacity: 0
            enabled: !isLocked && !dragProxy.isComposition
            Drag.active: fadeOutMouseArea.drag.active
            visible : clipRoot.width > 3 * width
            MouseArea {
                id: fadeOutMouseArea
                anchors.fill: parent
                hoverEnabled: true
                cursorShape: Qt.PointingHandCursor
                drag.target: parent
                drag.axis: Drag.XAxis
                drag.minimumX: -root.baseUnit
                drag.maximumX: container.width
                property int startX
                property int startFadeOut
                onEntered: parent.opacity = 0.7
                onExited: {
                    if (!pressed) {
                    parent.opacity = 0
734
                    }
735
                }
736
737
738
739
740
741
742
743
744
                drag.smoothed: false
                onPressed: {
                    root.stopScrolling = true
                    startX = Math.round(parent.x / timeScale)
                    startFadeOut = clipRoot.fadeOut
                    parent.anchors.right = undefined
                    parent.anchors.horizontalCenter = undefined
                    parent.opacity = 1
                    fadeOutCanvas.opacity = 0.5
745
                }
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
                onReleased: {
                    fadeOutCanvas.opacity = 0.3
                    parent.opacity = 0
                    root.stopScrolling = false
                    if (fadeOutCanvas.width > parent.radius)
                        parent.anchors.horizontalCenter = fadeOutCanvas.left
                    else
                        parent.anchors.right = fadeOutCanvas.right
                    timeline.adjustFade(clipRoot.clipId, 'fadeout', clipRoot.fadeOut, startFadeOut)
                    bubbleHelp.hide()
                }
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
                        var delta = startX - Math.round(parent.x / timeScale)
                        var duration = Math.max(0, startFadeOut + delta)
                        duration = Math.min(duration, clipRoot.clipDuration)
                        if (clipRoot.fadeOut != duration) {
                            timeline.adjustFade(clipRoot.clipId, 'fadeout', duration, -1)
                            // Show fade duration as time in a "bubble" help.
                            var s = timeline.timecode(Math.max(duration, 0))
                            bubbleHelp.show(clipRoot.x + clipRoot.width, parentTrack.y + clipRoot.height, s)
                        }
768
                    }
769
770
                }
            }
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
            SequentialAnimation on scale {
                loops: Animation.Infinite
                running: fadeOutMouseArea.containsMouse && !fadeOutMouseArea.pressed
                NumberAnimation {
                    from: 1.0
                    to: 0.7
                    duration: 250
                    easing.type: Easing.InOutQuad
                }
                NumberAnimation {
                    from: 0.7
                    to: 1.0
                    duration: 250
                    easing.type: Easing.InOutQuad
                }
786
787
            }
        }
788
    }
789
790
791

    Rectangle {
        id: trimIn
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
792
        anchors.left: clipRoot.left
793
794
        anchors.leftMargin: 0
        height: parent.height
795
        enabled: !isLocked
796
797
798
799
800
        width: 5
        color: isAudio? 'green' : 'lawngreen'
        opacity: 0
        Drag.active: trimInMouseArea.drag.active
        Drag.proposedAction: Qt.MoveAction
801
        visible: trimInMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && clipRoot.width > 4 * width)
802
803
804
805
806
807
808

        MouseArea {
            id: trimInMouseArea
            anchors.fill: parent
            hoverEnabled: true
            drag.target: parent
            drag.axis: Drag.XAxis
809
            drag.smoothed: false
810
            property bool shiftTrim: false
811
            property bool sizeChanged: false
812
            cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
813
814
            onPressed: {
                root.stopScrolling = true
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
815
                clipRoot.originalX = clipRoot.x
816
                clipRoot.originalDuration = clipDuration
817
                parent.anchors.left = undefined
818
                shiftTrim = mouse.modifiers & Qt.ShiftModifier
819
                parent.opacity = 0
820
821
822
823
            }
            onReleased: {
                root.stopScrolling = false
                parent.anchors.left = clipRoot.left
824
825
826
827
                if (sizeChanged) {
                    clipRoot.trimmedIn(clipRoot, shiftTrim)
                    sizeChanged = false
                }
828
829
830
            }
            onPositionChanged: {
                if (mouse.buttons === Qt.LeftButton) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
831
                    var delta = Math.round((trimIn.x) / timeScale)
832
                    if (delta !== 0) {
833
834
835
                        if (delta < -modelStart) {
                            delta = -modelStart
                        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
836
                        var newDuration =  clipDuration - delta
837
                        sizeChanged = true
838
                        clipRoot.trimmingIn(clipRoot, newDuration, mouse, shiftTrim)
839
                    }
840
841
                }
            }
842
            onEntered: {
843
844
845
                if (!pressed) {
                    parent.opacity = 0.5
                }
846
847
848
849
            }
            onExited: {
                parent.opacity = 0
            }
850
851
852
853
        }
    }
    Rectangle {
        id: trimOut
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
854
        anchors.right: clipRoot.right
855
856
857
858
859
        anchors.rightMargin: 0
        height: parent.height
        width: 5
        color: 'red'
        opacity: 0
860
        enabled: !isLocked
861
862
        Drag.active: trimOutMouseArea.drag.active
        Drag.proposedAction: Qt.MoveAction
863
        visible: trimOutMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && clipRoot.width > 4 * width)
864
865
866
867
868

        MouseArea {
            id: trimOutMouseArea
            anchors.fill: parent
            hoverEnabled: true
869
            property bool shiftTrim: false
870
            property bool sizeChanged: false
871
            cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
872
873
            drag.target: parent
            drag.axis: Drag.XAxis
874
            drag.smoothed: false
875
876
877

            onPressed: {
                root.stopScrolling = true
878
                clipRoot.originalDuration = clipDuration
879
                parent.anchors.right = undefined
880
                shiftTrim = mouse.modifiers & Qt.ShiftModifier
881
                parent.opacity = 0
882
883
884
885
            }
            onReleased: {
                root.stopScrolling = false
                parent.anchors.right = clipRoot.right
886
887
888
889
                if (sizeChanged) {
                    clipRoot.trimmedOut(clipRoot, shiftTrim)
                    sizeChanged = false
                }
890
891
892
893
            }
            onPositionChanged: {
                if (mouse.buttons === Qt.LeftButton) {
                    var newDuration = Math.round((parent.x + parent.width) / timeScale)
894
                    if (newDuration != clipDuration) {
895
                        sizeChanged = true
896
                        clipRoot.trimmingOut(clipRoot, newDuration, mouse, shiftTrim)
897
                    }
898
899
                }
            }
900
901
902
903
904
            onEntered: {
                if (!pressed) {
                    parent.opacity = 0.5
                }
            }
905
906
907
            onExited: parent.opacity = 0
        }
    }
908

909
        /*MenuItem {
910
            id: mergeItem
911
            text: i18n('Merge with next clip')
912
913
914
            onTriggered: timeline.mergeClipWithNext(trackIndex, index, false)
        }
        MenuItem {
915
            text: i18n('Rebuild Audio Waveform')
916
            onTriggered: timeline.remakeAudioLevels(trackIndex, index)
917
        }*/
918
        /*onPopupVisibleChanged: {
919
920
921
922
923
            if (visible && application.OS !== 'OS X' && __popupGeometry.height > 0) {
                // Try to fix menu running off screen. This only works intermittently.
                menu.__yOffset = Math.min(0, Screen.height - (__popupGeometry.y + __popupGeometry.height + 40))
                menu.__xOffset = Math.min(0, Screen.width - (__popupGeometry.x + __popupGeometry.width))
            }
924
        }*/
925
}