Clip.qml 38.2 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
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
    property int inPoint: 0
    property int outPoint: 0
    property int clipDuration: 0
39
    property int maxDuration: 0
40
    property bool isAudio: false
41
    property int audioChannels
42
    property bool showKeyframes: false
43
    property bool isGrabbed: false
44
    property bool grouped: false
45
    property var markers
46
    property var keyframeModel
47
48
    property int clipStatus: 0
    property int itemType: 0
49
50
    property int fadeIn: 0
    property int fadeOut: 0
51
    property int binId: 0
52
    property int positionOffset: 0
53
    property var parentTrack
54
55
    property int trackIndex //Index in track repeater
    property int clipId     //Id of the clip in the model
56
57
58
    property int trackId: -1 // Id of the parent track in the model
    property int fakeTid: -1
    property int fakePosition: 0
59
    property int originalTrackId: -1
60
    property int originalX: x
61
62
    property int originalDuration: clipDuration
    property int lastValidDuration: clipDuration
63
    property int draggedX: x
64
    property bool selected: false
65
    property bool isLocked: parentTrack && parentTrack.isLocked == true
66
    property bool hasAudio
67
68
    property bool canBeAudio
    property bool canBeVideo
69
    property double speed: 1.0
70
    property color borderColor: 'black'
71
    property bool forceReloadThumb
72
    property bool isComposition: false
73
    property bool hideClipViews: false
74
    property var groupTrimData
75
    property int scrollStart: scrollView.contentItem.contentX - (clipRoot.modelStart * timeline.scaleFactor)
76
    width : clipDuration * timeScale
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
77
    opacity: dragProxyArea.drag.active && dragProxy.draggedItem == clipId ? 0.8 : 1.0
78

79
80
    signal trimmingIn(var clip, real newDuration, var mouse, bool shiftTrim, bool controlTrim)
    signal trimmedIn(var clip, bool shiftTrim, bool controlTrim)
81
    signal initGroupTrim(var clip)
82
83
    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
84

85
    onScrollStartChanged: {
86
        clipRoot.hideClipViews = scrollStart > (clipDuration * timeline.scaleFactor) || scrollStart + scrollView.contentItem.width < 0
87
    }
88

89
90
    onIsGrabbedChanged: {
        if (clipRoot.isGrabbed) {
91
            grabItem()
92
93
        } else {
            mouseArea.focus = false
94
95
96
        }
    }

97
98
99
100
101
    function grabItem() {
        clipRoot.forceActiveFocus()
        mouseArea.focus = true
    }

102
103
    function clearAndMove(offset) {
        controller.requestClearSelection()
104
105
        controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true)
        controller.requestAddToSelection(clipRoot.clipId)
106
107
    }

108
    onClipResourceChanged: {
109
        if (itemType == ProducerType.Color) {
110
            color: Qt.darker(getColor(), 1.5)
111
112
        }
    }
113
    ToolTip {
114
        visible: mouseArea.containsMouse && !dragProxyArea.pressed
115
116
117
118
119
120
121
122
        delay: 1000
        timeout: 5000
        background: Rectangle {
            color: activePalette.alternateBase
            border.color: activePalette.light
        }
        contentItem: Label {
            color: activePalette.text
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
123
            font.pointSize: root.fontUnit
124
            text: label.text + ' (' + timeline.simplifiedTC(clipRoot.inPoint) + '-' + timeline.simplifiedTC(clipRoot.outPoint) + ')'
125
126
127
        }
    }

128
    onKeyframeModelChanged: {
129
        if (effectRow.keyframecanvas) {
130
            console.log('keyframe model changed............')
131
132
            effectRow.keyframecanvas.requestPaint()
        }
133
134
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
135
136
    onClipDurationChanged: {
        width = clipDuration * timeScale;
137
138
139
140
        if (parentTrack && parentTrack.isAudio && thumbsLoader.item) {
            // Duration changed, we may need a different number of repeaters
            thumbsLoader.item.reload()
        }
141
    }
142

143
144
145
    onModelStartChanged: {
        x = modelStart * timeScale;
    }
146

147
148
149
150
151
    onFakePositionChanged: {
        x = fakePosition * timeScale;
    }
    onFakeTidChanged: {
        if (clipRoot.fakeTid > -1 && parentTrack) {
152
153
154
155
156
157
158
            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
            }
159
160
            clipRoot.y = Logic.getTrackById(clipRoot.fakeTid).y
        }
161
162
    }

163
    onForceReloadThumbChanged: {
164
        // TODO: find a way to force reload of clip thumbs
165
166
167
        if (thumbsLoader.item) {
            thumbsLoader.item.reload()
        }
168
169
    }

170
    onTimeScaleChanged: {
171
        x = modelStart * timeScale;
172
        width = clipDuration * timeScale;
173
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : clipRoot.border.width
174
175
    }
    onScrollXChanged: {
176
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : clipRoot.border.width
177
178
    }

Vincent Pinon's avatar
Vincent Pinon committed
179
    border.color: 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
190
        if (clipStatus == ClipState.Disabled) {
            return 'grey'
        }
191
        if (itemType == ProducerType.Color) {
192
193
194
            var color = clipResource.substring(clipResource.length - 9)
            if (color[0] == '#') {
                return color
195
            }
196
            return '#' + color.substring(color.length - 8, color.length - 2)
197
        }
198
        return isAudio? root.audioColor : root.videoColor
199
200
    }

201
/*    function reparent(track) {
202
        console.log('TrackId: ',trackId)
203
204
        parent = track
        height = track.height
205
        parentTrack = track
206
        trackId = parentTrack.trackId
207
        console.log('Reparenting clip to Track: ', trackId)
208
        //generateWaveform()
209
    }
210
*/
211
    property bool noThumbs: (isAudio || itemType == ProducerType.Color || mltService === '')
212
    property bool isImage: itemType == ProducerType.Image
213
    property string baseThumbPath: noThumbs ? '' : 'image://thumbnail/' + binId + '/' + documentId + '/' + (isImage ? '#0' : '#')
214

215
    DropArea { //Drop area for clips
216
217
218
        anchors.fill: clipRoot
        keys: 'kdenlive/effect'
        property string dropData
219
        property string dropSource
220
        property int dropRow: -1
221
222
        onEntered: {
            dropData = drag.getDataAsString('kdenlive/effect')
223
            dropSource = drag.getDataAsString('kdenlive/effectsource')
224
225
226
        }
        onDropped: {
            console.log("Add effect: ", dropData)
227
            if (dropSource == '') {
228
229
230
                // drop from effects list
                controller.addClipEffect(clipRoot.clipId, dropData);
            } else {
231
                controller.copyClipEffect(clipRoot.clipId, dropSource);
232
            }
233
            dropSource = ''
234
            dropRow = -1
235
            drag.acceptProposedAction
236
237
        }
    }
238
239
    MouseArea {
        id: mouseArea
240
        enabled: root.activeTool === 0
241
        anchors.fill: clipRoot
242
        acceptedButtons: Qt.RightButton
243
        hoverEnabled: root.activeTool === 0
244
        cursorShape: (trimInMouseArea.drag.active || trimOutMouseArea.drag.active)? Qt.SizeHorCursor : dragProxyArea.cursorShape
245
        onPressed: {
246
            root.autoScrolling = false
247
            root.mainItemId = clipRoot.clipId
248
            if (mouse.button == Qt.RightButton) {
249
                if (timeline.selection.indexOf(clipRoot.clipId) == -1) {
250
                    controller.requestAddToSelection(clipRoot.clipId, true)
251
                }
252
                root.mainFrame = Math.round(mouse.x / timeline.scaleFactor)
253
                root.showClipMenu()
254
            }
255
        }
256
        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)
257
        Keys.onLeftPressed: {
258
259
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
            controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true);
260
261
        }
        Keys.onRightPressed: {
262
263
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
            controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart + offset, true, true, true);
264
        }
265
266
267
268
269
270
        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);
        }
271
272
        Keys.onEscapePressed: {
            timeline.grabCurrent()
273
            //focus = false
274
        }
275
        onPositionChanged: {
276
277
            var mapped = parentTrack.mapFromItem(clipRoot, mouse.x, mouse.y).x
            root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
278
        }
279
280
281
282
        onEntered: {
            var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
            initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
        }
283

284
285
        onExited: {
            endDrag()
286
        }
287
        onWheel: zoomByWheel(wheel)
288

289
        Loader {
290
            // Thumbs container
291
            id: thumbsLoader
292
            anchors.fill: parent
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
293
294
            anchors.leftMargin: parentTrack.isAudio ? 0 : clipRoot.border.width
            anchors.rightMargin: parentTrack.isAudio ? 0 : clipRoot.border.width
295
296
297
            anchors.topMargin: clipRoot.border.width
            anchors.bottomMargin: clipRoot.border.width
            clip: true
298
299
300
            asynchronous: true
            visible: status == Loader.Ready
            source: clipRoot.hideClipViews ? "" : parentTrack.isAudio ? (timeline.showAudioThumbnails ? "ClipAudioThumbs.qml" : "") : itemType == ProducerType.Color ? "" : timeline.showThumbnails ? "ClipThumbs.qml" : ""
301
        }
302

303
304
305
306
        Item {
            // Clipping container
            id: container
            anchors.fill: parent
307
308
            anchors.margins: clipRoot.border.width
            //clip: true
309
            property bool showDetails: (!clipRoot.selected || !effectRow.visible) && container.height > 2.2 * labelRect.height
310
311

            Repeater {
312
                // Clip markers
313
314
315
316
317
318
319
320
                model: markers
                delegate:
                Item {
                    anchors.fill: parent
                    Rectangle {
                        id: markerBase
                        width: 1
                        height: parent.height
321
                        x: clipRoot.speed < 0 ? clipRoot.clipDuration * timeScale + (Math.round(model.frame / clipRoot.speed) - (clipRoot.maxDuration - clipRoot.outPoint)) * timeScale - clipRoot.border.width : (Math.round(model.frame / clipRoot.speed) - clipRoot.inPoint) * timeScale - clipRoot.border.width;
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
                        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
341
                            onDoubleClicked: timeline.editMarker(clipRoot.clipId, model.frame)
342
                            onClicked: proxy.position = (clipRoot.x + markerBase.x) / timeline.scaleFactor
343
344
345
346
347
348
                        }
                    }
                    Text {
                        id: mlabel
                        visible: timeline.showMarkers && parent.width > width * 1.5
                        text: model.comment
349
                        font.pointSize: root.fontUnit
350
351
352
353
354
355
356
357
                        x: markerBase.x
                        anchors {
                            bottom: parent.verticalCenter
                            topMargin: 2
                            leftMargin: 2
                        }
                        color: 'white'
                    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
358
                }
359
360
            }

361
362
363
            MouseArea {
                // Left resize handle
                id: trimInMouseArea
364
                x: -clipRoot.border.width
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
                height: parent.height
                width: root.baseUnit / 2
                enabled: !isLocked
                hoverEnabled: true
                drag.target: trimInMouseArea
                drag.axis: Drag.XAxis
                drag.smoothed: false
                property bool shiftTrim: false
                property bool controlTrim: false
                property bool sizeChanged: false
                cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
                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
389
                    x = -clipRoot.border.width
390
391
392
393
394
395
396
                    if (sizeChanged) {
                        clipRoot.trimmedIn(clipRoot, shiftTrim, controlTrim)
                        sizeChanged = false
                    }
                }
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
397
398
399
                        var currentFrame = Math.round((clipRoot.x + (x + clipRoot.border.width)) / timeScale)
                        var currentClipPos = clipRoot.modelStart
                        var delta = currentFrame - currentClipPos
400
                        if (delta !== 0) {
401
                            if (maxDuration > 0 && delta < -inPoint && !(mouse.modifiers & Qt.ControlModifier)) {
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
447
448
449
450
451
452
453
454
455
456
457
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
                                delta = -inPoint
                            }
                            var newDuration =  clipDuration - delta
                            sizeChanged = true
                            clipRoot.trimmingIn(clipRoot, newDuration, mouse, shiftTrim, controlTrim)
                        }
                    }
                }
                onEntered: {
                    if (!pressed) {
                        trimIn.opacity = 1
                    }
                }
                onExited: {
                    trimIn.opacity = 0
                }
                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
                    visible: trimInMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && clipRoot.width > 4 * width)

                    ToolTip {
                        visible: trimInMouseArea.containsMouse && !trimInMouseArea.pressed
                        delay: 1000
                        timeout: 5000
                        background: Rectangle {
                            color: activePalette.alternateBase
                            border.color: activePalette.light
                        }
                        contentItem: Label {
                            color: activePalette.text
                            font.pointSize: root.fontUnit
                            text: i18n("In:%1\nPosition:%2", timeline.simplifiedTC(clipRoot.inPoint),timeline.simplifiedTC(clipRoot.modelStart))
                        }
                    }
                }
            }

            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
                enabled: !isLocked
                property bool shiftTrim: false
                property bool controlTrim: false
                property bool sizeChanged: false
                cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
                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
                    }
                }
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
                        var newDuration = Math.round((x + width) / timeScale)
486
                        if (maxDuration > 0 && (newDuration > maxDuration - inPoint) && !(mouse.modifiers & Qt.ControlModifier)) {
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
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
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
                            newDuration = maxDuration - inPoint
                        }
                        if (newDuration != clipDuration) {
                            sizeChanged = true
                            clipRoot.trimmingOut(clipRoot, newDuration, mouse, shiftTrim, controlTrim)
                        }
                    }
                }
                onEntered: {
                    if (!pressed) {
                        trimOut.opacity = 1
                    }
                }
                onExited: trimOut.opacity = 0
                ToolTip {
                    visible: trimOutMouseArea.containsMouse && !trimOutMouseArea.pressed
                    delay: 1000
                    timeout: 5000
                    background: Rectangle {
                        color: activePalette.alternateBase
                        border.color: activePalette.light
                    }
                    contentItem: Label {
                        color: activePalette.text
                        font.pointSize: root.fontUnit
                        text: i18n("Out: ") + timeline.simplifiedTC(clipRoot.outPoint)
                    }
                }
                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
                    visible: trimOutMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && clipRoot.width > 4 * width)
                }
            }

            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
                clip: true
                Rectangle {
                    // Clip name background
                    id: labelRect
                    color: clipRoot.selected ? 'darkred' : '#66000000'
                    y: 0
                    width: label.width + 2 * clipRoot.border.width
                    height: label.height
                    visible: clipRoot.width > width / 2
                    Text {
                        // Clip name text
                        id: label
                        text: clipName + (clipRoot.speed != 1.0 ? ' [' + Math.round(clipRoot.speed*100) + '%]': '')
                        font.pointSize: root.fontUnit
                        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)
                        }
                        ToolTip {
                            visible: offsetArea.containsMouse
                            delay: 1000
                            timeout: 5000
                            background: Rectangle {
                                color: activePalette.alternateBase
                                border.color: activePalette.light
                            }
                            contentItem: Label {
                                color: activePalette.text
                                font.pointSize: root.fontUnit
                                text: positionOffset < 0 ? i18n("Offset: -%1", timeline.simplifiedTC(-positionOffset)) : i18n("Offset: %1", timeline.simplifiedTC(positionOffset))
                            }
                        }
                        Text {
                            id: offsetLabel
                            text: positionOffset
                            font.pointSize: root.fontUnit
                            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
                        font.pointSize: root.fontUnit
                        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'
                    }
                }
            }
654
655
656
657
658
659
660
661
662
663
664
665

            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
            }
666
        }
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698

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

699
700
701
        MouseArea {
            // Add start composition area
            id: compInArea
702
703
            anchors.left: parent.left
            anchors.bottom: parent.bottom
704
            width: Math.min(root.baseUnit, container.height / 3)
705
            height: width
706
707
708
709
710
711
712
713
714
715
716
717
718
719
            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
720
                }
721
722
723
724
                contentItem: Label {
                    color: activePalette.text
                    font.pointSize: root.fontUnit
                    text: i18n("Click to add composition")
725
726
                }
            }
727
728
729
730
731
            Rectangle {
                // Start composition box
                id: compositionIn
                anchors.bottom: parent.bottom
                anchors.left: parent.left
732
                width: compInArea.containsMouse ? parent.width : 5
733
734
735
736
737
738
739
740
                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 } }
            }
741
        }
742
743
744
745

        MouseArea {
            // Add end composition area
            id: compOutArea
746
747
            anchors.right: parent.right
            anchors.bottom: parent.bottom
748
            width: Math.min(root.baseUnit, container.height / 3)
749
            height: width
750
751
752
753
754
755
            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)
756
            }
757
            ToolTip {
758
                visible: compOutArea.containsMouse && !dragProxyArea.pressed
759
760
761
762
763
764
765
766
767
                delay: 1000
                timeout: 5000
                background: Rectangle {
                    color: activePalette.alternateBase
                    border.color: activePalette.light
                }
                contentItem: Label {
                    color: activePalette.text
                    font.pointSize: root.fontUnit
768
                    text: i18n("Click to add composition")
769
770
                }
            }
771
772
773
774
775
            Rectangle {
                // End composition box
                id: compositionOut
                anchors.bottom: parent.bottom
                anchors.right: parent.right
776
                width: compOutArea.containsMouse ? parent.height : 5
777
778
779
780
781
782
783
                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 } }
784
785
            }
        }
786
787
788

        MouseArea {
            // Fade out drag zone
789
            id: fadeOutMouseArea
790
791
792
            anchors.right: parent.right
            anchors.rightMargin: clipRoot.fadeOut <= 0 ? 0 : fadeOutCanvas.width - width / 2
            anchors.top: parent.top
793
            width: Math.min(root.baseUnit, container.height / 3)
794
            height: width
795
            hoverEnabled: true
796
797
            cursorShape: Qt.PointingHandCursor
            drag.target: fadeOutMouseArea
798
            drag.axis: Drag.XAxis
799
            drag.minimumX: - Math.ceil(width / 2)
800
801
            drag.maximumX: container.width + Math.ceil(width / 4)
            visible: clipRoot.width > 3 * width && mouseArea.containsMouse && !dragProxyArea.pressed
802
803
            property int startFadeOut
            property int lastDuration: -1
804
805
806
807
            drag.smoothed: false
            onClicked: {
                if (clipRoot.fadeOut == 0) {
                    timeline.adjustFade(clipRoot.clipId, 'fadeout', 0, -2)
808
809
                }
            }
810
            onPressed: {
811
                root.autoScrolling = false
812
813
                startFadeOut = clipRoot.fadeOut
                anchors.right = undefined
814
                fadeOutCanvas.opacity = 0.6
815
816
            }
            onReleased: {
817
                fadeOutCanvas.opacity = 0.4
818
                root.autoScrolling = timeline.autoScroll
819
                anchors.right = parent.right
820
821
822
                var duration = clipRoot.fadeOut
                if (duration > 0) {
                    duration += 1
823
                }
824
825
                timeline.adjustFade(clipRoot.clipId, 'fadeout', duration, startFadeOut)
                bubbleHelp.hide()
826
827
828
            }
            onPositionChanged: {
                if (mouse.buttons === Qt.LeftButton) {
829
                    var delta = clipRoot.clipDuration - Math.floor((x + width / 2 - clipRoot.border.width)/ timeScale)
830
831
832
833
834
835
836
837
                    var duration = Math.max(0, delta)
                    duration = Math.min(duration, clipRoot.clipDuration)
                    if (lastDuration != duration) {
                        lastDuration = duration
                        timeline.adjustFade(clipRoot.clipId, 'fadeout', duration, -1)
                        // Show fade duration as time in a "bubble" help.
                        var s = timeline.simplifiedTC(clipRoot.fadeOut + (clipRoot.fadeOut > 0 ? 1 : 0))
                        bubbleHelp.show(clipRoot.x + x, parentTrack.y + parentTrack.height, s)
838
                    }
839
840
                }
            }
841
842
            Rectangle {
                id: fadeOutControl
843
844
845
                anchors.top: parent.top
                anchors.right: clipRoot.fadeOut > 0 ? undefined : parent.right
                anchors.horizontalCenter: clipRoot.fadeOut > 0 ? parent.horizontalCenter : undefined
846
                width: fadeOutMouseArea.containsMouse || Drag.active ? parent.width : 5
847
                height: width
848
                radius: width / 2
849
850
                color: 'darkred'
                border.width: 3
851
852
853
                border.color: 'red'
                enabled: !isLocked && !dragProxy.isComposition
                Drag.active: fadeOutMouseArea.drag.active
854
                Behavior on width { NumberAnimation { duration: 100 } }
855
856
857
858
859
                Rectangle {
                    id: fadeOutMarker
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.top: parent.top
                    color: 'red'
860
                    height: container.height
861
                    width: 1
862
                    visible : clipRoot.fadeOut > 0 && (fadeOutMouseArea.containsMouse || fadeOutMouseArea.drag.active)
863
                }
864
            }
865
        }
866

867
        MouseArea {
868
            // Fade in drag zone
869
            id: fadeInMouseArea
870
871
872
            anchors.left: container.left
            anchors.leftMargin: clipRoot.fadeIn <= 0 ? 0 : (fadeInTriangle.width - width / 2)
            anchors.top: parent.top
873
            width: Math.min(root.baseUnit, container.height / 3)
874
            height: width
875
            hoverEnabled: true
876
877
878
879
            cursorShape: Qt.PointingHandCursor
            drag.target: fadeInMouseArea
            drag.minimumX: - Math.ceil(width / 2)
            drag.maximumX: container.width - width / 2
880
            drag.axis: Drag.XAxis
881
            drag.smoothed: false
882
            property int startFadeIn
883
884
885
886
            visible: clipRoot.width > 3 * width && mouseArea.containsMouse && !dragProxyArea.pressed
            onClicked: {
                if (clipRoot.fadeIn == 0) {
                    timeline.adjustFade(clipRoot.clipId, 'fadein', 0, -2)
887
888
                }
            }
889
            onPressed: {
890
                root.autoScrolling = false
891
892
                startFadeIn = clipRoot.fadeIn
                anchors.left = undefined
893
                fadeInTriangle.opacity = 0.6
894
                // parentTrack.clipSelected(clipRoot, parentTrack) TODO
895
896
            }
            onReleased: {
897
                root.autoScrolling = timeline.autoScroll
898
                fadeInTriangle.opacity = 0.4
899
900
                timeline.adjustFade(clipRoot.clipId, 'fadein', clipRoot.fadeIn, startFadeIn)
                bubbleHelp.hide()
901
                anchors.left = container.left
902
903
904
            }
            onPositionChanged: {
                if (mouse.buttons === Qt.LeftButton) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
905
                    var delta = Math.round((x + width / 2) / timeScale)
906
907
908
909
910
911
912
                    var duration = Math.max(0, delta)
                    duration = Math.min(duration, clipRoot.clipDuration - 1)
                    if (duration != clipRoot.fadeIn) {
                        timeline.adjustFade(clipRoot.clipId, 'fadein', duration, -1)
                        // Show fade duration as time in a "bubble" help.
                        var s = timeline.simplifiedTC(clipRoot.fadeIn)
                        bubbleHelp.show(clipRoot.x + x, parentTrack.y + parentTrack.height, s)
913
                    }
914
915
                }
            }
916
917
            Rectangle {
                id: fadeInControl
918
919
920
                anchors.top: parent.top
                anchors.left: clipRoot.fadeIn > 0 ? undefined : parent.left
                anchors.horizontalCenter: clipRoot.fadeIn > 0 ? parent.horizontalCenter : undefined
921
                width: fadeInMouseArea.containsMouse || Drag.active ? parent.width : 5
922
                height: width
923
                radius: width / 2
924
925
926
                color: 'green'
                border.width: 3
                border.color: '#FF66FFFF'
927
928
                enabled: !isLocked && !dragProxy.isComposition
                Drag.active: fadeInMouseArea.drag.active
929
                Behavior on width { NumberAnimation { duration: 100 } }
930
931
932
933
                Rectangle {
                    id: fadeInMarker
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.top: parent.top
934
935
                    color: '#FF66FFFF'
                    height: container.height
936
                    width: 1
937
                    visible : clipRoot.fadeIn > 0 && (fadeInMouseArea.containsMouse || fadeInMouseArea.drag.active)
938
939
                }
            }
940
        }
941
    }
942
}