diff --git a/CMakeLists.txt b/CMakeLists.txt index 29de2f2f8..80a390643 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,179 +1,184 @@ project(Kdenlive) # An odd patch version number means development version, while an even one means # stable release. An additional number can be used for bugfix-only releases. # KDE Application Version, managed by release script set(KDE_APPLICATIONS_VERSION_MAJOR "19") set(KDE_APPLICATIONS_VERSION_MINOR "11") -set(KDE_APPLICATIONS_VERSION_MICRO "70") +set(KDE_APPLICATIONS_VERSION_MICRO "80") set(KDENLIVE_VERSION ${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}) cmake_minimum_required(VERSION 3.0) if(POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() if(POLICY CMP0053) cmake_policy(SET CMP0053 NEW) endif() if(BUILD_FUZZING) set(CMAKE_CXX_FLAGS "${KDENLIVE_CXX_FLAGS} -fsanitize=fuzzer-no-link,address") endif() if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option") endif() # To be switched on when releasing. option(RELEASE_BUILD "Remove Git revision from program version" ON) option(BUILD_TESTING "Build tests" ON) option(BUILD_FUZZING "Build fuzzing target" OFF) # Minimum versions of main dependencies. set(MLT_MIN_MAJOR_VERSION 6) set(MLT_MIN_MINOR_VERSION 16) set(MLT_MIN_PATCH_VERSION 0) set(MLT_MIN_VERSION ${MLT_MIN_MAJOR_VERSION}.${MLT_MIN_MINOR_VERSION}.${MLT_MIN_PATCH_VERSION}) # KDE Frameworks find_package(ECM 5.18.0 REQUIRED CONFIG) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules) include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) include(ECMInstallIcons) include(GenerateExportHeader) include(KDEInstallDirs) include(KDECMakeSettings) include(ECMOptionalAddSubdirectory) include(ECMMarkNonGuiExecutable) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) include(ECMEnableSanitizers) add_definitions(-DTRANSLATION_DOMAIN=\"kdenlive\") find_package(KF5 REQUIRED COMPONENTS Archive Bookmarks CoreAddons Config ConfigWidgets DBusAddons KIO WidgetsAddons NotifyConfig NewStuff XmlGui Notifications GuiAddons TextWidgets IconThemes Declarative Solid OPTIONAL_COMPONENTS DocTools FileMetaData Crash Purpose) # Qt set(QT_MIN_VERSION 5.7.0) find_package(Qt5 REQUIRED COMPONENTS Core DBus Widgets Svg Quick Concurrent QuickWidgets Multimedia) find_package(Qt5 OPTIONAL_COMPONENTS WebKitWidgets QUIET) add_definitions(-DQT_NO_CAST_TO_ASCII -DQT_NO_URL_CAST_FROM_STRING) set(DEFAULT_CXX_FLAGS "${DEFAULT_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") # MLT find_package(MLT ${MLT_MIN_VERSION} REQUIRED) set_package_properties(MLT PROPERTIES DESCRIPTION "Multimedia framework" URL "https://mltframework.org" PURPOSE "Required to do video processing") message(STATUS "Found MLT++: ${MLTPP_LIBRARIES}") # Windows include(CheckIncludeFiles) check_include_files(malloc.h HAVE_MALLOC_H) check_include_files(pthread.h HAVE_PTHREAD_H) if(WIN32) find_package(DrMinGW) set(MLT_PREFIX "..") else() set(MLT_PREFIX ${MLT_ROOT_DIR}) endif() # Optional deps status find_package(KF5 5.23.0 OPTIONAL_COMPONENTS XmlGui QUIET) if(KF5XmlGui_FOUND) message(STATUS "Found KF5 >= 5.23.0 enabling icon coloring") else() message(STATUS "KF5 < 5.23.0 Disable icon coloring") set(KF5_ICON_COMPATIBILITY TRUE) endif() if(KF5FileMetaData_FOUND) message(STATUS "Found KF5 FileMetadata to extract file metadata") set(KF5_FILEMETADATA TRUE) else() message(STATUS "KF5 FileMetadata not found, file metadata will not be available") endif() if(KF5Purpose_FOUND) message(STATUS "Found KF5 Purpose, filesharing enabled") set(KF5_PURPOSE TRUE) else() message(STATUS "KF5 Purpose not found, filesharing disabled") endif() if(KF5DocTools_FOUND) add_subdirectory(doc) kdoctools_install(po) endif() # Get current version. set(KDENLIVE_VERSION_STRING "${KDENLIVE_VERSION}") if(NOT RELEASE_BUILD AND EXISTS ${CMAKE_SOURCE_DIR}/.git) # Probably a Git workspace; determine the revision. find_package(Git QUIET) if(GIT_FOUND) exec_program(${GIT_EXECUTABLE} ${CMAKE_SOURCE_DIR} ARGS "log -n 1 --pretty=format:\"%h\"" OUTPUT_VARIABLE KDENLIVE_GIT_REVISION) message(STATUS "Kdenlive Git revision: ${KDENLIVE_GIT_REVISION}") set(KDENLIVE_VERSION_STRING "${KDENLIVE_VERSION} (rev. ${KDENLIVE_GIT_REVISION})") else() message(STATUS "Kdenlive Git revision could not be determined") endif() endif() find_package(RTTR 0.9.6 QUIET) if(NOT RTTR_FOUND) message(STATUS "RTTR not found on system, will download source and build it") include(rttr.CMakeLists.txt) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) set(FFMPEG_SUFFIX "" CACHE STRING "FFmpeg custom suffix") configure_file(config-kdenlive.h.cmake config-kdenlive.h @ONLY) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wno-suggest-override") # Sources add_subdirectory(src) add_subdirectory(renderer) add_subdirectory(thumbnailer) add_subdirectory(data) ki18n_install(po) include(GNUInstallDirs) install(FILES AUTHORS COPYING README.md DESTINATION ${CMAKE_INSTALL_DOCDIR}) -install(FILES kdenlive.categories DESTINATION ${KDE_INSTALL_CONFDIR}) + +if (ECM_VERSION VERSION_GREATER_EQUAL "5.59.0") + install(FILES kdenlive.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) +else() + install(FILES kdenlive.categories DESTINATION ${KDE_INSTALL_CONFDIR}) +endif() ############################ # Tests ############################ if(BUILD_TESTING) message(STATUS "Building tests") add_subdirectory(tests) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fexceptions") include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/src ${MLT_INCLUDE_DIR} ${MLTPP_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib/external ${CMAKE_CURRENT_SOURCE_DIR}/lib src) add_executable(runTests ${Tests_SRCS}) set_property(TARGET runTests PROPERTY CXX_STANDARD 14) target_link_libraries(runTests kdenliveLib) add_test(runTests runTests -d yes) endif() if(BUILD_FUZZING) message(STATUS "Building fuzzing") set(CMAKE_CXX_COMPILER /usr/bin/clang++) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDENLIVE_CXX_FLAGS} -fsanitize=fuzzer-no-link,address") add_subdirectory(fuzzer) endif() diff --git a/src/timeline2/view/qml/TrackHead.qml b/src/timeline2/view/qml/TrackHead.qml index d70ffd923..8924cd179 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") } } 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") } 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 editing 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")) } 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") 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") 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 } } }