aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Baptiste Mardelle <[email protected]>2015-05-17 15:22:55 +0200
committerJean-Baptiste Mardelle <[email protected]>2015-05-17 15:22:55 +0200
commit969ff395193648c581e7e3c25c9612aee5c7de2c (patch)
tree02702bc0dd82f217a86411ef32f94904f385a6ca
parentc54d5e34afa899a2072c272d19b7ce11f7f9718a (diff)
parent1e11e1ab685e12101f0a1cbc7e95858a17d25f1a (diff)
Merge frameworks branch to master
-rw-r--r--CMakeLists.txt4
-rw-r--r--README4
-rw-r--r--cmake/modules/FindGLEW.cmake105
-rw-r--r--data/CMakeLists.txt3
-rw-r--r--data/blacklisted_effects.txt16
-rw-r--r--data/effects/CMakeLists.txt26
-rw-r--r--data/effects/audiowaveform.xml20
-rw-r--r--data/effects/frei0r_colgate.xml2
-rw-r--r--data/effects/lift_gamma_gain.xml32
-rw-r--r--data/effects/loudness.xml2
-rw-r--r--data/effects/movit_blur.xml3
-rw-r--r--data/effects/movit_deconvolution_sharpen.xml3
-rw-r--r--data/effects/movit_diffusion.xml3
-rw-r--r--data/effects/movit_glow.xml3
-rw-r--r--data/effects/movit_lift_gamma_gain.xml59
-rw-r--r--data/effects/movit_lift_gamma_gain2.xml1
-rw-r--r--data/effects/movit_mirror.xml3
-rw-r--r--data/effects/movit_opacity.xml3
-rw-r--r--data/effects/movit_rect.xml11
-rw-r--r--data/effects/movit_saturation.xml3
-rw-r--r--data/effects/movit_unsharp_mask.xml3
-rw-r--r--data/effects/movit_vignette.xml3
-rw-r--r--data/effects/movit_white_balance.xml23
-rw-r--r--data/effects/pan_zoom.xml2
-rw-r--r--data/icons/CMakeLists.txt8
-rw-r--r--data/icons/sc-actions-kdenlive-hide-audio-effects.svg65
-rw-r--r--data/icons/sc-actions-kdenlive-hide-video-effects.svg69
-rw-r--r--data/icons/sc-actions-kdenlive-show-all-effects.svg104
-rw-r--r--data/icons/sc-actions-kdenlive-show-audio-effects.svg178
-rw-r--r--data/icons/sc-actions-kdenlive-show-gpu-effects.svg295
-rw-r--r--data/icons/sc-actions-kdenlive-show-video-effects.svg65
-rw-r--r--data/kdenlive.appdata.xml9
-rw-r--r--data/kdenlive.notifyrc124
-rw-r--r--data/kdenliveeffectscategory.rc5
-rw-r--r--data/kdenlivemonitor.qml55
-rw-r--r--data/kdenlivemonitoreffectscene.qml300
-rw-r--r--data/kdenlivemonitorsplit.qml63
-rw-r--r--data/kdenlivetranscodingrc1
-rw-r--r--data/kdenliveui.rc12
-rw-r--r--data/org.kde.kdenlive.desktop37
-rw-r--r--plugins/sampleplugin/CMakeLists.txt9
-rw-r--r--renderer/CMakeLists.txt2
-rw-r--r--src/CMakeLists.txt7
-rw-r--r--src/bin/CMakeLists.txt13
-rw-r--r--src/bin/abstractprojectitem.cpp259
-rw-r--r--src/bin/abstractprojectitem.h200
-rw-r--r--src/bin/bin.cpp2076
-rw-r--r--src/bin/bin.h561
-rw-r--r--src/bin/bincommands.cpp206
-rw-r--r--src/bin/bincommands.h138
-rw-r--r--src/bin/projectclip.cpp784
-rw-r--r--src/bin/projectclip.h223
-rw-r--r--src/bin/projectfolder.cpp147
-rw-r--r--src/bin/projectfolder.h92
-rw-r--r--src/bin/projectfolderup.cpp93
-rw-r--r--src/bin/projectfolderup.h89
-rw-r--r--src/bin/projectitemmodel.cpp311
-rw-r--r--src/bin/projectitemmodel.h96
-rw-r--r--src/bin/projectsortproxymodel.cpp79
-rw-r--r--src/bin/projectsortproxymodel.h68
-rw-r--r--src/bin/projectsubclip.cpp124
-rw-r--r--src/bin/projectsubclip.h87
-rw-r--r--src/capture/mltdevicecapture.cpp37
-rw-r--r--src/capture/mltdevicecapture.h8
-rw-r--r--src/core.cpp22
-rw-r--r--src/core.h11
-rw-r--r--src/definitions.cpp75
-rw-r--r--src/definitions.h86
-rw-r--r--src/dialogs/CMakeLists.txt1
-rw-r--r--src/dialogs/clipcreationdialog.cpp529
-rw-r--r--src/dialogs/clipcreationdialog.h54
-rw-r--r--src/dialogs/encodingprofilesdialog.cpp8
-rw-r--r--src/dialogs/kdenlivesettingsdialog.cpp45
-rw-r--r--src/dialogs/kdenlivesettingsdialog.h4
-rw-r--r--src/dialogs/profilesdialog.cpp20
-rw-r--r--src/dialogs/profilesdialog.h6
-rw-r--r--src/dialogs/renderwidget.cpp27
-rw-r--r--src/dialogs/renderwidget.h2
-rw-r--r--src/dialogs/wizard.cpp4
-rw-r--r--src/doc/docclipbase.cpp17
-rw-r--r--src/doc/docclipbase.h7
-rw-r--r--src/doc/doccommands.cpp6
-rw-r--r--src/doc/documentvalidator.cpp330
-rw-r--r--src/doc/documentvalidator.h3
-rw-r--r--src/doc/kdenlivedoc.cpp388
-rw-r--r--src/doc/kdenlivedoc.h70
-rw-r--r--src/doc/kthumb.cpp13
-rw-r--r--src/dvdwizard/dvdwizardchapters.cpp3
-rw-r--r--src/effectslist/effectslist.cpp27
-rw-r--r--src/effectslist/effectslist.h11
-rw-r--r--src/effectslist/effectslistview.cpp136
-rw-r--r--src/effectslist/effectslistview.h51
-rw-r--r--src/effectslist/effectslistwidget.cpp44
-rw-r--r--src/effectslist/effectslistwidget.h12
-rw-r--r--src/effectslist/initeffects.cpp98
-rw-r--r--src/effectslist/initeffects.h21
-rw-r--r--src/effectstack/CMakeLists.txt2
-rw-r--r--src/effectstack/collapsibleeffect.cpp15
-rw-r--r--src/effectstack/collapsibleeffect.h2
-rw-r--r--src/effectstack/effectstackedit.cpp4
-rw-r--r--src/effectstack/effectstackedit.h2
-rw-r--r--src/effectstack/effectstackview2.cpp271
-rw-r--r--src/effectstack/effectstackview2.h28
-rw-r--r--src/effectstack/geometryval.cpp27
-rw-r--r--src/effectstack/geometryval.h8
-rw-r--r--src/effectstack/keyframeedit.cpp3
-rw-r--r--src/effectstack/keyframehelper.cpp5
-rw-r--r--src/effectstack/parametercontainer.cpp43
-rw-r--r--src/effectstack/parametercontainer.h14
-rw-r--r--src/effectstack/widgets/choosecolorwidget.cpp9
-rw-r--r--src/effectstack/widgets/choosecolorwidget.h5
-rw-r--r--src/effectstack/widgets/colorwheel.cpp283
-rw-r--r--src/effectstack/widgets/colorwheel.h75
-rw-r--r--src/effectstack/widgets/cornerswidget.cpp7
-rw-r--r--src/effectstack/widgets/geometrywidget.cpp235
-rw-r--r--src/effectstack/widgets/geometrywidget.h7
-rw-r--r--src/effectstack/widgets/lumaliftgain.cpp110
-rw-r--r--src/effectstack/widgets/lumaliftgain.h58
-rw-r--r--src/kdenlivesettings.kcfg56
-rw-r--r--src/main.cpp26
-rw-r--r--src/mainwindow.cpp935
-rw-r--r--src/mainwindow.h35
-rw-r--r--src/mltcontroller/CMakeLists.txt7
-rw-r--r--src/mltcontroller/bincontroller.cpp441
-rw-r--r--src/mltcontroller/bincontroller.h179
-rw-r--r--src/mltcontroller/clipcontroller.cpp663
-rw-r--r--src/mltcontroller/clipcontroller.h195
-rw-r--r--src/mltcontroller/clippropertiescontroller.cpp238
-rw-r--r--src/mltcontroller/clippropertiescontroller.h79
-rw-r--r--src/mltcontroller/effectscontroller.cpp403
-rw-r--r--src/mltcontroller/effectscontroller.h116
-rw-r--r--src/monitor/CMakeLists.txt5
-rw-r--r--src/monitor/abstractmonitor.cpp109
-rw-r--r--src/monitor/abstractmonitor.h30
-rw-r--r--src/monitor/glwidget.cpp1137
-rw-r--r--src/monitor/glwidget.h219
-rw-r--r--src/monitor/monitor.cpp851
-rw-r--r--src/monitor/monitor.h123
-rw-r--r--src/monitor/monitormanager.cpp46
-rw-r--r--src/monitor/monitormanager.h8
-rw-r--r--src/monitor/monitorscene.cpp2
-rw-r--r--src/monitor/recmonitor.cpp67
-rw-r--r--src/monitor/sharedframe.cpp204
-rw-r--r--src/monitor/sharedframe.h76
-rw-r--r--src/monitor/smallruler.cpp55
-rw-r--r--src/monitor/smallruler.h6
-rw-r--r--src/monitor/twostateaction.cpp34
-rw-r--r--src/monitor/twostateaction.h40
-rw-r--r--src/monitor/videoglwidget.cpp23
-rw-r--r--src/monitor/videoglwidget.h5
-rw-r--r--src/onmonitoritems/rotoscoping/rotowidget.cpp7
-rw-r--r--src/project/CMakeLists.txt19
-rw-r--r--src/project/clipmanager.cpp425
-rw-r--r--src/project/clipmanager.h49
-rw-r--r--src/project/clipproperties.cpp12
-rw-r--r--src/project/clippropertiesmanager.cpp46
-rw-r--r--src/project/clippropertiesmanager.h3
-rw-r--r--src/project/clipstabilize.cpp6
-rw-r--r--src/project/cliptranscode.cpp4
-rw-r--r--src/project/dialogs/archivewidget.cpp33
-rw-r--r--src/project/dialogs/archivewidget.h3
-rw-r--r--src/project/dialogs/noteswidget.cpp4
-rw-r--r--src/project/dialogs/projectsettings.cpp64
-rw-r--r--src/project/dialogs/projectsettings.h5
-rw-r--r--src/project/dialogs/slideshowclip.cpp16
-rw-r--r--src/project/dialogs/slideshowclip.h2
-rw-r--r--src/project/invaliddialog.cpp4
-rw-r--r--src/project/jobs/CMakeLists.txt9
-rw-r--r--src/project/jobs/abstractclipjob.h12
-rw-r--r--src/project/jobs/cutclipjob.cpp173
-rw-r--r--src/project/jobs/cutclipjob.h15
-rw-r--r--src/project/jobs/filterjob.cpp237
-rw-r--r--src/project/jobs/filterjob.h40
-rw-r--r--src/project/jobs/jobmanager.cpp323
-rw-r--r--src/project/jobs/jobmanager.h133
-rw-r--r--src/project/jobs/meltjob.cpp56
-rw-r--r--src/project/jobs/meltjob.h27
-rw-r--r--src/project/jobs/proxyclipjob.cpp53
-rw-r--r--src/project/jobs/proxyclipjob.h5
-rw-r--r--src/project/notesplugin.cpp2
-rw-r--r--src/project/projectcommands.cpp65
-rw-r--r--src/project/projectcommands.h28
-rw-r--r--src/project/projectitem.cpp25
-rw-r--r--src/project/projectitem.h4
-rw-r--r--src/project/projectlist.cpp898
-rw-r--r--src/project/projectlist.h38
-rw-r--r--src/project/projectlistview.cpp7
-rw-r--r--src/project/projectmanager.cpp71
-rw-r--r--src/project/projectmanager.h30
-rw-r--r--src/project/transitionsettings.cpp11
-rw-r--r--src/renderer.cpp1060
-rw-r--r--src/renderer.h115
-rw-r--r--src/scopes/abstractscopewidget.cpp20
-rw-r--r--src/scopes/abstractscopewidget.h2
-rw-r--r--src/scopes/audioscopes/audiosignal.cpp2
-rw-r--r--src/scopes/audioscopes/audiospectrum.cpp4
-rw-r--r--src/scopes/audioscopes/spectrogram.cpp4
-rw-r--r--src/scopes/colorscopes/histogram.cpp7
-rw-r--r--src/scopes/colorscopes/histogramgenerator.cpp2
-rw-r--r--src/scopes/colorscopes/rgbparade.cpp6
-rw-r--r--src/scopes/colorscopes/vectorscope.cpp14
-rw-r--r--src/scopes/colorscopes/waveform.cpp6
-rw-r--r--src/scopes/scopemanager.cpp7
-rw-r--r--src/stopmotion/stopmotion.cpp13
-rw-r--r--src/stopmotion/stopmotion.h2
-rw-r--r--src/timecodedisplay.cpp2
-rw-r--r--src/timecodedisplay.h2
-rw-r--r--src/timeline/CMakeLists.txt4
-rw-r--r--src/timeline/clip.cpp97
-rw-r--r--src/timeline/clip.h55
-rw-r--r--src/timeline/clipitem.cpp308
-rw-r--r--src/timeline/clipitem.h39
-rw-r--r--src/timeline/customruler.cpp95
-rw-r--r--src/timeline/customruler.h12
-rw-r--r--src/timeline/customtrackview.cpp827
-rw-r--r--src/timeline/customtrackview.h49
-rw-r--r--src/timeline/headertrack.cpp110
-rw-r--r--src/timeline/headertrack.h20
-rw-r--r--src/timeline/markerdialog.cpp45
-rw-r--r--src/timeline/markerdialog.h12
-rw-r--r--src/timeline/timeline.cpp868
-rw-r--r--src/timeline/timeline.h (renamed from src/timeline/trackview.h)52
-rw-r--r--src/timeline/timelinecommands.cpp32
-rw-r--r--src/timeline/timelinecommands.h16
-rw-r--r--src/timeline/timelinesearch.cpp9
-rw-r--r--src/timeline/track.cpp241
-rw-r--r--src/timeline/track.h128
-rw-r--r--src/timeline/trackview.cpp1210
-rw-r--r--src/titler/titledocument.cpp10
-rw-r--r--src/titler/titlewidget.cpp2
-rw-r--r--src/ui/configenv_ui.ui87
-rw-r--r--src/ui/configsdl_ui.ui151
-rw-r--r--src/ui/effectlist_ui.ui182
-rw-r--r--src/ui/profiledialog_ui.ui32
-rw-r--r--src/ui/timeline_ui.ui5
-rw-r--r--src/ui/trackheader_ui.ui102
-rw-r--r--src/ui/transitionsettings_ui.ui35
-rw-r--r--src/utils/CMakeLists.txt1
-rw-r--r--src/utils/archiveorg.cpp6
-rw-r--r--src/utils/flowlayout.cpp189
-rw-r--r--src/utils/flowlayout.h76
-rw-r--r--src/utils/freesound.cpp4
-rw-r--r--src/utils/openclipart.cpp2
-rw-r--r--thumbnailer/mltpreview.desktop12
244 files changed, 19641 insertions, 6909 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18b8523..e2d7f72 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@ 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.
-set(KDENLIVE_VERSION 15.04.1)
+set(KDENLIVE_VERSION 15.07.0)
cmake_minimum_required(VERSION 2.8.12)
# Minimum versions of main dependencies.
set(MLT_MIN_MAJOR_VERSION 0)
@@ -80,7 +80,7 @@ set(MLT_PREFIX ${MLT_ROOT_DIR})
add_subdirectory(data)
add_subdirectory(doc)
#add_subdirectory(plugins)
-#macro_optional_add_subdirectory(po)
+#ecm_optional_add_subdirectory(po)
add_subdirectory(renderer)
add_subdirectory(src)
add_subdirectory(thumbnailer)
diff --git a/README b/README
index 76d4c1f..bd255a2 100644
--- a/README
+++ b/README
@@ -5,7 +5,7 @@ Kdenlive is a video editing application,
based on MLT Framework and KDE Frameworks 5
Please check the project page for more information:
-http://kdenlive.org
+https://kdenlive.org
Building from source
====================
@@ -36,7 +36,7 @@ We welcome all feedback and offers for help!
* Talk about us!
* Report bugs you encounter (if not already done) on:
- http://bugs.kde.org
+ https://bugs.kde.org
* Help other users on forum and bug tracker:
http://forum.kde.org/viewforum.php?f=262
* Help to fill the manual at:
diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake
deleted file mode 100644
index 54da20f..0000000
--- a/cmake/modules/FindGLEW.cmake
+++ /dev/null
@@ -1,105 +0,0 @@
-# Copyright (c) 2009 Boudewijn Rempt <[email protected]>
-#
-# Redistribution and use is allowed according to the terms of the BSD license.
-# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
-#
-# - try to find glew library and include files
-# GLEW_INCLUDE_DIR, where to find GL/glew.h, etc.
-# GLEW_LIBRARIES, the libraries to link against
-# GLEW_FOUND, If false, do not try to use GLEW.
-# Also defined, but not for general use are:
-# GLEW_GLEW_LIBRARY = the full path to the glew library.
-
-IF (WIN32)
-
- IF(CYGWIN)
-
- FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h)
-
- FIND_LIBRARY( GLEW_GLEW_LIBRARY glew32
- ${OPENGL_LIBRARY_DIR}
- /usr/lib/w32api
- /usr/X11R6/lib
- )
-
-
- ELSE(CYGWIN)
-
- FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h
- $ENV{GLEW_ROOT_PATH}/include
- )
-
- FIND_LIBRARY( GLEW_GLEW_LIBRARY
- NAMES glew glew32
- PATHS
- $ENV{GLEW_ROOT_PATH}/lib
- ${OPENGL_LIBRARY_DIR}
- )
-
- ENDIF(CYGWIN)
-
-ELSE (WIN32)
-
- IF (APPLE)
-# These values for Apple could probably do with improvement.
- FIND_PATH( GLEW_INCLUDE_DIR glew.h
- /System/Library/Frameworks/GLEW.framework/Versions/A/Headers
- ${OPENGL_LIBRARY_DIR}
- )
- SET(GLEW_GLEW_LIBRARY "-framework GLEW" CACHE STRING "GLEW library for OSX")
- SET(GLEW_cocoa_LIBRARY "-framework Cocoa" CACHE STRING "Cocoa framework for OSX")
- ELSE (APPLE)
-
- FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h
- /usr/include/GL
- /usr/openwin/share/include
- /usr/openwin/include
- /usr/X11R6/include
- /usr/include/X11
- /opt/graphics/OpenGL/include
- /opt/graphics/OpenGL/contrib/libglew
- )
-
- FIND_LIBRARY( GLEW_GLEW_LIBRARY GLEW
- /usr/openwin/lib
- /usr/X11R6/lib
- )
-
- ENDIF (APPLE)
-
-ENDIF (WIN32)
-
-SET( GLEW_FOUND "NO" )
-IF(GLEW_INCLUDE_DIR)
- IF(GLEW_GLEW_LIBRARY)
- # Is -lXi and -lXmu required on all platforms that have it?
- # If not, we need some way to figure out what platform we are on.
- SET( GLEW_LIBRARIES
- ${GLEW_GLEW_LIBRARY}
- ${GLEW_cocoa_LIBRARY}
- )
- SET( GLEW_FOUND "YES" )
-
-#The following deprecated settings are for backwards compatibility with CMake1.4
- SET (GLEW_LIBRARY ${GLEW_LIBRARIES})
- SET (GLEW_INCLUDE_PATH ${GLEW_INCLUDE_DIR})
-
- ENDIF(GLEW_GLEW_LIBRARY)
-ENDIF(GLEW_INCLUDE_DIR)
-
-IF(GLEW_FOUND)
- IF(NOT GLEW_FIND_QUIETLY)
- MESSAGE(STATUS "Found Glew: ${GLEW_LIBRARIES}")
- ENDIF(NOT GLEW_FIND_QUIETLY)
-ELSE(GLEW_FOUND)
- IF(GLEW_FIND_REQUIRED)
- MESSAGE(FATAL_ERROR "Could not find Glew")
- ENDIF(GLEW_FIND_REQUIRED)
-ENDIF(GLEW_FOUND)
-
-MARK_AS_ADVANCED(
- GLEW_INCLUDE_DIR
- GLEW_GLEW_LIBRARY
- GLEW_Xmu_LIBRARY
- GLEW_Xi_LIBRARY
-)
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
index fe77316..593c721 100644
--- a/data/CMakeLists.txt
+++ b/data/CMakeLists.txt
@@ -18,6 +18,9 @@ install(FILES
timeline_vthumbs.png
kdenliveeffectscategory.rc
kdenlivetranscodingrc
+ kdenlivemonitor.qml
+ kdenlivemonitoreffectscene.qml
+ kdenlivemonitorsplit.qml
DESTINATION ${DATA_INSTALL_DIR}/kdenlive)
install(FILES kdenlive.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR})
install(FILES kdenlive_projectprofiles.knsrc kdenlive_renderprofiles.knsrc kdenlive_wipes.knsrc kdenlive_titles.knsrc DESTINATION ${CONFIG_INSTALL_DIR})
diff --git a/data/blacklisted_effects.txt b/data/blacklisted_effects.txt
index 4ff137b..ab1b372 100644
--- a/data/blacklisted_effects.txt
+++ b/data/blacklisted_effects.txt
@@ -74,6 +74,19 @@ frei0r.vectorscope
frei0r.vertigo
frei0r.vignette
+#Movit effects with XML UI
+movit.blur
+movit.sharpen
+movit.diffusion
+movit.glow
+movit.lift_gamma_gain
+movit.mirror
+movit.opacity
+movit.rect
+movit.saturation
+movit.unsharp_mask
+movit.vignette
+movit.white_balance
#MLT effects with XML UI
channelcopy
@@ -87,6 +100,9 @@ rotoscoping
wave
vignette
volume
+lift_gamma_gain
+audiowaveform
+audiowave
#Effects not usable with a simple GUI
sox
diff --git a/data/effects/CMakeLists.txt b/data/effects/CMakeLists.txt
index 3ce75f8..9e2a3f6 100644
--- a/data/effects/CMakeLists.txt
+++ b/data/effects/CMakeLists.txt
@@ -1,7 +1,10 @@
INSTALL (FILES
audiowave.xml
+audiowaveform.xml
automask.xml
+audiobalance.xml
+audiopan.xml
boxblur.xml
brightness.xml
channelcopy.xml
@@ -103,16 +106,29 @@ frei0r_vertigo.xml
frei0r_vignette.xml
frei0r_facebl0r.xml
frei0r_facedetect.xml
-gain.xml
fade_from_black.xml
fade_to_black.xml
+gain.xml
+lift_gamma_gain.xml
+movit_blur.xml
+movit_deconvolution_sharpen.xml
+movit_diffusion.xml
+movit_glow.xml
+movit_lift_gamma_gain.xml
+movit_mirror.xml
+movit_opacity.xml
+movit_rect.xml
+movit_saturation.xml
+movit_unsharp_mask.xml
+movit_vignette.xml
+movit_white_balance.xml
+rotoscoping.xml
speed.xml
+swapchannels.xml
tcolor.xml
vignette.xml
-swapchannels.xml
-audiobalance.xml
-audiopan.xml
-rotoscoping.xml
+
+
DESTINATION ${DATA_INSTALL_DIR}/kdenlive/effects)
diff --git a/data/effects/audiowaveform.xml b/data/effects/audiowaveform.xml
new file mode 100644
index 0000000..2337fb2
--- /dev/null
+++ b/data/effects/audiowaveform.xml
@@ -0,0 +1,20 @@
+<!DOCTYPE kpartgui>
+<effect tag="audiowaveform" id="audiowaveform" type="audio">
+ <name>Audio Waveform Filter</name>
+ <author>Brian Matherly</author>
+ <parameter type="color" name="bgcolor" default="0x00000000" alpha="1">
+ <name>Background Color</name>
+ </parameter>
+ <parameter type="color" name="color.1" default="0xffffffff" alpha="1">
+ <name>Foreground Color</name>
+ </parameter>
+ <parameter type="constant" name="thickness" max="20" min="0" default="0">
+ <name>Line Thickness</name>
+ </parameter>
+ <parameter type="geometry" name="rect" default="0%,0%:100%x100%" fixed="1" opacity="false">
+ <name>Rectangle</name>
+ </parameter>
+ <parameter type="bool" name="fill" default="0">
+ <name>Fill</name>
+ </parameter>
+</effect>
diff --git a/data/effects/frei0r_colgate.xml b/data/effects/frei0r_colgate.xml
index 4eafc82..df9f13a 100644
--- a/data/effects/frei0r_colgate.xml
+++ b/data/effects/frei0r_colgate.xml
@@ -1,5 +1,5 @@
<!DOCTYPE kpartgui>
-<effect tag="frei0r.colgate" id="frei0r.colgate">
+<effect tag="frei0r.colgate" id="frei0r.colgate" context="nomovit">
<name>White Balance (LMS space)</name>
<description>Do simple color correction, in a physically meaningful
way</description>
diff --git a/data/effects/lift_gamma_gain.xml b/data/effects/lift_gamma_gain.xml
new file mode 100644
index 0000000..acab182
--- /dev/null
+++ b/data/effects/lift_gamma_gain.xml
@@ -0,0 +1,32 @@
+<!DOCTYPE kpartgui>
+<effect tag="lift_gamma_gain" id="lift_gamma_gain">
+ <name>Lift/gamma/gain</name>
+ <author>Brian Matherly</author>
+ <parameter type="double" name="lift_r" default="0" min="0" max="500" factor="100">
+ <name>Lift: Red</name>
+ </parameter>
+ <parameter type="double" name="lift_g" default="0" min="0" max="500" factor="100">
+ <name>Lift: Green</name>
+ </parameter>
+ <parameter type="double" name="lift_b" default="0" min="0" max="500" factor="100">
+ <name>Lift: Blue</name>
+ </parameter>
+ <parameter type="double" name="gamma_r" default="100" min="0" max="500" factor="100">
+ <name>Gamma: Red</name>
+ </parameter>
+ <parameter type="double" name="gamma_g" default="100" min="0" max="500" factor="100">
+ <name>Gamma: Green</name>
+ </parameter>
+ <parameter type="double" name="gamma_b" default="100" min="0" max="500" factor="100">
+ <name>Gamma: Blue</name>
+ </parameter>
+ <parameter type="double" name="gain_r" default="100" min="0" max="500" factor="100">
+ <name>Gain: Red</name>
+ </parameter>
+ <parameter type="double" name="gain_g" default="100" min="0" max="500" factor="100">
+ <name>Gain: Green</name>
+ </parameter>
+ <parameter type="double" name="gain_b" default="100" min="0" max="500" factor="100">
+ <name>Gain: Blue</name>
+ </parameter>
+</effect>
diff --git a/data/effects/loudness.xml b/data/effects/loudness.xml
index 8175394..aceeea6 100644
--- a/data/effects/loudness.xml
+++ b/data/effects/loudness.xml
@@ -2,7 +2,7 @@
<effect tag="loudness" id="loudness" type="audio">
<name>Loudness</name>
<description>Correct audio loudness as recommended by EBU R128</description>
- <author>Brian Matherly &lt;[email protected]&gt;</author>
+ <author>Brian Matherly</author>
<parameter type="double" name="program" max="-10" min="-50" default="-23.00" decimals="2">
<name>Target Program Loudness</name>
</parameter>
diff --git a/data/effects/movit_blur.xml b/data/effects/movit_blur.xml
index cd9a19f..cc05097 100644
--- a/data/effects/movit_blur.xml
+++ b/data/effects/movit_blur.xml
@@ -1,7 +1,6 @@
<!DOCTYPE kpartgui>
<effect tag="movit.blur" id="movit.blur">
- <name>Movit: Blur</name>
- <description>Blur</description>
+ <name>Blur (GPU)</name>
<author>Steinar H. Gunderson</author>
<parameter type="simplekeyframe" name="radius" default="150" min="0" max="1000" factor="10">
<name>Radius</name>
diff --git a/data/effects/movit_deconvolution_sharpen.xml b/data/effects/movit_deconvolution_sharpen.xml
index 07232d9..ac156d7 100644
--- a/data/effects/movit_deconvolution_sharpen.xml
+++ b/data/effects/movit_deconvolution_sharpen.xml
@@ -1,7 +1,6 @@
<!DOCTYPE kpartgui>
<effect tag="movit.sharpen" id="movit.sharpen">
- <name>Movit: Deconvolution sharpen</name>
- <description>Sharpens in a more intelligent way</description>
+ <name>Deconvolution sharpen (GPU)</name>
<author>Steinar H. Gunderson</author>
<parameter type="simplekeyframe" name="matrix_size" default="5" min="1" max="10" factor="1">
<name>Matrix size</name>
diff --git a/data/effects/movit_diffusion.xml b/data/effects/movit_diffusion.xml
index 43a852a..9090db4 100644
--- a/data/effects/movit_diffusion.xml
+++ b/data/effects/movit_diffusion.xml
@@ -1,7 +1,6 @@
<!DOCTYPE kpartgui>
<effect tag="movit.diffusion" id="movit.diffusion">
- <name>Movit: Diffusion</name>
- <description>Diffusion</description>
+ <name>Diffusion (GPU)</name>
<author>Steinar H. Gunderson</author>
<parameter type="simplekeyframe" name="radius" default="150" min="0" max="1000" factor="10">
<name>Radius</name>
diff --git a/data/effects/movit_glow.xml b/data/effects/movit_glow.xml
index e5608b5..b9302ca 100644
--- a/data/effects/movit_glow.xml
+++ b/data/effects/movit_glow.xml
@@ -1,7 +1,6 @@
<!DOCTYPE kpartgui>
<effect tag="movit.glow" id="movit.glow">
- <name>Movit: Glow</name>
- <description>Glow</description>
+ <name>Glow (GPU)</name>
<author>Steinar H. Gunderson</author>
<parameter type="simplekeyframe" name="radius" default="200" min="0" max="1000" factor="10">
<name>Radius</name>
diff --git a/data/effects/movit_lift_gamma_gain.xml b/data/effects/movit_lift_gamma_gain.xml
index 2c8a552..ad2624b 100644
--- a/data/effects/movit_lift_gamma_gain.xml
+++ b/data/effects/movit_lift_gamma_gain.xml
@@ -1,33 +1,32 @@
<!DOCTYPE kpartgui>
<effect tag="movit.lift_gamma_gain" id="movit.lift_gamma_gain">
- <name>Movit: Lift/gamma/gain</name>
- <description>Do simple color grading</description>
- <author>Steinar H. Gunderson</author>
- <parameter type="simplekeyframe" name="lift_r" default="0" min="0" max="500" factor="100">
- <name>Lift: Red</name>
- </parameter>
- <parameter type="simplekeyframe" name="lift_g" default="0" min="0" max="500" factor="100">
- <name>Lift: Green</name>
- </parameter>
- <parameter type="simplekeyframe" name="lift_b" default="0" min="0" max="500" factor="100">
- <name>Lift: Blue</name>
- </parameter>
- <parameter type="simplekeyframe" name="gamma_r" default="100" min="0" max="500" factor="100">
- <name>Gamma: Red</name>
- </parameter>
- <parameter type="simplekeyframe" name="gamma_g" default="100" min="0" max="500" factor="100">
- <name>Gamma: Green</name>
- </parameter>
- <parameter type="simplekeyframe" name="gamma_b" default="100" min="0" max="500" factor="100">
- <name>Gamma: Blue</name>
- </parameter>
- <parameter type="simplekeyframe" name="gain_r" default="100" min="0" max="500" factor="100">
- <name>Gain: Red</name>
- </parameter>
- <parameter type="simplekeyframe" name="gain_g" default="100" min="0" max="500" factor="100">
- <name>Gain: Green</name>
- </parameter>
- <parameter type="simplekeyframe" name="gain_b" default="100" min="0" max="500" factor="100">
- <name>Gain: Blue</name>
- </parameter>
+ <name>Lift/gamma/gain (GPU)</name>
+ <author>Steinar H. Gunderson</author>
+ <parameter type="double" name="lift_r" default="0" min="0" max="500" factor="100">
+ <name>Lift: Red</name>
+ </parameter>
+ <parameter type="double" name="lift_g" default="0" min="0" max="500" factor="100">
+ <name>Lift: Green</name>
+ </parameter>
+ <parameter type="double" name="lift_b" default="0" min="0" max="500" factor="100">
+ <name>Lift: Blue</name>
+ </parameter>
+ <parameter type="double" name="gamma_r" default="100" min="0" max="500" factor="100">
+ <name>Gamma: Red</name>
+ </parameter>
+ <parameter type="double" name="gamma_g" default="100" min="0" max="500" factor="100">
+ <name>Gamma: Green</name>
+ </parameter>
+ <parameter type="double" name="gamma_b" default="100" min="0" max="500" factor="100">
+ <name>Gamma: Blue</name>
+ </parameter>
+ <parameter type="double" name="gain_r" default="100" min="0" max="500" factor="100">
+ <name>Gain: Red</name>
+ </parameter>
+ <parameter type="double" name="gain_g" default="100" min="0" max="500" factor="100">
+ <name>Gain: Green</name>
+ </parameter>
+ <parameter type="double" name="gain_b" default="100" min="0" max="500" factor="100">
+ <name>Gain: Blue</name>
+ </parameter>
</effect>
diff --git a/data/effects/movit_lift_gamma_gain2.xml b/data/effects/movit_lift_gamma_gain2.xml
index 4f77af3..fd64587 100644
--- a/data/effects/movit_lift_gamma_gain2.xml
+++ b/data/effects/movit_lift_gamma_gain2.xml
@@ -1,7 +1,6 @@
<!DOCTYPE kpartgui>
<effect tag="movit.lift_gamma_gain" id="movit.lift_gamma_gain2">
<name>Movit: Lift/gamma/gain (colors)</name>
- <description>Do simple color grading</description>
<author>Steinar H. Gunderson</author>
<parameter type="double" name="lift_r" default="0" min="0" max="500" factor="100">
<name>Lift: Red</name>
diff --git a/data/effects/movit_mirror.xml b/data/effects/movit_mirror.xml
index e67e76e..2e8a479 100644
--- a/data/effects/movit_mirror.xml
+++ b/data/effects/movit_mirror.xml
@@ -1,6 +1,5 @@
<!DOCTYPE kpartgui>
<effect tag="movit.mirror" id="movit.mirror">
- <name>Movit: Mirror</name>
- <description>Flips the image horizontally</description>
+ <name>Mirror (GPU)</name>
<author>Steinar H. Gunderson</author>
</effect>
diff --git a/data/effects/movit_opacity.xml b/data/effects/movit_opacity.xml
index 78a9ceb..303da73 100644
--- a/data/effects/movit_opacity.xml
+++ b/data/effects/movit_opacity.xml
@@ -1,7 +1,6 @@
<!DOCTYPE kpartgui>
<effect tag="movit.opacity" id="movit.opacity">
- <name>Movit: Opacity</name>
- <description>Change the opacity of the image</description>
+ <name>Opacity (GPU)</name>
<author>Steinar H. Gunderson</author>
<parameter type="simplekeyframe" name="opacity" default="100" min="0" max="200" factor="100">
<name>Opacity</name>
diff --git a/data/effects/movit_rect.xml b/data/effects/movit_rect.xml
new file mode 100644
index 0000000..677e5ec
--- /dev/null
+++ b/data/effects/movit_rect.xml
@@ -0,0 +1,11 @@
+<!DOCTYPE kpartgui>
+<effect tag="movit.rect" id="movit.rect">
+ <name>Pan and Zoom (GPU)</name>
+ <author>Steinar H. Gunderson</author>
+ <parameter type="geometry" name="rect" default="0%,0%:100%x100%" opacity="false">
+ <name>Rectangle</name>
+ </parameter>
+ <parameter type="bool" name="fill" default="1" min="0" max="1">
+ <name>Allow upscale</name>
+ </parameter>
+</effect>
diff --git a/data/effects/movit_saturation.xml b/data/effects/movit_saturation.xml
index 576368f..58959b4 100644
--- a/data/effects/movit_saturation.xml
+++ b/data/effects/movit_saturation.xml
@@ -1,7 +1,6 @@
<!DOCTYPE kpartgui>
<effect tag="movit.saturation" id="movit.saturation">
- <name>Movit: Saturation</name>
- <description>Saturate or desaturate the image</description>
+ <name>Saturation (GPU)</name>
<author>Steinar H. Gunderson</author>
<parameter type="simplekeyframe" name="saturation" default="100" min="0" max="1000" factor="100">
<name>Saturation</name>
diff --git a/data/effects/movit_unsharp_mask.xml b/data/effects/movit_unsharp_mask.xml
index 9accf8d..76477f6 100644
--- a/data/effects/movit_unsharp_mask.xml
+++ b/data/effects/movit_unsharp_mask.xml
@@ -1,7 +1,6 @@
<!DOCTYPE kpartgui>
<effect tag="movit.unsharp_mask" id="movit.unsharp_mask">
- <name>Movit: Unsharp mask</name>
- <description>Sharpens the image by subtracting a blurred copy</description>
+ <name>Unsharp mask (GPU)</name>
<author>Steinar H. Gunderson</author>
<parameter type="simplekeyframe" name="radius" default="150" min="0" max="1000" factor="10">
<name>Radius</name>
diff --git a/data/effects/movit_vignette.xml b/data/effects/movit_vignette.xml
index 8a667e4..32a81d1 100644
--- a/data/effects/movit_vignette.xml
+++ b/data/effects/movit_vignette.xml
@@ -1,7 +1,6 @@
<!DOCTYPE kpartgui>
<effect tag="movit.vignette" id="movit.vignette">
- <name>Movit: Vignette</name>
- <description>Vignette</description>
+ <name>Vignette (GPU)</name>
<author>Steinar H. Gunderson</author>
<parameter type="simplekeyframe" name="radius" default="300" min="0" max="1000" factor="1000">
<name>Radius</name>
diff --git a/data/effects/movit_white_balance.xml b/data/effects/movit_white_balance.xml
index 9ba55f1..d1106cf 100644
--- a/data/effects/movit_white_balance.xml
+++ b/data/effects/movit_white_balance.xml
@@ -1,15 +1,12 @@
<!DOCTYPE kpartgui>
-<group>
- <effect tag="movit.white_balance" id="movit.white_balance">
- <name>Movit: White balance</name>
- <description>Adjust the white balance / color temperature</description>
- <author>Steinar H. Gunderson</author>
- <parameter type="color" name="neutral_color" default="0xffffffff">
- <name>Neutral Color</name>
- </parameter>
- <parameter type="simplekeyframe" name="color_temperature" default="6500" min="1500" max="15000" factor="1">
- <name>Color Temperature</name>
- </parameter>
- </effect>
-</group>
+<effect tag="movit.white_balance" id="movit.white_balance">
+ <name>White Balance (GPU)</name>
+ <author>Steinar H. Gunderson</author>
+ <parameter type="color" name="neutral_color" default="0x7f7f7fff">
+ <name>Neutral Color</name>
+ </parameter>
+ <parameter type="simplekeyframe" name="color_temperature" default="6500" min="1000" max="15000" factor="1">
+ <name>Color Temperature</name>
+ </parameter>
+</effect>
diff --git a/data/effects/pan_zoom.xml b/data/effects/pan_zoom.xml
index 3f5544a..b33aabf 100644
--- a/data/effects/pan_zoom.xml
+++ b/data/effects/pan_zoom.xml
@@ -1,5 +1,5 @@
<!DOCTYPE kpartgui>
-<effect tag="affine" id="pan_zoom" type="video">
+<effect tag="affine" id="pan_zoom" type="video" context="nomovit">
<name>Pan and Zoom</name>
<description>Adjust size and position of clip</description>
<author>Charles Yates</author>
diff --git a/data/icons/CMakeLists.txt b/data/icons/CMakeLists.txt
index d778bdf..e3f47e2 100644
--- a/data/icons/CMakeLists.txt
+++ b/data/icons/CMakeLists.txt
@@ -1,5 +1,5 @@
install(FILES
- visible.png
+ visible.png
novisible.png
DESTINATION ${DATA_INSTALL_DIR}/kdenlive/pics)
@@ -76,6 +76,12 @@ ecm_install_icons(ICONS
sc-actions-kdenlive-select-rects.svgz
sc-actions-kdenlive-select-texts.svgz
sc-actions-kdenlive-select-tool.svgz
+ sc-actions-kdenlive-show-all-effects.svg
+ sc-actions-kdenlive-show-video-effects.svg
+ sc-actions-kdenlive-hide-video-effects.svg
+ sc-actions-kdenlive-show-audio-effects.svg
+ sc-actions-kdenlive-hide-audio-effects.svg
+ sc-actions-kdenlive-show-gpu-effects.svg
sc-actions-kdenlive-spacer-tool.svgz
sc-actions-kdenlive-unselect-all.svgz
sc-actions-kdenlive-zindex-bottom.svgz
diff --git a/data/icons/sc-actions-kdenlive-hide-audio-effects.svg b/data/icons/sc-actions-kdenlive-hide-audio-effects.svg
new file mode 100644
index 0000000..6292a06
--- /dev/null
+++ b/data/icons/sc-actions-kdenlive-hide-audio-effects.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ id="svg2"
+ height="16"
+ width="16">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient7864">
+ <stop
+ id="stop7866"
+ offset="0"
+ style="stop-color:#fdfeff;stop-opacity:1;" />
+ <stop
+ id="stop7868"
+ offset="1"
+ style="stop-color:#fdfeff;stop-opacity:0.22368421;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7847">
+ <stop
+ id="stop7849"
+ offset="0"
+ style="stop-color:#8ace62;stop-opacity:1;" />
+ <stop
+ id="stop7851"
+ offset="1"
+ style="stop-color:#3e8414;stop-opacity:1;" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ transform="translate(-286.28571,-144.36218)"
+ id="layer1">
+ <path
+ id="rect4172-9"
+ d="m 298.87237,144.78413 -5.5789,4.01583 0,0.0147 -3.71355,0 0,7.03876 3.71355,0 0,0.0147 5.5789,4.01583 0,-15.09991 z"
+ style="opacity:1;fill:#ffffff;fill-opacity:0.50196081;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ id="rect4172"
+ d="m 298.42453,145.6368 -4.96965,3.57727 0,0.0131 -3.308,0 0,6.27008 3.308,0 0,0.0131 4.96965,3.57727 0,-13.45089 z"
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ id="path4180"
+ d="m 286.96081,157.6142 14.71831,-10.72301"
+ style="fill:none;fill-rule:evenodd;stroke:#b80000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/data/icons/sc-actions-kdenlive-hide-video-effects.svg b/data/icons/sc-actions-kdenlive-hide-video-effects.svg
new file mode 100644
index 0000000..8bd363b
--- /dev/null
+++ b/data/icons/sc-actions-kdenlive-hide-video-effects.svg
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ id="svg2"
+ height="16"
+ width="16">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient7864">
+ <stop
+ id="stop7866"
+ offset="0"
+ style="stop-color:#fdfeff;stop-opacity:1;" />
+ <stop
+ id="stop7868"
+ offset="1"
+ style="stop-color:#fdfeff;stop-opacity:0.22368421;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7847">
+ <stop
+ id="stop7849"
+ offset="0"
+ style="stop-color:#8ace62;stop-opacity:1;" />
+ <stop
+ id="stop7851"
+ offset="1"
+ style="stop-color:#3e8414;stop-opacity:1;" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ transform="translate(-286.28571,-144.36218)"
+ id="layer1">
+ <rect
+ y="146.91576"
+ x="286.78571"
+ height="10.870536"
+ width="14.852678"
+ id="rect3378"
+ style="fill:#ffffff;fill-opacity:0.50196081;stroke:none;stroke-opacity:1" />
+ <path
+ id="rect4172"
+ transform="translate(286.28571,144.36218)"
+ d="M 1.0019531 3.0527344 L 1.0019531 12.978516 L 14.927734 12.978516 L 14.927734 3.0527344 L 1.0019531 3.0527344 z M 2.0214844 4.0234375 L 3.7226562 4.0234375 L 3.7226562 5.7226562 L 2.0214844 5.7226562 L 2.0214844 4.0234375 z M 5.4941406 4.0234375 L 7.1933594 4.0234375 L 7.1933594 5.7226562 L 5.4941406 5.7226562 L 5.4941406 4.0234375 z M 8.9648438 4.0234375 L 10.666016 4.0234375 L 10.666016 5.7226562 L 8.9648438 5.7226562 L 8.9648438 4.0234375 z M 12.4375 4.0234375 L 14.136719 4.0234375 L 14.136719 5.7226562 L 12.4375 5.7226562 L 12.4375 4.0234375 z M 2.0351562 10.25 L 3.7363281 10.25 L 3.7363281 11.951172 L 2.0351562 11.951172 L 2.0351562 10.25 z M 5.5078125 10.25 L 7.2070312 10.25 L 7.2070312 11.951172 L 5.5078125 11.951172 L 5.5078125 10.25 z M 8.9785156 10.25 L 10.679688 10.25 L 10.679688 11.951172 L 8.9785156 11.951172 L 8.9785156 10.25 z M 12.451172 10.25 L 14.150391 10.25 L 14.150391 11.951172 L 12.451172 11.951172 L 12.451172 10.25 z "
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ id="path4180"
+ d="M 286.90724,157.66331 301.62555,146.9403"
+ style="fill:none;fill-rule:evenodd;stroke:#b80000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/data/icons/sc-actions-kdenlive-show-all-effects.svg b/data/icons/sc-actions-kdenlive-show-all-effects.svg
new file mode 100644
index 0000000..87570ad
--- /dev/null
+++ b/data/icons/sc-actions-kdenlive-show-all-effects.svg
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ id="svg2"
+ height="16"
+ width="16">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient7864">
+ <stop
+ id="stop7866"
+ offset="0"
+ style="stop-color:#fdfeff;stop-opacity:1;" />
+ <stop
+ id="stop7868"
+ offset="1"
+ style="stop-color:#fdfeff;stop-opacity:0.22368421;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7847">
+ <stop
+ id="stop7849"
+ offset="0"
+ style="stop-color:#8ace62;stop-opacity:1;" />
+ <stop
+ id="stop7851"
+ offset="1"
+ style="stop-color:#3e8414;stop-opacity:1;" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ transform="translate(-286.28571,-144.36218)"
+ id="layer1">
+ <g
+ transform="translate(0.08928572,0.71428572)"
+ id="g4906">
+ <circle
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4170"
+ cx="289.125"
+ cy="147.70148"
+ r="1" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4172"
+ width="8.125"
+ height="2"
+ x="291.92856"
+ y="146.70148" />
+ </g>
+ <g
+ transform="translate(0.08928572,4.6830353)"
+ id="g4906-6">
+ <circle
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4170-9"
+ cx="289.125"
+ cy="147.70148"
+ r="1" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4172-0"
+ width="8.125"
+ height="2"
+ x="291.92856"
+ y="146.70148" />
+ </g>
+ <g
+ transform="translate(0.08928572,8.6517852)"
+ id="g4906-0">
+ <circle
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4170-6"
+ cx="289.125"
+ cy="147.70148"
+ r="1" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4172-08"
+ width="8.125"
+ height="2"
+ x="291.92856"
+ y="146.70148" />
+ </g>
+ </g>
+</svg>
diff --git a/data/icons/sc-actions-kdenlive-show-audio-effects.svg b/data/icons/sc-actions-kdenlive-show-audio-effects.svg
new file mode 100644
index 0000000..a2ad092
--- /dev/null
+++ b/data/icons/sc-actions-kdenlive-show-audio-effects.svg
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="sc-actions-kdenlive-show-audio-effects.svg"
+ inkscape:export-filename="/data/cworkspace/kdenlive-svk/icons/ox16-action-kdenlive-select-all.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient7864">
+ <stop
+ style="stop-color:#fdfeff;stop-opacity:1;"
+ offset="0"
+ id="stop7866" />
+ <stop
+ style="stop-color:#fdfeff;stop-opacity:0.22368421;"
+ offset="1"
+ id="stop7868" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7847">
+ <stop
+ style="stop-color:#8ace62;stop-opacity:1;"
+ offset="0"
+ id="stop7849" />
+ <stop
+ style="stop-color:#3e8414;stop-opacity:1;"
+ offset="1"
+ id="stop7851" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective7054"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5138"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5160"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5207"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5228"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5249"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5270"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5291"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5318"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="44.8"
+ inkscape:cx="0.86163534"
+ inkscape:cy="8.3573529"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="3200"
+ inkscape:window-height="1668"
+ inkscape:window-x="-8"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:snap-grids="true"
+ inkscape:snap-to-guides="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid7060"
+ empspacing="2"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ color="#000000"
+ opacity="0.05098039"
+ empcolor="#000000"
+ empopacity="0.30588235"
+ spacingx="0.5px"
+ spacingy="0.5px" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-286.28571,-144.36218)">
+ <path
+ style="opacity:1;fill:#ffffff;fill-opacity:0.50196081;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 298.87237,144.78413 -5.5789,4.01583 0,0.0147 -3.71355,0 0,7.03876 3.71355,0 0,0.0147 5.5789,4.01583 0,-15.09991 z"
+ id="rect4172-9"
+ inkscape:connector-curvature="0" />
+ <path
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 298.42453,145.6368 -4.96965,3.57727 0,0.0131 -3.308,0 0,6.27008 3.308,0 0,0.0131 4.96965,3.57727 0,-13.45089 z"
+ id="rect4172"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/data/icons/sc-actions-kdenlive-show-gpu-effects.svg b/data/icons/sc-actions-kdenlive-show-gpu-effects.svg
new file mode 100644
index 0000000..5d56154
--- /dev/null
+++ b/data/icons/sc-actions-kdenlive-show-gpu-effects.svg
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="sc-actions-kdenlive-show-gpu-effects.svg"
+ inkscape:export-filename="/data/cworkspace/kdenlive-svk/icons/ox16-action-kdenlive-select-all.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient7864">
+ <stop
+ style="stop-color:#fdfeff;stop-opacity:1;"
+ offset="0"
+ id="stop7866" />
+ <stop
+ style="stop-color:#fdfeff;stop-opacity:0.22368421;"
+ offset="1"
+ id="stop7868" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7847">
+ <stop
+ style="stop-color:#8ace62;stop-opacity:1;"
+ offset="0"
+ id="stop7849" />
+ <stop
+ style="stop-color:#3e8414;stop-opacity:1;"
+ offset="1"
+ id="stop7851" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective7054"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5138"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5160"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5207"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5228"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5249"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5270"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5291"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective5318"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="32"
+ inkscape:cx="4.3147613"
+ inkscape:cy="6.7856516"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1920"
+ inkscape:window-height="1010"
+ inkscape:window-x="1916"
+ inkscape:window-y="40"
+ inkscape:window-maximized="1"
+ inkscape:snap-grids="true"
+ inkscape:snap-to-guides="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid7060"
+ empspacing="2"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ color="#000000"
+ opacity="0.05098039"
+ empcolor="#000000"
+ empopacity="0.30588235"
+ spacingx="0.5px"
+ spacingy="0.5px" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-286.28571,-144.36218)">
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5208"
+ width="11"
+ height="11"
+ x="288.78571"
+ y="146.86218" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5329"
+ width="1.28125"
+ height="2"
+ x="289.81696"
+ y="145.39343" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5329-5"
+ width="1.28125"
+ height="2"
+ x="292.23361"
+ y="145.39343" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5329-55"
+ width="1.28125"
+ height="2"
+ x="294.6503"
+ y="145.39343" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5329-9"
+ width="1.28125"
+ height="2"
+ x="297.06696"
+ y="145.39343" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5329-6"
+ width="1.28125"
+ height="2"
+ x="289.84821"
+ y="157.39343" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5329-5-5"
+ width="1.28125"
+ height="2"
+ x="292.26486"
+ y="157.39343" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5329-55-5"
+ width="1.28125"
+ height="2"
+ x="294.68155"
+ y="157.39343" />
+ <rect
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5329-9-6"
+ width="1.28125"
+ height="2"
+ x="297.09821"
+ y="157.39343" />
+ <g
+ id="g5406"
+ transform="matrix(0,1,-1,0,447.63226,-140.75165)">
+ <rect
+ y="158.39343"
+ x="288.84821"
+ height="2"
+ width="1.28125"
+ id="rect5329-6-3"
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="158.39343"
+ x="291.26486"
+ height="2"
+ width="1.28125"
+ id="rect5329-5-5-2"
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="158.39343"
+ x="293.68155"
+ height="2"
+ width="1.28125"
+ id="rect5329-55-5-9"
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="158.39343"
+ x="296.09821"
+ height="2"
+ width="1.28125"
+ id="rect5329-9-6-8"
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ <g
+ id="g5406-0"
+ transform="matrix(0,1,-1,0,459.66351,-140.9079)">
+ <rect
+ y="158.39343"
+ x="288.84821"
+ height="2"
+ width="1.28125"
+ id="rect5329-6-3-3"
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="158.39343"
+ x="291.26486"
+ height="2"
+ width="1.28125"
+ id="rect5329-5-5-2-5"
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="158.39343"
+ x="293.68155"
+ height="2"
+ width="1.28125"
+ id="rect5329-55-5-9-9"
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <rect
+ y="158.39343"
+ x="296.09821"
+ height="2"
+ width="1.28125"
+ id="rect5329-9-6-8-5"
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/data/icons/sc-actions-kdenlive-show-video-effects.svg b/data/icons/sc-actions-kdenlive-show-video-effects.svg
new file mode 100644
index 0000000..b62fced
--- /dev/null
+++ b/data/icons/sc-actions-kdenlive-show-video-effects.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ id="svg2"
+ height="16"
+ width="16">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient7864">
+ <stop
+ id="stop7866"
+ offset="0"
+ style="stop-color:#fdfeff;stop-opacity:1;" />
+ <stop
+ id="stop7868"
+ offset="1"
+ style="stop-color:#fdfeff;stop-opacity:0.22368421;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7847">
+ <stop
+ id="stop7849"
+ offset="0"
+ style="stop-color:#8ace62;stop-opacity:1;" />
+ <stop
+ id="stop7851"
+ offset="1"
+ style="stop-color:#3e8414;stop-opacity:1;" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ transform="translate(-286.28571,-144.36218)"
+ id="layer1">
+ <rect
+ y="146.91576"
+ x="286.78571"
+ height="10.870536"
+ width="14.852678"
+ id="rect3378"
+ style="fill:#ffffff;fill-opacity:0.50196081;stroke:none;stroke-opacity:1" />
+ <path
+ id="rect4172"
+ transform="translate(286.28571,144.36218)"
+ d="M 1.0019531 3.0527344 L 1.0019531 12.978516 L 14.927734 12.978516 L 14.927734 3.0527344 L 1.0019531 3.0527344 z M 2.0214844 4.0234375 L 3.7226562 4.0234375 L 3.7226562 5.7226562 L 2.0214844 5.7226562 L 2.0214844 4.0234375 z M 5.4941406 4.0234375 L 7.1933594 4.0234375 L 7.1933594 5.7226562 L 5.4941406 5.7226562 L 5.4941406 4.0234375 z M 8.9648438 4.0234375 L 10.666016 4.0234375 L 10.666016 5.7226562 L 8.9648438 5.7226562 L 8.9648438 4.0234375 z M 12.4375 4.0234375 L 14.136719 4.0234375 L 14.136719 5.7226562 L 12.4375 5.7226562 L 12.4375 4.0234375 z M 2.0351562 10.25 L 3.7363281 10.25 L 3.7363281 11.951172 L 2.0351562 11.951172 L 2.0351562 10.25 z M 5.5078125 10.25 L 7.2070312 10.25 L 7.2070312 11.951172 L 5.5078125 11.951172 L 5.5078125 10.25 z M 8.9785156 10.25 L 10.679688 10.25 L 10.679688 11.951172 L 8.9785156 11.951172 L 8.9785156 10.25 z M 12.451172 10.25 L 14.150391 10.25 L 14.150391 11.951172 L 12.451172 11.951172 L 12.451172 10.25 z "
+ style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/data/kdenlive.appdata.xml b/data/kdenlive.appdata.xml
index 9bace20..2b00130 100644
--- a/data/kdenlive.appdata.xml
+++ b/data/kdenlive.appdata.xml
@@ -95,6 +95,9 @@
<ul>
<li>Intuitive multitrack interface.</li>
<li xml:lang="ast">Interfaz multi-pista intuitiva.</li>
+ <li xml:lang="ar">واجهة متعدّدة المسارات بديهيّة</li>
+ <li xml:lang="ca">Interfície multipista intuïtiva</li>
+ <li xml:lang="de">Intuitive Benutzerschnittstelle für mehrere Spuren.</li>
<li xml:lang="ca">Interfície multipista intuïtiva.</li>
<li xml:lang="de">Intuitive Benutzerschnittstelle für mehrere Spuren.</li>
<li xml:lang="en-GB">Intuitive multitrack interface.</li>
@@ -108,9 +111,11 @@
<li xml:lang="sk">Intuitívne viacstopové rozhranie.</li>
<li xml:lang="sl">Intuitiven vmesnik z več sledmi.</li>
<li xml:lang="sv">Intuitivt gränssnitt med flera spår.</li>
+ <li xml:lang="tr">Sezgisel çok parçalı arayüz.</li>
<li xml:lang="uk">Інтуїтивно зрозумілий інтерфейс для роботи з декількома доріжками одночасно.</li>
<li xml:lang="x-test">xxIntuitive multitrack interface.xx</li>
<li xml:lang="zh-CN">直观的多轨界面。</li>
+ <li xml:lang="zh-TW">直觀式的多軌介面</li>
<li>Many effects and transitions.</li>
<li xml:lang="ar">تأثيرات وانتقالات عديدة.</li>
<li xml:lang="ast">Munchos efeutos y transiciones.</li>
@@ -173,8 +178,8 @@
<li xml:lang="zh-TW">基本 DVD 精靈</li>
</ul>
</description>
- <url type="homepage">http://kdenlive.org/</url>
- <url type="bugtracker">https://bugs.kdenlive.org</url>
+ <url type="homepage">https://kdenlive.org/</url>
+ <url type="bugtracker">https://bugs.kde.org</url>
<url type="help">https://userbase.kde.org/Kdenlive/Manual</url>
<screenshots>
<screenshot type="default" width="1280" height="720">
diff --git a/data/kdenlive.notifyrc b/data/kdenlive.notifyrc
index f96da45..7297b8e 100644
--- a/data/kdenlive.notifyrc
+++ b/data/kdenlive.notifyrc
@@ -4,27 +4,15 @@ Comment=Kdenlive
Comment[ast]=Kdenlive
Comment[bs]=Kdenlive
Comment[ca]=Kdenlive
-Comment[[email protected]]=Kdenlive
Comment[cs]=Kdenlive
-Comment[da]=Kdenlive
Comment[de]=Kdenlive
-Comment[el]=Kdenlive
Comment[en_GB]=Kdenlive
Comment[es]=Kdenlive
-Comment[et]=Kdenlive
Comment[fi]=Kdenlive
Comment[fr]=Kdenlive
-Comment[ga]=Kdenlive
-Comment[gl]=Kdenlive
-Comment[hu]=Kdenlive
Comment[it]=Kdenlive
Comment[ja]=Kdenlive
-Comment[km]=Kdenlive
-Comment[lt]=Kdenlive
-Comment[lv]=Kdenlive
-Comment[mr]=के-डि-एनलाइव्ह
Comment[nb]=Kdenlive
-Comment[nds]=Kdenlive
Comment[nl]=Kdenlive
Comment[pl]=Kdenlive
Comment[pt]=Kdenlive
@@ -35,11 +23,9 @@ Comment[sk]=Kdenlive
Comment[sl]=Kdenlive
Comment[sv]=Kdenlive
Comment[tr]=Kdenlive
-Comment[ug]=Kdenlive
Comment[uk]=Kdenlive
Comment[x-test]=xxKdenlivexx
Comment[zh_CN]=Kdenlive
-Comment[zh_TW]=Kdenlive
[Event/RenderFinished]
Name=Rendering finished
@@ -47,30 +33,19 @@ Name[ar]=انتهى التّصيير
Name[ast]=Finó'l renderizáu
Name[bs]=Iscrtavanje završeno
Name[ca]=Ha acabat la renderització
-Name[[email protected]]=Ha acabat la renderització
Name[cs]=Renderování bylo dokončeno
-Name[da]=Rendering gennemført
Name[de]=Rendern fertiggestellt
-Name[el]=Η αποτύπωση ολοκληρώθηκε
Name[en_GB]=Rendering finished
Name[es]=Procesamiento finalizado
-Name[et]=Renderdamine on valmis
Name[fi]=Renderöinti valmistui
Name[fr]=Rendu terminé
-Name[gl]=Renderizado finalizado
-Name[hu]=A renderelés befejeződött
Name[it]=Esportazione terminata
Name[ja]=レンダリングが完了しました
-Name[km]=បាន​បញ្ចប់​ការ​បង្ហាញ
-Name[lt]=Atvaizdavimas baigtas
-Name[lv]=Renderēšana pabeigta
-Name[mr]=रेंडरींग पूर्ण झाले
Name[nb]=Opptegning avsluttet
Name[nl]=Weergave uitwerken beëindigd
Name[pl]=Ukończono renderowanie
Name[pt]=A geração terminou
Name[pt_BR]=A renderização terminou
-Name[ro]=Randare încheiată
Name[sk]=Renderovanie ukončené
Name[sl]=Izrisovanje končano
Name[sv]=Återgivning klar
@@ -78,36 +53,24 @@ Name[tr]=Hazırlama tamamlandı
Name[uk]=Обробку завершено
Name[x-test]=xxRendering finishedxx
Name[zh_CN]=渲染结束
-Name[zh_TW]=已完成導出
Comment=Rendering is over
Comment[ar]=قد انتهى التّصيير
Comment[ast]=Acabóse'l renderizáu
Comment[bs]=Iscrtavanje je gotovo
Comment[ca]=La renderització ja ha acabat
-Comment[[email protected]]=La renderització ja ha acabat
Comment[cs]=Renderování je hotové
-Comment[da]=Renderingen er slut
Comment[de]=Das Rendern ist beendet
-Comment[el]=Η αποτύπωση τελείωσε
Comment[en_GB]=Rendering is over
Comment[es]=El procesamiento ha finalizado
-Comment[et]=Renderdamine on valmis
Comment[fi]=Renderöinti on tehty
Comment[fr]=Le rendu est terminé
-Comment[gl]=Rematou o renderizado
-Comment[hu]=A renderelésnek vége
Comment[it]=L'esportazione è terminata
Comment[ja]=レンダリングが終了しました
-Comment[km]=ការ​បង្ហាញ​បាន​ចប់
-Comment[lt]=Atvaizdavimas baigtas
-Comment[lv]=Renderēšana ir beigusies
-Comment[mr]=रेंडरींग पूर्ण झाले आहे
Comment[nb]=Ferdig med opptegning
Comment[nl]=Weergave uitwerken is gereed
Comment[pl]=Ukończono zostało zakończone
Comment[pt]=A geração terminou
Comment[pt_BR]=A renderização foi terminada
-Comment[ro]=Randarea s-a încheiat
Comment[sk]=Renderovanie je skončené
Comment[sl]=Izrisovanje je končano
Comment[sv]=Återgivningen är gjord
@@ -115,7 +78,6 @@ Comment[tr]=Hazırlama bitti
Comment[uk]=Виконання обробки завершено
Comment[x-test]=xxRendering is overxx
Comment[zh_CN]=渲染结束
-Comment[zh_TW]=導出已結束
Action=Popup
[Event/RenderStarted]
@@ -124,30 +86,19 @@ Name[ar]=بدأ التّصيير
Name[ast]=Entamó'l renderizáu
Name[bs]=Iscrtavanje započelo
Name[ca]=Ha començat la renderització
-Name[[email protected]]=Ha començat la renderització
Name[cs]=Renderování začalo
-Name[da]=Rendering påbegyndt
Name[de]=Rendern wurde gestartet
-Name[el]=Η αποτύπωση ξεκίνησε
Name[en_GB]=Rendering started
Name[es]=Procesamiento inciado
-Name[et]=Renderdamist on alustatud
Name[fi]=Renderöinti aloitettu
Name[fr]=Rendu démarré
-Name[gl]=Iniciado o renderizado
-Name[hu]=A renderelés elkezdődött
Name[it]=Esportazione avviata
Name[ja]=レンダリングを開始しました
-Name[km]=ការ​បង្ហាញ​បាន​ចាប់ផ្ដើម
-Name[lt]=Atvaizdavimas pradėtas
-Name[lv]=Renderēšana sākta
-Name[mr]=रेंडरींग सुरु झाले
Name[nb]=Opptegning påbegynt
Name[nl]=Weergave uitwerken begonnen
Name[pl]=Rozpoczęto renderowanie
Name[pt]=A geração foi iniciada
Name[pt_BR]=A renderização iniciou
-Name[ro]=Randarea începută
Name[sk]=Renderovanie spustené
Name[sl]=Izrisovanje začeto
Name[sv]=Återgivning startad
@@ -155,36 +106,24 @@ Name[tr]=Hazırlama başladı
Name[uk]=Обробку розпочато
Name[x-test]=xxRendering startedxx
Name[zh_CN]=渲染开始
-Name[zh_TW]=導出已開始
Comment=Rendering was started
Comment[ar]=قد بدأ التّصيير
Comment[ast]=Anicióse'l rederizáu
Comment[bs]=Iscrtavanje je započelo
Comment[ca]=La renderització ja ha començat
-Comment[[email protected]]=La renderització ja ha començat
Comment[cs]=Renderování bylo začato
-Comment[da]=Renderingen blev startet
Comment[de]=Das Rendern wurde gestartet
-Comment[el]=Η αποτύπωση ξεκίνησε
Comment[en_GB]=Rendering was started
Comment[es]=El procesamiento ha sido iniciado
-Comment[et]=Renderdamist on alustatud
Comment[fi]=Renderöinti aloitettiin
Comment[fr]=Le rendu a démarré
-Comment[gl]=Vaise iniciar o renderizado
-Comment[hu]=A renderelés elkezdődött
Comment[it]=L'esportazione è stata avviata
Comment[ja]=レンダリングを開始しました
-Comment[km]=ការ​បង្ហាញ​ត្រូវ​បាន​ចាប់ផ្ដើម
-Comment[lt]=Atvaizdavimas buvo pradėtas
-Comment[lv]=Renderēšana tika palaista
-Comment[mr]=रेंडरींग सुरु झाले होते
Comment[nb]=Startet opptegning
Comment[nl]=Weergave uitwerken is begonnen
Comment[pl]=Renderowanie zostało rozpoczęte
Comment[pt]=A geração foi iniciada
Comment[pt_BR]=A renderização foi iniciada
-Comment[ro]=Randarea a început
Comment[sk]=Renderovanie bolo spustené
Comment[sl]=Izrisovanje se je začelo
Comment[sv]=Återgivningen har startats
@@ -192,7 +131,6 @@ Comment[tr]=Hazırlama başlatıldı
Comment[uk]=Було розпочато обробку
Comment[x-test]=xxRendering was startedxx
Comment[zh_CN]=渲染开始
-Comment[zh_TW]=導出已開始
Action=Popup
[Event/FrameCaptured]
@@ -200,30 +138,19 @@ Name=Frame captured
Name[ar]=التُقط إطار
Name[bs]=Kadar uhvaćen
Name[ca]=S'ha capturat un fotograma
-Name[[email protected]]=S'ha capturat un fotograma
Name[cs]=Snímek zachycen
-Name[da]=Billed indfanget
Name[de]=Bild aufgenommen
-Name[el]=Σύλληψη πλαισίου
Name[en_GB]=Frame captured
Name[es]=Fotograma capturado
-Name[et]=Kaader on salvestatud
Name[fi]=Ruutu kaapattu
Name[fr]=Cadre capturé
-Name[gl]=Fotograma capturado
-Name[hu]=Képkocka rögzítve
Name[it]=Fotogramma acquisito
Name[ja]=フレームをキャプチャしました
-Name[km]=បាន​ចាប់​យក​ស៊ុម
-Name[lt]=Kadras išsaugotas
-Name[lv]=Kadrs notverts
-Name[mr]=फ्रेम पकडली
Name[nb]=Stillbilde tatt
Name[nl]=Frame opgenomen
Name[pl]=Przechwycono klatkę
Name[pt]=Imagem capturada
Name[pt_BR]=Imagem capturada
-Name[ro]=Cadru captat
Name[sk]=Snímka zachytená
Name[sl]=Sličica zajeta
Name[sv]=Ram lagrad
@@ -231,35 +158,23 @@ Name[tr]=Kare yakalandı
Name[uk]=Захоплено кадр
Name[x-test]=xxFrame capturedxx
Name[zh_CN]=已抓取帧
-Name[zh_TW]=影格已擷取
Comment=A frame was captured to disk
Comment[ar]=قد التُقط إطار إلى القرص
Comment[bs]=Kadar je uhvaćen na disk
Comment[ca]=S'ha capturat i desat un fotograma al disc
-Comment[[email protected]]=S'ha capturat i guardat un fotograma al disc
Comment[cs]=Snímek byl zachycen na disk
-Comment[da]=Et billed blev indfanget til disken
Comment[de]=Ein Bild wurde aufgenommen und auf auf Festplatte gespeichert
-Comment[el]=Έγινε σύλληψη πλαισίου στο δίσκο
Comment[en_GB]=A frame was captured to disk
Comment[es]=Un fotograma fue capturado al disco
-Comment[et]=Kaader on kettale salvestatud
Comment[fi]=Ruutu kaapattiin levylle
Comment[fr]=Un cadre a été capturé sur le disque
-Comment[gl]=Capturouse un fotograma para o disco
-Comment[hu]=Egy képkocka rögzítésre került a lemezre
Comment[it]=È stato acquisito un fotogramma sul disco
Comment[ja]=フレームをディスクにキャプチャしました
-Comment[km]=ស៊ុម​ត្រូវ​បាន​ចាប់​យក​ទៅកាន់​ថាស
-Comment[lt]=Kadras buvo išsaugotas į diską
-Comment[lv]=Kadrs tika notverts diskā
-Comment[mr]=फ्रेम डिस्कवर पकडली
Comment[nb]=Et stillbilde ble lagret
Comment[nl]=Een frame is op schijf opgenomen
Comment[pl]=Klatka została przechwycona na dysk
Comment[pt]=Foi capturada uma imagem para o disco
Comment[pt_BR]=Foi capturada uma imagem para o disco
-Comment[ro]=Un cadru a fost captat pe disc
Comment[sk]=Snímka bola uložená na disk
Comment[sl]=Sličica je bila zajeta na disk
Comment[sv]=En ram har lagrats på disk
@@ -267,7 +182,6 @@ Comment[tr]=Bir kare diske yakalandı
Comment[uk]=Було захоплено кадр, програма зберегла його на диску
Comment[x-test]=xxA frame was captured to diskxx
Comment[zh_CN]=一帧图像已被抓取到磁盘
-Comment[zh_TW]=已擷取一個影格到磁碟中
Sound=KDE-Sys-App-Message.ogg
Action=Sound
@@ -276,30 +190,19 @@ Name=Ready to capture
Name[ar]=جاهز للالتقاط
Name[bs]=Spreman za hvatanje
Name[ca]=A punt per capturar
-Name[[email protected]]=A punt per capturar
Name[cs]=Připraven zachytávat
-Name[da]=Klar til at indfange
Name[de]=Bereit zur Aufnahme
-Name[el]=Έτοιμο για σύλληψη
Name[en_GB]=Ready to capture
Name[es]=Listo para capturar
-Name[et]=Salvestamiseks valmis
Name[fi]=Valmiina kaappaamaan
Name[fr]=Prêt pour la capture
-Name[gl]=Listo para a captura
-Name[hu]=Kezdődhet a rögzítés
Name[it]=Pronto per la registrazione
Name[ja]=キャプチャ準備完了
-Name[km]=រួចរាល់​ដើម្បី​ចាប់​យក
-Name[lt]=Pasiruošęs išsaugoti
-Name[lv]=Gatavs notveršanai
-Name[mr]=पकडण्याकरिता तयार
Name[nb]=Klar til å ta stillbilde
Name[nl]=Gereed om op te nemen
Name[pl]=Gotowy do przechwycenia
Name[pt]=Pronto para capturar
Name[pt_BR]=Pronto para capturar
-Name[ro]=Gata de captare
Name[sk]=Pripravené na zachytenie
Name[sl]=Pripravljen na zajem
Name[sv]=Klar att lagra
@@ -307,7 +210,6 @@ Name[tr]=Yakalamak için hazır
Name[uk]=Приготовано до захоплення
Name[x-test]=xxReady to capturexx
Name[zh_CN]=准备抓取
-Name[zh_TW]=準備擷取
Sound=KDE-Sys-App-Positive.ogg
Action=Sound
@@ -317,27 +219,15 @@ Name[ar]=خطأ
Name[ast]=Fallu
Name[bs]=Greška
Name[ca]=Error
-Name[[email protected]]=Error
Name[cs]=Chyba
-Name[da]=Fejl
Name[de]=Fehler
-Name[el]=Σφάλμα
Name[en_GB]=Error
Name[es]=Error
-Name[et]=Tõrge
Name[fi]=Virhe
Name[fr]=Erreur
-Name[ga]=Earráid
-Name[gl]=Erro
-Name[hu]=Hiba
Name[it]=Errore
Name[ja]=エラー
-Name[km]=កំហុស
-Name[lt]=Klaida
-Name[lv]=Kļūda
-Name[mr]=त्रुटी
Name[nb]=Feil
-Name[nds]=Fehler
Name[nl]=Fout
Name[pl]=Błąd
Name[pt]=Erro
@@ -348,34 +238,21 @@ Name[sk]=Chyba
Name[sl]=Napaka
Name[sv]=Fel
Name[tr]=Hata
-Name[ug]=خاتالىق
Name[uk]=Помилка
Name[x-test]=xxErrorxx
Name[zh_CN]=错误
-Name[zh_TW]=錯誤
Comment=An error occurred in Kdenlive
Comment[ast]=Asocedió un fallu en Kdenlive
Comment[bs]=Desila se greška u KDenlive
Comment[ca]=S'ha produït un error al Kdenlive
-Comment[[email protected]]=S'ha produït un error al Kdenlive
Comment[cs]=V Kdenlive došlo k chybě
-Comment[da]=En fejl opstod i Kdenlive
Comment[de]=In Kdenlive ist ein Fehler aufgetreten.
-Comment[el]=Εμφανίστηκε σφάλμα στο Kdenlive
Comment[en_GB]=An error occurred in Kdenlive
Comment[es]=Ha ocurrido un error en Kdenlive
-Comment[et]=Kdenlive'is tekkis tõrge
Comment[fi]=Kdenlivessä ilmeni virhe
Comment[fr]=Une erreur est survenue dans Kdenlive
-Comment[ga]=Tharla earráid in Kdenlive
-Comment[gl]=Aconteceu un erro en Kdenlive
-Comment[hu]=Hiba történt a Kdenlive-ban
Comment[it]=Si è verificato un errore in Kdenlive
Comment[ja]=Kdenlive 内でエラーが発生
-Comment[km]=មាន​កំហុស​បាន​កើតឡើង​នៅ​ក្នុង Kdenlive
-Comment[lt]=Įvyko Kdenlive klaida
-Comment[lv]=Programmā Kdenlive radās kļūda
-Comment[mr]=के-डि-एनलाइव्ह मध्ये त्रुटी निर्माण झाली
Comment[nb]=En feil oppsto i Kdenlive
Comment[nl]=Er is een fout opgetreden in Kdenlive
Comment[pl]=Wystąpił błąd w Kdenlive
@@ -390,6 +267,5 @@ Comment[tr]=Kdenlive içerisinde bir hata oluştu
Comment[uk]=У коді Kdenlive сталася помилка
Comment[x-test]=xxAn error occurred in Kdenlivexx
Comment[zh_CN]=Kdenlive 发生了一个错误
-Comment[zh_TW]=Kdenlive 中發生錯誤
Sound=KDE-Sys-Warning.ogg
Action=Sound
diff --git a/data/kdenliveeffectscategory.rc b/data/kdenliveeffectscategory.rc
index eddc4cd..641fdfc 100644
--- a/data/kdenliveeffectscategory.rc
+++ b/data/kdenliveeffectscategory.rc
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<data name="effects" version="0">
- <group list="brightness,gamma,frei0r.colgate,frei0r.balanc0r,frei0r.brightness,frei0r.levels,frei0r.three_point_balance,frei0r.curves,frei0r.coloradj_RGB,frei0r.sopsat,frei0r.bezier_curves">
+ <group list="lift_gamma_gain,brightness,gamma,frei0r.colgate,frei0r.balanc0r,frei0r.brightness,frei0r.levels,frei0r.three_point_balance,frei0r.curves,frei0r.coloradj_RGB,frei0r.sopsat,frei0r.bezier_curves">
<text>Colour correction</text>
</group>
<group list="invert,sepia,tcolor,greyscale,frei0r.B,frei0r.G,frei0r.R,frei0r.contrast0r,frei0r.saturat0r,frei0r.tint0r,frei0r.primaries,frei0r.rgbparade,chroma_hold,frei0r.hueshift0r">
@@ -37,4 +37,7 @@
<group list="frei0r.d90stairsteppingfix,frei0r.hqdn3d,frei0r.sharpness">
<text>Enhancement</text>
</group>
+ <group list="movit.blur,movit.sharpen,movit.diffusion,movit.glow,movit.lift_gamma_gain,movit.mirror,movit.opacity,movit.rect,movit.saturation,movit.unsharp_mask,movit.vignette,movit.white_balance">
+ <text>GPU effects</text>
+ </group>
</data>
diff --git a/data/kdenlivemonitor.qml b/data/kdenlivemonitor.qml
new file mode 100644
index 0000000..255f59f
--- /dev/null
+++ b/data/kdenlivemonitor.qml
@@ -0,0 +1,55 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ objectName: "root"
+
+ // default size, but scalable by user
+ height: 300; width: 400
+ property string comment
+ property string framenum
+ property point center
+ property double scale
+
+ Rectangle {
+ id: overlaybg
+ anchors {
+ right: parent.right
+ //left: parent.left
+ bottom: parent.bottom
+ }
+ width: label.width + 10
+ height: label.height + 10
+ //height: root.height * 0.1
+ color: "#99ff0000"
+
+ Text {
+ id: label
+ objectName: "overlaytext"
+ anchors.centerIn: parent
+ text: root.framenum
+ }
+ }
+
+ Rectangle {
+ objectName: "marker"
+ anchors {
+ right: parent.right
+ top: parent.top
+ }
+ width: marker.width + 10
+ height: marker.height + 5
+ color: "#99ff0000"
+ border.color: "#33ff0000"
+ border.width: 3
+ radius: 5
+ visible: root.comment != ""
+ Text {
+ id: marker
+ objectName: "markertext"
+ anchors.centerIn: parent
+ color: "white"
+ text: root.comment
+ }
+ }
+}
diff --git a/data/kdenlivemonitoreffectscene.qml b/data/kdenlivemonitoreffectscene.qml
new file mode 100644
index 0000000..eaba3bb
--- /dev/null
+++ b/data/kdenlivemonitoreffectscene.qml
@@ -0,0 +1,300 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ objectName: "rooteffectscene"
+
+ // default size, but scalable by user
+ height: 300; width: 400
+ property string comment
+ property string framenum
+ property rect framesize
+ property point profile
+ property point center
+ property double scale
+ onScaleChanged: canvas.requestPaint()
+ property bool iskeyframe
+ property var centerPoints: []
+ onCenterPointsChanged: canvas.requestPaint()
+ signal effectChanged()
+ signal addKeyframe()
+
+ Canvas {
+ id: canvas
+ width: root.width
+ height: root.height
+ anchors.centerIn: root
+ contextType: "2d";
+ renderStrategy: Canvas.Threaded;
+ onPaint:
+ {
+ if (context) {
+ context.clearRect(0,0, width, height);
+ context.beginPath()
+ context.strokeStyle = Qt.rgba(1, 0, 0, 0.5)
+ context.lineWidth = 2
+
+ for(var i = 0; i < root.centerPoints.length; i++)
+ {
+ var p1 = convertPoint(root.centerPoints[i])
+ if(i == 0)
+ {
+ context.moveTo(p1.x, p1.y)
+ continue
+ }
+ context.lineTo(p1.x, p1.y)
+ }
+ context.stroke()
+ context.restore()
+ }
+ }
+
+ function convertPoint(p)
+ {
+ var x = frame.x + p.x * root.scale
+ var y = frame.y + p.y * root.scale
+ return Qt.point(x,y);
+ }
+ }
+ Rectangle {
+ id: frame
+ objectName: "referenceframe"
+ property color hoverColor: "#ff0000"
+ width: root.profile.x * root.scale
+ height: root.profile.y * root.scale
+ x: root.center.x - width / 2
+ y: root.center.y - height / 2
+ color: "transparent"
+ border.color: "#ffffff00"
+ }
+ MouseArea {
+ id: global
+ objectName: "global"
+ width: root.width; height: root.height
+ anchors.centerIn: root
+ onClicked: {
+
+ }
+ onDoubleClicked: {
+ root.addKeyframe()
+ }
+ }
+ Rectangle {
+ id: framerect
+ property color hoverColor: "#ff0000"
+ x: frame.x + root.framesize.x * root.scale
+ y: frame.y + root.framesize.y * root.scale
+ width: root.framesize.width * root.scale
+ height: root.framesize.height * root.scale
+ color: "transparent"
+ border.color: "#ffff0000"
+ Rectangle {
+ id: "tlhandle"
+ anchors {
+ top: parent.top
+ left: parent.left
+ }
+ visible: root.iskeyframe
+ width: effectsize.height * 0.7
+ height: this.width
+ color: "red"
+ MouseArea {
+ property int oldMouseX
+ property int oldMouseY
+ width: parent.width; height: parent.height
+ anchors.centerIn: parent
+ hoverEnabled: true
+ cursorShape: Qt.SizeFDiagCursor
+ onEntered: { tlhandle.color = '#ffff00'}
+ onExited: { tlhandle.color = '#ff0000'}
+ onPressed: {
+ oldMouseX = mouseX
+ oldMouseY = mouseY
+ effectsize.visible = true
+ }
+ onPositionChanged: {
+ if (pressed) {
+ framesize.x = (framerect.x + (mouseX - oldMouseX) - frame.x) / root.scale;
+ framesize.width = (framerect.width - (mouseX - oldMouseX)) / root.scale;
+ framesize.y = (framerect.y + (mouseY - oldMouseY) - frame.y) / root.scale;
+ framesize.height = (framerect.height - (mouseY - oldMouseY)) / root.scale;
+ }
+ }
+ onReleased: {
+ root.effectChanged()
+ effectsize.visible = false
+ }
+ }
+ Text {
+ id: effectpos
+ objectName: "effectpos"
+ color: "red"
+ visible: false
+ anchors {
+ top: parent.bottom
+ left: parent.right
+ }
+ text: framesize.x.toFixed(0) + "x" + framesize.y.toFixed(0)
+ }
+ }
+ Rectangle {
+ id: "trhandle"
+ anchors {
+ top: parent.top
+ right: parent.right
+ }
+ width: effectsize.height * 0.7
+ height: this.width
+ color: "red"
+ visible: root.iskeyframe
+ MouseArea {
+ property int oldMouseX
+ property int oldMouseY
+ width: parent.width; height: parent.height
+ anchors.centerIn: parent
+ hoverEnabled: true
+ cursorShape: Qt.SizeBDiagCursor
+ onEntered: { trhandle.color = '#ffff00'}
+ onExited: { trhandle.color = '#ff0000'}
+ onPressed: {
+ oldMouseX = mouseX
+ oldMouseY = mouseY
+ effectsize.visible = true
+ }
+ onPositionChanged: {
+ if (pressed) {
+ framesize.width = (framerect.width + (mouseX - oldMouseX)) / root.scale;
+ framesize.y = (framerect.y + (mouseY - oldMouseY) - frame.y) / root.scale;
+ framesize.height = (framerect.height - (mouseY - oldMouseY)) / root.scale;
+ }
+ }
+ onReleased: {
+ root.effectChanged()
+ effectsize.visible = false
+ }
+ }
+ }
+ Rectangle {
+ id: "blhandle"
+ anchors {
+ bottom: parent.bottom
+ left: parent.left
+ }
+ width: effectsize.height * 0.7
+ height: this.width
+ color: "red"
+ visible: root.iskeyframe
+ MouseArea {
+ property int oldMouseX
+ property int oldMouseY
+ width: parent.width; height: parent.height
+ anchors.centerIn: parent
+ hoverEnabled: true
+ cursorShape: Qt.SizeBDiagCursor
+ onEntered: { blhandle.color = '#ffff00'}
+ onExited: { blhandle.color = '#ff0000'}
+ onPressed: {
+ oldMouseX = mouseX
+ oldMouseY = mouseY
+ effectsize.visible = true
+ }
+ onPositionChanged: {
+ if (pressed) {
+ framesize.x = (framerect.x + (mouseX - oldMouseX) - frame.x) / root.scale;
+ framesize.width = (framerect.width - (mouseX - oldMouseX)) / root.scale;
+ framesize.height = (framerect.height + (mouseY - oldMouseY)) / root.scale;
+ }
+ }
+ onReleased: {
+ root.effectChanged()
+ effectsize.visible = false
+ }
+ }
+ }
+ Rectangle {
+ id: "brhandle"
+ anchors {
+ bottom: parent.bottom
+ right: parent.right
+ }
+ width: effectsize.height * 0.7
+ height: this.width
+ color: "red"
+ visible: root.iskeyframe
+ MouseArea {
+ property int oldMouseX
+ property int oldMouseY
+ width: parent.width; height: parent.height
+ anchors.centerIn: parent
+ hoverEnabled: true
+ cursorShape: Qt.SizeFDiagCursor
+ onEntered: { brhandle.color = '#ffff00'}
+ onExited: { brhandle.color = '#ff0000'}
+ onPressed: {
+ oldMouseX = mouseX
+ oldMouseY = mouseY
+ effectsize.visible = true
+ }
+ onPositionChanged: {
+ if (pressed) {
+ framesize.width = (framerect.width + (mouseX - oldMouseX)) / root.scale;
+ framesize.height = (framerect.height + (mouseY - oldMouseY)) / root.scale;
+ }
+ }
+ onReleased: {
+ root.effectChanged()
+ effectsize.visible = false
+ }
+ }
+ Text {
+ id: effectsize
+ objectName: "effectsize"
+ color: "red"
+ visible: false
+ anchors {
+ bottom: parent.top
+ right: parent.left
+ }
+ text: framesize.width.toFixed(0) + "x" + framesize.height.toFixed(0)
+ }
+ }
+ Rectangle {
+ anchors.centerIn: parent
+ width: 1
+ height: root.iskeyframe ? effectsize.height * 1.5 : effectsize.height / 2
+ color: framerect.hoverColor
+ MouseArea {
+ width: effectsize.height * 1.5; height: effectsize.height * 1.5
+ anchors.centerIn: parent
+ property int oldMouseX
+ property int oldMouseY
+ hoverEnabled: true
+ enabled: root.iskeyframe
+ cursorShape: root.iskeyframe ? Qt.SizeAllCursor : Qt.ArrowCursor
+ onEntered: { framerect.hoverColor = '#ffff00'}
+ onExited: { framerect.hoverColor = '#ff0000'}
+ onPressed: {
+ oldMouseX = mouseX
+ oldMouseY = mouseY
+ effectpos.visible = true
+ }
+ onPositionChanged: {
+ if (pressed) {
+ framesize.x = (framerect.x + (mouseX - oldMouseX) - frame.x) / root.scale;
+ framesize.y = (framerect.y + (mouseY - oldMouseY) - frame.y) / root.scale;
+ }
+ }
+ onReleased: {
+ root.effectChanged()
+ effectpos.visible = false
+ }
+ }
+ }
+ Rectangle {
+ anchors.centerIn: parent
+ width: root.iskeyframe ? effectsize.height * 1.5 : effectsize.height / 2
+ height: 1
+ color: framerect.hoverColor
+ }
+ }
+}
diff --git a/data/kdenlivemonitorsplit.qml b/data/kdenlivemonitorsplit.qml
new file mode 100644
index 0000000..16450a4
--- /dev/null
+++ b/data/kdenlivemonitorsplit.qml
@@ -0,0 +1,63 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ objectName: "rootsplit"
+
+ // default size, but scalable by user
+ height: 300; width: 400
+ signal qmlMoveSplit()
+ property int splitterPos
+ property point center
+ property double scale
+ // percentage holds splitter pos relative to the scene percentage
+ property double percentage
+ // realpercent holds splitter pos relative to the frame width percentage
+ property double realpercent
+
+ percentage: 0.5
+ realpercent: 0.5
+ splitterPos: this.width / 2
+
+ MouseArea {
+ width: root.width; height: root.height
+ anchors.centerIn: parent
+ hoverEnabled: true
+ cursorShape: Qt.SizeHorCursor
+ onPressed: {
+ root.percentage = mouseX / width
+ root.splitterPos = mouseX
+ root.qmlMoveSplit()
+ }
+ onPositionChanged: {
+ if (pressed) {
+ root.percentage = mouseX / width
+ root.splitterPos = mouseX
+ root.qmlMoveSplit()
+ }
+ timer.restart()
+ splitter.visible = true
+ }
+ //onEntered: { splitter.visible = true }
+ onExited: { splitter.visible = false }
+ }
+
+ Rectangle {
+ id: splitter
+ x: root.splitterPos
+ y: 0
+ width: 1
+ height: root.height
+ color: "red"
+ visible: false
+ }
+
+ Timer {
+ id: timer
+
+ interval: 1000; running: false; repeat: false
+ onTriggered: {
+ splitter.visible = false
+ }
+ }
+}
diff --git a/data/kdenlivetranscodingrc b/data/kdenlivetranscodingrc
index 852a308..87359cb 100644
--- a/data/kdenlivetranscodingrc
+++ b/data/kdenlivetranscodingrc
@@ -16,6 +16,7 @@ DNxHD 720p 59.94 fps 145 Mb/s=-s 1280x720 -r 60000/1001 -vb 145000k -threads 2 -
Fix MPEG-1=-sameq -acodec copy -vcodec mpeg1video %1.mpg;Fix unplayable MPEG-1 files;;vcodec=mpeg1video
Fix Ogg Theora=-sameq -vcodec libtheora -acodec copy %1.ogv;Fix unplayable OGG Theora files;;vcodec=theora
Remux MPEG-2 PS/VOB=-vcodec copy -acodec copy %1.mpg;Fix audio sync in MPEG-2 vob files;;vcodec=mpeg2video
+Remux MPEG-2 PS/VOB=-vcodec copy -acodec copy %1.mpg;Fix audio sync in MPEG-2 vob files 2;
Lossless Matroska=-sn -vcodec huffyuv -acodec flac %1.mkv;High quality lossless encoding
Wav 48000Hz=-vn -ar 48000 %1.wav;Extract audio as WAV file;audio
Remux with MKV=-vcodec copy -acodec copy -sn %1.mkv
diff --git a/data/kdenliveui.rc b/data/kdenliveui.rc
index d059fe7..a0bc435 100644
--- a/data/kdenliveui.rc
+++ b/data/kdenliveui.rc
@@ -1,5 +1,5 @@
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
-<kpartgui name="kdenlive" version="78" translationDomain="kdenlive">
+<kpartgui name="kdenlive" version="81" translationDomain="kdenlive">
<ToolBar name="extraToolBar" >
<text>Extra Toolbar</text>
<Action name="project_render" />
@@ -45,6 +45,7 @@
<Action name="stopmotion" />
<Separator />
<Action name="reload_clip" />
+ <Action name="duplicate_clip" />
<Action name="proxy_clip" />
<Menu name="clip_in_timeline"><text>Clip in Timeline</text>
</Menu>
@@ -73,6 +74,7 @@
<Menu name="clip" ><text>Clip</text>
<Menu name="marker_menu" ><text>Markers</text>
<Action name="add_clip_marker" />
+ <Action name="add_marker_guide_quickly" />
<Action name="edit_clip_marker" />
<Action name="delete_clip_marker" />
<Action name="delete_all_clip_markers" />
@@ -160,8 +162,12 @@
<Action name="mark_out" />
<Separator />
<Action name="monitor_fullscreen" />
- <Action name="mlt_interlace" />
- <Action name="mlt_interpolation" />
+ <Action name="monitor_overlay" />
+ <Menu name="monitor_config" ><text>Monitor config</text>
+ <Action name="mlt_interlace" />
+ <Action name="mlt_interpolation" />
+ <Action name="mlt_gamma" />
+ </Menu>
<Action name="switch_monitor" />
<Action name="insert_project_tree" />
<Action name="insert_timeline" />
diff --git a/data/org.kde.kdenlive.desktop b/data/org.kde.kdenlive.desktop
index 10f74ee..e82f0e9 100644
--- a/data/org.kde.kdenlive.desktop
+++ b/data/org.kde.kdenlive.desktop
@@ -4,27 +4,15 @@ Name=Kdenlive
Name[ast]=Kdenlive
Name[bs]=Kdenlive
Name[ca]=Kdenlive
-Name[[email protected]]=Kdenlive
Name[cs]=Kdenlive
-Name[da]=Kdenlive
Name[de]=Kdenlive
-Name[el]=Kdenlive
Name[en_GB]=Kdenlive
Name[es]=Kdenlive
-Name[et]=Kdenlive
Name[fi]=Kdenlive
Name[fr]=Kdenlive
-Name[ga]=Kdenlive
-Name[gl]=Kdenlive
-Name[hu]=Kdenlive
Name[it]=Kdenlive
Name[ja]=Kdenlive
-Name[km]=Kdenlive
-Name[lt]=Kdenlive
-Name[lv]=Kdenlive
-Name[mr]=के-डि-एनलाइव्ह
Name[nb]=Kdenlive
-Name[nds]=Kdenlive
Name[nl]=Kdenlive
Name[pl]=Kdenlive
Name[pt]=Kdenlive
@@ -35,35 +23,22 @@ Name[sk]=Kdenlive
Name[sl]=Kdenlive
Name[sv]=Kdenlive
Name[tr]=Kdenlive
-Name[ug]=Kdenlive
Name[uk]=Kdenlive
Name[x-test]=xxKdenlivexx
Name[zh_CN]=Kdenlive
-Name[zh_TW]=Kdenlive
GenericName=Video Editor
GenericName[ar]=محرّر فيديوهات
GenericName[ast]=Editor de videu
GenericName[bs]=Video uređivač
GenericName[ca]=Editor de vídeo
-GenericName[[email protected]]=Editor de vídeo
GenericName[cs]=Editor videí
-GenericName[da]=Videoredigering
GenericName[de]=Video-Editor
-GenericName[el]=Επεξεργαστής βίντεο
GenericName[en_GB]=Video Editor
GenericName[es]=Editor de video
-GenericName[et]=Videoredaktor
GenericName[fi]=Videomuokkain
GenericName[fr]=Éditeur vidéo
-GenericName[ga]=Eagarthóir Físe
-GenericName[gl]=Editor de vídeo
-GenericName[hu]=Videoszerkesztő
GenericName[it]=Editor video
GenericName[ja]=ビデオエディタ
-GenericName[km]=កម្មវិធី​កែសម្រួល​វីដេអូ
-GenericName[lt]=Video redaktorius
-GenericName[lv]=Video redaktors
-GenericName[mr]=व्हिडीओ संपादक
GenericName[nb]=Videoredigeringsprogram
GenericName[nl]=Video-bewerker
GenericName[pl]=Edytor wideo
@@ -78,30 +53,19 @@ GenericName[tr]=Video Düzenleyici
GenericName[uk]=Відеоредактор
GenericName[x-test]=xxVideo Editorxx
GenericName[zh_CN]=视频编辑器
-GenericName[zh_TW]=影像編輯器
Comment=Nonlinear video editor for KDE
Comment[ar]=محرّر فيديوهات غير خطّيّ لكدي
Comment[ast]=Editor de videu non llinial pa KDE
Comment[bs]=Nelinearni video uređivač za KDE
Comment[ca]=Editor de vídeo no lineal per al KDE
-Comment[[email protected]]=Editor de vídeo no lineal per al KDE
Comment[cs]=Nelineární editor videí pro KDE
-Comment[da]=Ikke-lineær videoredigering til KDE
Comment[de]=Nichtlinearer Video-Editor für KDE
-Comment[el]=Μη γραμμικός επεξεργαστής βίντεο για το KDE
Comment[en_GB]=Nonlinear video editor for KDE
Comment[es]=Editor no lineal de video para KDE
-Comment[et]=KDE mittelineaarne videoredaktor
Comment[fi]=Epälineaarinen videomuokkain KDE:lle
Comment[fr]=Éditeur vidéo non linéaire pour KDE
-Comment[gl]=Editor de vídeo non linear para KDE
-Comment[hu]=Nemlineáris videoszerkesztő a KDE-hez
Comment[it]=Editor di video non lineare per KDE
Comment[ja]=KDE 向けノンリニアビデオエディタ
-Comment[km]=កម្មវិធី​កែសម្រួល​វីដេអូ​មិន​លីនេអ៊ែរ​សម្រាប់ KDE
-Comment[lt]=Nelinijinis veido redaktorius skirtas KDE
-Comment[lv]=Nelineārais video redaktors KDE videi
-Comment[mr]=केडीई करिता अरेषीय व्हिडीओ संपादक
Comment[nb]=Videoredigeringsprogram for KDE med dataklipping
Comment[nl]=Niet-lineaire video-bewerker voor KDE
Comment[pl]=Nieliniowy edytor wideo dla KDE
@@ -117,7 +81,6 @@ Comment[ug]=KDE ئۈچۈن سىزىقسىز سىن تەھرىرلىگۈچ
Comment[uk]=Нелінійний редактор відео для KDE
Comment[x-test]=xxNonlinear video editor for KDExx
Comment[zh_CN]=KDE 的非线性视频编辑器
-Comment[zh_TW]=KDE 上的非線性影像編輯器
Type=Application
Exec=kdenlive %U
Icon=kdenlive
diff --git a/plugins/sampleplugin/CMakeLists.txt b/plugins/sampleplugin/CMakeLists.txt
index afc0c57..55e0a85 100644
--- a/plugins/sampleplugin/CMakeLists.txt
+++ b/plugins/sampleplugin/CMakeLists.txt
@@ -2,14 +2,13 @@ set(sampleplugin_SRCS sampleplugin.cpp)
ki18n_wrap_ui(sampleplugin_UIS countdown_ui.ui)
-kde4_add_plugin(kdenlive_sampleplugin WITH_PREFIX
+add_library(kdenlive_sampleplugin MODULE WITH_PREFIX
${sampleplugin_SRCS}
${sampleplugin_UIS}
)
add_definitions(${KDE4_DEFINITIONS})
-include_directories(${KDE4_INCLUDES})
include(${QT_USE_FILE})
@@ -19,9 +18,9 @@ add_definitions(-DQT_PLUGIN)
add_definitions(-DQT_SHARED)
target_link_libraries(kdenlive_sampleplugin
- ${KDE4_KDECORE_LIBS}
- ${KDE4_KDEUI_LIBS}
- ${KDE4_KIO_LIBS}
+ KF5::KDELibs4Support
+
+ KF5::KIOCore
${QT_LIBRARIES}
)
diff --git a/renderer/CMakeLists.txt b/renderer/CMakeLists.txt
index 74ec9b3..cf4ef53 100644
--- a/renderer/CMakeLists.txt
+++ b/renderer/CMakeLists.txt
@@ -9,7 +9,7 @@ set(kdenlive_render_SRCS
add_executable(kdenlive_render ${kdenlive_render_SRCS})
include_directories(
- ${QT_INCLUDES}
+
)
#include(${QT_USE_FILE})
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ca607d0..b3a9c5d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,9 +5,6 @@ set(QT_USE_QTSVG 1)
set(QT_USE_QTXML 1)
find_package(OpenGL REQUIRED)
-if(APPLE)
- find_package(SDL REQUIRED)
-endif(APPLE)
option(WITH_JogShuttle "Build Jog/Shuttle support" ON)
@@ -74,6 +71,8 @@ add_subdirectory(stopmotion)
add_subdirectory(titler)
add_subdirectory(utils)
add_subdirectory(onmonitoritems/rotoscoping)
+add_subdirectory(mltcontroller)
+add_subdirectory(bin)
list(APPEND kdenlive_SRCS
colortools.cpp
@@ -242,7 +241,7 @@ target_link_libraries(kdenlive
kiss_fft
)
-qt5_use_modules( kdenlive Script Widgets Concurrent OpenGL)
+qt5_use_modules( kdenlive Script Widgets Concurrent Qml Quick)
if(Q_WS_X11)
include_directories(${X11_Xlib_INCLUDE_PATH})
diff --git a/src/bin/CMakeLists.txt b/src/bin/CMakeLists.txt
new file mode 100644
index 0000000..497ab38
--- /dev/null
+++ b/src/bin/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(kdenlive_SRCS
+ ${kdenlive_SRCS}
+ bin/bin.cpp
+ bin/projectitemmodel.cpp
+ bin/abstractprojectitem.cpp
+ bin/projectclip.cpp
+ bin/projectsubclip.cpp
+ bin/projectfolder.cpp
+ bin/projectfolderup.cpp
+ bin/projectsortproxymodel.cpp
+ bin/bincommands.cpp
+ PARENT_SCOPE
+)
diff --git a/src/bin/abstractprojectitem.cpp b/src/bin/abstractprojectitem.cpp
new file mode 100644
index 0000000..9accf3c
--- /dev/null
+++ b/src/bin/abstractprojectitem.cpp
@@ -0,0 +1,259 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "abstractprojectitem.h"
+#include "bin.h"
+
+#include <QDomElement>
+#include <QVariant>
+#include <QPainter>
+#include <QDebug>
+
+
+AbstractProjectItem::AbstractProjectItem(PROJECTITEMTYPE type, const QString &id, AbstractProjectItem* parent) :
+ QObject()
+ , m_parent(NULL)
+ , m_id(id)
+ , m_isCurrent(false)
+ , m_jobProgress(0)
+ , m_jobType(AbstractClipJob::NOJOBTYPE)
+ , m_itemType(type)
+{
+}
+
+AbstractProjectItem::AbstractProjectItem(PROJECTITEMTYPE type, const QDomElement& description, AbstractProjectItem* parent) :
+ QObject()
+ , m_parent(NULL)
+ , m_id(description.attribute("id"))
+ , m_isCurrent(false)
+ , m_jobProgress(0)
+ , m_jobType(AbstractClipJob::NOJOBTYPE)
+ , m_itemType(type)
+{
+}
+
+AbstractProjectItem::~AbstractProjectItem()
+{
+ while (!isEmpty()) {
+ AbstractProjectItem *child = takeFirst();
+ removeChild(child);
+ delete child;
+ }
+}
+
+bool AbstractProjectItem::operator==(const AbstractProjectItem* projectItem) const
+{
+ // FIXME: only works for folders
+ bool equal = static_cast<const QList* const>(this) == static_cast<const QList* const>(projectItem);
+ equal &= m_parent == projectItem->parent();
+ return equal;
+}
+
+AbstractProjectItem* AbstractProjectItem::parent() const
+{
+ return m_parent;
+}
+
+const QString &AbstractProjectItem::clipId() const
+{
+ return m_id;
+}
+
+
+void AbstractProjectItem::setParent(AbstractProjectItem* parent)
+{
+ if (m_parent != parent) {
+ if (m_parent) {
+ m_parent->removeChild(this);
+ }
+
+ // Check if we are trying to delete the item
+ if (m_isCurrent && parent == NULL) {
+ /*if (bin()) {
+ bin()->setCurrentItem(NULL);
+ }*/
+ }
+ m_parent = parent;
+ QObject::setParent(m_parent);
+ }
+
+ if (m_parent && !m_parent->contains(this)) {
+ m_parent->addChild(this);
+ }
+}
+
+Bin* AbstractProjectItem::bin()
+{
+ if (m_parent) {
+ return m_parent->bin();
+ }
+ return NULL;
+}
+
+QPixmap AbstractProjectItem::roundedPixmap(const QPixmap &source)
+{
+ QPixmap pix(source.width(), source.height());
+ pix.fill(Qt::transparent);
+ QPainter p(&pix);
+ p.setRenderHint(QPainter::Antialiasing, true);
+ QPainterPath path;
+ path.addRoundedRect(0.5, 0.5, pix.width() - 1, pix.height() - 1, 4, 4);
+ p.setClipPath(path);
+ p.drawPixmap(0, 0, source);
+ p.end();
+ return pix;
+}
+
+void AbstractProjectItem::finishInsert(AbstractProjectItem* parent)
+{
+ /*if (m_parent && !m_parent->contains(this)) {
+ m_parent->addChild(this);
+ }*/
+ //bin()->emitItemReady(this);
+}
+
+void AbstractProjectItem::addChild(AbstractProjectItem* child)
+{
+ if (child && !contains(child)) {
+ bin()->emitAboutToAddItem(child);
+ append(child);
+ bin()->emitItemAdded(child);
+ }
+}
+
+void AbstractProjectItem::removeChild(AbstractProjectItem* child)
+{
+ if (child && contains(child)) {
+ bin()->emitAboutToRemoveItem(child);
+ removeAll(child);
+ bin()->emitItemRemoved(child);
+ }
+}
+
+int AbstractProjectItem::index() const
+{
+ if (m_parent) {
+ return m_parent->indexOf(const_cast<AbstractProjectItem*>(this));
+ }
+
+ return 0;
+}
+
+AbstractProjectItem::PROJECTITEMTYPE AbstractProjectItem::itemType() const
+{
+ return m_itemType;
+}
+
+
+QVariant AbstractProjectItem::data(DataType type) const
+{
+ QVariant data;
+ switch (type) {
+ case DataName:
+ data = QVariant(m_name);
+ break;
+ case DataDescription:
+ data = QVariant(m_description);
+ break;
+ case DataThumbnail:
+ data = QVariant(m_thumbnail);
+ break;
+ case DataDuration:
+ data = QVariant(m_duration);
+ break;
+ case ItemTypeRole:
+ data = QVariant(m_itemType);
+ break;
+ case JobType:
+ data = QVariant(m_jobType);
+ break;
+ case JobProgress:
+ data = QVariant(m_jobProgress);
+ break;
+ case JobMessage:
+ data = QVariant(m_jobMessage);
+ break;
+ case ClipStatus:
+ data = QVariant(m_clipStatus);
+ break;
+ case ClipToolTip:
+ data = QVariant(getToolTip());
+ break;
+ case DataId:
+ data = QVariant(m_id);
+ break;
+ default:
+ break;
+ }
+ return data;
+}
+
+int AbstractProjectItem::supportedDataCount() const
+{
+ return 1;
+}
+
+QString AbstractProjectItem::name() const
+{
+ return m_name;
+}
+
+void AbstractProjectItem::setName(const QString& name)
+{
+ m_name = name;
+}
+
+QString AbstractProjectItem::description() const
+{
+ return m_description;
+}
+
+void AbstractProjectItem::setDescription(const QString& description)
+{
+ m_description = description;
+}
+
+void AbstractProjectItem::setCurrent(bool current, bool notify)
+{
+ if (m_isCurrent != current)
+ {
+ m_isCurrent = current;
+ if (!notify)
+ return;
+ /*if (current) {
+ bin()->setCurrentItem(this);
+ } else {
+ bin()->setCurrentItem(NULL);
+ }*/
+ }
+}
+
+QPoint AbstractProjectItem::zone() const
+{
+ return QPoint();
+}
+
+void AbstractProjectItem::setClipStatus(CLIPSTATUS status)
+{
+ m_clipStatus = status;
+}
+
diff --git a/src/bin/abstractprojectitem.h b/src/bin/abstractprojectitem.h
new file mode 100644
index 0000000..4f7e2a6
--- /dev/null
+++ b/src/bin/abstractprojectitem.h
@@ -0,0 +1,200 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ABSTRACTPROJECTITEM_H
+#define ABSTRACTPROJECTITEM_H
+
+#include "project/jobs/abstractclipjob.h"
+
+#include <QObject>
+#include <QPixmap>
+
+
+class ProjectClip;
+class ProjectFolder;
+class Bin;
+class QDomElement;
+class QDomDocument;
+
+
+
+/**
+ * @class AbstractProjectItem
+ * @brief Base class for all project items (clips, folders, ...).
+ *
+ * Project items are stored in a tree like structure ...
+ */
+
+
+class AbstractProjectItem : public QObject, public QList<AbstractProjectItem *>
+{
+ Q_OBJECT
+
+public:
+
+ enum PROJECTITEMTYPE {
+ FolderUpItem = 0,
+ FolderItem = 1,
+ ClipItem = 2,
+ SubClipItem = 3
+ };
+
+ /**
+ * @brief Constructor.
+ * @param parent parent this item should be added to
+ */
+ AbstractProjectItem(PROJECTITEMTYPE type, const QString &id, AbstractProjectItem *parent = 0);
+ /**
+ * @brief Creates a project item upon project load.
+ * @param description element for this item.
+ * @param parent parent this item should be added to
+ *
+ * We try to read the attributes "name" and "description"
+ */
+ AbstractProjectItem(PROJECTITEMTYPE type, const QDomElement &description, AbstractProjectItem* parent = 0);
+ virtual ~AbstractProjectItem();
+
+ bool operator==(const AbstractProjectItem *projectItem) const;
+
+ /** @brief Returns a pointer to the parent item (or NULL). */
+ AbstractProjectItem *parent() const;
+ /** @brief Removes the item from its current parent and adds it as a child to @param parent. */
+ virtual void setParent(AbstractProjectItem *parent);
+ void finishInsert(AbstractProjectItem* parent);
+
+ /**
+ * @brief Adds a new child item and notifies the bin model about it (before and after).
+ * @param child project item which should be added as a child
+ *
+ * This function is called by setParent.
+ */
+ virtual void addChild(AbstractProjectItem *child);
+
+ /**
+ * @brief Removes a child item and notifies the bin model about it (before and after).
+ * @param child project which sould be removed from the child list
+ *
+ * This function is called when a child's parent is changed through setParent
+ */
+ virtual void removeChild(AbstractProjectItem *child);
+
+ /** @brief Returns a pointer to the bin model this item is child of (through its parent item). */
+ virtual Bin *bin();
+
+ /** @brief Returns the index this item has in its parent's child list. */
+ int index() const;
+
+ /** @brief Returns the type of this item (folder, clip, subclip, etc). */
+ PROJECTITEMTYPE itemType() const;
+
+ /** @brief Used to search for a clip with a specific id. */
+ virtual ProjectClip *clip(const QString &id) = 0;
+ /** @brief Used to search for a folder with a specific id. */
+ virtual ProjectFolder* folder(const QString &id) = 0;
+ virtual ProjectClip *clipAt(int ix) = 0;
+
+ /** @brief Returns the clip's id. */
+ const QString &clipId() const;
+ virtual QPoint zone() const;
+
+ enum DataType {
+ DataName = Qt::DisplayRole,
+ DataThumbnail = Qt::DecorationRole,
+ DataId = Qt::UserRole,
+ DataDescription,
+ DataDate,
+ IconOverlay,
+ ItemTypeRole,
+ DataDuration,
+ JobType,
+ JobProgress,
+ JobMessage,
+ ClipStatus,
+ ClipToolTip = Qt::ToolTipRole
+ };
+
+ enum CLIPSTATUS {
+ StatusReady = 0,
+ StatusMissing,
+ StatusWaiting
+ };
+
+ void setClipStatus(AbstractProjectItem::CLIPSTATUS status);
+
+ /** @brief Returns the data that describes this item.
+ * @param type type of data to return
+ *
+ * This function is necessary for interaction with ProjectItemModel.
+ */
+ virtual QVariant data(DataType type) const;
+
+ /**
+ * @brief Returns the amount of different types of data this item supports.
+ *
+ * This base class supports only DataName and DataDescription, so the return value is always 2.
+ * This function is necessary for interaction with ProjectItemModel.
+ */
+ virtual int supportedDataCount() const;
+
+ /** @brief Returns the (displayable) name of this item. */
+ QString name() const;
+ /** @brief Sets a new (displayable) name. */
+ virtual void setName(const QString &name);
+
+ /** @brief Returns the (displayable) description of this item. */
+ QString description() const;
+ /** @brief Sets a new description. */
+ virtual void setDescription(const QString &description);
+
+ /** @brief Flags this item as being current (or not) and notifies the bin model about it. */
+ virtual void setCurrent(bool current, bool notify = true);
+
+ virtual QDomElement toXml(QDomDocument &document) = 0;
+ virtual QString getToolTip() const = 0;
+ virtual bool rename(const QString &name) = 0;
+
+signals:
+ void childAdded(AbstractProjectItem *child);
+ void aboutToRemoveChild(AbstractProjectItem *child);
+
+protected:
+ AbstractProjectItem *m_parent;
+ QString m_name;
+ QString m_description;
+ QIcon m_thumbnail;
+ QString m_duration;
+ QString m_id;
+ CLIPSTATUS m_clipStatus;
+ AbstractClipJob::JOBTYPE m_jobType;
+ int m_jobProgress;
+
+ QString m_jobMessage;
+ PROJECTITEMTYPE m_itemType;
+
+ /** @brief Returns a rounded border pixmap from the @param source pixmap. */
+ QPixmap roundedPixmap(const QPixmap &source);
+
+private:
+ bool m_isCurrent;
+};
+
+#endif
diff --git a/src/bin/bin.cpp b/src/bin/bin.cpp
new file mode 100644
index 0000000..eb422cc
--- /dev/null
+++ b/src/bin/bin.cpp
@@ -0,0 +1,2076 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "bin.h"
+#include "mainwindow.h"
+#include "projectitemmodel.h"
+#include "projectclip.h"
+#include "projectsubclip.h"
+#include "projectfolder.h"
+#include "projectfolderup.h"
+#include "kdenlivesettings.h"
+#include "project/projectmanager.h"
+#include "project/clipmanager.h"
+#include "project/jobs/jobmanager.h"
+#include "monitor/monitor.h"
+#include "doc/kdenlivedoc.h"
+#include "dialogs/clipcreationdialog.h"
+#include "titler/titlewidget.h"
+#include "core.h"
+#include "mltcontroller/clipcontroller.h"
+#include "mltcontroller/clippropertiescontroller.h"
+#include "project/projectcommands.h"
+#include "projectsortproxymodel.h"
+#include "bincommands.h"
+#include "mlt++/Mlt.h"
+
+#include <KToolBar>
+#include <KColorScheme>
+#include <KMessageBox>
+#include <KSplitterCollapserButton>
+
+
+#include <QDialogButtonBox>
+#include <QVBoxLayout>
+#include <QTimeLine>
+#include <QSlider>
+#include <QMenu>
+#include <QDebug>
+#include <QUndoCommand>
+
+
+BinMessageWidget::BinMessageWidget(QWidget *parent) : KMessageWidget(parent) {}
+BinMessageWidget::BinMessageWidget(const QString &text, QWidget *parent) : KMessageWidget(text, parent) {}
+
+
+bool BinMessageWidget::event(QEvent* ev) {
+ if (ev->type() == QEvent::Hide || ev->type() == QEvent::Close) emit messageClosing();
+ return KMessageWidget::event(ev);
+}
+
+SmallJobLabel::SmallJobLabel(QWidget *parent) : QPushButton(parent)
+{
+ setFixedWidth(0);
+ setFlat(true);
+ m_timeLine = new QTimeLine(500, this);
+ QObject::connect(m_timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(slotTimeLineChanged(qreal)));
+ QObject::connect(m_timeLine, SIGNAL(finished()), this, SLOT(slotTimeLineFinished()));
+ hide();
+}
+
+const QString SmallJobLabel::getStyleSheet(const QPalette &p)
+{
+ KColorScheme scheme(p.currentColorGroup(), KColorScheme::Window, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
+ QColor bg = scheme.background(KColorScheme::LinkBackground).color();
+ QColor fg = scheme.foreground(KColorScheme::LinkText).color();
+ QString style = QString("QPushButton {margin:3px;padding:2px;background-color: rgb(%1, %2, %3);border-radius: 4px;border: none;color: rgb(%4, %5, %6)}").arg(bg.red()).arg(bg.green()).arg(bg.blue()).arg(fg.red()).arg(fg.green()).arg(fg.blue());
+
+ bg = scheme.background(KColorScheme::ActiveBackground).color();
+ fg = scheme.foreground(KColorScheme::ActiveText).color();
+ style.append(QString("\nQPushButton:hover {margin:3px;padding:2px;background-color: rgb(%1, %2, %3);border-radius: 4px;border: none;color: rgb(%4, %5, %6)}").arg(bg.red()).arg(bg.green()).arg(bg.blue()).arg(fg.red()).arg(fg.green()).arg(fg.blue()));
+
+ return style;
+}
+
+void SmallJobLabel::setAction(QAction *action)
+{
+ m_action = action;
+}
+
+void SmallJobLabel::slotTimeLineChanged(qreal value)
+{
+ setFixedWidth(qMin(value * 2, qreal(1.0)) * sizeHint().width());
+ update();
+}
+
+void SmallJobLabel::slotTimeLineFinished()
+{
+ if (m_timeLine->direction() == QTimeLine::Forward) {
+ // Show
+ m_action->setVisible(true);
+ } else {
+ // Hide
+ m_action->setVisible(false);
+ setText(QString());
+ }
+}
+
+void SmallJobLabel::slotSetJobCount(int jobCount)
+{
+ if (jobCount > 0) {
+ // prepare animation
+ setText(i18np("%1 job", "%1 jobs", jobCount));
+ setToolTip(i18np("%1 pending job", "%1 pending jobs", jobCount));
+
+ //if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
+ if (style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) {
+ setFixedWidth(sizeHint().width());
+ m_action->setVisible(true);
+ return;
+ }
+
+ if (m_action->isVisible()) {
+ setFixedWidth(sizeHint().width());
+ update();
+ return;
+ }
+
+ setFixedWidth(0);
+ m_action->setVisible(true);
+ int wantedWidth = sizeHint().width();
+ setGeometry(-wantedWidth, 0, wantedWidth, height());
+ m_timeLine->setDirection(QTimeLine::Forward);
+ if (m_timeLine->state() == QTimeLine::NotRunning) {
+ m_timeLine->start();
+ }
+ }
+ else {
+ //if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
+ if (style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) {
+ setFixedWidth(0);
+ m_action->setVisible(false);
+ return;
+ }
+ // hide
+ m_timeLine->setDirection(QTimeLine::Backward);
+ if (m_timeLine->state() == QTimeLine::NotRunning) {
+ m_timeLine->start();
+ }
+ }
+}
+
+EventEater::EventEater(QObject *parent) : QObject(parent)
+{
+}
+
+bool EventEater::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::MouseButtonPress) {
+ emit focusClipMonitor();
+ return QObject::eventFilter(obj, event);
+ }
+ if (event->type() == QEvent::MouseButtonDblClick) {
+ QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
+ QAbstractItemView *view = qobject_cast<QAbstractItemView*>(obj->parent());
+ if (view) {
+ QModelIndex idx = view->indexAt(mouseEvent->pos());
+ if (!idx.isValid()) {
+ // User double clicked on empty area
+ emit addClip();
+ }
+ else {
+ /*AbstractProjectItem *item = static_cast<AbstractProjectItem*>(idx.internalPointer());
+ if (item->itemType() == AbstractProjectItem::FolderItem) qDebug()<<"***************** FLD CLK ****************";*/
+ emit itemDoubleClicked(idx, mouseEvent->pos());
+ //return QObject::eventFilter(obj, event);
+ }
+ }
+ else {
+ qDebug()<<" +++++++ NO VIEW-------!!";
+ }
+ return true;
+ } else {
+ return QObject::eventFilter(obj, event);
+ }
+}
+
+
+Bin::Bin(QWidget* parent) :
+ QWidget(parent)
+ , m_itemModel(NULL)
+ , m_itemView(NULL)
+ , m_listType((BinViewType) KdenliveSettings::binMode())
+ , m_jobManager(NULL)
+ , m_rootFolder(NULL)
+ , m_folderUp(NULL)
+ , m_doc(NULL)
+ , m_iconSize(160, 90)
+ , m_blankThumb()
+ , m_propertiesPanel(NULL)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ // Create toolbar for buttons
+ m_toolbar = new KToolBar(this);
+ m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
+ m_toolbar->setIconDimensions(style()->pixelMetric(QStyle::PM_SmallIconSize));
+ layout->addWidget(m_toolbar);
+
+ // Search line
+ m_proxyModel = new ProjectSortProxyModel(this);
+ m_proxyModel->setDynamicSortFilter(true);
+ QLineEdit *searchLine = new QLineEdit(this);
+ searchLine->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
+ searchLine->setClearButtonEnabled(true);
+ connect(searchLine, SIGNAL(textChanged(const QString &)), m_proxyModel, SLOT(slotSetSearchString(const QString &)));
+ m_toolbar->addWidget(searchLine);
+
+ // small info button for pending jobs
+ m_infoLabel = new SmallJobLabel(this);
+ m_infoLabel->setStyleSheet(SmallJobLabel::getStyleSheet(palette()));
+ QAction *infoAction = m_toolbar->addWidget(m_infoLabel);
+ m_jobsMenu = new QMenu(this);
+ connect(m_jobsMenu, SIGNAL(aboutToShow()), this, SLOT(slotPrepareJobsMenu()));
+ m_cancelJobs = new QAction(i18n("Cancel All Jobs"), this);
+ m_cancelJobs->setCheckable(false);
+ m_discardCurrentClipJobs = new QAction(i18n("Cancel Current Clip Jobs"), this);
+ m_discardCurrentClipJobs->setCheckable(false);
+ m_jobsMenu->addAction(m_cancelJobs);
+ m_jobsMenu->addAction(m_discardCurrentClipJobs);
+ m_infoLabel->setMenu(m_jobsMenu);
+
+ m_infoLabel->setAction(infoAction);
+
+ // Build item view model
+ m_itemModel = new ProjectItemModel(this);
+
+ // Connect models
+ m_proxyModel->setSourceModel(m_itemModel);
+ connect(m_itemModel, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)), m_proxyModel, SLOT(slotDataChanged(const QModelIndex&,const
+ QModelIndex&)));
+ connect(m_itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)));
+ connect(m_itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)));
+ connect(m_proxyModel, SIGNAL(selectModel(QModelIndex)), this, SLOT(selectProxyModel(QModelIndex)));
+ connect(m_itemModel, SIGNAL(itemDropped(QStringList, const QModelIndex &)), this, SLOT(slotItemDropped(QStringList, const QModelIndex &)));
+ connect(m_itemModel, SIGNAL(itemDropped(const QList<QUrl>&, const QModelIndex &)), this, SLOT(slotItemDropped(const QList<QUrl>&, const QModelIndex &)));
+ connect(m_itemModel, SIGNAL(effectDropped(QString, const QModelIndex &)), this, SLOT(slotEffectDropped(QString, const QModelIndex &)));
+ connect(m_itemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(slotItemEdited(QModelIndex,QModelIndex,QVector<int>)));
+ connect(m_itemModel, SIGNAL(addClipCut(QString,int,int)), this, SLOT(slotAddClipCut(QString,int,int)));
+
+ // Zoom slider
+ m_slider = new QSlider(Qt::Horizontal, this);
+ m_slider->setMaximumWidth(100);
+ m_slider->setMinimumWidth(40);
+ m_slider->setRange(0, 10);
+ // TODO: fix view zoom on startup
+ m_slider->setValue(KdenliveSettings::bin_zoom());
+ connect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(slotSetIconSize(int)));
+ QWidgetAction * widgetslider = new QWidgetAction(this);
+ widgetslider->setDefaultWidget(m_slider);
+
+ // View type
+ KSelectAction *listType = new KSelectAction(QIcon::fromTheme("view-list-tree"), i18n("View Mode"), this);
+ QAction *treeViewAction = listType->addAction(QIcon::fromTheme("view-list-tree"), i18n("Tree View"));
+ treeViewAction->setData(BinTreeView);
+ if (m_listType == treeViewAction->data().toInt()) {
+ listType->setCurrentAction(treeViewAction);
+ }
+ QAction *iconViewAction = listType->addAction(QIcon::fromTheme("view-list-icons"), i18n("Icon View"));
+ iconViewAction->setData(BinIconView);
+ if (m_listType == iconViewAction->data().toInt()) {
+ listType->setCurrentAction(iconViewAction);
+ }
+ listType->setToolBarMode(KSelectAction::MenuMode);
+ connect(listType, SIGNAL(triggered(QAction*)), this, SLOT(slotInitView(QAction*)));
+
+ // Settings menu
+ QMenu *settingsMenu = new QMenu(i18n("Settings"), this);
+ settingsMenu->addAction(listType);
+ QMenu *sliderMenu = new QMenu(i18n("Zoom"), this);
+ sliderMenu->setIcon(QIcon::fromTheme("file-zoom-in"));
+ sliderMenu->addAction(widgetslider);
+ settingsMenu->addMenu(sliderMenu);
+ QToolButton *button = new QToolButton;
+ button->setIcon(QIcon::fromTheme("configure"));
+ button->setMenu(settingsMenu);
+ button->setPopupMode(QToolButton::InstantPopup);
+ m_toolbar->addWidget(button);
+
+ m_eventEater = new EventEater(this);
+ connect(m_eventEater, SIGNAL(addClip()), this, SLOT(slotAddClip()));
+ connect(m_eventEater, SIGNAL(deleteSelectedClips()), this, SLOT(slotDeleteClip()));
+ m_binTreeViewDelegate = new BinItemDelegate(this);
+ //connect(pCore->projectManager(), SIGNAL(projectOpened(Project*)), this, SLOT(setProject(Project*)));
+ m_splitter = new QSplitter(this);
+ m_headerInfo = QByteArray::fromBase64(KdenliveSettings::treeviewheaders().toLatin1());
+
+ connect(m_eventEater, SIGNAL(itemDoubleClicked(QModelIndex,QPoint)), this, SLOT(slotItemDoubleClicked(QModelIndex,QPoint)), Qt::UniqueConnection);
+
+ layout->addWidget(m_splitter);
+ m_propertiesPanel = new QWidget(m_splitter);
+ m_splitter->addWidget(m_propertiesPanel);
+ m_collapser = new KSplitterCollapserButton(m_propertiesPanel, m_splitter);
+ connect(m_collapser, SIGNAL(clicked(bool)), this, SLOT(slotRefreshClipProperties()));
+
+ // Info widget for failed jobs, other errors
+ m_infoMessage = new BinMessageWidget;
+ layout->addWidget(m_infoMessage);
+ m_infoMessage->setCloseButtonVisible(true);
+ connect(m_infoMessage, SIGNAL(messageClosing()), this, SLOT(slotResetInfoMessage()));
+ //m_infoMessage->setWordWrap(true);
+ m_infoMessage->hide();
+ m_logAction = new QAction(i18n("Show Log"), this);
+ m_logAction->setCheckable(false);
+ connect(m_logAction, SIGNAL(triggered()), this, SLOT(slotShowJobLog()));
+}
+
+Bin::~Bin()
+{
+ delete m_jobManager;
+ delete m_infoMessage;
+}
+
+void Bin::slotSaveHeaders()
+{
+ if (m_itemView && m_listType == BinTreeView) {
+ // save current treeview state (column width)
+ QTreeView *view = static_cast<QTreeView*>(m_itemView);
+ m_headerInfo = view->header()->saveState();
+ KdenliveSettings::setTreeviewheaders(m_headerInfo.toBase64());
+ }
+}
+
+Monitor *Bin::monitor()
+{
+ return m_monitor;
+}
+
+const QStringList Bin::getFolderInfo(QModelIndex selectedIx)
+{
+ QStringList folderInfo;
+ QModelIndexList indexes;
+ if (selectedIx.isValid()) {
+ indexes << selectedIx;
+ } else {
+ indexes = m_proxyModel->selectionModel()->selectedIndexes();
+ }
+ if (indexes.isEmpty()) {
+ return folderInfo;
+ }
+ QModelIndex ix = indexes.first();
+ if (ix.isValid() && m_proxyModel->selectionModel()->isSelected(ix)) {
+ AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(m_proxyModel->mapToSource(ix).internalPointer());
+ while (currentItem->itemType() != AbstractProjectItem::FolderItem) {
+ currentItem = currentItem->parent();
+ }
+ if (currentItem == m_rootFolder) {
+ // clip was added to root folder, leave folder info empty
+ } else {
+ folderInfo << currentItem->clipId();
+ folderInfo << currentItem->name();
+ }
+ }
+ return folderInfo;
+}
+
+void Bin::slotAddClip()
+{
+ // Check if we are in a folder
+ QStringList folderInfo = getFolderInfo();
+ ClipCreationDialog::createClipsCommand(m_doc, folderInfo, this);
+}
+
+void Bin::deleteClip(const QString &id)
+{
+ if (m_monitor->activeClipId() == id) {
+ m_monitor->openClip(NULL);
+ }
+ ProjectClip *clip = m_rootFolder->clip(id);
+ if (!clip) return;
+ m_jobManager->discardJobs(id);
+ AbstractProjectItem *parent = clip->parent();
+ parent->removeChild(clip);
+ delete clip;
+ if (m_listType == BinTreeView) {
+ ((QTreeView *)m_itemView)->resizeColumnToContents(0);
+ }
+}
+
+ProjectClip *Bin::getFirstSelectedClip()
+{
+ QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
+ if (indexes.isEmpty()) {
+ return NULL;
+ }
+ foreach (const QModelIndex &ix, indexes) {
+ AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
+ ProjectClip *clip = qobject_cast<ProjectClip*>(item);
+ if (clip) {
+ return clip;
+ }
+ }
+ return NULL;
+}
+
+void Bin::slotDeleteClip()
+{
+ QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
+ QStringList clipIds;
+ QStringList subClipIds;
+ QStringList foldersIds;
+ foreach (const QModelIndex &ix, indexes) {
+ if (!ix.isValid()) continue;
+ AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
+ if (!item) continue;
+ AbstractProjectItem::PROJECTITEMTYPE type = item->itemType();
+ switch (type) {
+ case AbstractProjectItem::ClipItem:
+ clipIds << item->clipId();
+ break;
+ case AbstractProjectItem::FolderItem:
+ foldersIds << item->clipId();
+ break;
+ case AbstractProjectItem::SubClipItem:
+ //TODO
+ subClipIds << item->clipId();
+ break;
+ default:
+ break;
+ }
+ }
+ // For some reason, we get duplicates, which is not expected
+ //ids.removeDuplicates();
+ m_doc->clipManager()->deleteProjectItems(clipIds, foldersIds);
+}
+
+void Bin::slotReloadClip()
+{
+ QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
+ foreach (const QModelIndex &ix, indexes) {
+ if (!ix.isValid()) {
+ continue;
+ }
+ AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
+ ProjectClip *currentItem = qobject_cast<ProjectClip*>(item);
+ if (currentItem) {
+ m_monitor->openClip(NULL);
+ QDomDocument doc;
+ QDomElement xml = currentItem->toXml(doc);
+ if (!xml.isNull()) {
+ currentItem->setClipStatus(AbstractProjectItem::StatusWaiting);
+ // We need to set a temporary id before all outdated producers are replaced;
+ m_doc->renderer()->getFileProperties(xml, currentItem->clipId(), 150, true);
+ }
+ }
+ }
+}
+
+void Bin::slotDuplicateClip()
+{
+ QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
+ foreach (const QModelIndex &ix, indexes) {
+ if (!ix.isValid()) {
+ continue;
+ }
+ AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
+ ProjectClip *currentItem = qobject_cast<ProjectClip*>(item);
+ if (currentItem) {
+ QStringList folderInfo = getFolderInfo(ix);
+ QDomDocument doc;
+ QDomElement xml = currentItem->toXml(doc);
+ if (!xml.isNull()) ClipCreationDialog::createClipFromXml(m_doc, xml, folderInfo, this);
+ }
+ }
+}
+
+ProjectFolder *Bin::rootFolder()
+{
+ return m_rootFolder;
+}
+
+double Bin::projectRatio()
+{
+ return m_doc->dar();
+}
+
+QUrl Bin::projectFolder() const
+{
+ return m_doc->projectFolder();
+}
+
+void Bin::setMonitor(Monitor *monitor)
+{
+ m_monitor = monitor;
+ connect(m_eventEater, SIGNAL(focusClipMonitor()), m_monitor, SLOT(slotActivateMonitor()), Qt::UniqueConnection);
+}
+
+int Bin::getFreeFolderId()
+{
+ return m_folderCounter++;
+}
+
+int Bin::getFreeClipId()
+{
+ return m_clipCounter++;
+}
+
+int Bin::lastClipId() const
+{
+ return qMax(0, m_clipCounter - 1);
+}
+
+void Bin::setDocument(KdenliveDoc* project)
+{
+ // Remove clip from Bin's monitor
+ m_monitor->openClip(NULL);
+ closeEditing();
+ setEnabled(false);
+
+ // Cleanup previous project
+ if (m_rootFolder) {
+ while (!m_rootFolder->isEmpty()) {
+ AbstractProjectItem *child = m_rootFolder->at(0);
+ m_rootFolder->removeChild(child);
+ delete child;
+ }
+ }
+ delete m_rootFolder;
+ delete m_itemView;
+ m_itemView = NULL;
+ delete m_jobManager;
+ m_clipCounter = 1;
+ m_folderCounter = 1;
+ m_doc = project;
+ int iconHeight = QFontInfo(font()).pixelSize() * 3.5;
+ m_iconSize = QSize(iconHeight * m_doc->dar(), iconHeight);
+ m_jobManager = new JobManager(this, project->fps());
+ m_rootFolder = new ProjectFolder(this);
+ setEnabled(true);
+ connect(this, SIGNAL(producerReady(QString)), m_doc->renderer(), SLOT(slotProcessingDone(QString)));
+ connect(m_jobManager, SIGNAL(addClip(QString)), this, SLOT(slotAddUrl(QString)));
+ connect(m_proxyAction, SIGNAL(toggled(bool)), m_doc, SLOT(slotProxyCurrentItem(bool)));
+ connect(m_jobManager, SIGNAL(jobCount(int)), m_infoLabel, SLOT(slotSetJobCount(int)));
+ connect(m_discardCurrentClipJobs, SIGNAL(triggered()), m_jobManager, SLOT(slotDiscardClipJobs()));
+ connect(m_cancelJobs, SIGNAL(triggered()), m_jobManager, SLOT(slotCancelJobs()));
+ connect(m_jobManager, SIGNAL(updateJobStatus(QString,int,int,QString,QString,QString)), this, SLOT(slotUpdateJobStatus(QString,int,int,QString,QString,QString)));
+
+ connect(m_jobManager, SIGNAL(gotFilterJobResults(QString,int,int,stringMap,stringMap)), this, SLOT(slotGotFilterJobResults(QString,int,int,stringMap,stringMap)));
+
+ //connect(m_itemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), m_itemView
+ //connect(m_itemModel, SIGNAL(updateCurrentItem()), this, SLOT(autoSelect()));
+ slotInitView(NULL);
+ autoSelect();
+}
+
+void Bin::slotAddUrl(QString url, QMap <QString, QString> data)
+{
+ QList <QUrl>urls;
+ urls << QUrl::fromLocalFile(url);
+ QStringList folderInfo = getFolderInfo();
+ ClipCreationDialog::createClipsCommand(m_doc, urls, folderInfo, this, data);
+}
+
+void Bin::createClip(QDomElement xml)
+{
+ // Check if clip should be in a folder
+ QString groupId = ProjectClip::getXmlProperty(xml, "kdenlive:folderid");
+ ProjectFolder *parentFolder = m_rootFolder;
+ if (!groupId.isEmpty()) {
+ parentFolder = m_rootFolder->folder(groupId);
+ if (!parentFolder) {
+ // parent folder does not exist, put in root folder
+ parentFolder = m_rootFolder;
+ }
+ }
+ ProjectClip *newItem = new ProjectClip(xml, m_blankThumb, parentFolder);
+ if (m_listType == BinTreeView) {
+ ((QTreeView *)m_itemView)->resizeColumnToContents(0);
+ }
+}
+
+void Bin::slotAddFolder()
+{
+ // Check parent item
+ QModelIndex ix = m_proxyModel->selectionModel()->currentIndex();
+ ProjectFolder *parentFolder = m_rootFolder;
+ if (ix.isValid() && m_proxyModel->selectionModel()->isSelected(ix)) {
+ AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(m_proxyModel->mapToSource(ix).internalPointer());
+ while (currentItem->itemType() != AbstractProjectItem::FolderItem) {
+ currentItem = currentItem->parent();
+ }
+ if (currentItem->itemType() == AbstractProjectItem::FolderItem) {
+ parentFolder = qobject_cast<ProjectFolder *>(currentItem);
+ }
+ }
+ QString newId = QString::number(getFreeFolderId());
+ AddBinFolderCommand *command = new AddBinFolderCommand(this, newId, i18n("Folder"), parentFolder->clipId());
+ m_doc->commandStack()->push(command);
+
+ // Edit folder name
+ ix = getIndexForId(newId, true);
+ if (ix.isValid()) {
+ m_proxyModel->selectionModel()->select(m_proxyModel->mapFromSource(ix), QItemSelectionModel::ClearAndSelect);
+ m_itemView->edit(m_proxyModel->mapFromSource(ix));
+ }
+}
+
+QModelIndex Bin::getIndexForId(const QString &id, bool folderWanted) const
+{
+ QModelIndexList items = m_itemModel->match(m_itemModel->index(0, 0), AbstractProjectItem::DataId, QVariant::fromValue(id), 2, Qt::MatchRecursive);
+ for (int i = 0; i < items.count(); i++) {
+ AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(items.at(i).internalPointer());
+ AbstractProjectItem::PROJECTITEMTYPE type = currentItem->itemType();
+ if (folderWanted && type == AbstractProjectItem::FolderItem) {
+ // We found our folder
+ return items.at(i);
+ }
+ else if (!folderWanted && type == AbstractProjectItem::ClipItem) {
+ // We found our clip
+ return items.at(i);
+ }
+ }
+ return QModelIndex();
+}
+
+void Bin::selectClipById(const QString &id)
+{
+ QModelIndex ix = getIndexForId(id, false);
+ if (ix.isValid()) {
+ m_proxyModel->selectionModel()->select(m_proxyModel->mapFromSource(ix), QItemSelectionModel::ClearAndSelect);
+ }
+}
+
+void Bin::doAddFolder(const QString &id, const QString &name, const QString &parentId)
+{
+ ProjectFolder *parentFolder = m_rootFolder->folder(parentId);
+ if (!parentFolder) {
+ qDebug()<<" / / ERROR IN PARENT FOLDER";
+ return;
+ }
+ ProjectFolder *newItem = new ProjectFolder(id, name, parentFolder);
+ emit storeFolder(id, parentId, QString(), name);
+}
+
+void Bin::renameFolder(const QString &id, const QString &name)
+{
+ ProjectFolder *folder = m_rootFolder->folder(id);
+ if (!folder || !folder->parent()) {
+ qDebug()<<" / / ERROR IN PARENT FOLDER";
+ return;
+ }
+ folder->setName(name);
+ emit itemUpdated(folder);
+ emit storeFolder(id, folder->parent()->clipId(), QString(), name);
+}
+
+
+void Bin::slotLoadFolders(QMap<QString,QString> foldersData)
+{
+ // Folder parent is saved in folderId, separated by a dot. for example "1.3" means parent folder id is "1" and new folder id is "3".
+ ProjectFolder *parentFolder = m_rootFolder;
+ QStringList folderIds = foldersData.keys();
+ for (int i = 0; i < folderIds.count(); i++) {
+ QString id = folderIds.at(i);
+ QString parentId = id.section(".", 0, 0);
+ if (parentId == "-1") {
+ parentFolder = m_rootFolder;
+ }
+ else {
+ // This is a sub-folder
+ parentFolder = m_rootFolder->folder(parentId);
+ if (parentFolder == m_rootFolder) {
+ // parent folder not yet created, create unnamed placeholder
+ parentFolder = new ProjectFolder(parentId, QString(), parentFolder);
+ }
+ }
+ // parent was found, create our folder
+ QString folderId = id.section(".", 1, 1);
+ int numericId = folderId.toInt();
+ if (numericId >= m_folderCounter) m_folderCounter = numericId + 1;
+ // Check if placeholder folder was created
+ ProjectFolder *placeHolder = parentFolder->folder(folderId);
+ if (placeHolder) {
+ // Rename placeholder
+ placeHolder->setName(foldersData.value(id));
+ }
+ else {
+ // Create new folder
+ ProjectFolder *newItem = new ProjectFolder(folderId, foldersData.value(id), parentFolder);
+ }
+ }
+}
+
+void Bin::removeFolder(const QString &id, QUndoCommand *deleteCommand)
+{
+ // Check parent item
+ ProjectFolder *folder = m_rootFolder->folder(id);
+ AbstractProjectItem *parent = folder->parent();
+ new AddBinFolderCommand(this, folder->clipId(), folder->name(), parent->clipId(), true, deleteCommand);
+}
+
+void Bin::doRemoveFolder(const QString &id)
+{
+ ProjectFolder *folder = m_rootFolder->folder(id);
+ if (!folder) {
+ qDebug()<<" / / FOLDER not found";
+ return;
+ }
+ //TODO: warn user on non-empty folders
+ AbstractProjectItem *parent = folder->parent();
+ parent->removeChild(folder);
+ emit storeFolder(id, parent->clipId(), QString(), QString());
+ delete folder;
+}
+
+
+void Bin::emitAboutToAddItem(AbstractProjectItem* item)
+{
+ m_itemModel->onAboutToAddItem(item);
+}
+
+void Bin::emitItemAdded(AbstractProjectItem* item)
+{
+ m_itemModel->onItemAdded(item);
+}
+
+void Bin::emitAboutToRemoveItem(AbstractProjectItem* item)
+{
+ m_itemModel->onAboutToRemoveItem(item);
+}
+
+void Bin::emitItemRemoved(AbstractProjectItem* item)
+{
+ m_itemModel->onItemRemoved(item);
+}
+
+void Bin::rowsInserted(const QModelIndex &/*parent*/, int /*start*/, int end)
+{
+ QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
+ if (indexes.isEmpty()) {
+ const QModelIndex id = m_itemModel->index(end, 0, QModelIndex());
+ m_proxyModel->selectionModel()->select(m_proxyModel->mapFromSource(id), QItemSelectionModel::Select);
+ }
+ //selectModel(id);
+}
+
+void Bin::rowsRemoved(const QModelIndex &/*parent*/, int start, int /*end*/)
+{
+ const QModelIndex id = m_itemModel->index(start, 0, QModelIndex());
+ m_proxyModel->selectionModel()->select(m_proxyModel->mapFromSource(id), QItemSelectionModel::Select);
+ //selectModel(id);
+}
+
+void Bin::selectModel(const QModelIndex &id)
+{
+ m_proxyModel->selectionModel()->select(m_proxyModel->mapFromSource(id), QItemSelectionModel::Select);
+ /*if (id.isValid()) {
+ AbstractProjectItem *currentItem = static_cast<AbstractProjectItem*>(id.internalPointer());
+ if (currentItem) {
+ //m_openedProducer = currentItem->clipId();
+ }
+ }*/
+}
+
+void Bin::selectProxyModel(const QModelIndex &id)
+{
+ if (id.isValid()) {
+ AbstractProjectItem *currentItem = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(id).internalPointer());
+ if (currentItem) {
+ // Set item as current so that it displays its content in clip monitor
+ currentItem->setCurrent(true);
+ if (currentItem->itemType() != AbstractProjectItem::FolderItem) {
+ m_editAction->setEnabled(true);
+ m_reloadAction->setEnabled(true);
+ m_duplicateAction->setEnabled(true);
+ if (m_propertiesPanel->width() > 0) {
+ // if info panel is displayed, update info
+ showClipProperties((ProjectClip *)currentItem);
+ m_deleteAction->setText(i18n("Delete Clip"));
+ m_proxyAction->setText(i18n("Proxy Clip"));
+ }
+ } else {
+ // A folder was selected, disable editing clip
+ m_editAction->setEnabled(false);
+ m_reloadAction->setEnabled(false);
+ m_duplicateAction->setEnabled(false);
+ m_deleteAction->setText(i18n("Delete Folder"));
+ m_proxyAction->setText(i18n("Proxy Folder"));
+ }
+ m_deleteAction->setEnabled(true);
+ } else {
+ m_reloadAction->setEnabled(false);
+ m_duplicateAction->setEnabled(false);
+ m_editAction->setEnabled(false);
+ m_deleteAction->setEnabled(false);
+ }
+ }
+ else {
+ // No item selected in bin
+ m_editAction->setEnabled(false);
+ m_deleteAction->setEnabled(false);
+ // Hide properties panel
+ m_collapser->collapse();
+ showClipProperties(NULL);
+ // Display black bg in clip monitor
+ m_monitor->openClip(NULL);
+ }
+}
+
+void Bin::autoSelect()
+{
+ /*QModelIndex current = m_proxyModel->selectionModel()->currentIndex();
+ AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(m_proxyModel->mapToSource(current).internalPointer());
+ if (!currentItem) {
+ QModelIndex id = m_proxyModel->index(0, 0, QModelIndex());
+ //selectModel(id);
+ //m_proxyModel->selectionModel()->select(m_proxyModel->mapFromSource(id), QItemSelectionModel::Select);
+ }*/
+}
+
+QList <ProjectClip *> Bin::selectedClips()
+{
+ //TODO: handle clips inside folders
+ QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
+ QList <ProjectClip *> list;
+ foreach (const QModelIndex &ix, indexes) {
+ AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
+ ProjectClip *currentItem = qobject_cast<ProjectClip*>(item);
+ if (currentItem) {
+ list << currentItem;
+ }
+ }
+ return list;
+}
+
+void Bin::slotInitView(QAction *action)
+{
+ closeEditing();
+ if (action) {
+ int viewType = action->data().toInt();
+ KdenliveSettings::setBinMode(viewType);
+ if (viewType == m_listType) {
+ return;
+ }
+ if (m_listType == BinTreeView) {
+ // save current treeview state (column width)
+ QTreeView *view = static_cast<QTreeView*>(m_itemView);
+ m_headerInfo = view->header()->saveState();
+ }
+ else {
+ // remove the current folderUp item if any
+ if (m_folderUp) {
+ delete m_folderUp;
+ m_folderUp = NULL;
+ }
+ }
+ m_listType = static_cast<BinViewType>(viewType);
+ }
+
+ if (m_itemView) {
+ delete m_itemView;
+ }
+
+ switch (m_listType) {
+ case BinIconView:
+ m_itemView = new QListView(m_splitter);
+ m_folderUp = new ProjectFolderUp(NULL);
+ break;
+ default:
+ m_itemView = new QTreeView(m_splitter);
+ break;
+ }
+ m_itemView->setMouseTracking(true);
+ m_itemView->viewport()->installEventFilter(m_eventEater);
+ QSize zoom = m_iconSize * (m_slider->value() / 4.0);
+ m_itemView->setIconSize(zoom);
+ QPixmap pix(zoom);
+ pix.fill(Qt::lightGray);
+ m_blankThumb.addPixmap(pix);
+ m_itemView->setModel(m_proxyModel);
+ m_itemView->setSelectionModel(m_proxyModel->selectionModel());
+ m_splitter->addWidget(m_itemView);
+ m_splitter->insertWidget(2, m_propertiesPanel);
+ m_splitter->setSizes(QList <int>() << 4 << 2);
+ m_collapser->collapse();
+
+ // setup some default view specific parameters
+ if (m_listType == BinTreeView) {
+ m_itemView->setItemDelegate(m_binTreeViewDelegate);
+ QTreeView *view = static_cast<QTreeView*>(m_itemView);
+ view->setSortingEnabled(true);
+ view->setHeaderHidden(true);
+ if (!m_headerInfo.isEmpty()) {
+ view->header()->restoreState(m_headerInfo);
+ } else {
+ view->header()->resizeSections(QHeaderView::ResizeToContents);
+ }
+ view->resizeColumnToContents(0);
+ connect(view->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(slotSaveHeaders()));
+ }
+ else if (m_listType == BinIconView) {
+ QListView *view = static_cast<QListView*>(m_itemView);
+ view->setViewMode(QListView::IconMode);
+ view->setMovement(QListView::Static);
+ view->setResizeMode(QListView::Adjust);
+ view->setUniformItemSizes(true);
+ }
+ m_itemView->setEditTriggers(QAbstractItemView::NoEditTriggers); //DoubleClicked);
+ m_itemView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ m_itemView->setDragDropMode(QAbstractItemView::DragDrop);
+ m_itemView->setAlternatingRowColors(true);
+ m_itemView->setAcceptDrops(true);
+ m_itemView->setFocus();
+}
+
+void Bin::slotSetIconSize(int size)
+{
+ if (!m_itemView) {
+ return;
+ }
+ KdenliveSettings::setBin_zoom(size);
+ QSize zoom = m_iconSize;
+ zoom = zoom * (size / 4.0);
+ m_itemView->setIconSize(zoom);
+ QPixmap pix(zoom);
+ pix.fill(Qt::lightGray);
+ m_blankThumb.addPixmap(pix);
+}
+
+
+void Bin::closeEditing()
+{
+ //delete m_propertiesPanel;
+ //m_propertiesPanel = NULL;
+}
+
+
+void Bin::contextMenuEvent(QContextMenuEvent *event)
+{
+ bool enableClipActions = false;
+ if (m_itemView) {
+ QModelIndex idx = m_itemView->indexAt(m_itemView->viewport()->mapFromGlobal(event->globalPos()));
+ if (idx.isValid()) {
+ // User right clicked on a clip
+ AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(m_proxyModel->mapToSource(idx).internalPointer());
+ if (currentItem) {
+ enableClipActions = true;
+ m_proxyAction->blockSignals(true);
+ ProjectClip *clip = qobject_cast<ProjectClip*>(currentItem);
+ if (clip) {
+ m_proxyAction->setChecked(clip->hasProxy());
+ QList<QAction *> transcodeActions = m_transcodeAction->actions();
+ QStringList data;
+ QString condition;
+ QString audioCodec = clip->codec(true);
+ QString videoCodec = clip->codec(false);
+ bool noCodecInfo = false;
+ if (audioCodec.isEmpty() && videoCodec.isEmpty()) {
+ noCodecInfo = true;
+ }
+ for (int i = 0; i < transcodeActions.count(); ++i) {
+ data = transcodeActions.at(i)->data().toStringList();
+ if (data.count() > 4) {
+ condition = data.at(4);
+ if (condition.isEmpty()) {
+ transcodeActions.at(i)->setEnabled(true);
+ continue;
+ }
+ if (noCodecInfo) {
+ // No audio / video codec, this is an MLT clip, disable conditionnal transcoding
+ transcodeActions.at(i)->setEnabled(false);
+ continue;
+ }
+ if (condition.startsWith("vcodec"))
+ transcodeActions.at(i)->setEnabled(condition.section('=', 1, 1) == videoCodec);
+ else if (condition.startsWith("acodec"))
+ transcodeActions.at(i)->setEnabled(condition.section('=', 1, 1) == audioCodec);
+ }
+ }
+ }
+ m_proxyAction->blockSignals(false);
+ }
+ }
+ }
+ // Enable / disable clip actions
+ m_proxyAction->setEnabled(enableClipActions);
+ m_transcodeAction->setEnabled(enableClipActions);
+ m_editAction->setEnabled(enableClipActions);
+ m_reloadAction->setEnabled(enableClipActions);
+ m_duplicateAction->setEnabled(enableClipActions);
+ m_clipsActionsMenu->setEnabled(enableClipActions);
+ m_extractAudioAction->setEnabled(enableClipActions);
+ // Show menu
+ m_menu->exec(event->globalPos());
+}
+
+
+void Bin::slotRefreshClipProperties()
+{
+ QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
+ foreach (const QModelIndex &ix, indexes) {
+ if (ix.isValid()) {
+ AbstractProjectItem *clip = static_cast<AbstractProjectItem *>(m_proxyModel->mapToSource(ix).internalPointer());
+ if (clip && clip->itemType() == AbstractProjectItem::ClipItem) {
+ showClipProperties(qobject_cast<ProjectClip *>(clip));
+ break;
+ }
+ }
+ }
+}
+
+
+void Bin::slotItemDoubleClicked(const QModelIndex &ix, const QPoint pos)
+{
+ AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
+ if (m_listType == BinIconView) {
+ if (item->count() > 0 || item->itemType() == AbstractProjectItem::FolderItem) {
+ m_folderUp->setParent(item);
+ m_itemView->setRootIndex(ix);
+ return;
+ }
+ if (item == m_folderUp) {
+ AbstractProjectItem *parentItem = item->parent();
+ QModelIndex parent = getIndexForId(parentItem->parent()->clipId(), parentItem->parent()->itemType() == AbstractProjectItem::FolderItem);
+ if (parentItem->parent() != m_rootFolder) {
+ // We are entering a parent folder
+ m_folderUp->setParent(parentItem->parent());
+ }
+ else m_folderUp->setParent(NULL);
+ m_itemView->setRootIndex(m_proxyModel->mapFromSource(parent));
+ return;
+ }
+ }
+ else {
+ if (item->count() > 0) {
+ QTreeView *view = static_cast<QTreeView*>(m_itemView);
+ view->setExpanded(ix, !view->isExpanded(ix));
+ return;
+ }
+ }
+ if (ix.isValid()) {
+ QRect IconRect = m_itemView->visualRect(ix);
+ IconRect.setSize(m_itemView->iconSize());
+ if (!pos.isNull() && !IconRect.contains(pos)) {
+ // User clicked outside icon, trigger rename
+ m_itemView->edit(ix);
+ return;
+ }
+ slotSwitchClipProperties(ix);
+ }
+}
+
+void Bin::slotSwitchClipProperties()
+{
+ QModelIndex current = m_proxyModel->selectionModel()->currentIndex();
+ slotSwitchClipProperties(current);
+}
+
+void Bin::slotSwitchClipProperties(const QModelIndex &ix)
+{
+ if (ix.isValid()) {
+ // User clicked in the icon, open clip properties
+ if (m_collapser->isWidgetCollapsed()) {
+ AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
+ ProjectClip *clip = qobject_cast<ProjectClip*>(item);
+ if (clip) {
+ m_collapser->restore();
+ showClipProperties(clip);
+ }
+ else m_collapser->collapse();
+ }
+ else m_collapser->collapse();
+ }
+ else m_collapser->collapse();
+}
+
+void Bin::showClipProperties(ProjectClip *clip)
+{
+ closeEditing();
+ // Special case: text clips open title widget
+ if (clip && clip->clipType() == Text) {
+ // Cleanup widget for new content
+ foreach (QWidget * w, m_propertiesPanel->findChildren<ClipPropertiesController*>()) {
+ delete w;
+ }
+ showTitleWidget(clip);
+ m_collapser->collapse();
+ return;
+ }
+ QString panelId = m_propertiesPanel->property("clipId").toString();
+ if (!clip || m_propertiesPanel->width() == 0) {
+ m_propertiesPanel->setProperty("clipId", QVariant());
+ foreach (QWidget * w, m_propertiesPanel->findChildren<ClipPropertiesController*>()) {
+ delete w;
+ }
+ return;
+ }
+ if (panelId == clip->clipId()) {
+ // the properties panel is already displaying current clip, do nothing
+ return;
+ }
+
+ // Cleanup widget for new content
+ foreach (QWidget * w, m_propertiesPanel->findChildren<ClipPropertiesController*>()) {
+ delete w;
+ }
+ m_propertiesPanel->setProperty("clipId", clip->clipId());
+
+ QVBoxLayout *lay = (QVBoxLayout*) m_propertiesPanel->layout();
+ if (lay == 0) {
+ lay = new QVBoxLayout(m_propertiesPanel);
+ m_propertiesPanel->setLayout(lay);
+ }
+ ClipPropertiesController *panel = clip->buildProperties(m_propertiesPanel);
+ connect(this, SIGNAL(refreshTimeCode()), panel, SLOT(slotRefreshTimeCode()));
+ connect(panel, SIGNAL(updateClipProperties(const QString &, QMap<QString, QString>, QMap<QString, QString>)), this, SLOT(slotEditClipCommand(const QString &, QMap<QString, QString>, QMap<QString, QString>)));
+ lay->addWidget(panel);
+}
+
+
+void Bin::slotEditClipCommand(const QString &id, QMap<QString, QString>oldProps, QMap<QString, QString>newProps)
+{
+ EditClipCommand *command = new EditClipCommand(m_doc, id, oldProps, newProps, true);
+ m_doc->commandStack()->push(command);
+}
+
+void Bin::reloadClip(const QString &id)
+{
+ ProjectClip *clip = m_rootFolder->clip(id);
+ if (!clip) return;
+ QDomDocument doc;
+ QDomElement xml = clip->toXml(doc);
+ if (!xml.isNull()) m_doc->renderer()->getFileProperties(xml, id, 150, true);
+}
+
+void Bin::refreshEditedClip()
+{
+ const QString id = m_propertiesPanel->property("clipId").toString();
+ /*m_doc->bin()->refreshThumnbail(id);
+ m_doc->binMonitor()->refresh();*/
+}
+
+void Bin::slotThumbnailReady(const QString &id, const QImage &img, bool fromFile)
+{
+ ProjectClip *clip = m_rootFolder->clip(id);
+ if (clip) {
+ clip->setThumbnail(img);
+ // Save thumbnail for later reuse
+ if (!fromFile) img.save(m_doc->projectFolder().path() + "/thumbs/" + clip->hash() + ".png");
+ }
+}
+
+QStringList Bin::getBinFolderClipIds(const QString &id) const
+{
+ QStringList ids;
+ ProjectFolder *folder = m_rootFolder->folder(id);
+ if (folder) {
+ for (int i = 0; i < folder->count(); i++) {
+ AbstractProjectItem *child = folder->at(i);
+ if (child->itemType() == AbstractProjectItem::ClipItem) {
+ ids << child->clipId();
+ }
+ }
+ }
+ return ids;
+}
+
+ProjectClip *Bin::getBinClip(const QString &id)
+{
+ ProjectClip *clip = NULL;
+ if (id.contains("_")) {
+ clip = m_rootFolder->clip(id.section("_", 0, 0));
+ }
+ else {
+ clip = m_rootFolder->clip(id);
+ }
+ return clip;
+}
+
+void Bin::setWaitingStatus(const QString &id)
+{
+ ProjectClip *clip = m_rootFolder->clip(id);
+ if (clip) clip->setClipStatus(AbstractProjectItem::StatusWaiting);
+}
+
+void Bin::slotProducerReady(requestClipInfo info, ClipController *controller)
+{
+ ProjectClip *clip = m_rootFolder->clip(info.clipId);
+ if (clip) {
+ if (!clip->hasProxy()) {
+ // Check for file modifications
+ ClipType t = clip->clipType();
+ if (t == AV || t == Audio || t == Image || t == Video || t == Playlist) {
+ m_doc->watchFile(clip->url());
+ }
+ }
+ if (controller) clip->setProducer(controller, info.replaceProducer);
+ QString currentClip = m_monitor->activeClipId();
+ if (currentClip.isEmpty()) {
+ //No clip displayed in monitor, check if item is selected
+ QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
+ foreach (const QModelIndex &ix, indexes) {
+ ProjectClip *currentItem = static_cast<ProjectClip *>(m_proxyModel->mapToSource(ix).internalPointer());
+ if (currentItem->clipId() == info.clipId) {
+ // Item was selected, show it in monitor
+ currentItem->setCurrent(true);
+ break;
+ }
+ }
+ }
+ else if (currentClip == info.clipId) {
+ m_monitor->openClip(NULL);
+ clip->setCurrent(true);
+ //m_monitor->openClip(controller);
+ }
+ }
+ else {
+ // Clip not found, create it
+ QString groupId = controller->property("kdenlive:folderid");
+ ProjectFolder *parentFolder;
+ if (!groupId.isEmpty()) {
+ parentFolder = m_rootFolder->folder(groupId);
+ if (!parentFolder) {
+ // parent folder does not exist, put in root folder
+ parentFolder = m_rootFolder;
+ }
+ if (groupId.toInt() >= m_folderCounter) m_folderCounter = groupId.toInt() + 1;
+ }
+ else parentFolder = m_rootFolder;
+ ProjectClip *newItem = new ProjectClip(info.clipId, m_blankThumb, controller, parentFolder);
+ if (info.clipId.toInt() >= m_clipCounter) m_clipCounter = info.clipId.toInt() + 1;
+ if (m_listType == BinTreeView) {
+ ((QTreeView *)m_itemView)->resizeColumnToContents(0);
+ }
+ }
+ emit producerReady(info.clipId);
+}
+
+void Bin::openProducer(ClipController *controller)
+{
+ m_monitor->openClip(controller);
+}
+
+void Bin::openProducer(ClipController *controller, int in, int out)
+{
+ m_monitor->openClipZone(controller, in, out);
+}
+
+void Bin::emitItemUpdated(AbstractProjectItem* item)
+{
+ emit itemUpdated(item);
+}
+
+
+void Bin::setupGeneratorMenu(const QHash<QString,QMenu*>& menus)
+{
+ if (!m_menu) {
+ //qDebug()<<"Warning, menu was not created, something is wrong";
+ return;
+ }
+ /*if (!menus.contains("addMenu") && ! menus.value("addMenu") )
+ return;*/
+
+ QMenu *menu = m_addButton->menu();
+ if (menus.contains("addMenu") && menus.value("addMenu")){
+ QMenu* addMenu=menus.value("addMenu");
+ menu->addMenu(addMenu);
+ m_addButton->setMenu(menu);
+ if (addMenu->isEmpty())
+ addMenu->setEnabled(false);
+ }
+ if (menus.contains("extractAudioMenu") && menus.value("extractAudioMenu") ){
+ QMenu* extractAudioMenu = menus.value("extractAudioMenu");
+ m_menu->addMenu(extractAudioMenu);
+ m_extractAudioAction = extractAudioMenu;
+ }
+ if (menus.contains("transcodeMenu") && menus.value("transcodeMenu") ){
+ QMenu* transcodeMenu = menus.value("transcodeMenu");
+ m_menu->addMenu(transcodeMenu);
+ if (transcodeMenu->isEmpty())
+ transcodeMenu->setEnabled(false);
+ m_transcodeAction = transcodeMenu;
+ }
+ if (menus.contains("clipActionsMenu") && menus.value("clipActionsMenu") ){
+ QMenu* stabilizeMenu=menus.value("clipActionsMenu");
+ m_menu->addMenu(stabilizeMenu);
+ if (stabilizeMenu->isEmpty())
+ stabilizeMenu->setEnabled(false);
+ m_clipsActionsMenu = stabilizeMenu;
+
+ }
+ if (m_reloadAction) m_menu->addAction(m_reloadAction);
+ if (m_duplicateAction) m_menu->addAction(m_duplicateAction);
+ if (m_proxyAction) m_menu->addAction(m_proxyAction);
+ if (menus.contains("inTimelineMenu") && menus.value("inTimelineMenu")){
+ QMenu* inTimelineMenu=menus.value("inTimelineMenu");
+ m_menu->addMenu(inTimelineMenu);
+ inTimelineMenu->setEnabled(false);
+ }
+ m_menu->addAction(m_editAction);
+ m_menu->addAction(m_openAction);
+ m_menu->addAction(m_deleteAction);
+ m_menu->insertSeparator(m_deleteAction);
+}
+
+void Bin::setupMenu(QMenu *addMenu, QAction *defaultAction, QHash <QString, QAction*> actions)
+{
+ // Setup actions
+ m_editAction = actions.value("properties");
+ m_toolbar->addAction(m_editAction);
+
+ m_deleteAction = actions.value("delete");
+ m_toolbar->addAction(m_deleteAction);
+
+ m_openAction = actions.value("open");
+ m_reloadAction = actions.value("reload");
+ m_duplicateAction = actions.value("duplicate");
+ m_proxyAction = actions.value("proxy");
+
+ QMenu *m = new QMenu;
+ m->addActions(addMenu->actions());
+ m_addButton = new QToolButton;
+ m_addButton->setMenu(m);
+ m_addButton->setDefaultAction(defaultAction);
+ m_addButton->setPopupMode(QToolButton::MenuButtonPopup);
+ m_toolbar->addWidget(m_addButton);
+ m_menu = new QMenu();
+ //m_menu->addActions(addMenu->actions());
+}
+
+const QString Bin::getDocumentProperty(const QString &key)
+{
+ return m_doc->getDocumentProperty(key);
+}
+
+const QSize Bin::getRenderSize()
+{
+ return m_doc->getRenderSize();
+}
+
+JobManager *Bin::jobManager()
+{
+ return m_jobManager;
+}
+
+void Bin::slotUpdateJobStatus(const QString&id, int jobType, int status, const QString &label, const QString &actionName, const QString &details)
+{
+ ProjectClip *clip = m_rootFolder->clip(id);
+ if (clip) {
+ clip->setJobStatus((AbstractClipJob::JOBTYPE) jobType, (ClipJobStatus) status);
+ }
+ if (status == JobCrashed) {
+ QList<QAction *> actions = m_infoMessage->actions();
+ if (m_infoMessage->isHidden()) {
+ m_infoMessage->setText(label);
+ m_infoMessage->setWordWrap(m_infoMessage->text().length() > 35);
+ m_infoMessage->setMessageType(KMessageWidget::Warning);
+ }
+
+ if (!actionName.isEmpty()) {
+ QAction *action = NULL;
+ QList< KActionCollection * > collections = KActionCollection::allCollections();
+ for (int i = 0; i < collections.count(); ++i) {
+ KActionCollection *coll = collections.at(i);
+ action = coll->action(actionName);
+ if (action) break;
+ }
+ if (action && !actions.contains(action)) m_infoMessage->addAction(action);
+ }
+ if (!details.isEmpty()) {
+ m_errorLog.append(details);
+ if (!actions.contains(m_logAction)) m_infoMessage->addAction(m_logAction);
+ }
+ m_infoMessage->animatedShow();
+ }
+}
+
+void Bin::displayMessage(const QString &text, KMessageWidget::MessageType type)
+{
+ if (m_infoMessage->isHidden()) {
+ m_infoMessage->setText(text);
+ m_infoMessage->setWordWrap(m_infoMessage->text().length() > 35);
+ m_infoMessage->setMessageType(type);
+ m_infoMessage->animatedShow();
+ }
+}
+
+void Bin::slotShowJobLog()
+{
+ QDialog d(this);
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
+ QWidget *mainWidget = new QWidget(this);
+ QVBoxLayout *l = new QVBoxLayout;
+ QTextEdit t(&d);
+ for (int i = 0; i < m_errorLog.count(); ++i) {
+ if (i > 0) t.insertHtml("<br><hr /><br>");
+ t.insertPlainText(m_errorLog.at(i));
+ }
+ t.setReadOnly(true);
+ l->addWidget(&t);
+ mainWidget->setLayout(l);
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ d.setLayout(mainLayout);
+ mainLayout->addWidget(mainWidget);
+ mainLayout->addWidget(buttonBox);
+ d.connect(buttonBox, SIGNAL(rejected()), &d, SLOT(accept()));
+ d.exec();
+}
+
+void Bin::gotProxy(const QString &id)
+{
+ ProjectClip *clip = m_rootFolder->clip(id);
+ if (clip) {
+ QDomDocument doc;
+ QDomElement xml = clip->toXml(doc);
+ if (!xml.isNull()) m_doc->renderer()->getFileProperties(xml, id, 150, true);
+ }
+}
+
+void Bin::reloadProducer(const QString &id, QDomElement xml)
+{
+ m_doc->renderer()->getFileProperties(xml, id, 150, true);
+}
+
+void Bin::refreshClip(const QString &id)
+{
+ emit clipNeedsReload(id, false);
+ if (m_monitor->activeClipId() == id)
+ m_monitor->refreshMonitor();
+}
+
+void Bin::refreshClipMarkers(const QString &id)
+{
+ if (m_monitor->activeClipId() == id)
+ m_monitor->updateMarkers();
+}
+
+void Bin::discardJobs(const QString &id, AbstractClipJob::JOBTYPE type)
+{
+ m_jobManager->discardJobs(id, type);
+}
+
+
+void Bin::slotStartCutJob(const QString &id)
+{
+ startJob(id, AbstractClipJob::CUTJOB);
+}
+
+void Bin::startJob(const QString &id, AbstractClipJob::JOBTYPE type)
+{
+ QList <ProjectClip *> clips;
+ ProjectClip *clip = getBinClip(id);
+ if (clip && !hasPendingJob(id, type)) {
+ // Launch job
+ clips << clip;
+ m_jobManager->prepareJobs(clips, type);
+ }
+}
+
+bool Bin::hasPendingJob(const QString &id, AbstractClipJob::JOBTYPE type)
+{
+ return m_jobManager->hasPendingJob(id, type);
+}
+
+void Bin::slotCreateProjectClip()
+{
+ QAction* act = qobject_cast<QAction *>(sender());
+ if (act == 0) {
+ // Cannot access triggering action, something is wrong
+ qDebug()<<"// Error in clip creation action";
+ return;
+ }
+ ClipType type = (ClipType) act->data().toInt();
+ QStringList folderInfo = getFolderInfo();
+ switch (type) {
+ case Color:
+ ClipCreationDialog::createColorClip(m_doc, folderInfo, this);
+ break;
+ case SlideShow:
+ ClipCreationDialog::createSlideshowClip(m_doc, folderInfo, this);
+ break;
+ case Text:
+ ClipCreationDialog::createTitleClip(m_doc, folderInfo, QString(), this);
+ break;
+ case TextTemplate:
+ ClipCreationDialog::createTitleTemplateClip(m_doc, folderInfo, QString(), this);
+ break;
+ default:
+ break;
+ }
+}
+
+void Bin::slotItemDropped(QStringList ids, const QModelIndex &parent)
+{
+ AbstractProjectItem *parentItem;
+ if (parent.isValid()) {
+ parentItem = static_cast<AbstractProjectItem *>(parent.internalPointer());
+ while (parentItem->itemType() != AbstractProjectItem::FolderItem) {
+ parentItem = parentItem->parent();
+ }
+ }
+ else {
+ parentItem = m_rootFolder;
+ }
+ QUndoCommand *moveCommand = new QUndoCommand();
+ moveCommand->setText(i18np("Move Clip", "Move Clips", ids.count()));
+ QStringList folderIds;
+ foreach(const QString &id, ids) {
+ if (id.contains("/")) {
+ // trying to move clip zone, not allowed. Ignore
+ continue;
+ }
+ if (id.startsWith("#")) {
+ // moving a folder, keep it for later
+ folderIds << id;
+ continue;
+ }
+ ProjectClip *currentItem = m_rootFolder->clip(id);
+ AbstractProjectItem *currentParent = currentItem->parent();
+ if (currentParent != parentItem) {
+ // Item was dropped on a different folder
+ new MoveBinClipCommand(this, id, currentParent->clipId(), parentItem->clipId(), moveCommand);
+ }
+ }
+ if (!folderIds.isEmpty()) {
+ foreach(QString id, folderIds) {
+ id.remove(0, 1);
+ ProjectFolder *currentItem = m_rootFolder->folder(id);
+ AbstractProjectItem *currentParent = currentItem->parent();
+ if (currentParent != parentItem) {
+ // Item was dropped on a different folder
+ new MoveBinFolderCommand(this, id, currentParent->clipId(), parentItem->clipId(), moveCommand);
+ }
+ }
+ }
+ m_doc->commandStack()->push(moveCommand);
+}
+
+
+void Bin::slotEffectDropped(QString effect, const QModelIndex &parent)
+{
+ AbstractProjectItem *parentItem;
+ if (parent.isValid()) {
+ parentItem = static_cast<AbstractProjectItem *>(parent.internalPointer());
+ if (parentItem->itemType() != AbstractProjectItem::ClipItem) {
+ // effect only supported on clip items
+ return;
+ }
+ QDomDocument doc;
+ doc.setContent(effect);
+ QDomElement e = doc.documentElement();
+ AddBinEffectCommand *command = new AddBinEffectCommand(this, parentItem->clipId(), e);
+ m_doc->commandStack()->push(command);
+ }
+}
+
+void Bin::slotDeleteEffect(const QString &id, QDomElement effect)
+{
+ RemoveBinEffectCommand *command = new RemoveBinEffectCommand(this, id, effect);
+ m_doc->commandStack()->push(command);
+}
+
+void Bin::removeEffect(const QString &id, const QDomElement &effect)
+{
+ ProjectClip *currentItem = NULL;
+ if (id.isEmpty()) {
+ currentItem = getFirstSelectedClip();
+ }
+ else currentItem = m_rootFolder->clip(id);
+ if (!currentItem) return;
+ currentItem->removeEffect(m_monitor->profileInfo(), effect.attribute("kdenlive_ix").toInt());
+ m_monitor->refreshMonitor();
+}
+
+void Bin::addEffect(const QString &id, QDomElement &effect)
+{
+ ProjectClip *currentItem = NULL;
+ if (id.isEmpty()) {
+ currentItem = getFirstSelectedClip();
+ }
+ else currentItem = m_rootFolder->clip(id);
+ if (!currentItem) return;
+ currentItem->addEffect(m_monitor->profileInfo(), effect);
+ m_monitor->refreshMonitor();
+}
+
+void Bin::editMasterEffect(ClipController *ctl)
+{
+ emit masterClipSelected(ctl, m_monitor);
+}
+
+void Bin::doMoveClip(const QString &id, const QString &newParentId)
+{
+ ProjectClip *currentItem = m_rootFolder->clip(id);
+ if (!currentItem) return;
+ AbstractProjectItem *currentParent = currentItem->parent();
+ ProjectFolder *newParent = m_rootFolder->folder(newParentId);
+ currentParent->removeChild(currentItem);
+ currentItem->setParent(newParent);
+ currentItem->updateParentInfo(newParentId, newParent->name());
+}
+
+void Bin::doMoveFolder(const QString &id, const QString &newParentId)
+{
+ ProjectFolder *currentItem = m_rootFolder->folder(id);
+ AbstractProjectItem *currentParent = currentItem->parent();
+ ProjectFolder *newParent = m_rootFolder->folder(newParentId);
+ currentParent->removeChild(currentItem);
+ currentItem->setParent(newParent);
+ emit storeFolder(id, newParent->clipId(), currentParent->clipId(), currentItem->name());
+}
+
+void Bin::droppedUrls(QList <QUrl> urls, const QMap<QString,QString> properties)
+{
+ QModelIndex current = m_proxyModel->mapToSource(m_proxyModel->selectionModel()->currentIndex());
+ slotItemDropped(urls, current);
+}
+
+void Bin::slotItemDropped(const QList<QUrl>&urls, const QModelIndex &parent)
+{
+ QStringList folderInfo;
+ if (parent.isValid()) {
+ // Check if drop occured on a folder
+ AbstractProjectItem *parentItem = static_cast<AbstractProjectItem *>(parent.internalPointer());
+ while (parentItem->itemType() != AbstractProjectItem::FolderItem) {
+ parentItem = parentItem->parent();
+ }
+ if (parentItem != m_rootFolder) {
+ folderInfo << parentItem->clipId();
+ }
+ }
+ //TODO: verify if urls exist, check for folders
+ ClipCreationDialog::createClipsCommand(m_doc, urls, folderInfo, this);
+}
+
+void Bin::slotItemEdited(QModelIndex ix,QModelIndex,QVector<int>)
+{
+ // An item name was edited
+/* if (!ix.isValid()) return;
+ AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(ix.internalPointer());
+ if (currentItem && currentItem->itemType() == AbstractProjectItem::FolderItem) {
+ //TODO: Use undo command for this
+
+ AbstractProjectItem *parentFolder = currentItem->parent();
+ RenameBinFolderCommand *command = new RenameBinFolderCommand(this, currentItem->clipId(), const QString &newName, const QString &oldName, QUndoCommand *parent) :
+ emit storeFolder(currentItem->clipId(), parentFolder->clipId(), QString(), currentItem->name());
+ }*/
+}
+
+void Bin::renameFolderCommand(const QString &id, const QString &newName, const QString &oldName)
+{
+ RenameBinFolderCommand *command = new RenameBinFolderCommand(this, id, newName, oldName);
+ m_doc->commandStack()->push(command);
+}
+
+void Bin::renameSubClipCommand(const QString &id, const QString &newName, const QString oldName, int in, int out)
+{
+ RenameBinSubClipCommand *command = new RenameBinSubClipCommand(this, id, newName, oldName, in, out);
+ m_doc->commandStack()->push(command);
+}
+
+void Bin::renameSubClip(const QString &id, const QString &newName, const QString oldName, int in, int out)
+{
+ ProjectClip *clip = m_rootFolder->clip(id);
+ if (!clip) return;
+ ProjectSubClip *sub = clip->getSubClip(in, out);
+ if (!sub) return;
+ sub->setName(newName);
+ clip->setProducerProperty("kdenlive:clipzone." + oldName, QString());
+ clip->setProducerProperty("kdenlive:clipzone." + newName, QString::number(in) + ";" + QString::number(out));
+ emit itemUpdated(sub);
+}
+
+void Bin::slotStartClipJob(bool enable)
+{
+ QAction* act = qobject_cast<QAction *>(sender());
+ if (act == 0) {
+ // Cannot access triggering action, something is wrong
+ qDebug()<<"// Error in clip job action";
+ return;
+ }
+ startClipJob(act->data().toStringList());
+}
+
+void Bin::startClipJob(const QStringList &params)
+{
+ QStringList data = params;
+ if (data.isEmpty()) {
+ qDebug()<<"// Error in clip job action";
+ return;
+ }
+ AbstractClipJob::JOBTYPE jobType = (AbstractClipJob::JOBTYPE) data.takeFirst().toInt();
+ QList <ProjectClip *>clips = selectedClips();
+ m_jobManager->prepareJobs(clips, jobType, data);
+}
+
+void Bin::slotCancelRunningJob(const QString &id, const QMap<QString, QString> &newProps)
+{
+ if (newProps.isEmpty()) return;
+ ProjectClip *clip = getBinClip(id);
+ if (!clip) return;
+ QMap <QString, QString> oldProps;
+ QMapIterator<QString, QString> i(newProps);
+ while (i.hasNext()) {
+ i.next();
+ QString value = newProps.value(i.key());
+ oldProps.insert(i.key(), value);
+ }
+ if (newProps == oldProps) return;
+ EditClipCommand *command = new EditClipCommand(m_doc, id, oldProps, newProps, true);
+ m_doc->commandStack()->push(command);
+}
+
+
+void Bin::slotPrepareJobsMenu()
+{
+ ProjectClip *item = getFirstSelectedClip();
+ if (item) {
+ QString id = item->clipId();
+ m_discardCurrentClipJobs->setData(id);
+ QStringList jobs = m_jobManager->getPendingJobs(id);
+ m_discardCurrentClipJobs->setEnabled(!jobs.isEmpty());
+ } else {
+ m_discardCurrentClipJobs->setData(QString());
+ m_discardCurrentClipJobs->setEnabled(false);
+ }
+}
+
+void Bin::slotAddClipCut(const QString&id, int in, int out)
+{
+ AddBinClipCutCommand *command = new AddBinClipCutCommand(this, id, in, out, true);
+ m_doc->commandStack()->push(command);
+}
+
+void Bin::loadSubClips(const QString&id, const QMap <QString,QString> data)
+{
+ ProjectClip *clip = getBinClip(id);
+ if (!clip) return;
+ QMapIterator<QString, QString> i(data);
+ while (i.hasNext()) {
+ i.next();
+ if (!i.value().contains(";")) {
+ // Problem, the zone has no in/out points
+ continue;
+ }
+ int in = i.value().section(";", 0, 0).toInt();
+ int out = i.value().section(";", 1, 1).toInt();
+ new ProjectSubClip(clip, in, out, i.key());
+ }
+}
+
+void Bin::addClipCut(const QString&id, int in, int out)
+{
+ ProjectClip *clip = getBinClip(id);
+ if (!clip) return;
+ // Check that we don't already have that subclip
+ ProjectSubClip *sub = clip->getSubClip(in, out);
+ if (sub) {
+ // A subclip with same zone already exists
+ return;
+ }
+ sub = new ProjectSubClip(clip, in, out);
+}
+
+void Bin::removeClipCut(const QString&id, int in, int out)
+{
+ ProjectClip *clip = getBinClip(id);
+ if (!clip) return;
+ ProjectSubClip *sub = clip->getSubClip(in, out);
+ if (sub) {
+ clip->removeChild(sub);
+ sub->discard();
+ delete sub;
+ }
+}
+
+Timecode Bin::projectTimecode() const
+{
+ return m_doc->timecode();
+}
+
+void Bin::slotStartFilterJob(const ItemInfo &info, const QString&id, QMap <QString, QString> &filterParams, QMap <QString, QString> &consumerParams, QMap <QString, QString> &extraParams)
+{
+ ProjectClip *clip = getBinClip(id);
+ if (!clip) return;
+
+ QMap <QString, QString> producerParams = QMap <QString, QString> ();
+ producerParams.insert("producer", clip->url().path());
+ producerParams.insert("in", QString::number((int) info.cropStart.frames(m_doc->fps())));
+ producerParams.insert("out", QString::number((int) (info.cropStart + info.cropDuration).frames(m_doc->fps())));
+ extraParams.insert("clipStartPos", QString::number((int) info.startPos.frames(m_doc->fps())));
+ extraParams.insert("clipTrack", QString::number(info.track));
+
+ m_jobManager->prepareJobFromTimeline(clip, producerParams, filterParams, consumerParams, extraParams);
+}
+
+void Bin::focusBinView() const
+{
+ m_itemView->setFocus();
+}
+
+
+void Bin::slotOpenClip()
+{
+ ProjectClip *clip = getFirstSelectedClip();
+ if (!clip) return;
+ if (clip->clipType() == Image) {
+ if (KdenliveSettings::defaultimageapp().isEmpty())
+ KMessageBox::sorry(QApplication::activeWindow(), i18n("Please set a default application to open images in the Settings dialog"));
+ else
+ QProcess::startDetached(KdenliveSettings::defaultimageapp(), QStringList() << clip->url().path());
+ }
+ if (clip->clipType() == Audio) {
+ if (KdenliveSettings::defaultaudioapp().isEmpty())
+ KMessageBox::sorry(QApplication::activeWindow(), i18n("Please set a default application to open audio files in the Settings dialog"));
+ else
+ QProcess::startDetached(KdenliveSettings::defaultaudioapp(), QStringList() << clip->url().path());
+ }
+}
+
+void Bin::updateTimecodeFormat()
+{
+ emit refreshTimeCode();
+}
+
+
+void Bin::slotGotFilterJobResults(QString id, int , int , stringMap results, stringMap filterInfo)
+{
+ // Currently, only the first value of results is used
+ ProjectClip *clip = getBinClip(id);
+ if (!clip) return;
+
+ // Check for return value
+ int markersType = -1;
+ if (filterInfo.contains("addmarkers")) markersType = filterInfo.value("addmarkers").toInt();
+ if (results.isEmpty()) {
+ emit displayMessage(i18n("No data returned from clip analysis"), KMessageWidget::Warning);
+ return;
+ }
+ bool dataProcessed = false;
+ QString key = filterInfo.value("key");
+ int offset = filterInfo.value("offset").toInt();
+ QStringList value = results.value(key).split(';', QString::SkipEmptyParts);
+ //qDebug()<<"// RESULT; "<<key<<" = "<<value;
+ if (filterInfo.contains("resultmessage")) {
+ QString mess = filterInfo.value("resultmessage");
+ mess.replace("%count", QString::number(value.count()));
+ emit displayMessage(mess, KMessageWidget::Information);
+ }
+ else emit displayMessage(i18n("Processing data analysis"), KMessageWidget::Information);
+ if (filterInfo.contains("cutscenes")) {
+ // Check if we want to cut scenes from returned data
+ dataProcessed = true;
+ int cutPos = 0;
+ QUndoCommand *command = new QUndoCommand();
+ command->setText(i18n("Auto Split Clip"));
+ foreach (const QString &pos, value) {
+ if (!pos.contains("=")) continue;
+ int newPos = pos.section('=', 0, 0).toInt();
+ // Don't use scenes shorter than 1 second
+ if (newPos - cutPos < 24) continue;
+ new AddBinClipCutCommand(this, id, cutPos + offset, newPos + offset, true, command);
+ cutPos = newPos;
+ }
+ if (command->childCount() == 0)
+ delete command;
+ else m_doc->commandStack()->push(command);
+ }
+ if (markersType >= 0) {
+ // Add markers from returned data
+ dataProcessed = true;
+ int cutPos = 0;
+ QUndoCommand *command = new QUndoCommand();
+ command->setText(i18n("Add Markers"));
+ QList <CommentedTime> markersList;
+ int index = 1;
+ foreach (const QString &pos, value) {
+ if (!pos.contains("=")) continue;
+ int newPos = pos.section('=', 0, 0).toInt();
+ // Don't use scenes shorter than 1 second
+ if (newPos - cutPos < 24) continue;
+ CommentedTime m(GenTime(newPos + offset, m_doc->fps()), QString::number(index), markersType);
+ markersList << m;
+ index++;
+ cutPos = newPos;
+ }
+ slotAddClipMarker(id, markersList);
+ }
+ if (!dataProcessed || filterInfo.contains("storedata")) {
+ // Store returned data as clip extra data
+ //TODO find a way to store analysis data into clip controller
+ //clip->setAnalysisData(filterInfo.contains("displaydataname") ? filterInfo.value("displaydataname") : key, results.value(key), filterInfo.value("offset").toInt());
+ //emit updateAnalysisData(clip->referencedClip());
+ }
+}
+
+void Bin::slotAddClipMarker(const QString &id, QList <CommentedTime> newMarkers, QUndoCommand *groupCommand)
+{
+ ProjectClip *clip = getBinClip(id);
+ if (!clip) return;
+ if (groupCommand == NULL) {
+ groupCommand = new QUndoCommand;
+ groupCommand->setText(i18np("Add marker", "Add markers", newMarkers.count()));
+ }
+ clip->addClipMarker(newMarkers, groupCommand);
+ if (groupCommand->childCount() > 0) m_doc->commandStack()->push(groupCommand);
+ else delete groupCommand;
+
+}
+
+void Bin::slotLoadClipMarkers(const QString &id)
+{
+ KComboBox *cbox = new KComboBox;
+ for (int i = 0; i < 5; ++i) {
+ cbox->insertItem(i, i18n("Category %1", i));
+ cbox->setItemData(i, CommentedTime::markerColor(i), Qt::DecorationRole);
+ }
+ cbox->setCurrentIndex(KdenliveSettings::default_marker_type());
+ //TODO KF5 how to add custom cbox to Qfiledialog
+ QPointer<QFileDialog> fd = new QFileDialog(this, i18n("Load Clip Markers"), m_doc->projectFolder().path());
+ fd->setMimeTypeFilters(QStringList()<<"text/plain");
+ fd->setFileMode(QFileDialog::ExistingFile);
+ if (fd->exec() != QDialog::Accepted) return;
+ QStringList selection = fd->selectedFiles();
+ QString url;
+ if (!selection.isEmpty()) url = selection.first();
+ delete fd;
+
+ //QUrl url = KFileDialog::getOpenUrl(QUrl("kfiledialog:///projectfolder"), "text/plain", this, i18n("Load marker file"));
+ if (url.isEmpty()) return;
+ int category = cbox->currentIndex();
+ delete cbox;
+ QFile file(url);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ emit displayMessage(i18n("Cannot open file %1", QUrl(url).fileName()), KMessageWidget::Warning);
+ return;
+ }
+ QString data = QString::fromUtf8(file.readAll());
+ file.close();
+ QStringList lines = data.split('\n', QString::SkipEmptyParts);
+ QStringList values;
+ bool ok;
+ QUndoCommand *command = new QUndoCommand();
+ command->setText("Load markers");
+ QString markerText;
+ QList <CommentedTime> markersList;
+ foreach(const QString &line, lines) {
+ markerText.clear();
+ values = line.split('\t', QString::SkipEmptyParts);
+ double time1 = values.at(0).toDouble(&ok);
+ double time2 = -1;
+ if (!ok) continue;
+ if (values.count() >1) {
+ time2 = values.at(1).toDouble(&ok);
+ if (values.count() == 2) {
+ // Check if second value is a number or text
+ if (!ok) {
+ time2 = -1;
+ markerText = values.at(1);
+ }
+ else markerText = i18n("Marker");
+ }
+ else {
+ // We assume 3 values per line: in out name
+ if (!ok) {
+ // 2nd value is not a number, drop
+ }
+ else {
+ markerText = values.at(2);
+ }
+ }
+ }
+ if (!markerText.isEmpty()) {
+ // Marker found, add it
+ //TODO: allow user to set a marker category
+ CommentedTime marker1(GenTime(time1), markerText, category);
+ markersList << marker1;
+ if (time2 > 0 && time2 != time1) {
+ CommentedTime marker2(GenTime(time2), markerText, category);
+ markersList << marker2;
+ }
+ }
+ }
+ if (!markersList.isEmpty()) slotAddClipMarker(id, markersList, command);
+ if (command->childCount() > 0) m_doc->commandStack()->push(command);
+ else delete command;
+}
+
+void Bin::deleteClipMarker(const QString &comment, const QString &id, const GenTime &position)
+{
+ ProjectClip *clip = getBinClip(id);
+ if (!clip) return;
+ QUndoCommand *command = new QUndoCommand;
+ command->setText(i18n("Delete marker"));
+ CommentedTime marker(position, comment);
+ marker.setMarkerType(-1);
+ QList <CommentedTime> markers;
+ markers << marker;
+ clip->addClipMarker(markers, command);
+ if (command->childCount() > 0) m_doc->commandStack()->push(command);
+ else delete command;
+}
+
+void Bin::deleteAllClipMarkers(const QString &id)
+{
+ ProjectClip *clip = getBinClip(id);
+ if (!clip) return;
+ QUndoCommand *command = new QUndoCommand;
+ command->setText(i18n("Delete clip markers"));
+ if (!clip->deleteClipMarkers(command)) {
+ emit displayMessage(i18n("Clip has no markers"), KMessageWidget::Warning);
+ }
+ if (command->childCount() > 0) m_doc->commandStack()->push(command);
+ else delete command;
+}
+
+// TODO: move title editing into a better place...
+void Bin::showTitleWidget(ProjectClip *clip)
+{
+ QString path = clip->getProducerProperty("resource");
+ QString titlepath = m_doc->projectFolder().path() + QDir::separator() + "titles/";
+ QPointer<TitleWidget> dia_ui = new TitleWidget(QUrl(), m_doc->timecode(), titlepath, pCore->monitorManager()->projectMonitor()->render, pCore->window());
+ QDomDocument doc;
+ doc.setContent(clip->getProducerProperty("xmldata"));
+ dia_ui->setXml(doc);
+ if (dia_ui->exec() == QDialog::Accepted) {
+ QMap <QString, QString> newprops;
+ newprops.insert("xmldata", dia_ui->xml().toString());
+ if (dia_ui->duration() != clip->duration().frames(m_doc->fps())) {
+ // duration changed, we need to update duration
+ newprops.insert("out", QString::number(dia_ui->duration() - 1));
+ int currentLength = clip->getProducerIntProperty("length");
+ if (currentLength <= dia_ui->duration()) {
+ newprops.insert("length", QString::number(dia_ui->duration()));
+ } else {
+ newprops.insert("length", clip->getProducerProperty("length"));
+ }
+ }
+ // trigger producer reload
+ newprops.insert("force_reload", "2");
+ if (!path.isEmpty()) {
+ // we are editing an external file, asked if we want to detach from that file or save the result to that title file.
+ if (KMessageBox::questionYesNo(pCore->window(), i18n("You are editing an external title clip (%1). Do you want to save your changes to the title file or save the changes for this project only?", path), i18n("Save Title"), KGuiItem(i18n("Save to title file")), KGuiItem(i18n("Save in project only"))) == KMessageBox::Yes) {
+ // save to external file
+ dia_ui->saveTitle(QUrl::fromLocalFile(path));
+ } else {
+ newprops.insert("resource", QString());
+ }
+ }
+ slotEditClipCommand(clip->clipId(), clip->currentProperties(newprops), newprops);
+ }
+ delete dia_ui;
+}
+
+void Bin::slotResetInfoMessage()
+{
+ m_errorLog.clear();
+ QList<QAction *> actions = m_infoMessage->actions();
+ for (int i = 0; i < actions.count(); ++i) {
+ m_infoMessage->removeAction(actions.at(i));
+ }
+}
+
+void Bin::emitMessage(const QString &text, MessageType type)
+{
+ emit displayMessage(text, type);
+}
+
diff --git a/src/bin/bin.h b/src/bin/bin.h
new file mode 100644
index 0000000..d58efcb
--- /dev/null
+++ b/src/bin/bin.h
@@ -0,0 +1,561 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef KDENLIVE_BIN_H
+#define KDENLIVE_BIN_H
+
+#include "abstractprojectitem.h"
+#include "timecode.h"
+
+#include <KMessageWidget>
+
+#include <QWidget>
+#include <QApplication>
+#include <QStyledItemDelegate>
+#include <QPainter>
+#include <QDomElement>
+#include <QPushButton>
+
+class KdenliveDoc;
+class ClipController;
+class QSplitter;
+class QTimeLine;
+class KToolBar;
+class KSplitterCollapserButton;
+class QMenu;
+class QToolButton;
+class QUndoCommand;
+class ProjectItemModel;
+class ProjectClip;
+class ProjectFolder;
+class AbstractProjectItem;
+class Monitor;
+class QItemSelectionModel;
+class ProjectSortProxyModel;
+class JobManager;
+class ProjectFolderUp;
+
+namespace Mlt {
+ class Producer;
+};
+
+class BinMessageWidget: public KMessageWidget
+{
+ Q_OBJECT
+public:
+ BinMessageWidget(QWidget *parent = 0);
+ BinMessageWidget(const QString &text, QWidget *parent = 0);
+
+protected:
+ bool event(QEvent* ev);
+
+signals:
+ void messageClosing();
+};
+
+
+class SmallJobLabel: public QPushButton
+{
+ Q_OBJECT
+public:
+ SmallJobLabel(QWidget *parent = 0);
+ static const QString getStyleSheet(const QPalette &p);
+ void setAction(QAction *action);
+private:
+ enum ItemRole {
+ NameRole = Qt::UserRole,
+ DurationRole,
+ UsageRole
+ };
+
+ QTimeLine* m_timeLine;
+ QAction *m_action;
+
+public slots:
+ void slotSetJobCount(int jobCount);
+
+private slots:
+ void slotTimeLineChanged(qreal value);
+ void slotTimeLineFinished();
+};
+
+
+/**
+ * @class BinItemDelegate
+ * @brief This class is responsible for drawing items in the QTreeView.
+ */
+
+class BinItemDelegate: public QStyledItemDelegate
+{
+public:
+ BinItemDelegate(QObject* parent = 0): QStyledItemDelegate(parent) {
+ }
+
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+ {
+ QSize hint = QStyledItemDelegate::sizeHint(option, index);
+ QString text = index.data(AbstractProjectItem::DataName).toString();
+ QRectF r = option.rect;
+ QFont ft = option.font;
+ ft.setBold(true);
+ QFontMetricsF fm(ft);
+ QStyle *style = option.widget ? option.widget->style() : QApplication::style();
+ const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
+ int width = fm.boundingRect(r, Qt::AlignLeft | Qt::AlignTop, text).width() + option.decorationSize.width() + 2 * textMargin;
+ hint.setWidth(width);
+ int type = index.data(AbstractProjectItem::ItemTypeRole).toInt();
+ if (type == AbstractProjectItem::FolderItem || type == AbstractProjectItem::FolderUpItem) {
+ return QSize(hint.width(), qMin(option.fontMetrics.lineSpacing() + 4, hint.height()));
+ }
+ if (type == AbstractProjectItem::ClipItem) {
+ return QSize(hint.width(), qMax(option.fontMetrics.lineSpacing() * 2 + 4, qMax(hint.height(), option.decorationSize.height())));
+ }
+ if (type == AbstractProjectItem::SubClipItem) {
+ return QSize(hint.width(), qMin((int) (option.fontMetrics.lineSpacing() * 1.5) + 4, hint.height()));
+ }
+ QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
+ QString line1 = index.data(Qt::DisplayRole).toString();
+ QString line2 = index.data(Qt::UserRole).toString();
+
+ int textW = qMax(option.fontMetrics.width(line1), option.fontMetrics.width(line2));
+ QSize iconSize = icon.actualSize(option.decorationSize);
+
+ return QSize(qMax(textW, iconSize.width()) + 4, option.fontMetrics.lineSpacing() * 2 + 4);
+ }
+
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
+ if (index.column() == 0 && !index.data().isNull()) {
+ QRect r1 = option.rect;
+ painter->save();
+ painter->setClipRect(r1);
+ QStyleOptionViewItemV4 opt(option);
+ initStyleOption(&opt, index);
+ int type = index.data(AbstractProjectItem::ItemTypeRole).toInt();
+ QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
+ const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
+ //QRect r = QStyle::alignedRect(opt.direction, Qt::AlignVCenter | Qt::AlignLeft, opt.decorationSize, r1);
+
+ style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
+ if (option.state & QStyle::State_Selected) {
+ painter->setPen(option.palette.highlightedText().color());
+ }
+ QRect r = r1;
+ r.setWidth(opt.decorationSize.width());
+ // Draw thumbnail
+ opt.icon.paint(painter, r);
+
+ int decoWidth = r.width() + 2 * textMargin;
+ QFont font = painter->font();
+ font.setBold(true);
+ painter->setFont(font);
+ if (type == AbstractProjectItem::ClipItem) {
+ int mid = (int)((r1.height() / 2));
+ r1.adjust(decoWidth, 0, 0, -mid);
+ QRect r2 = option.rect;
+ r2.adjust(decoWidth, mid, 0, 0);
+ QRectF bounding;
+ painter->drawText(r1, Qt::AlignLeft | Qt::AlignTop, index.data(AbstractProjectItem::DataName).toString(), &bounding);
+ font.setBold(false);
+ painter->setFont(font);
+ QString subText = index.data(AbstractProjectItem::DataDuration).toString();
+ //int usage = index.data(UsageRole).toInt();
+ //if (usage != 0) subText.append(QString(" (%1)").arg(usage));
+ //if (option.state & (QStyle::State_Selected)) painter->setPen(option.palette.color(QPalette::Mid));
+ r2.adjust(0, bounding.bottom() - r2.top(), 0, 0);
+ QColor subTextColor = painter->pen().color();
+ subTextColor.setAlphaF(.5);
+ painter->setPen(subTextColor);
+ painter->drawText(r2, Qt::AlignLeft | Qt::AlignTop , subText, &bounding);
+
+ // Overlay icon if necessary
+ QVariant v = index.data(AbstractProjectItem::IconOverlay);
+ if (!v.isNull()) {
+ QIcon reload = QIcon::fromTheme(v.toString());
+ r.setTop(r.bottom() - bounding.height());
+ r.setWidth(bounding.height());
+ reload.paint(painter, r);
+ }
+
+ int jobProgress = index.data(AbstractProjectItem::JobProgress).toInt();
+ if (jobProgress != 0 && jobProgress != JobDone && jobProgress != JobAborted) {
+ if (jobProgress != JobCrashed) {
+ // Draw job progress bar
+ QColor color = option.palette.alternateBase().color();
+ painter->setPen(Qt::NoPen);
+ color.setAlpha(180);
+ painter->setBrush(QBrush(color));
+ QRect progress(r1.x() + 1, opt.rect.bottom() - 12, r1.width() / 2, 8);
+ painter->drawRect(progress);
+ painter->setBrush(option.palette.text());
+ if (jobProgress > 0) {
+ progress.adjust(1, 1, 0, -1);
+ progress.setWidth((progress.width() - 4) * jobProgress / 100);
+ painter->drawRect(progress);
+ } else if (jobProgress == JobWaiting) {
+ // Draw kind of a pause icon
+ progress.adjust(1, 1, 0, -1);
+ progress.setWidth(2);
+ painter->drawRect(progress);
+ progress.moveLeft(progress.right() + 2);
+ painter->drawRect(progress);
+ }
+ } else if (jobProgress == JobCrashed) {
+ QString jobText = index.data(AbstractProjectItem::JobMessage).toString();
+ if (!jobText.isEmpty()) {
+ QRectF txtBounding = painter->boundingRect(r2, Qt::AlignRight | Qt::AlignVCenter, " " + jobText + " ");
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(option.palette.highlight());
+ painter->drawRoundedRect(txtBounding, 2, 2);
+ painter->setPen(option.palette.highlightedText().color());
+ painter->drawText(txtBounding, Qt::AlignCenter, jobText);
+ }
+ }
+ }
+ }
+ else {
+ r1.adjust(decoWidth, 0, 0, 0);
+ QRectF bounding;
+ painter->drawText(r1, Qt::AlignLeft | Qt::AlignTop, index.data(AbstractProjectItem::DataName).toString(), &bounding);
+ }
+ painter->restore();
+ } else {
+ QStyledItemDelegate::paint(painter, option, index);
+ }
+ }
+};
+
+/**
+ * @class EventEater
+ * @brief Filter mouse clicks in the Item View.
+ */
+
+class EventEater : public QObject
+{
+ Q_OBJECT
+public:
+ EventEater(QObject *parent = 0);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event);
+
+signals:
+ void focusClipMonitor();
+ void addClip();
+ void deleteSelectedClips();
+ void itemDoubleClicked(const QModelIndex&, const QPoint pos);
+ //void editItemInTimeline(const QString&, const QString&, ProducerWrapper*);
+};
+
+
+/**
+ * @class Bin
+ * @brief The bin widget takes care of both item model and view upon project opening.
+ */
+
+class Bin : public QWidget
+{
+ Q_OBJECT
+
+ /** @brief Defines the view types (icon view, tree view,...) */
+ enum BinViewType {BinTreeView, BinIconView };
+
+public:
+ explicit Bin(QWidget* parent = 0);
+ ~Bin();
+
+ /** @brief Sets the document for the bin and initialize some stuff */
+ void setDocument(KdenliveDoc *project);
+
+ /** @brief Returns the root folder, which is the parent for all items in the view */
+ ProjectFolder *rootFolder();
+
+ /** @brief Create a clip item from its xml description */
+ void createClip(QDomElement xml);
+
+ /** @brief Returns current doc's project ratio for thumbnail scaling */
+ double projectRatio();
+
+ /** @brief Used to notify the Model View that an item was updated */
+ void emitItemUpdated(AbstractProjectItem* item);
+
+ /** @brief Set monitor associated with this bin (clipmonitor) */
+ void setMonitor(Monitor *monitor);
+
+ /** @brief Returns the clip monitor */
+ Monitor *monitor();
+
+ /** @brief Open a producer in the clip monitor */
+ void openProducer(ClipController *controller);
+ void openProducer(ClipController *controller, int in, int out);
+
+ /** @brief Trigger deletion of an item */
+ void deleteClip(const QString &id);
+
+ /** @brief Get a clip from it's id */
+ ProjectClip *getBinClip(const QString &id);
+
+ /** @brief Returns a list of selected clips */
+ QList <ProjectClip *> selectedClips();
+
+ /** @brief Start a job of selected type for a clip */
+ void startJob(const QString &id, AbstractClipJob::JOBTYPE type);
+
+ /** @brief Discard jobs from a chosen type, use NOJOBTYPE to discard all jobs for this clip */
+ void discardJobs(const QString &id, AbstractClipJob::JOBTYPE type = AbstractClipJob::NOJOBTYPE);
+
+ /** @brief Check if there is a job waiting / running for this clip */
+ bool hasPendingJob(const QString &id, AbstractClipJob::JOBTYPE type);
+
+ /** @brief Reload / replace a producer */
+ void reloadProducer(const QString &id, QDomElement xml);
+
+ /** @brief Current producer has changed, refresh monitor and timeline*/
+ void refreshClip(const QString &id);
+
+ /** @brief Some stuff used to notify the Item Model */
+ void emitAboutToAddItem(AbstractProjectItem* item);
+ void emitItemAdded(AbstractProjectItem* item);
+ void emitAboutToRemoveItem(AbstractProjectItem* item);
+ void emitItemRemoved(AbstractProjectItem* item);
+ void setupMenu(QMenu *addMenu, QAction *defaultAction, QHash <QString, QAction*> actions);
+
+ /** @brief The source file was modified, we will reload it soon, disable item in the meantime */
+ void setWaitingStatus(const QString &id);
+
+
+ const QString getDocumentProperty(const QString &key);
+
+ /** @brief A proxy clip was just created, pass it to the responsible item */
+ void gotProxy(const QString &id);
+
+ /** @brief Returns the job manager, responsible for handling clip jobs */
+ JobManager *jobManager();
+
+ /** @brief Get the document's renderer frame size */
+ const QSize getRenderSize();
+
+ /** @brief Give a number available for a clip id, used when adding a new clip to the project. Id must be unique */
+ int getFreeClipId();
+
+ /** @brief Give a number available for a folder id, used when adding a new folder to the project. Id must be unique */
+ int getFreeFolderId();
+
+ /** @brief Returns the id of the last inserted clip */
+ int lastClipId() const;
+
+ /** @brief Ask MLT to reload this clip's producer */
+ void reloadClip(const QString &id);
+
+ /** @brief Delete a folder */
+ void doRemoveFolder(const QString &id);
+ /** @brief Add a folder */
+ void doAddFolder(const QString &id, const QString &name, const QString &parentId);
+ void removeFolder(const QString &id, QUndoCommand *deleteCommand);
+ void doMoveClip(const QString &id, const QString &newParentId);
+ void doMoveFolder(const QString &id, const QString &newParentId);
+ void setupGeneratorMenu(const QHash<QString,QMenu*>& menus);
+ void startClipJob(const QStringList &params);
+ void droppedUrls(QList <QUrl> urls, const QMap<QString,QString> properties = QMap<QString,QString>());
+ void displayMessage(const QString &text, KMessageWidget::MessageType type);
+
+ void addClipCut(const QString&id, int in, int out);
+ void removeClipCut(const QString&id, int in, int out);
+
+ /** @brief Create the subclips defined in the parent clip. */
+ void loadSubClips(const QString&id, const QMap <QString,QString> data);
+
+ /** @brief Select a clip in the Bin from its id. */
+ void selectClipById(const QString &id);
+ /** @brief Set focus to the Bin view. */
+ void focusBinView() const;
+ /** @brief Get a string list of all clip ids that are inside a folder defined by id. */
+ QStringList getBinFolderClipIds(const QString &id) const;
+ /** @brief Build a rename folder command. */
+ void renameFolderCommand(const QString &id, const QString &newName, const QString &oldName);
+ /** @brief Rename a folder and store new name in MLT. */
+ void renameFolder(const QString &id, const QString &name);
+ /** @brief Build a rename subclip command. */
+ void renameSubClipCommand(const QString &id, const QString &newName, const QString oldName, int in, int out);
+ /** @brief Rename a clip zone (subclip). */
+ void renameSubClip(const QString &id, const QString &newName, const QString oldName, int in, int out);
+ /** @brief Returns current project's timecode. */
+ Timecode projectTimecode() const;
+ /** @brief Trigger timecode format refresh where needed. */
+ void updateTimecodeFormat();
+ /** @brief If clip monitor is displaying clip with id @param id, refresh markers. */
+ void refreshClipMarkers(const QString &id);
+ /** @brief Delete a clip marker. */
+ void deleteClipMarker(const QString &comment, const QString &id, const GenTime &position);
+ /** @brief Delete all markers from @param id clip. */
+ void deleteAllClipMarkers(const QString &id);
+ /** @brief Remove an effect from a bin clip. */
+ void removeEffect(const QString &id, const QDomElement &effect);
+ /** @brief Add an effect to a bin clip. */
+ void addEffect(const QString &id, QDomElement &effect);
+ /** @brief Edit an effect settings to a bin clip. */
+ void editMasterEffect(ClipController *ctl);
+ /** @brief Returns current project's folder for storing items. */
+ QUrl projectFolder() const;
+ /** @brief Display a message about an operation in status bar. */
+ void emitMessage(const QString &, MessageType);
+
+private slots:
+ void slotAddClip();
+ void slotReloadClip();
+
+ /** @brief Setup the bin view type (icon view, tree view, ...).
+ * @param action The action whose data defines the view type or NULL to keep default view */
+ void slotInitView(QAction *action);
+
+ /** @brief Update status for clip jobs */
+ void slotUpdateJobStatus(const QString&, int, int, const QString &label = QString(), const QString &actionName = QString(), const QString &details = QString());
+ void slotSetIconSize(int size);
+ void rowsInserted(const QModelIndex &parent, int start, int end);
+ void rowsRemoved(const QModelIndex &parent, int start, int end);
+ void selectProxyModel(const QModelIndex &id);
+ void autoSelect();
+ void closeEditing();
+ void refreshEditedClip();
+ void slotSaveHeaders();
+ void slotItemDropped(QStringList ids, const QModelIndex &parent);
+ void slotItemDropped(const QList<QUrl>&urls, const QModelIndex &parent);
+ void slotEffectDropped(QString effect, const QModelIndex &parent);
+ void slotItemEdited(QModelIndex,QModelIndex,QVector<int>);
+ void slotAddUrl(QString url, QMap <QString, QString> data = QMap <QString, QString>());
+ void slotPrepareJobsMenu();
+ void slotShowJobLog();
+ /** @brief process clip job result. */
+ void slotGotFilterJobResults(QString ,int , int, stringMap, stringMap);
+ /** @brief Reset all text and log data from info message widget. */
+ void slotResetInfoMessage();
+
+public slots:
+ void slotThumbnailReady(const QString &id, const QImage &img, bool fromFile = false);
+ /** @brief The producer for this clip is ready.
+ * @param id the clip id
+ * @param controller The Controller for this clip
+ */
+ void slotProducerReady(requestClipInfo info, ClipController *controller);
+ /** @brief Create a folder when opening a document */
+ void slotLoadFolders(QMap<QString,QString> foldersData);
+ void slotDeleteClip();
+ void slotRefreshClipProperties();
+ void slotItemDoubleClicked(const QModelIndex &ix, const QPoint pos);
+ void slotSwitchClipProperties(const QModelIndex &ix);
+ void slotSwitchClipProperties();
+ void slotAddFolder();
+ void slotCreateProjectClip();
+ /** @brief Start a Cut Clip job on this clip (extract selected zone using FFmpeg) */
+ void slotStartCutJob(const QString &id);
+ /** @brief Triggered by a clip job action, start the job */
+ void slotStartClipJob(bool enable);
+ void slotEditClipCommand(const QString &id, QMap<QString, QString>oldProps, QMap<QString, QString>newProps);
+ void slotCancelRunningJob(const QString &id, const QMap<QString, QString> &newProps);
+ /** @brief Start a filter job requested by a filter applied in timeline */
+ void slotStartFilterJob(const ItemInfo &info, const QString&id, QMap <QString, QString> &filterParams, QMap <QString, QString> &consumerParams, QMap <QString, QString> &extraParams);
+ /** @brief Add a sub clip */
+ void slotAddClipCut(const QString&id, int in, int out);
+ /** @brief Open current clip in an external editing application */
+ void slotOpenClip();
+ /** @brief Creates a AddClipCommand to add, edit or delete a marker.
+ * @param id Id of the marker's clip
+ * @param t Position of the marker
+ * @param c Comment of the marker */
+ void slotAddClipMarker(const QString &id, QList <CommentedTime> newMarker, QUndoCommand *groupCommand = 0);
+ void slotLoadClipMarkers(const QString &id);
+ void slotDuplicateClip();
+ void slotDeleteEffect(const QString &id, QDomElement effect);
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *event);
+
+private:
+ ProjectItemModel *m_itemModel;
+ QAbstractItemView *m_itemView;
+ ProjectFolder *m_rootFolder;
+ /** @brief An "Up" item that is inserted in bin when using icon view so that user can navigate up */
+ ProjectFolderUp *m_folderUp;
+ BinItemDelegate *m_binTreeViewDelegate;
+ ProjectSortProxyModel *m_proxyModel;
+ JobManager *m_jobManager;
+ KToolBar *m_toolbar;
+ KdenliveDoc* m_doc;
+ QToolButton *m_addButton;
+ QMenu *m_extractAudioAction;
+ QMenu *m_transcodeAction;
+ QMenu *m_clipsActionsMenu;
+ QSplitter *m_splitter;
+ /** @brief Holds an available unique id for a clip to be created */
+ int m_clipCounter;
+ /** @brief Holds an available unique id for a folder to be created */
+ int m_folderCounter;
+ /** @brief Default view type (icon, tree, ...) */
+ BinViewType m_listType;
+ /** @brief Default icon size for the views. */
+ QSize m_iconSize;
+ /** @brief Keeps the column width info of the tree view. */
+ QByteArray m_headerInfo;
+ EventEater *m_eventEater;
+ QWidget *m_propertiesPanel;
+ QSlider *m_slider;
+ KSplitterCollapserButton *m_collapser;
+ Monitor *m_monitor;
+ QIcon m_blankThumb;
+ QMenu *m_menu;
+ QAction *m_openAction;
+ QAction *m_reloadAction;
+ QAction *m_duplicateAction;
+ QAction *m_proxyAction;
+ QAction *m_editAction;
+ QAction *m_deleteAction;
+ QMenu *m_jobsMenu;
+ QAction *m_cancelJobs;
+ QAction *m_discardCurrentClipJobs;
+ SmallJobLabel *m_infoLabel;
+ /** @brief The info widget for failed jobs. */
+ BinMessageWidget *m_infoMessage;
+ /** @brief The action that will trigger the log dialog. */
+ QAction *m_logAction;
+ QStringList m_errorLog;
+ void showClipProperties(ProjectClip *clip);
+ void selectModel(const QModelIndex &id);
+ const QStringList getFolderInfo(QModelIndex selectedIx = QModelIndex());
+ /** @brief Get the QModelIndex value for an item in the Bin. */
+ QModelIndex getIndexForId(const QString &id, bool folderWanted) const;
+ ProjectClip *getFirstSelectedClip();
+ void showTitleWidget(ProjectClip *clip);
+
+signals:
+ void itemUpdated(AbstractProjectItem*);
+ void producerReady(const QString &id);
+ /** @brief Save folder info into MLT. */
+ void storeFolder(QString folderId, QString parentId, QString oldParentId, QString folderName);
+ void gotFilterJobResults(QString,int,int,stringMap,stringMap);
+ /** @brief The clip was changed and thumbnail needs a refresh. */
+ void clipNeedsReload(const QString &,bool);
+ /** @brief Trigger timecode format refresh where needed. */
+ void refreshTimeCode();
+ void masterClipSelected(ClipController *, Monitor *);
+ void displayMessage(const QString &, MessageType);
+};
+
+#endif
diff --git a/src/bin/bincommands.cpp b/src/bin/bincommands.cpp
new file mode 100644
index 0000000..66a309e
--- /dev/null
+++ b/src/bin/bincommands.cpp
@@ -0,0 +1,206 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Jean-Baptiste Mardelle ([email protected]) *
+ * *
+ * *
+ * 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) 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "bincommands.h"
+#include "bin.h"
+
+#include <klocalizedstring.h>
+
+AddBinFolderCommand::AddBinFolderCommand(Bin *bin, const QString &id, const QString &name, const QString &parentId, bool remove, QUndoCommand *parent) :
+ QUndoCommand(parent),
+ m_bin(bin),
+ m_id(id),
+ m_name(name),
+ m_parentId(parentId),
+ m_remove(remove)
+{
+ if (remove) setText(i18n("Remove Folder"));
+ else setText(i18n("Add Folder"));
+}
+// virtual
+void AddBinFolderCommand::undo()
+{
+ if (m_remove)
+ m_bin->doAddFolder(m_id, m_name, m_parentId);
+ else
+ m_bin->doRemoveFolder(m_id);
+}
+// virtual
+void AddBinFolderCommand::redo()
+{
+ if (m_remove)
+ m_bin->doRemoveFolder(m_id);
+ else
+ m_bin->doAddFolder(m_id, m_name, m_parentId);
+}
+
+
+MoveBinClipCommand::MoveBinClipCommand(Bin *bin, const QString &clipId, const QString &oldParentId, const QString &newParentId, QUndoCommand *parent) :
+ QUndoCommand(parent),
+ m_bin(bin),
+ m_clipId(clipId),
+ m_oldParentId(oldParentId),
+ m_newParentId(newParentId)
+{
+ setText(i18n("Move Clip"));
+}
+// virtual
+void MoveBinClipCommand::undo()
+{
+ m_bin->doMoveClip(m_clipId, m_oldParentId);
+}
+// virtual
+void MoveBinClipCommand::redo()
+{
+ m_bin->doMoveClip(m_clipId, m_newParentId);
+}
+
+MoveBinFolderCommand::MoveBinFolderCommand(Bin *bin, const QString &clipId, const QString &oldParentId, const QString &newParentId, QUndoCommand *parent) :
+ QUndoCommand(parent),
+ m_bin(bin),
+ m_clipId(clipId),
+ m_oldParentId(oldParentId),
+ m_newParentId(newParentId)
+{
+ setText(i18n("Move Clip"));
+}
+// virtual
+void MoveBinFolderCommand::undo()
+{
+ m_bin->doMoveFolder(m_clipId, m_oldParentId);
+}
+// virtual
+void MoveBinFolderCommand::redo()
+{
+ m_bin->doMoveFolder(m_clipId, m_newParentId);
+}
+
+RenameBinFolderCommand::RenameBinFolderCommand(Bin *bin, const QString &folderId, const QString &newName, const QString &oldName, QUndoCommand *parent) :
+ QUndoCommand(parent),
+ m_bin(bin),
+ m_clipId(folderId),
+ m_oldName(oldName),
+ m_newName(newName)
+{
+ setText(i18n("Rename Folder"));
+}
+// virtual
+void RenameBinFolderCommand::undo()
+{
+ m_bin->renameFolder(m_clipId, m_oldName);
+}
+// virtual
+void RenameBinFolderCommand::redo()
+{
+ m_bin->renameFolder(m_clipId, m_newName);
+}
+
+AddBinEffectCommand::AddBinEffectCommand(Bin *bin, const QString &clipId, QDomElement &effect, QUndoCommand *parent) :
+ QUndoCommand(parent),
+ m_bin(bin),
+ m_clipId(clipId),
+ m_effect(effect)
+{
+ setText(i18n("Add Bin Effect"));
+}
+// virtual
+void AddBinEffectCommand::undo()
+{
+ m_bin->removeEffect(m_clipId, m_effect);
+}
+// virtual
+void AddBinEffectCommand::redo()
+{
+ m_bin->addEffect(m_clipId, m_effect);
+}
+
+RemoveBinEffectCommand::RemoveBinEffectCommand(Bin *bin, const QString &clipId, QDomElement &effect, QUndoCommand *parent) :
+ QUndoCommand(parent),
+ m_bin(bin),
+ m_clipId(clipId),
+ m_effect(effect)
+{
+ setText(i18n("Remove Bin Effect"));
+}
+// virtual
+void RemoveBinEffectCommand::undo()
+{
+ m_bin->addEffect(m_clipId, m_effect);
+}
+// virtual
+void RemoveBinEffectCommand::redo()
+{
+ m_bin->removeEffect(m_clipId, m_effect);
+}
+
+RenameBinSubClipCommand::RenameBinSubClipCommand(Bin *bin, const QString &clipId, const QString &newName, const QString &oldName, int in, int out, QUndoCommand *parent) :
+ QUndoCommand(parent),
+ m_bin(bin),
+ m_clipId(clipId),
+ m_oldName(oldName),
+ m_newName(newName),
+ m_in(in),
+ m_out(out)
+{
+ setText(i18n("Rename Zone"));
+}
+// virtual
+void RenameBinSubClipCommand::undo()
+{
+ m_bin->renameSubClip(m_clipId, m_oldName, m_newName, m_in, m_out);
+}
+// virtual
+void RenameBinSubClipCommand::redo()
+{
+ m_bin->renameSubClip(m_clipId, m_newName, m_oldName, m_in, m_out);
+}
+
+
+AddBinClipCutCommand::AddBinClipCutCommand(Bin *bin, const QString &clipId, int in, int out, bool add, QUndoCommand *parent) :
+ QUndoCommand(parent)
+ , m_bin(bin)
+ , m_clipId(clipId)
+ , m_in(in)
+ , m_out(out)
+ , m_addCut(add)
+{
+ setText(i18n("Add Sub Clip"));
+}
+
+// virtual
+void AddBinClipCutCommand::undo()
+{
+ if (m_addCut) {
+ m_bin->removeClipCut(m_clipId, m_in, m_out);
+ }
+ else {
+ m_bin->addClipCut(m_clipId, m_in, m_out);
+ }
+}
+// virtual
+void AddBinClipCutCommand::redo()
+{
+ if (m_addCut) {
+ m_bin->addClipCut(m_clipId, m_in, m_out);
+ }
+ else {
+ m_bin->removeClipCut(m_clipId, m_in, m_out);
+ }
+}
diff --git a/src/bin/bincommands.h b/src/bin/bincommands.h
new file mode 100644
index 0000000..8af1f9f
--- /dev/null
+++ b/src/bin/bincommands.h
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Jean-Baptiste Mardelle ([email protected]) *
+ * *
+ * 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) 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+
+#ifndef BINCOMMANDS_H
+#define BINCOMMANDS_H
+
+#include <QUndoCommand>
+#include <QDomElement>
+
+class Bin;
+
+
+class AddBinFolderCommand : public QUndoCommand
+{
+public:
+ explicit AddBinFolderCommand(Bin *bin, const QString &id, const QString &name, const QString &parentId, bool remove = false, QUndoCommand * parent = 0);
+ void undo();
+ void redo();
+private:
+ Bin *m_bin;
+ QString m_id;
+ QString m_name;
+ QString m_parentId;
+ bool m_remove;
+};
+
+
+class MoveBinClipCommand : public QUndoCommand
+{
+public:
+ explicit MoveBinClipCommand(Bin *bin, const QString &clipId, const QString &oldParentId, const QString &newParentId, QUndoCommand *parent = 0);
+ void undo();
+ void redo();
+private:
+ Bin *m_bin;
+ QString m_clipId;
+ QString m_oldParentId;
+ QString m_newParentId;
+};
+
+class MoveBinFolderCommand : public QUndoCommand
+{
+public:
+ explicit MoveBinFolderCommand(Bin *bin, const QString &clipId, const QString &oldParentId, const QString &newParentId, QUndoCommand *parent = 0);
+ void undo();
+ void redo();
+private:
+ Bin *m_bin;
+ QString m_clipId;
+ QString m_oldParentId;
+ QString m_newParentId;
+};
+
+class RenameBinFolderCommand : public QUndoCommand
+{
+public:
+ explicit RenameBinFolderCommand(Bin *bin, const QString &folderId, const QString &newName, const QString &oldName, QUndoCommand *parent = 0);
+ void undo();
+ void redo();
+private:
+ Bin *m_bin;
+ QString m_clipId;
+ QString m_oldName;
+ QString m_newName;
+};
+
+class AddBinEffectCommand : public QUndoCommand
+{
+public:
+ explicit AddBinEffectCommand(Bin *bin, const QString &clipId, QDomElement &effect, QUndoCommand *parent = 0);
+ void undo();
+ void redo();
+private:
+ Bin *m_bin;
+ QString m_clipId;
+ QDomElement m_effect;
+};
+
+class RemoveBinEffectCommand : public QUndoCommand
+{
+public:
+ explicit RemoveBinEffectCommand(Bin *bin, const QString &clipId, QDomElement &effect, QUndoCommand *parent = 0);
+ void undo();
+ void redo();
+private:
+ Bin *m_bin;
+ QString m_clipId;
+ QDomElement m_effect;
+};
+
+class RenameBinSubClipCommand : public QUndoCommand
+{
+public:
+ explicit RenameBinSubClipCommand(Bin *bin, const QString &clipId, const QString &newName, const QString &oldName, int in, int out, QUndoCommand *parent = 0);
+ void undo();
+ void redo();
+private:
+ Bin *m_bin;
+ QString m_clipId;
+ QString m_oldName;
+ QString m_newName;
+ int m_in;
+ int m_out;
+};
+
+class AddBinClipCutCommand : public QUndoCommand
+{
+public:
+ explicit AddBinClipCutCommand(Bin *bin, const QString &clipId, int in, int out, bool add, QUndoCommand *parent = 0);
+ void undo();
+ void redo();
+private:
+ Bin *m_bin;
+ QString m_clipId;
+ int m_in;
+ int m_out;
+ bool m_addCut;
+};
+
+#endif
+
diff --git a/src/bin/projectclip.cpp b/src/bin/projectclip.cpp
new file mode 100644
index 0000000..3403c13
--- /dev/null
+++ b/src/bin/projectclip.cpp
@@ -0,0 +1,784 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "projectclip.h"
+#include "projectfolder.h"
+#include "projectsubclip.h"
+#include "bin.h"
+#include "timecode.h"
+#include "doc/kthumb.h"
+#include "kdenlivesettings.h"
+#include "project/projectcommands.h"
+#include "mltcontroller/clipcontroller.h"
+#include "lib/audio/audioStreamInfo.h"
+#include "mltcontroller/clippropertiescontroller.h"
+
+#include <QDomElement>
+#include <QFile>
+#include <QDir>
+#include <QDebug>
+#include <QCryptographicHash>
+#include <QtConcurrent>
+#include <KLocalizedString>
+
+
+
+ProjectClip::ProjectClip(const QString &id, QIcon thumb, ClipController *controller, ProjectFolder* parent) :
+ AbstractProjectItem(AbstractProjectItem::ClipItem, id, parent)
+ , m_controller(controller)
+ , audioFrameCache()
+ , m_audioThumbCreated(false)
+ , m_gpuProducer(NULL)
+ , m_abortAudioThumb(false)
+{
+ m_clipStatus = StatusReady;
+ m_thumbnail = thumb;
+ m_name = m_controller->clipName();
+ m_duration = m_controller->getStringDuration();
+ getFileHash();
+ setParent(parent);
+ bin()->loadSubClips(id, m_controller->getSubClips());
+ if (KdenliveSettings::audiothumbnails()) {
+ QtConcurrent::run(this, &ProjectClip::slotCreateAudioThumbs);
+ }
+}
+
+ProjectClip::ProjectClip(const QDomElement& description, QIcon thumb, ProjectFolder* parent) :
+ AbstractProjectItem(AbstractProjectItem::ClipItem, description, parent)
+ , m_controller(NULL)
+ , audioFrameCache()
+ , m_audioThumbCreated(false)
+ , m_gpuProducer(NULL)
+ , m_abortAudioThumb(false)
+{
+ Q_ASSERT(description.hasAttribute("id"));
+ m_clipStatus = StatusWaiting;
+ m_thumbnail = thumb;
+ m_temporaryUrl = QUrl::fromLocalFile(getXmlProperty(description, "resource"));
+ QString clipName = getXmlProperty(description, "kdenlive:clipname");
+ if (!clipName.isEmpty()) {
+ m_name = clipName;
+ }
+ else if (m_temporaryUrl.isValid()) {
+ m_name = m_temporaryUrl.fileName();
+ }
+ else m_name = i18n("Untitled");
+ setParent(parent);
+}
+
+
+ProjectClip::~ProjectClip()
+{
+ // controller is deleted in bincontroller
+}
+
+QString ProjectClip::getToolTip() const
+{
+ return url().toLocalFile();
+}
+
+QString ProjectClip::getXmlProperty(const QDomElement &producer, const QString &propertyName)
+{
+ QString value;
+ QDomNodeList props = producer.elementsByTagName("property");
+ for (int i = 0; i < props.count(); ++i) {
+ if (props.at(i).toElement().attribute("name") == propertyName) {
+ value = props.at(i).firstChild().nodeValue();
+ break;
+ }
+ }
+ return value;
+}
+
+void ProjectClip::updateAudioThumbnail(const audioByteArray& data)
+{
+ ////qDebug() << "CLIPBASE RECIEDVED AUDIO DATA*********************************************";
+ audioFrameCache = data;
+ m_audioThumbCreated = true;
+ emit gotAudioData();
+}
+
+QList < CommentedTime > ProjectClip::commentedSnapMarkers() const
+{
+ if (m_controller) return m_controller->commentedSnapMarkers();
+ return QList < CommentedTime > ();
+}
+
+bool ProjectClip::audioThumbCreated() const
+{
+ return m_audioThumbCreated;
+}
+
+ClipType ProjectClip::clipType() const
+{
+ if (m_controller == NULL) return Unknown;
+ return m_controller->clipType();
+}
+
+ProjectClip* ProjectClip::clip(const QString &id)
+{
+ if (id == m_id) {
+ return this;
+ }
+ return NULL;
+}
+
+ProjectFolder* ProjectClip::folder(const QString &id)
+{
+ return NULL;
+}
+
+ProjectSubClip* ProjectClip::getSubClip(int in, int out)
+{
+ ProjectSubClip *clip;
+ for (int i = 0; i < count(); ++i) {
+ clip = static_cast<ProjectSubClip *>(at(i))->subClip(in, out);
+ if (clip) {
+ return clip;
+ }
+ }
+ return NULL;
+}
+
+ProjectClip* ProjectClip::clipAt(int ix)
+{
+ if (ix == index()) {
+ return this;
+ }
+ return NULL;
+}
+
+/*bool ProjectClip::isValid() const
+{
+ return m_controller->isValid();
+}*/
+
+QUrl ProjectClip::url() const
+{
+ if (m_controller) return m_controller->clipUrl();
+ return m_temporaryUrl;
+}
+
+bool ProjectClip::hasLimitedDuration() const
+{
+ if (m_controller) {
+ return m_controller->hasLimitedDuration();
+ }
+ return true;
+}
+
+GenTime ProjectClip::duration() const
+{
+ if (m_controller) {
+ return m_controller->getPlaytime();
+ }
+ return GenTime();
+}
+
+QString ProjectClip::serializeClip()
+{
+ /*Mlt::Consumer *consumer = bin()->project()->xmlConsumer();
+ consumer->connect(*m_baseProducer);
+ consumer->run();
+ return QString::fromUtf8(consumer->get("kdenlive_clip"));*/
+ return QString();
+}
+
+void ProjectClip::reloadProducer(bool thumbnailOnly)
+{
+ QDomDocument doc;
+ QDomElement xml = toXml(doc);
+ if (thumbnailOnly) {
+ // set a special flag to request thumbnail only
+ xml.setAttribute("thumbnailOnly", "1");
+ }
+ bin()->reloadProducer(m_id, xml);
+}
+
+void ProjectClip::setCurrent(bool current, bool notify)
+{
+ AbstractProjectItem::setCurrent(current, notify);
+ if (current && m_controller) {
+ bin()->openProducer(m_controller);
+ bin()->editMasterEffect(m_controller);
+ }
+}
+
+QDomElement ProjectClip::toXml(QDomDocument& document)
+{
+ if (m_controller) {
+ m_controller->getProducerXML(document);
+ return document.documentElement().firstChildElement("producer");
+ }
+ return QDomElement();
+}
+
+void ProjectClip::setThumbnail(QImage img)
+{
+ QPixmap thumb = roundedPixmap(QPixmap::fromImage(img));
+ if (hasProxy() && !thumb.isNull()) {
+ // Overlay proxy icon
+ QPainter p(&thumb);
+ QColor c(220, 220, 10, 200);
+ QRect r(0, 0, thumb.height() / 2.5, thumb.height() / 2.5);
+ p.fillRect(r, c);
+ QFont font = p.font();
+ font.setPixelSize(r.height());
+ font.setBold(true);
+ p.setFont(font);
+ p.setPen(Qt::black);
+ p.drawText(r, Qt::AlignCenter, i18nc("The first letter of Proxy, used as abbreviation", "P"));
+ }
+ m_thumbnail = QIcon(thumb);
+ bin()->emitItemUpdated(this);
+}
+
+void ProjectClip::setProducer(ClipController *controller, bool replaceProducer)
+{
+ if (!replaceProducer && m_controller) {
+ qDebug()<<"// RECIEVED PRODUCER BUT WE ALREADY HAVE ONE\n----------";
+ return;
+ }
+ if (m_controller) {
+ // Replace clip for this controller
+ //m_controller->updateProducer(m_id, &(controller->originalProducer()));
+ delete controller;
+ }
+ else {
+ // We did not yet have the controller, update info
+ m_controller = controller;
+ if (m_name.isEmpty()) m_name = m_controller->clipName();
+ m_duration = m_controller->getStringDuration();
+ m_temporaryUrl.clear();
+ }
+ m_clipStatus = StatusReady;
+ bin()->emitItemUpdated(this);
+ getFileHash();
+ if (KdenliveSettings::audiothumbnails()) QtConcurrent::run(this, &ProjectClip::slotCreateAudioThumbs);
+}
+
+Mlt::Producer *ProjectClip::producer()
+{
+ if (!m_controller) return NULL;
+ return m_controller->masterProducer();
+}
+
+ClipController *ProjectClip::controller()
+{
+ return m_controller;
+}
+
+bool ProjectClip::isReady() const
+{
+ return m_controller!= NULL;
+}
+
+/*void ProjectClip::setZone(const QPoint &zone)
+{
+ m_zone = zone;
+}*/
+
+QPoint ProjectClip::zone() const
+{
+ int x = getProducerIntProperty("kdenlive:zone_in");
+ int y = getProducerIntProperty("kdenlive:zone_out");
+ return QPoint(x, y);
+}
+
+void ProjectClip::addMarker(int position)
+{
+ m_markers << position;
+ //bin()->markersUpdated(m_id, m_markers);
+}
+
+void ProjectClip::removeMarker(int position)
+{
+ m_markers.removeAll(position);
+ //bin()->markersUpdated(m_id, m_markers);
+}
+
+void ProjectClip::resetProducerProperty(const QString &name)
+{
+ if (m_controller) {
+ m_controller->resetProperty(name);
+ }
+}
+
+void ProjectClip::setProducerProperty(const QString &name, int data)
+{
+ if (m_controller) {
+ m_controller->setProperty(name, data);
+ }
+}
+
+void ProjectClip::setProducerProperty(const QString &name, double data)
+{
+ if (m_controller) {
+ m_controller->setProperty(name, data);
+ }
+}
+
+void ProjectClip::setProducerProperty(const QString &name, const QString &data)
+{
+ if (m_controller) {
+ m_controller->setProperty(name, data);
+ }
+}
+
+QMap <QString, QString> ProjectClip::currentProperties(const QMap <QString, QString> &props)
+{
+ QMap <QString, QString> currentProps;
+ if (!m_controller) {
+ return currentProps;
+ }
+ QMap<QString, QString>::const_iterator i = props.constBegin();
+ while (i != props.constEnd()) {
+ currentProps.insert(i.key(), m_controller->property(i.key()));
+ ++i;
+ }
+ return currentProps;
+}
+
+QColor ProjectClip::getProducerColorProperty(const QString &key) const
+{
+ if (m_controller) {
+ return m_controller->color_property(key);
+ }
+ return QColor();
+}
+
+int ProjectClip::getProducerIntProperty(const QString &key) const
+{
+ int value = 0;
+ if (m_controller) {
+ value = m_controller->int_property(key);
+ }
+ return value;
+}
+
+QString ProjectClip::getProducerProperty(const QString &key) const
+{
+ QString value;
+ if (m_controller) {
+ value = m_controller->property(key);
+ }
+ return value;
+}
+
+const QString ProjectClip::hash()
+{
+ if (m_controller) {
+ QString clipHash = m_controller->property("kdenlive:file_hash");
+ if (clipHash.isEmpty()) {
+ return getFileHash();
+ }
+ return clipHash;
+ }
+ return getFileHash();
+}
+
+const QString ProjectClip::getFileHash() const
+{
+ if (clipType() == SlideShow) return QString();
+ QFile file(m_controller ? m_controller->clipUrl().toLocalFile() : m_temporaryUrl.toLocalFile());
+ if (file.open(QIODevice::ReadOnly)) { // write size and hash only if resource points to a file
+ QByteArray fileData;
+ QByteArray fileHash;
+ ////qDebug() << "SETTING HASH of" << value;
+ //m_properties.insert("file_size", QString::number(file.size()));
+ /*
+ * 1 MB = 1 second per 450 files (or faster)
+ * 10 MB = 9 seconds per 450 files (or faster)
+ */
+ if (file.size() > 2000000) {
+ fileData = file.read(1000000);
+ if (file.seek(file.size() - 1000000))
+ fileData.append(file.readAll());
+ } else
+ fileData = file.readAll();
+ file.close();
+ fileHash = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
+ QString result = fileHash.toHex();
+ if (m_controller) m_controller->setProperty("kdenlive:file_hash", result);
+ return result;
+ }
+ return QString();
+}
+
+double ProjectClip::getOriginalFps() const
+{
+ if (!m_controller) return 0;
+ return m_controller->originalFps();
+}
+
+bool ProjectClip::hasProxy() const
+{
+ QString proxy = getProducerProperty("kdenlive:proxy");
+ if (proxy.isEmpty() || proxy == "-") return false;
+ return true;
+}
+
+void ProjectClip::setProperties(QMap <QString, QString> properties, bool refreshPanel)
+{
+ QMapIterator<QString, QString> i(properties);
+ bool refreshProducer = false;
+ QStringList keys;
+ keys << "luma_duration" << "luma_file" << "fade" << "ttl" << "softness" << "crop" << "animation";
+ while (i.hasNext()) {
+ i.next();
+ setProducerProperty(i.key(), i.value());
+ if (clipType() == SlideShow && keys.contains(i.key())) refreshProducer = true;
+ }
+ if (properties.contains("kdenlive:proxy")) {
+ QString value = properties.value("kdenlive:proxy");
+ // If value is "-", that means user manually disabled proxy on this clip
+ if (value.isEmpty() || value == "-") {
+ // reset proxy
+ if (bin()->hasPendingJob(m_id, AbstractClipJob::PROXYJOB)) {
+ bin()->discardJobs(m_id, AbstractClipJob::PROXYJOB);
+ }
+ else {
+ reloadProducer();
+ }
+ }
+ else {
+ // A proxy was requested, make sure to keep original url
+ setProducerProperty("kdenlive:originalurl", url().toLocalFile());
+ bin()->startJob(m_id, AbstractClipJob::PROXYJOB);
+ }
+ }
+ else if (properties.contains("resource")) {
+ // Clip resource changed, update thumbnail
+ if (clipType() == Color) {
+ //reloadProducer(true);
+ }
+ else {
+ reloadProducer();
+ }
+ refreshProducer = true;
+ }
+ if (properties.contains("xmldata")) {
+ refreshProducer = true;
+ }
+ if (properties.contains("length")) {
+ m_duration = m_controller->getStringDuration();
+ bin()->emitItemUpdated(this);
+ }
+
+ if (properties.contains("kdenlive:clipname")) {
+ m_name = properties.value("kdenlive:clipname");
+ bin()->emitItemUpdated(this);
+ }
+ if (refreshPanel) {
+ // Some of the clip properties have changed through a command, update properties panel
+ emit refreshPropertiesPanel();
+ }
+ if (refreshProducer) {
+ // producer has changed, refresh monitor
+ bin()->refreshClip(m_id);
+ }
+}
+
+void ProjectClip::setJobStatus(AbstractClipJob::JOBTYPE jobType, ClipJobStatus status, int progress, const QString &statusMessage)
+{
+ m_jobType = jobType;
+ if (progress > 0) {
+ if (m_jobProgress == progress) return;
+ m_jobProgress = progress;
+ }
+ else {
+ m_jobProgress = status;
+ if ((status == JobAborted || status == JobCrashed || status == JobDone) || !statusMessage.isEmpty()) {
+ m_jobMessage = statusMessage;
+ bin()->emitMessage(statusMessage, OperationCompletedMessage);
+ }
+ }
+ bin()->emitItemUpdated(this);
+}
+
+
+ClipPropertiesController *ProjectClip::buildProperties(QWidget *parent)
+{
+ ClipPropertiesController *panel = new ClipPropertiesController(bin()->projectTimecode(), m_controller, parent);
+ connect(this, SIGNAL(refreshPropertiesPanel()), panel, SLOT(slotReloadProperties()));
+ return panel;
+}
+
+void ProjectClip::updateParentInfo(const QString &folderid, const QString &foldername)
+{
+ m_controller->setProperty("kdenlive:folderid", folderid);
+}
+
+bool ProjectClip::matches(QString condition)
+{
+ //TODO
+ return true;
+}
+
+const QString ProjectClip::codec(bool audioCodec) const
+{
+ if (!m_controller) return QString();
+ return m_controller->codec(audioCodec);
+}
+
+bool ProjectClip::rename(const QString &name)
+{
+ if (m_name == name) return false;
+ // Rename clip
+ QMap <QString, QString> newProperites;
+ QMap <QString, QString> oldProperites;
+ oldProperites.insert("kdenlive:clipname", m_name);
+ newProperites.insert("kdenlive:clipname", name);
+ bin()->slotEditClipCommand(m_id, oldProperites, newProperites);
+ m_name = name;
+ return true;
+}
+
+void ProjectClip::addClipMarker(QList <CommentedTime> newMarkers, QUndoCommand *groupCommand)
+{
+ if (!m_controller) return;
+ QList <CommentedTime> oldMarkers;
+ for (int i = 0; i < newMarkers.count(); ++i) {
+ CommentedTime oldMarker = m_controller->markerAt(newMarkers.at(i).time());
+ if (oldMarker == CommentedTime()) {
+ oldMarker = newMarkers.at(i);
+ oldMarker.setMarkerType(-1);
+ }
+ oldMarkers << oldMarker;
+ }
+ (void) new AddMarkerCommand(this, oldMarkers, newMarkers, groupCommand);
+}
+
+bool ProjectClip::deleteClipMarkers(QUndoCommand *command)
+{
+ QList <CommentedTime> markers = commentedSnapMarkers();
+ if (markers.isEmpty()) {
+ return false;
+ }
+ QList <CommentedTime> newMarkers;
+ for (int i = 0; i < markers.size(); ++i) {
+ CommentedTime marker = markers.at(i);
+ marker.setMarkerType(-1);
+ newMarkers << marker;
+ }
+ new AddMarkerCommand(this, markers, newMarkers, command);
+ return true;
+}
+
+void ProjectClip::addMarkers(QList <CommentedTime> &markers)
+{
+ if (!m_controller) return;
+ for (int i = 0; i < markers.count(); ++i) {
+ if (markers.at(i).markerType() < 0) m_controller->deleteSnapMarker(markers.at(i).time());
+ else m_controller->addSnapMarker(markers.at(i));
+ }
+ // refresh markers in clip monitor
+ bin()->refreshClipMarkers(m_id);
+ // refresh markers in timeline clips
+ emit refreshClipDisplay();
+}
+
+void ProjectClip::addEffect(const ProfileInfo pInfo, QDomElement &effect)
+{
+ m_controller->addEffect(pInfo, effect);
+ bin()->editMasterEffect(m_controller);
+ bin()->emitItemUpdated(this);
+}
+
+void ProjectClip::removeEffect(const ProfileInfo pInfo, int ix)
+{
+ m_controller->removeEffect(pInfo, ix);
+ bin()->editMasterEffect(m_controller);
+ bin()->emitItemUpdated(this);
+}
+
+QVariant ProjectClip::data(DataType type) const
+{
+ switch (type) {
+ case AbstractProjectItem::IconOverlay:
+ return m_controller != NULL ? (m_controller->hasEffects() ? QVariant("kdenlive-track_has_effect") : QVariant()) : QVariant();
+ break;
+ default:
+ break;
+ }
+ return AbstractProjectItem::data(type);
+}
+
+void ProjectClip::slotExtractImage(QList <int> frames)
+{
+ Mlt::Producer *prod = producer();
+ if (prod == NULL) return;
+ // Check if we are using GPU accel, then we need to use alternate producer
+ if (KdenliveSettings::gpu_accel()) {
+ if (m_gpuProducer == NULL) {
+ QString service = prod->get("mlt_service");
+ m_gpuProducer = new Mlt::Producer(*prod->profile(), service.toUtf8().constData(), prod->get("resource"));
+ Mlt::Filter scaler(*prod->profile(), "swscale");
+ Mlt::Filter converter(*prod->profile(), "avcolor_space");
+ m_gpuProducer->attach(scaler);
+ m_gpuProducer->attach(converter);
+ }
+ prod = m_gpuProducer;
+ }
+ int imageWidth = (int)((double) 150 * prod->profile()->width() / prod->profile()->height() + 0.5);
+ int fullWidth = (int)((double) 150 * prod->profile()->dar() + 0.5);
+ QDir thumbFolder(bin()->projectFolder().path() + "/thumbs/");
+ for (int i = 0; i < frames.count(); i++) {
+ int pos = frames.at(i);
+ if (thumbFolder.exists(hash() + '_' + QString::number(pos) + ".png")) {
+ emit thumbReady(pos, QImage(thumbFolder.absoluteFilePath(hash() + '_' + QString::number(pos) + ".png")));
+ continue;
+ }
+ int max = prod->get_out();
+ if (pos >= max) pos = max - 1;
+ prod->seek(pos);
+ Mlt::Frame *frame = prod->get_frame();
+ if (frame && frame->is_valid()) {
+ QImage img = KThumb::getFrame(frame, imageWidth, fullWidth, 150);
+ emit thumbReady(frames.at(i), img);
+ }
+ delete frame;
+ }
+}
+
+void ProjectClip::slotCreateAudioThumbs()
+{
+ Mlt::Producer *prod = producer();
+ AudioStreamInfo *audioInfo = m_controller->audioInfo();
+ if (audioInfo == NULL) return;
+ QString clipHash = hash();
+ if (clipHash.isEmpty()) return;
+ QString audioPath = bin()->projectFolder().path() + "/thumbs/" + clipHash + ".thumb";
+ double lengthInFrames = prod->get_playtime();
+ int frequency = audioInfo->samplingRate();
+ if (frequency <= 0) frequency = 48000;
+ int channels = audioInfo->channels();
+ if (channels <= 0) channels = 2;
+ int arrayWidth = 20;
+ double frame = 0.0;
+ int maxVolume = 0;
+ audioByteArray storeIn;
+ QFile f(audioPath);
+ if (QFileInfo(audioPath).size() > 0 && f.open(QIODevice::ReadOnly)) {
+ bool reading = true;
+ const QByteArray channelarray = f.readAll();
+ f.close();
+ if (channelarray.size() != arrayWidth*(frame + lengthInFrames) * channels) {
+ //qDebug() << "--- BROKEN THUMB FOR: " << url.fileName() << " ---------------------- ";
+ f.remove();
+ reading = false;
+ }
+ //qDebug() << "reading audio thumbs from file";
+
+ if (reading) {
+ int h1 = arrayWidth * channels;
+ int h2 = (int) frame * h1;
+ for (int z = (int) frame; z < (int)(frame + lengthInFrames) && !m_abortAudioThumb; ++z) {
+ int h3 = 0;
+ for (int c = 0; c < channels; ++c) {
+ QByteArray audioArray(arrayWidth, '\x00');
+ for (int i = 0; i < arrayWidth; ++i) {
+ audioArray[i] = channelarray.at(h2 + h3 + i);
+ if (audioArray.at(i) > maxVolume) maxVolume = audioArray.at(i);
+ }
+ h3 += arrayWidth;
+ storeIn[z][c] = audioArray;
+ }
+ h2 += h1;
+ }
+ if (!m_abortAudioThumb) {
+ setProducerProperty("audio_max", QString::number(maxVolume - 64));
+ updateAudioThumbnail(storeIn);
+ }
+ return;
+ }
+ }
+ if (!f.open(QIODevice::WriteOnly)) {
+ //qDebug() << "++++++++ ERROR WRITING TO FILE: " << audioPath;
+ //qDebug() << "++++++++ DISABLING AUDIO THUMBS";
+ KdenliveSettings::setAudiothumbnails(false);
+ return;
+ }
+ QString service = prod->get("mlt_service");
+ Mlt::Producer *audioProducer = new Mlt::Producer(*prod->profile(), service.toUtf8().constData(), prod->get("resource"));
+ if (!audioProducer->is_valid()) {
+ //qDebug() << "++++++++ INVALID CLIP: " << url.path();
+ delete audioProducer;
+ return;
+ }
+ audioProducer->set("video_index", "-1");
+
+ //if (KdenliveSettings::normaliseaudiothumbs()) {
+ //Mlt::Filter m_convert(prof, "volume");
+ //m_convert.set("gain", "normalise");
+ //producer.attach(m_convert);
+ //}
+
+ int last_val = 0;
+ setJobStatus(AbstractClipJob::THUMBJOB, JobWaiting, 0, i18n("Creating audio thumbnails"));
+ double framesPerSecond = audioProducer->get_fps();
+ mlt_audio_format audioFormat = mlt_audio_s16;
+ for (int z = (int) frame; z < (int)(frame + lengthInFrames) && !m_abortAudioThumb; ++z) {
+ int val = (int)((z - frame) / (frame + lengthInFrames) * 100.0);
+ if (last_val != val && val > 1) {
+ setJobStatus(AbstractClipJob::THUMBJOB, JobWorking, val);
+ last_val = val;
+ }
+ audioProducer->seek(z);
+ Mlt::Frame *mlt_frame = audioProducer->get_frame();
+ if (mlt_frame && mlt_frame->is_valid()) {
+ int samples = mlt_sample_calculator(framesPerSecond, frequency, mlt_frame->get_position());
+ qint16* pcm = static_cast<qint16*>(mlt_frame->get_audio(audioFormat, frequency, channels, samples));
+ for (int c = 0; c < channels; ++c) {
+ QByteArray audioArray;
+ audioArray.resize(arrayWidth);
+ for (int i = 0; i < audioArray.size(); ++i) {
+ double pcmval = *(pcm + c + i * samples / audioArray.size());
+ if (pcmval >= 0) {
+ pcmval = sqrt(pcmval) / 2.83 + 64;
+ audioArray[i] = pcmval;
+ if (pcmval > maxVolume) maxVolume = pcmval;
+ }
+ else {
+ pcmval = -sqrt(-pcmval) / 2.83 + 64;
+ audioArray[i] = pcmval;
+ if (-pcmval > maxVolume) maxVolume = -pcmval;
+ }
+ }
+ f.write(audioArray);
+ storeIn[z][c] = audioArray;
+ }
+ } else {
+ f.write(QByteArray(arrayWidth, '\x00'));
+ }
+ delete mlt_frame;
+ }
+ f.close();
+ delete audioProducer;
+ setJobStatus(AbstractClipJob::THUMBJOB, JobDone, 0, i18n("Audio thumbnails done"));
+ if (m_abortAudioThumb) {
+ f.remove();
+ } else {
+ updateAudioThumbnail(storeIn);
+ setProducerProperty("audio_max", QString::number(maxVolume - 64));
+ }
+}
diff --git a/src/bin/projectclip.h b/src/bin/projectclip.h
new file mode 100644
index 0000000..6d28ff9
--- /dev/null
+++ b/src/bin/projectclip.h
@@ -0,0 +1,223 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PROJECTCLIP_H
+#define PROJECTCLIP_H
+
+#include "abstractprojectitem.h"
+#include "definitions.h"
+
+
+#include <QUrl>
+#include <QMutex>
+
+class ProjectFolder;
+class QDomElement;
+class ClipController;
+class ClipPropertiesController;
+class ProjectSubClip;
+class QUndoCommand;
+namespace Mlt {
+ class Producer;
+ class Properties;
+};
+
+
+/**
+ * @class ProjectClip
+ * @brief Represents a clip in the project (not timeline).
+ *
+
+ */
+
+class ProjectClip : public AbstractProjectItem
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief Constructor; used when loading a project and the producer is already available.
+ */
+ ProjectClip(const QString &id, QIcon thumb, ClipController *controller, ProjectFolder* parent);
+ /**
+ * @brief Constructor.
+ * @param description element describing the clip; the "id" attribute and "resource" property are used
+ */
+ ProjectClip(const QDomElement &description, QIcon thumb, ProjectFolder *parent);
+ virtual ~ProjectClip();
+
+ void reloadProducer(bool thumbnailOnly = false);
+
+ /** @brief Returns a unique hash identifier used to store clip thumbnails. */
+ //virtual void hash() = 0;
+
+ /** @brief Returns this if @param id matches the clip's id or NULL otherwise. */
+ ProjectClip *clip(const QString &id);
+
+ ProjectFolder* folder(const QString &id);
+
+ ProjectSubClip* getSubClip(int in, int out);
+
+ /** @brief Returns this if @param ix matches the clip's index or NULL otherwise. */
+ ProjectClip* clipAt(int ix);
+
+ /** @brief Returns the clip type as defined in definitions.h */
+ ClipType clipType() const;
+ ClipPropertiesController *buildProperties(QWidget *parent);
+ QPoint zone() const;
+ //TODO
+ void addMarker(int position);
+ //TODO
+ void removeMarker(int position);
+
+ /** @brief Returns whether this clip has a url (=describes a file) or not. */
+ bool hasUrl() const;
+
+ /** @brief Returns the clip's url. */
+ QUrl url() const;
+
+ /** @brief Returns the clip's xml data by using MLT's XML consumer. */
+ QString serializeClip();
+
+ /** @brief Returns whether this clip has a limited duration or whether it is resizable ad infinitum. */
+ virtual bool hasLimitedDuration() const;
+
+ /** @brief Returns the clip's duration. */
+ GenTime duration() const;
+
+ /** @brief Returns the original clip's fps. */
+ double getOriginalFps() const;
+
+ /** @brief Calls AbstractProjectItem::setCurrent and sets the bin monitor to use the clip's producer. */
+ virtual void setCurrent(bool current, bool notify = true);
+
+ virtual bool rename(const QString &name);
+
+ virtual QDomElement toXml(QDomDocument &document);
+
+ QVariant data(DataType type) const;
+
+ /** @brief Set the Job status on a clip.
+ * @param jobType The job type
+ * @param status The job status (see definitions.h)
+ * @param progress The job progress (in percents)
+ * @param statusMessage The job info message */
+ void setJobStatus(AbstractClipJob::JOBTYPE jobType, ClipJobStatus status, int progress = 0, const QString &statusMessage = QString());
+
+ /** @brief Sets thumbnail for this clip. */
+ void setThumbnail(QImage);
+
+ /** @brief Sets the MLT producer associated with this clip
+ * @param producer The producer
+ * @param replaceProducer If true, we replace existing producer with this one
+ * . */
+ void setProducer(ClipController *controller, bool replaceProducer);
+
+ /** @brief Returns true if this clip already has a producer. */
+ bool isReady() const;
+
+ /** @brief Returns this clip's producer. */
+ Mlt::Producer *producer();
+
+ ClipController *controller();
+
+ /** @brief Set properties on this clip. TODO: should we store all in MLT or use extra m_properties ?. */
+ void setProperties(QMap <QString, QString> properties, bool refreshPanel = false);
+
+ /** @brief Get an XML property from MLT produced xml. */
+ static QString getXmlProperty(const QDomElement &producer, const QString &propertyName);
+
+ virtual QString getToolTip() const;
+
+ /** @brief The clip hash created from the clip's resource. */
+ const QString hash();
+
+ /** @brief Set a property on the MLT producer. */
+ void setProducerProperty(const QString &name, int data);
+ /** @brief Set a property on the MLT producer. */
+ void setProducerProperty(const QString &name, double data);
+ /** @brief Set a property on the MLT producer. */
+ void setProducerProperty(const QString &name, const QString &data);
+ /** @brief Reset a property on the MLT producer (=delete the property). */
+ void resetProducerProperty(const QString &name);
+
+ /** @brief Get a property from the MLT producer. */
+ QMap <QString, QString> currentProperties(const QMap <QString, QString> &props);
+ QString getProducerProperty(const QString &key) const;
+ int getProducerIntProperty(const QString &key) const;
+ QColor getProducerColorProperty(const QString &key) const;
+
+ QList < CommentedTime > commentedSnapMarkers() const;
+
+ /** @brief Returns true if we are using a proxy for this clip. */
+ bool hasProxy() const;
+
+ /** Cache for every audio Frame with 10 Bytes */
+ /** format is frame -> channel ->bytes */
+ QMap<int, QMap<int, QByteArray> > audioFrameCache;
+ bool audioThumbCreated() const;
+
+ void updateParentInfo(const QString &folderid, const QString &foldername);
+ void setWaitingStatus(const QString &id);
+ /** @brief Returns true if the clip matched a condition, for example vcodec=mpeg1video. */
+ bool matches(QString condition);
+ /** @brief Returns true if the clip's video codec is equal to @param codec.
+ * @param audioCodec set to true if you want to check audio codec. When false, this will check the video codec
+ */
+ const QString codec(bool audioCodec) const;
+
+ void addClipMarker(QList <CommentedTime> newMarkers, QUndoCommand *groupCommand);
+ bool deleteClipMarkers(QUndoCommand *groupCommand);
+ void addMarkers(QList <CommentedTime> &markers);
+ /** @brief Add an effect to bin clip. */
+ void addEffect(const ProfileInfo pInfo, QDomElement &effect);
+ void removeEffect(const ProfileInfo pInfo, int ix);
+
+public slots:
+ void updateAudioThumbnail(const audioByteArray& data);
+ void slotExtractImage(QList <int> frames);
+ void slotCreateAudioThumbs();
+
+protected:
+ bool m_hasLimitedDuration;
+
+private:
+ //TODO: clip markers
+ QList <int> m_markers;
+ /** @brief The Clip controller for this clip. */
+ ClipController *m_controller;
+ Mlt::Producer *m_gpuProducer;
+ /** @brief Generate and store file hash if not available. */
+ const QString getFileHash() const;
+ bool m_audioThumbCreated;
+ /** @brief Store clip url temporarily while the clip controller has not been created. */
+ QUrl m_temporaryUrl;
+ bool m_abortAudioThumb;
+
+signals:
+ void gotAudioData();
+ void refreshPropertiesPanel();
+ void refreshClipDisplay();
+ void thumbReady(int, QImage);
+};
+
+#endif
diff --git a/src/bin/projectfolder.cpp b/src/bin/projectfolder.cpp
new file mode 100644
index 0000000..bcbeaaa
--- /dev/null
+++ b/src/bin/projectfolder.cpp
@@ -0,0 +1,147 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "projectfolder.h"
+#include "projectclip.h"
+#include "bin.h"
+
+#include <QDomElement>
+#include <KLocalizedString>
+
+ProjectFolder::ProjectFolder(const QString &id, const QString &name, ProjectFolder* parent) :
+ AbstractProjectItem(AbstractProjectItem::FolderItem, id, parent)
+ , m_bin(NULL)
+{
+ //loadChildren(description);
+ m_name = name;
+ m_clipStatus = StatusReady;
+ m_thumbnail = QIcon::fromTheme("folder");
+ setParent(parent);
+}
+
+ProjectFolder::ProjectFolder(Bin *bin) :
+ AbstractProjectItem(AbstractProjectItem::FolderItem, QString::number(-1))
+ , m_bin(bin)
+{
+ m_name = "root";
+ setParent(NULL);
+}
+
+ProjectFolder::~ProjectFolder()
+{
+}
+
+void ProjectFolder::setCurrent(bool current, bool notify)
+{
+ AbstractProjectItem::setCurrent(current, notify);
+ if (current) {
+ bin()->openProducer(NULL);
+ }
+}
+
+ProjectClip* ProjectFolder::clip(const QString &id)
+{
+ ProjectClip *clip;
+ for (int i = 0; i < count(); ++i) {
+ clip = at(i)->clip(id);
+ if (clip) {
+ return clip;
+ }
+ }
+ return NULL;
+}
+
+
+QString ProjectFolder::getToolTip() const
+{
+ return QString(i18np("%1 clip", "%1 clips", size()));
+}
+
+ProjectFolder* ProjectFolder::folder(const QString &id)
+{
+ if (m_id == id) return this;
+ ProjectFolder *folderItem;
+ for (int i = 0; i < count(); ++i) {
+ folderItem = at(i)->folder(id);
+ if (folderItem) {
+ return folderItem;
+ }
+ }
+ return NULL;
+}
+
+ProjectClip* ProjectFolder::clipAt(int index)
+{
+ ProjectClip *clip;
+ if (isEmpty()) return NULL;
+ for (int i = 0; i < count(); ++i) {
+ clip = at(i)->clipAt(index);
+ if (clip) {
+ return clip;
+ }
+ }
+ return NULL;
+}
+
+Bin* ProjectFolder::bin()
+{
+ if (m_bin) {
+ return m_bin;
+ } else {
+ if (parent()) {
+ return parent()->bin();
+ }
+ return AbstractProjectItem::bin();
+ }
+}
+
+QDomElement ProjectFolder::toXml(QDomDocument& document)
+{
+ QDomElement folder = document.createElement("folder");
+ folder.setAttribute("name", name());
+ for (int i = 0; i < count(); ++i) {
+ folder.appendChild(at(i)->toXml(document));
+ }
+ return folder;
+}
+
+void ProjectFolder::loadChildren(const QDomElement& description)
+{
+ /*QDomNodeList childen = description.childNodes();
+ for (int i = 0; i < childen.count(); ++i) {
+ QDomElement childElement = childen.at(i).toElement();
+ if (childElement.tagName() == "folder") {
+ new ProjectFolder(childElement, this);
+ } else {
+ childElement.setTagName("producer");
+ }
+ }*/
+}
+
+bool ProjectFolder::rename(const QString &name)
+{
+ if (m_name == name) return false;
+ // Rename folder
+ bin()->renameFolderCommand(m_id, name, m_name);
+ return true;
+}
+
diff --git a/src/bin/projectfolder.h b/src/bin/projectfolder.h
new file mode 100644
index 0000000..c25f45e
--- /dev/null
+++ b/src/bin/projectfolder.h
@@ -0,0 +1,92 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PROJECTFOLDER_H
+#define PROJECTFOLDER_H
+
+#include "abstractprojectitem.h"
+
+
+/**
+ * @class ProjectFolder
+ * @brief A folder in the bin.
+ */
+
+class ProjectClip;
+class Bin;
+
+class ProjectFolder : public AbstractProjectItem
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief Creates the supplied folder and loads its children.
+ * @param description element describing the folder and its children
+ * @param parent parent folder
+ */
+ ProjectFolder(const QString &id, const QString &name, ProjectFolder* parent = 0);
+
+ /** @brief Creates an empty root folder. */
+ ProjectFolder(Bin *bin);
+
+ ~ProjectFolder();
+
+ /**
+ * @brief Returns the clip if it is a child (also indirect).
+ * @param id id of the child which should be returned
+ */
+ ProjectClip *clip(const QString &id);
+
+ /**
+ * @brief Returns itself or a child folder that matches the requested id.
+ * @param id id of the child which should be returned
+ */
+ ProjectFolder* folder(const QString &id);
+
+ /** @brief Calls AbstractProjectItem::setCurrent and blank the bin monitor. */
+ virtual void setCurrent(bool current, bool notify = true);
+
+ /**
+ * @brief Returns the clip if it is a child (also indirect).
+ * @param index index of the child which should be returned
+ */
+ ProjectClip* clipAt(int index);
+
+ /** @brief Returns a pointer to the bin model this folder belongs to. */
+ Bin *bin();
+
+ /** @brief Returns an xml description of the folder. */
+ QDomElement toXml(QDomDocument &document);
+ virtual QString getToolTip() const;
+ virtual bool rename(const QString &name);
+
+protected:
+
+
+private:
+ void loadChildren(const QDomElement &description);
+
+ Bin *m_bin;
+};
+
+#endif
diff --git a/src/bin/projectfolderup.cpp b/src/bin/projectfolderup.cpp
new file mode 100644
index 0000000..fefc666
--- /dev/null
+++ b/src/bin/projectfolderup.cpp
@@ -0,0 +1,93 @@
+/*
+Copyright (C) 2015 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "projectfolderup.h"
+#include "projectclip.h"
+#include "bin.h"
+
+#include <QDomElement>
+#include <KLocalizedString>
+
+ProjectFolderUp::ProjectFolderUp(AbstractProjectItem* parent) :
+ AbstractProjectItem(AbstractProjectItem::FolderUpItem, QString(), parent)
+ , m_bin(NULL)
+{
+ m_thumbnail = QIcon::fromTheme("go-up");
+ m_name = i18n("Up");
+ setParent(parent);
+}
+
+
+ProjectFolderUp::~ProjectFolderUp()
+{
+}
+
+void ProjectFolderUp::setCurrent(bool current, bool notify)
+{
+ AbstractProjectItem::setCurrent(current, notify);
+}
+
+ProjectClip* ProjectFolderUp::clip(const QString &id)
+{
+ return NULL;
+}
+
+
+QString ProjectFolderUp::getToolTip() const
+{
+ return i18n("Go up");
+}
+
+ProjectFolder* ProjectFolderUp::folder(const QString &id)
+{
+ return NULL;
+}
+
+ProjectClip* ProjectFolderUp::clipAt(int index)
+{
+ return NULL;
+}
+
+Bin* ProjectFolderUp::bin()
+{
+ if (m_bin) {
+ return m_bin;
+ } else {
+ if (parent()) {
+ return parent()->bin();
+ }
+ return AbstractProjectItem::bin();
+ }
+}
+
+QDomElement ProjectFolderUp::toXml(QDomDocument& document)
+{
+ return document.documentElement();
+}
+
+void ProjectFolderUp::loadChildren(const QDomElement& description)
+{
+}
+
+bool ProjectFolderUp::rename(const QString &)
+{
+ return false;
+}
diff --git a/src/bin/projectfolderup.h b/src/bin/projectfolderup.h
new file mode 100644
index 0000000..3a8aa09
--- /dev/null
+++ b/src/bin/projectfolderup.h
@@ -0,0 +1,89 @@
+/*
+Copyright (C) 2015 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PROJECTFOLDERUP_H
+#define PROJECTFOLDERUP_H
+
+#include "abstractprojectitem.h"
+
+
+/**
+ * @class ProjectFolderUpUp
+ * @brief A simple "folder up" item allowing to navigate up when the bin is in icon view.
+ */
+
+class ProjectClip;
+class Bin;
+
+class ProjectFolderUp : public AbstractProjectItem
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief Creates the supplied folder and loads its children.
+ * @param description element describing the folder and its children
+ * @param parent parent folder
+ */
+ ProjectFolderUp(AbstractProjectItem* parent);
+
+
+ ~ProjectFolderUp();
+
+ /**
+ * @brief Returns the clip if it is a child (also indirect).
+ * @param id id of the child which should be returned
+ */
+ ProjectClip *clip(const QString &id);
+
+ /**
+ * @brief Returns itself or a child folder that matches the requested id.
+ * @param id id of the child which should be returned
+ */
+ ProjectFolder* folder(const QString &id);
+
+ /** @brief Calls AbstractProjectItem::setCurrent and blank the bin monitor. */
+ virtual void setCurrent(bool current, bool notify = true);
+
+ /**
+ * @brief Returns the clip if it is a child (also indirect).
+ * @param index index of the child which should be returned
+ */
+ ProjectClip* clipAt(int index);
+
+ /** @brief Returns a pointer to the bin model this folder belongs to. */
+ Bin *bin();
+
+ /** @brief Returns an xml description of the folder. */
+ QDomElement toXml(QDomDocument &document);
+ virtual QString getToolTip() const;
+ virtual bool rename(const QString &name);
+
+protected:
+
+
+private:
+ void loadChildren(const QDomElement &description);
+
+ Bin *m_bin;
+};
+
+#endif
diff --git a/src/bin/projectitemmodel.cpp b/src/bin/projectitemmodel.cpp
new file mode 100644
index 0000000..4af7288
--- /dev/null
+++ b/src/bin/projectitemmodel.cpp
@@ -0,0 +1,311 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "projectitemmodel.h"
+#include "abstractprojectitem.h"
+#include "projectclip.h"
+#include "projectsubclip.h"
+#include "projectfolder.h"
+#include "bin.h"
+
+#include <qvarlengtharray.h>
+#include <KLocalizedString>
+#include <QItemSelectionModel>
+#include <QIcon>
+#include <QMimeData>
+#include <QDebug>
+#include <QStringListModel>
+
+
+ProjectItemModel::ProjectItemModel(Bin *bin) :
+ QAbstractItemModel(bin)
+ , m_bin(bin)
+{
+ connect(m_bin, SIGNAL(itemUpdated(AbstractProjectItem*)), this, SLOT(onItemUpdated(AbstractProjectItem*)));
+}
+
+ProjectItemModel::~ProjectItemModel()
+{
+}
+
+QVariant ProjectItemModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid()) {
+ return QVariant();
+ }
+ if (role == Qt::DisplayRole || role == Qt::EditRole) {
+ AbstractProjectItem *item = static_cast<AbstractProjectItem *>(index.internalPointer());
+ return item->data(static_cast<AbstractProjectItem::DataType>(index.column()));
+ }
+ if (role == Qt::DecorationRole && index.column() == 0) {
+ // Data has to be returned as icon to allow the view to scale it
+ AbstractProjectItem *item = static_cast<AbstractProjectItem *>(index.internalPointer());
+ QIcon icon = item->data(AbstractProjectItem::DataThumbnail).value<QIcon>();
+ return icon;
+ }
+ else {
+ AbstractProjectItem *item = static_cast<AbstractProjectItem *>(index.internalPointer());
+ return item->data((AbstractProjectItem::DataType) role);
+ }
+ return QVariant();
+}
+
+bool ProjectItemModel::setData(const QModelIndex & index, const QVariant & value, int role)
+{
+ AbstractProjectItem *item = static_cast<AbstractProjectItem *>(index.internalPointer());
+ if (item->rename(value.toString())) {
+ emit dataChanged(index, index, QVector<int> () << role);
+ return true;
+ }
+ // Item name was not changed
+ return false;
+}
+
+Qt::ItemFlags ProjectItemModel::flags(const QModelIndex& index) const
+{
+ if (!index.isValid()) {
+ return Qt::ItemIsDropEnabled;
+ }
+ AbstractProjectItem *item = static_cast<AbstractProjectItem *>(index.internalPointer());
+ AbstractProjectItem::PROJECTITEMTYPE type = item->itemType();
+ switch (type) {
+ case AbstractProjectItem::FolderItem:
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
+ break;
+ case AbstractProjectItem::ClipItem:
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
+ break;
+ case AbstractProjectItem::SubClipItem:
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
+ break;
+ case AbstractProjectItem::FolderUpItem:
+ return Qt::ItemIsEnabled;
+ break;
+ default:
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
+ }
+}
+
+bool ProjectItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
+{
+ if (action == Qt::IgnoreAction)
+ return true;
+
+ if (data->hasUrls()) {
+ emit itemDropped(data->urls(), parent);
+ return true;
+ }
+
+ if (data->hasFormat("kdenlive/producerslist")) {
+ // Dropping an Bin item
+ QStringList ids = QString(data->data("kdenlive/producerslist")).split(';');
+ emit itemDropped(ids, parent);
+ return true;
+ }
+
+ if (data->hasFormat("kdenlive/effectslist")) {
+ // Dropping effect on a Bin item
+ const QString effect = QString::fromUtf8(data->data("kdenlive/effectslist"));
+ emit effectDropped(effect, parent);
+ return true;
+ }
+
+ if (data->hasFormat("kdenlive/clip")) {
+ QStringList list = QString(data->data("kdenlive/clip")).split(';');
+ emit addClipCut(list.at(0), list.at(1).toInt(), list.at(2).toInt());
+ }
+
+ return false;
+}
+
+QVariant ProjectItemModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ QVariant columnName;
+ switch ((AbstractProjectItem::DataType)section) {
+ case AbstractProjectItem::DataName:
+ columnName = i18n("Name");
+ break;
+ case AbstractProjectItem::DataDescription:
+ columnName = i18n("Description");
+ break;
+ case AbstractProjectItem::DataDate:
+ columnName = i18n("Date");
+ break;
+ default:
+ columnName = i18n("Unknown");
+ break;
+ }
+ return columnName;
+ }
+
+ return QAbstractItemModel::headerData(section, orientation, role);
+}
+
+QModelIndex ProjectItemModel::index(int row, int column, const QModelIndex& parent) const
+{
+ if (!hasIndex(row, column, parent)) {
+ return QModelIndex();
+ }
+
+ AbstractProjectItem *parentItem;
+
+ if (parent.isValid()) {
+ parentItem = static_cast<AbstractProjectItem*>(parent.internalPointer());
+ } else {
+ parentItem = m_bin->rootFolder();
+ }
+
+ AbstractProjectItem *childItem = parentItem->at(row);
+ return createIndex(row, column, childItem);
+}
+
+QModelIndex ProjectItemModel::parent(const QModelIndex& index) const
+{
+ if (!index.isValid()) {
+ return QModelIndex();
+ }
+
+ AbstractProjectItem * parentItem = static_cast<AbstractProjectItem*>(index.internalPointer())->parent();
+
+ if (!parentItem || parentItem == m_bin->rootFolder()) {
+ return QModelIndex();
+ }
+
+ return createIndex(parentItem->index(), 0, parentItem);
+}
+
+int ProjectItemModel::rowCount(const QModelIndex& parent) const
+{
+ // ?
+ if (parent.column() > 0) {
+ return 0;
+ }
+
+ AbstractProjectItem *parentItem;
+ if (parent.isValid()) {
+ parentItem = static_cast<AbstractProjectItem*>(parent.internalPointer());
+ } else {
+ parentItem = m_bin->rootFolder();
+ }
+
+ return parentItem->count();
+}
+
+int ProjectItemModel::columnCount(const QModelIndex& parent) const
+{
+ if (parent.isValid()) {
+ return static_cast<AbstractProjectItem*>(parent.internalPointer())->supportedDataCount();
+ } else {
+ return m_bin->rootFolder()->supportedDataCount();
+ }
+}
+
+Qt::DropActions ProjectItemModel::supportedDropActions() const
+{
+ return Qt::CopyAction | Qt::MoveAction;
+}
+
+QStringList ProjectItemModel::mimeTypes() const
+{
+ QStringList types;
+ types << QLatin1String("kdenlive/producerslist") << QLatin1String("text/uri-list") << QLatin1String("kdenlive/clip") << QLatin1String("kdenlive/effectslist");
+ return types;
+}
+
+QMimeData* ProjectItemModel::mimeData(const QModelIndexList& indices) const
+{
+ // Mime data is a list of id's separated by ';'.
+ // Clip ids are represented like: 2 (where 2 is the clip's id)
+ // Clip zone ids are represented like: 2/10/200 (where 2 is the clip's id, 10 and 200 are in and out points)
+ // Folder ids are represented like: #2 (where 2 is the folder's id)
+ QMimeData *mimeData = new QMimeData();
+ QStringList list;
+ for (int i = 0; i < indices.count(); i++) {
+ QModelIndex ix = indices.at(i);
+ if (!ix.isValid()) continue;
+ AbstractProjectItem *item = static_cast<AbstractProjectItem*>(ix.internalPointer());
+ AbstractProjectItem::PROJECTITEMTYPE type = item->itemType();
+ if (type == AbstractProjectItem::ClipItem) {
+ list << item->clipId();
+ } else if (type == AbstractProjectItem::SubClipItem) {
+ QPoint p = item->zone();
+ list << item->clipId() + "/" + QString::number(p.x()) + "/" + QString::number(p.y());
+ }
+ else if (type == AbstractProjectItem::FolderItem) {
+ list << "#" + item->clipId();
+ }
+ }
+ if (!list.isEmpty()) {
+ QByteArray data;
+ data.append(list.join(QLatin1String(";")).toUtf8());
+ mimeData->setData(QLatin1String("kdenlive/producerslist"), data);
+ }
+ return mimeData;
+}
+
+
+void ProjectItemModel::onAboutToAddItem(AbstractProjectItem* item)
+{
+ AbstractProjectItem *parentItem = item->parent();
+ QModelIndex parentIndex;
+ if (parentItem != m_bin->rootFolder()) {
+ parentIndex = createIndex(parentItem->index(), 0, parentItem);
+ }
+
+ beginInsertRows(parentIndex, parentItem->count(), parentItem->count());
+}
+
+void ProjectItemModel::onItemAdded(AbstractProjectItem* item)
+{
+ Q_UNUSED(item)
+ endInsertRows();
+}
+
+void ProjectItemModel::onAboutToRemoveItem(AbstractProjectItem* item)
+{
+ AbstractProjectItem *parentItem = item->parent();
+ QModelIndex parentIndex;
+ if (parentItem != m_bin->rootFolder()) {
+ parentIndex = createIndex(parentItem->index(), 0, parentItem);
+ }
+
+ beginRemoveRows(parentIndex, item->index(), item->index());
+}
+
+void ProjectItemModel::onItemRemoved(AbstractProjectItem* item)
+{
+ Q_UNUSED(item)
+
+ endRemoveRows();
+}
+
+
+void ProjectItemModel::onItemUpdated(AbstractProjectItem* item)
+{
+ AbstractProjectItem *parentItem = item->parent();
+ QModelIndex parentIndex;
+ if (parentItem != m_bin->rootFolder()) {
+ parentIndex = createIndex(parentItem->index(), 0, parentItem);
+ }
+ emit dataChanged(parentIndex, parentIndex);
+}
diff --git a/src/bin/projectitemmodel.h b/src/bin/projectitemmodel.h
new file mode 100644
index 0000000..3906908
--- /dev/null
+++ b/src/bin/projectitemmodel.h
@@ -0,0 +1,96 @@
+/*
+Copyright (C) 2012 Till Theato <[email protected]>
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PROJECTITEMMODEL_H
+#define PROJECTITEMMODEL_H
+
+
+#include <QAbstractItemModel>
+#include <QSize>
+
+class AbstractProjectItem;
+class Bin;
+
+/**
+ * @class ProjectItemModel
+ * @brief Acts as an adaptor to be able to use BinModel with item views.
+ */
+
+class ProjectItemModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ ProjectItemModel(Bin *bin);
+ ~ProjectItemModel();
+
+ /** @brief Returns item data depending on role requested */
+ QVariant data(const QModelIndex &index, int role) const;
+ /** @brief Called when user edits an item */
+ bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
+ /** @brief Allow selection and drag & drop */
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ /** @brief Returns column names in case we want to use columns in QTreeView */
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ /** @brief Mandatory reimplementation from QAbstractItemModel */
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ /** @brief Mandatory reimplementation from QAbstractItemModel */
+ QModelIndex parent(const QModelIndex &index) const;
+ /** @brief Mandatory reimplementation from QAbstractItemModel */
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ /** @brief Mandatory reimplementation from QAbstractItemModel */
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ /** @brief Returns the mimetype used for Drag actions */
+ QStringList mimeTypes() const;
+ /** @brief Create data that will be used for Drag events */
+ QMimeData *mimeData(const QModelIndexList &indices) const;
+ /** @brief Set size for thumbnails */
+ void setIconSize(QSize s);
+ /** @brief Prepare some stuff before inserting a new item */
+ void onAboutToAddItem(AbstractProjectItem *item);
+ /** @brief Prepare some stuff after inserting a new item */
+ void onItemAdded(AbstractProjectItem *item);
+ /** @brief Prepare some stuff before removing a new item */
+ void onAboutToRemoveItem(AbstractProjectItem *item);
+ /** @brief Prepare some stuff after removing a new item */
+ void onItemRemoved(AbstractProjectItem *item);
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
+ Qt::DropActions supportedDropActions() const;
+
+public slots:
+ /** @brief An item in the list was modified, notify */
+ void onItemUpdated(AbstractProjectItem* item);
+
+private:
+ /** @brief Reference to the project bin */
+ Bin *m_bin;
+
+signals:
+ //TODO
+ void markersNeedUpdate(const QString &id,const QList<int>&);
+ void itemDropped(QStringList, const QModelIndex &);
+ void itemDropped(const QList <QUrl> &, const QModelIndex &);
+ void effectDropped(QString, const QModelIndex &);
+ void addClipCut(const QString &,int,int);
+};
+
+#endif
diff --git a/src/bin/projectsortproxymodel.cpp b/src/bin/projectsortproxymodel.cpp
new file mode 100644
index 0000000..220543d
--- /dev/null
+++ b/src/bin/projectsortproxymodel.cpp
@@ -0,0 +1,79 @@
+/*
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "projectsortproxymodel.h"
+#include "abstractprojectitem.h"
+
+#include <QItemSelectionModel>
+
+
+ProjectSortProxyModel::ProjectSortProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
+ m_selection = new QItemSelectionModel(this);
+ connect(m_selection, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(onCurrentRowChanged(QItemSelection,QItemSelection)));
+ setDynamicSortFilter(true);
+}
+
+bool ProjectSortProxyModel::filterAcceptsRow(int sourceRow,
+ const QModelIndex &sourceParent) const
+{
+ QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
+ return (sourceModel()->data(index0).toString().contains(m_searchString));
+}
+
+bool ProjectSortProxyModel::lessThan(const QModelIndex & left, const QModelIndex & right) const
+{
+ // Check item type (folder or clip) as defined in projectitemmodel
+ int leftType = sourceModel()->data(left, AbstractProjectItem::ItemTypeRole).toInt();
+ int rightType = sourceModel()->data(right, AbstractProjectItem::ItemTypeRole).toInt();
+ if (leftType == rightType) {
+ // Let the normal alphabetical sort happen
+ return QSortFilterProxyModel::lessThan(right, left);
+ }
+ return leftType > rightType;
+}
+
+QItemSelectionModel* ProjectSortProxyModel::selectionModel()
+{
+ return m_selection;
+}
+
+void ProjectSortProxyModel::slotSetSearchString(const QString &str)
+{
+ m_searchString = str;
+ invalidateFilter();
+}
+
+void ProjectSortProxyModel::onCurrentRowChanged(const QItemSelection& current, const QItemSelection& previous)
+{
+ Q_UNUSED(previous)
+ QModelIndex id;
+ QModelIndexList indexes = current.indexes();
+ if (!indexes.isEmpty()) id = indexes.first();
+ emit selectModel(id);
+}
+
+void ProjectSortProxyModel::slotDataChanged(const QModelIndex &ix1, const QModelIndex &ix2)
+{
+ emit dataChanged(ix1, ix2);
+}
+
diff --git a/src/bin/projectsortproxymodel.h b/src/bin/projectsortproxymodel.h
new file mode 100644
index 0000000..02249c7
--- /dev/null
+++ b/src/bin/projectsortproxymodel.h
@@ -0,0 +1,68 @@
+/*
+Copyright (C) 2014 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PROJECTSORTPROXYMODEL_H
+#define PROJECTSORTPROXYMODEL_H
+
+#include <QSortFilterProxyModel>
+
+class AbstractProjectItem;
+class QItemSelectionModel;
+
+/**
+ * @class ProjectSortProxyModel
+ * @brief Acts as an filtering proxy for the Bin Views, used when triggering the lineedit filter.
+ */
+
+class ProjectSortProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ ProjectSortProxyModel(QObject *parent = 0);
+ QItemSelectionModel* selectionModel();
+
+public slots:
+ /** @brief Set search string that will filter the view */
+ void slotSetSearchString(const QString &str);
+ /** @brief Relay datachanged signal from view's model */
+ void slotDataChanged(const QModelIndex &ix1, const QModelIndex &ix2);
+
+private slots:
+ /** @brief Called when a row change is detected by selection model */
+ void onCurrentRowChanged(const QItemSelection& current, const QItemSelection& previous);
+
+protected:
+ /** @brief Decide which items should be displayed depending on the search string */
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
+ /** @brief Reimplemented to show folders first */
+ bool lessThan(const QModelIndex & left, const QModelIndex & right) const;
+
+private:
+ QItemSelectionModel*m_selection;
+ QString m_searchString;
+
+signals:
+ /** @brief Emitted when the row changes, used to prepare action for selected item */
+ void selectModel(const QModelIndex&);
+ };
+
+#endif
diff --git a/src/bin/projectsubclip.cpp b/src/bin/projectsubclip.cpp
new file mode 100644
index 0000000..98eca51
--- /dev/null
+++ b/src/bin/projectsubclip.cpp
@@ -0,0 +1,124 @@
+/*
+Copyright (C) 2015 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "projectsubclip.h"
+#include "projectclip.h"
+#include "bin.h"
+
+#include <QDomElement>
+#include <KLocalizedString>
+
+class ClipController;
+
+ProjectSubClip::ProjectSubClip(ProjectClip *parent, int in, int out, const QString &name) :
+ AbstractProjectItem(AbstractProjectItem::SubClipItem, parent->clipId(), parent)
+ , m_masterClip(parent)
+ , m_in(in)
+ , m_out(out)
+{
+ QPixmap pix(64, 36);
+ pix.fill(Qt::lightGray);
+ m_thumbnail = QIcon(pix);
+ if (name.isEmpty()) {
+ m_name = i18n("Zone %1-%2", in, out);
+ }
+ else {
+ m_name = name;
+ }
+ m_clipStatus = StatusReady;
+ setParent(parent);
+ // Save subclip in MLT
+ parent->setProducerProperty("kdenlive:clipzone." + m_name, QString::number(in) + ";" + QString::number(out));
+}
+
+ProjectSubClip::~ProjectSubClip()
+{
+ // controller is deleted in bincontroller
+}
+
+void ProjectSubClip::discard()
+{
+ if (m_masterClip) m_masterClip->resetProducerProperty("kdenlive:clipzone." + m_name);
+}
+
+QString ProjectSubClip::getToolTip() const
+{
+ return "test";
+}
+
+ProjectClip* ProjectSubClip::clip(const QString &id)
+{
+ return NULL;
+}
+
+ProjectFolder* ProjectSubClip::folder(const QString &id)
+{
+ return NULL;
+}
+
+GenTime ProjectSubClip::duration() const
+{
+ //TODO
+ return GenTime();
+}
+
+QPoint ProjectSubClip::zone() const
+{
+ return QPoint(m_in, m_out);
+}
+
+ProjectClip* ProjectSubClip::clipAt(int ix)
+{
+ return NULL;
+}
+
+QDomElement ProjectSubClip::toXml(QDomDocument& document)
+{
+ QDomElement sub = document.createElement("subclip");
+ sub.setAttribute("id", m_masterClip->clipId());
+ sub.setAttribute("in", m_in);
+ sub.setAttribute("out", m_out);
+ return sub;
+}
+
+ProjectSubClip *ProjectSubClip::subClip(int in, int out)
+{
+ if (m_in == in && m_out == out) return this;
+ return NULL;
+}
+
+void ProjectSubClip::setCurrent(bool current, bool notify)
+{
+ AbstractProjectItem::setCurrent(current, notify);
+ if (current) {
+ m_masterClip->bin()->openProducer(m_masterClip->controller(), m_in, m_out);
+ }
+}
+
+bool ProjectSubClip::rename(const QString &name)
+{
+ if (m_name == name) return false;
+ // Rename folder
+ bin()->renameSubClipCommand(m_id, name, m_name, m_in, m_out);
+ return true;
+}
+
+
diff --git a/src/bin/projectsubclip.h b/src/bin/projectsubclip.h
new file mode 100644
index 0000000..b0799e7
--- /dev/null
+++ b/src/bin/projectsubclip.h
@@ -0,0 +1,87 @@
+/*
+Copyright (C) 2015 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PROJECTSUBCLIP_H
+#define PROJECTSUBCLIP_H
+
+#include "abstractprojectitem.h"
+#include "definitions.h"
+
+#include <QUrl>
+#include <QMutex>
+
+class ProjectFolder;
+class ProjectClip;
+class QDomElement;
+
+namespace Mlt {
+ class Producer;
+ class Properties;
+};
+
+
+/**
+ * @class ProjectSubClip
+ * @brief Represents a clip in the project (not timeline).
+ *
+
+ */
+
+class ProjectSubClip : public AbstractProjectItem
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief Constructor; used when loading a project and the producer is already available.
+ */
+ ProjectSubClip(ProjectClip *parent, int in, int out, const QString &name = QString());
+ virtual ~ProjectSubClip();
+
+ ProjectClip *clip(const QString &id);
+ ProjectFolder* folder(const QString &id);
+ ProjectSubClip* subClip(int in, int out);
+ ProjectClip *clipAt(int ix);
+ QDomElement toXml(QDomDocument &document);
+
+ /** @brief Returns the clip's duration. */
+ GenTime duration() const;
+
+ /** @brief Calls AbstractProjectItem::setCurrent and sets the bin monitor to use the clip's producer. */
+ virtual void setCurrent(bool current, bool notify = true);
+
+ /** @brief Sets thumbnail for this clip. */
+ void setThumbnail(QImage);
+
+ /** @brief Remove reference to this subclip in the master clip, to be done before a subclip is deleted. */
+ void discard();
+ QPoint zone() const;
+ virtual QString getToolTip() const;
+ virtual bool rename(const QString &name);
+
+private:
+ ProjectClip *m_masterClip;
+ int m_in;
+ int m_out;
+
+};
+
+#endif
diff --git a/src/capture/mltdevicecapture.cpp b/src/capture/mltdevicecapture.cpp
index 9c5291e..538a7b8 100644
--- a/src/capture/mltdevicecapture.cpp
+++ b/src/capture/mltdevicecapture.cpp
@@ -19,7 +19,6 @@
#include "kdenlivesettings.h"
#include "definitions.h"
-#include "monitor/videosurface.h"
#include <mlt++/Mlt.h>
@@ -63,7 +62,7 @@ static void rec_consumer_frame_preview(mlt_consumer, MltDeviceCapture * self, ml
}
-MltDeviceCapture::MltDeviceCapture(QString profile, VideoSurface *surface, QWidget *parent) :
+MltDeviceCapture::MltDeviceCapture(QString profile, /*VideoSurface *surface, */QWidget *parent) :
AbstractRender(Kdenlive::RecordMonitor, parent),
doCapture(0),
processingImage(false),
@@ -72,10 +71,8 @@ MltDeviceCapture::MltDeviceCapture(QString profile, VideoSurface *surface, QWidg
m_mltProfile(NULL),
m_showFrameEvent(NULL),
m_droppedFrames(0),
- m_livePreview(KdenliveSettings::enable_recording_preview()),
- m_winid((int) surface->winId())
+ m_livePreview(KdenliveSettings::enable_recording_preview())
{
- m_captureDisplayWidget = surface;
analyseAudio = KdenliveSettings::monitor_audio();
if (profile.isEmpty())
profile = KdenliveSettings::current_profile();
@@ -115,19 +112,11 @@ bool MltDeviceCapture::buildConsumer(const QString &profileName)
}
}
qputenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1");
-
-
- if (m_winid == 0) {
- // OpenGL monitor
- m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_audio");
- m_mltConsumer->set("preview_off", 1);
- m_mltConsumer->set("preview_format", mlt_image_rgb24);
- m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
- } else {
- m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview");
- m_mltConsumer->set("window_id", m_winid);
- m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview);
- }
+ // OpenGL monitor
+ m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_audio");
+ m_mltConsumer->set("preview_off", 1);
+ m_mltConsumer->set("preview_format", mlt_image_rgb24);
+ m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
//m_mltConsumer->set("resize", 1);
//m_mltConsumer->set("terminate_on_pause", 1);
m_mltConsumer->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
@@ -377,9 +366,7 @@ bool MltDeviceCapture::slotStartCapture(const QString &params, const QString &pa
}
return false;
}
-
- m_winid = (int) m_captureDisplayWidget->winId();
-
+
// Create multi consumer setup
Mlt::Properties *renderProps = new Mlt::Properties;
renderProps->set("mlt_service", "avformat");
@@ -417,19 +404,13 @@ bool MltDeviceCapture::slotStartCapture(const QString &params, const QString &pa
}
qputenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1");
- if (m_winid == 0) {
+
// OpenGL monitor
previewProps->set("mlt_service", "sdl_audio");
previewProps->set("preview_off", 1);
previewProps->set("preview_format", mlt_image_rgb24);
previewProps->set("terminate_on_pause", 0);
m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
- } else {
- previewProps->set("mlt_service", "sdl_preview");
- previewProps->set("window_id", m_winid);
- previewProps->set("terminate_on_pause", 0);
- //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview);
- }
//m_mltConsumer->set("resize", 1);
previewProps->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
QString audioDevice = KdenliveSettings::audiodevicename();
diff --git a/src/capture/mltdevicecapture.h b/src/capture/mltdevicecapture.h
index 074aedc..c9e7453 100644
--- a/src/capture/mltdevicecapture.h
+++ b/src/capture/mltdevicecapture.h
@@ -56,7 +56,7 @@ Q_OBJECT public:
/** @brief Build a MLT Renderer
* @param winid The parent widget identifier (required for SDL display). Set to 0 for OpenGL rendering
* @param profile The MLT profile used for the capture (default one will be used if empty). */
- explicit MltDeviceCapture(QString profile, VideoSurface *surface, QWidget *parent = 0);
+ explicit MltDeviceCapture(QString profile, /*VideoSurface *surface,*/ QWidget *parent = 0);
/** @brief Destroy the MLT Renderer. */
~MltDeviceCapture();
@@ -109,12 +109,6 @@ private:
/** @brief Count captured frames, used to display only one in ten images while capturing. */
int m_frameCount;
- /** @brief The surface onto which the captured frames should be painted. */
- VideoSurface *m_captureDisplayWidget;
-
- /** @brief A human-readable description of this renderer. */
- int m_winid;
-
void uyvy2rgb(unsigned char *yuv_buffer, int width, int height);
QString m_capturePath;
diff --git a/src/core.cpp b/src/core.cpp
index d28cfde..2758940 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -12,6 +12,8 @@ the Free Software Foundation, either version 3 of the License, or
#include "mainwindow.h"
#include "project/projectmanager.h"
#include "monitor/monitormanager.h"
+#include "mltcontroller/bincontroller.h"
+#include "bin/bin.h"
#include <QCoreApplication>
#include <QDebug>
@@ -31,6 +33,9 @@ Core::Core(MainWindow *mainWindow) :
Core::~Core()
{
+ delete m_projectManager;
+ delete m_binWidget;
+ delete m_binController;
m_self = 0;
}
@@ -44,9 +49,16 @@ void Core::init()
{
initLocale();
m_projectManager = new ProjectManager(this);
+ m_binWidget = new Bin();
+ m_binController = new BinController();
+ connect(m_binWidget, SIGNAL(storeFolder(QString,QString,QString,QString)), m_binController, SLOT(slotStoreFolder(QString,QString,QString,QString)));
+ connect(m_binController, SIGNAL(loadFolders(QMap<QString,QString>)), m_binWidget, SLOT(slotLoadFolders(QMap<QString,QString>)));
+ connect(m_binController, SIGNAL(loadThumb(QString,QImage,bool)), m_binWidget, SLOT(slotThumbnailReady(QString,QImage,bool)));
m_monitorManager = new MonitorManager(this);
+ emit coreIsReady();
}
+
Core* Core::self()
{
return m_self;
@@ -67,6 +79,16 @@ MonitorManager* Core::monitorManager()
return m_monitorManager;
}
+BinController *Core::binController()
+{
+ return m_binController;
+}
+
+Bin *Core::bin()
+{
+ return m_binWidget;
+}
+
void Core::initLocale()
{
QLocale systemLocale = QLocale();
diff --git a/src/core.h b/src/core.h
index a82840a..ad389eb 100644
--- a/src/core.h
+++ b/src/core.h
@@ -17,6 +17,8 @@ the Free Software Foundation, either version 3 of the License, or
class MainWindow;
class ProjectManager;
class MonitorManager;
+class BinController;
+class Bin;
#define pCore Core::self()
@@ -52,7 +54,11 @@ public:
ProjectManager *projectManager();
/** @brief Returns a pointer to the monitor manager. */
MonitorManager *monitorManager();
+ /** @brief Returns a pointer to the project bin controller. */
+ BinController *binController();
+ Bin *bin();
+
private:
explicit Core(MainWindow *mainWindow);
static Core *m_self;
@@ -64,6 +70,11 @@ private:
MainWindow *m_mainWindow;
ProjectManager *m_projectManager;
MonitorManager *m_monitorManager;
+ BinController *m_binController;
+ Bin *m_binWidget;
+
+signals:
+ void coreIsReady();
};
#endif
diff --git a/src/definitions.cpp b/src/definitions.cpp
index f5c87a1..75620bc 100644
--- a/src/definitions.cpp
+++ b/src/definitions.cpp
@@ -66,79 +66,15 @@ bool MltVideoProfile::operator!=(const MltVideoProfile &other) const {
return !(*this == other);
}
-
-EffectInfo::EffectInfo() {isCollapsed = false; groupIndex = -1; groupIsCollapsed = false;}
-
-QString EffectInfo::toString() const {
- QStringList data;
- // effect collapsed state: 0 = effect not collapsed, 1 = effect collapsed,
- // 2 = group collapsed - effect not, 3 = group and effect collapsed
- int collapsedState = (int) isCollapsed;
- if (groupIsCollapsed) collapsedState += 2;
- data << QString::number(collapsedState) << QString::number(groupIndex) << groupName;
- return data.join(QLatin1String("/"));
-}
-
-void EffectInfo::fromString(QString value) {
- if (value.isEmpty()) return;
- QStringList data = value.split(QLatin1String("/"));
- isCollapsed = data.at(0).toInt() == 1 || data.at(0).toInt() == 3;
- groupIsCollapsed = data.at(0).toInt() >= 2;
- if (data.count() > 1) groupIndex = data.at(1).toInt();
- if (data.count() > 2) groupName = data.at(2);
-}
-
-
-EffectParameter::EffectParameter(const QString &name, const QString &value): m_name(name), m_value(value) {}
-
-QString EffectParameter::name() const {
- return m_name;
-}
-
-QString EffectParameter::value() const {
- return m_value;
-}
-
-void EffectParameter::setValue(const QString &value) {
- m_value = value;
-}
-
-
-EffectsParameterList::EffectsParameterList(): QList < EffectParameter >() {}
-
-bool EffectsParameterList::hasParam(const QString &name) const {
- for (int i = 0; i < size(); ++i)
- if (at(i).name() == name) return true;
- return false;
-}
-
-QString EffectsParameterList::paramValue(const QString &name, const QString &defaultValue) const {
- for (int i = 0; i < size(); ++i) {
- if (at(i).name() == name) return at(i).value();
- }
- return defaultValue;
-}
-
-void EffectsParameterList::addParam(const QString &name, const QString &value) {
- if (name.isEmpty()) return;
- append(EffectParameter(name, value));
-}
-
-void EffectsParameterList::removeParam(const QString &name) {
- for (int i = 0; i < size(); ++i)
- if (at(i).name() == name) {
- removeAt(i);
- break;
- }
-}
-
-
CommentedTime::CommentedTime(): t(GenTime(0)), type(0) {}
CommentedTime::CommentedTime(const GenTime &time, const QString &comment, int markerType)
: t(time), c(comment), type(markerType) { }
+CommentedTime::CommentedTime(const QString &hash, const GenTime &time)
+ : t(time), c(hash.section(":", 1)), type(hash.section(":", 0, 0).toInt()) { }
+
QString CommentedTime::comment() const {
return (c.isEmpty() ? i18n("Marker") : c);
}
@@ -155,6 +91,11 @@ void CommentedTime::setMarkerType(int t) {
type = t;
}
+QString CommentedTime::hash() const
+{
+ return QString::number(type) + ":" + (c.isEmpty() ? i18n("Marker") : c);
+}
+
int CommentedTime::markerType() const {
return type;
}
diff --git a/src/definitions.h b/src/definitions.h
index 73ce577..44c2451 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -52,6 +52,8 @@ const QString stopmotionMonitor("stopmotionMonitor");
}
+
+
enum OperationType {
None = 0,
MoveOperation = 1,
@@ -69,6 +71,16 @@ enum OperationType {
ScrollTimeline = 13
};
+namespace PlaylistState {
+
+ enum ClipState {
+ Original = 0,
+ VideoOnly = 1,
+ AudioOnly = 2
+ };
+
+};
+
enum ClipType {
Unknown = 0,
Audio = 1,
@@ -79,7 +91,9 @@ enum ClipType {
Text = 6,
SlideShow = 7,
Virtual = 8,
- Playlist = 9
+ Playlist = 9,
+ WebVfx = 10,
+ TextTemplate = 11,
};
enum ProjectItemType {
@@ -148,7 +162,27 @@ public:
isMute(0),
isBlind(0),
isLocked(0),
- duration(0) {}
+ duration(0),
+ effectsList() {}
+};
+
+
+struct ProfileInfo {
+ QSize profileSize;
+ double profileFps;
+};
+
+struct requestClipInfo {
+ QDomElement xml;
+ QString clipId;
+ int binIndex;
+ int imageHeight;
+ bool replaceProducer;
+
+ bool operator==(const requestClipInfo &a)
+ {
+ return clipId == a.clipId;
+ }
};
typedef QMap<QString, QString> stringMap;
@@ -206,59 +240,19 @@ public:
bool operator!=(const MltVideoProfile &other) const;
};
-/**)
- * @class EffectInfo
- * @brief A class holding some meta info for effects widgets, like state (collapsed or not, ...)
- * @author Jean-Baptiste Mardelle
- */
-
-class EffectInfo
-{
-public:
- EffectInfo();
- bool isCollapsed;
- bool groupIsCollapsed;
- int groupIndex;
- QString groupName;
- QString toString() const;
- void fromString(QString value);
-};
-
-class EffectParameter
-{
-public:
- EffectParameter(const QString &name, const QString &value);
- QString name() const;
- QString value() const;
- void setValue(const QString &value);
-private:
- QString m_name;
- QString m_value;
-};
-
-/** Use our own list for effect parameters so that they are not sorted in any ways, because
- some effects like sox need a precise order
-*/
-class EffectsParameterList: public QList < EffectParameter >
-{
-public:
- EffectsParameterList();
- bool hasParam(const QString &name) const;
-
- QString paramValue(const QString &name, const QString &defaultValue = QString()) const;
- void addParam(const QString &name, const QString &value);
- void removeParam(const QString &name);
-};
class CommentedTime
{
public:
CommentedTime();
CommentedTime(const GenTime &time, const QString& comment, int markerType = 0);
+ CommentedTime(const QString& hash, const GenTime &time);
- QString comment() const;
+ QString comment() const;
GenTime time() const;
+ /** @brief Returns a string containing infos needed to store marker info. string equals marker type + ":" + marker comment */
+ QString hash() const;
void setComment(const QString &comm);
void setMarkerType(int t);
int markerType() const;
diff --git a/src/dialogs/CMakeLists.txt b/src/dialogs/CMakeLists.txt
index 9c4e1cf..8c5ad6e 100644
--- a/src/dialogs/CMakeLists.txt
+++ b/src/dialogs/CMakeLists.txt
@@ -1,5 +1,6 @@
set(kdenlive_SRCS
${kdenlive_SRCS}
+ dialogs/clipcreationdialog.cpp
dialogs/encodingprofilesdialog.cpp
dialogs/kdenlivesettingsdialog.cpp
dialogs/profilesdialog.cpp
diff --git a/src/dialogs/clipcreationdialog.cpp b/src/dialogs/clipcreationdialog.cpp
new file mode 100644
index 0000000..5751b93
--- /dev/null
+++ b/src/dialogs/clipcreationdialog.cpp
@@ -0,0 +1,529 @@
+/*
+Copyright (C) 2015 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "clipcreationdialog.h"
+#include "kdenlivesettings.h"
+#include "doc/kdenlivedoc.h"
+#include "bin/bin.h"
+#include "bin/bincommands.h"
+#include "ui_colorclip_ui.h"
+#include "ui_templateclip_ui.h"
+#include "timecodedisplay.h"
+#include "doc/doccommands.h"
+#include "titler/titlewidget.h"
+#include "project/dialogs/slideshowclip.h"
+
+#include <KMessageBox>
+#include <KRecentDirs>
+#include <KFileWidget>
+#include <KWindowConfig>
+#include "klocalizedstring.h"
+
+#include <QDir>
+#include <QWindow>
+#include <QUndoStack>
+#include <QUndoCommand>
+#include <QStandardPaths>
+#include <QPushButton>
+#include <QDebug>
+#include <QDialog>
+#include <QPointer>
+#include <QMimeDatabase>
+
+
+// static
+QStringList ClipCreationDialog::getExtensions()
+{
+ // Build list of mime types
+ QStringList mimeTypes = QStringList() << "application/x-kdenlive" << "application/x-kdenlivetitle" << "video/mlt-playlist" << "text/plain";
+
+ // Video mimes
+ mimeTypes << "video/x-flv" << "application/vnd.rn-realmedia" << "video/x-dv" << "video/dv" << "video/x-msvideo" << "video/x-matroska" << "video/mpeg" << "video/ogg" << "video/x-ms-wmv" << "video/mp4" << "video/quicktime" << "video/webm" << "video/3gpp" << "video/mp2t";
+
+ // Audio mimes
+ mimeTypes << "audio/x-flac" << "audio/x-matroska" << "audio/mp4" << "audio/mpeg" << "audio/x-mp3" << "audio/ogg" << "audio/x-wav" << "audio/x-aiff" << "audio/aiff" << "application/ogg" << "application/mxf" << "application/x-shockwave-flash" << "audio/ac3";
+
+ // Image mimes
+ mimeTypes << "image/gif" << "image/jpeg" << "image/png" << "image/x-tga" << "image/x-bmp" << "image/svg+xml" << "image/tiff" << "image/x-xcf" << "image/x-xcf-gimp" << "image/x-vnd.adobe.photoshop" << "image/x-pcx" << "image/x-exr" << "image/x-portable-pixmap";
+
+ QMimeDatabase db;
+ QStringList allExtensions;
+ foreach(const QString & mimeType, mimeTypes) {
+ QMimeType mime = db.mimeTypeForName(mimeType);
+ if (mime.isValid()) {
+ allExtensions.append(mime.globPatterns());
+ }
+ }
+ allExtensions.removeDuplicates();
+ return allExtensions;
+}
+
+//static
+void ClipCreationDialog::createClipFromXml(KdenliveDoc *doc, QDomElement xml, QStringList groupInfo, Bin *bin)
+{
+ uint id = bin->getFreeClipId();
+ xml.setAttribute("id", QString::number(id));
+ AddClipCommand *command = new AddClipCommand(doc, xml, QString::number(id), true);
+ doc->commandStack()->push(command);
+}
+
+//static
+void ClipCreationDialog::createColorClip(KdenliveDoc *doc, QStringList groupInfo, Bin *bin)
+{
+ QPointer<QDialog> dia = new QDialog(bin);
+ Ui::ColorClip_UI dia_ui;
+ dia_ui.setupUi(dia);
+ dia->setWindowTitle(i18n("Color Clip"));
+ dia_ui.clip_name->setText(i18n("Color Clip"));
+
+ TimecodeDisplay *t = new TimecodeDisplay(doc->timecode());
+ t->setValue(KdenliveSettings::color_duration());
+ dia_ui.clip_durationBox->addWidget(t);
+ dia_ui.clip_color->setColor(KdenliveSettings::colorclipcolor());
+
+ if (dia->exec() == QDialog::Accepted) {
+ QString color = dia_ui.clip_color->color().name();
+ KdenliveSettings::setColorclipcolor(color);
+ color = color.replace(0, 1, "0x") + "ff";
+ // Everything is ready. create clip xml
+ QDomDocument xml;
+ QDomElement prod = xml.createElement("producer");
+ xml.appendChild(prod);
+ prod.setAttribute("type", (int) Color);
+ uint id = bin->getFreeClipId();
+ prod.setAttribute("id", QString::number(id));
+ prod.setAttribute("in", "0");
+ prod.setAttribute("out", doc->getFramePos(doc->timecode().getTimecode(t->gentime())) - 1);
+ QMap <QString, QString> properties;
+ properties.insert("resource", color);
+ properties.insert("kdenlive:clipname", dia_ui.clip_name->text());
+ properties.insert("mlt_service", "color");
+ if (!groupInfo.isEmpty()) {
+ properties.insert("kdenlive:folderid", groupInfo.at(0));
+ }
+ addXmlProperties(prod, properties);
+ AddClipCommand *command = new AddClipCommand(doc, xml.documentElement(), QString::number(id), true);
+ doc->commandStack()->push(command);
+ }
+ delete t;
+ delete dia;
+}
+
+//static
+void ClipCreationDialog::createSlideshowClip(KdenliveDoc *doc, QStringList groupInfo, Bin *bin)
+{
+ QPointer<SlideshowClip> dia = new SlideshowClip(doc->timecode(), KRecentDirs::dir(":KdenliveSlideShowFolder"), bin);
+
+ if (dia->exec() == QDialog::Accepted) {
+ // Ready, create xml
+ KRecentDirs::add(":KdenliveSlideShowFolder", QUrl::fromLocalFile(dia->selectedPath()).adjusted(QUrl::RemoveFilename).path());
+ QDomDocument xml;
+ QDomElement prod = xml.createElement("producer");
+ xml.appendChild(prod);
+ prod.setAttribute("in", "0");
+ prod.setAttribute("out", QString::number(doc->getFramePos(dia->clipDuration()) * dia->imageCount() - 1));
+ prod.setAttribute("type", (int) SlideShow);
+ QMap <QString, QString> properties;
+ properties.insert("kdenlive:clipname", dia->clipName());
+ properties.insert("resource", dia->selectedPath());
+ properties.insert("ttl", QString::number(doc->getFramePos(dia->clipDuration())));
+ properties.insert("loop", QString::number(dia->loop()));
+ properties.insert("crop", QString::number(dia->crop()));
+ properties.insert("fade", QString::number(dia->fade()));
+ properties.insert("luma_duration", dia->lumaDuration());
+ properties.insert("luma_file", dia->lumaFile());
+ properties.insert("softness", QString::number(dia->softness()));
+ properties.insert("animation", dia->animation());
+ if (!groupInfo.isEmpty()) {
+ properties.insert("kdenlive:folderid", groupInfo.at(0));
+ }
+ addXmlProperties(prod, properties);
+ uint id = bin->getFreeClipId();
+ AddClipCommand *command = new AddClipCommand(doc, xml.documentElement(), QString::number(id), true);
+ doc->commandStack()->push(command);
+ }
+ delete dia;
+}
+
+void ClipCreationDialog::createTitleClip(KdenliveDoc *doc, QStringList groupInfo, QString templatePath, Bin *bin)
+{
+ // Make sure the titles folder exists
+ QDir dir(doc->projectFolder().path());
+ dir.mkdir("titles");
+ dir.cd("titles");
+ QPointer<TitleWidget> dia_ui = new TitleWidget(QUrl::fromLocalFile(templatePath), doc->timecode(), dir.path(), doc->renderer(), bin);
+ if (dia_ui->exec() == QDialog::Accepted) {
+ // Ready, create clip xml
+ QDomDocument xml;
+ QDomElement prod = xml.createElement("producer");
+ xml.appendChild(prod);
+ //prod.setAttribute("resource", imagePath);
+ uint id = bin->getFreeClipId();
+ prod.setAttribute("id", QString::number(id));
+
+ QMap <QString, QString> properties;
+ properties.insert("xmldata", dia_ui->xml().toString());
+ properties.insert("kdenlive:clipname", i18n("Title clip"));
+ if (!groupInfo.isEmpty()) {
+ properties.insert("kdenlive:folderid", groupInfo.at(0));
+ }
+ addXmlProperties(prod, properties);
+ prod.setAttribute("type", (int) Text);
+ prod.setAttribute("transparency", "1");
+ prod.setAttribute("in", "0");
+ prod.setAttribute("out", dia_ui->duration() - 1);
+ AddClipCommand *command = new AddClipCommand(doc, xml.documentElement(), QString::number(id), true);
+ doc->commandStack()->push(command);
+ }
+ delete dia_ui;
+}
+
+
+void ClipCreationDialog::createTitleTemplateClip(KdenliveDoc *doc, QStringList groupInfo, QString templatePath, Bin *bin)
+{
+ // Get the list of existing templates
+ QStringList filter;
+ filter << "*.kdenlivetitle";
+ const QString path = doc->projectFolder().path() + QDir::separator() + "titles/";
+ QStringList templateFiles = QDir(path).entryList(filter, QDir::Files);
+
+ QPointer<QDialog> dia = new QDialog(QApplication::activeWindow());
+ Ui::TemplateClip_UI dia_ui;
+ dia_ui.setupUi(dia);
+ for (int i = 0; i < templateFiles.size(); ++i)
+ dia_ui.template_list->comboBox()->addItem(templateFiles.at(i), path + templateFiles.at(i));
+
+ if (!templateFiles.isEmpty())
+ dia_ui.buttonBox->button(QDialogButtonBox::Ok)->setFocus();
+ QStringList mimeTypeFilters;
+ mimeTypeFilters <<"application/x-kdenlivetitle";
+ dia_ui.template_list->setFilter(mimeTypeFilters.join(' '));
+ //warning: setting base directory doesn't work??
+ dia_ui.template_list->setUrl(QUrl::fromLocalFile(path));
+ dia_ui.text_box->setHidden(true);
+ if (dia->exec() == QDialog::Accepted) {
+ QString textTemplate = dia_ui.template_list->comboBox()->itemData(dia_ui.template_list->comboBox()->currentIndex()).toString();
+ if (textTemplate.isEmpty()) textTemplate = dia_ui.template_list->comboBox()->currentText();
+ // Create a cloned template clip
+ QDomDocument xml;
+ QDomElement prod = xml.createElement("producer");
+ xml.appendChild(prod);
+
+ QMap <QString, QString> properties;
+ properties.insert("resource", textTemplate);
+ properties.insert("kdenlive:clipname", i18n("Template title clip"));
+ if (!groupInfo.isEmpty()) {
+ properties.insert("kdenlive:folderid", groupInfo.at(0));
+ }
+ addXmlProperties(prod, properties);
+ uint id = bin->getFreeClipId();
+ prod.setAttribute("id", QString::number(id));
+ prod.setAttribute("type", (int) Text);
+ prod.setAttribute("transparency", "1");
+ prod.setAttribute("in", "0");
+
+ int duration = 0;
+ QDomDocument titledoc;
+ QFile txtfile(textTemplate);
+ if (txtfile.open(QIODevice::ReadOnly) && titledoc.setContent(&txtfile)) {
+ if (titledoc.documentElement().hasAttribute("duration")) {
+ duration = titledoc.documentElement().attribute("duration").toInt();
+ } else {
+ // keep some time for backwards compatibility - 26/12/12
+ duration = titledoc.documentElement().attribute("out").toInt();
+ }
+ }
+ txtfile.close();
+
+ if (duration == 0) duration = doc->getFramePos(KdenliveSettings::title_duration());
+ prod.setAttribute("duration", duration - 1);
+ prod.setAttribute("out", duration - 1);
+
+ AddClipCommand *command = new AddClipCommand(doc, xml.documentElement(), QString::number(id), true);
+ doc->commandStack()->push(command);
+ }
+ delete dia;
+}
+
+void ClipCreationDialog::addXmlProperties(QDomElement &producer, QMap <QString, QString> &properties)
+{
+ QMapIterator<QString, QString> i(properties);
+ while (i.hasNext()) {
+ i.next();
+ QDomElement prop = producer.ownerDocument().createElement("property");
+ prop.setAttribute("name", i.key());
+ QDomText value = producer.ownerDocument().createTextNode(i.value());
+ prop.appendChild(value);
+ producer.appendChild(prop);
+ }
+}
+
+void ClipCreationDialog::createClipsCommand(KdenliveDoc *doc, const QList<QUrl> &urls, QStringList groupInfo, Bin *bin, const QMap <QString, QString> &data)
+{
+ QUndoCommand *addClips = new QUndoCommand();
+
+ //TODO: check files on removable volume
+ /*listRemovableVolumes();
+ foreach(const QUrl &file, urls) {
+ if (QFile::exists(file.path())) {
+ //TODO check for duplicates
+ if (!data.contains("bypassDuplicate") && !getClipByResource(file.path()).empty()) {
+ if (KMessageBox::warningContinueCancel(QApplication::activeWindow(), i18n("Clip <b>%1</b><br />already exists in project, what do you want to do?", file.path()), i18n("Clip already exists")) == KMessageBox::Cancel)
+ continue;
+ }
+ if (isOnRemovableDevice(file) && !isOnRemovableDevice(m_doc->projectFolder())) {
+ int answer = KMessageBox::warningYesNoCancel(QApplication::activeWindow(), i18n("Clip <b>%1</b><br /> is on a removable device, will not be available when device is unplugged", file.path()), i18n("File on a Removable Device"), KGuiItem(i18n("Copy file to project folder")), KGuiItem(i18n("Continue")), KStandardGuiItem::cancel(), QString("copyFilesToProjectFolder"));
+
+ if (answer == KMessageBox::Cancel) continue;
+ else if (answer == KMessageBox::Yes) {
+ // Copy files to project folder
+ QDir sourcesFolder(m_doc->projectFolder().toLocalFile());
+ sourcesFolder.cd("clips");
+ KIO::MkdirJob *mkdirJob = KIO::mkdir(QUrl::fromLocalFile(sourcesFolder.absolutePath()));
+ KJobWidgets::setWindow(mkdirJob, QApplication::activeWindow());
+ if (!mkdirJob->exec()) {
+ KMessageBox::sorry(QApplication::activeWindow(), i18n("Cannot create directory %1", sourcesFolder.absolutePath()));
+ continue;
+ }
+ //KIO::filesize_t m_requestedSize;
+ KIO::CopyJob *copyjob = KIO::copy(file, QUrl::fromLocalFile(sourcesFolder.absolutePath()));
+ //TODO: for some reason, passing metadata does not work...
+ copyjob->addMetaData("group", data.value("group"));
+ copyjob->addMetaData("groupId", data.value("groupId"));
+ copyjob->addMetaData("comment", data.value("comment"));
+ KJobWidgets::setWindow(copyjob, QApplication::activeWindow());
+ connect(copyjob, &KIO::CopyJob::copyingDone, this, &ClipManager::slotAddCopiedClip);
+ continue;
+ }
+ }*/
+
+
+
+ //TODO check folders
+ /*QList < QList<QUrl> > foldersList;
+ QMimeDatabase db;
+ foreach(const QUrl & file, list) {
+ // Check there is no folder here
+ QMimeType type = db.mimeTypeForUrl(file);
+ if (type.inherits("inode/directory")) {
+ // user dropped a folder, import its files
+ list.removeAll(file);
+ QDir dir(file.path());
+ QStringList result = dir.entryList(QDir::Files);
+ QList <QUrl> folderFiles;
+ folderFiles << file;
+ foreach(const QString & path, result) {
+ // TODO: create folder command
+ folderFiles.append(QUrl::fromLocalFile(dir.absoluteFilePath(path)));
+ }
+ if (folderFiles.count() > 1) foldersList.append(folderFiles);
+ }
+ }*/
+
+ foreach(const QUrl &file, urls) {
+ QDomDocument xml;
+ QDomElement prod = xml.createElement("producer");
+ xml.appendChild(prod);
+ QMap <QString, QString> properties;
+ properties.insert("resource", file.path());
+ if (!groupInfo.isEmpty()) {
+ properties.insert("kdenlive:folderid", groupInfo.at(0));
+ }
+ // Merge data
+ QMapIterator<QString, QString> i(data);
+ while (i.hasNext()) {
+ i.next();
+ properties.insert(i.key(), i.value());
+ }
+ addXmlProperties(prod, properties);
+ //prod.setAttribute("resource", file.path());
+ uint id = bin->getFreeClipId();
+ prod.setAttribute("id", QString::number(id));
+ QMimeDatabase db;
+ QMimeType type = db.mimeTypeForUrl(file);
+ if (type.name().startsWith(QLatin1String("image/"))) {
+ prod.setAttribute("type", (int) Image);
+ prod.setAttribute("in", 0);
+ prod.setAttribute("out", doc->getFramePos(KdenliveSettings::image_duration()) - 1);
+ if (KdenliveSettings::autoimagetransparency()) prod.setAttribute("transparency", 1);
+ // Read EXIF metadata for JPEG
+ if (type.inherits("image/jpeg")) {
+ //TODO KF5 how to read metadata?
+ /*
+ KFileMetaInfo metaInfo(file.path(), QString("image/jpeg"), KFileMetaInfo::TechnicalInfo);
+ const QHash<QString, KFileMetaInfoItem> metaInfoItems = metaInfo.items();
+ foreach(const KFileMetaInfoItem & metaInfoItem, metaInfoItems) {
+ QDomElement meta = xml.createElement("metaproperty");
+ meta.setAttribute("name", "meta.attr." + metaInfoItem.name().section('#', 1));
+ QDomText value = xml.createTextNode(metaInfoItem.value().toString());
+ meta.setAttribute("tool", "KDE Metadata");
+ meta.appendChild(value);
+ prod.appendChild(meta);
+ }*/
+ }
+ } else if (type.inherits("application/x-kdenlivetitle")) {
+ // opening a title file
+ QDomDocument txtdoc("titledocument");
+ QFile txtfile(file.path());
+ if (txtfile.open(QIODevice::ReadOnly) && txtdoc.setContent(&txtfile)) {
+ txtfile.close();
+ prod.setAttribute("type", (int) Text);
+ // extract embeded images
+ QDomNodeList items = txtdoc.elementsByTagName("content");
+ for (int i = 0; i < items.count() ; ++i) {
+ QDomElement content = items.item(i).toElement();
+ if (content.hasAttribute("base64")) {
+ QString titlesFolder = doc->projectFolder().path() + QDir::separator() + "titles/";
+ QString path = TitleDocument::extractBase64Image(titlesFolder, content.attribute("base64"));
+ if (!path.isEmpty()) {
+ content.setAttribute("url", path);
+ content.removeAttribute("base64");
+ }
+ }
+ }
+ prod.setAttribute("transparency", 1);
+ prod.setAttribute("in", 0);
+ int duration = 0;
+ if (txtdoc.documentElement().hasAttribute("duration")) {
+ duration = txtdoc.documentElement().attribute("duration").toInt();
+ } else if (txtdoc.documentElement().hasAttribute("out")) {
+ duration = txtdoc.documentElement().attribute("out").toInt();
+ }
+ if (duration <= 0)
+ duration = doc->getFramePos(KdenliveSettings::title_duration()) - 1;
+ prod.setAttribute("duration", duration);
+ prod.setAttribute("out", duration);
+ txtdoc.documentElement().setAttribute("duration", duration);
+ txtdoc.documentElement().setAttribute("out", duration);
+ QString titleData = txtdoc.toString();
+ prod.setAttribute("xmldata", titleData);
+ } else {
+ txtfile.close();
+ }
+ }
+ new AddClipCommand(doc, xml.documentElement(), QString::number(id), true, addClips);
+ }
+ if (addClips->childCount() > 0) {
+ addClips->setText(i18np("Add clip", "Add clips", addClips->childCount()));
+ doc->commandStack()->push(addClips);
+ }
+}
+
+void ClipCreationDialog::createClipsCommand(KdenliveDoc *doc, QStringList groupInfo, Bin *bin)
+{
+ QList <QUrl> list;
+ QString allExtensions = getExtensions().join(" ");
+ QString dialogFilter = allExtensions + "|" + i18n("All Supported Files") + "\n*|" + i18n("All Files");
+ QCheckBox *b = new QCheckBox(i18n("Import image sequence"));
+ b->setChecked(KdenliveSettings::autoimagesequence());
+ QCheckBox *c = new QCheckBox(i18n("Transparent background for images"));
+ c->setChecked(KdenliveSettings::autoimagetransparency());
+ QFrame *f = new QFrame();
+ f->setFrameShape(QFrame::NoFrame);
+ QHBoxLayout *l = new QHBoxLayout;
+ l->addWidget(b);
+ l->addWidget(c);
+ l->addStretch(5);
+ f->setLayout(l);
+ QString clipFolder = KRecentDirs::dir(":KdenliveClipFolder");
+ if (clipFolder.isEmpty()) {
+ clipFolder = QDir::homePath();
+ }
+ QDialog *dlg = new QDialog((QWidget *) doc->parent());
+ KFileWidget *fileWidget = new KFileWidget(QUrl::fromLocalFile(clipFolder), dlg);
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->addWidget(fileWidget);
+ fileWidget->setCustomWidget(f);
+ fileWidget->okButton()->show();
+ fileWidget->cancelButton()->show();
+ QObject::connect(fileWidget->okButton(), &QPushButton::clicked, fileWidget, &KFileWidget::slotOk);
+ QObject::connect(fileWidget, &KFileWidget::accepted, fileWidget, &KFileWidget::accept);
+ QObject::connect(fileWidget, &KFileWidget::accepted, dlg, &QDialog::accept);
+ QObject::connect(fileWidget->cancelButton(), &QPushButton::clicked, dlg, &QDialog::reject);
+ dlg->setLayout(layout);
+ fileWidget->setFilter(dialogFilter);
+ fileWidget->setMode(KFile::Files | KFile::ExistingOnly);
+ KSharedConfig::Ptr conf = KSharedConfig::openConfig();
+ KWindowConfig::restoreWindowSize(dlg->windowHandle(), conf->group("FileDialogSize"));
+ dlg->resize(dlg->windowHandle()->size());
+ if (dlg->exec() == QDialog::Accepted) {
+ KdenliveSettings::setAutoimagetransparency(c->isChecked());
+ list = fileWidget->selectedUrls();
+ if (!list.isEmpty()) {
+ KRecentDirs::add(":KdenliveClipFolder", list.first().adjusted(QUrl::RemoveFilename).path());
+ }
+ if (b->isChecked() && list.count() == 1) {
+ // Check for image sequence
+ QUrl url = list.at(0);
+ QString fileName = url.fileName().section('.', 0, -2);
+ if (fileName.at(fileName.size() - 1).isDigit()) {
+ KFileItem item(url);
+ if (item.mimetype().startsWith(QLatin1String("image"))) {
+ // import as sequence if we found more than one image in the sequence
+ QStringList list;
+ QString pattern = SlideshowClip::selectedPath(url, false, QString(), &list);
+ int count = list.count();
+ if (count > 1) {
+ delete fileWidget;
+ delete dlg;
+ // get image sequence base name
+ while (fileName.at(fileName.size() - 1).isDigit()) {
+ fileName.chop(1);
+ }
+ QDomDocument xml;
+ QDomElement prod = xml.createElement("producer");
+ xml.appendChild(prod);
+ prod.setAttribute("in", "0");
+ QString duration = doc->timecode().reformatSeparators(KdenliveSettings::sequence_duration());
+ prod.setAttribute("out", QString::number(doc->getFramePos(duration) * count));
+ QMap <QString, QString> properties;
+ properties.insert("resource", pattern);
+ properties.insert("kdenlive:clipname", fileName);
+ properties.insert("ttl", QString::number(doc->getFramePos(duration)));
+ properties.insert("loop", QString::number(false));
+ properties.insert("crop", QString::number(false));
+ properties.insert("fade", QString::number(false));
+ properties.insert("luma_duration", QString::number(doc->getFramePos(doc->timecode().getTimecodeFromFrames(int(ceil(doc->timecode().fps()))))));
+ if (!groupInfo.isEmpty()) {
+ properties.insert("kdenlive:folderid", groupInfo.at(0));
+ }
+ addXmlProperties(prod, properties);
+ uint id = bin->getFreeClipId();
+ AddClipCommand *command = new AddClipCommand(doc, xml.documentElement(), QString::number(id), true);
+ doc->commandStack()->push(command);
+ return;
+ }
+ }
+ }
+ }
+ }
+ KConfigGroup group = conf->group("FileDialogSize");
+ KWindowConfig::saveWindowSize(dlg->windowHandle(), group);
+
+ delete fileWidget;
+ delete dlg;
+ if (!list.isEmpty()) {
+ ClipCreationDialog::createClipsCommand(doc, list, groupInfo, bin);
+ }
+}
+
+
+
diff --git a/src/dialogs/clipcreationdialog.h b/src/dialogs/clipcreationdialog.h
new file mode 100644
index 0000000..6e038da
--- /dev/null
+++ b/src/dialogs/clipcreationdialog.h
@@ -0,0 +1,54 @@
+/*
+Copyright (C) 2015 Jean-Baptiste Mardelle <[email protected]>
+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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef CLIPCREATIONDIALOG_H
+#define CLIPCREATIONDIALOG_H
+
+
+#include "definitions.h"
+
+class KdenliveDoc;
+class Bin;
+
+/**
+ * @namespace ClipCreationDialog
+ * @brief This namespace contains a list of static methods displaying widgets
+ * allowing creation of all clip types.
+ */
+
+namespace ClipCreationDialog
+{
+
+ QStringList getExtensions();
+ void createColorClip(KdenliveDoc *doc, QStringList groupInfo, Bin *bin);
+ void createClipFromXml(KdenliveDoc *doc, QDomElement xml, QStringList groupInfo, Bin *bin);
+ void createSlideshowClip(KdenliveDoc *doc, QStringList groupInfo, Bin *bin);
+ void createTitleClip(KdenliveDoc *doc, QStringList groupInfo, QString templatePath, Bin *bin);
+ void createTitleTemplateClip(KdenliveDoc *doc, QStringList groupInfo, QString templatePath, Bin *bin);
+ void createClipsCommand(KdenliveDoc *doc, const QList<QUrl> &urls, QStringList groupInfo, Bin *bin, const QMap <QString, QString> &data = QMap <QString, QString>());
+ void createClipsCommand(KdenliveDoc *doc, QStringList groupInfo, Bin *bin);
+ void addXmlProperties(QDomElement &producer, QMap <QString, QString> &properties);
+};
+
+
+#endif
+
diff --git a/src/dialogs/encodingprofilesdialog.cpp b/src/dialogs/encodingprofilesdialog.cpp
index e5d1f2c..0b5ede4 100644
--- a/src/dialogs/encodingprofilesdialog.cpp
+++ b/src/dialogs/encodingprofilesdialog.cpp
@@ -132,8 +132,8 @@ void EncodingProfilesDialog::slotAddProfile()
QLineEdit *pext = new QLineEdit;
l->addWidget(pext);
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
- connect(box, SIGNAL(accepted()), d, SLOT(accept()));
- connect(box, SIGNAL(rejected()), d, SLOT(reject()));
+ connect(box, &QDialogButtonBox::accepted, d.data(), &QDialog::accept);
+ connect(box, &QDialogButtonBox::rejected, d.data(), &QDialog::reject);
l->addWidget(box);
d->setLayout(l);
@@ -164,8 +164,8 @@ void EncodingProfilesDialog::slotEditProfile()
QLineEdit *pext = new QLineEdit;
l->addWidget(pext);
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
- connect(box, SIGNAL(accepted()), d, SLOT(accept()));
- connect(box, SIGNAL(rejected()), d, SLOT(reject()));
+ connect(box, &QDialogButtonBox::accepted, d.data(), &QDialog::accept);
+ connect(box, &QDialogButtonBox::rejected, d.data(), &QDialog::reject);
l->addWidget(box);
d->setLayout(l);
diff --git a/src/dialogs/kdenlivesettingsdialog.cpp b/src/dialogs/kdenlivesettingsdialog.cpp
index b33df23..760068f 100644
--- a/src/dialogs/kdenlivesettingsdialog.cpp
+++ b/src/dialogs/kdenlivesettingsdialog.cpp
@@ -49,7 +49,7 @@
#endif
-KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap<QString, QString>& mappable_actions, QWidget * parent) :
+KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap<QString, QString>& mappable_actions, bool gpuAllowed, QWidget * parent) :
KConfigDialog(parent, "settings", KdenliveSettings::self()),
m_modified(false),
m_shuttleModified(false),
@@ -169,9 +169,8 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap<QString, QString>& map
m_configSdl.reload_blackmagic->setIcon(QIcon::fromTheme("view-refresh"));
connect(m_configSdl.reload_blackmagic, SIGNAL(clicked(bool)), this, SLOT(slotReloadBlackMagic()));
-#ifndef USE_OPENGL
- m_configSdl.kcfg_openglmonitors->setHidden(true);
-#endif
+ //m_configSdl.kcfg_openglmonitors->setHidden(true);
+
m_page6 = addPage(p6, i18n("Playback"), "media-playback-start");
@@ -264,6 +263,10 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap<QString, QString>& map
slotUpdateV4lProfile(-1);
slotUpdateGrabProfile(-1);
slotUpdateDecklinkProfile(-1);
+
+ // enable GPU accel only if Movit is found
+ m_configSdl.kcfg_gpu_accel->setEnabled(gpuAllowed);
+ m_configSdl.kcfg_gpu_accel->setToolTip(i18n("GPU processing needs MLT compiled with Movit and Rtaudio modules"));
Render::getBlackMagicDeviceList(m_configCapture.kcfg_decklink_capturedevice);
if (!Render::getBlackMagicOutputDeviceList(m_configSdl.kcfg_blackmagic_output_device)) {
@@ -444,27 +447,13 @@ void KdenliveSettingsDialog::initDevices()
}
}
- // Fill video drivers
- m_configSdl.kcfg_video_driver->addItem(i18n("Automatic"), QString());
-#ifndef Q_WS_MAC
- m_configSdl.kcfg_video_driver->addItem(i18n("XVideo"), "x11");
- m_configSdl.kcfg_video_driver->addItem(i18n("X11"), "x11_noaccel");
- m_configSdl.kcfg_video_driver->addItem(i18n("XFree86 DGA 2.0"), "dga");
- m_configSdl.kcfg_video_driver->addItem(i18n("Nano X"), "nanox");
- m_configSdl.kcfg_video_driver->addItem(i18n("Framebuffer console"), "fbcon");
- m_configSdl.kcfg_video_driver->addItem(i18n("Direct FB"), "directfb");
- m_configSdl.kcfg_video_driver->addItem(i18n("SVGAlib"), "svgalib");
- m_configSdl.kcfg_video_driver->addItem(i18n("General graphics interface"), "ggi");
- m_configSdl.kcfg_video_driver->addItem(i18n("Ascii art library"), "aalib");
-#endif
-
// Fill the list of audio playback / recording devices
m_configSdl.kcfg_audio_device->addItem(i18n("Default"), QString());
m_configCapture.kcfg_v4l_alsadevice->addItem(i18n("Default"), "default");
if (!QStandardPaths::findExecutable("aplay").isEmpty()) {
m_readProcess.setOutputChannelMode(KProcess::OnlyStdoutChannel);
m_readProcess.setProgram("aplay", QStringList() << "-l");
- connect(&m_readProcess, SIGNAL(readyReadStandardOutput()) , this, SLOT(slotReadAudioDevices()));
+ connect(&m_readProcess, &KProcess::readyReadStandardOutput, this, &KdenliveSettingsDialog::slotReadAudioDevices);
m_readProcess.execute(5000);
} else {
// If aplay is not installed on the system, parse the /proc/asound/pcm file
@@ -763,12 +752,6 @@ void KdenliveSettingsDialog::updateSettings()
resetProfile = true;
}
- value = m_configSdl.kcfg_video_driver->itemData(m_configSdl.kcfg_video_driver->currentIndex()).toString();
- if (value != KdenliveSettings::videodrivername()) {
- KdenliveSettings::setVideodrivername(value);
- resetProfile = true;
- }
-
if (m_configSdl.kcfg_window_background->color() != KdenliveSettings::window_background()) {
KdenliveSettings::setWindow_background(m_configSdl.kcfg_window_background->color());
resetProfile = true;
@@ -798,10 +781,22 @@ void KdenliveSettingsDialog::updateSettings()
if (KdenliveSettings::shuttlebuttons() != maps)
KdenliveSettings::setShuttlebuttons(maps);
#endif
+
+ bool restart = false;
+ if (m_configSdl.kcfg_gpu_accel->isChecked() != KdenliveSettings::gpu_accel()) {
+ // GPU setting was changed, we need to restart Kdenlive or everything will be corrupted
+ if (KMessageBox::warningContinueCancel(this, i18n("Kdenlive must be restarted to change this setting")) == KMessageBox::Continue) {
+ restart = true;
+ }
+ else {
+ m_configSdl.kcfg_gpu_accel->setChecked(KdenliveSettings::gpu_accel());
+ }
+ }
KConfigDialog::settingsChangedSlot();
//KConfigDialog::updateSettings();
if (resetProfile) emit doResetProfile();
+ if (restart) emit restartKdenlive();
}
void KdenliveSettingsDialog::slotUpdateDisplay()
diff --git a/src/dialogs/kdenlivesettingsdialog.h b/src/dialogs/kdenlivesettingsdialog.h
index d502e02..3645463 100644
--- a/src/dialogs/kdenlivesettingsdialog.h
+++ b/src/dialogs/kdenlivesettingsdialog.h
@@ -39,7 +39,7 @@ class KdenliveSettingsDialog : public KConfigDialog
Q_OBJECT
public:
- KdenliveSettingsDialog(const QMap<QString, QString>& mappable_actions, QWidget * parent = 0);
+ KdenliveSettingsDialog(const QMap<QString, QString>& mappable_actions, bool gpuAllowed, QWidget * parent = 0);
~KdenliveSettingsDialog();
void showPage(int page, int option);
void checkProfile();
@@ -119,6 +119,8 @@ signals:
void updateCaptureFolder();
// Screengrab method changed between fullsceen and region, update rec monitor
void updateFullScreenGrab();
+ /** @brief A settings changed that requires a Kdenlive restart, trigger it */
+ void restartKdenlive();
};
diff --git a/src/dialogs/profilesdialog.cpp b/src/dialogs/profilesdialog.cpp
index 46ce29a..7663a9e 100644
--- a/src/dialogs/profilesdialog.cpp
+++ b/src/dialogs/profilesdialog.cpp
@@ -286,18 +286,6 @@ MltVideoProfile ProfilesDialog::getVideoProfile(const QString &name)
}
// static
-double ProfilesDialog::getStringEval(const MltVideoProfile &profile, QString eval, const QPoint& frameSize)
-{
- QScriptEngine sEngine;
- sEngine.globalObject().setProperty("maxWidth", profile.width > frameSize.x() ? profile.width : frameSize.x());
- sEngine.globalObject().setProperty("maxHeight", profile.height > frameSize.y() ? profile.height : frameSize.y());
- sEngine.globalObject().setProperty("width", profile.width);
- sEngine.globalObject().setProperty("height", profile.height);
- return sEngine.evaluate(eval.remove('%')).toNumber();
-}
-
-
-// static
bool ProfilesDialog::existingProfileDescription(const QString &desc)
{
QStringList profilesFilter;
@@ -312,7 +300,7 @@ bool ProfilesDialog::existingProfileDescription(const QString &desc)
}
// List custom profiles
- QStringList customProfiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, "profiles");
+ QStringList customProfiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, "/profiles/", QStandardPaths::LocateDirectory);
for (int i = 0; i < customProfiles.size(); ++i) {
QDir customDir(customProfiles.at(i));
profilesFiles = customDir.entryList(profilesFilter, QDir::Files);
@@ -350,7 +338,7 @@ QString ProfilesDialog::existingProfile(const MltVideoProfile &profile)
}
// Check custom profiles
- QStringList customProfiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, "profiles");
+ QStringList customProfiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, "/profiles/", QStandardPaths::LocateDirectory);
for (int i = 0; i < customProfiles.size(); ++i) {
profilesFiles = QDir(customProfiles.at(i)).entryList(profilesFilter, QDir::Files);
for (int j = 0; j < profilesFiles.size(); ++j) {
@@ -388,7 +376,7 @@ QMap <QString, QString> ProfilesDialog::getProfilesInfo()
}
// List custom profiles
- QStringList customProfiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, "profiles");
+ QStringList customProfiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, "/profiles/", QStandardPaths::LocateDirectory);
for (int i = 0; i < customProfiles.size(); ++i) {
profilesFiles = QDir(customProfiles.at(i)).entryList(profilesFilter, QDir::Files);
for (int j = 0; j < profilesFiles.size(); ++j) {
@@ -457,7 +445,7 @@ QMap <QString, QString> ProfilesDialog::getProfilesFromProperties(int width, int
}
// List custom profiles
- QStringList customProfiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, "profiles");
+ QStringList customProfiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, "/profiles/", QStandardPaths::LocateDirectory);
for (int i = 0; i < customProfiles.size(); ++i) {
QStringList profiles = QDir(customProfiles.at(i)).entryList(profilesFilter, QDir::Files);
for (int j = 0; j < profiles.size(); ++j) {
diff --git a/src/dialogs/profilesdialog.h b/src/dialogs/profilesdialog.h
index 272b1b0..0374085 100644
--- a/src/dialogs/profilesdialog.h
+++ b/src/dialogs/profilesdialog.h
@@ -61,12 +61,6 @@ public:
* @return A string list of the matching profiles description */
static QMap <QString, QString> getProfilesFromProperties(int width, int height, double fps, double par, bool useDisplayWidth = false);
- /** @brief Returns an value from a string by replacing "%width" and "%height" with given profile values:
- * @param profile The profile that gives width & height
- * @param eval The string to be evaluated, for example: "%width / 2"
- * @return the evaluated value */
- static double getStringEval(const MltVideoProfile &profile, QString eval, const QPoint &frameSize = QPoint());
-
/** @brief Get the descriptive text for given colorspace code (defined by MLT)
* @param colorspace An int as defined in mlt_profile.h
* @return The string description */
diff --git a/src/dialogs/renderwidget.cpp b/src/dialogs/renderwidget.cpp
index c10472e..0e9d76c 100644
--- a/src/dialogs/renderwidget.cpp
+++ b/src/dialogs/renderwidget.cpp
@@ -379,12 +379,11 @@ void RenderWidget::slotCheckEndGuidePosition()
m_view.guide_end->setCurrentIndex(m_view.guide_start->currentIndex());
}
-void RenderWidget::setGuides(QDomElement guidesxml, double duration)
+void RenderWidget::setGuides(QMap <double, QString> guidesData, double duration)
{
m_view.guide_start->clear();
m_view.guide_end->clear();
- QDomNodeList nodes = guidesxml.elementsByTagName("guide");
- if (!nodes.isEmpty()) {
+ if (!guidesData.isEmpty()) {
m_view.guide_start->addItem(i18n("Beginning"), "0");
m_view.render_guide->setEnabled(true);
m_view.create_chapter->setEnabled(true);
@@ -393,16 +392,15 @@ void RenderWidget::setGuides(QDomElement guidesxml, double duration)
m_view.create_chapter->setEnabled(false);
}
double fps = (double) m_profile.frame_rate_num / m_profile.frame_rate_den;
- for (int i = 0; i < nodes.count(); ++i) {
- QDomElement e = nodes.item(i).toElement();
- if (!e.isNull()) {
- GenTime pos = GenTime(e.attribute("time").toDouble());
- const QString guidePos = Timecode::getStringTimecode(pos.frames(fps), fps);
- m_view.guide_start->addItem(e.attribute("comment") + '/' + guidePos, e.attribute("time").toDouble());
- m_view.guide_end->addItem(e.attribute("comment") + '/' + guidePos, e.attribute("time").toDouble());
- }
- }
- if (!nodes.isEmpty())
+ QMapIterator<double, QString> i(guidesData);
+ while (i.hasNext()) {
+ i.next();
+ GenTime pos = GenTime(i.key());
+ const QString guidePos = Timecode::getStringTimecode(pos.frames(fps), fps);
+ m_view.guide_start->addItem(i.value() + '/' + guidePos, i.key());
+ m_view.guide_end->addItem(i.value() + '/' + guidePos, i.key());
+ }
+ if (!guidesData.isEmpty())
m_view.guide_end->addItem(i18n("End"), QString::number(duration));
}
@@ -1145,6 +1143,9 @@ void RenderWidget::slotExport(bool scriptExport, int zoneIn, int zoneOut,
render_process_args << (scriptExport ? "$SOURCE_" + QString::number(stemIdx) : playlistPaths.at(stemIdx));
render_process_args << (scriptExport ? "$TARGET_" + QString::number(stemIdx) : QUrl(dest).url());
+ if (KdenliveSettings::gpu_accel()) {
+ render_process_args << "glsl.=1";
+ }
render_process_args << paramsList;
QString group = m_view.size_list->currentItem()->data(MetaGroupRole).toString();
diff --git a/src/dialogs/renderwidget.h b/src/dialogs/renderwidget.h
index 24be002..0c7c239 100644
--- a/src/dialogs/renderwidget.h
+++ b/src/dialogs/renderwidget.h
@@ -113,7 +113,7 @@ class RenderWidget : public QDialog
public:
explicit RenderWidget(const QString &projectfolder, bool enableProxy, const MltVideoProfile &profile, QWidget * parent = 0);
virtual ~RenderWidget();
- void setGuides(QDomElement guidesxml, double duration);
+ void setGuides(QMap <double, QString> guidesData, double duration);
void focusFirstVisibleItem(const QString &profile = QString());
void setProfile(const MltVideoProfile& profile);
void setRenderJob(const QString &dest, int progress = 0);
diff --git a/src/dialogs/wizard.cpp b/src/dialogs/wizard.cpp
index c0ecf2c..293dd17 100644
--- a/src/dialogs/wizard.cpp
+++ b/src/dialogs/wizard.cpp
@@ -70,7 +70,7 @@ Wizard::Wizard(bool upgrade, QWidget *parent) :
m_startLayout = new QVBoxLayout;
m_startLayout->addWidget(m_welcomeLabel);
QPushButton *but = new QPushButton(QIcon::fromTheme("help-about"), i18n("Discover the features of this Kdenlive release"), this);
- connect(but, SIGNAL(clicked()), this, SLOT(slotShowWebInfos()));
+ connect(but, &QPushButton::clicked, this, &Wizard::slotShowWebInfos);
m_startLayout->addStretch();
m_startLayout->addWidget(but);
@@ -461,7 +461,7 @@ void Wizard::checkMissingCodecs()
infoMessage->setCloseButtonVisible(false);
infoMessage->setWordWrap(true);
infoMessage->setMessageType(KMessageWidget::Warning);
- connect(infoMessage, SIGNAL(linkActivated(QString)), this, SLOT(slotOpenManual()));
+ connect(infoMessage, &KMessageWidget::linkActivated, this, &Wizard::slotOpenManual);
infoMessage->setText(missing);
infoMessage->animatedShow();
}
diff --git a/src/doc/docclipbase.cpp b/src/doc/docclipbase.cpp
index 46221f5..aca93ec 100644
--- a/src/doc/docclipbase.cpp
+++ b/src/doc/docclipbase.cpp
@@ -86,7 +86,7 @@ DocClipBase::DocClipBase(ClipManager *clipManager, QDomElement xml, const QStrin
}
QUrl url = QUrl::fromLocalFile(xml.attribute("resource"));
- if (!m_properties.contains("file_hash") && url.isValid()) getFileHash(url.toLocalFile());
+ if (!m_properties.contains("kdenlive:file_hash") && url.isValid()) getFileHash(url.toLocalFile());
if (xml.hasAttribute("duration")) {
setDuration(GenTime(xml.attribute("duration").toInt(), KdenliveSettings::project_fps()));
@@ -98,7 +98,7 @@ DocClipBase::DocClipBase(ClipManager *clipManager, QDomElement xml, const QStrin
if (!m_properties.contains("name")) m_properties.insert("name", url.fileName());
- m_thumbProd = new KThumb(clipManager, url, m_id, m_properties.value("file_hash"));
+ m_thumbProd = new KThumb(clipManager, url, m_id, m_properties.value("kdenlive:file_hash"));
// Setup timer to trigger audio thumbs creation
m_audioTimer.setSingleShot(true);
@@ -524,8 +524,11 @@ void DocClipBase::setValid()
m_placeHolder = false;
}
-void DocClipBase::setProducer(Mlt::Producer *producer, bool reset, bool readPropertiesFromProducer)
+void DocClipBase::setProducer(Mlt::Producer &producer, bool reset, bool readPropertiesFromProducer)
{
+ setDuration(GenTime(producer.get_length(), KdenliveSettings::project_fps()));
+ return;
+ /*
if (producer == NULL) return;
if (reset) {
QMutexLocker locker(&m_producerMutex);
@@ -603,6 +606,7 @@ void DocClipBase::setProducer(Mlt::Producer *producer, bool reset, bool readProp
}
if (updated && readPropertiesFromProducer && (m_clipType != Color && m_clipType != Image && m_clipType != Text))
setDuration(GenTime(producer->get_length(), KdenliveSettings::project_fps()));
+ */
}
static double getPixelAspect(QMap<QString, QString>& props) {
@@ -744,6 +748,7 @@ Mlt::Producer *DocClipBase::getCloneProducer()
Mlt::Producer *DocClipBase::getProducer(int track)
{
QMutexLocker locker(&m_producerMutex);
+
if (track == -1 || (m_clipType != Audio && m_clipType != AV && m_clipType != Playlist)) {
if (m_baseTrackProducers.count() == 0) {
return NULL;
@@ -1080,7 +1085,7 @@ void DocClipBase::getFileHash(const QString &url)
fileData = file.readAll();
file.close();
fileHash = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
- m_properties.insert("file_hash", QString(fileHash.toHex()));
+ m_properties.insert("kdenlive:file_hash", QString(fileHash.toHex()));
}
}
@@ -1098,7 +1103,7 @@ QString DocClipBase::getClipHash() const
else if (m_clipType == Color) hash = QCryptographicHash::hash(m_properties.value("colour").toLatin1().data(), QCryptographicHash::Md5).toHex();
else if (m_clipType == Text) hash = QCryptographicHash::hash(QString("title" + getId() + m_properties.value("xmldata")).toUtf8().data(), QCryptographicHash::Md5).toHex();
else {
- if (m_properties.contains("file_hash")) hash = m_properties.value("file_hash");
+ if (m_properties.contains("kdenlive:file_hash")) hash = m_properties.value("kdenlive:file_hash");
if (hash.isEmpty()) hash = getHash(fileURL().path());
}
@@ -1138,7 +1143,7 @@ void DocClipBase::setProperty(const QString &key, const QString &value)
m_properties.insert(key, value);
if (key == "resource") {
getFileHash(value);
- if (m_thumbProd) m_thumbProd->updateClipUrl(QUrl::fromLocalFile(value), m_properties.value("file_hash"));
+ if (m_thumbProd) m_thumbProd->updateClipUrl(QUrl::fromLocalFile(value), m_properties.value("kdenlive:file_hash"));
//else if (key == "transparency") m_clipProducer->set("transparency", value.toInt());
} else if (key == "out") {
setDuration(GenTime(value.toInt() + 1, KdenliveSettings::project_fps()));
diff --git a/src/doc/docclipbase.h b/src/doc/docclipbase.h
index f96f1cd..7d0b2e3 100644
--- a/src/doc/docclipbase.h
+++ b/src/doc/docclipbase.h
@@ -58,7 +58,7 @@ public:
* done here. If a new clip type is added then it should be possible to combine it with both audio
* and video. */
- DocClipBase(ClipManager *clipManager, QDomElement xml, const QString &id);
+ Q_DECL_DEPRECATED DocClipBase(ClipManager *clipManager, QDomElement xml, const QString &id);
// DocClipBase & operator=(const DocClipBase & clip);
virtual ~ DocClipBase();
@@ -112,7 +112,7 @@ public:
}
/** Sets producers for the current clip (one for each track due to a limitation in MLT's track mixing */
- void setProducer(Mlt::Producer *producer, bool reset = false, bool readPropertiesFromProducer = false);
+ void setProducer(Mlt::Producer &producer, bool reset = false, bool readPropertiesFromProducer = false);
/** Retrieve a producer for a track */
Mlt::Producer *getProducer(int track = -1);
/** Get a copy of the producer, for use in the clip monitor */
@@ -207,6 +207,9 @@ private: // Private attributes
/** The number of times this clip is used in the project - the number of references to this clip
* that exist. */
uint m_refcount;
+
+ /** The index of this clip in Bin Playlist, used to retrieve MLT::Producer for this clip */
+ int m_binIndex;
QList <Mlt::Producer *> m_baseTrackProducers;
QList <Mlt::Producer *> m_videoTrackProducers;
QList <Mlt::Producer *> m_audioTrackProducers;
diff --git a/src/doc/doccommands.cpp b/src/doc/doccommands.cpp
index 0eda79f..5f33390 100644
--- a/src/doc/doccommands.cpp
+++ b/src/doc/doccommands.cpp
@@ -31,10 +31,15 @@ AddClipCommand::AddClipCommand(KdenliveDoc *doc, const QDomElement &xml, const Q
{
if (doIt) setText(i18n("Add clip"));
else setText(i18n("Delete clip"));
+ QString str;
+ QTextStream stream(&str);
+ m_xml.save(stream, 4);
+ qDebug()<<text()<<"\n-----------\n"<<str;
}
// virtual
void AddClipCommand::undo()
{
+ qDebug()<<"/ / / /UNDOING COMMAND";
if (m_doIt)
m_doc->deleteClip(m_id);
else
@@ -43,6 +48,7 @@ void AddClipCommand::undo()
// virtual
void AddClipCommand::redo()
{
+ qDebug()<<"/ / / /REDOING COMMAND";
if (m_doIt)
m_doc->addClip(m_xml, m_id);
else
diff --git a/src/doc/documentvalidator.cpp b/src/doc/documentvalidator.cpp
index 93e1884..827152d 100644
--- a/src/doc/documentvalidator.cpp
+++ b/src/doc/documentvalidator.cpp
@@ -23,6 +23,8 @@
#include "definitions.h"
#include "effectslist/initeffects.h"
#include "mainwindow.h"
+#include "core.h"
+#include "mltcontroller/bincontroller.h"
#include <QDebug>
#include <KMessageBox>
@@ -37,6 +39,10 @@
#include <mlt++/Mlt.h>
#include <locale>
+#ifdef Q_OS_MAC
+#include <xlocale.h>
+#endif
+
#include <QStandardPaths>
@@ -111,9 +117,9 @@ bool DocumentValidator::validate(const double currentVersion)
QLocale::setDefault(documentLocale);
// locale conversion might need to be redone
#ifndef Q_OS_MAC
- initEffects::parseEffectFiles(setlocale(LC_NUMERIC, NULL));
+ initEffects::parseEffectFiles(pCore->binController()->mltRepository(), setlocale(LC_NUMERIC, NULL));
#else
- initEffects::parseEffectFiles(setlocale(LC_NUMERIC_MASK, NULL));
+ initEffects::parseEffectFiles(pCore->binController()->mltRepository(), setlocale(LC_NUMERIC_MASK, NULL));
#endif
}
@@ -137,6 +143,7 @@ bool DocumentValidator::validate(const double currentVersion)
if (!upgrade(version, currentVersion))
return false;
+ return true;
/*
* Check the syntax (this will be replaced by XSD validation with Qt 4.6)
* and correct some errors
@@ -158,17 +165,31 @@ bool DocumentValidator::validate(const double currentVersion)
* Make sure at least one track exists, and they're equal in number to
* to the maximum between MLT and Kdenlive playlists and tracks
*/
+ // In older Kdenlive project files, one playlist is not a real track (the black track), we have: track count = playlist count- 1
+ // In newer Qt5 Kdenlive, the Bin playlist should not appear as a track. So we should have: track count = playlist count- 2
+ int trackOffset = 1;
QDomNodeList playlists = m_doc.elementsByTagName("playlist");
- int tracksMax = playlists.count() - 1; // Remove the black track
+ // Remove "main bin" playlist that simply holds the bin's clips and is not a real playlist
+ for (int i = 0; i < playlists.count(); ++i) {
+ QString playlistId = playlists.at(i).toElement().attribute("id");
+ if (playlistId == BinController::binPlaylistId()) {
+ // remove pseudo-playlist
+ //playlists.at(i).parentNode().removeChild(playlists.at(i));
+ trackOffset = 2;
+ break;
+ }
+ }
+
+ int tracksMax = playlists.count() - trackOffset; // Remove the black track and bin track
QDomNodeList tracks = tractor.elementsByTagName("track");
tracksMax = qMax(tracks.count() - 1, tracksMax);
QDomNodeList tracksinfo = kdenliveDoc.elementsByTagName("trackinfo");
tracksMax = qMax(tracksinfo.count(), tracksMax);
tracksMax = qMax(1, tracksMax); // Force existence of one track
- if (playlists.count() - 1 < tracksMax ||
- tracks.count() - 1 < tracksMax ||
+ if (playlists.count() - trackOffset < tracksMax ||
+ tracks.count() < tracksMax ||
tracksinfo.count() < tracksMax) {
- //qDebug() << "//// WARNING, PROJECT IS CORRUPTED, MISSING TRACK";
+ qDebug() << "//// WARNING, PROJECT IS CORRUPTED, MISSING TRACK";
m_modified = true;
int difference;
// use the MLT tracks as reference
@@ -979,6 +1000,176 @@ bool DocumentValidator::upgrade(double version, const double currentVersion)
m_doc.firstChildElement("mlt").setAttribute("LC_NUMERIC", "C");
}
}
+
+ if (version <= 0.88) {
+ // convert to new MLT-only format
+ QDomNodeList producers = m_doc.elementsByTagName("producer");
+ QDomDocumentFragment frag = m_doc.createDocumentFragment();
+
+ // Create Bin Playlist
+ QDomElement main_playlist = m_doc.createElement("playlist");
+ QDomElement prop = m_doc.createElement("property");
+ prop.setAttribute("name", "xml_retain");
+ QDomText val = m_doc.createTextNode("1");
+ prop.appendChild(val);
+ main_playlist.appendChild(prop);
+
+ // Move markers
+ QDomNodeList markers = m_doc.elementsByTagName("marker");
+ for (int i = 0; i < markers.count(); ++i) {
+ QDomElement marker = markers.at(i).toElement();
+ QDomElement prop = m_doc.createElement("property");
+ prop.setAttribute("name", "kdenlive:marker." + marker.attribute("id") + ":" + marker.attribute("time"));
+ QDomText val = m_doc.createTextNode(marker.attribute("type") + ":" + marker.attribute("comment"));
+ prop.appendChild(val);
+ main_playlist.appendChild(prop);
+ }
+
+ // Move guides
+ QDomNodeList guides = m_doc.elementsByTagName("guide");
+ for (int i = 0; i < guides.count(); ++i) {
+ QDomElement guide = guides.at(i).toElement();
+ QDomElement prop = m_doc.createElement("property");
+ prop.setAttribute("name", "kdenlive:guide." + guide.attribute("time"));
+ QDomText val = m_doc.createTextNode(guide.attribute("comment"));
+ prop.appendChild(val);
+ main_playlist.appendChild(prop);
+ }
+
+ // Move folders
+ QDomNodeList folders = m_doc.elementsByTagName("folder");
+ for (int i = 0; i < folders.count(); ++i) {
+ QDomElement folder = folders.at(i).toElement();
+ QDomElement prop = m_doc.createElement("property");
+ prop.setAttribute("name", "kdenlive:folder.-1." + folder.attribute("id"));
+ QDomText val = m_doc.createTextNode(folder.attribute("name"));
+ prop.appendChild(val);
+ main_playlist.appendChild(prop);
+ }
+
+ QDomNode mlt = m_doc.firstChildElement("mlt");
+ main_playlist.setAttribute("id", pCore->binController()->binPlaylistId());
+ mlt.toElement().setAttribute("producer", pCore->binController()->binPlaylistId());
+ QStringList ids;
+ QStringList slowmotionIds;
+ QDomNode firstProd = m_doc.firstChildElement("producer");
+ for (int i = 0; i < producers.count(); ++i) {
+ QDomElement prod = producers.at(i).toElement();
+ QString id = prod.attribute("id");
+ if (id.startsWith("slowmotion")) {
+ // No need to process slowmotion producers
+ QString slowmo = id.section(":", 1, 1).section("_", 0, 0);
+ if (!slowmotionIds.contains(slowmo)) {
+ slowmotionIds << slowmo;
+ }
+ continue;
+ }
+ QString prodId = id.section("_", 0, 0);
+ if (ids.contains(prodId)) {
+ // Already processed, continue
+ continue;
+ }
+ if (id == prodId) {
+ // This is an original producer, move it to the main playlist
+ QDomElement entry = m_doc.createElement("entry");
+ entry.setAttribute("in", prod.attribute("in"));
+ entry.setAttribute("out", prod.attribute("out"));
+ entry.setAttribute("producer", id);
+ main_playlist.appendChild(entry);
+ frag.appendChild(prod);
+ i--;
+ }
+ else {
+ QDomElement originalProd = prod.cloneNode().toElement();
+ originalProd.setAttribute("id", prodId);
+ frag.appendChild(originalProd);
+ QDomElement entry = m_doc.createElement("entry");
+ entry.setAttribute("in", prod.attribute("in"));
+ entry.setAttribute("out", prod.attribute("out"));
+ entry.setAttribute("producer", prodId);
+ main_playlist.appendChild(entry);
+ }
+ ids.append(prodId);
+ }
+
+ // Set clip folders
+ QDomNodeList kdenlive_producers = m_doc.elementsByTagName("kdenlive_producer");
+ for (int j = 0; j < kdenlive_producers.count(); j++) {
+ QDomElement prod = kdenlive_producers.at(j).toElement();
+ QString prodName = prod.attribute("name");
+ if (!prod.hasAttribute("groupid") && prodName.isEmpty()) continue;
+ QString id = prod.attribute("id");
+ QString folder = prod.attribute("groupid");
+ QDomNodeList mlt_producers = frag.childNodes();
+ for (int k = 0; k < mlt_producers.count(); k++) {
+ QDomElement mltprod = mlt_producers.at(k).toElement();
+ if (mltprod.tagName() != "producer") continue;
+ if (mltprod.attribute("id") == id) {
+ if (!folder.isEmpty()) {
+ // We have found our producer, set folder info
+ QDomElement prop = m_doc.createElement("property");
+ prop.setAttribute("name", "kdenlive:folderid");
+ QDomText val = m_doc.createTextNode(folder);
+ prop.appendChild(val);
+ mltprod.appendChild(prop);
+ }
+ if (!prodName.isEmpty()) {
+ // Set clip name
+ QDomElement prop = m_doc.createElement("property");
+ prop.setAttribute("name", "kdenlive:clipname");
+ QDomText val = m_doc.createTextNode(prodName);
+ prop.appendChild(val);
+ mltprod.appendChild(prop);
+ }
+ break;
+ }
+ }
+ }
+
+ // Make sure all slowmotion producers have a master clip
+ for (int i = 0; i < slowmotionIds.count(); i++) {
+ QString slo = slowmotionIds.at(i);
+ if (!ids.contains(slo)) {
+ // rebuild producer from Kdenlive's old xml format
+ for (int j = 0; j < kdenlive_producers.count(); j++) {
+ QDomElement prod = kdenlive_producers.at(j).toElement();
+ QString id = prod.attribute("id");
+ if (id == slo) {
+ // We found the kdenlive_producer, build MLT producer
+ QDomElement original = m_doc.createElement("producer");
+ original.setAttribute("in", 0);
+ original.setAttribute("out", prod.attribute("duration").toInt() - 1);
+ original.setAttribute("id", id);
+ QDomElement prop = m_doc.createElement("property");
+ prop.setAttribute("name", "resource");
+ QDomText val = m_doc.createTextNode(prod.attribute("resource"));
+ prop.appendChild(val);
+ original.appendChild(prop);
+ QDomElement prop2 = m_doc.createElement("property");
+ prop2.setAttribute("name", "mlt_service");
+ QDomText val2 = m_doc.createTextNode("avformat");
+ prop2.appendChild(val2);
+ original.appendChild(prop2);
+ QDomElement prop3 = m_doc.createElement("property");
+ prop3.setAttribute("name", "length");
+ QDomText val3 = m_doc.createTextNode(prod.attribute("duration"));
+ prop3.appendChild(val3);
+ original.appendChild(prop3);
+ QDomElement entry = m_doc.createElement("entry");
+ entry.setAttribute("in", original.attribute("in"));
+ entry.setAttribute("out", original.attribute("out"));
+ entry.setAttribute("producer", id);
+ main_playlist.appendChild(entry);
+ frag.appendChild(original);
+ ids << slo;
+ break;
+ }
+ }
+ }
+ }
+ frag.appendChild(main_playlist);
+ mlt.insertBefore(frag, firstProd);
+ }
// The document has been converted: mark it as modified
infoXml.setAttribute("version", currentVersion);
@@ -1209,3 +1400,130 @@ bool DocumentValidator::updateEffectParameters(const QDomNodeList &parameters, c
return updated;
}
+bool DocumentValidator::checkMovit()
+{
+ QString playlist = m_doc.toString();
+ if (!playlist.contains("movit.")) {
+ // Project does not use Movit GLSL effects, we can load it
+ return true;
+ }
+ if (KMessageBox::questionYesNo(QApplication::activeWindow(), i18n("The project file uses some GPU effects. GPU acceleration is not currently enabled.\nDo you want to convert the project to a non-GPU version ?\nThis might result in data loss.")) != KMessageBox::Yes) {
+ return false;
+ }
+ // Try to convert Movit filters to their non GPU equivalent
+
+ QStringList convertedFilters;
+ QStringList discardedFilters;
+ int ix = MainWindow::videoEffects.hasEffect("frei0r.colgate", "frei0r.colgate");
+ bool hasWB = ix > -1;
+ ix = MainWindow::videoEffects.hasEffect("frei0r.IIRblur", "frei0r.IIRblur");
+ bool hasBlur = ix > -1;
+ ix = MainWindow::transitions.hasTransition("frei0r.cairoblend");
+ bool hasCairo = ix > -1;
+
+ // Parse all effects in document
+ QDomNodeList filters = m_doc.elementsByTagName("filter");
+ int max = filters.count();
+ QLocale locale;
+ for (int i = 0; i < max; ++i) {
+ QDomElement filt = filters.at(i).toElement();
+ QString filterId = filt.attribute("id");
+ if (!filterId.startsWith("movit.")) {
+ continue;
+ }
+ if (filterId == "movit.white_balance" && hasWB) {
+ // Convert to frei0r.colgate
+ filt.setAttribute("id", "frei0r.colgate");
+ EffectsList::setProperty(filt, "kdenlive_id", "frei0r.colgate");
+ EffectsList::setProperty(filt, "tag", "frei0r.colgate");
+ EffectsList::setProperty(filt, "mlt_service", "frei0r.colgate");
+ EffectsList::renameProperty(filt, "neutral_color", "Neutral Color");
+ QString value = EffectsList::property(filt, "color_temperature");
+ value = factorizeGeomValue(value, 15000.0);
+ EffectsList::setProperty(filt, "color_temperature", value);
+ EffectsList::renameProperty(filt, "color_temperature", "Color Temperature");
+ convertedFilters << filterId;
+ continue;
+ }
+ if (filterId == "movit.blur" && hasBlur) {
+ // Convert to frei0r.IIRblur
+ filt.setAttribute("id", "frei0r.IIRblur");
+ EffectsList::setProperty(filt, "kdenlive_id", "frei0r.IIRblur");
+ EffectsList::setProperty(filt, "tag", "frei0r.IIRblur");
+ EffectsList::setProperty(filt, "mlt_service", "frei0r.IIRblur");
+ EffectsList::renameProperty(filt, "radius", "Amount");
+ QString value = EffectsList::property(filt, "Amount");
+ value = factorizeGeomValue(value, 14.0);
+ EffectsList::setProperty(filt, "Amount", value);
+ convertedFilters << filterId;
+ continue;
+ }
+ if (filterId == "movit.mirror") {
+ // Convert to MLT's mirror
+ filt.setAttribute("id", "mirror");
+ EffectsList::setProperty(filt, "kdenlive_id", "mirror");
+ EffectsList::setProperty(filt, "tag", "mirror");
+ EffectsList::setProperty(filt, "mlt_service", "mirror");
+ EffectsList::setProperty(filt, "mirror", "flip");
+ convertedFilters << filterId;
+ continue;
+ }
+ if (filterId.startsWith("movit.")) {
+ //TODO: implement conversion for more filters
+ discardedFilters << filterId;
+ }
+ }
+
+ // Parse all transitions in document
+ QDomNodeList transitions = m_doc.elementsByTagName("transition");
+ max = transitions.count();
+ for (int i = 0; i < max; ++i) {
+ QDomElement t = transitions.at(i).toElement();
+ QString transId = EffectsList::property(t, "mlt_service");
+ if (!transId.startsWith("movit.")) {
+ continue;
+ }
+ if (transId == "movit.overlay" && hasCairo) {
+ // Convert to frei0r.colgate
+ EffectsList::setProperty(t, "mlt_service", "frei0r.cairoblend");
+ convertedFilters << transId;
+ continue;
+ }
+ if (transId.startsWith("movit.")) {
+ //TODO: implement conversion for more filters
+ discardedFilters << transId;
+ }
+ }
+
+ convertedFilters.removeDuplicates();
+ discardedFilters.removeDuplicates();
+ if (discardedFilters.isEmpty()) {
+ KMessageBox::informationList(QApplication::activeWindow(), i18n("The following filters/transitions were converted to non GPU versions:"), convertedFilters);
+ }
+ else {
+ KMessageBox::informationList(QApplication::activeWindow(), i18n("The following filters/transitions were deleted from the project:"), discardedFilters);
+ }
+ m_modified = true;
+ QString scene = m_doc.toString();
+ scene.replace("movit.", "");
+ m_doc.setContent(scene);
+ return true;
+}
+
+QString DocumentValidator::factorizeGeomValue(QString value, double factor)
+{
+ QStringList vals = value.split(";");
+ QString result;
+ QLocale locale;
+ for (int i = 0; i < vals.count(); i++) {
+ QString s = vals.at(i);
+ QString key = s.section("=", 0, 0);
+ QString val = s.section("=", 1, 1);
+ double v = locale.toDouble(val) / factor;
+ result.append(key + "=" + locale.toString(v));
+ if ( i + 1 < vals.count()) result.append(";");
+ }
+ return result;
+}
+
+
diff --git a/src/doc/documentvalidator.h b/src/doc/documentvalidator.h
index 4d008bd..1def80f 100644
--- a/src/doc/documentvalidator.h
+++ b/src/doc/documentvalidator.h
@@ -36,6 +36,8 @@ public:
bool isProject() const;
bool validate(const double currentVersion);
bool isModified() const;
+ /** @brief Check if the project contains references to Movit stuff (GLSL), and try to convert if wanted. */
+ bool checkMovit();
private:
QDomDocument m_doc;
@@ -49,6 +51,7 @@ private:
/** @brief Updates the parameters according to the updateRules.
* @see the related in README in effects/update */
bool updateEffectParameters(const QDomNodeList &parameters, const QScriptValue *updateRules, const double serviceVersion, const double effectVersion);
+ QString factorizeGeomValue(QString value, double factor);
};
#endif
diff --git a/src/doc/kdenlivedoc.cpp b/src/doc/kdenlivedoc.cpp
index 29ef90c..2f7ece6 100644
--- a/src/doc/kdenlivedoc.cpp
+++ b/src/doc/kdenlivedoc.cpp
@@ -19,21 +19,26 @@
#include "kdenlivedoc.h"
-#include "docclipbase.h"
#include "documentchecker.h"
#include "documentvalidator.h"
-
+#include "mltcontroller/clipcontroller.h"
#include <config-kdenlive.h>
#include "kdenlivesettings.h"
#include "renderer.h"
#include "mainwindow.h"
#include "project/clipmanager.h"
+#include "project/projectcommands.h"
#include "project/projectlist.h"
#include "effectslist/initeffects.h"
#include "dialogs/profilesdialog.h"
#include "titler/titlewidget.h"
#include "project/notesplugin.h"
#include "project/dialogs/noteswidget.h"
+#include "core.h"
+#include "bin/bin.h"
+#include "bin/projectclip.h"
+#include "mltcontroller/bincontroller.h"
+#include "mltcontroller/effectscontroller.h"
#include <KMessageBox>
#include <KRecentDirs>
@@ -64,8 +69,7 @@
#include <xlocale.h>
#endif
-
-const double DOCUMENTVERSION = 0.88;
+const double DOCUMENTVERSION = 0.90;
KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup *undoGroup, const QString &profileName, const QMap <QString, QString>& properties, const QMap <QString, QString>& metadata, const QPoint &tracks, Render *render, NotesPlugin *notes, bool *openBackup, MainWindow *parent, QProgressDialog *progressDialog) :
QObject(parent),
@@ -90,11 +94,19 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
m_profile.colorspace = 0;
m_clipManager = new ClipManager(this);
- m_autoSaveTimer = new QTimer(this);
- m_autoSaveTimer->setSingleShot(true);
connect(m_clipManager, SIGNAL(displayMessage(QString,int)), parent, SLOT(slotGotProgressInfo(QString,int)));
bool success = false;
+
+ connect(m_commandStack, SIGNAL(indexChanged(int)), this, SLOT(slotModified()));
+ //connect(m_commandStack, SIGNAL(cleanChanged(bool)), this, SLOT(setModified(bool)));
+
+ // Init clip modification tracker
+ m_modifiedTimer.setInterval(1500);
+ connect(&m_fileWatcher, &KDirWatch::dirty, this, &KdenliveDoc::slotClipModified);
+ connect(&m_fileWatcher, &KDirWatch::deleted, this, &KdenliveDoc::slotClipMissing);
+ connect(&m_modifiedTimer, &QTimer::timeout, this, &KdenliveDoc::slotProcessModifiedClips);
+
// init default document properties
m_documentProperties["zoom"] = '7';
m_documentProperties["verticalzoom"] = '1';
@@ -129,13 +141,13 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
systemLocale.setNumberOptions(QLocale::OmitGroupSeparator);
QLocale::setDefault(systemLocale);
// locale conversion might need to be redone
- initEffects::parseEffectFiles(setlocale(LC_NUMERIC, NULL));
+ initEffects::parseEffectFiles(pCore->binController()->mltRepository(), setlocale(LC_NUMERIC, NULL));
}
*openBackup = false;
if (url.isValid()) {
- QFile file(url.path());
+ QFile file(url.toLocalFile());
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
// The file cannot be opened
if (KMessageBox::warningContinueCancel(parent, i18n("Cannot open the project file,\nDo you want to open a backup file?"), i18n("Error opening file"), KGuiItem(i18n("Open Backup"))) == KMessageBox::Continue) {
@@ -144,6 +156,7 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
//KMessageBox::error(parent, KIO::NetAccess::lastErrorString());
}
else {
+ qDebug()<<" // / processing file open";
QString errorMsg;
int line;
int col;
@@ -191,6 +204,7 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
}
}
if (success) {
+ qDebug()<<" // / processing file open: validate";
parent->slotGotProgressInfo(i18n("Validating"), 0);
qApp->processEvents();
DocumentValidator validator(m_document, url);
@@ -208,7 +222,11 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
*/
// TODO: backup the document or alert the user?
success = validator.validate(DOCUMENTVERSION);
+ if (success && !KdenliveSettings::gpu_accel()) {
+ success = validator.checkMovit();
+ }
if (success) { // Let the validator handle error messages
+ qDebug()<<" // / processing file validate ok";
parent->slotGotProgressInfo(i18n("Check missing clips"), 0);
qApp->processEvents();
QDomNodeList infoproducers = m_document.elementsByTagName("kdenlive_producer");
@@ -263,7 +281,8 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
for (int i = 0; i < folders.count(); ++i) {
e = folders.item(i).cloneNode().toElement();
if (e.hasAttribute("opened")) expandedFolders.append(e.attribute("id"));
- m_clipManager->addFolder(e.attribute("id"), e.attribute("name"));
+ //Deprecated
+ //m_clipManager->addFolder(e.attribute("id"), e.attribute("name"));
}
m_documentProperties["expandedfolders"] = expandedFolders.join(";");
@@ -283,7 +302,7 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
progressDialog->show();
qApp->processEvents();
- for (int i = 0; i < infomax; ++i) {
+ /*for (int i = 0; i < infomax; ++i) {
e = infoproducers.item(i).cloneNode().toElement();
QString prodId = e.attribute("id");
if (!e.isNull() && prodId != "black" && !prodId.startsWith(QLatin1String("slowmotion"))) {
@@ -301,6 +320,7 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
if (!addClipInfo(e, orig, prodId)) {
// The user manually aborted the loading.
+ qDebug()<<" *********** ADDCLIPINFO ERROR *************";
success = false;
emit resetProjectList();
m_tracksList.clear();
@@ -310,7 +330,7 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
}
if (i % 10 == 0)
progressDialog->setValue(i);
- }
+ }*/
if (success) {
QDomElement markers = infoXml.firstChildElement("markers");
@@ -321,8 +341,8 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
e = markerslist.at(k).toElement();
if (e.tagName() == "marker") {
CommentedTime marker(GenTime(e.attribute("time").toDouble()), e.attribute("comment"), e.attribute("type").toInt());
- DocClipBase *baseClip = m_clipManager->getClipById(e.attribute("id"));
- if (baseClip) baseClip->addSnapMarker(marker);
+ ClipController *controller = getClipController(e.attribute("id"));
+ if (controller) controller->addSnapMarker(marker);
else qDebug()<< " / / Warning, missing clip: "<< e.attribute("id");
}
}
@@ -384,18 +404,15 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, const QUrl &projectFolder, QUndoGroup
updateProjectFolderPlacesEntry();
////qDebug() << "// SETTING SCENE LIST:\n\n" << m_document.toString();
- connect(m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
- connect(m_render, SIGNAL(addClip(QUrl,stringMap)), this, SLOT(slotAddClipFile(QUrl,stringMap)));
+ connect(m_render, SIGNAL(addClip(const QString&,const QMap<QString,QString>&)), pCore->bin(), SLOT(slotAddUrl(const QString&,const QMap<QString,QString>&)));
}
KdenliveDoc::~KdenliveDoc()
{
- m_autoSaveTimer->stop();
delete m_commandStack;
//qDebug() << "// DEL CLP MAN";
delete m_clipManager;
//qDebug() << "// DEL CLP MAN done";
- delete m_autoSaveTimer;
if (m_autosave) {
if (!m_autosave->fileName().isEmpty()) m_autosave->remove();
delete m_autosave;
@@ -409,9 +426,8 @@ int KdenliveDoc::setSceneList()
// INVALID MLT Consumer, something is wrong
return -1;
}
+ pCore->binController()->checkThumbnails(projectFolder().path() + "/thumbs/");
m_documentProperties.remove("position");
- // m_document xml is now useless, clear it
- m_document.clear();
return 0;
}
@@ -517,11 +533,12 @@ QDomDocument KdenliveDoc::createEmptyDocument(const QList <TrackInfo> &tracks)
playlist.appendChild(blank0);
// create playlists
- int total = tracks.count() + 1;
+ int total = tracks.count();
- for (int i = 1; i < total; ++i) {
+ for (int i = 0; i < total; ++i) {
QDomElement playlist = doc.createElement("playlist");
playlist.setAttribute("id", "playlist" + QString::number(i));
+ playlist.setAttribute("kdenlive:track_name", tracks.at(i).trackName);
mlt.appendChild(playlist);
}
@@ -530,19 +547,19 @@ QDomDocument KdenliveDoc::createEmptyDocument(const QList <TrackInfo> &tracks)
tractor.appendChild(track0);
// create audio and video tracks
- for (int i = 1; i < total; ++i) {
+ for (int i = 0; i < total; ++i) {
QDomElement track = doc.createElement("track");
track.setAttribute("producer", "playlist" + QString::number(i));
- if (tracks.at(i - 1).type == AudioTrack) {
+ if (tracks.at(i).type == AudioTrack) {
track.setAttribute("hide", "video");
- } else if (tracks.at(i - 1).isBlind)
+ } else if (tracks.at(i).isBlind)
track.setAttribute("hide", "video");
- if (tracks.at(i - 1).isMute)
+ if (tracks.at(i).isMute)
track.setAttribute("hide", "audio");
tractor.appendChild(track);
}
- for (int i = 2; i < total ; ++i) {
+ for (int i = 2; i < total + 1 ; ++i) {
QDomElement transition = doc.createElement("transition");
transition.setAttribute("always_active", "1");
@@ -581,37 +598,21 @@ QDomDocument KdenliveDoc::createEmptyDocument(const QList <TrackInfo> &tracks)
return doc;
}
-
-void KdenliveDoc::syncGuides(const QList <Guide *> &guides)
+bool KdenliveDoc::useProxy() const
{
- m_guidesXml.clear();
- QDomElement guideNode = m_guidesXml.createElement("guides");
- m_guidesXml.appendChild(guideNode);
- QDomElement e;
-
- for (int i = 0; i < guides.count(); ++i) {
- e = m_guidesXml.createElement("guide");
- e.setAttribute("time", guides.at(i)->position().ms() / 1000);
- e.setAttribute("comment", guides.at(i)->label());
- guideNode.appendChild(e);
- }
- setModified(true);
- emit guidesUpdated();
+ return m_documentProperties.value("enableproxy").toInt();
}
-QDomElement KdenliveDoc::guidesXml() const
-{
- return m_guidesXml.documentElement();
-}
-void KdenliveDoc::slotAutoSave()
+void KdenliveDoc::slotAutoSave(QMap <double, QString> guidesData)
{
if (m_render && m_autosave) {
if (!m_autosave->isOpen() && !m_autosave->open(QIODevice::ReadWrite)) {
// show error: could not open the autosave file
//qDebug() << "ERROR; CANNOT CREATE AUTOSAVE FILE";
}
- QDomDocument sceneList = xmlSceneList(m_render->sceneList(), QStringList());
+ //qDebug() << "// AUTOSAVE FILE: " << m_autosave->fileName();
+ QDomDocument sceneList = xmlSceneList(m_render->sceneList(), guidesData);
if (sceneList.isNull()) {
//Make sure we don't save if scenelist is corrupted
KMessageBox::error(QApplication::activeWindow(), i18n("Cannot write to file %1, scene list is corrupted.", m_autosave->fileName()));
@@ -645,7 +646,7 @@ QPoint KdenliveDoc::zone() const
return QPoint(m_documentProperties.value("zonein").toInt(), m_documentProperties.value("zoneout").toInt());
}
-QDomDocument KdenliveDoc::xmlSceneList(const QString &scene, const QStringList &expandedFolders)
+QDomDocument KdenliveDoc::xmlSceneList(const QString &scene, QMap <double, QString> guidesData)
{
QDomDocument sceneList;
sceneList.setContent(scene, true);
@@ -666,6 +667,31 @@ QDomDocument KdenliveDoc::xmlSceneList(const QString &scene, const QStringList &
}
}
}
+ QDomNodeList pls = mlt.elementsByTagName("playlist");
+ QDomElement mainPlaylist;
+ for (int i = 0; i < pls.count(); ++i) {
+ if (pls.at(i).toElement().attribute("id") == pCore->binController()->binPlaylistId()) {
+ mainPlaylist = pls.at(i).toElement();
+ break;
+ }
+ }
+
+ // Append markers
+
+
+ // Append guides
+ QMapIterator<double, QString> g(guidesData);
+ QLocale locale;
+ while (g.hasNext()) {
+ g.next();
+ QString propertyName = "kdenlive:guide." + locale.toString(g.key());
+ QDomElement prop = sceneList.createElement("property");
+ prop.setAttribute("name", propertyName);
+ QDomText val = sceneList.createTextNode(g.value());
+ prop.appendChild(val);
+ mainPlaylist.appendChild(prop);
+ }
+
QDomElement addedXml = sceneList.createElement("kdenlivedoc");
mlt.appendChild(addedXml);
@@ -758,32 +784,29 @@ QDomDocument KdenliveDoc::xmlSceneList(const QString &scene, const QStringList &
QDomElement folder = sceneList.createElement("folder");
folder.setAttribute("id", f.key());
folder.setAttribute("name", f.value());
- if (expandedFolders.contains(f.key())) folder.setAttribute("opened", "1");
addedXml.appendChild(folder);
}
// Save project clips
+
QDomElement e;
- QList <DocClipBase*> list = m_clipManager->documentClipList();
+ QList <ClipController*> list = pCore->binController()->getControllerList();
for (int i = 0; i < list.count(); ++i) {
- e = list.at(i)->toXML(true);
+ /*e = list.at(i)->toXML(true);
e.setTagName("kdenlive_producer");
- addedXml.appendChild(sceneList.importNode(e, true));
+ addedXml.appendChild(sceneList.importNode(e, true));*/
QList < CommentedTime > marks = list.at(i)->commentedSnapMarkers();
for (int j = 0; j < marks.count(); ++j) {
QDomElement marker = sceneList.createElement("marker");
marker.setAttribute("time", marks.at(j).time().ms() / 1000);
marker.setAttribute("comment", marks.at(j).comment());
- marker.setAttribute("id", e.attribute("id"));
+ marker.setAttribute("id", list.at(i)->clipId());
marker.setAttribute("type", marks.at(j).markerType());
markers.appendChild(marker);
}
}
addedXml.appendChild(markers);
- // Add guides
- if (!m_guidesXml.isNull()) addedXml.appendChild(sceneList.importNode(m_guidesXml.documentElement(), true));
-
// Add clip groups
addedXml.appendChild(sceneList.importNode(m_clipManager->groupsXml(), true));
@@ -792,9 +815,9 @@ QDomDocument KdenliveDoc::xmlSceneList(const QString &scene, const QStringList &
}
-bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const QStringList &expandedFolders)
+bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, QMap <double, QString> guidesData)
{
- QDomDocument sceneList = xmlSceneList(scene, expandedFolders);
+ QDomDocument sceneList = xmlSceneList(scene, guidesData);
if (sceneList.isNull()) {
//Make sure we don't save if scenelist is corrupted
KMessageBox::error(QApplication::activeWindow(), i18n("Cannot write to file %1, scene list is corrupted.", path));
@@ -860,13 +883,13 @@ void KdenliveDoc::setProjectFolder(QUrl url)
void KdenliveDoc::moveProjectData(const QUrl &url)
{
- QList <DocClipBase*> list = m_clipManager->documentClipList();
+ QList <ClipController*> list = pCore->binController()->getControllerList();
QList<QUrl> cacheUrls;
for (int i = 0; i < list.count(); ++i) {
- DocClipBase *clip = list.at(i);
+ ClipController *clip = list.at(i);
if (clip->clipType() == Text) {
// the image for title clip must be moved
- QUrl oldUrl = clip->fileURL();
+ QUrl oldUrl = clip->clipUrl();
QUrl newUrl = QUrl::fromLocalFile(url.toLocalFile() + QDir::separator() + "titles/" + oldUrl.fileName());
KIO::Job *job = KIO::copy(oldUrl, newUrl);
if (job->exec()) clip->setProperty("resource", newUrl.path());
@@ -902,6 +925,19 @@ MltVideoProfile KdenliveDoc::mltProfile() const
return m_profile;
}
+ProfileInfo KdenliveDoc::getProfileInfo() const
+{
+ ProfileInfo info;
+ info.profileSize = getRenderSize();
+ info.profileFps = fps();
+ return info;
+}
+
+/*Mlt::Profile *KdenliveDoc::profile()
+{
+ return pCore->binController()->profile();
+}*/
+
bool KdenliveDoc::setProfilePath(QString path)
{
if (path.isEmpty())
@@ -980,24 +1016,10 @@ QUndoStack *KdenliveDoc::commandStack()
return m_commandStack;
}
-/*
-void KdenliveDoc::setRenderer(Render *render) {
- if (m_render) return;
- m_render = render;
- emit progressInfo(i18n("Loading playlist..."), 0);
- //qApp->processEvents();
- if (m_render) {
- m_render->setSceneList(m_document.toString(), m_startPos);
- //qDebug() << "// SETTING SCENE LIST:\n\n" << m_document.toString();
- checkProjectClips();
- }
- emit progressInfo(QString(), -1);
-}*/
-
void KdenliveDoc::checkProjectClips(bool displayRatioChanged, bool fpsChanged)
{
if (m_render == NULL) return;
- m_clipManager->resetProducersList(m_render->producersList(), displayRatioChanged, fpsChanged);
+ //m_clipManager->resetProducersList(m_render->producersList(), displayRatioChanged, fpsChanged);
}
Render *KdenliveDoc::renderer()
@@ -1063,11 +1085,16 @@ void KdenliveDoc::setUrl(const QUrl &url)
m_url = url;
}
+void KdenliveDoc::slotModified()
+{
+ setModified(m_commandStack->isClean() == false);
+}
+
void KdenliveDoc::setModified(bool mod)
{
// fix mantis#3160: The document may have an empty URL if not saved yet, but should have a m_autosave in any case
if (m_autosave && mod && KdenliveSettings::crashrecovery()) {
- m_autoSaveTimer->start(3000); // will trigger slotAutoSave() in 3 seconds
+ emit startAutoSave();
}
if (mod == m_modified) return;
m_modified = mod;
@@ -1090,7 +1117,16 @@ const QString KdenliveDoc::description() const
bool KdenliveDoc::addClip(QDomElement elem, const QString &clipId, bool createClipItem)
{
const QString producerId = clipId.section('_', 0, 0);
- DocClipBase *clip = m_clipManager->getClipById(producerId);
+ elem.setAttribute("id", producerId);
+ pCore->bin()->createClip(elem);
+ m_render->getFileProperties(elem, producerId, 150, true);
+
+ /*QString str;
+ QTextStream stream(&str);
+ elem.save(stream, 4);
+ qDebug()<<"ADDING CLIP COMMAND\n-----------\n"<<str;*/
+ return true;
+ /*DocClipBase *clip = m_clipManager->getClipById(producerId);
if (clip == NULL) {
QString clipFolder = KRecentDirs::dir(":KdenliveClipFolder");
@@ -1162,7 +1198,7 @@ bool KdenliveDoc::addClip(QDomElement elem, const QString &clipId, bool createCl
if (createClipItem) {
emit addProjectClip(clip);
}
-
+ */
return true;
}
@@ -1227,7 +1263,7 @@ QString KdenliveDoc::searchFileRecursively(const QDir &dir, const QString &match
bool KdenliveDoc::addClipInfo(QDomElement elem, QDomElement orig, const QString &clipId)
{
- DocClipBase *clip = m_clipManager->getClipById(clipId);
+ /*DocClipBase *clip = m_clipManager->getClipById(clipId);
if (clip == NULL) {
if (!addClip(elem, clipId, false))
return false;
@@ -1259,73 +1295,46 @@ bool KdenliveDoc::addClipInfo(QDomElement elem, QDomElement orig, const QString
clip->setMetadata(meta);
}
}
- return true;
+ return true;*/
}
void KdenliveDoc::deleteClip(const QString &clipId)
{
- emit signalDeleteProjectClip(clipId);
+ ClipController *controller = pCore->binController()->getController(clipId);
+ ClipType type = controller->clipType();
+ QString url = controller->clipUrl().toLocalFile();
+ if (type != Color && type != SlideShow && !url.isEmpty()) {
+ m_fileWatcher.removeFile(url);
+ }
+ // Delete clip in bin
+ pCore->bin()->deleteClip(clipId);
+ // Delete controller and Mlt::Producer
+ pCore->binController()->removeBinClip(clipId);
}
-void KdenliveDoc::slotAddClipList(const QList<QUrl> &urls, const stringMap &data)
+ProjectClip *KdenliveDoc::getBinClip(const QString &clipId)
{
- m_clipManager->slotAddClipList(urls, data);
- //emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
- setModified(true);
+ return pCore->bin()->getBinClip(clipId);
}
-
-void KdenliveDoc::slotAddClipFile(const QUrl &url, const stringMap &data)
+QStringList KdenliveDoc::getBinFolderClipIds(const QString &folderId) const
{
- m_clipManager->slotAddClipFile(url, data);
- emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
- setModified(true);
+ return pCore->bin()->getBinFolderClipIds(folderId);
}
-const QString KdenliveDoc::getFreeClipId()
+ClipController *KdenliveDoc::getClipController(const QString &clipId)
{
- return QString::number(m_clipManager->getFreeClipId());
+ return pCore->binController()->getController(clipId);
}
-DocClipBase *KdenliveDoc::getBaseClip(const QString &clipId)
-{
- return m_clipManager->getClipById(clipId);
-}
void KdenliveDoc::slotCreateXmlClip(const QString &name, const QDomElement &xml, const QString &group, const QString &groupId)
{
m_clipManager->slotAddXmlClipFile(name, xml, group, groupId);
- setModified(true);
- emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
-}
-
-void KdenliveDoc::slotCreateColorClip(const QString &name, const QString &color, const QString &duration, const QString &group, const QString &groupId)
-{
- m_clipManager->slotAddColorClipFile(name, color, duration, group, groupId);
- setModified(true);
- emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
-}
-
-void KdenliveDoc::slotCreateSlideshowClipFile(const QMap <QString, QString> &properties, const QString &group, const QString &groupId)
-{
- m_clipManager->slotAddSlideshowClipFile(properties, group, groupId);
- setModified(true);
emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
}
-void KdenliveDoc::slotCreateTextClip(QString group, const QString &groupId, const QString &templatePath)
-{
- QString titlesFolder = QDir::cleanPath(projectFolder().path() + QDir::separator() + "titles/");
- QPointer<TitleWidget> dia_ui = new TitleWidget(QUrl::fromLocalFile(templatePath), m_timecode, titlesFolder, m_render, QApplication::activeWindow());
- if (dia_ui->exec() == QDialog::Accepted) {
- m_clipManager->slotAddTextClipFile(i18n("Title clip"), dia_ui->duration(), dia_ui->xml().toString(), group, groupId);
- setModified(true);
- emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
- }
- delete dia_ui;
-}
-
void KdenliveDoc::slotCreateTextTemplateClip(const QString &group, const QString &groupId, QUrl path)
{
QString titlesFolder = QDir::cleanPath(projectFolder().path() + QDir::separator() + "titles/");
@@ -1343,7 +1352,6 @@ void KdenliveDoc::slotCreateTextTemplateClip(const QString &group, const QString
//TODO: rewrite with new title system (just set resource)
m_clipManager->slotAddTextTemplateClip(i18n("Template title clip"), path, group, groupId);
- setModified(true);
emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
}
@@ -1529,7 +1537,7 @@ void KdenliveDoc::addTrackEffect(int ix, QDomElement effect)
// Check if this effect has a variable parameter
if (e.attribute("default").contains('%')) {
- double evaluatedValue = ProfilesDialog::getStringEval(m_profile, e.attribute("default"));
+ double evaluatedValue = EffectsController::getStringEval(getProfileInfo(), e.attribute("default"));
e.setAttribute("default", evaluatedValue);
if (e.hasAttribute("value") && e.attribute("value").startsWith('%')) {
e.setAttribute("value", evaluatedValue);
@@ -1672,7 +1680,7 @@ void KdenliveDoc::updateProjectFolderPlacesEntry()
*/
const QString file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/user-places.xbel";
- KBookmarkManager *bookmarkManager = KBookmarkManager::managerForFile(file, "kfilePlaces");
+ KBookmarkManager *bookmarkManager = KBookmarkManager::managerForExternalFile(file);
if (!bookmarkManager) return;
KBookmarkGroup root = bookmarkManager->root();
@@ -1717,6 +1725,15 @@ QStringList KdenliveDoc::getExpandedFolders()
return result;
}
+const QSize KdenliveDoc::getRenderSize() const
+{
+ QSize size;
+ if (m_render) {
+ size.setWidth(m_render->frameRenderWidth());
+ size.setHeight(m_render->renderHeight());
+ }
+ return size;
+}
// static
double KdenliveDoc::getDisplayRatio(const QString &path)
{
@@ -1866,5 +1883,122 @@ void KdenliveDoc::setMetadata(const QMap<QString, QString> &meta)
m_documentMetadata = meta;
}
+void KdenliveDoc::slotProxyCurrentItem(bool doProxy)
+{
+ QList<ProjectClip *> clipList = pCore->bin()->selectedClips();
+ QUndoCommand *command = new QUndoCommand();
+ if (doProxy) command->setText(i18np("Add proxy clip", "Add proxy clips", clipList.count()));
+ else command->setText(i18np("Remove proxy clip", "Remove proxy clips", clipList.count()));
+
+ // Make sure the proxy folder exists
+ QString proxydir = projectFolder().path() + QDir::separator() + "proxy/";
+ QDir dir(projectFolder().path());
+ dir.mkdir("proxy");
+
+ // Prepare updated properties
+ QMap <QString, QString> newProps;
+ QMap <QString, QString> oldProps;
+ if (!doProxy) newProps.insert("kdenlive:proxy", "-");
+
+ // Parse clips
+ for (int i = 0; i < clipList.count(); ++i) {
+ ProjectClip *item = clipList.at(i);
+ ClipType t = item->clipType();
+
+ // Only allow proxy on some clip types
+ if ((t == Video || t == AV || t == Unknown || t == Image || t == Playlist) && item->isReady()) {
+ if ((doProxy && item->hasProxy()) || (!doProxy && !item->hasProxy() && pCore->binController()->hasClip(item->clipId()))) continue;
+ if (m_render->isProcessing(item->clipId())) {
+ continue;
+ }
+
+ if (doProxy) {
+ newProps.clear();
+ QString path = proxydir + item->hash() + '.' + (t == Image ? "png" : getDocumentProperty("proxyextension"));
+ // insert required duration for proxy
+ newProps.insert("proxy_out", item->getProducerProperty("out"));
+ newProps.insert("kdenlive:proxy", path);
+ }
+ else if (!pCore->binController()->hasClip(item->clipId())) {
+ // Force clip reload
+ newProps.insert("resource", item->url().toLocalFile());
+ }
+ // We need to insert empty proxy so that undo will work
+ //TODO: how to handle clip properties
+ //oldProps = clip->currentProperties(newProps);
+ if (doProxy) oldProps.insert("kdenlive:proxy", "-");
+ new EditClipCommand(this, item->clipId(), oldProps, newProps, true, command);
+ }
+ }
+ if (command->childCount() > 0) {
+ m_commandStack->push(command);
+ }
+ else delete command;
+}
+void KdenliveDoc::slotUpdateClipProperties(const QString &id, QMap <QString, QString> properties, bool refreshPropertiesPanel)
+{
+ ProjectClip *item = pCore->bin()->getBinClip(id);
+ if (item) {
+ item->setProperties(properties, refreshPropertiesPanel);
+ }
+}
+
+//TODO put all file watching stuff in own class
+
+void KdenliveDoc::watchFile(const QUrl &url)
+{
+ m_fileWatcher.addFile(url.toLocalFile());
+}
+
+void KdenliveDoc::slotClipModified(const QString &path)
+{
+ qDebug() << "// CLIP: " << path << " WAS MODIFIED";
+ QStringList ids = pCore->binController()->getBinIdsByResource(QUrl::fromLocalFile(path));
+ foreach (const QString &id, ids) {
+ if (!m_modifiedClips.contains(id)) {
+ pCore->bin()->setWaitingStatus(id);
+ }
+ m_modifiedClips[id] = QTime::currentTime();
+ }
+ if (!m_modifiedTimer.isActive()) m_modifiedTimer.start();
+}
+
+
+void KdenliveDoc::slotClipMissing(const QString &path)
+{
+ qDebug() << "// CLIP: " << path << " WAS MISSING";
+ QStringList ids = pCore->binController()->getBinIdsByResource(QUrl::fromLocalFile(path));
+ foreach (const QString &id, ids) {
+ //TODO handle missing clips by replacing producer with an invalid producer
+ //emit missingClip(id);
+ }
+}
+
+void KdenliveDoc::slotClipAvailable(const QString &path)
+{
+ qDebug() << "// CLIP: " << path << " WAS ADDED";
+ QStringList ids = pCore->binController()->getBinIdsByResource(QUrl::fromLocalFile(path));
+
+ foreach (const QString &id, ids) {
+ pCore->bin()->reloadClip(id);
+ //emit availableClip(id);
+ }
+}
+
+void KdenliveDoc::slotProcessModifiedClips()
+{
+ if (!m_modifiedClips.isEmpty()) {
+ QMapIterator<QString, QTime> i(m_modifiedClips);
+ while (i.hasNext()) {
+ i.next();
+ if (QTime::currentTime().msecsTo(i.value()) <= -1500) {
+ pCore->bin()->reloadClip(i.key());
+ m_modifiedClips.remove(i.key());
+ break;
+ }
+ }
+ }
+ if (m_modifiedClips.isEmpty()) m_modifiedTimer.stop();
+}
diff --git a/src/doc/kdenlivedoc.h b/src/doc/kdenlivedoc.h
index aa085ab..4fb032a 100644
--- a/src/doc/kdenlivedoc.h
+++ b/src/doc/kdenlivedoc.h
@@ -30,21 +30,25 @@
#include <QList>
#include <QDir>
#include <QObject>
-
+#include <QTimer>
#include <QUrl>
+
#include <kautosavefile.h>
+#include <KDirWatch>
#include "gentime.h"
#include "timecode.h"
#include "definitions.h"
#include "timeline/guide.h"
+#include "mltcontroller/effectscontroller.h"
class Render;
class ClipManager;
-class DocClipBase;
class MainWindow;
class TrackInfo;
class NotesPlugin;
+class ProjectClip;
+class ClipController;
class QTextEdit;
class QProgressDialog;
@@ -52,6 +56,10 @@ class QUndoGroup;
class QTimer;
class QUndoStack;
+namespace Mlt {
+ class Profile;
+}
+
class KdenliveDoc: public QObject
{
Q_OBJECT
@@ -67,11 +75,8 @@ public:
KAutoSaveFile *m_autosave;
Timecode timecode() const;
QDomDocument toXml();
- //void setRenderer(Render *render);
QUndoStack *commandStack();
Render *renderer();
- QDomDocument m_guidesXml;
- QDomElement guidesXml() const;
ClipManager *clipManager();
/** @brief Adds a clip to the project tree.
@@ -87,38 +92,41 @@ public:
*
* If the clip wasn't added before, it tries to add it to the project. */
bool addClipInfo(QDomElement elem, QDomElement orig, const QString &clipId);
- void slotAddClipList(const QList<QUrl> &urls, const stringMap &data = stringMap());
void deleteClip(const QString &clipId);
int getFramePos(const QString &duration);
- DocClipBase *getBaseClip(const QString &clipId);
+ /** @brief Get a bin's clip from its id. */
+ ProjectClip *getBinClip(const QString &clipId);
+ /** @brief Get a list of all clip ids that are inside a folder. */
+ QStringList getBinFolderClipIds(const QString &folderId) const;
+ ClipController *getClipController(const QString &clipId);
void updateClip(const QString &id);
/** @brief Informs Kdenlive of the audio thumbnails generation progress. */
void setThumbsProgress(const QString &message, int progress);
const QString &profilePath() const;
MltVideoProfile mltProfile() const;
+ ProfileInfo getProfileInfo() const;
+ //Mlt::Profile *profile();
const QString description() const;
void setUrl(const QUrl &url);
/** @brief Updates the project profile.
* @return true if frame rate was changed */
bool setProfilePath(QString path);
- const QString getFreeClipId();
/** @brief Defines whether the document needs to be saved. */
bool isModified() const;
/** @brief Returns the project folder, used to store project files. */
QUrl projectFolder() const;
- void syncGuides(const QList <Guide *> &guides);
void setZoom(int horizontal, int vertical);
QPoint zoom() const;
double dar() const;
double projectDuration() const;
/** @brief Returns the project file xml. */
- QDomDocument xmlSceneList(const QString &scene, const QStringList &expandedFolders);
+ QDomDocument xmlSceneList(const QString &scene, QMap <double, QString> guidesData);
/** @brief Saves the project file xml to a file. */
- bool saveSceneList(const QString &path, const QString &scene, const QStringList &expandedFolders);
+ bool saveSceneList(const QString &path, const QString &scene, QMap <double, QString> guidesData);
int tracksCount() const;
TrackInfo trackInfoAt(int ix) const;
void insertTrack(int ix, const TrackInfo &type);
@@ -172,10 +180,22 @@ public:
const QMap <QString, QString> metadata() const;
/** @brief Set the document metadata (author, copyright, ...) */
void setMetadata(const QMap <QString, QString>& meta);
+ void slotUpdateClipProperties(const QString &id, QMap <QString, QString> properties, bool refreshPropertiesPanel);
+ /** @brief Get frame size of the renderer (profile)*/
+ const QSize getRenderSize() const;
+ /** @brief Add url to the file watcher so that we monitor changes */
+ void watchFile(const QUrl &url);
+
+ bool useProxy() const;
private:
QUrl m_url;
QDomDocument m_document;
+ KDirWatch m_fileWatcher;
+ /** Timer used to reload clips when they have been externally modified */
+ QTimer m_modifiedTimer;
+ /** List of the clip IDs that need to be reloaded after being externally modified */
+ QMap <QString, QTime> m_modifiedClips;
double m_fps;
int m_width;
int m_height;
@@ -185,7 +205,6 @@ private:
QUndoStack *m_commandStack;
ClipManager *m_clipManager;
MltVideoProfile m_profile;
- QTimer *m_autoSaveTimer;
QString m_searchFolder;
/** @brief Tells whether the current document has been changed after being saved. */
@@ -222,16 +241,6 @@ private:
public slots:
void slotCreateXmlClip(const QString &name, const QDomElement &xml, const QString &group, const QString &groupId);
- void slotCreateColorClip(const QString &name, const QString &color, const QString &duration, const QString &group, const QString &groupId);
- void slotCreateSlideshowClipFile(const QMap<QString, QString> &properties, const QString &group, const QString &groupId);
- /**
- * @brief Create a title clip.
- * Instansiates TitleWidget objects
- * @param group
- * @param groupId
- * @param templatePath
- */
- void slotCreateTextClip(QString group, const QString &groupId, const QString &templatePath = QString());
void slotCreateTextTemplateClip(const QString &group, const QString &groupId, QUrl path);
/** @brief Sets the document as modified or up to date.
@@ -240,17 +249,20 @@ public slots:
* @param mod (optional) true if the document has to be saved */
void setModified(bool mod = true);
void checkProjectClips(bool displayRatioChanged = false, bool fpsChanged = false);
- void slotAddClipFile(const QUrl &url, const stringMap &data);
-
-private slots:
+ void slotProxyCurrentItem(bool doProxy);
/** @brief Saves the current project at the autosave location.
* @description The autosave files are in ~/.kde/data/stalefiles/kdenlive/ */
- void slotAutoSave();
+ void slotAutoSave(QMap <double, QString> guidesData);
+
+private slots:
+ void slotClipModified(const QString &path);
+ void slotClipMissing(const QString &path);
+ void slotClipAvailable(const QString &path);
+ void slotProcessModifiedClips();
+ void slotModified();
signals:
void resetProjectList();
- void addProjectClip(DocClipBase *, bool getInfo = true);
- void signalDeleteProjectClip(const QString &);
void updateClipDisplay(const QString&);
void progressInfo(const QString &, int);
@@ -262,6 +274,8 @@ signals:
void guidesUpdated();
/** @brief When creating a backup file, also save a thumbnail of current timeline */
void saveTimelinePreview(const QString &path);
+ /** @brief Trigger the autosave timer start */
+ void startAutoSave();
};
#endif
diff --git a/src/doc/kthumb.cpp b/src/doc/kthumb.cpp
index c55fe92..9480ccb 100644
--- a/src/doc/kthumb.cpp
+++ b/src/doc/kthumb.cpp
@@ -179,6 +179,12 @@ QImage KThumb::getProducerFrame(int framepos, int frameWidth, int displayWidth,
//static
QImage KThumb::getFrame(Mlt::Producer *producer, int framepos, int frameWidth, int displayWidth, int height)
{
+
+ QImage p1(displayWidth, height, QImage::Format_ARGB32_Premultiplied);
+ p1.fill(QColor(Qt::red).rgb());
+ return p1;
+
+
if (producer == NULL || !producer->is_valid()) {
QImage p(displayWidth, height, QImage::Format_ARGB32_Premultiplied);
p.fill(QColor(Qt::red).rgb());
@@ -207,11 +213,16 @@ QImage KThumb::getFrame(Mlt::Frame *frame, int frameWidth, int displayWidth, int
Q_UNUSED(frameWidth)
QImage p(displayWidth, height, QImage::Format_ARGB32_Premultiplied);
+ /*p.fill(QColor(Qt::red).rgb());
+ return p;*/
+
if (frame == NULL || !frame->is_valid()) {
p.fill(QColor(Qt::red).rgb());
return p;
}
-
+ frame->set("rescale.interp", "bilinear");
+ frame->set("deinterlace_method", "onefield");
+ frame->set("top_field_first", -1);
int ow = displayWidth;//frameWidth;
int oh = height;
mlt_image_format format = mlt_image_rgb24a;
diff --git a/src/dvdwizard/dvdwizardchapters.cpp b/src/dvdwizard/dvdwizardchapters.cpp
index b656897..8e1b3b7 100644
--- a/src/dvdwizard/dvdwizardchapters.cpp
+++ b/src/dvdwizard/dvdwizardchapters.cpp
@@ -146,7 +146,8 @@ void DvdWizardChapters::createMonitor(DVDFORMAT format)
{
QString profile = DvdWizardVob::getDvdProfile(format);
if (m_monitor == NULL) {
- m_monitor = new Monitor(Kdenlive::DvdMonitor, m_manager, profile, this);
+ //TODO: allow monitor with different profile for DVD
+ m_monitor = new Monitor(Kdenlive::DvdMonitor, m_manager/*, profile*/, this);
//m_monitor->start();
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(m_monitor);
diff --git a/src/effectslist/effectslist.cpp b/src/effectslist/effectslist.cpp
index 40c03d6..55d6f3c 100644
--- a/src/effectslist/effectslist.cpp
+++ b/src/effectslist/effectslist.cpp
@@ -17,6 +17,7 @@
#include "effectslist.h"
+#include "kdenlivesettings.h"
#include <QDebug>
#include <klocalizedstring.h>
@@ -93,6 +94,18 @@ QDomElement EffectsList::getEffectByTag(const QString & tag, const QString & id)
return QDomElement();
}
+bool EffectsList::hasTransition(const QString & tag) const
+{
+ QDomNodeList trans = m_baseElement.childNodes();
+ for (int i = 0; i < trans.count(); ++i) {
+ QDomElement effect = trans.at(i).toElement();
+ if (effect.attribute("tag") == tag) {
+ return true;
+ }
+ }
+ return false;
+}
+
int EffectsList::hasEffect(const QString & tag, const QString & id) const
{
QDomNodeList effects = m_baseElement.childNodes();
@@ -115,6 +128,18 @@ QStringList EffectsList::effectIdInfo(const int ix) const
QString groupName = effect.attribute("name");
info << groupName << groupName << effect.attribute("id") << QString::number(Kdenlive::groupEffect);
} else {
+ if (KdenliveSettings::gpu_accel()) {
+ // Using Movit
+ if (effect.attribute("context") == "nomovit") {
+ // This effect has a Movit counterpart, so hide it when using Movit
+ return info;
+ }
+ } else {
+ // Not using Movit, don't display movit effects
+ if (effect.attribute("tag").startsWith("movit.")) {
+ return info;
+ }
+ }
QDomElement namenode = effect.firstChildElement("name");
QString name = namenode.text();
if (name.isEmpty()) {
@@ -309,7 +334,7 @@ QDomElement EffectsList::append(QDomElement e)
{
QDomElement result;
if (!e.isNull()) {
- result = m_baseElement.appendChild(importNode(e, true)).toElement();
+ result = m_baseElement.appendChild(e).toElement();//importNode(e, true)).toElement();
if (m_useIndex) {
updateIndexes(m_baseElement.childNodes(), m_baseElement.childNodes().count() - 1);
}
diff --git a/src/effectslist/effectslist.h b/src/effectslist/effectslist.h
index 0d26bc6..838b187 100644
--- a/src/effectslist/effectslist.h
+++ b/src/effectslist/effectslist.h
@@ -20,17 +20,14 @@
* @brief List for effects objects.
* @author Jason Wood
*
- * This is a list of DocClipBase objects, to be used instead of
- * QList<DocClipBase> to enable sorting lists correctly. It also contains the
- * ability to set a "master clip", which can be used by a number of operations
- * where there is the need of one clip to act as a reference for what happens to
- * all clips.
- */
+*/
#ifndef EFFECTSLIST_H
#define EFFECTSLIST_H
+
#include <QDomDocument>
+#include <QSize>
namespace Kdenlive {
enum EFFECTTYPE { simpleEffect, groupEffect };
@@ -41,7 +38,6 @@ class EffectsList: public QDomDocument
public:
explicit EffectsList(bool indexRequired = false);
~EffectsList();
-
/** @brief Returns the XML element of an effect.
* @param name name of the effect to be returned */
QDomElement getEffectByName(const QString & name) const;
@@ -52,6 +48,7 @@ public:
* @param id effect id
* @return effect index if the effect exists, -1 otherwise */
int hasEffect(const QString & tag, const QString & id) const;
+ bool hasTransition(const QString & tag) const;
/** @brief Lists the core properties of an effect.
* @param ix effect index
diff --git a/src/effectslist/effectslistview.cpp b/src/effectslist/effectslistview.cpp
index 2aa9f47..14acafb 100644
--- a/src/effectslist/effectslistview.cpp
+++ b/src/effectslist/effectslistview.cpp
@@ -31,13 +31,14 @@
#include <QDir>
#include <QStandardPaths>
+
EffectsListView::EffectsListView(QWidget *parent) :
QWidget(parent)
{
setupUi(this);
- QMenu *contextMenu = new QMenu(this);
- m_effectsList = new EffectsListWidget(contextMenu);
+ m_contextMenu = new QMenu(this);
+ m_effectsList = new EffectsListWidget();
m_effectsList->setStyleSheet(customStyleSheet());
QVBoxLayout *lyr = new QVBoxLayout(effectlistframe);
lyr->addWidget(m_effectsList);
@@ -59,15 +60,59 @@ EffectsListView::EffectsListView(QWidget *parent) :
else
infopanel->hide();
- contextMenu->addAction(QIcon::fromTheme("edit-delete"), i18n("Delete effect"), this, SLOT(slotRemoveEffect()));
+ m_removeAction = m_contextMenu->addAction(QIcon::fromTheme("edit-delete"), i18n("Delete effect"), this, SLOT(slotRemoveEffect()));
- connect(type_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(filterList(int)));
+ effectsAll->setIcon(QIcon::fromTheme("kdenlive-show-all-effects"));
+ effectsAll->setToolTip(i18n("Show all effects"));
+ effectsVideo->setIcon(QIcon::fromTheme("kdenlive-show-video-effects"));
+ effectsVideo->setToolTip(i18n("Show video effects"));
+ effectsAudio->setIcon(QIcon::fromTheme("kdenlive-show-audio-effects"));
+ effectsAudio->setToolTip(i18n("Show audio effects"));
+ effectsGPU->setIcon(QIcon::fromTheme("kdenlive-show-gpu-effects"));
+ effectsGPU->setToolTip(i18n("Show GPU effects"));
+ if (!KdenliveSettings::gpu_accel()) {
+ effectsGPU->setHidden(true);
+ }
+ effectsCustom->setIcon(QIcon::fromTheme("kdenlive-show-custom"));
+ effectsCustom->setToolTip(i18n("Show custom effects"));
+ m_effectsFavorites = new MyDropButton(this);
+ horizontalLayout->addWidget(m_effectsFavorites);
+ m_effectsFavorites->setIcon(QIcon::fromTheme("favorites"));
+ m_effectsFavorites->setToolTip(i18n("Show favorite effects"));
+ connect(m_effectsFavorites, SIGNAL(addEffectToFavorites(QString)), this, SLOT(slotAddFavorite(QString)));
+
+ connect(effectsAll, SIGNAL(clicked()), this, SLOT(filterList()));
+ connect(effectsVideo, SIGNAL(clicked()), this, SLOT(filterList()));
+ connect(effectsAudio, SIGNAL(clicked()), this, SLOT(filterList()));
+ connect(effectsGPU, SIGNAL(clicked()), this, SLOT(filterList()));
+ connect(effectsCustom, SIGNAL(clicked()), this, SLOT(filterList()));
+ connect(m_effectsFavorites, SIGNAL(clicked()), this, SLOT(filterList()));
connect(buttonInfo, SIGNAL(clicked()), this, SLOT(showInfoPanel()));
- connect(m_effectsList, SIGNAL(itemSelectionChanged()), this, SLOT(slotUpdateInfo()));
- connect(m_effectsList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotEffectSelected()));
+ connect(m_effectsList, &EffectsListWidget::itemSelectionChanged, this, &EffectsListView::slotUpdateInfo);
+ connect(m_effectsList, &EffectsListWidget::itemDoubleClicked, this, &EffectsListView::slotEffectSelected);
+ connect(m_effectsList, SIGNAL(displayMenu(QTreeWidgetItem *, const QPoint &)), this, SLOT(slotDisplayMenu(QTreeWidgetItem *, const QPoint &)));
connect(search_effect, SIGNAL(hiddenChanged(QTreeWidgetItem*,bool)), this, SLOT(slotUpdateSearch(QTreeWidgetItem*,bool)));
- connect(m_effectsList, SIGNAL(applyEffect(QDomElement)), this, SIGNAL(addEffect(QDomElement)));
+ connect(m_effectsList, &EffectsListWidget::applyEffect, this, &EffectsListView::addEffect);
connect(search_effect, SIGNAL(textChanged(QString)), this, SLOT(slotAutoExpand(QString)));
+
+ // Select preferred effect tab
+ switch (KdenliveSettings::selected_effecttab()) {
+ case EffectsListWidget::EFFECT_VIDEO:
+ effectsVideo->setChecked(true);
+ break;
+ case EffectsListWidget::EFFECT_AUDIO:
+ effectsAudio->setChecked(true);
+ break;
+ case EffectsListWidget::EFFECT_GPU:
+ if (KdenliveSettings::gpu_accel()) effectsGPU->setChecked(true);
+ break;
+ case EffectsListWidget::EFFECT_CUSTOM:
+ effectsCustom->setChecked(true);
+ break;
+ case EffectsListWidget::EFFECT_FAVORITES:
+ m_effectsFavorites->setChecked(true);
+ break;
+ }
//m_effectsList->setCurrentRow(0);
}
@@ -82,15 +127,48 @@ const QString EffectsListView::customStyleSheet() const
border-image: none;image: url(:/images/stylesheet-branch-open.png);}");
}
+void EffectsListView::slotAddFavorite(QString id)
+{
+ QStringList favs = KdenliveSettings::favorite_effects();
+ if (!favs.contains(id)) {
+ favs << id;
+ KdenliveSettings::setFavorite_effects(favs);
+ }
+}
-void EffectsListView::filterList(int pos)
+void EffectsListView::filterList()
{
+ int pos = 0;
+ if (effectsVideo->isChecked()) pos = EffectsListWidget::EFFECT_VIDEO;
+ else if (effectsAudio->isChecked()) pos = EffectsListWidget::EFFECT_AUDIO;
+ else if (effectsGPU->isChecked()) pos = EffectsListWidget::EFFECT_GPU;
+ else if (m_effectsFavorites->isChecked()) pos = EffectsListWidget::EFFECT_FAVORITES;
+ else if (effectsCustom->isChecked()) pos = EffectsListWidget::EFFECT_CUSTOM;
+ KdenliveSettings::setSelected_effecttab(pos);
+ if (pos == EffectsListWidget::EFFECT_CUSTOM) {
+ m_removeAction->setText(i18n("Delete effect"));
+ }
+ else if (pos == EffectsListWidget::EFFECT_FAVORITES) {
+ m_removeAction->setText(i18n("Remove from favorites"));
+ }
for (int i = 0; i < m_effectsList->topLevelItemCount(); ++i) {
QTreeWidgetItem *folder = m_effectsList->topLevelItem(i);
bool hideFolder = true;
for (int j = 0; j < folder->childCount(); ++j) {
QTreeWidgetItem *item = folder->child(j);
- if (pos == 0 || pos == item->data(0, Qt::UserRole).toInt()) {
+ if (pos == EffectsListWidget::EFFECT_FAVORITES) {
+ QStringList data = item->data(0, Qt::UserRole + 1).toStringList();
+ QString id = data.at(1);
+ if (id.isEmpty()) id = data.at(0);
+ if (KdenliveSettings::favorite_effects().contains(id)) {
+ item->setHidden(false);
+ hideFolder = false;
+ }
+ else {
+ item->setHidden(true);
+ }
+ }
+ else if (pos == 0 || pos == item->data(0, Qt::UserRole).toInt()) {
item->setHidden(false);
hideFolder = false;
} else {
@@ -143,10 +221,35 @@ void EffectsListView::slotUpdateInfo()
void EffectsListView::reloadEffectList(QMenu *effectsMenu, KActionCategory *effectActions)
{
m_effectsList->initList(effectsMenu, effectActions);
+ filterList();
+}
+
+void EffectsListView::slotDisplayMenu(QTreeWidgetItem *item, const QPoint &pos)
+{
+ if (KdenliveSettings::selected_effecttab() == EffectsListWidget::EFFECT_FAVORITES) {
+ m_contextMenu->popup(pos);
+ }
+ else if (item->data(0, Qt::UserRole + 1).toInt() == EffectsListWidget::EFFECT_CUSTOM) {
+ m_contextMenu->popup(pos);
+ }
+
}
void EffectsListView::slotRemoveEffect()
{
+ if (KdenliveSettings::selected_effecttab() == EffectsListWidget::EFFECT_FAVORITES) {
+ QDomElement e = m_effectsList->currentEffect();
+ QString id = e.attribute("id");
+ if (id.isEmpty()) {
+ id = e.attribute("tag");
+ }
+ QStringList favs = KdenliveSettings::favorite_effects();
+ favs.removeAll(id);
+ KdenliveSettings::setFavorite_effects(favs);
+ filterList();
+ return;
+ }
+
QTreeWidgetItem *item = m_effectsList->currentItem();
QString effectId = item->text(0);
QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/effects";
@@ -179,11 +282,11 @@ void EffectsListView::slotRemoveEffect()
void EffectsListView::slotUpdateSearch(QTreeWidgetItem *item, bool hidden)
{
if (!hidden) {
- if (item->data(0, Qt::UserRole).toInt() == type_combo->currentIndex()) {
+ if (item->data(0, Qt::UserRole).toInt() == KdenliveSettings::selected_effecttab()) {
if (item->parent())
item->parent()->setHidden(false);
} else {
- if (type_combo->currentIndex() != 0)
+ if (KdenliveSettings::selected_effecttab() != 0)
item->setHidden(true);
}
}
@@ -191,14 +294,23 @@ void EffectsListView::slotUpdateSearch(QTreeWidgetItem *item, bool hidden)
void EffectsListView::slotAutoExpand(const QString &text)
{
+ QTreeWidgetItem *curr = m_effectsList->currentItem();
search_effect->updateSearch();
bool selected = false;
+ if (curr && !curr->isHidden()) {
+ selected = true;
+ }
for (int i = 0; i < m_effectsList->topLevelItemCount(); ++i) {
QTreeWidgetItem *folder = m_effectsList->topLevelItem(i);
bool expandFolder = false;
/*if (folder->isHidden())
continue;*/
- if (!text.isEmpty()) {
+ if (text.isEmpty()) {
+ if (curr && curr->parent() == folder) {
+ expandFolder = true;
+ }
+ }
+ else {
for (int j = 0; j < folder->childCount(); ++j) {
QTreeWidgetItem *item = folder->child(j);
if (!item->isHidden()) {
diff --git a/src/effectslist/effectslistview.h b/src/effectslist/effectslistview.h
index 7ac3044..7ba4118 100644
--- a/src/effectslist/effectslistview.h
+++ b/src/effectslist/effectslistview.h
@@ -26,6 +26,9 @@
#include "gentime.h"
#include <QDomElement>
+#include <QMimeData>
+#include <QDragEnterEvent>
+#include <QDebug>
class EffectsList;
class EffectsListWidget;
@@ -33,6 +36,41 @@ class QTreeWidget;
class KActionCategory;
/**
+ * @class MyDropButton
+ * @brief A QToolButton accepting effect drops
+ * @author Jean-Baptiste Mardelle
+ */
+
+class MyDropButton : public QToolButton
+{
+ Q_OBJECT
+
+public:
+ explicit MyDropButton(QWidget *parent = 0) : QToolButton(parent) {
+ setAcceptDrops(true);
+ setAutoExclusive(true);
+ setCheckable(true);
+ setAutoRaise(true);
+ }
+protected:
+ void dragEnterEvent(QDragEnterEvent * event) {
+ if (event->mimeData()->hasFormat("kdenlive/effectslist")) {
+ event->accept();
+ }
+ }
+ void dropEvent(QDropEvent * event) {
+ const QString effects = QString::fromUtf8(event->mimeData()->data("kdenlive/effectslist"));
+ QDomDocument doc;
+ doc.setContent(effects, true);
+ QString id = doc.documentElement().attribute("id");
+ if (id.isEmpty()) id = doc.documentElement().attribute("tag");
+ emit addEffectToFavorites(id);
+ }
+signals:
+ void addEffectToFavorites(QString);
+};
+
+/**
* @class EffectsListView
* @brief Manages the controls for the treewidget containing the effects.
* @author Jean-Baptiste Mardelle
@@ -56,11 +94,16 @@ public:
private:
EffectsListWidget *m_effectsList;
const QString customStyleSheet() const;
+ /** @brief Custom button to display favorite effects, accepts drops to add effect to favorites. */
+ MyDropButton *m_effectsFavorites;
+ /** @brief Action triggering remove effect from favorites or delete custom effect, depending on active tab. */
+ QAction *m_removeAction;
+ QMenu *m_contextMenu;
private slots:
/** @brief Applies the type filter to the effect list.
- * @param pos Index of the combo box; where 0 = All, 1 = Video, 2 = Audio, 3 = Custom */
- void filterList(int pos);
+ * @param pos Index of the combo box; where 0 = All, 1 = Video, 2 = Audio, 3 = GPU, 4 = Custom */
+ void filterList();
/** @brief Updates the info panel to match the selected effect. */
void slotUpdateInfo();
@@ -87,6 +130,10 @@ private slots:
/** @brief Expands folders that match our search.
* @param text Current search string */
void slotAutoExpand(const QString &text);
+ /** @brief Add an effect to the favorites
+ * @param id id of the effect we want */
+ void slotAddFavorite(QString id);
+ void slotDisplayMenu(QTreeWidgetItem *item, const QPoint &pos);
signals:
void addEffect(const QDomElement&);
diff --git a/src/effectslist/effectslistwidget.cpp b/src/effectslist/effectslistwidget.cpp
index 04334ea..06b9705 100644
--- a/src/effectslist/effectslistwidget.cpp
+++ b/src/effectslist/effectslistwidget.cpp
@@ -21,6 +21,7 @@
#include "effectslistwidget.h"
#include "effectslist/effectslist.h"
#include "mainwindow.h"
+#include "kdenlivesettings.h"
#include "klocalizedstring.h"
#include <QDebug>
@@ -30,18 +31,12 @@
#include <QStandardPaths>
-static const int EFFECT_VIDEO = 1;
-static const int EFFECT_AUDIO = 2;
-static const int EFFECT_CUSTOM = 3;
-static const int EFFECT_FOLDER = 4;
-
const int TypeRole = Qt::UserRole;
const int IdRole = TypeRole + 1;
-EffectsListWidget::EffectsListWidget(QMenu *contextMenu, QWidget *parent) :
- QTreeWidget(parent),
- m_menu(contextMenu)
+EffectsListWidget::EffectsListWidget(QWidget *parent) :
+ QTreeWidget(parent)
{
setColumnCount(1);
setDragEnabled(true);
@@ -54,7 +49,7 @@ EffectsListWidget::EffectsListWidget(QMenu *contextMenu, QWidget *parent) :
//setSelectionMode(QAbstractItemView::ExtendedSelection);
setDragDropMode(QAbstractItemView::DragOnly);
updatePalette();
- connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(slotExpandItem(QModelIndex)));
+ connect(this, &EffectsListWidget::activated, this, &EffectsListWidget::slotExpandItem);
}
EffectsListWidget::~EffectsListWidget()
@@ -152,7 +147,6 @@ void EffectsListWidget::initList(QMenu *effectsMenu, KActionCategory *effectActi
loadEffects(&MainWindow::videoEffects, QIcon::fromTheme("kdenlive-show-video"), misc, &folders, EFFECT_VIDEO, current, &found);
loadEffects(&MainWindow::audioEffects, QIcon::fromTheme("kdenlive-show-audio"), audio, &folders, EFFECT_AUDIO, current, &found);
loadEffects(&MainWindow::customEffects, QIcon::fromTheme("kdenlive-custom-effect"), custom, static_cast<QList<QTreeWidgetItem *> *>(0), EFFECT_CUSTOM, current, &found);
-
if (!found && !currentFolder.isEmpty()) {
// previously selected effect was removed, focus on its parent folder
for (int i = 0; i < topLevelItemCount(); ++i) {
@@ -229,10 +223,9 @@ void EffectsListWidget::loadEffects(const EffectsList *effectlist, QIcon icon, Q
QTreeWidgetItem *item;
int ct = effectlist->count();
-
for (int ix = 0; ix < ct; ++ix) {
effectInfo = effectlist->effectIdInfo(ix);
- effectInfo.append(QString::number(type));
+ if (effectInfo.isEmpty()) continue;
QTreeWidgetItem *parentItem = NULL;
if (folders) {
@@ -249,11 +242,19 @@ void EffectsListWidget::loadEffects(const EffectsList *effectlist, QIcon icon, Q
if (!effectInfo.isEmpty()) {
item = new QTreeWidgetItem(parentItem, QStringList(effectInfo.takeFirst()));
+ QString tag = effectInfo.at(0);
+ if (type != EFFECT_CUSTOM && tag.startsWith("movit.")) {
+ // GPU effect
+ effectInfo.append(QString::number(EFFECT_GPU));
+ item->setData(0, TypeRole, EFFECT_GPU);
+ } else {
+ effectInfo.append(QString::number(type));
+ item->setData(0, TypeRole, type);
+ }
if (effectInfo.count() == 4) item->setIcon(0, QIcon::fromTheme("folder"));
else item->setIcon(0, icon);
- item->setData(0, TypeRole, type);
item->setData(0, IdRole, effectInfo);
- item->setToolTip(0, effectlist->getInfo(effectInfo.at(0), effectInfo.at(1)));
+ item->setToolTip(0, effectlist->getInfo(tag, effectInfo.at(1)));
if (item->text(0) == current) {
setCurrentItem(item);
*found = true;
@@ -288,10 +289,11 @@ const QDomElement EffectsListWidget::itemEffect(QTreeWidgetItem *item) const
if (!item || item->data(0, TypeRole).toInt() == (int)EFFECT_FOLDER) return effect;
QStringList effectInfo = item->data(0, IdRole).toStringList();
switch (item->data(0, TypeRole).toInt()) {
- case 1:
+ case EFFECT_VIDEO:
+ case EFFECT_GPU:
effect = MainWindow::videoEffects.getEffectByTag(effectInfo.at(0), effectInfo.at(1)).cloneNode().toElement();
break;
- case 2:
+ case EFFECT_AUDIO:
effect = MainWindow::audioEffects.getEffectByTag(effectInfo.at(0), effectInfo.at(1)).cloneNode().toElement();
break;
default:
@@ -309,10 +311,11 @@ QString EffectsListWidget::currentInfo() const
QString info;
QStringList effectInfo = item->data(0, IdRole).toStringList();
switch (item->data(0, TypeRole).toInt()) {
- case 1:
+ case EFFECT_VIDEO:
+ case EFFECT_GPU:
info = MainWindow::videoEffects.getInfo(effectInfo.at(0), effectInfo.at(1));
break;
- case 2:
+ case EFFECT_AUDIO:
info = MainWindow::audioEffects.getInfo(effectInfo.at(0), effectInfo.at(1));
break;
default:
@@ -366,8 +369,9 @@ void EffectsListWidget::dragMoveEvent(QDragMoveEvent *event)
void EffectsListWidget::contextMenuEvent(QContextMenuEvent * event)
{
QTreeWidgetItem *item = itemAt(event->pos());
- if (item && item->data(0, TypeRole).toInt() == EFFECT_CUSTOM)
- m_menu->popup(event->globalPos());
+ if (item && item->data(0, TypeRole) != EFFECT_FOLDER) {
+ emit displayMenu(item, event->globalPos());
+ }
}
diff --git a/src/effectslist/effectslistwidget.h b/src/effectslist/effectslistwidget.h
index b45bcf6..ff80420 100644
--- a/src/effectslist/effectslistwidget.h
+++ b/src/effectslist/effectslistwidget.h
@@ -32,8 +32,15 @@ class EffectsListWidget : public QTreeWidget
{
Q_OBJECT
-public:
- explicit EffectsListWidget(QMenu *contextMenu, QWidget *parent = 0);
+ public:
+ static const int EFFECT_VIDEO = 1;
+ static const int EFFECT_AUDIO = 2;
+ static const int EFFECT_GPU = 3;
+ static const int EFFECT_CUSTOM = 4;
+ static const int EFFECT_FAVORITES = 5;
+ static const int EFFECT_FOLDER = 6;
+
+ explicit EffectsListWidget(QWidget *parent = 0);
virtual ~EffectsListWidget();
const QDomElement currentEffect() const;
QString currentInfo() const;
@@ -68,6 +75,7 @@ private slots:
signals:
void applyEffect(const QDomElement);
+ void displayMenu(QTreeWidgetItem *, const QPoint &);
};
#endif
diff --git a/src/effectslist/initeffects.cpp b/src/effectslist/initeffects.cpp
index 6117fd0..761d5cf 100644
--- a/src/effectslist/initeffects.cpp
+++ b/src/effectslist/initeffects.cpp
@@ -35,34 +35,10 @@
#include <xlocale.h>
#endif
-initEffectsThumbnailer::initEffectsThumbnailer() :
- QThread()
-{
-}
-
-void initEffectsThumbnailer::prepareThumbnailsCall(const QStringList& list)
-{
- m_list = list;
- start();
- //qDebug() << "done";
-}
-
-void initEffectsThumbnailer::run()
-{
- foreach(const QString & entry, m_list) {
- //qDebug() << entry;
- if (!entry.isEmpty() && (entry.endsWith(QLatin1String(".png")) || entry.endsWith(QLatin1String(".pgm")))) {
- if (!MainWindow::m_lumacache.contains(entry)) {
- QImage pix(entry);
- //if (!pix.isNull())
- MainWindow::m_lumacache.insert(entry, pix.scaled(30, 30, Qt::KeepAspectRatio, Qt::SmoothTransformation));
- //qDebug() << "stored";
- }
- }
- }
-}
-
-initEffectsThumbnailer initEffects::thumbnailer;
+#include <locale>
+#ifdef Q_OS_MAC
+#include <xlocale.h>
+#endif
// static
void initEffects::refreshLumas()
@@ -73,7 +49,7 @@ void initEffects::refreshLumas()
QStringList filters;
filters << "*.pgm" << "*.png";
- QStringList customLumas = QStandardPaths::locateAll(QStandardPaths::DataLocation, "lumas");
+ QStringList customLumas = QStandardPaths::locateAll(QStandardPaths::DataLocation, "lumas", QStandardPaths::LocateDirectory);
foreach(const QString & folder, customLumas) {
QDir directory(folder);
QStringList filesnames = directory.entryList(filters, QDir::Files);
@@ -133,17 +109,17 @@ QDomDocument initEffects::getUsedCustomEffects(const QMap <QString, QString>& ef
}
//static
-Mlt::Repository *initEffects::parseEffectFiles(const QString &locale)
+bool initEffects::parseEffectFiles(Mlt::Repository* repository, const QString &locale)
{
+ bool movit = false;
QStringList::Iterator more;
QStringList::Iterator it;
QStringList fileList;
QString itemName;
- Mlt::Repository *repository = Mlt::Factory::init();
if (!repository) {
//qDebug() << "Repository didn't finish initialisation" ;
- return NULL;
+ return movit;
}
// Warning: Mlt::Factory::init() resets the locale to the default system value, make sure we keep correct locale
@@ -172,12 +148,21 @@ Mlt::Repository *initEffects::parseEffectFiles(const QString &locale)
KdenliveSettings::setProducerslist(producersList);
delete producers;
+ if (filtersList.contains("glsl.manager") && producersList.contains("rtaudio")) {
+ // enable movit GPU effects / display. Currently, Movit crashes with sdl_audio,
+ // So enable only when rtaudio is available
+ movit = true;
+ }
+ else KdenliveSettings::setGpu_accel(false);
+
// Retrieve the list of available transitions.
Mlt::Properties *transitions = repository->transitions();
QStringList transitionsItemList;
max = transitions->count();
- for (int i = 0; i < max; ++i)
+ for (int i = 0; i < max; ++i) {
+ //qDebug()<<"TRANSITION "<<i<<" = "<<transitions->get_name(i);
transitionsItemList << transitions->get_name(i);
+ }
delete transitions;
// Remove blacklisted transitions from the list.
@@ -198,6 +183,7 @@ Mlt::Repository *initEffects::parseEffectFiles(const QString &locale)
// Remove blacklisted effects from the filters list.
QStringList mltFiltersList = filtersList;
+ QStringList mltBlackList;
QFile file2(QStandardPaths::locate(QStandardPaths::DataLocation, "blacklisted_effects.txt"));
if (file2.open(QIODevice::ReadOnly)) {
QTextStream in(&file2);
@@ -206,6 +192,7 @@ Mlt::Repository *initEffects::parseEffectFiles(const QString &locale)
if (!black.isEmpty() && !black.startsWith('#') &&
mltFiltersList.contains(black)) {
mltFiltersList.removeAll(black);
+ mltBlackList << black;
}
}
@@ -235,7 +222,27 @@ Mlt::Repository *initEffects::parseEffectFiles(const QString &locale)
MainWindow::transitions.clearList();
foreach(const QDomElement & effect, effectsMap)
MainWindow::transitions.append(effect);
- effectsMap.clear();
+ effectsMap.clear();
+
+ // Create structure holding all effects descriptions so that if an XML effect has no description, we take it from MLT
+ QMap <QString, QString> effectDescriptions;
+ foreach(const QString & filtername, mltBlackList) {
+ QDomDocument doc = createDescriptionFromMlt(repository, "filters", filtername);
+ if (!doc.isNull()) {
+ if (doc.elementsByTagName("description").count() > 0) {
+ QString desc = doc.documentElement().firstChildElement("description").text();
+ //WARNING: TEMPORARY FIX for unusable MLT SOX parameters description
+ if (desc.startsWith(QLatin1String("Process audio using a SoX"))) {
+ // Remove MLT's SOX generated effects since the parameters properties are unusable for us
+ continue;
+ }
+ if (!desc.isEmpty()) {
+ effectDescriptions.insert(filtername, desc);
+ }
+ }
+ }
+ }
+
// Create effects from MLT
foreach(const QString & filtername, mltFiltersList) {
QDomDocument doc = createDescriptionFromMlt(repository, "filters", filtername);
@@ -271,7 +278,7 @@ Mlt::Repository *initEffects::parseEffectFiles(const QString &locale)
parseEffectFile(&MainWindow::customEffects,
&MainWindow::audioEffects,
&MainWindow::videoEffects,
- itemName, filtersList, producersList, repository);
+ itemName, filtersList, producersList, repository, effectDescriptions);
}
}
@@ -309,7 +316,7 @@ Mlt::Repository *initEffects::parseEffectFiles(const QString &locale)
foreach(const QDomElement & effect, videoEffectsMap)
MainWindow::videoEffects.append(effect);
- return repository;
+ return movit;
}
// static
@@ -359,7 +366,7 @@ void initEffects::parseCustomEffectsFile()
}
// static
-void initEffects::parseEffectFile(EffectsList *customEffectList, EffectsList *audioEffectList, EffectsList *videoEffectList, const QString &name, QStringList filtersList, QStringList producersList, Mlt::Repository *repository)
+void initEffects::parseEffectFile(EffectsList *customEffectList, EffectsList *audioEffectList, EffectsList *videoEffectList, const QString &name, QStringList filtersList, QStringList producersList, Mlt::Repository *repository, QMap <QString, QString> effectDescriptions)
{
QDomDocument doc;
QFile file(name);
@@ -371,7 +378,7 @@ void initEffects::parseEffectFile(EffectsList *customEffectList, EffectsList *au
effects = doc.elementsByTagName("effect");
if (effects.count() == 0) {
- //qDebug() << "Effect broken: " << name;
+ qDebug() << "+++++++++++++\nEffect broken: " << name<<"\n+++++++++++";;
return;
}
@@ -380,6 +387,18 @@ void initEffects::parseEffectFile(EffectsList *customEffectList, EffectsList *au
QLocale locale;
documentElement = effects.item(i).toElement();
QString tag = documentElement.attribute("tag", QString());
+
+ //If XML has no description, take it fom MLT's descriptions
+ if (effectDescriptions.contains(tag)) {
+ QDomNodeList desc = documentElement.elementsByTagName("description");
+ if (desc.isEmpty()) {
+ QDomElement d = documentElement.ownerDocument().createElement("description");
+ QDomText value = documentElement.ownerDocument().createTextNode(effectDescriptions.value(tag));
+ d.appendChild(value);
+ documentElement.appendChild(d);
+ }
+ }
+
if (documentElement.hasAttribute("LC_NUMERIC")) {
// set a locale for that file
locale = QLocale(documentElement.attribute("LC_NUMERIC"));
@@ -433,8 +452,9 @@ void initEffects::parseEffectFile(EffectsList *customEffectList, EffectsList *au
QString type = documentElement.attribute("type", QString());
if (type == "audio")
audioEffectList->append(documentElement);
- else if (type == "custom")
+ else if (type == "custom") {
customEffectList->append(documentElement);
+ }
else
videoEffectList->append(documentElement);
}
diff --git a/src/effectslist/initeffects.h b/src/effectslist/initeffects.h
index 49cdf71..9c0d52e 100644
--- a/src/effectslist/initeffects.h
+++ b/src/effectslist/initeffects.h
@@ -19,8 +19,8 @@
#define InitEffects_H
#include <QDomDocument>
-#include <QThread>
#include <QStringList>
+#include <QMap>
#include <mlt++/Mlt.h>
@@ -30,18 +30,6 @@
class EffectsList;
-class initEffectsThumbnailer : public QThread
-{
- Q_OBJECT
-public:
- initEffectsThumbnailer();
- void prepareThumbnailsCall(const QStringList&);
- void run();
-private :
- QStringList m_list;
-
-};
-
class initEffects
{
public:
@@ -49,12 +37,12 @@ public:
/** @brief Fills the effects and transitions lists.
* @ref fillTransitionsList
* @ref parseEffectFile
- * @return pointer to the MLT repository
+ * @return true if Movit GPU effects are available
*
* It checks for all available effects and transitions, removes blacklisted
* ones, calls fillTransitionsList() and parseEffectFile() to fill the lists
* (with sorted, unique items) and then fills the global lists. */
- static Mlt::Repository *parseEffectFiles(const QString &locale = QString());
+ static bool parseEffectFiles(Mlt::Repository* repository, const QString &locale = QString());
static void refreshLumas();
static QDomDocument createDescriptionFromMlt(Mlt::Repository* repository, const QString& type, const QString& name);
static QDomDocument getUsedCustomEffects(const QMap<QString, QString> &effectids);
@@ -104,14 +92,13 @@ public:
EffectsList *videoEffectList,
const QString &name, QStringList filtersList,
QStringList producersList,
- Mlt::Repository *repository);
+ Mlt::Repository *repository, QMap <QString, QString> effectDescriptions);
/** @brief Reloads information about custom effects. */
static void parseCustomEffectsFile();
private:
initEffects(); // disable the constructor
- static initEffectsThumbnailer thumbnailer;
};
diff --git a/src/effectstack/CMakeLists.txt b/src/effectstack/CMakeLists.txt
index 2d891e1..0cd0a12 100644
--- a/src/effectstack/CMakeLists.txt
+++ b/src/effectstack/CMakeLists.txt
@@ -22,5 +22,7 @@ set(kdenlive_SRCS
effectstack/widgets/geometrywidget.cpp
effectstack/widgets/kis_cubic_curve.cpp
effectstack/widgets/kis_curve_widget.cpp
+ effectstack/widgets/colorwheel.cpp
+ effectstack/widgets/lumaliftgain.cpp
PARENT_SCOPE)
diff --git a/src/effectstack/collapsibleeffect.cpp b/src/effectstack/collapsibleeffect.cpp
index 0570476..adc6fbf 100644
--- a/src/effectstack/collapsibleeffect.cpp
+++ b/src/effectstack/collapsibleeffect.cpp
@@ -21,7 +21,7 @@
#include "collapsibleeffect.h"
#include "effectslist/effectslist.h"
#include "kdenlivesettings.h"
-#include "project/projectlist.h"
+#include "dialogs/clipcreationdialog.h"
#include <QInputDialog>
#include <QDialog>
@@ -157,7 +157,7 @@ void CollapsibleEffect::slotCreateGroup()
void CollapsibleEffect::slotCreateRegion()
{
- QString allExtensions = ProjectList::getExtensions().join(" ");
+ QString allExtensions = ClipCreationDialog::getExtensions().join(" ");
const QString dialogFilter = allExtensions + ' ' + QLatin1Char('|') + i18n("All Supported Files") + "\n* " + QLatin1Char('|') + i18n("All Files");
QString clipFolder = KRecentDirs::dir(":KdenliveClipFolder");
if (clipFolder.isEmpty()) clipFolder = QDir::homePath();
@@ -292,9 +292,12 @@ void CollapsibleEffect::slotSaveEffect()
{
QString name = QInputDialog::getText(this, i18n("Save Effect"), i18n("Name for saved effect: "));
if (name.isEmpty()) return;
- QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/effects";
- path = path + name + ".xml";
- if (QFile::exists(path)) if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", path)) == KMessageBox::No) return;
+ QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/effects/");
+ if (!dir.exists()) {
+ dir.mkpath(".");
+ }
+
+ if (dir.exists(name + ".xml")) if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", name + ".xml")) == KMessageBox::No) return;
QDomDocument doc;
QDomElement effect = m_effect.cloneNode().toElement();
@@ -313,7 +316,7 @@ void CollapsibleEffect::slotSaveEffect()
effectprops.setAttribute("id", name);
effectprops.setAttribute("type", "custom");
- QFile file(path);
+ QFile file(dir.absoluteFilePath(name + ".xml"));
if (file.open(QFile::WriteOnly | QFile::Truncate)) {
QTextStream out(&file);
out << doc.toString();
diff --git a/src/effectstack/collapsibleeffect.h b/src/effectstack/collapsibleeffect.h
index 3c7484b..abf0a8e 100644
--- a/src/effectstack/collapsibleeffect.h
+++ b/src/effectstack/collapsibleeffect.h
@@ -24,7 +24,7 @@
#include "parametercontainer.h"
#include "abstractcollapsiblewidget.h"
#include "timecode.h"
-
+#include "mltcontroller/effectscontroller.h"
#include <QDomElement>
diff --git a/src/effectstack/effectstackedit.cpp b/src/effectstack/effectstackedit.cpp
index a93f189..03d4da7 100644
--- a/src/effectstack/effectstackedit.cpp
+++ b/src/effectstack/effectstackedit.cpp
@@ -43,7 +43,6 @@ EffectStackEdit::EffectStackEdit(Monitor *monitor, QWidget *parent) :
{
m_baseWidget = new QWidget(this);
m_metaInfo.monitor = monitor;
- m_metaInfo.trackMode = false;
setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
setFrameStyle(QFrame::NoFrame);
@@ -74,9 +73,8 @@ Monitor *EffectStackEdit::monitor()
return m_metaInfo.monitor;
}
-void EffectStackEdit::updateProjectFormat(const MltVideoProfile &profile, const Timecode &t)
+void EffectStackEdit::updateProjectFormat(const Timecode &t)
{
- m_metaInfo.profile = profile;
m_metaInfo.timecode = t;
}
diff --git a/src/effectstack/effectstackedit.h b/src/effectstack/effectstackedit.h
index b285f1d..ad92f9a 100644
--- a/src/effectstack/effectstackedit.h
+++ b/src/effectstack/effectstackedit.h
@@ -37,7 +37,7 @@ class EffectStackEdit : public QScrollArea
public:
explicit EffectStackEdit(Monitor *monitor, QWidget *parent = 0);
~EffectStackEdit();
- void updateProjectFormat(const MltVideoProfile &profile, const Timecode &t);
+ void updateProjectFormat(const Timecode &t);
static QMap<QString, QImage> iconCache;
/** @brief Sets attribute @param name to @param value.
*
diff --git a/src/effectstack/effectstackview2.cpp b/src/effectstack/effectstackview2.cpp
index 78b176e..c2b1c2f 100644
--- a/src/effectstack/effectstackview2.cpp
+++ b/src/effectstack/effectstackview2.cpp
@@ -23,12 +23,13 @@
#include "kdenlivesettings.h"
#include "mainwindow.h"
#include "doc/kthumb.h"
-#include "doc/docclipbase.h"
+#include "bin/projectclip.h"
#include "project/projectlist.h"
#include "effectslist/effectslist.h"
#include "timeline/clipitem.h"
#include "monitor/monitoreditwidget.h"
#include "monitor/monitorscene.h"
+#include "mltcontroller/clipcontroller.h"
#include <QDebug>
#include <klocalizedstring.h>
@@ -40,17 +41,18 @@
#include <QDrag>
#include <QMimeData>
-EffectStackView2::EffectStackView2(Monitor *monitor, QWidget *parent) :
+EffectStackView2::EffectStackView2(QWidget *parent) :
QWidget(parent),
m_clipref(NULL),
m_trackindex(-1),
m_draggedEffect(NULL),
m_draggedGroup(NULL),
m_groupIndex(0),
- m_monitorSceneWanted(false)
+ m_monitorSceneWanted(false),
+ m_status(EMPTY),
+ m_trackInfo()
{
- m_effectMetaInfo.trackMode = false;
- m_effectMetaInfo.monitor = monitor;
+ m_effectMetaInfo.monitor = NULL;
m_effects = QList <CollapsibleEffect*>();
setAcceptDrops(true);
@@ -83,7 +85,7 @@ void EffectStackView2::slotRenderPos(int pos)
{
if (m_effects.isEmpty()) return;
if (m_monitorSceneWanted) slotCheckMonitorPosition(pos);
- if (!m_effectMetaInfo.trackMode && m_clipref) pos = pos - m_clipref->startPos().frames(KdenliveSettings::project_fps());
+ if (m_status == TIMELINE_CLIP && m_clipref) pos = pos - m_clipref->startPos().frames(KdenliveSettings::project_fps());
for (int i = 0; i< m_effects.count(); ++i)
m_effects.at(i)->slotSyncEffectsPos(pos);
@@ -98,15 +100,17 @@ void EffectStackView2::slotClipItemUpdate()
}
}
-void EffectStackView2::slotClipItemSelected(ClipItem* c)
+void EffectStackView2::slotClipItemSelected(ClipItem* c, Monitor *m)
{
if (c && !c->isEnabled()) return;
if (c && c == m_clipref) {
} else {
- if (m_clipref) disconnect(m_clipref, SIGNAL(updateRange()), this, SLOT(slotClipItemUpdate()));
+ m_effectMetaInfo.monitor = m;
+ m_masterclipref = NULL;
+ if (m_clipref) disconnect(m_clipref, SIGNAL(updateRange()), this, SLOT(slotClipItemUpdate()));
m_clipref = c;
if (c) {
- connect(m_clipref, SIGNAL(updateRange()), this, SLOT(slotClipItemUpdate()));
+ connect(m_clipref, SIGNAL(updateRange()), this, SLOT(slotClipItemUpdate()));
QString cname = m_clipref->clipName();
if (cname.length() > 30) {
m_ui.checkAll->setToolTip(i18n("Effects for %1", cname));
@@ -117,9 +121,11 @@ void EffectStackView2::slotClipItemSelected(ClipItem* c)
m_ui.checkAll->setText(i18n("Effects for %1", cname));
}
m_ui.checkAll->setEnabled(true);
- QString size = c->baseClip()->getProperty("frame_size");
- double factor = c->baseClip()->getProperty("aspect_ratio").toDouble();
- m_effectMetaInfo.frameSize = QPoint((int)(size.section('x', 0, 0).toInt() * factor + 0.5), size.section('x', 1, 1).toInt());
+ //TODO
+ int frameWidth = c->binClip()->getProducerIntProperty("meta.media.width");
+ int frameHeight = c->binClip()->getProducerIntProperty("meta.media.height");
+ double factor = c->binClip()->getProducerProperty("aspect_ratio").toDouble();
+ m_effectMetaInfo.frameSize = QPoint((int)(frameWidth * factor + 0.5), frameHeight);
}
}
if (m_clipref == NULL) {
@@ -127,23 +133,72 @@ void EffectStackView2::slotClipItemSelected(ClipItem* c)
// If monitor scene is displayed, hide it
if (m_monitorSceneWanted) {
m_effectMetaInfo.monitor->slotShowEffectScene(false);
+ m_monitorSceneWanted = false;
}
- m_monitorSceneWanted = false;
+ m_status = EMPTY;
clear();
return;
}
setEnabled(true);
- m_effectMetaInfo.trackMode = false;
+ m_status = TIMELINE_CLIP;
m_currentEffectList = m_clipref->effectList();
setupListView();
}
-void EffectStackView2::slotTrackItemSelected(int ix, const TrackInfo &info)
+void EffectStackView2::slotMasterClipItemSelected(ClipController* c, Monitor *m)
+{
+ if (c && c == m_masterclipref) {
+ } else {
+ if (!c->hasEffects() && m_status != MASTER_CLIP) {
+ return;
+ }
+ m_masterclipref = c;
+ m_clipref = NULL;
+ m_effectMetaInfo.monitor = m;
+ if (m_masterclipref) {
+ QString cname = m_masterclipref->clipName();
+ if (cname.length() > 30) {
+ m_ui.checkAll->setToolTip(i18n("Bin effects for %1", cname));
+ cname.truncate(27);
+ m_ui.checkAll->setText(i18n("Bin effects for %1", cname) + "...");
+ } else {
+ m_ui.checkAll->setToolTip(QString());
+ m_ui.checkAll->setText(i18n("Bin effects for %1", cname));
+ }
+ m_ui.checkAll->setEnabled(true);
+ //TODO
+ int frameWidth = m_masterclipref->int_property("meta.media.width");
+ int frameHeight = m_masterclipref->int_property("meta.media.height");
+ double factor = m_masterclipref->double_property("aspect_ratio");
+ m_effectMetaInfo.frameSize = QPoint((int)(frameWidth * factor + 0.5), frameHeight);
+ }
+ }
+ if (m_masterclipref == NULL) {
+ //TODO: clear list, reset paramdesc and info
+ // If monitor scene is displayed, hide it
+ if (m_monitorSceneWanted) {
+ m_effectMetaInfo.monitor->slotShowEffectScene(false);
+ m_monitorSceneWanted = false;
+ }
+ m_status = EMPTY;
+ clear();
+ return;
+ }
+ setEnabled(true);
+ m_status = MASTER_CLIP;
+ m_currentEffectList = m_masterclipref->effectList();
+ setupListView();
+}
+
+void EffectStackView2::slotTrackItemSelected(int ix, const TrackInfo &info, Monitor *m)
{
m_clipref = NULL;
- m_effectMetaInfo.trackMode = true;
+ m_status = TIMELINE_TRACK;
+ m_effectMetaInfo.monitor = m;
m_currentEffectList = info.effectsList;
m_trackInfo = info;
+ m_clipref = NULL;
+ m_masterclipref = NULL;
setEnabled(true);
m_ui.checkAll->setToolTip(QString());
m_ui.checkAll->setText(i18n("Effects for track %1", info.trackName.isEmpty() ? QString::number(ix) : info.trackName));
@@ -177,7 +232,7 @@ void EffectStackView2::setupListView()
int effectsCount = m_currentEffectList.count();
// Make sure we always have one effect selected
- if (!m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_CLIP) {
int selectedEffect = m_clipref->selectedEffectIndex();
if (selectedEffect < 1 && effectsCount > 0) m_clipref->setSelectedEffect(1);
else if (selectedEffect > effectsCount) m_clipref->setSelectedEffect(effectsCount);
@@ -218,24 +273,32 @@ void EffectStackView2::setupListView()
ItemInfo info;
bool isSelected = false;
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
+ // ?? cleanup following line
info.track = m_trackInfo.type;
info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
info.cropStart = GenTime(0);
info.startPos = GenTime(-1);
info.track = 0;
- }
- else {
+ } else if (m_status == TIMELINE_CLIP) {
info = m_clipref->info();
+ } else if (m_status == MASTER_CLIP) {
+ info.cropDuration = m_masterclipref->getPlaytime();
+ info.cropStart = GenTime(0);
+ info.startPos = GenTime(0);
}
+
CollapsibleEffect *currentEffect = new CollapsibleEffect(d, m_currentEffectList.at(i), info, &m_effectMetaInfo, i == effectsCount - 1, view);
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
isSelected = currentEffect->effectIndex() == 1;
}
- else {
+ else if (m_status == TIMELINE_CLIP) {
isSelected = currentEffect->effectIndex() == m_clipref->selectedEffectIndex();
}
+ else if (m_status == TIMELINE_CLIP) {
+ isSelected = currentEffect->effectIndex() == m_masterclipref->selectedEffectIndex;
+ }
if (isSelected) {
currentEffect->setActive(true);
if (currentEffect->needsMonitorEffectScene()) m_monitorSceneWanted = true;
@@ -263,10 +326,8 @@ void EffectStackView2::setupListView()
vbox1->addStretch(10);
slotUpdateCheckAllButton();
- if (!m_monitorSceneWanted) {
- // monitor scene not wanted
- m_effectMetaInfo.monitor->slotShowEffectScene(false);
- }
+ // show monitor scene if necessary
+ if (!m_monitorSceneWanted) m_effectMetaInfo.monitor->slotShowEffectScene(false);
// Wait a little bit for the new layout to be ready, then check if we have a scrollbar
QTimer::singleShot(200, this, SLOT(slotCheckWheelEventFilter()));
@@ -382,7 +443,7 @@ void EffectStackView2::startDrag()
QPixmap pixmap;
if (m_draggedEffect) {
QDomElement effect = m_draggedEffect->effect().cloneNode().toElement();
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK || m_status == MASTER_CLIP) {
// Keep clip crop start in case we want to paste effect
effect.setAttribute("clipstart", 0);
}
@@ -395,7 +456,7 @@ void EffectStackView2::startDrag()
}
else if (m_draggedGroup) {
doc = m_draggedGroup->effectsData();
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK || m_status == MASTER_CLIP) {
doc.documentElement().setAttribute("clipstart", 0);
}
else {
@@ -428,27 +489,37 @@ void EffectStackView2::slotUpdateEffectState(bool disable, int index, bool needs
m_effectMetaInfo.monitor->slotShowEffectScene(true);
m_monitorSceneWanted = true;
}
- if (m_effectMetaInfo.trackMode)
- emit changeEffectState(NULL, m_trackindex, QList <int>() << index, disable);
- else
- emit changeEffectState(m_clipref, -1, QList <int>() <<index, disable);
+ switch (m_status) {
+ case TIMELINE_TRACK:
+ emit changeEffectState(NULL, m_trackindex, QList <int>() << index, disable);
+ break;
+ case MASTER_CLIP:
+ m_masterclipref->changeEffectState(QList <int>() << index, disable);
+ m_effectMetaInfo.monitor->refreshMonitor();
+ break;
+ default:
+ // timeline clip effect
+ emit changeEffectState(m_clipref, -1, QList <int>() <<index, disable);
+ }
slotUpdateCheckAllButton();
}
void EffectStackView2::raiseWindow(QWidget* dock)
{
- if ((m_clipref || m_effectMetaInfo.trackMode) && dock)
+ if (m_status != EMPTY && dock)
dock->raise();
}
void EffectStackView2::slotSeekTimeline(int pos)
{
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
emit seekTimeline(pos);
- } else if (m_clipref) {
+ } else if (m_status == TIMELINE_CLIP) {
emit seekTimeline(m_clipref->startPos().frames(KdenliveSettings::project_fps()) + pos);
+ } else if (m_status == MASTER_CLIP) {
+
}
}
@@ -461,7 +532,7 @@ void EffectStackView2::slotSeekTimeline(int pos)
void EffectStackView2::slotCheckMonitorPosition(int renderPos)
{
if (m_monitorSceneWanted) {
- if (m_effectMetaInfo.trackMode || (m_clipref && renderPos >= m_clipref->startPos().frames(KdenliveSettings::project_fps()) && renderPos <= m_clipref->endPos().frames(KdenliveSettings::project_fps()))) {
+ if (m_status == TIMELINE_TRACK || m_status == MASTER_CLIP || (m_clipref && renderPos >= m_clipref->startPos().frames(KdenliveSettings::project_fps()) && renderPos <= m_clipref->endPos().frames(KdenliveSettings::project_fps()))) {
if (!m_effectMetaInfo.monitor->effectSceneDisplayed()) {
m_effectMetaInfo.monitor->slotShowEffectScene(true);
}
@@ -474,9 +545,13 @@ void EffectStackView2::slotCheckMonitorPosition(int renderPos)
}
}
-int EffectStackView2::isTrackMode(bool *ok) const
+EFFECTMODE EffectStackView2::effectStatus() const
+{
+ return m_status;
+}
+
+int EffectStackView2::trackIndex() const
{
- *ok = m_effectMetaInfo.trackMode;
return m_trackindex;
}
@@ -517,10 +592,12 @@ void EffectStackView2::slotCheckAll(int state)
allGroups.at(i)->slotEnable(disabled, false);
}
- if (m_effectMetaInfo.trackMode)
+ if (m_status == TIMELINE_TRACK)
emit changeEffectState(NULL, m_trackindex, indexes, disabled);
- else
+ else if (m_status == TIMELINE_CLIP)
emit changeEffectState(m_clipref, -1, indexes, disabled);
+ else if (m_status == MASTER_CLIP)
+ m_masterclipref->changeEffectState(indexes, disabled);
}
void EffectStackView2::slotUpdateCheckAllButton()
@@ -553,9 +630,8 @@ void EffectStackView2::deleteCurrentEffect()
}
}
-void EffectStackView2::updateProjectFormat(const MltVideoProfile &profile, const Timecode &t)
+void EffectStackView2::updateProjectFormat(const Timecode &t)
{
- m_effectMetaInfo.profile = profile;
m_effectMetaInfo.timecode = t;
}
@@ -577,30 +653,36 @@ CollapsibleEffect *EffectStackView2::getEffectByIndex(int ix)
void EffectStackView2::slotUpdateEffectParams(const QDomElement &old, const QDomElement &e, int ix)
{
- if (m_effectMetaInfo.trackMode)
+ if (m_status == TIMELINE_TRACK)
emit updateEffect(NULL, m_trackindex, old, e, ix,false);
- else if (m_clipref) {
+ else if (m_status == TIMELINE_CLIP && m_clipref) {
emit updateEffect(m_clipref, -1, old, e, ix, false);
// Make sure the changed effect is currently displayed
slotSetCurrentEffect(ix);
}
+ else if (m_status == MASTER_CLIP) {
+ m_masterclipref->updateEffect(m_effectMetaInfo.monitor->profileInfo(), old, e, ix);
+ m_effectMetaInfo.monitor->refreshMonitor();
+ }
QTimer::singleShot(200, this, SLOT(slotCheckWheelEventFilter()));
}
void EffectStackView2::slotSetCurrentEffect(int ix)
{
- if (m_clipref && ix != m_clipref->selectedEffectIndex()) {
- m_clipref->setSelectedEffect(ix);
- for (int i = 0; i < m_effects.count(); ++i) {
- if (m_effects.at(i)->effectIndex() == ix) {
- if (m_effects.at(i)->isActive()) return;
- m_effects.at(i)->setActive(true);
- m_monitorSceneWanted = m_effects.at(i)->needsMonitorEffectScene();
- slotCheckMonitorPosition(m_effectMetaInfo.monitor->render->seekFramePosition());
- m_ui.labelComment->setText(i18n(m_effects.at(i)->effect().firstChildElement("description").firstChildElement("full").text().toUtf8().data()));
- m_ui.labelComment->setHidden(!m_ui.buttonShowComments->isChecked() || m_ui.labelComment->text().isEmpty());
+ if (m_status == TIMELINE_CLIP) {
+ if (m_clipref && ix != m_clipref->selectedEffectIndex()) {
+ m_clipref->setSelectedEffect(ix);
+ for (int i = 0; i < m_effects.count(); ++i) {
+ if (m_effects.at(i)->effectIndex() == ix) {
+ if (m_effects.at(i)->isActive()) return;
+ m_effects.at(i)->setActive(true);
+ m_monitorSceneWanted = m_effects.at(i)->needsMonitorEffectScene();
+ slotCheckMonitorPosition(m_effectMetaInfo.monitor->render->seekFramePosition());
+ m_ui.labelComment->setText(i18n(m_effects.at(i)->effect().firstChildElement("description").firstChildElement("full").text().toUtf8().data()));
+ m_ui.labelComment->setHidden(!m_ui.buttonShowComments->isChecked() || m_ui.labelComment->text().isEmpty());
+ }
+ else m_effects.at(i)->setActive(false);
}
- else m_effects.at(i)->setActive(false);
}
}
}
@@ -610,10 +692,14 @@ void EffectStackView2::slotDeleteGroup(QDomDocument doc)
QDomNodeList effects = doc.elementsByTagName("effect");
ClipItem * clip = NULL;
int ix;
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == MASTER_CLIP) {
+ //TODO
+ return;
+ }
+ if (m_status == TIMELINE_TRACK) {
ix = m_trackindex;
}
- else {
+ else if (m_status == TIMELINE_CLIP) {
clip = m_clipref;
ix = -1;
}
@@ -624,10 +710,13 @@ void EffectStackView2::slotDeleteGroup(QDomDocument doc)
void EffectStackView2::slotDeleteEffect(const QDomElement &effect)
{
- if (m_effectMetaInfo.trackMode)
+ if (m_status == TIMELINE_TRACK)
emit removeEffect(NULL, m_trackindex, effect);
- else
+ else if (m_status == TIMELINE_CLIP)
emit removeEffect(m_clipref, -1, effect);
+ if (m_status == MASTER_CLIP) {
+ emit removeMasterEffect(m_masterclipref->clipId(), effect);
+ }
}
void EffectStackView2::slotAddEffect(const QDomElement &effect)
@@ -646,14 +735,17 @@ void EffectStackView2::slotMoveEffectUp(const QList<int> &indexes, bool up)
else {
endPos = indexes.last() + 1;
}
- if (m_effectMetaInfo.trackMode) emit changeEffectPosition(NULL, m_trackindex, indexes, endPos);
- else emit changeEffectPosition(m_clipref, -1, indexes, endPos);
+ if (m_status == TIMELINE_TRACK) emit changeEffectPosition(NULL, m_trackindex, indexes, endPos);
+ else if (m_status == TIMELINE_CLIP) emit changeEffectPosition(m_clipref, -1, indexes, endPos);
+ else if (m_status == MASTER_CLIP) {
+ //TODO
+ }
}
void EffectStackView2::slotStartFilterJob(QMap <QString, QString> &filterParams, QMap <QString, QString> &consumerParams, QMap <QString, QString> &extraParams)
{
if (!m_clipref) return;
- emit startFilterJob(m_clipref->info(), m_clipref->clipProducer(), filterParams, consumerParams, extraParams);
+ emit startFilterJob(m_clipref->info(), m_clipref->getBinId(), filterParams, consumerParams, extraParams);
}
void EffectStackView2::slotResetEffect(int ix)
@@ -671,7 +763,7 @@ void EffectStackView2::slotResetEffect(int ix)
}
if (!dom.isNull()) {
dom.setAttribute("kdenlive_ix", old.attribute("kdenlive_ix"));
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
EffectsList::setParameter(dom, "in", QString::number(0));
EffectsList::setParameter(dom, "out", QString::number(m_trackInfo.duration));
ItemInfo info;
@@ -687,8 +779,8 @@ void EffectStackView2::slotResetEffect(int ix)
}
}
emit updateEffect(NULL, m_trackindex, old, dom, ix,false);
- } else {
- m_clipref->initEffect(dom);
+ } else if (m_status == TIMELINE_CLIP) {
+ m_clipref->initEffect(m_effectMetaInfo.monitor->profileInfo(), dom);
for (int i = 0; i < m_effects.count(); ++i) {
if (m_effects.at(i)->effectIndex() == ix) {
m_effects.at(i)->updateWidget(m_clipref->info(), dom, &m_effectMetaInfo);
@@ -697,6 +789,8 @@ void EffectStackView2::slotResetEffect(int ix)
}
//m_ui.region_url->setUrl(QUrl(dom.attribute("region")));
emit updateEffect(m_clipref, -1, old, dom, ix,false);
+ } else if (m_status == MASTER_CLIP) {
+ //TODO
}
}
@@ -718,26 +812,33 @@ void EffectStackView2::slotCreateRegion(int ix, QUrl url)
region.appendChild(region.ownerDocument().importNode(neweffect, true));
region.setAttribute("kdenlive_ix", ix);
EffectsList::setParameter(region, "resource", url.path());
- if (m_effectMetaInfo.trackMode)
+ if (m_status == TIMELINE_TRACK) {
emit updateEffect(NULL, m_trackindex, oldeffect, region, ix,false);
- else if (m_clipref) {
+ }
+ else if (m_status == TIMELINE_CLIP && m_clipref) {
emit updateEffect(m_clipref, -1, oldeffect, region, ix, false);
// Make sure the changed effect is currently displayed
//slotSetCurrentEffect(ix);
}
+ else if (m_status == MASTER_CLIP) {
+ //TODO
+ }
// refresh effect stack
ItemInfo info;
bool isSelected = false;
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
info.track = m_trackInfo.type;
info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
info.cropStart = GenTime(0);
info.startPos = GenTime(-1);
info.track = 0;
}
- else if (m_clipref) {
+ else if (m_status == TIMELINE_CLIP && m_clipref) {
info = m_clipref->info();
}
+ else if (m_status == MASTER_CLIP) {
+ //TODO
+ }
CollapsibleEffect *current = getEffectByIndex(ix);
m_effects.removeAll(current);
current->setEnabled(false);
@@ -747,12 +848,15 @@ void EffectStackView2::slotCreateRegion(int ix, QUrl url)
CollapsibleEffect *currentEffect = new CollapsibleEffect(region, m_currentEffectList.itemFromIndex(ix), info, &m_effectMetaInfo, ix == m_currentEffectList.count() - 1, m_ui.container->widget());
connectEffect(currentEffect);
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
isSelected = currentEffect->effectIndex() == 1;
}
- else if (m_clipref) {
+ else if (m_status == TIMELINE_CLIP && m_clipref) {
isSelected = currentEffect->effectIndex() == m_clipref->selectedEffectIndex();
}
+ else if (m_status == MASTER_CLIP) {
+ //TODO
+ }
if (isSelected) currentEffect->setActive(true);
m_effects.append(currentEffect);
// TODO: region in group?
@@ -780,15 +884,17 @@ void EffectStackView2::slotCreateGroup(int ix)
neweffect.setAttribute("kdenlive_info", effectinfo.toString());
ItemInfo info;
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
info.track = m_trackInfo.type;
info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
info.cropStart = GenTime(0);
info.startPos = GenTime(-1);
info.track = 0;
emit updateEffect(NULL, m_trackindex, oldeffect, neweffect, ix, false);
- } else {
+ } else if (m_status == TIMELINE_CLIP) {
emit updateEffect(m_clipref, -1, oldeffect, neweffect, ix, false);
+ } else if (m_status == MASTER_CLIP) {
+ //TODO
}
QVBoxLayout *l = static_cast<QVBoxLayout *>(m_ui.container->widget()->layout());
@@ -841,25 +947,28 @@ void EffectStackView2::slotMoveEffect(QList <int> currentIndexes, int newIndex,
if (oldeffect.attribute("kdenlive_info") != effectinfo.toString()) {
// effect's group info or collapsed state changed
ItemInfo info;
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
info.track = m_trackInfo.type;
info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
info.cropStart = GenTime(0);
info.startPos = GenTime(-1);
info.track = 0;
emit updateEffect(NULL, m_trackindex, oldeffect, neweffect, effectToMove->effectIndex(),false);
- } else {
+ } else if (m_status == TIMELINE_CLIP) {
emit updateEffect(m_clipref, -1, oldeffect, neweffect, effectToMove->effectIndex(),false);
+ } else if (m_status == MASTER_CLIP) {
+ //TODO
}
}
}
// Update effect index with new position
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
emit changeEffectPosition(NULL, m_trackindex, currentIndexes, newIndex);
- }
- else {
+ } else if (m_status == TIMELINE_CLIP) {
emit changeEffectPosition(m_clipref, -1, currentIndexes, newIndex);
+ } else if (m_status == MASTER_CLIP) {
+ //TODO
}
}
@@ -878,10 +987,12 @@ void EffectStackView2::slotRenameGroup(CollapsibleGroup *group)
QDomElement origin = effects.at(i)->effect();
QDomElement changed = origin.cloneNode().toElement();
changed.setAttribute("kdenlive_info", effects.at(i)->infoString());
- if (m_effectMetaInfo.trackMode) {
+ if (m_status == TIMELINE_TRACK) {
emit updateEffect(NULL, m_trackindex, origin, changed, effects.at(i)->effectIndex(),false);
- } else {
+ } else if (m_status == TIMELINE_CLIP) {
emit updateEffect(m_clipref, -1, origin, changed, effects.at(i)->effectIndex(),false);
+ } else if (m_status == MASTER_CLIP) {
+ //TODO
}
}
}
diff --git a/src/effectstack/effectstackview2.h b/src/effectstack/effectstackview2.h
index e66e822..8093b6d 100644
--- a/src/effectstack/effectstackview2.h
+++ b/src/effectstack/effectstackview2.h
@@ -30,6 +30,7 @@
class EffectsList;
class ClipItem;
+class ClipController;
class MltVideoProfile;
class Monitor;
@@ -39,21 +40,22 @@ class EffectStackView2 : public QWidget
Q_OBJECT
public:
- explicit EffectStackView2(Monitor *monitor, QWidget *parent = 0);
+ explicit EffectStackView2(QWidget *parent = 0);
virtual ~EffectStackView2();
/** @brief Raises @param dock if a clip is loaded. */
void raiseWindow(QWidget* dock);
- /** @brief return the index of the track displayed in effect stack
- ** @param ok set to true if we are looking at a track's effects, otherwise false. */
- int isTrackMode(bool *ok) const;
+ /** @brief return the current status of effect stack (timeline clip, track or master clip). */
+ EFFECTMODE effectStatus() const;
+ /** @brief return the index of the track displayed in effect stack */
+ int trackIndex() const;
/** @brief Clears the list of effects and updates the buttons accordingly. */
void clear();
- /** @brief Passes updates on @param profile and @param t on to the effect editor. */
- void updateProjectFormat(const MltVideoProfile &profile, const Timecode &t);
+ /** @brief Passes updates @param t on to the effect editor. */
+ void updateProjectFormat(const Timecode &t);
/** @brief Tells the effect editor to update its timecode format. */
void updateTimecodeFormat();
@@ -88,9 +90,12 @@ protected:
private:
Ui::EffectStack2_UI m_ui;
ClipItem* m_clipref;
+ ClipController *m_masterclipref;
QList <CollapsibleEffect*> m_effects;
EffectsList m_currentEffectList;
-
+ /** @brief Current status of the effect stack (if it contains a timeline clip, track or master clip effect. */
+ EFFECTMODE m_status;
+
/** @brief Contains info about effect like is it a track effect, which monitor displays it,... */
EffectMetaInfo m_effectMetaInfo;
@@ -129,13 +134,15 @@ private:
public slots:
/** @brief Sets the clip whose effect list should be managed.
* @param c Clip whose effect list should be managed */
- void slotClipItemSelected(ClipItem* c);
+ void slotClipItemSelected(ClipItem* c, Monitor *m = NULL);
+
+ void slotMasterClipItemSelected(ClipController* c, Monitor *m = NULL);
/** @brief Update the clip range (in-out points)
* @param c Clip whose effect list should be managed */
void slotClipItemUpdate();
- void slotTrackItemSelected(int ix, const TrackInfo &info);
+ void slotTrackItemSelected(int ix, const TrackInfo &info, Monitor *m = NULL);
/** @brief Check if the mouse wheel events should be used for scrolling the widget view. */
void slotCheckWheelEventFilter();
@@ -213,7 +220,8 @@ private slots:
signals:
void removeEffect(ClipItem*, int, const QDomElement&);
- /** Parameters for an effect changed, update the filter in playlist */
+ void removeMasterEffect(const QString &id, const QDomElement&);
+ /** Parameters for an effect changed, update the filter in timeline */
void updateEffect(ClipItem*, int, const QDomElement&, const QDomElement &, int,bool);
/** An effect in stack was moved, we need to regenerate
all effects for this clip in the playlist */
diff --git a/src/effectstack/geometryval.cpp b/src/effectstack/geometryval.cpp
index e02ec62..89e7f87 100644
--- a/src/effectstack/geometryval.cpp
+++ b/src/effectstack/geometryval.cpp
@@ -28,8 +28,9 @@
#include <QMenu>
#include <QTimer>
+#include <mlt++/Mlt.h>
-Geometryval::Geometryval(const MltVideoProfile &profile, const Timecode &t, const QPoint &frame_size, int startPoint, QWidget* parent) :
+Geometryval::Geometryval(const Mlt::Profile *profile, const Timecode &t, const QPoint &frame_size, int startPoint, QWidget* parent) :
QWidget(parent),
m_profile(profile),
m_paramRect(NULL),
@@ -63,10 +64,10 @@ Geometryval::Geometryval(const MltVideoProfile &profile, const Timecode &t, cons
m_scene = new GraphicsSceneRectMove(this);
m_scene->setTool(TITLE_SELECT);
m_sceneview->setScene(m_scene);
- m_dar = (m_profile.height * m_profile.display_aspect_num / (double) m_profile.display_aspect_den) / (double) m_profile.width;
+ m_dar = (m_profile->height() * m_profile->dar()) / (double) m_profile->width();
- m_realWidth = (int)(profile.height * profile.display_aspect_num / (double) profile.display_aspect_den + 0.5);
- QGraphicsRectItem *frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_realWidth, profile.height));
+ m_realWidth = (int)(profile->height() * profile->dar() + 0.5);
+ QGraphicsRectItem *frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_realWidth, profile->height()));
frameBorder->setZValue(-1100);
frameBorder->setBrush(QColor(255, 255, 0, 30));
frameBorder->setPen(QPen(QBrush(QColor(255, 255, 255, 255)), 1.0, Qt::DashLine));
@@ -97,9 +98,9 @@ Geometryval::Geometryval(const MltVideoProfile &profile, const Timecode &t, cons
m_syncAction->setCheckable(true);
m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
- //scene->setSceneRect(0, 0, profile.width * 2, profile.height * 2);
+ //scene->setSceneRect(0, 0, profile->width * 2, profile->height * 2);
//view->fitInView(m_frameBorder, Qt::KeepAspectRatio);
- const double sc = 100.0 / profile.height * 0.8;
+ const double sc = 100.0 / profile->height() * 0.8;
QRectF srect = m_sceneview->sceneRect();
m_sceneview->setSceneRect(srect.x(), -srect.height() / 3 + 10, srect.width(), srect.height() + srect.height() / 3 * 2 - 10);
m_scene->setZoom(sc);
@@ -167,7 +168,7 @@ void Geometryval::slotAlignVCenter()
{
if (!keyframeSelected())
return;
- m_paramRect->setPos(m_paramRect->pos().x(), (m_profile.height - m_paramRect->rect().height()) / 2);
+ m_paramRect->setPos(m_paramRect->pos().x(), (m_profile->height() - m_paramRect->rect().height()) / 2);
slotUpdateTransitionProperties();
}
@@ -183,7 +184,7 @@ void Geometryval::slotAlignBottom()
{
if (!keyframeSelected())
return;
- m_paramRect->setPos(m_paramRect->pos().x(), m_profile.height - m_paramRect->rect().height());
+ m_paramRect->setPos(m_paramRect->pos().x(), m_profile->height() - m_paramRect->rect().height());
slotUpdateTransitionProperties();
}
@@ -208,7 +209,7 @@ void Geometryval::slotResizeOriginal()
if (!keyframeSelected())
return;
if (m_frameSize.isNull())
- m_paramRect->setRect(0, 0, m_realWidth, m_profile.height);
+ m_paramRect->setRect(0, 0, m_realWidth, m_profile->height());
else
m_paramRect->setRect(0, 0, m_frameSize.x(), m_frameSize.y());
slotUpdateTransitionProperties();
@@ -219,7 +220,7 @@ void Geometryval::slotResizeCustom()
if (!keyframeSelected())
return;
int value = spinResize->value();
- m_paramRect->setRect(0, 0, m_realWidth * value / 100, m_profile.height * value / 100);
+ m_paramRect->setRect(0, 0, m_realWidth * value / 100, m_profile->height() * value / 100);
slotUpdateTransitionProperties();
}
@@ -385,9 +386,9 @@ void Geometryval::setupParam(const QDomElement par, int minFrame, int maxFrame)
spinTransp->setHidden(true);
}
if (m_geom)
- m_geom->parse(val.toUtf8().data(), maxFrame - minFrame, m_profile.width, m_profile.height);
+ m_geom->parse(val.toUtf8().data(), maxFrame - minFrame, m_profile->width(), m_profile->height());
else
- m_geom = new Mlt::Geometry(val.toUtf8().data(), maxFrame - minFrame, m_profile.width, m_profile.height);
+ m_geom = new Mlt::Geometry(val.toUtf8().data(), maxFrame - minFrame, m_profile->width(), m_profile->height());
////qDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
//read param her and set rect
@@ -482,7 +483,7 @@ void Geometryval::slotResetPosition()
if (m_frameSize.isNull()) {
spinWidth->setValue(m_realWidth);
- spinHeight->setValue(m_profile.height);
+ spinHeight->setValue(m_profile->height());
} else {
spinWidth->setValue(m_frameSize.x());
spinHeight->setValue(m_frameSize.y());
diff --git a/src/effectstack/geometryval.h b/src/effectstack/geometryval.h
index a7142bd..82d7e71 100644
--- a/src/effectstack/geometryval.h
+++ b/src/effectstack/geometryval.h
@@ -34,12 +34,16 @@ class GraphicsSceneRectMove;
class QGraphicsRectItem;
class QGraphicsView;
+namespace Mlt {
+ class Profile;
+}
+
class Geometryval : public QWidget, public Ui::Geometryval
{
Q_OBJECT
public:
- explicit Geometryval(const MltVideoProfile &profile, const Timecode &t, const QPoint &frame_size, int startPoint = 0, QWidget* parent = 0);
+ explicit Geometryval(const Mlt::Profile *profile, const Timecode &t, const QPoint &frame_size, int startPoint = 0, QWidget* parent = 0);
virtual ~Geometryval();
QDomElement getParamDesc();
QString getValue() const;
@@ -49,7 +53,7 @@ public:
void slotUpdateRange(int inPoint, int outPoint);
private:
- MltVideoProfile m_profile;
+ const Mlt::Profile *m_profile;
int m_realWidth;
GraphicsSceneRectMove *m_scene;
QGraphicsRectItem *m_paramRect;
diff --git a/src/effectstack/keyframeedit.cpp b/src/effectstack/keyframeedit.cpp
index 9f28313..a294efc 100644
--- a/src/effectstack/keyframeedit.cpp
+++ b/src/effectstack/keyframeedit.cpp
@@ -36,6 +36,7 @@ KeyframeEdit::KeyframeEdit(const QDomElement &e, int minFrame, int maxFrame, con
// special case: keyframe for tracks, do not allow keyframes
widgetTable->setHidden(true);
}
+ setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
keyframe_list->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
buttonSeek->setChecked(KdenliveSettings::keyframeseek());
connect(buttonSeek, SIGNAL(toggled(bool)), this, SLOT(slotSetSeeking(bool)));
@@ -154,6 +155,8 @@ void KeyframeEdit::addParameter(const QDomElement &e, int activeKeyframe)
}
keyframe_list->resizeColumnsToContents();
keyframe_list->blockSignals(false);
+ keyframe_list->horizontalHeader()->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
+ keyframe_list->verticalHeader()->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
slotAdjustKeyframeInfo(false);
button_delete->setEnabled(keyframe_list->rowCount() > 1);
}
diff --git a/src/effectstack/keyframehelper.cpp b/src/effectstack/keyframehelper.cpp
index dbaa846..f5ca282 100644
--- a/src/effectstack/keyframehelper.cpp
+++ b/src/effectstack/keyframehelper.cpp
@@ -316,7 +316,10 @@ void KeyframeHelper::paintEvent(QPaintEvent *e)
int KeyframeHelper::value() const
{
- return m_position;
+ if (m_seekPosition == SEEK_INACTIVE) {
+ return m_position;
+ }
+ return m_seekPosition;
}
void KeyframeHelper::setValue(const int pos)
diff --git a/src/effectstack/parametercontainer.cpp b/src/effectstack/parametercontainer.cpp
index 33742eb..0cf3684 100644
--- a/src/effectstack/parametercontainer.cpp
+++ b/src/effectstack/parametercontainer.cpp
@@ -30,13 +30,13 @@
#include "widgets/doubleparameterwidget.h"
#include "widgets/cornerswidget.h"
#include "widgets/bezier/beziersplinewidget.h"
+#include "effectstack/widgets/lumaliftgain.h"
#include "kdenlivesettings.h"
#include "mainwindow.h"
#include "colortools.h"
-#include "dialogs/profilesdialog.h"
-#include "project/projectlist.h"
-#include "timeline/customtrackview.h"
+#include "dialogs/clipcreationdialog.h"
+#include "mltcontroller/effectscontroller.h"
#include "onmonitoritems/rotoscoping/rotowidget.h"
#include "ui_listval_ui.h"
@@ -123,7 +123,14 @@ ParameterContainer::ParameterContainer(const QDomElement &effect, const ItemInfo
m_vbox->setContentsMargins(4, 0, 4, 0);
m_vbox->setSpacing(2);
- for (int i = 0; i < namenode.count() ; ++i) {
+ if (effect.attribute("id") == "movit.lift_gamma_gain" || effect.attribute("id") == "lift_gamma_gain" ) {
+ // We use a special custom widget here
+ LumaLiftGain *gainWidget = new LumaLiftGain(namenode, parent);
+ m_vbox->addWidget(gainWidget);
+ m_valueItems[effect.attribute("id")] = gainWidget;
+ connect(gainWidget, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
+ }
+ else for (int i = 0; i < namenode.count() ; ++i) {
QDomElement pa = namenode.item(i).toElement();
if (pa.tagName() != "parameter") continue;
QDomElement na = pa.firstChildElement("name");
@@ -144,11 +151,11 @@ ParameterContainer::ParameterContainer(const QDomElement &effect, const ItemInfo
double min;
double max;
if (pa.attribute("min").contains('%'))
- min = ProfilesDialog::getStringEval(m_metaInfo->profile, pa.attribute("min"), m_metaInfo->frameSize);
+ min = EffectsController::getStringEval(m_metaInfo->monitor->profileInfo(), pa.attribute("min"), m_metaInfo->frameSize);
else
min = pa.attribute("min").toDouble();
if (pa.attribute("max").contains('%'))
- max = ProfilesDialog::getStringEval(m_metaInfo->profile, pa.attribute("max"), m_metaInfo->frameSize);
+ max = EffectsController::getStringEval(m_metaInfo->monitor->profileInfo(), pa.attribute("max"), m_metaInfo->frameSize);
else
max = pa.attribute("max").toDouble();
@@ -219,9 +226,9 @@ ParameterContainer::ParameterContainer(const QDomElement &effect, const ItemInfo
m_valueItems[paramName+"complex"] = pl;
connect(pl, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
} else if (type == "geometry") {
- if (KdenliveSettings::on_monitor_effects()) {
+ if (true /*KdenliveSettings::on_monitor_effects()*/) {
m_needsMonitorEffectScene = true;
- m_geometryWidget = new GeometryWidget(m_metaInfo->monitor, m_metaInfo->timecode, 0, effect.hasAttribute("showrotation"), parent);
+ m_geometryWidget = new GeometryWidget(m_metaInfo->monitor, m_metaInfo->timecode, info.startPos.frames(KdenliveSettings::project_fps()), effect.hasAttribute("showrotation"), parent);
m_geometryWidget->setFrameSize(m_metaInfo->frameSize);
connect(m_geometryWidget, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
if (minFrame == maxFrame) {
@@ -236,7 +243,7 @@ ParameterContainer::ParameterContainer(const QDomElement &effect, const ItemInfo
connect(m_geometryWidget, SIGNAL(importClipKeyframes()), this, SIGNAL(importClipKeyframes()));
connect(this, SIGNAL(syncEffectsPos(int)), m_geometryWidget, SLOT(slotSyncPosition(int)));
} else {
- Geometryval *geo = new Geometryval(m_metaInfo->profile, m_metaInfo->timecode, m_metaInfo->frameSize, 0);
+ Geometryval *geo = new Geometryval(m_metaInfo->monitor->profile(), m_metaInfo->timecode, m_metaInfo->frameSize, 0);
if (minFrame == maxFrame) {
geo->setupParam(pa, m_in, m_out);
connect(this, SIGNAL(updateRange(int,int)), geo, SLOT(slotUpdateRange(int,int)));
@@ -417,7 +424,7 @@ ParameterContainer::ParameterContainer(const QDomElement &effect, const ItemInfo
cval->setupUi(toFillin);
cval->label->setText(paramName);
cval->setToolTip(comment);
- cval->urlwidget->setFilter(ProjectList::getExtensions().join(' '));
+ cval->urlwidget->setFilter(ClipCreationDialog::getExtensions().join(' '));
m_valueItems[paramName] = cval;
cval->urlwidget->setUrl(QUrl(value));
connect(cval->urlwidget, SIGNAL(returnPressed()) , this, SLOT(slotCollectAllParameters()));
@@ -593,6 +600,14 @@ void ParameterContainer::slotCollectAllParameters()
locale.setNumberOptions(QLocale::OmitGroupSeparator);
const QDomElement oldparam = m_effect.cloneNode().toElement();
//QDomElement newparam = oldparam.cloneNode().toElement();
+
+ if (m_effect.attribute("id") == "movit.lift_gamma_gain" || m_effect.attribute("id") == "lift_gamma_gain" ) {
+ LumaLiftGain *gainWidget = ((LumaLiftGain*)m_valueItems.value(m_effect.attribute("id")));
+ gainWidget->updateEffect(m_effect);
+ emit parameterChanged(oldparam, m_effect, m_effect.attribute("kdenlive_ix").toInt());
+ return;
+ }
+
QDomNodeList namenode = m_effect.elementsByTagName("parameter");
for (int i = 0; i < namenode.count() ; ++i) {
@@ -631,12 +646,12 @@ void ParameterContainer::slotCollectAllParameters()
ComplexParameter *complex = static_cast<ComplexParameter*>(m_valueItems.value(paramName));
namenode.item(i) = complex->getParamDesc();
} else if (type == "geometry") {
- if (KdenliveSettings::on_monitor_effects()) {
+ /*if (KdenliveSettings::on_monitor_effects())*/ {
if (m_geometryWidget) namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getValue());
- } else {
+ }/* else {
Geometryval *geom = static_cast<Geometryval*>(m_valueItems.value(paramName));
namenode.item(i).toElement().setAttribute("value", geom->getValue());
- }
+ }*/
} else if (type == "addedgeometry") {
if (m_geometryWidget) namenode.item(i).toElement().setAttribute("value", m_geometryWidget->getExtraValue(namenode.item(i).toElement().attribute("name")));
} else if (type == "position") {
@@ -839,7 +854,7 @@ void ParameterContainer::slotStartFilterJobAction()
// Replace with current geometry
EffectsParameterList parameters;
QDomNodeList params = m_effect.elementsByTagName("parameter");
- CustomTrackView::adjustEffectParameters(parameters, params, m_metaInfo->profile);
+ EffectsController::adjustEffectParameters(parameters, params, m_metaInfo->monitor->profileInfo());
QString paramData;
for (int j = 0; j < parameters.count(); ++j) {
filterParams.insert(parameters.at(j).name(), parameters.at(j).value());
diff --git a/src/effectstack/parametercontainer.h b/src/effectstack/parametercontainer.h
index 26283f0..69d2b4f 100644
--- a/src/effectstack/parametercontainer.h
+++ b/src/effectstack/parametercontainer.h
@@ -25,12 +25,22 @@
class GeometryWidget;
class Monitor;
+namespace Mlt {
+ class Profile;
+}
+
+enum EFFECTMODE {
+ EMPTY = 0,
+ TIMELINE_CLIP,
+ TIMELINE_TRACK,
+ MASTER_CLIP,
+};
+
struct EffectMetaInfo {
- MltVideoProfile profile;
Timecode timecode;
Monitor *monitor;
QPoint frameSize;
- bool trackMode;
+ EFFECTMODE status;
};
enum WIPE_DIRECTON { UP = 0, DOWN = 1, LEFT = 2, RIGHT = 3, CENTER = 4 };
diff --git a/src/effectstack/widgets/choosecolorwidget.cpp b/src/effectstack/widgets/choosecolorwidget.cpp
index 946121f..9ade2b0 100644
--- a/src/effectstack/widgets/choosecolorwidget.cpp
+++ b/src/effectstack/widgets/choosecolorwidget.cpp
@@ -112,7 +112,7 @@ ChooseColorWidget::ChooseColorWidget(const QString &text, const QString &color,
connect(picker, SIGNAL(colorPicked(QColor)), this, SLOT(setColor(QColor)));
connect(picker, SIGNAL(displayMessage(QString,int)), this, SIGNAL(displayMessage(QString,int)));
connect(picker, SIGNAL(disableCurrentFilter(bool)), this, SIGNAL(disableCurrentFilter(bool)));
- connect(m_button, SIGNAL(changed(QColor)), this, SIGNAL(modified()));
+ connect(m_button, SIGNAL(changed(QColor)), this, SIGNAL(modified(QColor)));
}
QString ChooseColorWidget::getColor() const
@@ -127,4 +127,9 @@ void ChooseColorWidget::setColor(const QColor& color)
m_button->setColor(color);
}
-
+void ChooseColorWidget::slotColorModified(const QColor &color)
+{
+ blockSignals(true);
+ m_button->setColor(color);
+ blockSignals(false);
+}
diff --git a/src/effectstack/widgets/choosecolorwidget.h b/src/effectstack/widgets/choosecolorwidget.h
index 86c4d78..3773df7 100644
--- a/src/effectstack/widgets/choosecolorwidget.h
+++ b/src/effectstack/widgets/choosecolorwidget.h
@@ -47,13 +47,16 @@ public:
private:
KColorButton *m_button;
+public slots:
+ void slotColorModified(const QColor &color);
+
private slots:
/** @brief Updates the different color choosing options to have all selected @param color. */
void setColor(const QColor &color);
signals:
/** @brief Emitted whenever a different color was chosen. */
- void modified();
+ void modified(QColor = QColor());
void displayMessage(const QString&, int);
/** @brief When user wants to pick a color, it's better to disable filter so we get proper color values. */
void disableCurrentFilter(bool);
diff --git a/src/effectstack/widgets/colorwheel.cpp b/src/effectstack/widgets/colorwheel.cpp
new file mode 100644
index 0000000..d3d702e
--- /dev/null
+++ b/src/effectstack/widgets/colorwheel.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2013 Meltytech, LLC
+ * Author: Dan Dennedy <[email protected]>
+ * Some ideas came from Qt-Plus: https://github.com/liuyanghejerry/Qt-Plus
+ * and Steinar Gunderson's Movit demo app.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "colorwheel.h"
+#include <qmath.h>
+
+ColorWheel::ColorWheel(QString id, QString name, QColor color, QWidget *parent)
+ : QWidget(parent)
+ , m_id(id)
+ , m_name(name)
+ , m_isMouseDown(false)
+ , m_margin(5)
+ , m_color(color)
+ , m_isInWheel(false)
+ , m_isInSquare(false)
+{
+ QFontInfo info(font());
+ m_unitSize = info.pixelSize();
+ m_initialSize = QSize(m_unitSize * 11.5, m_unitSize * 11);
+ m_sliderWidth = m_unitSize * 1.5;
+ resize(m_initialSize);
+ setMinimumSize(100, 100);
+ setMaximumSize(m_initialSize);
+ setCursor(Qt::CrossCursor);
+}
+
+QColor ColorWheel::color()
+{
+ return m_color;
+}
+
+void ColorWheel::setColor(const QColor& color)
+{
+ m_color = color;
+}
+
+int ColorWheel::wheelSize() const
+{
+ return qMin(width() - m_sliderWidth, height() - m_unitSize);
+}
+
+QColor ColorWheel::colorForPoint(const QPoint &point)
+{
+ if (! m_image.valid(point)) return QColor();
+ if (m_isInWheel) {
+ qreal w = wheelSize();
+ qreal xf = qreal(point.x()) / w;
+ qreal yf = 1.0 - qreal(point.y()) / w;
+ qreal xp = 2.0 * xf - 1.0;
+ qreal yp = 2.0 * yf - 1.0;
+ qreal rad = qMin(hypot(xp, yp), 1.0);
+ qreal theta = qAtan2(yp, xp);
+ theta -= 105.0 / 360.0 * 2.0 * M_PI;
+ if (theta < 0.0)
+ theta += 2.0 * M_PI;
+ qreal hue = (theta * 180.0 / M_PI) / 360.0;
+ return QColor::fromHsvF( hue, rad, m_color.valueF() );
+ }
+ if (m_isInSquare) {
+ qreal value = 1.0 - qreal(point.y() - m_margin) / (wheelSize() - m_margin * 2);
+ return QColor::fromHsvF( m_color.hueF(), m_color.saturationF(), value);
+ }
+ return QColor();
+}
+
+QSize ColorWheel::sizeHint () const
+{
+ return QSize(width(),height());
+}
+QSize ColorWheel::minimumSizeHint () const
+{
+ return QSize(100, 100);
+}
+
+void ColorWheel::mousePressEvent(QMouseEvent *event)
+{
+ m_lastPoint = event->pos();
+ if (m_wheelRegion.contains(m_lastPoint)) {
+ m_isInWheel = true;
+ m_isInSquare = false;
+ if (event->button() != Qt::MidButton) {
+ changeColor(colorForPoint(m_lastPoint));
+ }
+ else {
+ // reset to default on middle button
+ qreal r = m_color.redF();
+ qreal b = m_color.blueF();
+ qreal g = m_color.greenF();
+ qreal max = qMax(r, b);
+ max = qMax(max, g);
+ changeColor(QColor::fromRgbF(max, max, max));
+ }
+ } else if (m_sliderRegion.contains(m_lastPoint)) {
+ m_isInWheel = false;
+ m_isInSquare = true;
+ if (event->button() != Qt::MidButton) {
+ changeColor(colorForPoint(m_lastPoint));
+ }
+ else {
+ QColor c;
+ if (m_id == "lift") {
+ c = QColor::fromRgbF(0, 0, 0);
+ }
+ else if (m_id == "gamma") {
+ c = QColor::fromRgbF(0.5, 0.5, 0.5);
+ }
+ else {
+ c = QColor::fromRgbF(0.25, 0.25, 0.25);
+ }
+ changeColor(c);
+ }
+ }
+ m_isMouseDown = true;
+}
+
+void ColorWheel::mouseMoveEvent(QMouseEvent *event)
+{
+ m_lastPoint = event->pos();
+ if (!m_isMouseDown) return;
+ if (m_wheelRegion.contains(m_lastPoint) && m_isInWheel) {
+ QColor color = colorForPoint(m_lastPoint);
+ changeColor(color);
+ } else if(m_sliderRegion.contains(m_lastPoint) && m_isInSquare) {
+ QColor color = colorForPoint(m_lastPoint);
+ changeColor(color);
+ }
+}
+
+void ColorWheel::mouseReleaseEvent(QMouseEvent *event)
+{
+ m_isMouseDown = false;
+ m_isInWheel = false;
+ m_isInSquare = false;
+}
+
+void ColorWheel::resizeEvent(QResizeEvent *event)
+{
+ m_image = QImage(event->size(), QImage::Format_ARGB32_Premultiplied);
+ m_image.fill(palette().background().color().rgb());
+
+ drawWheel();
+ drawSlider();
+ update();
+}
+
+QString ColorWheel::getParamValues()
+{
+ if (m_id == "gamma") {
+ return QString::number(m_color.redF() * 2, 'g', 2) + "," + QString::number(m_color.greenF() * 2, 'g', 2) + "," + QString::number(m_color.blueF() * 2, 'g', 2);
+ }
+ else if (m_id == "gain") {
+ return QString::number(m_color.redF() * 4, 'g', 2) + "," + QString::number(m_color.greenF() * 4, 'g', 2) + "," + QString::number(m_color.blueF() * 4, 'g', 2);
+ }
+ // default (lift)
+ return QString::number(m_color.redF(), 'g', 2) + "," + QString::number(m_color.greenF(), 'g', 2) + "," + QString::number(m_color.blueF(), 'g', 2);
+
+}
+
+void ColorWheel::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(this);
+// QStyleOption opt;
+// opt.init(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.drawImage(0, 0, m_image);
+ //painter.drawRect(0, 0, width(), height());
+ painter.drawText(m_margin, wheelSize() + m_unitSize - m_margin, m_name + " " + getParamValues());
+ drawWheelDot(painter);
+ drawSliderBar(painter);
+// style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+}
+
+void ColorWheel::drawWheel()
+{
+ int r = wheelSize();
+ QPainter painter(&m_image);
+ painter.setRenderHint(QPainter::Antialiasing);
+ m_image.fill(0); // transparent
+
+ QConicalGradient conicalGradient;
+ conicalGradient.setColorAt( 0.0, Qt::red);
+ conicalGradient.setColorAt( 60.0/360.0, Qt::yellow);
+ conicalGradient.setColorAt(135.0/360.0, Qt::green);
+ conicalGradient.setColorAt(180.0/360.0, Qt::cyan);
+ conicalGradient.setColorAt(240.0/360.0, Qt::blue);
+ conicalGradient.setColorAt(315.0/360.0, Qt::magenta);
+ conicalGradient.setColorAt( 1.0, Qt::red);
+
+ QRadialGradient radialGradient(0.0, 0.0, r/2);
+ radialGradient.setColorAt(0.0, Qt::white);
+ radialGradient.setColorAt(1.0, Qt::transparent);
+
+ painter.translate(r / 2, r / 2 );
+ painter.rotate(-105);
+
+ QBrush hueBrush(conicalGradient);
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(hueBrush);
+ painter.drawEllipse(QPoint(0, 0), r/2-m_margin, r/2-m_margin);
+
+ QBrush saturationBrush(radialGradient);
+ painter.setBrush(saturationBrush);
+ painter.drawEllipse(QPoint(0, 0), r/2-m_margin, r/2-m_margin);
+
+ m_wheelRegion = QRegion(r/2, r/2, r-2*m_margin, r-2*m_margin, QRegion::Ellipse);
+ m_wheelRegion.translate(-(r-2*m_margin)/2, -(r-2*m_margin)/2);
+}
+
+void ColorWheel::drawSlider()
+{
+ QPainter painter(&m_image);
+ painter.setRenderHint(QPainter::Antialiasing);
+ int ws = wheelSize();
+ qreal scale = qreal(ws + m_sliderWidth) / maximumWidth();
+ int w = m_sliderWidth * scale;
+ int h = ws - m_margin * 2;
+ QLinearGradient gradient(0, 0, w, h);
+ gradient.setColorAt(0.0, Qt::white);
+ gradient.setColorAt(1.0, Qt::black);
+ QBrush brush(gradient);
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(brush);
+ painter.translate(ws, m_margin);
+ painter.drawRect(0, 0, w, h);
+ m_sliderRegion = QRegion(ws, m_margin, w, h);
+}
+
+void ColorWheel::drawWheelDot(QPainter& painter)
+{
+ int r = wheelSize() / 2;
+ QPen pen(Qt::white);
+ pen.setWidth(2);
+ painter.setPen(pen);
+ painter.setBrush(Qt::black);
+ painter.translate(r, r);
+ painter.rotate(360.0 - m_color.hue());
+ painter.rotate(-105);
+// r -= margin;
+ painter.drawEllipse(QPointF(m_color.saturationF() * r, 0.0), 4, 4);
+ painter.resetTransform();
+}
+
+void ColorWheel::drawSliderBar(QPainter &painter)
+{
+ qreal value = 1.0 - m_color.valueF();
+ int ws = wheelSize();
+ qreal scale = qreal(ws + m_sliderWidth) / maximumWidth();
+ int w = m_sliderWidth * scale;
+ int h = ws - m_margin * 2;
+ QPen pen(Qt::white);
+ pen.setWidth(2);
+ painter.setPen(pen);
+ painter.setBrush(Qt::black);
+ painter.translate(ws, m_margin + value * h);
+ painter.drawRect(0, 0, w, 4);
+ painter.resetTransform();
+}
+
+void ColorWheel::changeColor(const QColor &color)
+{
+ m_color = color;
+ drawWheel();
+ drawSlider();
+ update();
+ emit colorChange(m_color);
+}
diff --git a/src/effectstack/widgets/colorwheel.h b/src/effectstack/widgets/colorwheel.h
new file mode 100644
index 0000000..b6734b9
--- /dev/null
+++ b/src/effectstack/widgets/colorwheel.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2013 Meltytech, LLC
+ * Author: Dan Dennedy <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef COLORWHEEL_H
+#define COLORWHEEL_H
+
+#include <QWidget>
+#include <QPainter>
+#include <QResizeEvent>
+
+class ColorWheel : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit ColorWheel(QString id, QString name, QColor color, QWidget *parent = 0);
+
+ virtual QSize sizeHint () const;
+ virtual QSize minimumSizeHint () const;
+ QColor color();
+ void setColor(const QColor &color);
+
+signals:
+ void colorChange(const QColor &color);
+
+public slots:
+ void changeColor(const QColor &color);
+
+protected:
+ void mousePressEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ void paintEvent(QPaintEvent *event);
+
+private:
+ QString m_id;
+ QSize m_initialSize;
+ QImage m_image;
+ bool m_isMouseDown;
+ QPoint m_lastPoint;
+ int m_margin;
+ int m_sliderWidth;
+ QRegion m_wheelRegion;
+ QRegion m_sliderRegion;
+ QColor m_color;
+ bool m_isInWheel;
+ bool m_isInSquare;
+ int m_unitSize;
+ QString m_name;
+
+ int wheelSize() const;
+ QColor colorForPoint(const QPoint &point);
+ void drawWheel();
+ void drawWheelDot(QPainter &painter);
+ void drawSliderBar(QPainter &painter);
+ void drawSlider();
+ QString getParamValues();
+};
+
+#endif // COLORWHEEL_H
diff --git a/src/effectstack/widgets/cornerswidget.cpp b/src/effectstack/widgets/cornerswidget.cpp
index 972e378..ef90d21 100644
--- a/src/effectstack/widgets/cornerswidget.cpp
+++ b/src/effectstack/widgets/cornerswidget.cpp
@@ -35,12 +35,13 @@ inline int lerp( const int a, const int b, double t )
return a + (b - a) * t;
}
+//TODO: port to new qml monitor edit
CornersWidget::CornersWidget(Monitor *monitor, const QDomElement& e, int minFrame, int maxFrame, const Timecode &tc, int activeKeyframe, QWidget* parent) :
KeyframeEdit(e, minFrame, maxFrame, tc, activeKeyframe, parent),
m_monitor(monitor),
m_pos(0)
{
- MonitorEditWidget *edit = monitor->getEffectEdit();
+ MonitorEditWidget *edit = NULL; //monitor->getEffectEdit();
m_scene = edit->getScene();
m_scene->cleanup();
@@ -66,8 +67,8 @@ CornersWidget::~CornersWidget()
m_scene->removeItem(m_item);
delete m_item;
if (m_monitor) {
- MonitorEditWidget *edit = m_monitor->getEffectEdit();
- edit->removeCustomControls();
+ /*MonitorEditWidget *edit = m_monitor->getEffectEdit();
+ edit->removeCustomControls();*/
}
}
diff --git a/src/effectstack/widgets/geometrywidget.cpp b/src/effectstack/widgets/geometrywidget.cpp
index c2f627f..cb19c66 100644
--- a/src/effectstack/widgets/geometrywidget.cpp
+++ b/src/effectstack/widgets/geometrywidget.cpp
@@ -45,19 +45,19 @@ GeometryWidget::GeometryWidget(Monitor* monitor, const Timecode &timecode, int c
m_clipPos(clipPos),
m_inPoint(0),
m_outPoint(1),
- m_rect(NULL),
m_geomPath(NULL),
m_previous(NULL),
- m_geometry(NULL)
+ m_geometry(NULL),
+ m_fixedGeom(false)
{
m_ui.setupUi(this);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
- MonitorEditWidget *edit = monitor->getEffectEdit();
+ /*MonitorEditWidget *edit = monitor->getEffectEdit();
edit->removeCustomControls();
edit->addCustomButton(QIcon::fromTheme("draw-path"), i18n("Show path"), this, SLOT(slotShowPath(bool)), true, KdenliveSettings::onmonitoreffects_geometryshowpath());
edit->addCustomButton(QIcon::fromTheme("transform-crop"), i18n("Show previous keyframe"), this, SLOT(slotShowPreviousKeyFrame(bool)), true, KdenliveSettings::onmonitoreffects_geometryshowprevious());
m_scene = edit->getScene();
- m_scene->cleanup();
+ m_scene->cleanup();*/
/*
Setup of timeline and keyframe controls
@@ -251,13 +251,12 @@ GeometryWidget::GeometryWidget(Monitor* monitor, const Timecode &timecode, int c
Setup of configuration controls
*/
- connect(m_scene, SIGNAL(addKeyframe()), this, SLOT(slotAddKeyframe()));
+ connect(m_monitor, SIGNAL(addKeyframe()), this, SLOT(slotAddKeyframe()));
connect(this, SIGNAL(parameterChanged()), this, SLOT(slotUpdateProperties()));
}
GeometryWidget::~GeometryWidget()
{
- m_scene->setEnabled(false);
delete m_timePos;
delete m_timeline;
delete m_spinX;
@@ -265,14 +264,10 @@ GeometryWidget::~GeometryWidget()
delete m_spinWidth;
delete m_spinHeight;
delete m_opacity;
- if (m_rect) {
- m_scene->removeItem(m_rect);
- delete m_rect;
- }
- if (m_geomPath) {
+ /*if (m_geomPath) {
m_scene->removeItem(m_geomPath);
delete m_geomPath;
- }
+ }*/
delete m_previous;
delete m_geometry;
m_extraGeometryNames.clear();
@@ -293,8 +288,8 @@ void GeometryWidget::slotShowPath(bool show)
{
KdenliveSettings::setOnmonitoreffects_geometryshowpath(show);
if (m_geomPath) {
- if (show) m_scene->addItem(m_geomPath);
- else m_scene->removeItem(m_geomPath);
+ /*if (show) m_scene->addItem(m_geomPath);
+ else m_scene->removeItem(m_geomPath);*/
}
slotPositionChanged(-1, false);
}
@@ -306,7 +301,11 @@ void GeometryWidget::updateTimecodeFormat()
QString GeometryWidget::getValue() const
{
- return m_geometry->serialise();
+ QString result = m_geometry->serialise();
+ if (!m_fixedGeom && result.contains(";") && !result.section(";",0,0).contains("=")) {
+ result.prepend("0=");
+ }
+ return result;
}
QString GeometryWidget::getExtraValue(const QString &name) const
@@ -337,8 +336,10 @@ void GeometryWidget::setupParam(const QDomElement &elem, int minframe, int maxfr
if (elem.attribute("fixed") == "1" || maxframe < minframe) {
// Keyframes are disabled
+ m_fixedGeom = true;
m_ui.widgetTimeWrapper->setHidden(true);
} else {
+ m_fixedGeom = false;
m_ui.widgetTimeWrapper->setHidden(false);
m_timeline->setKeyGeometry(m_geometry, m_outPoint - m_inPoint);
}
@@ -351,28 +352,27 @@ void GeometryWidget::setupParam(const QDomElement &elem, int minframe, int maxfr
}
Mlt::GeometryItem item;
- m_geometry->fetch(&item, 0);
- if (m_rect) {
+ m_geometry->fetch(&item, m_monitor->render->seekFramePosition() - m_clipPos);
+ /*if (m_rect) {
m_scene->removeItem(m_rect);
delete m_rect;
- }
- if (m_geomPath) {
+ }*/
+ /*if (m_geomPath) {
m_scene->removeItem(m_geomPath);
delete m_geomPath;
- }
- m_rect = new OnMonitorRectItem(QRectF(0, 0, item.w(), item.h()), m_monitor->render->dar());
- m_rect->setPos(item.x(), item.y());
- m_rect->setZValue(0);
- m_scene->addItem(m_rect);
- connect(m_rect, SIGNAL(changed()), this, SLOT(slotUpdateGeometry()));
- m_geomPath = new OnMonitorPathItem();
+ }*/
+
+ m_monitor->slotShowEffectScene(true);
+ m_monitor->setUpEffectGeometry(QRect(item.x(), item.y(), item.w(), item.h()), calculateCenters());
+ connect(m_monitor, SIGNAL(effectChanged(QRect)), this, SLOT(slotUpdateGeometry(QRect)));
+ /*m_geomPath = new OnMonitorPathItem();
connect(m_geomPath, SIGNAL(changed()), this, SLOT(slotUpdatePath()));
m_geomPath->setPen(QPen(Qt::red));
m_geomPath->setPoints(m_geometry);
if (KdenliveSettings::onmonitoreffects_geometryshowpath())
m_scene->addItem(m_geomPath);
- m_scene->centerView();
- slotPositionChanged(0, false);
+ m_scene->centerView();*/
+ slotPositionChanged(m_monitor->render->seekFramePosition() - m_clipPos, false);
}
void GeometryWidget::addParameter(const QDomElement &elem)
@@ -389,8 +389,9 @@ void GeometryWidget::slotSyncPosition(int relTimelinePos)
// do only sync if this effect is keyframable
if (m_timePos->maximum() > 0 && KdenliveSettings::transitionfollowcursor()) {
relTimelinePos = qBound(0, relTimelinePos, m_timePos->maximum());
- if (relTimelinePos != m_timePos->getValue())
+ if (relTimelinePos != m_timePos->getValue()) {
slotPositionChanged(relTimelinePos, false);
+ }
}
}
@@ -402,7 +403,7 @@ int GeometryWidget::currentPosition() const
void GeometryWidget::slotRequestSeek(int pos)
{
if (KdenliveSettings::transitionfollowcursor())
- emit seekToPos(m_clipPos + pos);
+ emit seekToPos(pos);
}
@@ -419,25 +420,27 @@ void GeometryWidget::slotPositionChanged(int pos, bool seek)
Mlt::GeometryItem item;
Mlt::GeometryItem previousItem;
- if (m_geometry->fetch(&item, pos) || item.key() == false) {
+ if (!m_fixedGeom && (m_geometry->fetch(&item, pos) || item.key() == false)) {
// no keyframe
- m_rect->setEnabled(false);
- m_scene->setEnabled(false);
+ m_monitor->setEffectKeyframe(false);
+ //m_rect->setEnabled(false);
+ //m_scene->setEnabled(false);
m_ui.widgetGeometry->setEnabled(false);
m_ui.buttonAddDelete->setIcon(QIcon::fromTheme("list-add"));
m_ui.buttonAddDelete->setToolTip(i18n("Add keyframe"));
} else {
// keyframe
- m_rect->setEnabled(true);
- m_scene->setEnabled(true);
+ m_monitor->setEffectKeyframe(true);
+ //m_rect->setEnabled(true);
+ //m_scene->setEnabled(true);
m_ui.widgetGeometry->setEnabled(true);
m_ui.buttonAddDelete->setIcon(QIcon::fromTheme("list-remove"));
m_ui.buttonAddDelete->setToolTip(i18n("Delete keyframe"));
}
- if (KdenliveSettings::onmonitoreffects_geometryshowprevious() == false || m_geometry->prev_key(&previousItem, pos - 1) || previousItem.frame() == item.frame()) {
+ /*if (KdenliveSettings::onmonitoreffects_geometryshowprevious() == false || m_geometry->prev_key(&previousItem, pos - 1) || previousItem.frame() == item.frame()) {
if (m_previous) {
- m_scene->removeItem(m_previous);
+ //m_scene->removeItem(m_previous);
}
}
else if (m_previous && m_previous->scene() && m_previous->data(Qt::UserRole).toInt() == previousItem.frame()) {
@@ -459,9 +462,10 @@ void GeometryWidget::slotPositionChanged(int pos, bool seek)
m_previous->setData(Qt::UserRole, previousItem.frame());
if (m_previous->scene() == 0) m_scene->addItem(m_previous);
}
+ */
- m_rect->setPos(item.x(), item.y());
- m_rect->setRect(0, 0, item.w(), item.h());
+ QRect r((int) item.x(), (int) item.y(), (int) item.w(), (int) item.h());
+ m_monitor->setUpEffectGeometry(r);
m_opacity->blockSignals(true);
m_opacity->setValue(item.mix());
@@ -483,7 +487,7 @@ void GeometryWidget::slotPositionChanged(int pos, bool seek)
slotUpdateProperties();
if (seek && KdenliveSettings::transitionfollowcursor())
- emit seekToPos(m_clipPos + pos);
+ emit seekToPos(pos);
}
void GeometryWidget::slotKeyframeMoved(int pos)
@@ -500,14 +504,13 @@ void GeometryWidget::slotAddKeyframe(int pos)
return;
Mlt::GeometryItem item;
if (pos == -1)
- pos = m_timePos->getValue();
+ pos = m_timeline->value(); // m_timePos->getValue();
item.frame(pos);
- QRectF r = m_rect->rect().normalized();
- QPointF rectpos = m_rect->pos();
- item.x(rectpos.x());
- item.y(rectpos.y());
- item.w(r.width());
- item.h(r.height());
+ QRect rect = m_monitor->effectRect().normalized();
+ item.x(rect.x());
+ item.y(rect.y());
+ item.w(rect.width());
+ item.h(rect.height());
item.mix(m_opacity->value());
m_geometry->insert(item);
@@ -547,9 +550,9 @@ void GeometryWidget::slotDeleteKeyframe(int pos)
m_timeline->update();
if (m_geomPath && KdenliveSettings::onmonitoreffects_geometryshowpath()) {
- m_scene->removeItem(m_geomPath);
+ /*m_scene->removeItem(m_geomPath);
m_geomPath->setPoints(m_geometry);
- m_scene->addItem(m_geomPath);
+ m_scene->addItem(m_geomPath);*/
}
slotPositionChanged(pos, false);
emit parameterChanged();
@@ -612,13 +615,55 @@ void GeometryWidget::slotUpdateGeometry()
{
Mlt::GeometryItem item;
int pos = m_timePos->getValue();
+ if (m_fixedGeom) {
+ // This is a fixed rectangle parameter, use only keyframe 0
+ pos = 0;
+ }
+ // get keyframe and make sure it is the correct one
+ if (m_geometry->next_key(&item, pos) || item.frame() != pos) {
+ return;
+ }
+ QRectF rect = m_monitor->effectRect().normalized();
+ item.x(rect.x());
+ item.y(rect.y());
+ item.w(rect.width());
+ item.h(rect.height());
+ m_geometry->insert(item);
+
+ for (int i = 0; i < m_extraGeometries.count(); ++i) {
+ Mlt::Geometry *geom = m_extraGeometries.at(i);
+ QString name = m_extraGeometryNames.at(i);
+ Mlt::GeometryItem item2;
+ DragValue *widget = findChild<DragValue *>(name);
+ if (widget && !geom->next_key(&item2, pos) && item2.frame() == pos) {
+ item2.x((double) widget->value() / m_extraFactors.at(i).toInt());
+ geom->insert(item2);
+ }
+ }
+ if (m_geomPath && KdenliveSettings::onmonitoreffects_geometryshowpath()) {
+ /*m_scene->removeItem(m_geomPath);
+ m_geomPath->setPoints(m_geometry);
+ m_scene->addItem(m_geomPath);*/
+ }
+ emit parameterChanged();
+}
+
+void GeometryWidget::slotUpdateGeometry(const QRect r)
+{
+ Mlt::GeometryItem item;
+ int pos = m_timePos->getValue();
+ if (m_fixedGeom) {
+ // This is a fixed rectangle parameter, use only keyframe 0
+ pos = 0;
+ }
// get keyframe and make sure it is the correct one
- if (m_geometry->next_key(&item, pos) || item.frame() != pos)
+ if (m_geometry->next_key(&item, pos) || item.frame() != pos) {
return;
+ }
- QRectF rectSize = m_rect->rect().normalized();
- QPointF rectPos = m_rect->pos();
+ QRectF rectSize = r.normalized();
+ QPointF rectPos = r.topLeft();
item.x(rectPos.x());
item.y(rectPos.y());
item.w(rectSize.width());
@@ -635,23 +680,24 @@ void GeometryWidget::slotUpdateGeometry()
geom->insert(item2);
}
}
- if (m_geomPath && KdenliveSettings::onmonitoreffects_geometryshowpath()) {
+ /*if (m_geomPath && KdenliveSettings::onmonitoreffects_geometryshowpath()) {
m_scene->removeItem(m_geomPath);
m_geomPath->setPoints(m_geometry);
m_scene->addItem(m_geomPath);
- }
+ }*/
+ m_monitor->setUpEffectGeometry(QRect(), calculateCenters());
emit parameterChanged();
+
}
void GeometryWidget::slotUpdateProperties()
{
- QRectF rectSize = m_rect->rect().normalized();
- QPointF rectPos = m_rect->pos();
+ QRect rect = m_monitor->effectRect().normalized();
double size;
- if (rectSize.width() / m_monitor->render->dar() > rectSize.height())
- size = rectSize.width() * 100.0 / m_monitor->render->frameRenderWidth();
+ if (rect.width() / m_monitor->render->dar() > rect.height())
+ size = rect.width() * 100.0 / m_monitor->render->frameRenderWidth();
else
- size = rectSize.height() * 100.0 / m_monitor->render->renderHeight();
+ size = rect.height() * 100.0 / m_monitor->render->renderHeight();
m_spinX->blockSignals(true);
m_spinY->blockSignals(true);
@@ -659,10 +705,10 @@ void GeometryWidget::slotUpdateProperties()
m_spinHeight->blockSignals(true);
m_spinSize->blockSignals(true);
- m_spinX->setValue(rectPos.x());
- m_spinY->setValue(rectPos.y());
- m_spinWidth->setValue(rectSize.width());
- m_spinHeight->setValue(rectSize.height());
+ m_spinX->setValue(rect.x());
+ m_spinY->setValue(rect.y());
+ m_spinWidth->setValue(rect.width());
+ m_spinHeight->setValue(rect.height());
m_spinSize->setValue(size);
m_spinX->blockSignals(false);
@@ -673,42 +719,53 @@ void GeometryWidget::slotUpdateProperties()
}
+QVariantList GeometryWidget::calculateCenters()
+{
+ QVariantList points;
+ Mlt::GeometryItem item;
+ int pos = 0;
+ while (!m_geometry->next_key(&item, pos)) {
+ QRect r(item.x(), item.y(), item.w(), item.h());
+ points.append(QVariant(r.center()));
+ pos = item.frame() + 1;
+ }
+ return points;
+}
+
void GeometryWidget::slotSetX(double value)
{
- m_rect->setPos(value, m_spinY->value());
+ m_monitor->setUpEffectGeometry(QRect(value, m_spinY->value(), m_spinWidth->value(), m_spinHeight->value()), calculateCenters());
slotUpdateGeometry();
}
void GeometryWidget::slotSetY(double value)
{
- m_rect->setPos(m_spinX->value(), value);
+ m_monitor->setUpEffectGeometry(QRect(m_spinX->value(), value, m_spinWidth->value(), m_spinHeight->value()), calculateCenters());
slotUpdateGeometry();
}
void GeometryWidget::slotSetWidth(double value)
{
- m_rect->setRect(0, 0, value, m_spinHeight->value());
+ m_monitor->setUpEffectGeometry(QRect(m_spinX->value(), m_spinY->value(), value, m_spinHeight->value()), calculateCenters());
slotUpdateGeometry();
}
void GeometryWidget::slotSetHeight(double value)
{
- m_rect->setRect(0, 0, m_spinWidth->value(), value);
+ m_monitor->setUpEffectGeometry(QRect(m_spinX->value(), m_spinY->value(), m_spinWidth->value(), value), calculateCenters());
slotUpdateGeometry();
}
void GeometryWidget::updateMonitorGeometry()
{
- m_rect->setRect(0, 0, m_spinWidth->value(), m_spinHeight->value());
+ m_monitor->setUpEffectGeometry(QRect(m_spinX->value(), m_spinY->value(), m_spinWidth->value(), m_spinHeight->value()), calculateCenters());
slotUpdateGeometry();
}
void GeometryWidget::slotResize(double value)
{
- m_rect->setRect(0, 0,
- (int)((m_monitor->render->frameRenderWidth() * value / 100.0) + 0.5),
- (int)((m_monitor->render->renderHeight() * value / 100.0) + 0.5));
+ m_monitor->setUpEffectGeometry(QRect(m_spinX->value(), m_spinY->value(), (int)((m_monitor->render->frameRenderWidth() * value / 100.0) + 0.5), (int)((m_monitor->render->renderHeight() * value / 100.0) + 0.5)), calculateCenters());
slotUpdateGeometry();
}
@@ -727,38 +784,32 @@ void GeometryWidget::slotSetOpacity(double value)
void GeometryWidget::slotMoveLeft()
{
- m_rect->setPos(0, m_rect->pos().y());
- slotUpdateGeometry();
+ m_spinX->setValue(0);
}
void GeometryWidget::slotCenterH()
{
- m_rect->setPos((m_monitor->render->frameRenderWidth() - m_rect->rect().width()) / 2, m_rect->pos().y());
- slotUpdateGeometry();
+ m_spinX->setValue((m_monitor->render->frameRenderWidth() - m_spinWidth->value()) / 2);
}
void GeometryWidget::slotMoveRight()
{
- m_rect->setPos(m_monitor->render->frameRenderWidth() - m_rect->rect().width(), m_rect->pos().y());
- slotUpdateGeometry();
+ m_spinX->setValue(m_monitor->render->frameRenderWidth() - m_spinWidth->value());
}
void GeometryWidget::slotMoveTop()
{
- m_rect->setPos(m_rect->pos().x(), 0);
- slotUpdateGeometry();
+ m_spinY->setValue(0);
}
void GeometryWidget::slotCenterV()
{
- m_rect->setPos(m_rect->pos().x(), (m_monitor->render->renderHeight() - m_rect->rect().height()) / 2);
- slotUpdateGeometry();
+ m_spinY->setValue((m_monitor->render->renderHeight() - m_spinHeight->value()) / 2);
}
void GeometryWidget::slotMoveBottom()
{
- m_rect->setPos(m_rect->pos().x(), m_monitor->render->renderHeight() - m_rect->rect().height());
- slotUpdateGeometry();
+ m_spinY->setValue(m_monitor->render->renderHeight() - m_spinHeight->value());
}
@@ -766,7 +817,7 @@ void GeometryWidget::slotSetSynchronize(bool sync)
{
KdenliveSettings::setTransitionfollowcursor(sync);
if (sync)
- emit seekToPos(m_clipPos + m_timePos->getValue());
+ emit seekToPos(m_timePos->getValue());
}
void GeometryWidget::setFrameSize(const QPoint &size)
@@ -844,9 +895,9 @@ void GeometryWidget::slotResetKeyframes()
m_geometry->insert(item);
m_timeline->setKeyGeometry(m_geometry, m_outPoint - m_inPoint);
if (m_geomPath && KdenliveSettings::onmonitoreffects_geometryshowpath()) {
- m_scene->removeItem(m_geomPath);
+ /*m_scene->removeItem(m_geomPath);
m_geomPath->setPoints(m_geometry);
- m_scene->addItem(m_geomPath);
+ m_scene->addItem(m_geomPath);*/
}
slotPositionChanged(-1, false);
emit parameterChanged();
@@ -881,9 +932,9 @@ void GeometryWidget::slotResetNextKeyframes()
}
m_timeline->setKeyGeometry(m_geometry, m_outPoint - m_inPoint);
if (m_geomPath && KdenliveSettings::onmonitoreffects_geometryshowpath()) {
- m_scene->removeItem(m_geomPath);
+ /*m_scene->removeItem(m_geomPath);
m_geomPath->setPoints(m_geometry);
- m_scene->addItem(m_geomPath);
+ m_scene->addItem(m_geomPath);*/
}
slotPositionChanged(-1, false);
emit parameterChanged();
@@ -930,9 +981,9 @@ void GeometryWidget::slotResetPreviousKeyframes()
}
m_timeline->setKeyGeometry(m_geometry, m_outPoint - m_inPoint);
if (m_geomPath && KdenliveSettings::onmonitoreffects_geometryshowpath()) {
- m_scene->removeItem(m_geomPath);
+ /*m_scene->removeItem(m_geomPath);
m_geomPath->setPoints(m_geometry);
- m_scene->addItem(m_geomPath);
+ m_scene->addItem(m_geomPath);*/
}
slotPositionChanged(-1, false);
emit parameterChanged();
@@ -985,11 +1036,11 @@ void GeometryWidget::importKeyframes(const QString &data, int maximum)
m_geometry->insert(item);
}
m_timeline->setKeyGeometry(m_geometry, m_outPoint - m_inPoint);
- if (m_geomPath && KdenliveSettings::onmonitoreffects_geometryshowpath()) {
+ /*if (m_geomPath && KdenliveSettings::onmonitoreffects_geometryshowpath()) {
m_scene->removeItem(m_geomPath);
m_geomPath->setPoints(m_geometry);
m_scene->addItem(m_geomPath);
- }
+ }*/
slotPositionChanged(-1, false);
emit parameterChanged();
}
diff --git a/src/effectstack/widgets/geometrywidget.h b/src/effectstack/widgets/geometrywidget.h
index a6a5809..306fed1 100644
--- a/src/effectstack/widgets/geometrywidget.h
+++ b/src/effectstack/widgets/geometrywidget.h
@@ -82,8 +82,6 @@ private:
int m_inPoint;
/** Out point of the clip (crop from end). */
int m_outPoint;
- MonitorScene *m_scene;
- OnMonitorRectItem *m_rect;
OnMonitorPathItem *m_geomPath;
QGraphicsRectItem *m_previous;
KeyframeHelper *m_timeline;
@@ -102,8 +100,12 @@ private:
DragValue *m_rotateY;
DragValue *m_rotateZ;
QPoint m_frameSize;
+ /** @brief True if this is a fixed parameter (no kexframes allowed). */
+ bool m_fixedGeom;
/** @brief Update monitor rect with current width / height values. */
void updateMonitorGeometry();
+ /** @brief Calculate the path for rectangle center moves. */
+ QVariantList calculateCenters();
private slots:
/** @brief Updates controls according to position.
@@ -134,6 +136,7 @@ private slots:
void slotUpdatePath();
/** @brief Updates the Mlt::Geometry object. */
void slotUpdateGeometry();
+ void slotUpdateGeometry(const QRect r);
/** @brief Updates the spinBoxes according to the rect. */
void slotUpdateProperties();
diff --git a/src/effectstack/widgets/lumaliftgain.cpp b/src/effectstack/widgets/lumaliftgain.cpp
new file mode 100644
index 0000000..37a2881
--- /dev/null
+++ b/src/effectstack/widgets/lumaliftgain.cpp
@@ -0,0 +1,110 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Jean-Baptiste Mardelle ([email protected]) *
+ * Some code was borrowed from shotcut *
+ * *
+ * 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) 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+
+#include "effectstack/widgets/lumaliftgain.h"
+#include "effectstack/widgets/colorwheel.h"
+#include "utils/flowlayout.h"
+
+#include <QLabel>
+#include <QHBoxLayout>
+
+#include <QDebug>
+#include <KLocalizedString>
+
+static const double LIFT_FACTOR = 1.0;
+static const double GAMMA_FACTOR = 2.0;
+static const double GAIN_FACTOR = 4.0;
+
+LumaLiftGain::LumaLiftGain(const QDomNodeList &nodes, QWidget* parent) :
+ QWidget(parent)
+{
+ FlowLayout *flowLayout = new FlowLayout(this, 2, 2, 2);
+ /*QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->setSpacing(0);*/
+
+ QMap <QString, double> values;
+ for (int i = 0; i < nodes.count() ; ++i) {
+ QDomElement pa = nodes.item(i).toElement();
+ if (pa.tagName() != "parameter") continue;
+ double val = m_locale.toDouble(pa.attribute("value")) / m_locale.toDouble(pa.attribute("factor"));
+ values.insert(pa.attribute("name"), val);
+ }
+
+ QColor lift = QColor::fromRgbF(values.value("lift_r"),
+ values.value("lift_g"),
+ values.value("lift_b"));
+ QColor gamma = QColor::fromRgbF(values.value("gamma_r") / GAMMA_FACTOR,
+ values.value("gamma_g") / GAMMA_FACTOR,
+ values.value("gamma_b") / GAMMA_FACTOR);
+ QColor gain = QColor::fromRgbF(values.value("gain_r") / GAIN_FACTOR,
+ values.value("gain_g") / GAIN_FACTOR,
+ values.value("gain_b") / GAIN_FACTOR);
+
+ m_lift = new ColorWheel("lift", i18n("Lift"), lift, this);
+ connect(m_lift, SIGNAL(colorChange(const QColor &)), this, SIGNAL(valueChanged()));
+ m_gamma = new ColorWheel("gamma", i18n("Gamma"), gamma, this);
+ connect(m_gamma, SIGNAL(colorChange(const QColor &)), this, SIGNAL(valueChanged()));
+ m_gain = new ColorWheel("gain", i18n("Gain"), gain, this);
+ connect(m_gain, SIGNAL(colorChange(const QColor &)), this, SIGNAL(valueChanged()));
+
+ flowLayout->addWidget(m_lift);
+ flowLayout->addWidget(m_gamma);
+ flowLayout->addWidget(m_gain);
+ setLayout(flowLayout);
+
+ /*layout->addWidget(label);
+ layout->addWidget(m_lift);
+ layout->addWidget(label2);
+ layout->addWidget(m_gamma);
+ layout->addWidget(label3);
+ layout->addWidget(m_gain);
+ setLayout(layout);*/
+}
+
+void LumaLiftGain::updateEffect(QDomElement &effect)
+{
+ QColor lift = m_lift->color();
+ QColor gamma = m_gamma->color();
+ QColor gain = m_gain->color();
+ QMap <QString, double> values;
+ values.insert("lift_r", lift.redF());
+ values.insert("lift_g", lift.greenF());
+ values.insert("lift_b", lift.blueF());
+
+ values.insert("gamma_r", gamma.redF() * GAMMA_FACTOR);
+ values.insert("gamma_g", gamma.greenF() * GAMMA_FACTOR);
+ values.insert("gamma_b", gamma.blueF() * GAMMA_FACTOR);
+
+ values.insert("gain_r", gain.redF() * GAIN_FACTOR);
+ values.insert("gain_g", gain.greenF() * GAIN_FACTOR);
+ values.insert("gain_b", gain.blueF() * GAIN_FACTOR);
+
+ QDomNodeList namenode = effect.childNodes();
+ for (int i = 0; i < namenode.count() ; ++i) {
+ QDomElement pa = namenode.item(i).toElement();
+ if (pa.tagName() != "parameter") continue;
+ if (values.contains(pa.attribute("name"))) {
+ pa.setAttribute("value", (int) (values.value(pa.attribute("name")) * m_locale.toDouble(pa.attribute("factor", "1"))));
+ }
+ }
+}
+
diff --git a/src/effectstack/widgets/lumaliftgain.h b/src/effectstack/widgets/lumaliftgain.h
new file mode 100644
index 0000000..54278f4
--- /dev/null
+++ b/src/effectstack/widgets/lumaliftgain.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Jean-Baptiste Mardelle ([email protected]) *
+ * *
+ * 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) 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+
+#ifndef LUMALIFTGAINWIDGET_H
+#define LUMALIFTGAINWIDGET_H
+
+#include <QWidget>
+#include <QDomNodeList>
+#include <QLocale>
+
+class ColorWheel;
+
+/**
+ * @class ChooseColorWidget
+ * @brief Provides options to choose 3 colors.
+ * @author Jean-Baptiste Mardelle
+ */
+
+class LumaLiftGain : public QWidget
+{
+ Q_OBJECT
+public:
+ /** @brief Sets up the widget.
+ * @param text (optional) What the color will be used for
+ * @param color (optional) initial color
+ * @param alphaEnabled (optional) Should transparent colors be enabled */
+ explicit LumaLiftGain(const QDomNodeList &nodes, QWidget* parent = 0);
+ void updateEffect(QDomElement &effect);
+
+private:
+ QLocale m_locale;
+ ColorWheel *m_lift;
+ ColorWheel *m_gamma;
+ ColorWheel *m_gain;
+
+signals:
+ /** @brief Emitted whenever a different color was choosen. */
+ void valueChanged();
+};
+
+#endif
diff --git a/src/kdenlivesettings.kcfg b/src/kdenlivesettings.kcfg
index 5aff1f5..aa4d1d3 100644
--- a/src/kdenlivesettings.kcfg
+++ b/src/kdenlivesettings.kcfg
@@ -3,6 +3,20 @@
<kcfg>
<kcfgfile/>
+ <group name="bin">
+ <entry name="treeviewheaders" type="String">
+ <label>Bin treeview headers state.</label>
+ <default></default>
+ </entry>
+ <entry name="binMode" type="Int">
+ <label>Bin view mode.</label>
+ <default>0</default>
+ </entry>
+ <entry name="bin_zoom" type="Int">
+ <label>Bin default zoom.</label>
+ <default>4</default>
+ </entry>
+ </group>
<group name="misc">
<entry name="openlastproject" type="Bool">
@@ -192,15 +206,10 @@
</group>
<group name="sdl">
- <entry name="openglmonitors" type="Bool">
- <label>Use OpenGL for video display.</label>
+ <entry name="gpu_accel" type="Bool">
+ <label>Use Movit for GPU accelerated display and effects.</label>
<default>false</default>
</entry>
-
- <entry name="video_driver" type="UInt">
- <label>Video driver used for output.</label>
- <default>0</default>
- </entry>
<entry name="audio_driver" type="UInt">
<label>Audio driver used for sound output.</label>
@@ -228,7 +237,7 @@
</entry>
<entry name="window_background" type="Color">
- <label>Background color for SDL monitor.</label>
+ <label>Background color for OpenGL monitor.</label>
<default>#999999</default>
</entry>
@@ -236,6 +245,16 @@
<label>Volume used for SDL output.</label>
<default>100</default>
</entry>
+
+ <entry name="monitor_dropframes" type="Bool">
+ <label>Allow framedropping in monitor playback.</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="monitor_gamma" type="Int">
+ <label>Monitor gamma (rbg / rec 709).</label>
+ <default>0</default>
+ </entry>
<entry name="external_display" type="Bool">
<label>Use Blackmagic device for video out.</label>
@@ -248,11 +267,6 @@
</group>
<group name="env">
- <entry name="activate_nepomuk" type="Bool">
- <label>Integration with Desktop Search (Nepomuk).</label>
- <default>false</default>
- </entry>
-
<entry name="mltpath" type="Path">
<label>Mlt framework install path.</label>
<default></default>
@@ -631,7 +645,12 @@
<label>Show overlay info on monitor (in / out points, markers,...).</label>
<default>true</default>
</entry>
-
+
+ <entry name="displayAudioOverlay" type="Bool">
+ <label>Show audio overlay info on monitor.</label>
+ <default>false</default>
+ </entry>
+
<entry name="showOnMonitorScene" type="Bool">
<label>Show on monitor adjustable effect parameter (geometry, ..).</label>
<default>true</default>
@@ -810,7 +829,14 @@
<label>Name of the chosen interpol method.</label>
<default>nearest</default>
</entry>
-
+ <entry name="favorite_effects" type="StringList">
+ <label>List of favorite effects ids.</label>
+ <default></default>
+ </entry>
+ <entry name="selected_effecttab" type="Int">
+ <label>Last opened effect list tab.</label>
+ <default>0</default>
+ </entry>
</group>
</kcfg>
diff --git a/src/main.cpp b/src/main.cpp
index 7e18316..c4bc588 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -21,7 +21,6 @@
#include <config-kdenlive.h>
#include "mainwindow.h"
-
#include <KAboutData>
#include <QDebug>
@@ -31,10 +30,14 @@
#include <KDBusService>
#include <QCommandLineParser>
#include <QCommandLineOption>
-
+#include <QProcess>
int main(int argc, char *argv[])
{
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
+ QCoreApplication::setAttribute(Qt::AA_X11InitThreads);
+#endif
+
KLocalizedString::setApplicationDomain("kdenlive");
// Init application
@@ -46,11 +49,10 @@ int main(int argc, char *argv[])
i18n("An open source video editor."),
KAboutLicense::GPL,
i18n("Copyright © 2007–2015 Kdenlive authors"),
- i18n("Please report bugs to http://kdenlive.org/mantis"),
- "http://kdenlive.org",
- "http://bugs.kdenlive.org");
- aboutData.addAuthor(i18n("Jean-Baptiste Mardelle"), i18n("MLT and KDE SC 4 porting, main developer and maintainer"), "[email protected]");
- aboutData.addAuthor(i18n("Vincent Pinon"), i18n("Interim maintainer, bugs fixing, minor functions, profiles updates, etc."), "[email protected]");
+ i18n("Please report bugs to http://bugs.kde.org"),
+ "https://kdenlive.org");
+ aboutData.addAuthor(i18n("Jean-Baptiste Mardelle"), i18n("MLT and KDE SC 4 / KF5 port, main developer and maintainer"), "[email protected]");
+ aboutData.addAuthor(i18n("Vincent Pinon"), i18n("Interim maintainer, KF5 port, bugs fixing, minor functions, profiles updates, etc."), "[email protected]");
aboutData.addAuthor(i18n("Laurent Montel"), i18n("Bugs fixing, clean up code, optimization etc."), "[email protected]");
aboutData.addAuthor(i18n("Marco Gittler"), i18n("MLT transitions and effects, timeline, audio thumbs"), "[email protected]");
aboutData.addAuthor(i18n("Dan Dennedy"), i18n("Bug fixing, etc."), "[email protected]");
@@ -118,5 +120,15 @@ int main(int argc, char *argv[])
window->show();
}
int result = app.exec();
+
+ if (EXIT_RESTART == result) {
+ qDebug() << "restarting app";
+ QProcess* restart = new QProcess;
+ restart->start(app.applicationFilePath(), QStringList());
+ restart->waitForReadyRead();
+ restart->waitForFinished(1000);
+ result = EXIT_SUCCESS;
+ }
+
return result;
}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index d385b37..5cbfa2f 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -21,22 +21,25 @@
#include "mainwindow.h"
#include "mainwindowadaptor.h"
#include "core.h"
+#include "bin/projectclip.h"
+#include "mltcontroller/clipcontroller.h"
#include "kdenlivesettings.h"
#include "dialogs/kdenlivesettingsdialog.h"
+#include "dialogs/clipcreationdialog.h"
#include "effectslist/initeffects.h"
#include "dialogs/profilesdialog.h"
#include "project/dialogs/projectsettings.h"
#include "project/clipmanager.h"
-#include "project/projectlist.h"
#include "monitor/monitor.h"
#include "monitor/recmonitor.h"
#include "monitor/monitormanager.h"
#include "doc/kdenlivedoc.h"
-#include "timeline/trackview.h"
+#include "timeline/timeline.h"
#include "timeline/customtrackview.h"
#include "effectslist/effectslistview.h"
#include "effectstack/effectstackview2.h"
#include "project/transitionsettings.h"
+#include "mltcontroller/bincontroller.h"
#include "dialogs/renderwidget.h"
#include "renderer.h"
#include "project/clipproperties.h"
@@ -117,7 +120,6 @@ static bool sortByNames(const QPair<QString, QAction *> &a, const QPair<QString,
MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString & clipsToLoad, QWidget *parent) :
KXmlGuiWindow(parent),
- m_projectList(NULL),
m_stopmotion(NULL),
m_effectStack(NULL),
m_effectList(NULL),
@@ -127,13 +129,16 @@ MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString &
m_renderWidget(NULL),
m_mainClip(NULL),
m_transitionConfig(NULL),
- m_timelineArea(NULL)
+ m_timelineArea(NULL),
+ m_exitCode(EXIT_SUCCESS)
{
qRegisterMetaType<audioShortVector> ("audioShortVector");
qRegisterMetaType<MessageType> ("MessageType");
qRegisterMetaType<stringMap> ("stringMap");
qRegisterMetaType<audioByteArray> ("audioByteArray");
qRegisterMetaType< QVector <int> > ();
+ qRegisterMetaType<requestClipInfo> ("requestClipInfo");
+
new RenderingAdaptor(this);
Core::initialize(this);
MltConnection::locateMeltAndProfilesPath(MltPath);
@@ -164,46 +169,44 @@ MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString &
QTabBar *bar = m_timelineArea->findChild<QTabBar *>();
bar->setHidden(true);
- Mlt::Repository *repo = initEffects::parseEffectFiles();
+ m_gpuAllowed = initEffects::parseEffectFiles(pCore->binController()->mltRepository());
//initEffects::parseCustomEffectsFile();
m_shortcutRemoveFocus = new QShortcut(QKeySequence("Esc"), this);
connect(m_shortcutRemoveFocus, SIGNAL(activated()), this, SLOT(slotRemoveFocus()));
/// Add Widgets ///
+ m_projectBinDock = addDock(i18n("Project Bin"), "project_bin", pCore->bin());
- m_projectList = new ProjectList();
- m_projectListDock = addDock(i18n("Project Tree"), "project_tree", m_projectList);
-
- m_clipMonitor = new Monitor(Kdenlive::ClipMonitor, pCore->monitorManager(), QString(), m_timelineArea);
+ m_clipMonitor = new Monitor(Kdenlive::ClipMonitor, pCore->monitorManager(), this);
+ pCore->bin()->setMonitor(m_clipMonitor);
+ connect(pCore->bin(), SIGNAL(clipNeedsReload(QString,bool)),this, SLOT(slotUpdateClip(QString,bool)));
- // Connect the project list
- connect(m_projectList, SIGNAL(clipSelected(DocClipBase*,QPoint,bool)), m_clipMonitor, SLOT(slotSetClipProducer(DocClipBase*,QPoint,bool)));
- connect(m_projectList, SIGNAL(raiseClipMonitor(bool)), m_clipMonitor, SLOT(slotActivateMonitor(bool)));
- connect(m_projectList, SIGNAL(loadingIsOver()), this, SLOT(slotElapsedTime()));
+ //TODO deprecated, replace with Bin methods if necessary
+ /*connect(m_projectList, SIGNAL(loadingIsOver()), this, SLOT(slotElapsedTime()));
connect(m_projectList, SIGNAL(displayMessage(QString,int,MessageType)), this, SLOT(slotGotProgressInfo(QString,int,MessageType)));
connect(m_projectList, SIGNAL(updateRenderStatus()), this, SLOT(slotCheckRenderStatus()));
- connect(m_projectList, SIGNAL(clipNeedsReload(QString)),this, SLOT(slotUpdateClip(QString)));
connect(m_projectList, SIGNAL(updateProfile(QString)), this, SLOT(slotUpdateProjectProfile(QString)));
connect(m_projectList, SIGNAL(refreshClip(QString,bool)), pCore->monitorManager(), SLOT(slotRefreshCurrentMonitor(QString)));
connect(m_projectList, SIGNAL(findInTimeline(QString)), this, SLOT(slotClipInTimeline(QString)));
- connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint)));
- connect(m_clipMonitor, SIGNAL(extractZone(QString,QPoint)), m_projectList, SLOT(slotCutClipJob(QString,QPoint)));
+ connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint)));*/
+ connect(m_clipMonitor, SIGNAL(extractZone(QString)), pCore->bin(), SLOT(slotStartCutJob(QString)));
- m_projectMonitor = new Monitor(Kdenlive::ProjectMonitor, pCore->monitorManager(), QString());
+ m_projectMonitor = new Monitor(Kdenlive::ProjectMonitor, pCore->monitorManager(), this);
-#ifndef Q_WS_MAC
- m_recMonitor = new RecMonitor(Kdenlive::RecordMonitor, pCore->monitorManager());
+/*
+ //TODO disabled until ported to qml
+ m_recMonitor = new RecMonitor(Kdenlive::RecordMonitor, pCore->monitorManager(), this);
connect(m_recMonitor, SIGNAL(addProjectClip(QUrl)), this, SLOT(slotAddProjectClip(QUrl)));
connect(m_recMonitor, SIGNAL(addProjectClipList(QList<QUrl>)), this, SLOT(slotAddProjectClipList(QList<QUrl>)));
connect(m_recMonitor, SIGNAL(showConfigDialog(int,int)), this, SLOT(slotPreferences(int,int)));
-#endif /* ! Q_WS_MAC */
+*/
pCore->monitorManager()->initMonitors(m_clipMonitor, m_projectMonitor, m_recMonitor);
- m_projectMonitor->render->setMltRepository(repo);
- m_effectStack = new EffectStackView2(m_projectMonitor);
- connect(m_effectStack, SIGNAL(startFilterJob(const ItemInfo&,const QString&,QMap<QString,QString>&,QMap<QString,QString>&,QMap<QString,QString>&)), m_projectList, SLOT(slotStartFilterJob(const ItemInfo &,const QString&,QMap<QString,QString>&,QMap<QString,QString>&,QMap<QString,QString>&)));
+ m_effectStack = new EffectStackView2();
+ connect(m_effectStack, SIGNAL(startFilterJob(const ItemInfo&,const QString&,QMap<QString,QString>&,QMap<QString,QString>&,QMap<QString,QString>&)), pCore->bin(), SLOT(slotStartFilterJob(const ItemInfo &,const QString&,QMap<QString,QString>&,QMap<QString,QString>&,QMap<QString,QString>&)));
+ connect(pCore->bin(), SIGNAL(masterClipSelected(ClipController *, Monitor *)), m_effectStack, SLOT(slotMasterClipItemSelected(ClipController *, Monitor *)));
m_effectStackDock = addDock(i18n("Effect Stack"), "effect_stack", m_effectStack);
m_transitionConfig = new TransitionSettings(m_projectMonitor);
@@ -215,9 +218,9 @@ MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString &
// Add monitors here to keep them at the right of the window
m_clipMonitorDock = addDock(i18n("Clip Monitor"), "clip_monitor", m_clipMonitor);
m_projectMonitorDock = addDock(i18n("Project Monitor"), "project_monitor", m_projectMonitor);
-#ifndef Q_WS_MAC
- m_recMonitorDock = addDock(i18n("Record Monitor"), "record_monitor", m_recMonitor);
-#endif
+ if (m_recMonitor) {
+ m_recMonitorDock = addDock(i18n("Record Monitor"), "record_monitor", m_recMonitor);
+ }
m_undoView = new QUndoView();
m_undoView->setCleanIcon(QIcon::fromTheme("edit-clear"));
@@ -243,9 +246,9 @@ MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString &
tabifyDockWidget(m_effectListDock, m_transitionConfigDock);
tabifyDockWidget(m_clipMonitorDock, m_projectMonitorDock);
-#ifndef Q_WS_MAC
- tabifyDockWidget(m_clipMonitorDock, m_recMonitorDock);
-#endif
+ if (m_recMonitor) {
+ tabifyDockWidget(m_clipMonitorDock, m_recMonitorDock);
+ }
setCentralWidget(m_timelineArea);
readOptions();
@@ -302,7 +305,7 @@ MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString &
menus.insert("transcodeMenu",static_cast<QMenu*>(factory()->container("transcoders", this)));
menus.insert("clipActionsMenu",static_cast<QMenu*>(factory()->container("clip_actions", this)));
menus.insert("inTimelineMenu",clipInTimeline);
- m_projectList->setupGeneratorMenu(menus);
+ pCore->bin()->setupGeneratorMenu(menus);
// Setup and fill effects and transitions menus.
QMenu *m = static_cast<QMenu*>(factory()->container("video_effects_menu", this));
@@ -310,7 +313,7 @@ MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString &
m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
- for (int i = 0; i < transitions.count(); ++i)
+ for (int i = 0; i < m_transitions.count(); ++i)
m_transitionsMenu->addAction(m_transitions[i]);
connect(m, SIGNAL(triggered(QAction*)), this, SLOT(slotAddVideoEffect(QAction*)));
@@ -351,17 +354,15 @@ MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString &
m_timelineContextTransitionMenu->addAction(actionCollection()->action("auto_transition"));
- connect(m_projectMonitorDock, SIGNAL(visibilityChanged(bool)), m_projectMonitor, SLOT(refreshMonitor(bool)));
- connect(m_clipMonitorDock, SIGNAL(visibilityChanged(bool)), m_clipMonitor, SLOT(refreshMonitor(bool)));
connect(m_effectList, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
connect(m_effectList, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));
slotConnectMonitors();
- m_projectListDock->raise();
+ m_projectBinDock->raise();
- actionCollection()->addAssociatedWidget(m_clipMonitor->container());
- actionCollection()->addAssociatedWidget(m_projectMonitor->container());
+ /*actionCollection()->addAssociatedWidget(m_clipMonitor->container());
+ actionCollection()->addAssociatedWidget(m_projectMonitor->container());*/
QList<QPair<QString, QAction *> > viewActions;
QPair <QString, QAction *> pair;
@@ -444,12 +445,15 @@ MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString &
KdenliveSettings::setDecklink_extension(data.section(';', 1, 1));
}
}
-
+
pCore->projectManager()->init(Url, clipsToLoad);
+ QTimer::singleShot(0, pCore->projectManager(), SLOT(slotLoadOnOpen()));
connect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()), Qt::UniqueConnection);
+
#ifdef USE_JOGSHUTTLE
new JogManager(this);
#endif
+ //KMessageBox::information(this, "Warning, development version for testing only. we are currently working on core functionnalities,\ndo not save any project or your project files might be corrupted.");
}
void MainWindow::slotThemeChanged(const QString &theme)
@@ -466,10 +470,9 @@ void MainWindow::slotThemeChanged(const QString &theme)
if (m_clipMonitor) m_clipMonitor->setPalette(plt);
if (m_projectMonitor) m_projectMonitor->setPalette(plt);
setStatusBarStyleSheet(plt);
- if (pCore->projectManager()->currentTrackView()) {
- pCore->projectManager()->currentTrackView()->updatePalette();
+ if (pCore->projectManager()->currentTimeline()) {
+ pCore->projectManager()->currentTimeline()->updatePalette();
}
-
if (m_timelineArea) {
m_timelineArea->setPalette(plt);
}
@@ -508,20 +511,17 @@ void MainWindow::slotReloadTheme()
MainWindow::~MainWindow()
{
delete m_stopmotion;
-
m_effectStack->slotClipItemSelected(NULL);
m_transitionConfig->slotTransitionItemSelected(NULL, 0, QPoint(), false);
-
if (m_projectMonitor) m_projectMonitor->stop();
if (m_clipMonitor) m_clipMonitor->stop();
-
+ delete pCore;
delete m_effectStack;
delete m_transitionConfig;
delete m_projectMonitor;
delete m_clipMonitor;
- delete m_projectList;
delete m_shortcutRemoveFocus;
- delete[] m_transitions;
+ qDeleteAll(m_transitions);
Mlt::Factory::close();
}
@@ -623,7 +623,7 @@ void MainWindow::generateClip()
QUrl clipUrl = iGenerator->generatedClip(KdenliveSettings::rendererpath(), action->data().toString(), project->projectFolder(),
QStringList(), QStringList(), project->fps(), project->width(), project->height());
if (clipUrl.isValid()) {
- m_projectList->slotAddClip(QList <QUrl> () << clipUrl);
+ pCore->bin()->droppedUrls(QList <QUrl> () << clipUrl);
}
}
@@ -675,68 +675,50 @@ void MainWindow::slotAddEffect(const QDomElement &effect)
return;
}
QDomElement effectToAdd = effect.cloneNode().toElement();
- bool ok;
- int ix = m_effectStack->isTrackMode(&ok);
- if (ok) pCore->projectManager()->currentTrackView()->projectView()->slotAddTrackEffect(effectToAdd, pCore->projectManager()->current()->tracksCount() - ix);
- else pCore->projectManager()->currentTrackView()->projectView()->slotAddEffect(effectToAdd, GenTime(), -1);
+ EFFECTMODE status = m_effectStack->effectStatus();
+ if (status == TIMELINE_TRACK) pCore->projectManager()->currentTimeline()->projectView()->slotAddTrackEffect(effectToAdd, pCore->projectManager()->current()->tracksCount() - m_effectStack->trackIndex());
+ else if (status == TIMELINE_CLIP) pCore->projectManager()->currentTimeline()->projectView()->slotAddEffect(effectToAdd, GenTime(), -1);
+ else if (status == MASTER_CLIP) pCore->bin()->addEffect(QString(), effectToAdd);
}
-void MainWindow::slotUpdateClip(const QString &id)
+void MainWindow::slotUpdateClip(const QString &id, bool reload)
{
- DocClipBase *clip = pCore->projectManager()->current()->clipManager()->getClipById(id);
+ ProjectClip *clip = pCore->bin()->getBinClip(id);
if (!clip) {
return;
}
- if (clip->numReferences() > 0) {
- pCore->projectManager()->currentTrackView()->projectView()->slotUpdateClip(id);
+ //TODO
+ //if (clip->numReferences() > 0) {
+ pCore->projectManager()->currentTimeline()->projectView()->slotUpdateClip(id, reload);
+ //}
+ //TODO Should probably be removed
+ if (m_clipMonitor->activeClipId() == id) {
+ m_clipMonitor->openClip(pCore->binController()->getController(id));
}
- if (m_clipMonitor->activeClip() && m_clipMonitor->activeClip()->getId() == id) {
- Mlt::Producer *monitorProducer = clip->getCloneProducer();
- m_clipMonitor->updateClipProducer(monitorProducer);
- }
- clip->cleanupProducers();
+ //TODO
+ //clip->cleanupProducers();
}
void MainWindow::slotConnectMonitors()
{
- m_projectList->setRenderer(m_projectMonitor->render);
- connect(m_projectList, SIGNAL(pauseMonitor()), pCore->monitorManager(), SLOT(slotPause()));
- connect(m_projectList, SIGNAL(deleteProjectClips(QStringList,QMap<QString,QString>)), this, SLOT(slotDeleteProjectClips(QStringList,QMap<QString,QString>)));
- connect(m_projectMonitor->render, SIGNAL(replyGetImage(QString,QString,int,int)), m_projectList, SLOT(slotReplyGetImage(QString,QString,int,int)));
- connect(m_projectMonitor->render, SIGNAL(replyGetImage(QString,QImage)), m_projectList, SLOT(slotReplyGetImage(QString,QImage)));
+ //connect(m_projectList, SIGNAL(deleteProjectClips(QStringList,QMap<QString,QString>)), this, SLOT(slotDeleteProjectClips(QStringList,QMap<QString,QString>)));
+ connect(m_projectMonitor->render, SIGNAL(replyGetImage(QString,QImage)), pCore->bin(), SLOT(slotThumbnailReady(QString,QImage)));
+ connect(m_projectMonitor->render, SIGNAL(gotFileProperties(requestClipInfo,ClipController *)), pCore->bin(), SLOT(slotProducerReady(requestClipInfo,ClipController *)));
- connect(m_projectMonitor->render, SIGNAL(replyGetFileProperties(QString,Mlt::Producer*,stringMap,stringMap,bool)), m_projectList, SLOT(slotReplyGetFileProperties(QString,Mlt::Producer*,stringMap,stringMap,bool)), Qt::DirectConnection);
//DirectConnection was necessary not to mess the analyze queue, but the monitor thread shouldn't show any UI widget (profile dialog), so adding an AutoConnection in between?
- connect(m_projectList, SIGNAL(firstClip(ProjectItem*)), m_projectList, SLOT(adjustProjectProfileToItem(ProjectItem*)));
-
- connect(m_projectMonitor->render, SIGNAL(removeInvalidClip(QString,bool)), m_projectList, SLOT(slotRemoveInvalidClip(QString,bool)));
-
- connect(m_projectMonitor->render, SIGNAL(removeInvalidProxy(QString,bool)), m_projectList, SLOT(slotRemoveInvalidProxy(QString,bool)));
- connect(m_clipMonitor, SIGNAL(refreshClipThumbnail(QString,bool)), m_projectList, SLOT(slotRefreshClipThumbnail(QString,bool)));
-
- connect(m_clipMonitor, SIGNAL(adjustMonitorSize()), this, SLOT(slotAdjustClipMonitor()));
- connect(m_projectMonitor, SIGNAL(adjustMonitorSize()), this, SLOT(slotAdjustProjectMonitor()));
+ //TODO react in case of invalid clip added to Bin
+ /*connect(m_projectMonitor->render, SIGNAL(removeInvalidClip(QString,bool)), pCore->bin(), SLOT(slotRemoveInvalidClip(QString,bool)));
+ connect(m_projectMonitor->render, SIGNAL(removeInvalidProxy(QString,bool)), pCore->bin(), SLOT(slotRemoveInvalidProxy(QString,bool)));
+ connect(m_clipMonitor, SIGNAL(refreshClipThumbnail(QString,bool)), pCore->bin(), SLOT(slotRefreshClipThumbnail(QString,bool)));*/
connect(m_projectMonitor, SIGNAL(requestFrameForAnalysis(bool)), this, SLOT(slotMonitorRequestRenderFrame(bool)));
- connect(m_clipMonitor, SIGNAL(saveZone(Render*,QPoint,DocClipBase*)), this, SLOT(slotSaveZone(Render*,QPoint,DocClipBase*)));
- connect(m_projectMonitor, SIGNAL(saveZone(Render*,QPoint,DocClipBase*)), this, SLOT(slotSaveZone(Render*,QPoint,DocClipBase*)));
-}
-
-void MainWindow::slotAdjustClipMonitor()
-{
- m_clipMonitorDock->updateGeometry();
- m_clipMonitorDock->adjustSize();
- m_clipMonitor->resetSize();
+ //TODO
+ /*connect(m_clipMonitor, SIGNAL(saveZone(Render*,QPoint,DocClipBase*)), this, SLOT(slotSaveZone(Render*,QPoint,DocClipBase*)));
+ connect(m_projectMonitor, SIGNAL(saveZone(Render*,QPoint,DocClipBase*)), this, SLOT(slotSaveZone(Render*,QPoint,DocClipBase*)));*/
}
-void MainWindow::slotAdjustProjectMonitor()
-{
- m_projectMonitorDock->updateGeometry();
- m_projectMonitorDock->adjustSize();
- m_projectMonitor->resetSize();
-}
void MainWindow::addAction(const QString &name, QAction *action)
{
@@ -807,7 +789,7 @@ void MainWindow::setupActions()
toolbar->addSeparator();
// create tools buttons
- m_buttonSelectTool = new QAction(QIcon::fromTheme("kdenlive-select-tool"), i18n("Selection tool"), this);
+ m_buttonSelectTool = new QAction(QIcon::fromTheme("cursor-arrow"), i18n("Selection tool"), this);
m_buttonSelectTool->setShortcut(i18nc("Selection tool shortcut", "s"));
toolbar->addAction(m_buttonSelectTool);
m_buttonSelectTool->setCheckable(true);
@@ -819,12 +801,11 @@ void MainWindow::setupActions()
m_buttonRazorTool->setCheckable(true);
m_buttonRazorTool->setChecked(false);
- m_buttonSpacerTool = new QAction(QIcon::fromTheme("kdenlive-spacer-tool"), i18n("Spacer tool"), this);
+ m_buttonSpacerTool = new QAction(QIcon::fromTheme("distribute-horizontal-x"), i18n("Spacer tool"), this);
m_buttonSpacerTool->setShortcut(i18nc("Spacer tool shortcut", "m"));
toolbar->addAction(m_buttonSpa