diff --git a/Messages.sh b/Messages.sh
index b065c3d46..2372d6c56 100755
--- a/Messages.sh
+++ b/Messages.sh
@@ -1,9 +1,9 @@
-#!bin/sh
+#!/bin/sh
kdenlive_subdirs="plugins renderer data src src/ui"
$EXTRACTRC --tag=name --tag=description --tag=label --tag=comment --tag=paramlistdisplay data/effects/*.xml data/kdenliveeffectscategory.rc >> rc.cpp
$EXTRACTRC `find $kdenlive_subdirs -name \*.rc -a ! -name encodingprofiles.rc -a ! -name camcorderfilters.rc -o -name \*.ui` >> rc.cpp
-$XGETTEXT `find $kdenlive_subdirs -name \*.cpp -o -name \*.h` *.cpp -o $podir/kdenlive.pot
+$XGETTEXT `find $kdenlive_subdirs -name \*.cpp -o -name \*.h -o -name \*.qml` *.cpp -o $podir/kdenlive.pot
rm -f rc.cpp
diff --git a/src/assets/assetlist/view/qml/assetList.qml b/src/assets/assetlist/view/qml/assetList.qml
index dd6f76922..efdd93001 100644
--- a/src/assets/assetlist/view/qml/assetList.qml
+++ b/src/assets/assetlist/view/qml/assetList.qml
@@ -1,382 +1,382 @@
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.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 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* 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 . *
***************************************************************************/
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 1.5
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.2
import QtQml.Models 2.2
Rectangle {
id: listRoot
SystemPalette { id: activePalette }
color: activePalette.window
function assetType(){
return isEffectList ? i18n("effects") : i18n("compositions");
}
function expandNodes(indexes) {
for(var i = 0; i < indexes.length; i++) {
if (indexes[i].valid) {
treeView.expand(indexes[i]);
}
}
}
function rowPosition(model, index) {
var pos = 0;
for(var i = 0; i < index.parent.row; i++) {
var catIndex = model.getCategory(i);
if (treeView.isExpanded(catIndex)) {
pos += model.rowCount(catIndex);
}
pos ++;
}
pos += index.row + 2;
return pos;
}
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: false
spacing: 4
ExclusiveGroup { id: filterGroup}
ToolButton {
id: searchList
iconName: "edit-find"
checkable: true
- tooltip: isEffectList ? i18n('Find effect') : i18n('Find composition')
+ tooltip: isEffectList ? i18n("Find effect") : i18n("Find composition")
onCheckedChanged: {
searchInput.visible = searchList.checked
searchInput.focus = searchList.checked
if (!searchList.checked) {
searchInput.text = ''
treeView.focus = true
}
}
}
ToolButton {
id: showAll
iconName: "show-all-effects"
checkable: true
checked: true
exclusiveGroup: filterGroup
- tooltip: i18n('Main %1', assetType())
+ tooltip: i18n("Main %1", assetType())
onClicked: {
assetlist.setFilterType("")
}
}
ToolButton {
id: showVideo
visible: isEffectList
iconName: "kdenlive-show-video"
iconSource: 'qrc:///pics/kdenlive-show-video.svgz'
checkable:true
exclusiveGroup: filterGroup
- tooltip: i18n('Show all video effects')
+ tooltip: i18n("Show all video effects")
onClicked: {
assetlist.setFilterType("video")
}
}
ToolButton {
id: showAudio
visible: isEffectList
iconName: "kdenlive-show-audio"
iconSource: 'qrc:///pics/kdenlive-show-audio.svgz'
checkable:true
exclusiveGroup: filterGroup
- tooltip: i18n('Show all audio effects')
+ tooltip: i18n("Show all audio effects")
onClicked: {
assetlist.setFilterType("audio")
}
}
ToolButton {
id: showCustom
visible: isEffectList
iconName: "kdenlive-custom-effect"
checkable:true
exclusiveGroup: filterGroup
- tooltip: i18n('Show all custom effects')
+ tooltip: i18n("Show all custom effects")
onClicked: {
assetlist.setFilterType("custom")
}
}
ToolButton {
id: showFavorites
iconName: "favorite"
checkable:true
exclusiveGroup: filterGroup
- tooltip: i18n('Show favorite items')
+ tooltip: i18n("Show favorite items")
onClicked: {
assetlist.setFilterType("favorites")
}
}
ToolButton {
id: downloadTransitions
visible: !isEffectList
iconName: "edit-download"
- tooltip: i18n('Download New Wipes...')
+ tooltip: i18n("Download New Wipes...")
onClicked: {
assetlist.downloadNewLumas()
}
}
Rectangle {
//This is a spacer
Layout.fillHeight: false
Layout.fillWidth: true
color: "transparent"
}
ToolButton {
id: showDescription
iconName: "help-about"
checkable:true
- tooltip: i18n('Show/hide description of the ') + assetType()
+ tooltip: i18n("Show/hide description of the %1", assetType())
onCheckedChanged:{
assetlist.showDescription = checked
}
Component.onCompleted: checked = assetlist.showDescription
}
}
TextField {
id: searchInput
Layout.fillWidth:true
visible: false
Image {
id: clear
source: 'image://icon/edit-clear'
width: parent.height * 0.8
height: width
anchors { right: parent.right; rightMargin: 8; verticalCenter: parent.verticalCenter }
opacity: 0
MouseArea {
anchors.fill: parent
onClicked: { searchInput.text = ''; searchInput.focus = true; searchList.checked = false; }
}
}
states: State {
name: "hasText"; when: searchInput.text != ''
PropertyChanges { target: clear; opacity: 1 }
}
transitions: [
Transition {
from: ""; to: "hasText"
NumberAnimation { properties: "opacity" }
},
Transition {
from: "hasText"; to: ""
NumberAnimation { properties: "opacity" }
}
]
onTextChanged: {
var current = sel.currentIndex
var rowModelIndex = assetListModel.getModelIndex(sel.currentIndex);
assetlist.setFilterName(text)
if (text.length > 0) {
sel.setCurrentIndex(assetListModel.firstVisibleItem(current), ItemSelectionModel.ClearAndSelect)
} else {
sel.clearCurrentIndex()
sel.setCurrentIndex(assetListModel.getProxyIndex(rowModelIndex), ItemSelectionModel.ClearAndSelect)
}
treeView.__listView.positionViewAtIndex(rowPosition(assetListModel, sel.currentIndex), ListView.Visible)
}
/*onEditingFinished: {
if (!assetContextMenu.isDisplayed) {
searchList.checked = false
}
}*/
Keys.onDownPressed: {
sel.setCurrentIndex(assetListModel.getNextChild(sel.currentIndex), ItemSelectionModel.ClearAndSelect)
treeView.expand(sel.currentIndex.parent)
treeView.__listView.positionViewAtIndex(rowPosition(assetListModel, sel.currentIndex), ListView.Visible)
}
Keys.onUpPressed: {
sel.setCurrentIndex(assetListModel.getPreviousChild(sel.currentIndex), ItemSelectionModel.ClearAndSelect)
treeView.expand(sel.currentIndex.parent)
treeView.__listView.positionViewAtIndex(rowPosition(assetListModel, sel.currentIndex), ListView.Visible)
}
Keys.onReturnPressed: {
if (sel.hasSelection) {
assetlist.activate(sel.currentIndex)
searchList.checked = false
}
}
}
ItemSelectionModel {
id: sel
model: assetListModel
onSelectionChanged: {
assetDescription.text = assetlist.getDescription(sel.currentIndex)
}
}
SplitView {
orientation: Qt.Vertical
Layout.fillHeight: true
Layout.fillWidth: true
TreeView {
id: treeView
Layout.fillHeight: true
Layout.fillWidth: true
alternatingRowColors: false
headerVisible: false
selection: sel
selectionMode: SelectionMode.SingleSelection
itemDelegate: Rectangle {
id: assetDelegate
// These anchors are important to allow "copy" dragging
anchors.verticalCenter: parent ? parent.verticalCenter : undefined
anchors.right: parent ? parent.right : undefined
property bool isItem : styleData.value !== "root" && styleData.value !== ""
property string mimeType : isItem ? assetlist.getMimeType(styleData.value) : ""
height: assetText.implicitHeight
color: dragArea.containsMouse ? activePalette.highlight : "transparent"
Drag.active: isItem ? dragArea.drag.active : false
Drag.dragType: Drag.Automatic
Drag.supportedActions: Qt.CopyAction
Drag.mimeData: isItem ? assetlist.getMimeData(styleData.value) : {}
Drag.keys:[
isItem ? assetlist.getMimeType(styleData.value) : ""
]
Row {
anchors.fill:parent
anchors.leftMargin: 1
anchors.topMargin: 1
anchors.bottomMargin: 1
spacing: 4
Image{
id: assetThumb
anchors.verticalCenter: parent.verticalCenter
visible: assetDelegate.isItem
property bool isFavorite: model == undefined || model.favorite === undefined ? false : model.favorite
height: parent.height * 0.8
width: height
source: 'image://asseticon/' + styleData.value
}
Label {
id: assetText
font.bold : assetThumb.isFavorite
text: assetlist.getName(styleData.index)
}
}
MouseArea {
id: dragArea
anchors.fill: assetDelegate
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
drag.target: undefined
onReleased: {
drag.target = undefined
}
onPressed: {
if (assetDelegate.isItem) {
//sel.select(styleData.index, ItemSelectionModel.Select)
sel.setCurrentIndex(styleData.index, ItemSelectionModel.ClearAndSelect)
if (mouse.button === Qt.LeftButton) {
drag.target = parent
parent.grabToImage(function(result) {
parent.Drag.imageSource = result.url
})
} else {
drag.target = undefined
assetContextMenu.isItemFavorite = assetThumb.isFavorite
assetContextMenu.popup()
mouse.accepted = false
}
console.log(parent.Drag.keys)
} else {
if (treeView.isExpanded(styleData.index)) {
treeView.collapse(styleData.index)
} else {
treeView.expand(styleData.index)
}
}
treeView.focus = true
}
onDoubleClicked: {
if (isItem) {
assetlist.activate(styleData.index)
}
}
}
}
Menu {
id: assetContextMenu
property bool isItemFavorite
property bool isDisplayed: false
MenuItem {
id: favMenu
text: assetContextMenu.isItemFavorite ? "Remove from favorites" : "Add to favorites"
property url thumbSource
onTriggered: {
assetlist.setFavorite(sel.currentIndex, !assetContextMenu.isItemFavorite)
}
}
onAboutToShow: {
isDisplayed = true;
}
onAboutToHide: {
isDisplayed = false;
}
}
TableViewColumn { role: "identifier"; title: "Name"; }
model: assetListModel
Keys.onDownPressed: {
sel.setCurrentIndex(assetListModel.getNextChild(sel.currentIndex), ItemSelectionModel.ClearAndSelect)
treeView.expand(sel.currentIndex.parent)
treeView.__listView.positionViewAtIndex(rowPosition(assetListModel, sel.currentIndex), ListView.Visible)
}
Keys.onUpPressed: {
sel.setCurrentIndex(assetListModel.getPreviousChild(sel.currentIndex), ItemSelectionModel.ClearAndSelect)
treeView.expand(sel.currentIndex.parent)
treeView.__listView.positionViewAtIndex(rowPosition(assetListModel, sel.currentIndex), ListView.Visible)
}
Keys.onReturnPressed: {
if (sel.hasSelection) {
assetlist.activate(sel.currentIndex)
}
}
}
TextArea {
id: assetDescription
text: ""
visible: showDescription.checked
readOnly: true
Layout.fillWidth: true
states: State {
name: "hasDescription"; when: assetDescription.text != '' && showDescription.checked
PropertyChanges { target: assetDescription; visible: true}
}
}
}
}
}
diff --git a/src/monitor/view/kdenlivemonitorrotoscene.qml b/src/monitor/view/kdenlivemonitorrotoscene.qml
index 7df225587..806f13215 100644
--- a/src/monitor/view/kdenlivemonitorrotoscene.qml
+++ b/src/monitor/view/kdenlivemonitorrotoscene.qml
@@ -1,380 +1,380 @@
import QtQuick 2.6
import QtQuick.Controls 1.4
Item {
id: root
objectName: "rootrotoscene"
SystemPalette { id: activePalette }
// default size, but scalable by user
height: 300; width: 400
property string comment
property string framenum
property point profile
property point center
property real baseUnit: fontMetrics.font.pointSize
property double scalex : 1
property double scaley : 1
property double stretch : 1
property double sourcedar : 1
property double offsetx : 0
property double offsety : 0
property double frameSize: 10
property int duration: 300
property double timeScale: 1
property int mouseRulerPos: 0
onOffsetxChanged: canvas.requestPaint()
onOffsetyChanged: canvas.requestPaint()
onScalexChanged: canvas.requestPaint()
onScaleyChanged: canvas.requestPaint()
onSourcedarChanged: refreshdar()
property bool iskeyframe : true
property bool isDefined: false
property int requestedKeyFrame : -1
property int requestedSubKeyFrame : -1
property bool requestedCenter : false
// The coordinate points where the bezier curve passes
property var centerPoints : []
property var centerCross : []
// The control points for the bezier curve points (2 controls points for each coordinate)
property var centerPointsTypes : []
property bool showToolbar: false
onCenterPointsTypesChanged: checkDefined()
signal effectPolygonChanged()
signal addKeyframe()
signal seekToKeyframe()
onDurationChanged: {
clipMonitorRuler.updateRuler()
}
onWidthChanged: {
clipMonitorRuler.updateRuler()
}
onIskeyframeChanged: {
console.log('KEYFRAME CHANGED: ', iskeyframe)
canvas.requestPaint()
}
FontMetrics {
id: fontMetrics
font.family: "Arial"
}
function refreshdar() {
canvas.darOffset = root.sourcedar < root.profile.x * root.stretch / root.profile.y ? (root.profile.x * root.stretch - root.profile.y * root.sourcedar) / (2 * root.profile.x * root.stretch) :(root.profile.y - root.profile.x * root.stretch / root.sourcedar) / (2 * root.profile.y);
canvas.requestPaint()
}
function checkDefined() {
root.isDefined = root.centerPointsTypes.length > 0
canvas.requestPaint()
}
Item {
id: monitorOverlay
height: root.height - controller.rulerHeight
width: root.width
Canvas {
id: canvas
property double handleSize
property double darOffset : 0
anchors.fill: parent
contextType: "2d";
handleSize: root.baseUnit / 2
renderTarget: Canvas.FramebufferObject
renderStrategy: Canvas.Cooperative
onPaint:
{
var ctx = getContext('2d')
//if (context) {
ctx.clearRect(0,0, width, height);
ctx.beginPath()
ctx.strokeStyle = Qt.rgba(1, 0, 0, 0.5)
ctx.fillStyle = Qt.rgba(1, 0, 0, 0.5)
ctx.lineWidth = 2
if (root.centerPoints.length == 0) {
// no points defined yet
return
}
var p1 = convertPoint(root.centerPoints[0])
var startP = p1;
ctx.moveTo(p1.x, p1.y)
if (!isDefined) {
ctx.fillRect(p1.x - handleSize, p1.y - handleSize, 2 * handleSize, 2 * handleSize);
for (var i = 1; i < root.centerPoints.length; i++) {
p1 = convertPoint(root.centerPoints[i])
ctx.lineTo(p1.x, p1.y);
ctx.fillRect(p1.x - handleSize, p1.y - handleSize, 2 * handleSize, 2 * handleSize);
}
} else {
var c1; var c2
var topRight = []
var bottomLeft = []
for (var i = 0; i < root.centerPoints.length; i++) {
p1 = convertPoint(root.centerPoints[i])
// Control points
var subkf = false
if (i == 0) {
c1 = convertPoint(root.centerPointsTypes[root.centerPointsTypes.length - 1])
if (root.requestedSubKeyFrame == root.centerPointsTypes.length - 1) {
subkf = true
}
topRight.x = p1.x
topRight.y = p1.y
bottomLeft.x = p1.x
bottomLeft.y = p1.y
} else {
c1 = convertPoint(root.centerPointsTypes[2*i - 1])
if (root.requestedSubKeyFrame == 2*i - 1) {
subkf = true
}
// Find bounding box
topRight.x = Math.max(p1.x, topRight.x)
topRight.y = Math.min(p1.y, topRight.y)
bottomLeft.x = Math.min(p1.x, bottomLeft.x)
bottomLeft.y = Math.max(p1.y, bottomLeft.y)
}
c2 = convertPoint(root.centerPointsTypes[2*i])
ctx.bezierCurveTo(c1.x, c1.y, c2.x, c2.y, p1.x, p1.y);
if (iskeyframe) {
if (subkf) {
ctx.fillStyle = Qt.rgba(1, 1, 0, 0.8)
ctx.fillRect(c1.x - handleSize/2, c1.y - handleSize/2, handleSize, handleSize);
ctx.fillStyle = Qt.rgba(1, 0, 0, 0.5)
} else {
ctx.fillRect(c1.x - handleSize/2, c1.y - handleSize/2, handleSize, handleSize);
}
if (root.requestedSubKeyFrame == 2 * i) {
ctx.fillStyle = Qt.rgba(1, 1, 0, 0.8)
ctx.fillRect(c2.x - handleSize/2, c2.y - handleSize/2, handleSize, handleSize);
ctx.fillStyle = Qt.rgba(1, 0, 0, 0.5)
} else {
ctx.fillRect(c2.x - handleSize/2, c2.y - handleSize/2, handleSize, handleSize);
}
c1 = convertPoint(root.centerPointsTypes[2*i + 1])
ctx.lineTo(c1.x, c1.y);
ctx.moveTo(p1.x, p1.y)
ctx.lineTo(c2.x, c2.y);
ctx.moveTo(p1.x, p1.y)
if (i == root.requestedKeyFrame) {
ctx.fillStyle = Qt.rgba(1, 1, 0, 0.8)
ctx.fillRect(p1.x - handleSize, p1.y - handleSize, 2 * handleSize, 2 * handleSize);
ctx.fillStyle = Qt.rgba(1, 0, 0, 0.5)
} else {
ctx.fillRect(p1.x - handleSize, p1.y - handleSize, 2 * handleSize, 2 * handleSize);
}
}
}
if (root.centerPoints.length > 2) {
c1 = convertPoint(root.centerPointsTypes[root.centerPointsTypes.length - 1])
c2 = convertPoint(root.centerPointsTypes[0])
ctx.bezierCurveTo(c1.x, c1.y, c2.x, c2.y, startP.x, startP.y);
}
centerCross.x = bottomLeft.x + (topRight.x - bottomLeft.x)/2
centerCross.y = topRight.y + (bottomLeft.y - topRight.y)/2
ctx.moveTo(centerCross.x - root.baseUnit, centerCross.y)
ctx.lineTo(centerCross.x + root.baseUnit, centerCross.y)
ctx.moveTo(centerCross.x, centerCross.y - root.baseUnit)
ctx.lineTo(centerCross.x, centerCross.y + root.baseUnit)
}
ctx.stroke()
}
function convertPoint(p)
{
var x = frame.x + p.x * root.scalex
var y = frame.y + p.y * root.scaley
return Qt.point(x,y);
}
}
Rectangle {
id: frame
objectName: "referenceframe"
property color hoverColor: "#ff0000"
width: root.profile.x * root.scalex
height: root.profile.y * root.scaley
x: root.center.x - width / 2 - root.offsetx;
y: root.center.y - height / 2 - root.offsety;
color: "transparent"
border.color: "#ffffff00"
}
Rectangle {
anchors.centerIn: parent
width: label.contentWidth + 6
height: label.contentHeight + 6
visible: !root.isDefined && !global.containsMouse
opacity: 0.8
Text {
id: label
- text: i18n('Click to add points,\nright click to close shape.')
+ text: i18n("Click to add points,\nright click to close shape.")
font.pointSize: root.baseUnit
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors {
fill: parent
}
color: 'black'
}
color: "yellow"
}
MouseArea {
id: global
objectName: "global"
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
property bool pointContainsMouse
property bool centerContainsMouse
hoverEnabled: true
cursorShape: !root.isDefined ? Qt.PointingHandCursor : (pointContainsMouse || centerContainsMouse) ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
if (!root.isDefined) {
if (mouse.button == Qt.RightButton && root.centerPoints.length > 2) {
// close shape, define control points
var p0; var p1; var p2
for (var i = 0; i < root.centerPoints.length; i++) {
p1 = root.centerPoints[i]
if (i == 0) {
p0 = root.centerPoints[root.centerPoints.length - 1]
} else {
p0 = root.centerPoints[i - 1]
}
if (i == root.centerPoints.length - 1) {
p2 = root.centerPoints[0]
} else {
p2 = root.centerPoints[i + 1]
}
var ctrl1 = Qt.point((p0.x - p1.x) / 5, (p0.y - p1.y) / 5);
var ctrl2 = Qt.point((p2.x - p1.x) / 5, (p2.y - p1.y) / 5);
root.centerPointsTypes.push(Qt.point(p1.x + ctrl1.x, p1.y + ctrl1.y))
root.centerPointsTypes.push(Qt.point(p1.x + ctrl2.x, p1.y + ctrl2.y))
}
root.isDefined = true;
root.effectPolygonChanged()
canvas.requestPaint()
} else {
var newPoint = Qt.point((mouseX - frame.x) / root.scalex, (mouseY - frame.y) / root.scaley);
root.centerPoints.push(newPoint)
canvas.requestPaint()
}
}
}
onDoubleClicked: {
root.addKeyframe()
}
onPositionChanged: {
if (root.iskeyframe == false) return;
if (isDefined && pressed) {
if (centerContainsMouse) {
var xDiff = (mouseX - centerCross.x) / root.scalex
var yDiff = (mouseY - centerCross.y) / root.scaley
for (var j = 0; j < root.centerPoints.length; j++) {
root.centerPoints[j].x += xDiff
root.centerPoints[j].y += yDiff
root.centerPointsTypes[j * 2].x += xDiff
root.centerPointsTypes[j * 2].y += yDiff
root.centerPointsTypes[j * 2 + 1].x += xDiff
root.centerPointsTypes[j * 2 + 1].y += yDiff
}
canvas.requestPaint()
root.effectPolygonChanged()
return
}
if (root.requestedKeyFrame >= 0) {
var xDiff = (mouseX - frame.x) / root.scalex - root.centerPoints[root.requestedKeyFrame].x
var yDiff = (mouseY - frame.y) / root.scaley - root.centerPoints[root.requestedKeyFrame].y
root.centerPoints[root.requestedKeyFrame].x += xDiff
root.centerPoints[root.requestedKeyFrame].y += yDiff
root.centerPointsTypes[root.requestedKeyFrame * 2].x += xDiff
root.centerPointsTypes[root.requestedKeyFrame * 2].y += yDiff
root.centerPointsTypes[root.requestedKeyFrame * 2 + 1].x += xDiff
root.centerPointsTypes[root.requestedKeyFrame * 2 + 1].y += yDiff
canvas.requestPaint()
root.effectPolygonChanged()
} else if (root.requestedSubKeyFrame >= 0) {
root.centerPointsTypes[root.requestedSubKeyFrame].x = (mouseX - frame.x) / root.scalex
root.centerPointsTypes[root.requestedSubKeyFrame].y = (mouseY - frame.y) / root.scaley
canvas.requestPaint()
root.effectPolygonChanged()
}
} else if (root.centerPoints.length > 0) {
for(var i = 0; i < root.centerPoints.length; i++)
{
var p1 = canvas.convertPoint(root.centerPoints[i])
if (Math.abs(p1.x - mouseX) <= canvas.handleSize && Math.abs(p1.y - mouseY) <= canvas.handleSize) {
if (i == root.requestedKeyFrame) {
centerContainsMouse = false
pointContainsMouse = true;
return;
}
root.requestedKeyFrame = i
canvas.requestPaint()
centerContainsMouse = false
pointContainsMouse = true;
return;
}
}
for(var i = 0; i < root.centerPointsTypes.length; i++)
{
var p1 = canvas.convertPoint(root.centerPointsTypes[i])
if (Math.abs(p1.x - mouseX) <= canvas.handleSize/2 && Math.abs(p1.y - mouseY) <= canvas.handleSize/2) {
if (i == root.requestedSubKeyFrame) {
centerContainsMouse = false
pointContainsMouse = true;
return;
}
root.requestedSubKeyFrame = i
canvas.requestPaint()
centerContainsMouse = false
pointContainsMouse = true;
return;
}
}
if (Math.abs(centerCross.x - mouseX) <= canvas.handleSize/2 && Math.abs(centerCross.y - mouseY) <= canvas.handleSize/2) {
centerContainsMouse = true;
pointContainsMouse = false;
return;
}
if (root.requestedKeyFrame == -1 && root.requestedSubKeyFrame == -1) {
return;
}
root.requestedKeyFrame = -1
root.requestedSubKeyFrame = -1
pointContainsMouse = false;
centerContainsMouse = false
canvas.requestPaint()
}
}
}
}
EffectToolBar {
id: effectToolBar
anchors {
right: parent.right
top: parent.top
topMargin: 4
rightMargin: 4
}
visible: global.mouseX >= x - 10
}
MonitorRuler {
id: clipMonitorRuler
anchors {
left: root.left
right: root.right
bottom: root.bottom
}
height: controller.rulerHeight
}
}
diff --git a/src/qml/splash.qml b/src/qml/splash.qml
index 96b08c388..7b02b4fe1 100644
--- a/src/qml/splash.qml
+++ b/src/qml/splash.qml
@@ -1,221 +1,221 @@
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.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 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* 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 . *
***************************************************************************/
import QtQuick 2.6
import QtQuick.Controls 1.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4
Window {
id: splash
objectName: "splash"
color: "transparent"
title: "Splash Window"
SystemPalette { id: activePalette }
modality: Qt.ApplicationModal
flags: Qt.SplashScreen
property int timeoutInterval: 2000
signal timeout
x: (Screen.width - splashContent.width) / 2
y: (Screen.height - splashContent.height) / 2
width: splashContent.width
height: splashContent.height
property int border: 10
property bool splashing: true
function endSplash()
{
console.log("ending splash")
splash.splashing = false;
splash.close();
}
Rectangle {
id:splashContent
height: Screen.height / 2
width: Screen.width / 3
border.width:splash.border
border.color:"#bfbfbf"
color: "#31363b"
Image {
id:logo
anchors.left: splashContent.left
anchors.top: splashContent.top
anchors.margins: 50
// anchors.horizontalCenter: splashContent.horizontalCenter
source: "qrc:/pics/kdenlive-logo.png"
fillMode: Image.PreserveAspectFit
height: splashContent.height / 5 - 100
}
RowLayout {
//anchors.horizontalCenter: splashContent.horizontalCenter
anchors.bottom: logo.bottom
anchors.right: splashContent.right
anchors.rightMargin: logo.x
spacing: 100
Text {
color: "white"
- text: i18n('Website')
+ text: i18n("Website")
font.bold: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
}
Text {
color: "white"
- text: i18n('Donate')
+ text: i18n("Donate")
font.bold: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
}
Text {
color: "white"
- text: i18n('Forum')
+ text: i18n("Forum")
font.bold: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
}
}
Rectangle {
id:recentProjects
y: splashContent.height / 5
anchors.left: splashContent.left
anchors.leftMargin: splash.border
anchors.right: splashContent.right
anchors.rightMargin: splash.border
color:"#232629"
height: 3 * splashContent.height / 5
width: splashContent.width
visible: !splashing
Text {
id:txtProject
color: "#f38577"
- text: i18n('Recent Projects')
+ text: i18n("Recent Projects")
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 50
anchors.leftMargin: 100
font.bold: true
}
}
Image {
id:splash_img
y: splashContent.height / 5
anchors.left: splashContent.left
anchors.leftMargin: splash.border
anchors.right: splashContent.right
anchors.rightMargin: splash.border
height: 3 * splashContent.height / 5
width: splashContent.width
source: "qrc:/pics/splash-background.png"
fillMode: Image.PreserveAspectFit
visible: splashing
}
/*Text {
id:txtProject
color: "#f38577"
text: "Recent Projects"
anchors.horizontalCenter: splashContent.horizontalCenter
anchors.top: logo.bottom
anchors.topMargin: 50
font.bold: true
}
Rectangle {
id:recentProjects
border.width:5
border.color:"#efefef"
color: "#fafafa"
height: splashContent.height / 4
width: 5*splashContent.width/6
anchors.horizontalCenter: splashContent.horizontalCenter
anchors.top: txtProject.bottom
anchors.topMargin: 5
}*/
Item {
anchors.left: splashContent.left
anchors.right: splashContent.right
anchors.bottom: splashContent.bottom
anchors.top: recentProjects.bottom
anchors.margins: 50
visible: !splashing
CheckBox {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
style: CheckBoxStyle {
indicator: Rectangle {
implicitWidth: 32
implicitHeight: 32
radius: 3
//border.color: control.activeFocus ? "darkblue" : "gray"
border.width: 1
border.color:"white"
color: "#4d4d4d"
Rectangle {
visible: control.checked
color: "#555"
border.color: "#333"
radius: 1
anchors.margins: 4
anchors.fill: parent
}
}
label: Text {
- text: i18n('Hide on startup')
+ text: i18n("Hide on startup")
color: "white"
}
}
}
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 50
visible: !splashing
Button {
iconSource:"image://icon/document-new"
- text:i18n('New')
+ text:i18n("New")
//style: CustomButton {
// backColor: splashContent.color
// }
}
Button {
iconSource:"image://icon/document-open"
- text:i18n('Open')
+ text:i18n("Open")
//style: CustomButton {
// backColor: splashContent.color
// }
}
}
}
MouseArea {
id: clickZone
anchors.fill: splashContent
onClicked: {
console.log("clic");
splash.close();
}
}
}
Component.onCompleted: {
visible = true
clickZone.focus = true;
}
}
diff --git a/src/timeline2/view/qml/Clip.qml b/src/timeline2/view/qml/Clip.qml
index fa57ee4bb..dd7df95be 100644
--- a/src/timeline2/view/qml/Clip.qml
+++ b/src/timeline2/view/qml/Clip.qml
@@ -1,940 +1,940 @@
/*
* Copyright (c) 2013-2016 Meltytech, LLC
* Author: Dan Dennedy
*
* 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 .
*/
import QtQuick 2.6
import QtQuick.Controls 2.2
import Kdenlive.Controls 1.0
import QtQml.Models 2.2
import QtQuick.Window 2.2
import 'Timeline.js' as Logic
import com.enums 1.0
Rectangle {
id: clipRoot
property real timeScale: 1.0
property string clipName: ''
property string clipResource: ''
property string mltService: ''
property string effectNames
property int modelStart
property real scrollX: 0
property int inPoint: 0
property int outPoint: 0
property int clipDuration: 0
property int maxDuration: 0
property bool isAudio: false
property int audioChannels
property bool showKeyframes: false
property bool isGrabbed: false
property bool grouped: false
property var markers
property var keyframeModel
property int clipStatus: 0
property int itemType: 0
property int fadeIn: 0
property int fadeOut: 0
property int binId: 0
property int positionOffset: 0
property var parentTrack
property int trackIndex //Index in track repeater
property int clipId //Id of the clip in the model
property int trackId: -1 // Id of the parent track in the model
property int fakeTid: -1
property int fakePosition: 0
property int originalTrackId: -1
property int originalX: x
property int originalDuration: clipDuration
property int lastValidDuration: clipDuration
property int draggedX: x
property bool selected: false
property bool isLocked: parentTrack && parentTrack.isLocked == true
property bool hasAudio
property bool canBeAudio
property bool canBeVideo
property string hash: 'ccc' //TODO
property double speed: 1.0
property color borderColor: 'black'
property bool forceReloadThumb
property bool isComposition: false
property var groupTrimData
width : clipDuration * timeScale;
opacity: dragProxyArea.drag.active && dragProxy.draggedItem == clipId ? 0.8 : 1.0
signal trimmingIn(var clip, real newDuration, var mouse, bool shiftTrim)
signal trimmedIn(var clip, bool shiftTrim)
signal initGroupTrim(var clip)
signal trimmingOut(var clip, real newDuration, var mouse, bool shiftTrim)
signal trimmedOut(var clip, bool shiftTrim)
onIsGrabbedChanged: {
if (clipRoot.isGrabbed) {
clipRoot.forceActiveFocus();
mouseArea.focus = true
}
}
function clearAndMove(offset) {
controller.requestClearSelection()
controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true)
controller.requestAddToSelection(clipRoot.clipId)
}
onInPointChanged: {
if (parentTrack && parentTrack.isAudio && thumbsLoader.item) {
thumbsLoader.item.reload()
}
}
onClipResourceChanged: {
if (itemType == ProducerType.Color) {
color: Qt.darker(getColor(), 1.5)
}
}
ToolTip {
visible: mouseArea.containsMouse && !dragProxyArea.pressed
font.pixelSize: root.baseUnit
delay: 1000
timeout: 5000
background: Rectangle {
color: activePalette.alternateBase
border.color: activePalette.light
}
contentItem: Label {
color: activePalette.text
text: label.text + ' (' + timeline.timecode(clipRoot.inPoint) + '-' + timeline.timecode(clipRoot.outPoint) + ')'
}
}
onKeyframeModelChanged: {
console.log('keyframe model changed............')
if (effectRow.keyframecanvas) {
effectRow.keyframecanvas.requestPaint()
}
}
onClipDurationChanged: {
width = clipDuration * timeScale;
}
onModelStartChanged: {
x = modelStart * timeScale;
}
onFakePositionChanged: {
x = fakePosition * timeScale;
}
onFakeTidChanged: {
if (clipRoot.fakeTid > -1 && parentTrack) {
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
}
clipRoot.y = Logic.getTrackById(clipRoot.fakeTid).y
}
}
onForceReloadThumbChanged: {
// TODO: find a way to force reload of clip thumbs
if (thumbsLoader.item) {
thumbsLoader.item.reload()
}
}
onTimeScaleChanged: {
x = modelStart * timeScale;
width = clipDuration * timeScale;
labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : 0
if (parentTrack && parentTrack.isAudio && thumbsLoader.item) {
thumbsLoader.item.reload();
}
}
onScrollXChanged: {
labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : 0
}
border.color: selected ? root.selectionColor : grouped ? root.groupColor : borderColor
border.width: isGrabbed ? 8 : 1.5
function updateDrag() {
var itemPos = mapToItem(tracksContainerArea, 0, 0, clipRoot.width, clipRoot.height)
initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
}
function getColor() {
if (clipStatus == ClipState.Disabled) {
return 'grey'
}
if (itemType == ProducerType.Color) {
var color = clipResource.substring(clipResource.length - 9)
if (color[0] == '#') {
return color
}
return '#' + color.substring(color.length - 8, color.length - 2)
}
return isAudio? root.audioColor : root.videoColor
}
/* function reparent(track) {
console.log('TrackId: ',trackId)
parent = track
height = track.height
parentTrack = track
trackId = parentTrack.trackId
console.log('Reparenting clip to Track: ', trackId)
//generateWaveform()
}
*/
property bool variableThumbs: (isAudio || itemType == ProducerType.Color || mltService === '')
property bool isImage: itemType == ProducerType.Image
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)
DropArea { //Drop area for clips
anchors.fill: clipRoot
keys: 'kdenlive/effect'
property string dropData
property string dropSource
property int dropRow: -1
onEntered: {
dropData = drag.getDataAsString('kdenlive/effect')
dropSource = drag.getDataAsString('kdenlive/effectsource')
}
onDropped: {
console.log("Add effect: ", dropData)
if (dropSource == '') {
// drop from effects list
controller.addClipEffect(clipRoot.clipId, dropData);
} else {
controller.copyClipEffect(clipRoot.clipId, dropSource);
}
dropSource = ''
dropRow = -1
drag.acceptProposedAction
}
}
MouseArea {
id: mouseArea
enabled: root.activeTool === 0
anchors.fill: clipRoot
acceptedButtons: Qt.RightButton
hoverEnabled: root.activeTool === 0
cursorShape: dragProxyArea.drag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor
onPressed: {
root.stopScrolling = true
if (mouse.button == Qt.RightButton) {
if (timeline.selection.indexOf(clipRoot.clipId) == -1) {
controller.requestAddToSelection(clipRoot.clipId, true)
}
clipMenu.clipId = clipRoot.clipId
clipMenu.clipStatus = clipRoot.clipStatus
clipMenu.clipFrame = Math.round(mouse.x / timeline.scaleFactor)
clipMenu.grouped = clipRoot.grouped
clipMenu.trackId = clipRoot.trackId
clipMenu.canBeAudio = clipRoot.canBeAudio
clipMenu.canBeVideo = clipRoot.canBeVideo
clipMenu.popup()
}
}
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)
Keys.onLeftPressed: {
var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true);
}
Keys.onRightPressed: {
var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart + offset, true, true, true);
}
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);
}
onPositionChanged: {
var mapped = parentTrack.mapFromItem(clipRoot, mouse.x, mouse.y).x
root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
}
onEntered: {
var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
}
onExited: {
endDrag()
}
onWheel: zoomByWheel(wheel)
Item {
// Thumbs container
anchors.fill: parent
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()
}
}
}
Item {
// Clipping container
id: container
anchors.fill: parent
anchors.margins: 1.5
clip: true
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: 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: clipRoot.speed < 0 ? clipRoot.clipDuration * timeScale + (Math.round(model.frame / clipRoot.speed) - (clipRoot.maxDuration - clipRoot.outPoint)) * timeScale : (Math.round(model.frame / clipRoot.speed) - 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, model.frame)
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'
}
}
}
KeyframeView {
id: effectRow
visible: clipRoot.showKeyframes && clipRoot.keyframeModel
selected: clipRoot.selected
inPoint: clipRoot.inPoint
outPoint: clipRoot.outPoint
masterObject: clipRoot
kfrModel: clipRoot.keyframeModel
}
}
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
}
}
]
Rectangle {
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
MouseArea {
id: compInArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: parent.opacity = 0.7
onExited: {
if (!pressed) {
parent.opacity = 0
}
}
onPressed: {
timeline.addCompositionToClip('', clipRoot.clipId, 0)
endDrag()
}
onReleased: {
parent.opacity = 0
}
ToolTip {
visible: compInArea.containsMouse && !dragProxyArea.pressed
font.pixelSize: root.baseUnit
delay: 1000
timeout: 5000
background: Rectangle {
color: activePalette.alternateBase
border.color: activePalette.light
}
contentItem: Label {
color: activePalette.text
- text: i18n('Click to add composition')
+ text: i18n("Click to add composition")
}
}
}
}
Rectangle {
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
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: {
parent.opacity = 0.7
}
onExited: {
if (!pressed) {
parent.opacity = 0
}
}
onPressed: {
timeline.addCompositionToClip('', clipRoot.clipId, clipRoot.clipDuration - 1)
endDrag()
}
onReleased: {
parent.opacity = 0
}
ToolTip {
visible: compOutArea.containsMouse && !dragProxyArea.pressed
font.pixelSize: root.baseUnit
delay: 1000
timeout: 5000
background: Rectangle {
color: activePalette.alternateBase
border.color: activePalette.light
}
contentItem: Label {
color: activePalette.text
- text: i18n('Click to add composition')
+ text: i18n("Click to add composition")
}
}
}
}
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
}
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
}
}
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
}
onReleased: {
root.stopScrolling = false
fadeInTriangle.opacity = 0.3
parent.opacity = 0
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)
}
}
}
}
SequentialAnimation on scale {
loops: Animation.Infinite
running: fadeInMouseArea.containsMouse && !fadeInMouseArea.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
}
}
}
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
}
}
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
}
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
var duration = clipRoot.fadeOut
if (duration > 0) {
duration += 1
}
timeline.adjustFade(clipRoot.clipId, 'fadeout', duration, 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)
}
}
}
}
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
}
}
}
}
Rectangle {
id: trimIn
anchors.left: clipRoot.left
anchors.leftMargin: 0
height: parent.height
enabled: !isLocked
width: 5
color: isAudio? 'green' : '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)
MouseArea {
id: trimInMouseArea
anchors.fill: parent
hoverEnabled: true
drag.target: parent
drag.axis: Drag.XAxis
drag.smoothed: false
property bool shiftTrim: false
property bool sizeChanged: false
cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
onPressed: {
root.stopScrolling = true
clipRoot.originalX = clipRoot.x
clipRoot.originalDuration = clipDuration
parent.anchors.left = undefined
shiftTrim = mouse.modifiers & Qt.ShiftModifier
if (!shiftTrim && clipRoot.grouped) {
clipRoot.initGroupTrim(clipRoot)
}
parent.opacity = 0
}
onReleased: {
root.stopScrolling = false
parent.anchors.left = clipRoot.left
if (sizeChanged) {
clipRoot.trimmedIn(clipRoot, shiftTrim)
sizeChanged = false
}
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
var delta = Math.round((trimIn.x) / timeScale)
if (delta !== 0) {
if (delta < -modelStart) {
delta = -modelStart
}
var newDuration = clipDuration - delta
sizeChanged = true
clipRoot.trimmingIn(clipRoot, newDuration, mouse, shiftTrim)
}
}
}
onEntered: {
if (!pressed) {
parent.opacity = 0.5
}
}
onExited: {
parent.opacity = 0
}
}
}
Rectangle {
id: trimOut
anchors.right: clipRoot.right
anchors.rightMargin: 0
height: parent.height
width: 5
color: 'red'
opacity: 0
enabled: !isLocked
Drag.active: trimOutMouseArea.drag.active
Drag.proposedAction: Qt.MoveAction
visible: trimOutMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && clipRoot.width > 4 * width)
MouseArea {
id: trimOutMouseArea
anchors.fill: parent
hoverEnabled: true
property bool shiftTrim: false
property bool sizeChanged: false
cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
drag.target: parent
drag.axis: Drag.XAxis
drag.smoothed: false
onPressed: {
root.stopScrolling = true
clipRoot.originalDuration = clipDuration
parent.anchors.right = undefined
shiftTrim = mouse.modifiers & Qt.ShiftModifier
if (!shiftTrim && clipRoot.grouped) {
clipRoot.initGroupTrim(clipRoot)
}
parent.opacity = 0
}
onReleased: {
root.stopScrolling = false
parent.anchors.right = clipRoot.right
if (sizeChanged) {
clipRoot.trimmedOut(clipRoot, shiftTrim)
sizeChanged = false
}
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
var newDuration = Math.round((parent.x + parent.width) / timeScale)
if (newDuration != clipDuration) {
sizeChanged = true
clipRoot.trimmingOut(clipRoot, newDuration, mouse, shiftTrim)
}
}
}
onEntered: {
if (!pressed) {
parent.opacity = 0.5
}
}
onExited: parent.opacity = 0
}
}
/*MenuItem {
id: mergeItem
- text: i18n('Merge with next clip')
+ text: i18n("Merge with next clip")
onTriggered: timeline.mergeClipWithNext(trackIndex, index, false)
}
MenuItem {
- text: i18n('Rebuild Audio Waveform')
+ text: i18n("Rebuild Audio Waveform")
onTriggered: timeline.remakeAudioLevels(trackIndex, index)
}*/
/*onPopupVisibleChanged: {
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))
}
}*/
}
diff --git a/src/timeline2/view/qml/ClipMenu.qml b/src/timeline2/view/qml/ClipMenu.qml
index b621e2cd3..a8b64e7e4 100644
--- a/src/timeline2/view/qml/ClipMenu.qml
+++ b/src/timeline2/view/qml/ClipMenu.qml
@@ -1,152 +1,152 @@
import QtQuick 2.6
import QtQuick.Controls 1.4
import com.enums 1.0
Menu {
id: clipMenu
property int clipId
property int clipFrame
property int clipStatus
property int trackId
property bool grouped
property bool canBeAudio
property bool canBeVideo
property bool canBeGrouped: timeline.isInSelection(clipId)
onAboutToHide: {
timeline.ungrabHack()
}
onAboutToShow: {
canBeGrouped = timeline.isInSelection(clipId)
}
MenuItem {
- text: i18n('Copy')
+ text: i18n("Copy")
iconName: 'edit-copy'
onTriggered: {
root.copiedClip = clipId
timeline.copyItem()
}
}
MenuItem {
visible: canBeGrouped
- text: i18n('Group')
+ text: i18n("Group")
iconName: 'object-group'
onTriggered: timeline.triggerAction('group_clip')
}
MenuItem {
visible: grouped
- text: i18n('Ungroup')
+ text: i18n("Ungroup")
iconName: 'object-ungroup'
onTriggered: timeline.unGroupSelection(clipId)
}
MenuItem {
- text: i18n('Edit Duration')
+ text: i18n("Edit Duration")
iconName: 'measure'
onTriggered: {
timeline.editItemDuration(clipId)
}
}
MenuItem {
visible: root.copiedClip != -1 && root.copiedClip != clipId
- text: i18n('Paste Effects')
+ text: i18n("Paste Effects")
iconName: 'edit-paste'
onTriggered: timeline.pasteEffects(clipId)
}
MenuSeparator {
visible: true
}
MenuItem {
- text: i18n('Split Audio')
+ text: i18n("Split Audio")
onTriggered: timeline.splitAudio(clipId)
visible: !grouped && canBeAudio && clipStatus == ClipState.VideoOnly
}
MenuItem {
- text: i18n('Split Video')
+ text: i18n("Split Video")
onTriggered: timeline.splitVideo(clipId)
visible: !grouped && canBeVideo && clipStatus == ClipState.AudioOnly
}
Menu {
- title: i18n('Markers')
+ title: i18n("Markers")
MenuItem {
- text: i18n('Add Marker')
+ text: i18n("Add Marker")
onTriggered: timeline.addMarker(clipId)
}
MenuItem {
- text: i18n('Add Marker/Guide quickly')
+ text: i18n("Add Marker/Guide quickly")
onTriggered: timeline.addQuickMarker(clipId)
}
MenuItem {
- text: i18n('Edit Marker')
+ text: i18n("Edit Marker")
onTriggered: timeline.editMarker(clipId)
}
MenuItem {
- text: i18n('Delete Marker')
+ text: i18n("Delete Marker")
onTriggered: timeline.deleteMarker(clipId)
}
MenuItem {
- text: i18n('Delete All Markers')
+ text: i18n("Delete All Markers")
onTriggered: timeline.deleteAllMarkers(clipId)
}
}
MenuItem {
- text: i18n('Set Audio Reference')
+ text: i18n("Set Audio Reference")
onTriggered: timeline.setAudioRef(clipId)
visible: canBeAudio
}
MenuItem {
- text: i18n('Align Audio')
+ text: i18n("Align Audio")
onTriggered: timeline.alignAudio(clipId)
visible: canBeAudio
}
MenuItem {
- text: i18n('Remove')
+ text: i18n("Remove")
iconName: 'edit-delete'
onTriggered: timeline.triggerAction('delete_timeline_clip')
}
MenuItem {
visible: true
- text: i18n('Extract')
+ text: i18n("Extract")
onTriggered: timeline.extract(clipId)
}
MenuSeparator {
visible: true
}
MenuItem {
visible: true
- text: i18n('Change Speed')
+ text: i18n("Change Speed")
onTriggered: {
timeline.changeItemSpeed(clipId, -1)
}
}
MenuItem {
- text: i18n('Clip in Project Bin')
+ text: i18n("Clip in Project Bin")
onTriggered: timeline.triggerAction('clip_in_project_tree')
}
MenuItem {
visible: true
- text: i18n('Split At Playhead')
+ text: i18n("Split At Playhead")
onTriggered: timeline.triggerAction('cut_timeline_clip')
}
MenuItem {
visible: true
- text: clipStatus != ClipState.Disabled ? i18n('Disable clip') : i18n('Enable clip')
+ text: clipStatus != ClipState.Disabled ? i18n("Disable clip") : i18n("Enable clip")
onTriggered: timeline.switchEnableState(clipId)
}
AssetMenu {
- title: i18n('Insert an effect...')
+ title: i18n("Insert an effect...")
menuModel: effectModel
onAssetSelected: {
timeline.addEffectToClip(assetId, clipId)
}
}
AssetMenu {
- title: i18n('Insert a composition...')
+ title: i18n("Insert a composition...")
menuModel: transitionModel
isTransition: true
onAssetSelected: {
timeline.addCompositionToClip(assetId, clipId, clipFrame)
}
}
}
diff --git a/src/timeline2/view/qml/CompositionMenu.qml b/src/timeline2/view/qml/CompositionMenu.qml
index 99b1b1c2c..18c20b2ba 100644
--- a/src/timeline2/view/qml/CompositionMenu.qml
+++ b/src/timeline2/view/qml/CompositionMenu.qml
@@ -1,55 +1,55 @@
import QtQuick 2.6
import QtQuick.Controls 1.4
Menu {
id: compositionMenu
property int clipId
property int clipStatus
property int trackId
property bool grouped
onAboutToHide: {
timeline.ungrabHack()
}
function show() {
//mergeItem.visible = timeline.mergeClipWithNext(trackIndex, index, true)
popup()
}
MenuItem {
visible: !grouped && timeline.selection.length > 1
- text: i18n('Group')
+ text: i18n("Group")
iconName: 'object-group'
onTriggered: timeline.groupSelection()
}
MenuItem {
visible: grouped
- text: i18n('Ungroup')
+ text: i18n("Ungroup")
iconName: 'object-ungroup'
onTriggered: timeline.unGroupSelection(clipId)
}
MenuItem {
- text: i18n('Edit Duration')
+ text: i18n("Edit Duration")
iconName: 'measure'
onTriggered: {
timeline.editItemDuration(clipId)
}
}
MenuItem {
visible: true
- text: i18n('Copy')
+ text: i18n("Copy")
iconName: 'edit-copy'
onTriggered: {
root.copiedClip = clipId
timeline.copyItem()
}
}
MenuItem {
- text: i18n('Remove')
+ text: i18n("Remove")
iconName: 'edit-delete'
onTriggered: timeline.triggerAction('delete_timeline_clip')
}
MenuSeparator {
visible: true
}
}
diff --git a/src/timeline2/view/qml/TrackHead.qml b/src/timeline2/view/qml/TrackHead.qml
index 6ef39fb05..d70ffd923 100644
--- a/src/timeline2/view/qml/TrackHead.qml
+++ b/src/timeline2/view/qml/TrackHead.qml
@@ -1,505 +1,505 @@
/*
* Copyright (c) 2013-2016 Meltytech, LLC
* Author: Dan Dennedy
*
* 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 .
*/
import QtQuick 2.6
import QtQuick.Controls 1.4
import QtQuick.Controls 2.2 as NEWQML
import QtQuick.Controls.Styles 1.2
import QtQuick.Layouts 1.3
Rectangle {
id: trackHeadRoot
property string trackName
property string effectNames
property bool isStackEnabled
property bool isDisabled
property bool collapsed: false
property int isComposite
property bool isLocked: false
property bool isActive: false
property bool isAudio
property bool showAudioRecord
property bool current: false
property int myTrackHeight
property int trackId : -42
property int collapsedHeight: nameEdit.height + 2
property int iconSize: root.baseUnit * 2
property string trackTag
property int thumbsFormat: 0
border.width: 1
border.color: root.frameColor
signal clicked()
function pulseLockButton() {
flashLock.restart();
}
color: getTrackColor(isAudio, true)
//border.color: selected? 'red' : 'transparent'
//border.width: selected? 1 : 0
clip: true
state: 'normal'
states: [
State {
name: 'current'
when: trackHeadRoot.current
PropertyChanges {
target: trackHeadRoot
color: selectedTrackColor
}
},
State {
when: !trackHeadRoot.current
name: 'normal'
PropertyChanges {
target: trackHeadRoot
color: getTrackColor(isAudio, true)
}
}
]
Keys.onDownPressed: {
root.moveSelectedTrack(1)
}
Keys.onUpPressed: {
root.moveSelectedTrack(-1)
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: {
parent.clicked()
if (mouse.button == Qt.RightButton) {
headerMenu.trackId = trackId
headerMenu.thumbsFormat = thumbsFormat
headerMenu.audioTrack = trackHeadRoot.isAudio
headerMenu.recEnabled = trackHeadRoot.showAudioRecord
headerMenu.popup()
}
}
onClicked: {
parent.forceActiveFocus()
nameEdit.visible = false
if (mouse.button == Qt.LeftButton) {
timeline.showTrackAsset(trackId)
}
}
}
ColumnLayout {
id: targetColumn
width: root.baseUnit / 1.2
height: trackHeadRoot.height
Item {
width: parent.width
Layout.fillHeight: true
Layout.topMargin: 4
Layout.bottomMargin: 4
Layout.alignment: Qt.AlignVCenter
Rectangle {
id: trackTarget
color: 'grey'
anchors.fill: parent
width: height
border.width: 0
MouseArea {
id: targetArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (trackHeadRoot.isAudio) {
if (trackHeadRoot.trackId == timeline.audioTarget) {
timeline.audioTarget = -1;
} else {
timeline.audioTarget = trackHeadRoot.trackId;
}
} else {
if (trackHeadRoot.trackId == timeline.videoTarget) {
timeline.videoTarget = -1;
} else {
timeline.videoTarget = trackHeadRoot.trackId;
}
}
}
}
NEWQML.ToolTip {
visible: targetArea.containsMouse
font.pixelSize: root.baseUnit
delay: 1500
timeout: 5000
background: Rectangle {
color: activePalette.alternateBase
border.color: activePalette.light
}
contentItem: Label {
color: activePalette.text
- text: i18n('Click to toggle track as target. Target tracks will receive the inserted clips')
+ text: i18n("Click to toggle track as target. Target tracks will receive the inserted clips")
}
}
state: 'normalTarget'
states: [
State {
name: 'target'
when: (trackHeadRoot.isAudio && trackHeadRoot.trackId == timeline.audioTarget) || (!trackHeadRoot.isAudio && trackHeadRoot.trackId == timeline.videoTarget)
PropertyChanges {
target: trackTarget
color: 'green'
}
},
State {
name: 'noTarget'
when: !trackHeadRoot.isLocked && !trackHeadRoot.isDisabled
PropertyChanges {
target: trackTarget
color: 'grey'
}
}
]
transitions: [
Transition {
to: '*'
ColorAnimation { target: trackTarget; duration: 300 }
}
]
}
}
}
ColumnLayout {
id: trackHeadColumn
spacing: 0
anchors.fill: parent
anchors.leftMargin: targetColumn.width
anchors.topMargin: 0
RowLayout {
spacing: 0
Layout.leftMargin: 2
ToolButton {
id: expandButton
implicitHeight: root.baseUnit * 2
implicitWidth: root.baseUnit * 2
iconName: trackHeadRoot.collapsed ? 'arrow-right' : 'arrow-down'
onClicked: {
trackHeadRoot.myTrackHeight = trackHeadRoot.collapsed ? Math.max(collapsedHeight * 1.5, controller.getTrackProperty(trackId, "kdenlive:trackheight")) : collapsedHeight
}
- tooltip: trackLabel.visible? i18n('Minimize') : i18n('Expand')
+ tooltip: trackLabel.visible? i18n("Minimize") : i18n("Expand")
}
Item {
width: trackTag.contentWidth + 4
height: width
Rectangle {
id: trackLed
color: Qt.darker(trackHeadRoot.color, 0.45)
anchors.fill: parent
width: height
border.width: 0
Text {
id: trackTag
text: trackHeadRoot.trackTag
anchors.fill: parent
font.pixelSize: root.baseUnit * 1.5
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
MouseArea {
id: tagMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
timeline.switchTrackActive(trackHeadRoot.trackId)
}
}
NEWQML.ToolTip {
visible: tagMouseArea.containsMouse
font.pixelSize: root.baseUnit
delay: 1500
timeout: 5000
background: Rectangle {
color: activePalette.alternateBase
border.color: activePalette.light
}
contentItem: Label {
color: activePalette.text
- text: i18n('Click to make track active/inactive. Active tracks will react to insert/remove operations')
+ text: i18n("Click to make track active/inactive. Active tracks will react to insert/remove operations")
}
}
state: 'normalled'
states: [
State {
name: 'locked'
when: trackHeadRoot.isLocked
PropertyChanges {
target: trackLed
color: 'red'
}
},
State {
name: 'active'
when: trackHeadRoot.isActive
PropertyChanges {
target: trackLed
color: 'yellow'
}
},
State {
name: 'mute'
when: trackHeadRoot.isDisabled
PropertyChanges {
target: trackLed
color: 'orange'
}
},
State {
name: 'inactive'
when: !trackHeadRoot.isLocked && !trackHeadRoot.isActive
PropertyChanges {
target: trackLed
color: Qt.darker(trackHeadRoot.color, 0.45)
}
}
]
transitions: [
Transition {
to: '*'
ColorAnimation { target: trackLed; duration: 300 }
}
]
}
}
Item {
// Spacer
Layout.fillWidth: true
}
ToolButton {
iconName: 'tools-wizard'
checkable: true
enabled: trackHeadRoot.effectNames != ''
checked: enabled && trackHeadRoot.isStackEnabled
implicitHeight: trackHeadRoot.iconSize
implicitWidth: trackHeadRoot.iconSize
onClicked: {
timeline.showTrackAsset(trackId)
controller.setTrackStackEnabled(trackId, !isStackEnabled)
}
}
ToolButton {
id: muteButton
implicitHeight: trackHeadRoot.iconSize
implicitWidth: trackHeadRoot.iconSize
iconName: isAudio ? (isDisabled ? 'kdenlive-hide-audio' : 'kdenlive-show-audio') : (isDisabled ? 'kdenlive-hide-video' : 'kdenlive-show-video')
iconSource: isAudio ? (isDisabled ? 'qrc:///pics/kdenlive-hide-audio.svgz' : 'qrc:///pics/kdenlive-show-audio.svgz') : (isDisabled ? 'qrc:///pics/kdenlive-hide-video.svgz' : 'qrc:///pics/kdenlive-show-video.svgz')
onClicked: controller.setTrackProperty(trackId, "hide", isDisabled ? (isAudio ? '1' : '2') : '3')
- tooltip: isAudio ? (isDisabled? i18n('Unmute') : i18n('Mute')) : (isDisabled? i18n('Show') : i18n('Hide'))
+ tooltip: isAudio ? (isDisabled? i18n("Unmute") : i18n("Mute")) : (isDisabled? i18n("Show") : i18n("Hide"))
}
ToolButton {
id: lockButton
implicitHeight: trackHeadRoot.iconSize
implicitWidth: trackHeadRoot.iconSize
iconName: isLocked ? 'kdenlive-lock' : 'kdenlive-unlock'
iconSource: isLocked ? 'qrc:///pics/kdenlive-lock.svg' : 'qrc:///pics/kdenlive-unlock.svg'
onClicked: controller.setTrackLockedState(trackId, !isLocked)
- tooltip: isLocked? i18n('Unlock track') : i18n('Lock track')
+ tooltip: isLocked? i18n("Unlock track") : i18n("Lock track")
SequentialAnimation {
id: flashLock
loops: 1
ScaleAnimator {
target: lockButton
from: 1
to: 2
duration: 500
}
ScaleAnimator {
target: lockButton
from: 2
to: 1
duration: 500
}
}
}
Layout.rightMargin: 4
}
RowLayout {
id: recLayout
Layout.maximumHeight: showAudioRecord ? -1 : 0
Loader {
id: audioVuMeter
Layout.fillWidth: true
Layout.rightMargin: 2
Layout.leftMargin: 4
visible: showAudioRecord && (trackHeadRoot.height >= 2 * muteButton.height + resizer.height)
source: isAudio && showAudioRecord ? "AudioLevels.qml" : ""
onLoaded: item.trackId = trackId
}
}
RowLayout {
Rectangle {
id: trackLabel
color: 'transparent'
Layout.fillWidth: true
radius: 2
border.color: trackNameMouseArea.containsMouse ? activePalette.highlight : 'transparent'
height: nameEdit.height
visible: (trackHeadRoot.height >= trackLabel.height + muteButton.height + resizer.height + recLayout.height)
MouseArea {
id: trackNameMouseArea
anchors.fill: parent
hoverEnabled: true
propagateComposedEvents: true
onDoubleClicked: {
nameEdit.visible = true
nameEdit.focus = true
nameEdit.selectAll()
}
onClicked: {
trackHeadRoot.clicked()
trackHeadRoot.focus = true
}
onEntered: {
if (nameEdit.visible == false && trackName == '') {
placeHolder.visible = true
}
}
onExited: {
if (placeHolder.visible == true) {
placeHolder.visible = false
}
}
}
Label {
text: trackName
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
elide: Qt.ElideRight
font.pointSize: root.baseUnit * 0.9
}
Label {
id: placeHolder
visible: false
enabled: false
- text: i18n('Edit track name')
+ text: i18n("Edit track name")
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
elide: Qt.ElideRight
font.pointSize: root.baseUnit * 0.9
}
TextField {
id: nameEdit
visible: false
width: parent.width
text: trackName
font.pointSize: root.baseUnit * 0.9
style: TextFieldStyle {
padding.top:0
padding.bottom: 0
background: Rectangle {
color: activePalette.base
anchors.fill: parent
}
}
onEditingFinished: {
controller.setTrackProperty(trackId, "kdenlive:track_name", text)
visible = false
}
}
}
}
Item {
// Spacer
id: spacer
Layout.fillWidth: true
Layout.fillHeight: true
}
}
Rectangle {
id: resizer
height: 4
color: 'red'
opacity: 0
Drag.active: trimInMouseArea.drag.active
Drag.proposedAction: Qt.MoveAction
width: trackHeadRoot.width
y: trackHeadRoot.height - height
MouseArea {
id: trimInMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.SizeVerCursor
drag.target: parent
drag.axis: Drag.YAxis
drag.minimumY: trackHeadRoot.collapsedHeight - resizer.height
property double startY
property double originalY
drag.smoothed: false
onPressed: {
root.stopScrolling = true
startY = mapToItem(null, x, y).y
originalY = trackHeadRoot.height // reusing originalX to accumulate delta for bubble help
}
onReleased: {
root.stopScrolling = false
if (!trimInMouseArea.containsMouse) {
parent.opacity = 0
}
if (mouse.modifiers & Qt.ShiftModifier) {
timeline.adjustAllTrackHeight(trackHeadRoot.trackId, trackHeadRoot.myTrackHeight)
}
}
onEntered: parent.opacity = 0.3
onExited: parent.opacity = 0
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
parent.opacity = 0.5
var newHeight = originalY + (mapToItem(null, x, y).y - startY)
newHeight = Math.max(collapsedHeight, newHeight)
trackHeadRoot.myTrackHeight = newHeight
}
}
}
}
DropArea { //Drop area for tracks
anchors.fill: trackHeadRoot
keys: 'kdenlive/effect'
property string dropData
property string dropSource
property int dropRow: -1
onEntered: {
dropData = drag.getDataAsString('kdenlive/effect')
dropSource = drag.getDataAsString('kdenlive/effectsource')
}
onDropped: {
console.log("Add effect: ", dropData)
if (dropSource == '') {
// drop from effects list
controller.addTrackEffect(trackHeadRoot.trackId, dropData);
} else {
controller.copyTrackEffect(trackHeadRoot.trackId, dropSource);
}
dropSource = ''
dropRow = -1
drag.acceptProposedAction
}
}
}
diff --git a/src/timeline2/view/qml/timeline.qml b/src/timeline2/view/qml/timeline.qml
index 5adeb3ab5..dde94ad9c 100644
--- a/src/timeline2/view/qml/timeline.qml
+++ b/src/timeline2/view/qml/timeline.qml
@@ -1,1507 +1,1507 @@
import QtQuick 2.6
import QtQml.Models 2.2
import QtQuick.Controls 1.4 as OLD
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import Kdenlive.Controls 1.0
import QtQuick.Window 2.2
import 'Timeline.js' as Logic
Rectangle {
id: root
objectName: "timelineview"
SystemPalette { id: activePalette }
color: activePalette.window
property bool validMenu: false
property color textColor: activePalette.text
signal clipClicked()
signal mousePosChanged(int position)
signal zoomIn(bool onMouse)
signal zoomOut(bool onMouse)
FontMetrics {
id: fontMetrics
font.family: "Arial"
}
ClipMenu {
id: clipMenu
}
CompositionMenu {
id: compositionMenu
}
function fitZoom() {
return scrollView.width / (timeline.duration * 1.1)
}
function scrollPos() {
return scrollView.flickableItem.contentX
}
function goToStart(pos) {
scrollView.flickableItem.contentX = pos
}
function updatePalette() {
root.color = activePalette.window
root.textColor = activePalette.text
playhead.fillColor = activePalette.windowText
ruler.repaintRuler()
}
function moveSelectedTrack(offset) {
var cTrack = Logic.getTrackIndexFromId(timeline.activeTrack)
var newTrack = cTrack + offset
var max = tracksRepeater.count;
if (newTrack < 0) {
newTrack = max - 1;
} else if (newTrack >= max) {
newTrack = 0;
}
console.log('Setting curr tk: ', newTrack, 'MAX: ',max)
timeline.activeTrack = tracksRepeater.itemAt(newTrack).trackInternalId
}
function zoomByWheel(wheel) {
if (wheel.modifiers & Qt.AltModifier) {
// Seek to next snap
if (wheel.angleDelta.x > 0) {
timeline.triggerAction('monitor_seek_snap_backward')
} else {
timeline.triggerAction('monitor_seek_snap_forward')
}
} else if (wheel.modifiers & Qt.ControlModifier) {
root.wheelAccumulatedDelta += wheel.angleDelta.y;
// Zoom
if (root.wheelAccumulatedDelta >= defaultDeltasPerStep) {
root.zoomIn(true);
root.wheelAccumulatedDelta = 0;
} else if (root.wheelAccumulatedDelta <= -defaultDeltasPerStep) {
root.zoomOut(true);
root.wheelAccumulatedDelta = 0;
}
} else if (wheel.modifiers & Qt.ShiftModifier) {
// Vertical scroll
var newScroll = Math.min(scrollView.flickableItem.contentY - wheel.angleDelta.y, trackHeaders.height - tracksArea.height + scrollView.__horizontalScrollBar.height + cornerstone.height)
scrollView.flickableItem.contentY = Math.max(newScroll, 0)
} else {
// Horizontal scroll
var newScroll = Math.min(scrollView.flickableItem.contentX - wheel.angleDelta.y, timeline.fullDuration * root.timeScale - (scrollView.width - scrollView.__verticalScrollBar.width))
scrollView.flickableItem.contentX = Math.max(newScroll, 0)
}
wheel.accepted = true
}
function continuousScrolling(x) {
// This provides continuous scrolling at the left/right edges.
if (x > scrollView.flickableItem.contentX + scrollView.width - 50) {
scrollTimer.item = clip
scrollTimer.backwards = false
scrollTimer.start()
} else if (x < 50) {
scrollView.flickableItem.contentX = 0;
scrollTimer.stop()
} else if (x < scrollView.flickableItem.contentX + 50) {
scrollTimer.item = clip
scrollTimer.backwards = true
scrollTimer.start()
} else {
scrollTimer.stop()
}
}
function getTrackYFromId(a_track) {
return Logic.getTrackYFromId(a_track)
}
function getTrackYFromMltIndex(a_track) {
return Logic.getTrackYFromMltIndex(a_track)
}
function getTracksCount() {
return Logic.getTracksList()
}
function getMousePos() {
return (scrollView.flickableItem.contentX + tracksArea.mouseX) / timeline.scaleFactor
}
function getScrollPos() {
return scrollView.flickableItem.contentX
}
function setScrollPos(pos) {
return scrollView.flickableItem.contentX = pos
}
function getCopiedItemId() {
return copiedClip
}
function getMouseTrack() {
return Logic.getTrackIdFromPos(tracksArea.mouseY - ruler.height + scrollView.flickableItem.contentY)
}
function getTrackColor(audio, header) {
var col = activePalette.alternateBase
if (audio) {
col = Qt.tint(col, "#06FF00CC")
}
if (header) {
col = Qt.darker(col, 1.05)
}
return col
}
function clearDropData() {
clipBeingDroppedId = -1
droppedPosition = -1
droppedTrack = -1
scrollTimer.running = false
scrollTimer.stop()
}
function isDragging() {
return dragProxy.draggedItem > -1 && dragProxyArea.pressed
}
function initDrag(itemObject, itemCoord, itemId, itemPos, itemTrack, isComposition) {
dragProxy.x = itemObject.modelStart * timeScale
dragProxy.y = itemCoord.y
dragProxy.width = itemObject.clipDuration * timeScale
dragProxy.height = itemCoord.height
dragProxy.masterObject = itemObject
dragProxy.draggedItem = itemId
dragProxy.sourceTrack = itemTrack
dragProxy.sourceFrame = itemPos
dragProxy.isComposition = isComposition
dragProxy.verticalOffset = isComposition ? itemObject.displayHeight : 0
}
function endDrag() {
dragProxy.draggedItem = -1
dragProxy.x = 0
dragProxy.y = 0
dragProxy.width = 0
dragProxy.height = 0
dragProxy.verticalOffset = 0
}
function getItemAtPos(tk, posx, isComposition) {
var track = Logic.getTrackById(tk)
var container = track.children[0]
var tentativeClip = undefined
//console.log('TESTING ITMES OK TK: ', tk, ', POS: ', posx, ', CHILREN: ', container.children.length, ', COMPO: ', isComposition)
for (var i = 0 ; i < container.children.length; i++) {
if (container.children[i].children.length == 0 || container.children[i].children[0].children.length == 0) {
continue
}
tentativeClip = container.children[i].children[0].childAt(posx, 1)
if (tentativeClip && tentativeClip.clipId && (tentativeClip.isComposition == isComposition)) {
//console.log('found item with id: ', tentativeClip.clipId, ' IS COMPO: ', tentativeClip.isComposition)
break
}
}
return tentativeClip
}
property int headerWidth: timeline.headerWidth()
property int activeTool: 0
property real baseUnit: fontMetrics.font.pointSize
property color selectedTrackColor: Qt.rgba(activePalette.highlight.r, activePalette.highlight.g, activePalette.highlight.b, 0.2)
property color frameColor: Qt.rgba(activePalette.shadow.r, activePalette.shadow.g, activePalette.shadow.b, 0.3)
property bool stopScrolling: false
property int duration: timeline.duration
property color audioColor: timeline.audioColor
property color videoColor: timeline.videoColor
property color lockedColor: timeline.lockedColor
property color selectionColor: timeline.selectionColor
property color groupColor: timeline.groupColor
property int clipBeingDroppedId: -1
property string clipBeingDroppedData
property int droppedPosition: -1
property int droppedTrack: -1
property int clipBeingMovedId: -1
property int spacerGroup: -1
property int spacerFrame: -1
property int spacerClickFrame: -1
property real timeScale: timeline.scaleFactor
property real snapping: (timeline.snap && (timeScale < 2 * baseUnit)) ? 10 / Math.sqrt(timeScale) - 0.5 : -1
property var timelineSelection: timeline.selection
property int trackHeight
property int copiedClip: -1
property int zoomOnMouse: -1
property int viewActiveTrack: timeline.activeTrack
property int wheelAccumulatedDelta: 0
readonly property int defaultDeltasPerStep: 120
//onCurrentTrackChanged: timeline.selection = []
onTimeScaleChanged: {
if (root.zoomOnMouse >= 0) {
scrollView.flickableItem.contentX = Math.max(0, root.zoomOnMouse * timeline.scaleFactor - tracksArea.mouseX)
root.zoomOnMouse = -1
} else {
scrollView.flickableItem.contentX = Math.max(0, (timeline.seekPosition > -1 ? timeline.seekPosition : timeline.position) * timeline.scaleFactor - (scrollView.width / 2))
}
//root.snapping = timeline.snap ? 10 / Math.sqrt(root.timeScale) : -1
ruler.adjustStepSize()
if (dragProxy.draggedItem > -1 && dragProxy.masterObject) {
// update dragged item pos
dragProxy.masterObject.updateDrag()
}
}
onViewActiveTrackChanged: {
var tk = Logic.getTrackById(timeline.activeTrack)
if (tk.y < scrollView.flickableItem.contentY) {
scrollView.flickableItem.contentY = Math.max(0, tk.y - scrollView.height / 3)
} else if (tk.y + tk.height > scrollView.flickableItem.contentY + scrollView.viewport.height) {
scrollView.flickableItem.contentY = Math.min(trackHeaders.height - scrollView.height + scrollView.__horizontalScrollBar.height, tk.y - scrollView.height / 3)
}
}
onActiveToolChanged: {
if (root.activeTool == 2) {
// Spacer activated
endDrag()
} else if (root.activeTool == 0) {
var tk = getMouseTrack()
if (tk < 0) {
console.log('........ MOUSE OUTSIDE TRAKS\n\n.........')
return
}
var pos = getMousePos() * timeline.scaleFactor
var sourceTrack = Logic.getTrackById(tk)
var allowComposition = tracksArea.mouseY- sourceTrack.y > sourceTrack.height / 2
var tentativeItem = undefined
if (allowComposition) {
tentativeItem = getItemAtPos(tk, pos, true)
}
if (!tentativeItem) {
tentativeItem = getItemAtPos(tk, pos, false)
}
if (tentativeItem) {
tentativeItem.updateDrag()
}
}
}
DropArea { //Drop area for compositions
width: root.width - headerWidth
height: root.height - ruler.height
y: ruler.height
x: headerWidth
keys: 'kdenlive/composition'
onEntered: {
console.log("Trying to drop composition")
if (clipBeingMovedId == -1) {
console.log("No clip being moved")
var track = Logic.getTrackIdFromPos(drag.y + scrollView.flickableItem.contentY)
var frame = Math.round((drag.x + scrollView.flickableItem.contentX) / timeline.scaleFactor)
droppedPosition = frame
if (track >= 0 && !controller.isAudioTrack(track)) {
clipBeingDroppedData = drag.getDataAsString('kdenlive/composition')
console.log("Trying to insert",track, frame, clipBeingDroppedData)
clipBeingDroppedId = timeline.insertComposition(track, frame, clipBeingDroppedData, false)
console.log("id",clipBeingDroppedId)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
drag.acceptProposedAction()
} else {
drag.accepted = false
}
}
}
onPositionChanged: {
if (clipBeingMovedId == -1) {
var track = Logic.getTrackIdFromPos(drag.y + scrollView.flickableItem.contentY)
if (track !=-1) {
var frame = Math.round((drag.x + scrollView.flickableItem.contentX) / timeline.scaleFactor)
frame = controller.suggestSnapPoint(frame, Math.floor(root.snapping))
if (clipBeingDroppedId >= 0){
if (controller.isAudioTrack(track)) {
// Don't allow moving composition to an audio track
track = controller.getCompositionTrackId(clipBeingDroppedId)
}
controller.requestCompositionMove(clipBeingDroppedId, track, frame, true, false)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
} else if (!controller.isAudioTrack(track)) {
clipBeingDroppedData = drag.getDataAsString('kdenlive/composition')
clipBeingDroppedId = timeline.insertComposition(track, frame, clipBeingDroppedData , false)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
}
}
}
}
onExited:{
if (clipBeingDroppedId != -1) {
controller.requestItemDeletion(clipBeingDroppedId, false)
}
clearDropData()
}
onDropped: {
if (clipBeingDroppedId != -1) {
var frame = controller.getCompositionPosition(clipBeingDroppedId)
var track = controller.getCompositionTrackId(clipBeingDroppedId)
// we simulate insertion at the final position so that stored undo has correct value
controller.requestItemDeletion(clipBeingDroppedId, false)
timeline.insertNewComposition(track, frame, clipBeingDroppedData, true)
}
clearDropData()
}
}
DropArea { //Drop area for bin/clips
/** @brief local helper function to handle the insertion of multiple dragged items */
function insertAndMaybeGroup(track, frame, droppedData) {
var binIds = droppedData.split(";")
if (binIds.length == 0) {
return -1
}
var id = -1
if (binIds.length == 1) {
id = timeline.insertClip(timeline.activeTrack, frame, clipBeingDroppedData, false, true, false)
} else {
var ids = timeline.insertClips(timeline.activeTrack, frame, binIds, false, true, false)
// if the clip insertion succeeded, request the clips to be grouped
if (ids.length > 0) {
timeline.selectItems(ids)
id = ids[0]
}
}
return id
}
property int fakeFrame: -1
property int fakeTrack: -1
width: root.width - headerWidth
height: root.height - ruler.height
y: ruler.height
x: headerWidth
keys: 'kdenlive/producerslist'
onEntered: {
if (clipBeingMovedId == -1) {
//var track = Logic.getTrackIdFromPos(drag.y)
var track = Logic.getTrackIndexFromPos(drag.y + scrollView.flickableItem.contentY)
if (track >= 0 && track < tracksRepeater.count) {
var frame = Math.round((drag.x + scrollView.flickableItem.contentX) / timeline.scaleFactor)
droppedPosition = frame
timeline.activeTrack = tracksRepeater.itemAt(track).trackInternalId
//drag.acceptProposedAction()
clipBeingDroppedData = drag.getDataAsString('kdenlive/producerslist')
console.log('dropped data: ', clipBeingDroppedData)
if (controller.normalEdit()) {
clipBeingDroppedId = insertAndMaybeGroup(timeline.activeTrack, frame, clipBeingDroppedData)
} else {
// we want insert/overwrite mode, make a fake insert at end of timeline, then move to position
clipBeingDroppedId = insertAndMaybeGroup(timeline.activeTrack, timeline.fullDuration, clipBeingDroppedData)
fakeFrame = controller.suggestClipMove(clipBeingDroppedId, timeline.activeTrack, frame, timeline.position, Math.floor(root.snapping))
fakeTrack = timeline.activeTrack
}
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
} else {
drag.accepted = false
}
}
}
onExited:{
if (clipBeingDroppedId != -1) {
controller.requestItemDeletion(clipBeingDroppedId, false)
}
clearDropData()
}
onPositionChanged: {
if (clipBeingMovedId == -1) {
var track = Logic.getTrackIndexFromPos(drag.y + scrollView.flickableItem.contentY)
if (track >= 0 && track < tracksRepeater.count) {
timeline.activeTrack = tracksRepeater.itemAt(track).trackInternalId
var frame = Math.round((drag.x + scrollView.flickableItem.contentX) / timeline.scaleFactor)
if (clipBeingDroppedId >= 0){
fakeFrame = controller.suggestClipMove(clipBeingDroppedId, timeline.activeTrack, frame, timeline.position, Math.floor(root.snapping))
fakeTrack = timeline.activeTrack
//controller.requestClipMove(clipBeingDroppedId, timeline.activeTrack, frame, true, false, false)
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
} else {
frame = controller.suggestSnapPoint(frame, Math.floor(root.snapping))
if (controller.normalEdit()) {
clipBeingDroppedId = insertAndMaybeGroup(timeline.activeTrack, frame, drag.getDataAsString('kdenlive/producerslist'), false, true)
} else {
// we want insert/overwrite mode, make a fake insert at end of timeline, then move to position
clipBeingDroppedId = insertAndMaybeGroup(timeline.activeTrack, timeline.fullDuration, clipBeingDroppedData)
fakeFrame = controller.suggestClipMove(clipBeingDroppedId, timeline.activeTrack, frame, timeline.position, Math.floor(root.snapping))
fakeTrack = timeline.activeTrack
}
continuousScrolling(drag.x + scrollView.flickableItem.contentX)
}
}
}
}
onDropped: {
if (clipBeingDroppedId != -1) {
var frame = controller.getClipPosition(clipBeingDroppedId)
var track = controller.getClipTrackId(clipBeingDroppedId)
if (!controller.normalEdit()) {
frame = fakeFrame
track = fakeTrack
}
/* We simulate insertion at the final position so that stored undo has correct value
* NOTE: even if dropping multiple clips, requesting the deletion of the first one is
* enough as internally it will request the group deletion
*/
controller.requestItemDeletion(clipBeingDroppedId, false)
var binIds = clipBeingDroppedData.split(";")
if (binIds.length == 1) {
if (controller.normalEdit()) {
timeline.insertClip(track, frame, clipBeingDroppedData, true, true, false)
} else {
timeline.insertClipZone(clipBeingDroppedData, track, frame)
}
} else {
if (controller.normalEdit()) {
timeline.insertClips(track, frame, binIds, true, true)
} else {
// TODO
console.log('multiple clips insert/overwrite not supported yet')
}
}
fakeTrack = -1
fakeFrame = -1
}
clearDropData()
}
}
OLD.Menu {
id: menu
property int clickedX
property int clickedY
onAboutToHide: {
timeline.ungrabHack()
editGuideMenu.visible = false
}
OLD.MenuItem {
- text: i18n('Paste')
+ text: i18n("Paste")
iconName: 'edit-paste'
visible: copiedClip != -1
onTriggered: {
var track = Logic.getTrackIdFromPos(menu.clickedY - ruler.height + scrollView.flickableItem.contentY)
var frame = Math.floor((menu.clickedX + scrollView.flickableItem.contentX) / timeline.scaleFactor)
timeline.pasteItem(frame, track)
}
}
OLD.MenuItem {
- text: i18n('Insert Space')
+ text: i18n("Insert Space")
onTriggered: {
var track = Logic.getTrackIdFromPos(menu.clickedY - ruler.height + scrollView.flickableItem.contentY)
var frame = Math.floor((menu.clickedX + scrollView.flickableItem.contentX) / timeline.scaleFactor)
timeline.insertSpace(track, frame);
}
}
OLD.MenuItem {
- text: i18n('Remove Space On Active Track')
+ text: i18n("Remove Space On Active Track")
onTriggered: {
var track = Logic.getTrackIdFromPos(menu.clickedY - ruler.height + scrollView.flickableItem.contentY)
var frame = Math.floor((menu.clickedX + scrollView.flickableItem.contentX) / timeline.scaleFactor)
timeline.removeSpace(track, frame);
}
}
OLD.MenuItem {
- text: i18n('Remove Space')
+ text: i18n("Remove Space")
onTriggered: {
var track = Logic.getTrackIdFromPos(menu.clickedY - ruler.height + scrollView.flickableItem.contentY)
var frame = Math.floor((menu.clickedX + scrollView.flickableItem.contentX) / timeline.scaleFactor)
timeline.removeSpace(track, frame, true);
}
}
OLD.MenuItem {
id: addGuideMenu
- text: i18n('Add Guide')
+ text: i18n("Add Guide")
onTriggered: {
timeline.switchGuide(timeline.position);
}
}
GuidesMenu {
- title: i18n('Go to guide...')
+ title: i18n("Go to guide...")
menuModel: guidesModel
enabled: guidesDelegateModel.count > 0
onGuideSelected: {
timeline.seekPosition = assetFrame
timeline.position = timeline.seekPosition
}
}
OLD.MenuItem {
id: editGuideMenu
- text: i18n('Edit Guide')
+ text: i18n("Edit Guide")
visible: false
onTriggered: {
timeline.editGuide(timeline.position);
}
}
AssetMenu {
- title: i18n('Insert a composition...')
+ title: i18n("Insert a composition...")
menuModel: transitionModel
isTransition: true
onAssetSelected: {
var track = Logic.getTrackIdFromPos(menu.clickedY - ruler.height + scrollView.flickableItem.contentY)
var frame = Math.round((menu.clickedX + scrollView.flickableItem.contentX) / timeline.scaleFactor)
var id = timeline.insertComposition(track, frame, assetId, true)
if (id == -1) {
compositionFail.open()
}
}
}
onAboutToShow: {
if (guidesModel.hasMarker(timeline.position)) {
// marker at timeline position
- addGuideMenu.text = i18n('Remove Guide')
+ addGuideMenu.text = i18n("Remove Guide")
editGuideMenu.visible = true
} else {
- addGuideMenu.text = i18n('Add Guide')
+ addGuideMenu.text = i18n("Add Guide")
}
console.log("pop menu")
}
}
OLD.Menu {
id: rulermenu
property int clickedX
property int clickedY
onAboutToHide: {
timeline.ungrabHack()
editGuideMenu2.visible = false
}
OLD.MenuItem {
id: addGuideMenu2
- text: i18n('Add Guide')
+ text: i18n("Add Guide")
onTriggered: {
timeline.switchGuide(timeline.position);
}
}
GuidesMenu {
- title: i18n('Go to guide...')
+ title: i18n("Go to guide...")
menuModel: guidesModel
enabled: guidesDelegateModel.count > 0
onGuideSelected: {
timeline.seekPosition = assetFrame
timeline.position = timeline.seekPosition
}
}
OLD.MenuItem {
id: editGuideMenu2
- text: i18n('Edit Guide')
+ text: i18n("Edit Guide")
visible: false
onTriggered: {
timeline.editGuide(timeline.position);
}
}
OLD.MenuItem {
id: addProjectNote
- text: i18n('Add Project Note')
+ text: i18n("Add Project Note")
onTriggered: {
timeline.triggerAction('add_project_note')
}
}
onAboutToShow: {
if (guidesModel.hasMarker(timeline.position)) {
// marker at timeline position
- addGuideMenu2.text = i18n('Remove Guide')
+ addGuideMenu2.text = i18n("Remove Guide")
editGuideMenu2.visible = true
} else {
- addGuideMenu2.text = i18n('Add Guide')
+ addGuideMenu2.text = i18n("Add Guide")
}
console.log("pop menu")
}
}
MessageDialog {
id: compositionFail
title: i18n("Timeline error")
icon: StandardIcon.Warning
text: i18n("Impossible to add a composition at that position. There might not be enough space")
standardButtons: StandardButton.Ok
}
OLD.Menu {
id: headerMenu
property int trackId: -1
property int thumbsFormat: 0
property bool audioTrack: false
property bool recEnabled: false
onAboutToHide: {
timeline.ungrabHack()
}
OLD.MenuItem {
- text: i18n('Add Track')
+ text: i18n("Add Track")
onTriggered: {
timeline.addTrack(timeline.activeTrack)
}
}
OLD.MenuItem {
- text: i18n('Delete Track')
+ text: i18n("Delete Track")
onTriggered: {
timeline.deleteTrack(timeline.activeTrack)
}
}
OLD.MenuItem {
visible: headerMenu.audioTrack
id: showRec
- text: "Show Record Controls"
+ text: i18n("Show Record Controls")
onTriggered: {
controller.setTrackProperty(headerMenu.trackId, "kdenlive:audio_rec", showRec.checked ? '1' : '0')
}
checkable: true
checked: headerMenu.recEnabled
}
OLD.MenuItem {
visible: headerMenu.audioTrack
id: configRec
- text: "Configure Recording"
+ text: i18n("Configure Recording")
onTriggered: {
timeline.showConfig(4,2)
}
}
OLD.Menu {
- title: i18n('Track thumbnails')
+ title: i18n("Track thumbnails")
visible: !headerMenu.audioTrack
OLD.ExclusiveGroup { id: thumbStyle }
OLD.MenuItem {
- text: "In frame"
+ text: i18n("In frame")
id: inFrame
onTriggered:controller.setTrackProperty(headerMenu.trackId, "kdenlive:thumbs_format", 2)
checkable: true
exclusiveGroup: thumbStyle
}
OLD.MenuItem {
- text: "In / out frames"
+ text: i18n("In / out frames")
id: inOutFrame
onTriggered:controller.setTrackProperty(headerMenu.trackId, "kdenlive:thumbs_format", 0)
checkable: true
checked: true
exclusiveGroup: thumbStyle
}
OLD.MenuItem {
- text: "All frames"
+ text: i18n("All frames")
id: allFrame
onTriggered:controller.setTrackProperty(headerMenu.trackId, "kdenlive:thumbs_format", 1)
checkable: true
exclusiveGroup: thumbStyle
}
OLD.MenuItem {
- text: "No thumbnails"
+ text: i18n("No thumbnails")
id: noFrame
onTriggered:controller.setTrackProperty(headerMenu.trackId, "kdenlive:thumbs_format", 3)
checkable: true
exclusiveGroup: thumbStyle
}
onAboutToShow: {
switch(headerMenu.thumbsFormat) {
case 3:
noFrame.checked = true
break
case 2:
inFrame.checked = true
break
case 1:
allFrame.checked = true
break
default:
inOutFrame.checked = true
break
}
}
}
}
Row {
Column {
id: headerContainer
z: 1
Rectangle {
id: cornerstone
property bool selected: false
// Padding between toolbar and track headers.
width: headerWidth
height: ruler.height
color: 'transparent' //selected? shotcutBlue : activePalette.window
border.color: selected? 'red' : 'transparent'
border.width: selected? 1 : 0
z: 1
}
Flickable {
// Non-slider scroll area for the track headers.
id: headerFlick
contentY: scrollView.flickableItem.contentY
width: headerWidth
height: 100
interactive: false
MouseArea {
width: trackHeaders.width
height: trackHeaders.height
acceptedButtons: Qt.NoButton
onWheel: {
var newScroll = Math.min(scrollView.flickableItem.contentY - wheel.angleDelta.y, height - tracksArea.height + scrollView.__horizontalScrollBar.height + cornerstone.height)
scrollView.flickableItem.contentY = Math.max(newScroll, 0)
}
}
Column {
id: trackHeaders
spacing: 0
Repeater {
id: trackHeaderRepeater
model: multitrack
TrackHead {
trackName: model.name
thumbsFormat: model.thumbsFormat
trackTag: model.trackTag
isDisabled: model.disabled
isComposite: model.composite
isLocked: model.locked
isActive: model.trackActive
isAudio: model.audio
showAudioRecord: model.audioRecord
effectNames: model.effectNames
isStackEnabled: model.isStackEnabled
width: headerWidth
current: item === timeline.activeTrack
trackId: item
height: model.trackHeight
onIsLockedChanged: tracksRepeater.itemAt(index).isLocked = isLocked
collapsed: height <= collapsedHeight
onMyTrackHeightChanged: {
collapsed = myTrackHeight <= collapsedHeight
if (!collapsed) {
controller.setTrackProperty(trackId, "kdenlive:trackheight", myTrackHeight)
controller.setTrackProperty(trackId, "kdenlive:collapsed", "0")
} else {
controller.setTrackProperty(trackId, "kdenlive:collapsed", collapsedHeight)
}
// hack: change property to trigger transition adjustment
root.trackHeight = root.trackHeight === 1 ? 0 : 1
}
onClicked: {
timeline.activeTrack = tracksRepeater.itemAt(index).trackInternalId
console.log('track name: ',index, ' = ', model.name,'/',tracksRepeater.itemAt(index).trackInternalId)
//timeline.selectTrackHead(currentTrack)
}
}
}
}
Column {
id: trackHeadersResizer
spacing: 0
width: 5
Rectangle {
id: resizer
height: trackHeaders.height
width: 3
x: root.headerWidth - 2
color: 'red'
opacity: 0
Drag.active: headerMouseArea.drag.active
Drag.proposedAction: Qt.MoveAction
MouseArea {
id: headerMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.SizeHorCursor
drag.target: parent
drag.axis: Drag.XAxis
drag.minimumX: 2 * baseUnit
property double startX
property double originalX
drag.smoothed: false
onPressed: {
root.stopScrolling = true
}
onReleased: {
root.stopScrolling = false
parent.opacity = 0
}
onEntered: parent.opacity = 0.5
onExited: parent.opacity = 0
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
parent.opacity = 0.5
headerWidth = Math.max(10, mapToItem(null, x, y).x + 2)
timeline.setHeaderWidth(headerWidth)
}
}
}
}
}
}
}
MouseArea {
id: tracksArea
property real clickX
property real clickY
width: root.width - headerWidth
height: root.height
Keys.onDownPressed: {
root.moveSelectedTrack(1)
}
Keys.onUpPressed: {
root.moveSelectedTrack(-1)
}
// This provides continuous scrubbing and scimming at the left/right edges.
hoverEnabled: true
acceptedButtons: Qt.RightButton | Qt.LeftButton | Qt.MidButton
cursorShape: tracksArea.mouseY < ruler.height || root.activeTool === 0 ? Qt.ArrowCursor : root.activeTool === 1 ? Qt.IBeamCursor : Qt.SplitHCursor
onWheel: {
if (wheel.modifiers & Qt.AltModifier) {
// Alt + wheel = seek to next snap point
if (wheel.angleDelta.x > 0) {
timeline.triggerAction('monitor_seek_snap_backward')
} else {
timeline.triggerAction('monitor_seek_snap_forward')
}
} else {
var delta = wheel.modifiers & Qt.ShiftModifier ? timeline.fps() : 1
if (timeline.seekPosition > -1) {
timeline.seekPosition = Math.min(timeline.seekPosition - (wheel.angleDelta.y > 0 ? delta : -delta), timeline.fullDuration - 1)
} else {
timeline.seekPosition = Math.min(timeline.position - (wheel.angleDelta.y > 0 ? delta : -delta), timeline.fullDuration - 1)
}
timeline.position = timeline.seekPosition
}
}
onPressed: {
focus = true
if (mouse.buttons === Qt.MidButton || (root.activeTool == 0 && mouse.modifiers & Qt.ControlModifier)) {
clickX = mouseX
clickY = mouseY
return
}
if (root.activeTool === 0 && mouse.modifiers & Qt.ShiftModifier && mouse.y > ruler.height) {
// rubber selection
rubberSelect.x = mouse.x + tracksArea.x
rubberSelect.y = mouse.y
rubberSelect.originX = mouse.x
rubberSelect.originY = rubberSelect.y
rubberSelect.width = 0
rubberSelect.height = 0
} else if (mouse.button & Qt.LeftButton) {
if (root.activeTool === 1) {
// razor tool
var y = mouse.y - ruler.height + scrollView.flickableItem.contentY
timeline.cutClipUnderCursor((scrollView.flickableItem.contentX + mouse.x) / timeline.scaleFactor, tracksRepeater.itemAt(Logic.getTrackIndexFromPos(y)).trackInternalId)
}
if (dragProxy.draggedItem > -1) {
mouse.accepted = false
return
}
if (root.activeTool === 2 && mouse.y > ruler.height) {
// spacer tool
var y = mouse.y - ruler.height + scrollView.flickableItem.contentY
var frame = (scrollView.flickableItem.contentX + mouse.x) / timeline.scaleFactor
var track = (mouse.modifiers & Qt.ControlModifier) ? tracksRepeater.itemAt(Logic.getTrackIndexFromPos(y)).trackInternalId : -1
spacerGroup = timeline.requestSpacerStartOperation(track, frame)
if (spacerGroup > -1) {
drag.axis = Drag.XAxis
Drag.active = true
Drag.proposedAction = Qt.MoveAction
spacerClickFrame = frame
spacerFrame = controller.getItemPosition(spacerGroup)
}
} else if (root.activeTool === 0 || mouse.y <= ruler.height) {
if (mouse.y > ruler.height) {
controller.requestClearSelection();
}
timeline.seekPosition = Math.min((scrollView.flickableItem.contentX + mouse.x) / timeline.scaleFactor, timeline.fullDuration - 1)
timeline.position = timeline.seekPosition
}
} else if (mouse.button & Qt.RightButton) {
menu.clickedX = mouse.x
menu.clickedY = mouse.y
if (mouse.y > ruler.height) {
timeline.activeTrack = tracksRepeater.itemAt(Logic.getTrackIndexFromPos(mouse.y - ruler.height + scrollView.flickableItem.contentY)).trackInternalId
menu.popup()
} else {
// ruler menu
rulermenu.popup()
}
}
}
property bool scim: false
onExited: {
scim = false
}
onPositionChanged: {
if (pressed && ((mouse.buttons === Qt.MidButton) || (mouse.buttons === Qt.LeftButton && root.activeTool == 0 && mouse.modifiers & Qt.ControlModifier))) {
var newScroll = Math.min(scrollView.flickableItem.contentX - (mouseX - clickX), timeline.fullDuration * root.timeScale - (scrollView.width - scrollView.__verticalScrollBar.width))
var vertScroll = Math.min(scrollView.flickableItem.contentY - (mouseY - clickY), trackHeaders.height - scrollView.height + scrollView.__horizontalScrollBar.height)
scrollView.flickableItem.contentX = Math.max(newScroll, 0)
scrollView.flickableItem.contentY = Math.max(vertScroll, 0)
clickX = mouseX
clickY = mouseY
return
}
if (!pressed && !rubberSelect.visible && root.activeTool === 1) {
cutLine.x = Math.floor((scrollView.flickableItem.contentX + mouse.x) / timeline.scaleFactor) * timeline.scaleFactor - scrollView.flickableItem.contentX
if (mouse.modifiers & Qt.ShiftModifier) {
timeline.position = Math.floor((scrollView.flickableItem.contentX + mouse.x) / timeline.scaleFactor)
}
}
if (dragProxy.draggedItem > -1) {
mouse.accepted = false
return
}
var mousePos = Math.max(0, Math.round((mouse.x + scrollView.flickableItem.contentX) / timeline.scaleFactor))
root.mousePosChanged(mousePos)
ruler.showZoneLabels = mouse.y < ruler.height
if (mouse.modifiers & Qt.ShiftModifier && mouse.buttons === Qt.LeftButton && root.activeTool === 0 && !rubberSelect.visible && rubberSelect.y > 0) {
// rubber selection
rubberSelect.visible = true
}
if (rubberSelect.visible) {
var newX = mouse.x
var newY = mouse.y
if (newX < rubberSelect.originX) {
rubberSelect.x = newX + tracksArea.x
rubberSelect.width = rubberSelect.originX - newX
} else {
rubberSelect.x = rubberSelect.originX + tracksArea.x
rubberSelect.width = newX - rubberSelect.originX
}
if (newY < rubberSelect.originY) {
rubberSelect.y = newY
rubberSelect.height = rubberSelect.originY - newY
} else {
rubberSelect.y = rubberSelect.originY
rubberSelect.height= newY - rubberSelect.originY
}
} else if (mouse.buttons === Qt.LeftButton) {
if (root.activeTool === 0 || mouse.y < ruler.height) {
timeline.seekPosition = Math.max(0, Math.min((scrollView.flickableItem.contentX + mouse.x) / timeline.scaleFactor, timeline.fullDuration - 1))
timeline.position = timeline.seekPosition
} else if (root.activeTool === 2 && spacerGroup > -1) {
// Move group
var track = controller.getItemTrackId(spacerGroup)
var frame = Math.round((mouse.x + scrollView.flickableItem.contentX) / timeline.scaleFactor) + spacerFrame - spacerClickFrame
frame = controller.suggestItemMove(spacerGroup, track, frame, timeline.position, Math.floor(root.snapping))
continuousScrolling(mouse.x + scrollView.flickableItem.contentX)
}
scim = true
} else {
scim = false
}
}
onReleased: {
if (rubberSelect.visible) {
rubberSelect.visible = false
var y = rubberSelect.y - ruler.height + scrollView.flickableItem.contentY
var topTrack = Logic.getTrackIndexFromPos(Math.max(0, y))
var bottomTrack = Logic.getTrackIndexFromPos(y + rubberSelect.height)
if (bottomTrack >= topTrack) {
var t = []
for (var i = topTrack; i <= bottomTrack; i++) {
t.push(tracksRepeater.itemAt(i).trackInternalId)
}
var startFrame = (scrollView.flickableItem.contentX - tracksArea.x + rubberSelect.x) / timeline.scaleFactor
var endFrame = (scrollView.flickableItem.contentX - tracksArea.x + rubberSelect.x + rubberSelect.width) / timeline.scaleFactor
timeline.selectItems(t, startFrame, endFrame, mouse.modifiers & Qt.ControlModifier);
}
rubberSelect.y = -1
} else if (mouse.modifiers & Qt.ShiftModifier) {
// Shift click, process seek
timeline.seekPosition = Math.min((scrollView.flickableItem.contentX + mouse.x) / timeline.scaleFactor, timeline.fullDuration - 1)
timeline.position = timeline.seekPosition
}
if (spacerGroup > -1) {
var frame = controller.getItemPosition(spacerGroup)
timeline.requestSpacerEndOperation(spacerGroup, spacerFrame, frame);
spacerClickFrame = -1
spacerFrame = -1
spacerGroup = -1
}
scim = false
}
Column {
Flickable {
// Non-slider scroll area for the Ruler.
id: rulercontainer
width: root.width - headerWidth
height: fontMetrics.font.pixelSize * 2
contentX: scrollView.flickableItem.contentX
contentWidth: Math.max(parent.width, timeline.fullDuration * timeScale)
interactive: false
clip: true
Ruler {
id: ruler
width: rulercontainer.contentWidth
height: parent.height
Rectangle {
id: seekCursor
visible: timeline.seekPosition > -1
color: activePalette.highlight
width: 4
height: ruler.height
opacity: 0.5
x: timeline.seekPosition * timeline.scaleFactor
}
TimelinePlayhead {
id: playhead
visible: timeline.position > -1
height: baseUnit
width: baseUnit * 1.5
fillColor: activePalette.windowText
anchors.bottom: parent.bottom
x: timeline.position * timeline.scaleFactor - (width / 2)
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
onWheel: zoomByWheel(wheel)
cursorShape: dragProxyArea.drag.active ? Qt.ClosedHandCursor : tracksArea.cursorShape
}
}
}
OLD.ScrollView {
id: scrollView
width: root.width - headerWidth
height: root.height - ruler.height
y: ruler.height
// Click and drag should seek, not scroll the timeline view
flickableItem.interactive: false
clip: true
Rectangle {
id: tracksContainerArea
width: Math.max(scrollView.width - scrollView.__verticalScrollBar.width, timeline.fullDuration * timeScale)
height: trackHeaders.height
//Math.max(trackHeaders.height, scrollView.contentHeight - scrollView.__horizontalScrollBar.height)
color: root.color
Rectangle {
// Drag proxy, responsible for clip / composition move
id: dragProxy
x: 0
y: 0
width: 0
height: 0
property int draggedItem: -1
property int sourceTrack
property int sourceFrame
property bool isComposition
property int verticalOffset
property var masterObject
color: 'transparent'
//opacity: 0.8
MouseArea {
id: dragProxyArea
anchors.fill: parent
drag.target: parent
drag.axis: Drag.XAxis
drag.smoothed: false
drag.minimumX: 0
property int dragFrame
property bool shiftClick: false
enabled: root.activeTool == 0
onPressed: {
console.log('+++++++++++++++++++ DRAG CLICKED +++++++++++++')
if (mouse.modifiers & Qt.ControlModifier) {
mouse.accepted = false
return
}
if (!timeline.exists(dragProxy.draggedItem)) {
endDrag()
mouse.accepted = false
return
}
dragFrame = -1
timeline.activeTrack = dragProxy.sourceTrack
if (mouse.modifiers & Qt.ShiftModifier) {
if (timeline.selection.indexOf(dragProxy.draggedItem) == -1) {
console.log('ADD SELECTION: ', dragProxy.draggedItem)
controller.requestAddToSelection(dragProxy.draggedItem)
} else {
console.log('REMOVE SELECTION: ', dragProxy.draggedItem)
controller.requestRemoveFromSelection(dragProxy.draggedItem)
//endDrag()
shiftClick = true
return
}
shiftClick = true
} else {
if (timeline.selection.indexOf(dragProxy.draggedItem) == -1) {
controller.requestAddToSelection(dragProxy.draggedItem, /*clear=*/ true)
}
shiftClick = false
}
timeline.showAsset(dragProxy.draggedItem)
root.stopScrolling = true
clipBeingMovedId = dragProxy.draggedItem
if (dragProxy.draggedItem > -1) {
var tk = controller.getItemTrackId(dragProxy.draggedItem)
var x = controller.getItemPosition(dragProxy.draggedItem)
var posx = Math.round((parent.x)/ root.timeScale)
var clickAccepted = true
if (controller.normalEdit() && (tk != Logic.getTrackIdFromPos(parent.y) || x != posx)) {
console.log('INCORRECT DRAG, Trying to recover item: ', parent.y,' XPOS: ',x,'=',posx,'on track: ',tk ,'\n!!!!!!!!!!')
// Try to find correct item
var tentativeClip = getItemAtPos(tk, mouseX + parent.x, dragProxy.isComposition)
if (tentativeClip && tentativeClip.clipId) {
clickAccepted = true
dragProxy.draggedItem = tentativeClip.clipId
dragProxy.x = tentativeClip.x
dragProxy.y = tentativeClip.y
dragProxy.width = tentativeClip.width
dragProxy.height = tentativeClip.height
dragProxy.masterObject = tentativeClip
dragProxy.sourceTrack = tk
dragProxy.sourceFrame = tentativeClip.modelStart
dragProxy.isComposition = tentativeClip.isComposition
} else {
clickAccepted = false
mouse.accepted = false
dragProxy.draggedItem = -1
dragProxy.masterObject = undefined
dragProxy.sourceFrame = -1
parent.x = 0
parent.y = 0
parent.width = 0
parent.height = 0
}
}
if (clickAccepted && dragProxy.draggedItem != -1) {
focus = true;
dragProxy.masterObject.originalX = dragProxy.masterObject.x
dragProxy.masterObject.originalTrackId = dragProxy.masterObject.trackId
dragProxy.masterObject.forceActiveFocus();
}
} else {
mouse.accepted = false
parent.x = 0
parent.y = 0
parent.width = 0
parent.height = 0
}
}
onPositionChanged: {
// we have to check item validity in the controller, because they could have been deleted since the beginning of the drag
if (dragProxy.draggedItem > -1 && !timeline.exists(dragProxy.draggedItem)) {
endDrag()
return
}
if (!shiftClick && dragProxy.draggedItem > -1 && mouse.buttons === Qt.LeftButton && (controller.isClip(dragProxy.draggedItem) || controller.isComposition(dragProxy.draggedItem))) {
continuousScrolling(mouse.x + parent.x)
var mapped = tracksContainerArea.mapFromItem(dragProxy, mouse.x, mouse.y).x
root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
var posx = Math.round((parent.x)/ root.timeScale)
var posy = Math.min(Math.max(0, mouse.y + parent.y - dragProxy.verticalOffset), tracksContainerArea.height)
var tId = Logic.getTrackIdFromPos(posy)
if (dragProxy.masterObject && tId == dragProxy.masterObject.trackId) {
if (posx == dragFrame) {
return
}
}
if (dragProxy.isComposition) {
dragFrame = controller.suggestCompositionMove(dragProxy.draggedItem, tId, posx, timeline.position, Math.floor(root.snapping))
timeline.activeTrack = timeline.getItemMovingTrack(dragProxy.draggedItem)
} else {
if (!controller.normalEdit() && dragProxy.masterObject.parent != dragContainer) {
var pos = dragProxy.masterObject.mapToGlobal(dragProxy.masterObject.x, dragProxy.masterObject.y);
dragProxy.masterObject.parent = dragContainer
pos = dragProxy.masterObject.mapFromGlobal(pos.x, pos.y)
dragProxy.masterObject.x = pos.x
dragProxy.masterObject.y = pos.y
//console.log('bringing item to front')
}
dragFrame = controller.suggestClipMove(dragProxy.draggedItem, tId, posx, timeline.position, Math.floor(root.snapping))
timeline.activeTrack = timeline.getItemMovingTrack(dragProxy.draggedItem)
}
var delta = dragFrame - dragProxy.sourceFrame
if (delta != 0) {
var s = timeline.timecode(Math.abs(delta))
// remove leading zeroes
if (s.substring(0, 3) === '00:')
s = s.substring(3)
s = ((delta < 0)? '-' : (delta > 0)? '+' : '') + s
bubbleHelp.show(parent.x, ruler.height, s)
} else bubbleHelp.hide()
}
}
onReleased: {
clipBeingMovedId = -1
root.stopScrolling = false
if (!shiftClick && dragProxy.draggedItem > -1 && dragFrame > -1 && (controller.isClip(dragProxy.draggedItem) || controller.isComposition(dragProxy.draggedItem))) {
var tId = controller.getItemTrackId(dragProxy.draggedItem)
if (dragProxy.isComposition) {
controller.requestCompositionMove(dragProxy.draggedItem, dragProxy.sourceTrack, dragProxy.sourceFrame, true, false, false)
controller.requestCompositionMove(dragProxy.draggedItem, tId, dragFrame , true, true, true)
} else {
if (controller.normalEdit()) {
controller.requestClipMove(dragProxy.draggedItem, dragProxy.sourceTrack, dragProxy.sourceFrame, true, false, false)
controller.requestClipMove(dragProxy.draggedItem, tId, dragFrame , true, true, true)
} else {
// Fake move, only process final move
timeline.endFakeMove(dragProxy.draggedItem, dragFrame, true, true, true)
}
}
dragProxy.x = controller.getItemPosition(dragProxy.draggedItem) * timeline.scaleFactor
dragProxy.sourceFrame = dragFrame
bubbleHelp.hide()
}
}
onDoubleClicked: {
if (dragProxy.masterObject.keyframeModel) {
var newVal = (dragProxy.height - mouseY) / dragProxy.height
var newPos = Math.round(mouseX / timeScale) + dragProxy.masterObject.inPoint
timeline.addEffectKeyframe(dragProxy.draggedItem, newPos, newVal)
}
}
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
onWheel: zoomByWheel(wheel)
cursorShape: dragProxyArea.drag.active ? Qt.ClosedHandCursor : tracksArea.cursorShape
}
Column {
// These make the striped background for the tracks.
// It is important that these are not part of the track visual hierarchy;
// otherwise, the clips will be obscured by the Track's background.
Repeater {
model: multitrack
id: trackBaseRepeater
delegate: Rectangle {
width: tracksContainerArea.width
border.width: 1
border.color: root.frameColor
height: model.trackHeight
color: tracksRepeater.itemAt(index) ? ((tracksRepeater.itemAt(index).trackInternalId === timeline.activeTrack) ? Qt.tint(getTrackColor(tracksRepeater.itemAt(index).isAudio, false), selectedTrackColor) : getTrackColor(tracksRepeater.itemAt(index).isAudio, false)) : 'red'
}
}
}
Column {
id: tracksContainer
Repeater { id: tracksRepeater; model: trackDelegateModel }
Item {
id: dragContainer
z: 100
}
Repeater { id: guidesRepeater; model: guidesDelegateModel }
}
Rectangle {
id: cursor
visible: timeline.position > -1
color: root.textColor
width: Math.max(1, 1 * timeline.scaleFactor)
opacity: (width > 2) ? 0.5 : 1
height: parent.height
x: timeline.position * timeline.scaleFactor
}
}
}
}
/*CornerSelectionShadow {
y: tracksRepeater.count ? tracksRepeater.itemAt(currentTrack).y + ruler.height - scrollView.flickableItem.contentY : 0
clip: timeline.selection.length ?
tracksRepeater.itemAt(currentTrack).clipAt(timeline.selection[0]) : null
opacity: clip && clip.x + clip.width < scrollView.flickableItem.contentX ? 1 : 0
}
CornerSelectionShadow {
y: tracksRepeater.count ? tracksRepeater.itemAt(currentTrack).y + ruler.height - scrollView.flickableItem.contentY : 0
clip: timeline.selection.length ?
tracksRepeater.itemAt(currentTrack).clipAt(timeline.selection[timeline.selection.length - 1]) : null
opacity: clip && clip.x > scrollView.flickableItem.contentX + scrollView.width ? 1 : 0
anchors.right: parent.right
mirrorGradient: true
}*/
Rectangle {
id: cutLine
visible: root.activeTool == 1 && tracksArea.mouseY > ruler.height
color: 'red'
width: Math.max(1, 1 * timeline.scaleFactor)
opacity: (width > 2) ? 0.5 : 1
height: root.height - scrollView.__horizontalScrollBar.height - ruler.height
x: 0
//x: timeline.position * timeline.scaleFactor - scrollView.flickableItem.contentX
y: ruler.height
}
}
}
Rectangle {
id: bubbleHelp
property alias text: bubbleHelpLabel.text
color: root.color //application.toolTipBaseColor
width: bubbleHelpLabel.width + 8
height: bubbleHelpLabel.height + 8
radius: 4
states: [
State { name: 'invisible'; PropertyChanges { target: bubbleHelp; opacity: 0} },
State { name: 'visible'; PropertyChanges { target: bubbleHelp; opacity: 0.8} }
]
state: 'invisible'
transitions: [
Transition {
from: 'invisible'
to: 'visible'
OpacityAnimator { target: bubbleHelp; duration: 200; easing.type: Easing.InOutQuad }
},
Transition {
from: 'visible'
to: 'invisible'
OpacityAnimator { target: bubbleHelp; duration: 200; easing.type: Easing.InOutQuad }
}
]
Label {
id: bubbleHelpLabel
color: activePalette.text //application.toolTipTextColor
anchors.centerIn: parent
font.pixelSize: root.baseUnit
}
function show(x, y, text) {
bubbleHelp.x = x + tracksArea.x - scrollView.flickableItem.contentX - bubbleHelpLabel.width
bubbleHelp.y = y + tracksArea.y - scrollView.flickableItem.contentY - bubbleHelpLabel.height
bubbleHelp.text = text
if (bubbleHelp.state !== 'visible')
bubbleHelp.state = 'visible'
}
function hide() {
bubbleHelp.state = 'invisible'
bubbleHelp.opacity = 0
}
}
Rectangle {
id: rubberSelect
property int originX
property int originY
y: -1
color: Qt.rgba(activePalette.highlight.r, activePalette.highlight.g, activePalette.highlight.b, 0.4)
border.color: activePalette.highlight
border.width: 1
visible: false
}
/*DropShadow {
source: bubbleHelp
anchors.fill: bubbleHelp
opacity: bubbleHelp.opacity
horizontalOffset: 3
verticalOffset: 3
radius: 8
color: '#80000000'
transparentBorder: true
fast: true
}*/
DelegateModel {
id: trackDelegateModel
model: multitrack
delegate: Track {
trackModel: multitrack
rootIndex: trackDelegateModel.modelIndex(index)
timeScale: timeline.scaleFactor
width: tracksContainerArea.width
height: trackHeight
isAudio: audio
trackThumbsFormat: thumbsFormat
isCurrentTrack: item === timeline.activeTrack
trackInternalId: item
z: tracksRepeater.count - index
}
}
DelegateModel {
id: guidesDelegateModel
model: guidesModel
Item {
id: guideRoot
z: 20
Rectangle {
id: guideBase
width: 1
height: tracksContainer.height
x: model.frame * timeScale;
color: model.color
}
Rectangle {
visible: mlabel.visible
opacity: 0.7
x: guideBase.x
y: mlabel.y
radius: 2
width: mlabel.width + 4
height: mlabel.height
color: model.color
MouseArea {
z: 10
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
property int startX
drag.axis: Drag.XAxis
drag.target: guideRoot
onPressed: {
drag.target = guideRoot
startX = guideRoot.x
}
onReleased: {
if (startX != guideRoot.x) {
timeline.moveGuide(model.frame, model.frame + guideRoot.x / timeline.scaleFactor)
}
drag.target = undefined
}
onPositionChanged: {
if (pressed) {
var frame = Math.round(model.frame + guideRoot.x / timeline.scaleFactor)
frame = controller.suggestSnapPoint(frame, root.snapping)
guideRoot.x = (frame - model.frame) * timeline.scaleFactor
}
}
drag.smoothed: false
onDoubleClicked: {
timeline.editGuide(model.frame)
drag.target = undefined
}
onClicked: timeline.position = guideBase.x / timeline.scaleFactor
}
}
Text {
id: mlabel
visible: timeline.showMarkers
text: model.comment
font.pixelSize: root.baseUnit
x: guideBase.x + 2
y: scrollView.flickableItem.contentY
color: 'white'
}
}
}
Connections {
target: timeline
onPositionChanged: if (!stopScrolling) Logic.scrollIfNeeded()
onFrameFormatChanged: ruler.adjustFormat()
onSelectionChanged: {
if (dragProxy.draggedItem > -1 && !timeline.exists(dragProxy.draggedItem)) {
endDrag()
}
}
}
// This provides continuous scrolling at the left/right edges.
Timer {
id: scrollTimer
interval: 25
repeat: true
triggeredOnStart: true
property var item
property bool backwards
onTriggered: {
var delta = backwards? -10 : 10
if (item) item.x += delta
scrollView.flickableItem.contentX += delta
if (scrollView.flickableItem.contentX <= 0 || clipBeingMovedId == -1)
stop()
}
}
}