Track.qml 20.1 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 QtQml.Models 2.11
21
import com.enums 1.0
22

23
Item{
24
    id: trackRoot
25
    property alias trackModel: trackModel.model
26
    property alias rootIndex : trackModel.rootIndex
27
28
29
30
    property bool isAudio
    property real timeScale: 1.0
    property bool isCurrentTrack: false
    property bool isLocked: false
31
    property int trackInternalId : -42
32
    property int trackThumbsFormat
33
    property int itemType: 0
34
    opacity: model.disabled ? 0.4 : 1
35
36
37
38
39

    function clipAt(index) {
        return repeater.itemAt(index)
    }

40
41
42
43
    function isClip(type) {
        return type != ProducerType.Composition && type != ProducerType.Track;
    }

44
45
46
47
    width: clipRow.width

    DelegateModel {
        id: trackModel
48
        delegate: Item {
49
            property var itemModel : model
50
            property bool clipItem: isClip(model.clipType)
51
            z: model.clipType == ProducerType.Composition ? 5 : model.mixDuration > 0 ? model.start / 25 : 0
52
            Loader {
53
54
55
56
57
                id: loader
                Binding {
                    target: loader.item
                    property: "timeScale"
                    value: trackRoot.timeScale
58
                    when: loader.status == Loader.Ready && loader.item
59
                }
60
61
                Binding {
                    target: loader.item
62
63
                    property: "fakeTid"
                    value: model.fakeTrackId
64
                    when: loader.status == Loader.Ready && loader.item && clipItem
65
                }
66
67
68
69
70
71
                Binding {
                    target: loader.item
                    property: "tagColor"
                    value: model.tag
                    when: loader.status == Loader.Ready && loader.item && isClip(model.clipType)
                }
72
73
74
75
                Binding {
                    target: loader.item
                    property: "fakePosition"
                    value: model.fakePosition
76
                    when: loader.status == Loader.Ready && loader.item && clipItem
77
78
79
80
81
                }
                Binding {
                    target: loader.item
                    property: "mixDuration"
                    value: model.mixDuration
82
83
84
85
86
87
88
                    when: loader.status == Loader.Ready && loader.item && clipItem
                }
                Binding {
                    target: loader.item
                    property: "mixCut"
                    value: model.mixCut
                    when: loader.status == Loader.Ready && loader.item && clipItem
89
                }
90
91
92
                Binding {
                    target: loader.item
                    property: "selected"
93
                    value: model.selected
94
                    when: loader.status == Loader.Ready && model.clipType != ProducerType.Track
95
                }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
96
97
98
99
                Binding {
                    target: loader.item
                    property: "mltService"
                    value: model.mlt_service
100
                    when: loader.status == Loader.Ready && loader.item
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
101
102
103
104
105
                }
                Binding {
                    target: loader.item
                    property: "modelStart"
                    value: model.start
106
                    when: loader.status == Loader.Ready && loader.item
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
107
                }
108
109
110
                Binding {
                    target: loader.item
                    property: "scrollX"
111
                    value: scrollView.contentX
112
                    when: loader.status == Loader.Ready && loader.item
113
                }
114
115
116
117
                Binding {
                    target: loader.item
                    property: "fadeIn"
                    value: model.fadeIn
118
                    when: loader.status == Loader.Ready && clipItem
119
                }
120
121
122
123
                Binding {
                    target: loader.item
                    property: "positionOffset"
                    value: model.positionOffset
124
                    when: loader.status == Loader.Ready && clipItem
125
                }
126
127
128
129
                Binding {
                    target: loader.item
                    property: "effectNames"
                    value: model.effectNames
130
                    when: loader.status == Loader.Ready && clipItem
131
                }
132
133
134
135
                Binding {
                    target: loader.item
                    property: "clipStatus"
                    value: model.clipStatus
136
                    when: loader.status == Loader.Ready && clipItem
137
                }
138
139
140
141
                Binding {
                    target: loader.item
                    property: "fadeOut"
                    value: model.fadeOut
142
                    when: loader.status == Loader.Ready && clipItem
143
                }
144
145
146
147
                Binding {
                    target: loader.item
                    property: "showKeyframes"
                    value: model.showKeyframes
148
                    when: loader.status == Loader.Ready && loader.item
149
                }
150
151
152
153
                Binding {
                    target: loader.item
                    property: "isGrabbed"
                    value: model.isGrabbed
154
                    when: loader.status == Loader.Ready && loader.item
155
                }
156
157
158
159
                Binding {
                    target: loader.item
                    property: "keyframeModel"
                    value: model.keyframeModel
160
                    when: loader.status == Loader.Ready && loader.item
161
                }
162
163
                Binding {
                    target: loader.item
164
                    property: "aTrack"
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
165
                    value: model.a_track
166
                    when: loader.status == Loader.Ready && model.clipType == ProducerType.Composition
167
                }
168
169
170
171
                Binding {
                    target: loader.item
                    property: "trackHeight"
                    value: root.trackHeight
172
                    when: loader.status == Loader.Ready && model.clipType == ProducerType.Composition
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
173
174
175
176
177
                }
                Binding {
                    target: loader.item
                    property: "clipDuration"
                    value: model.duration
178
                    when: loader.status == Loader.Ready && loader.item
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
179
180
181
182
183
                }
                Binding {
                    target: loader.item
                    property: "inPoint"
                    value: model.in
184
                    when: loader.status == Loader.Ready && loader.item
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
185
186
187
188
189
                }
                Binding {
                    target: loader.item
                    property: "outPoint"
                    value: model.out
190
                    when: loader.status == Loader.Ready && loader.item
191
                }
192
193
194
195
                Binding {
                    target: loader.item
                    property: "grouped"
                    value: model.grouped
196
                    when: loader.status == Loader.Ready && loader.item
197
                }
198
199
200
201
                Binding {
                    target: loader.item
                    property: "clipName"
                    value: model.name
202
                    when: loader.status == Loader.Ready && loader.item
203
                }
204
205
206
207
                Binding {
                    target: loader.item
                    property: "clipResource"
                    value: model.resource
208
                    when: loader.status == Loader.Ready && clipItem
209
                }
210
211
                Binding {
                    target: loader.item
212
213
                    property: "clipState"
                    value: model.clipState
214
215
                    when: loader.status == Loader.Ready && isClip(model.clipType)
                }
216
217
218
219
                Binding {
                    target: loader.item
                    property: "maxDuration"
                    value: model.maxDuration
220
                    when: loader.status == Loader.Ready && clipItem
221
                }
222
                Binding {
223
                    target: loader.item
224
225
                    property: "forceReloadThumb"
                    value: model.reloadThumb
226
                    when: loader.status == Loader.Ready && clipItem
227
228
229
230
231
                }
                Binding {
                    target: loader.item
                    property: "binId"
                    value: model.binId
232
                    when: loader.status == Loader.Ready && clipItem
233
                }
234
                sourceComponent: {
235
                    if (clipItem) {
236
237
                        return clipDelegate
                    } else if (model.clipType == ProducerType.Composition) {
238
                        return compositionDelegate
239
                    } else {
240
241
                        // Track
                        return undefined
242
243
244
                    }
                }
                onLoaded: {
245
                    item.clipId= model.item
246
                    item.parentTrack = trackRoot
247
                    if (clipItem) {
248
                        console.log('loaded clip: ', model.start, ', ID: ', model.item, ', index: ', trackRoot.DelegateModel.itemsIndex,', TYPE:', model.clipType)
249
                        item.isAudio= model.audio
250
                        item.markers= model.markers
251
                        item.hasAudio = model.hasAudio
252
253
                        item.canBeAudio = model.canBeAudio
                        item.canBeVideo = model.canBeVideo
254
                        item.itemType = model.clipType
255
                        item.audioChannels = model.audioChannels
256
                        item.audioStream = model.audioStream
257
                        item.multiStream = model.multiStream
258
                        item.aStreamIndex = model.audioStreamIndex
259
                        console.log('loaded clip with Astream: ', model.audioStream)
260
261
                        // Speed change triggers a new clip insert so no binding necessary
                        item.speed = model.speed
262
                    } else if (model.clipType == ProducerType.Composition) {
263
                        console.log('loaded composition: ', model.start, ', ID: ', model.item, ', index: ', trackRoot.DelegateModel.itemsIndex)
264
                        //item.aTrack = model.a_track
265
266
                    } else {
                        console.log('loaded unwanted element: ', model.item, ', index: ', trackRoot.DelegateModel.itemsIndex)
267
                    }
268
                    item.trackId = model.trackId
269
                    //item.selected= trackRoot.selection.indexOf(item.clipId) !== -1
270
                    //console.log(width, height);
271
272
273
274
275
276
277
278
279
280
                }
            }
        }
    }

    Item {
        id: clipRow
        height: trackRoot.height
        Repeater { id: repeater; model: trackModel }
    }
281

282
283
284
    Component {
        id: clipDelegate
        Clip {
285
            height: trackRoot.height
286
287
288
289
            onInitGroupTrim: {
                // We are resizing a group, remember coordinates of all elements
                clip.groupTrimData = controller.getGroupData(clip.clipId)
            }
290
            onTrimmingIn: {
291
                if (controlTrim) {
292
                    newDuration = controller.requestItemSpeedChange(clip.clipId, newDuration, false, root.snapping)
293
294
295
296
297
298
299
300
                    if (!speedController.visible) {
                        // Store original speed
                        speedController.originalSpeed = clip.speed
                    }
                    clip.x += clip.width - (newDuration * trackRoot.timeScale)
                    clip.width = newDuration * root.timeScale
                    speedController.x = clip.x + clip.border.width
                    speedController.width = clip.width - 2 * clip.border.width
301
                    speedController.lastValidDuration = newDuration
302
                    clip.speed = clip.originalDuration * speedController.originalSpeed / newDuration
303
304
305
                    speedController.visible = true
                    return
                }
306
                var new_duration = controller.requestItemResize(clip.clipId, newDuration, false, false, root.snapping, shiftTrim)
307
308
                if (new_duration > 0) {
                    clip.lastValidDuration = new_duration
309
                    clip.originalX = clip.draggedX
310
                    // Show amount trimmed as a time in a "bubble" help.
311
                    var delta = new_duration - clip.originalDuration
312
                    var s = timeline.simplifiedTC(Math.abs(delta))
313
                    s = '%1%2, %3:%4'.arg((delta <= 0)? '+' : '-')
314
                        .arg(s)
315
316
                        .arg(i18n("In"))
                        .arg(timeline.simplifiedTC(clip.inPoint))
317
318
                    timeline.showToolTip(s)
                    //bubbleHelp.show(clip.x - 20, trackRoot.y + trackRoot.height, s)
319
320
321
                }
            }
            onTrimmedIn: {
322
323
                //bubbleHelp.hide()
                timeline.showToolTip();
324
                if (shiftTrim || clip.groupTrimData == undefined || controlTrim) {
325
                    // We only resize one element
326
                    controller.requestItemResize(clip.clipId, clip.originalDuration, false, false, 0, shiftTrim)
327
328
                    if (controlTrim) {
                        // Update speed
329
330
331
                        speedController.visible = false
                        controller.requestClipResizeAndTimeWarp(clip.clipId, speedController.lastValidDuration, false, root.snapping, shiftTrim, clip.originalDuration * speedController.originalSpeed / speedController.lastValidDuration)
                        speedController.originalSpeed = 1
332
333
334
                    } else {
                        controller.requestItemResize(clip.clipId, clip.lastValidDuration, false, true, 0, shiftTrim)
                    }
335
336
337
338
339
                } else {
                    var updatedGroupData = controller.getGroupData(clip.clipId)
                    controller.processGroupResize(clip.groupTrimData, updatedGroupData, false)
                }
                clip.groupTrimData = undefined
340
341
            }
            onTrimmingOut: {
342
                if (controlTrim) {
343
344
345
346
347
                    if (!speedController.visible) {
                        // Store original speed
                        speedController.originalSpeed = clip.speed
                    }
                    speedController.x = clip.x + clip.border.width
348
                    newDuration = controller.requestItemSpeedChange(clip.clipId, newDuration, true, root.snapping)
349
350
                    clip.width = newDuration * trackRoot.timeScale
                    speedController.width = clip.width - 2 * clip.border.width
351
                    speedController.lastValidDuration = newDuration
352
                    clip.speed = clip.originalDuration * speedController.originalSpeed / newDuration
353
354
355
                    speedController.visible = true
                    return
                }
356
                var new_duration = controller.requestItemResize(clip.clipId, newDuration, true, false, root.snapping, shiftTrim)
357
358
                if (new_duration > 0) {
                    clip.lastValidDuration = new_duration
359
                    // Show amount trimmed as a time in a "bubble" help.
360
                    var delta = clip.originalDuration - new_duration
361
                    var s = timeline.simplifiedTC(Math.abs(delta))
362
                    s = '%1%2, %3:%4'.arg((delta <= 0)? '+' : '-')
363
                        .arg(s)
364
365
                        .arg(i18n("Duration"))
                        .arg(timeline.simplifiedTC(new_duration))
366
367
                    timeline.showToolTip(s);
                    //bubbleHelp.show(clip.x + clip.width - 20, trackRoot.y + trackRoot.height, s)
368
369
370
                }
            }
            onTrimmedOut: {
371
372
                timeline.showToolTip();
                //bubbleHelp.hide()
373
                if (shiftTrim || clip.groupTrimData == undefined || controlTrim) {
374
                    controller.requestItemResize(clip.clipId, clip.originalDuration, true, false, 0, shiftTrim)
375
                    if (controlTrim) {
376
                        speedController.visible = false
377
                        // Update speed
378
379
                        controller.requestClipResizeAndTimeWarp(clip.clipId, speedController.lastValidDuration, true, root.snapping, shiftTrim, clip.originalDuration * speedController.originalSpeed / speedController.lastValidDuration)
                        speedController.originalSpeed = 1
380
381
382
                    } else {
                        controller.requestItemResize(clip.clipId, clip.lastValidDuration, true, true, 0, shiftTrim)
                    }
383
384
385
386
                } else {
                    var updatedGroupData = controller.getGroupData(clip.clipId)
                    controller.processGroupResize(clip.groupTrimData, updatedGroupData, true)
                }
387
                clip.groupTrimData = undefined
388
389
390
            }
        }
    }
391
    Component {
392
393
        id: compositionDelegate
        Composition {
394
            displayHeight: Math.max(trackRoot.height / 2, trackRoot.height - (root.baseUnit * 2))
395
            opacity: 0.8
396
            selected: root.timelineSelection.indexOf(clipId) !== -1
397
            onTrimmingIn: {
398
399
                var new_duration = controller.requestItemResize(clip.clipId, newDuration, false, false, root.snapping)
                if (new_duration > 0) {
400
401
402
                    clip.lastValidDuration = newDuration
                    clip.originalX = clip.draggedX
                    // Show amount trimmed as a time in a "bubble" help.
403
                    var delta = clip.originalDuration - new_duration
404
                    var s = timeline.simplifiedTC(Math.abs(delta))
405
406
                    s = i18n("%1%2, Duration = %3", ((delta <= 0)? '+' : '-')
                        , s, timeline.simplifiedTC(new_duration))
407
                    timeline.showToolTip(s)
408
409
410
                }
            }
            onTrimmedIn: {
411
412
                timeline.showToolTip()
                //bubbleHelp.hide()
413
414
                controller.requestItemResize(clip.clipId, clip.originalDuration, false, false, root.snapping)
                controller.requestItemResize(clip.clipId, clip.lastValidDuration, false, true, root.snapping)
415
416
            }
            onTrimmingOut: {
417
418
                var new_duration = controller.requestItemResize(clip.clipId, newDuration, true, false, root.snapping)
                if (new_duration > 0) {
419
420
                    clip.lastValidDuration = newDuration
                    // Show amount trimmed as a time in a "bubble" help.
421
                    var delta = clip.originalDuration - new_duration
422
                    var s = timeline.simplifiedTC(Math.abs(delta))
423
424
                    s = i18n("%1%2, Duration = %3", ((delta <= 0)? '+' : '-')
                        , s, timeline.simplifiedTC(new_duration))
425
                    timeline.showToolTip(s)
426
427
428
                }
            }
            onTrimmedOut: {
429
430
                timeline.showToolTip()
                //bubbleHelp.hide()
431
432
                controller.requestItemResize(clip.clipId, clip.originalDuration, true, false, root.snapping)
                controller.requestItemResize(clip.clipId, clip.lastValidDuration, true, true, root.snapping)
433
434
            }
        }
435
    }
436
437
    Rectangle {
        id: speedController
438
439
        anchors.bottom: parent.bottom
        color: activePalette.highlight //'#cccc0000'
440
        visible: false
441
        height: root.baseUnit * 1.5
442
        property int lastValidDuration: 0
443
        property real originalSpeed: 1
444
445
        Text {
            id: speedLabel
446
            text: i18n("Adjusting speed")
447
            font: miniFont
448
449
450
            anchors.fill: parent
            verticalAlignment: Text.AlignVCenter
            horizontalAlignment: Text.AlignHCenter
451
            color: activePalette.highlightedText
452
        }
453
454
455
        transitions: [ Transition {
            NumberAnimation { property: "opacity"; duration: 300}
        } ]
456
    }
457
}