diff --git a/CMakeLists.txt b/CMakeLists.txt
index 749acb89d..a24a0f04c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,155 +1,155 @@
project(Kdenlive)
# An odd patch version number means development version, while an even one means
# stable release. An additional number can be used for bugfix-only releases.
# KDE Application Version, managed by release script
set (KDE_APPLICATIONS_VERSION_MAJOR "17")
set (KDE_APPLICATIONS_VERSION_MINOR "11")
set (KDE_APPLICATIONS_VERSION_MICRO "70")
set(KDENLIVE_VERSION ${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO})
cmake_minimum_required(VERSION 3.0)
if(POLICY CMP0063)
cmake_policy(SET CMP0063 NEW)
endif()
if (POLICY CMP0053)
cmake_policy(SET CMP0053 NEW)
endif()
# Minimum versions of main dependencies.
set(MLT_MIN_MAJOR_VERSION 6)
set(MLT_MIN_MINOR_VERSION 4)
set(MLT_MIN_PATCH_VERSION 0)
set(MLT_MIN_VERSION ${MLT_MIN_MAJOR_VERSION}.${MLT_MIN_MINOR_VERSION}.${MLT_MIN_PATCH_VERSION})
set(QT_MIN_VERSION 5.6.0)
find_package(ECM 5.18.0 REQUIRED CONFIG)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(FeatureSummary)
include(ECMInstallIcons)
include(GenerateExportHeader)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(ECMOptionalAddSubdirectory)
include(ECMMarkNonGuiExecutable)
include(ECMAddAppIcon)
include(ECMQtDeclareLoggingCategory)
add_definitions(-DQT_NO_CAST_TO_ASCII)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
add_definitions(-DTRANSLATION_DOMAIN=\"kdenlive\")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fsanitize=address -fno-omit-frame-pointer")
# To be switched on when releasing.
option(RELEASE_BUILD "Remove Git revision from program version (use for stable releases)" ON)
option(BUILD_TESTS "Build tests" ON)
# Get current version.
set(KDENLIVE_VERSION_STRING "${KDENLIVE_VERSION}")
if(NOT RELEASE_BUILD AND EXISTS ${CMAKE_SOURCE_DIR}/.git)
# Probably a Git workspace; determine the revision.
find_package(Git QUIET)
if(GIT_FOUND)
exec_program(${GIT_EXECUTABLE}
${CMAKE_SOURCE_DIR}
ARGS "log -n 1 --pretty=format:\"%h\""
OUTPUT_VARIABLE KDENLIVE_GIT_REVISION
RETURN_VALUE TAG_RESULT
)
# git log failed; maybe the repository was checked with depth=1.
if(NOT ${TAG_RESULT} EQUAL 0)
exec_program(${GIT_EXECUTABLE}
${CMAKE_SOURCE_DIR}
ARGS "describe --always"
OUTPUT_VARIABLE KDENLIVE_GIT_REVISION
)
endif()
message(STATUS "Kdenlive Git revision: ${KDENLIVE_GIT_REVISION}")
set(KDENLIVE_VERSION_STRING "${KDENLIVE_VERSION} (rev. ${KDENLIVE_GIT_REVISION})")
else()
message(STATUS "Kdenlive Git revision could not be determined")
endif()
endif()
include(CheckIncludeFiles)
check_include_files(malloc.h HAVE_MALLOC_H)
check_include_files(pthread.h HAVE_PTHREAD_H)
find_package(Qt5 REQUIRED COMPONENTS Core DBus Widgets Svg Quick )
find_package(Qt5 OPTIONAL_COMPONENTS WebKitWidgets QUIET)
find_package(KF5 5.23.0 OPTIONAL_COMPONENTS XmlGui QUIET)
if (KF5XmlGui_FOUND)
message(STATUS "Found KF5 >= 5.23.0 enabling icon coloring")
else()
message(STATUS "KF5 < 5.23.0 Disable icon coloring")
set(KF5_ICON_COMPATIBILITY TRUE)
endif()
find_package(KF5 REQUIRED COMPONENTS Archive Bookmarks CoreAddons Config ConfigWidgets DBusAddons KIO WidgetsAddons NotifyConfig NewStuff XmlGui Notifications GuiAddons TextWidgets IconThemes Crash Declarative Solid OPTIONAL_COMPONENTS DocTools FileMetaData)
if (KF5FileMetaData_FOUND)
message(STATUS "Found KF5 FileMetadata to extract file metadata")
set(KF5_FILEMETADATA TRUE)
else()
message(STATUS "KF5 FileMetadata not found, file metadata will not be available")
endif()
# Search MLT package.
find_package(MLT ${MLT_MIN_VERSION} REQUIRED)
set_package_properties(MLT PROPERTIES
DESCRIPTION "Multimedia framework and video playout server for TV broadcasting"
URL "http://mltframework.org"
TYPE RUNTIME
PURPOSE "Required to do video processing")
set(MLT_PREFIX ${MLT_ROOT_DIR})
add_subdirectory(data)
if(KF5DocTools_FOUND)
add_subdirectory(doc)
endif()
#add_subdirectory(plugins)
ecm_optional_add_subdirectory(po)
add_subdirectory(renderer)
add_subdirectory(src)
add_subdirectory(thumbnailer)
############################
# Tests
############################
if (BUILD_TESTS)
message(STATUS "Building TESTS")
add_subdirectory(tests)
- SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -fexceptions")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -fexceptions")
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/src
${MLT_INCLUDE_DIR}
${MLTPP_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/lib/external
${CMAKE_CURRENT_SOURCE_DIR}/lib
src
)
ADD_EXECUTABLE(runTests ${Tests_SRCS})
target_link_libraries(runTests kdenliveLib)
ADD_TEST(runTests runTests -d yes)
else()
message(STATUS "Not building TESTS")
endif()
#add_subdirectory(testingArea)
install( FILES kdenlive.categories DESTINATION ${KDE_INSTALL_CONFDIR} )
configure_file(config-kdenlive.h.cmake config-kdenlive.h @ONLY)
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2b099ce13..628cca6ea 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,336 +1,338 @@
add_definitions(${Qt5Gui_DEFINITIONS})
if(${Qt5Gui_OPENGL_IMPLEMENTATION} STREQUAL "GL")
find_package(OpenGL REQUIRED)
set_package_properties(OpenGL PROPERTIES
DESCRIPTION "the OpenGL library"
URL ""
TYPE RUNTIME
PURPOSE "")
else()
find_package(OpenGLES REQUIRED)
set_package_properties(OpenGLES PROPERTIES
DESCRIPTION "the OpenGLES library"
URL ""
TYPE RUNTIME
PURPOSE "")
endif()
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -pedantic -Wextra")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -pedantic -Wextra")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-qual -Wcast-align -Wfloat-equal -Wpointer-arith")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunreachable-code -Wchar-subscripts -Wcomment -Wformat")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror-implicit-function-declaration -Wmain -Wmissing-braces")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wparentheses -Wsequence-point -Wreturn-type -Wswitch")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wuninitialized -Wreorder -Wundef -Wshadow -Wwrite-strings")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wconversion")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wmissing-noreturn -Wsign-conversion -Wunused ")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wstrict-aliasing -Wstrict-overflow -Wconversion")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdisabled-optimization")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-undef")
if (CMAKE_COMPILER_IS_GNUCXX)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wlogical-op -Wunsafe-loop-optimizations ")
endif()
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunused-parameter -Wshadow -Wno-variadic-macros -Wno-float-conversion")
find_package(PkgConfig QUIET)
execute_process(
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=mltdatadir mlt-framework
OUTPUT_VARIABLE MLT_DATADIR
RESULT_VARIABLE MLT_DATADIR_failed)
if (NOT MLT_DATADIR_failed)
string(REGEX REPLACE "[\r\n]" "" MLT_DATADIR "${MLT_DATADIR}")
endif()
execute_process(
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=meltbin mlt-framework
OUTPUT_VARIABLE MLT_MELTBIN
RESULT_VARIABLE MLT_MELTBIN_failed)
if (NOT MLT_MELTBIN_failed)
string(REGEX REPLACE "[\r\n]" "" MLT_MELTBIN "${MLT_MELTBIN}")
endif()
configure_file( mlt_config.h.in ${CMAKE_BINARY_DIR}/generated/mlt_config.h )
include_directories( ${CMAKE_BINARY_DIR}/generated/ ) # Make sure it can be included...
option(WITH_JogShuttle "Build Jog/Shuttle support" ON)
set(FFMPEG_SUFFIX "" CACHE STRING "FFmpeg custom suffix")
find_package(LibV4L2)
set_package_properties(LibV4L2 PROPERTIES
DESCRIPTION "Collection of video4linux support libraries"
URL "http://freecode.com/projects/libv4l"
TYPE RUNTIME
PURPOSE "Required for better webcam support")
if(WITH_JogShuttle)
check_include_files(linux/input.h HAVE_LINUX_INPUT_H)
if(HAVE_LINUX_INPUT_H)
set(BUILD_JogShuttle TRUE)
endif(HAVE_LINUX_INPUT_H)
endif()
set_package_properties(OpenGL PROPERTIES
DESCRIPTION "the OpenGL library"
URL ""
TYPE RUNTIME
PURPOSE "")
#if(APPLE)
# macro_log_feature(SDL_FOUND
# "SDL"
# "Cross-platform multimedia library"
# "http://www.libsdl.org"
# TRUE
# )
#endif(APPLE)
#add_definitions( -DQT_NO_CAST_FROM_ASCII )
#add_definitions( -DQT_NO_CAST_TO_ASCII )
install(FILES kdenlivesettings.kcfg DESTINATION ${KCFG_INSTALL_DIR})
kconfig_add_kcfg_files(kdenlive_SRCS kdenlivesettings.kcfgc)
add_subdirectory(abstractmodel)
add_subdirectory(assets)
add_subdirectory(bin)
add_subdirectory(capture)
add_subdirectory(dialogs)
add_subdirectory(doc)
add_subdirectory(dvdwizard)
add_subdirectory(effects)
add_subdirectory(effectslist)
+add_subdirectory(jobs)
add_subdirectory(lib)
add_subdirectory(mltcontroller)
add_subdirectory(monitor)
add_subdirectory(profiles)
add_subdirectory(project)
add_subdirectory(qml)
add_subdirectory(scopes)
add_subdirectory(simplekeyframes)
add_subdirectory(timeline2)
add_subdirectory(titler)
add_subdirectory(transitions)
add_subdirectory(utils)
add_subdirectory(widgets)
add_subdirectory(xml)
if (Qt5WebKitWidgets_FOUND)
add_subdirectory(qt-oauth-lib)
endif()
add_subdirectory(library)
list(APPEND kdenlive_SRCS
colortools.cpp
definitions.cpp
gentime.cpp
doc/kthumb.cpp
mainwindow.cpp
renderer.cpp
statusbarmessagelabel.cpp
timecode.cpp
timecodedisplay.cpp
layoutmanagement.cpp
hidetitlebars.cpp
mltconnection.cpp
core.cpp
undohelper.cpp
)
ecm_qt_declare_logging_category(kdenlive_SRCS HEADER kdenlive_debug.h IDENTIFIER KDENLIVE_LOG CATEGORY_NAME org.kde.multimedia.kdenlive)
ki18n_wrap_ui(kdenlive_UIS
ui/addtrack_ui.ui
ui/archivewidget_ui.ui
ui/audiospectrum_ui.ui
ui/backupdialog_ui.ui
ui/bezierspline_ui.ui
ui/boolparamwidget_ui.ui
ui/clipdurationdialog_ui.ui
ui/clipproperties_ui.ui
ui/clipspeed_ui.ui
ui/clipstabilize_ui.ui
ui/cliptranscode_ui.ui
ui/collapsiblewidget_ui.ui
ui/colorclip_ui.ui
ui/colorplaneexport_ui.ui
ui/configcapture_ui.ui
ui/configenv_ui.ui
ui/configjogshuttle_ui.ui
ui/configmisc_ui.ui
ui/configproject_ui.ui
ui/configsdl_ui.ui
ui/configtimeline_ui.ui
ui/configtranscode_ui.ui
ui/cutjobdialog_ui.ui
ui/dvdwizardchapters_ui.ui
ui/dvdwizardmenu_ui.ui
ui/dvdwizardstatus_ui.ui
ui/dvdwizardvob_ui.ui
ui/effectlist_ui.ui
ui/fontval_ui.ui
ui/freesound_ui.ui
ui/geometrywidget_ui.ui
ui/gradientedit_ui.ui
ui/histogram_ui.ui
ui/keyframedialog_ui.ui
ui/keyframeeditor_ui.ui
ui/keyframewidget_ui.ui
ui/keywordval_ui.ui
ui/listparamwidget_ui.ui
ui/logindialog_ui.ui
ui/managecaptures_ui.ui
ui/manageencodingprofile_ui.ui
ui/markerdialog_ui.ui
ui/missingclips_ui.ui
ui/monitoreditwidget_ui.ui
ui/profiledialog_ui.ui
ui/projectsettings_ui.ui
ui/qtextclip_ui.ui
ui/recmonitor_ui.ui
ui/renderwidget_ui.ui
ui/rgbparade_ui.ui
ui/saveprofile_ui.ui
ui/scenecutdialog_ui.ui
ui/selectivecolor_ui.ui
ui/slideshowclip_ui.ui
ui/smconfig_ui.ui
ui/spacerdialog_ui.ui
ui/spectrogram_ui.ui
ui/templateclip_ui.ui
ui/timeline_ui.ui
ui/timelinebuttons_ui.ui
ui/titlewidget_ui.ui
ui/trackheader_ui.ui
ui/tracksconfigdialog_ui.ui
ui/transitionsettings_ui.ui
ui/unicodewidget_ui.ui
ui/urlval_ui.ui
ui/vectorscope_ui.ui
ui/waveform_ui.ui
ui/wipeval_ui.ui
ui/wizardcapture_ui.ui
ui/wizardcheck_ui.ui
ui/wizardextra_ui.ui
ui/wizardmltcheck_ui.ui
ui/wizardstandard_ui.ui
)
if(BUILD_JogShuttle)
list(APPEND kdenlive_SRCS
jogshuttle/jogmanager.cpp
jogshuttle/jogaction.cpp
jogshuttle/jogshuttle.cpp
jogshuttle/jogshuttleconfig.cpp
)
endif()
# Sets the icon on Windows and OSX
file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/../data/icons/*-apps-kdenlive.png")
ecm_add_app_icon(kdenlive_SRCS ICONS ${ICONS_SRCS})
qt5_add_dbus_adaptor(kdenlive_SRCS
org.kdenlive.MainWindow.xml
mainwindow.h
MainWindow
)
qt5_add_resources(kdenlive_extra_SRCS icons.qrc ui/resources.qrc uiresources.qrc)
add_library(kdenliveLib STATIC ${kdenlive_SRCS} ${kdenlive_UIS})
add_executable(kdenlive
main.cpp
${kdenlive_extra_SRCS}
)
target_link_libraries(kdenlive
kdenliveLib
)
# To compile kiss_fft.
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99")
# KDE definitions and include directories *must* always come first, Qt follows
# (to avoid breaking builds when KDE and/or Qt are installed to different
# prefixes).
include_directories(
${CMAKE_BINARY_DIR}
${MLT_INCLUDE_DIR}
${MLTPP_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/lib/external
${CMAKE_CURRENT_SOURCE_DIR}/lib
)
# Adds Qt definitions and include directories, and sets QT_LIBRARIES according
# to the components requested in find_package().
#include(${QT_USE_FILE})
target_link_libraries(kdenliveLib
KF5::WidgetsAddons
KF5::Archive
KF5::CoreAddons
KF5::KIOCore
KF5::KIOFileWidgets
KF5::KIOWidgets
KF5::NotifyConfig
KF5::NewStuff
KF5::DBusAddons
KF5::XmlGui
KF5::GuiAddons
KF5::Notifications
KF5::TextWidgets
KF5::Declarative
KF5::IconThemes
KF5::Crash
KF5::Solid
Qt5::Svg
${OPENGL_LIBRARIES}
${OPENGLES_LIBRARIES}
${MLT_LIBRARIES}
${MLTPP_LIBRARIES}
${CMAKE_DL_LIBS}
${CMAKE_THREAD_LIBS_INIT}
kiss_fft
)
message(STATUS "Found MLT++: ${MLTPP_LIBRARIES}")
if (KF5_FILEMETADATA)
add_definitions(-DKF5_USE_FILEMETADATA)
target_link_libraries(kdenliveLib KF5::FileMetaData)
endif()
qt5_use_modules( kdenliveLib Widgets Concurrent Qml Quick QuickWidgets)
if (Qt5WebKitWidgets_FOUND)
message(STATUS "Found Qt5 WebKitWidgets. You can use your Freesound.org credentials to download files")
add_definitions(-DQT5_USE_WEBKIT)
target_link_libraries(kdenliveLib Qt5::WebKitWidgets)
else()
message(STATUS "Qt5 WebKitWidgets not found. You cannot use your Freesound.org credentials, only preview files can be downloaded from the Online Resources Widget")
endif()
if(Q_WS_X11)
include_directories(${X11_Xlib_INCLUDE_PATH})
target_link_libraries(kdenliveLib ${X11_LIBRARIES})
endif(Q_WS_X11)
if(SDL2_FOUND)
target_link_libraries(kdenliveLib ${SDL2_LIBRARY})
elseif(SDL_FOUND)
target_link_libraries(kdenliveLib ${SDL_LIBRARY})
endif(SDL2_FOUND)
if(LIBV4L2_FOUND)
include_directories(${LIBV4L2_INCLUDE_DIR})
target_link_libraries(kdenliveLib ${LIBV4L2_LIBRARY})
add_definitions(-DUSE_V4L)
endif()
if(BUILD_JogShuttle)
add_definitions(-DUSE_JOGSHUTTLE)
target_link_libraries(kdenliveLib
media_ctrl
)
endif()
install(TARGETS kdenlive DESTINATION ${BIN_INSTALL_DIR})
install(FILES kdenliveui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/kdenlive)
diff --git a/src/abstractmodel/treeitem.cpp b/src/abstractmodel/treeitem.cpp
index 98e69ba6f..bfab19586 100644
--- a/src/abstractmodel/treeitem.cpp
+++ b/src/abstractmodel/treeitem.cpp
@@ -1,267 +1,267 @@
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
***************************************************************************/
#include "treeitem.hpp"
#include "abstracttreemodel.hpp"
#include
#include
#include
TreeItem::TreeItem(const QList &data, const std::shared_ptr &model, bool isRoot, int id)
: m_itemData(data)
, m_model(model)
, m_depth(0)
, m_id(id == -1 ? AbstractTreeModel::getNextId() : id)
, m_isInModel(false)
, m_isRoot(isRoot)
{
}
std::shared_ptr TreeItem::construct(const QList &data, std::shared_ptr model, bool isRoot, int id)
{
std::shared_ptr self(new TreeItem(data, std::move(model), isRoot, id));
baseFinishConstruct(self);
return self;
}
// static
void TreeItem::baseFinishConstruct(const std::shared_ptr &self)
{
if (self->m_isRoot) {
registerSelf(self);
}
}
TreeItem::~TreeItem()
{
deregisterSelf();
}
std::shared_ptr TreeItem::appendChild(const QList &data)
{
if (auto ptr = m_model.lock()) {
auto child = construct(data, ptr, false);
appendChild(child);
return child;
}
qDebug() << "ERROR: Something went wrong when appending child in TreeItem. Model is not available anymore";
Q_ASSERT(false);
return std::shared_ptr();
}
bool TreeItem::appendChild(std::shared_ptr child)
{
if (hasAncestor(child->getId())) {
// in that case, we are trying to create a cycle, abort
return false;
}
if (auto oldParent = child->parentItem().lock()) {
if (oldParent->getId() == m_id) {
// no change needed
return true;
} else {
// in that case a call to removeChild should have been carried out
qDebug() << "ERROR: trying to append a child that alrealdy has a parent";
return false;
}
}
if (auto ptr = m_model.lock()) {
ptr->notifyRowAboutToAppend(shared_from_this());
child->m_depth = m_depth + 1;
child->m_parentItem = shared_from_this();
int id = child->getId();
auto it = m_childItems.insert(m_childItems.end(), child);
m_iteratorTable[id] = it;
registerSelf(child);
ptr->notifyRowAppended(child);
return true;
}
qDebug() << "ERROR: Something went wrong when appending child in TreeItem. Model is not available anymore";
Q_ASSERT(false);
return false;
}
void TreeItem::moveChild(int ix, std::shared_ptr child)
{
if (auto ptr = m_model.lock()) {
auto childPtr = child->m_parentItem.lock();
if (childPtr && childPtr->getId() != m_id) {
childPtr->removeChild(child);
} else {
// deletion of child
auto it = m_iteratorTable[child->getId()];
m_childItems.erase(it);
}
ptr->notifyRowAboutToAppend(shared_from_this());
child->m_depth = m_depth + 1;
child->m_parentItem = shared_from_this();
int id = child->getId();
auto pos = m_childItems.begin();
std::advance(pos, ix);
auto it = m_childItems.insert(pos, child);
m_iteratorTable[id] = it;
ptr->notifyRowAppended(child);
m_isInModel = true;
} else {
qDebug() << "ERROR: Something went wrong when moving child in TreeItem. Model is not available anymore";
Q_ASSERT(false);
}
}
void TreeItem::removeChild(const std::shared_ptr &child)
{
if (auto ptr = m_model.lock()) {
ptr->notifyRowAboutToDelete(shared_from_this(), child->row());
// get iterator corresponding to child
Q_ASSERT(m_iteratorTable.count(child->getId()) > 0);
auto it = m_iteratorTable[child->getId()];
// deletion of child
m_childItems.erase(it);
// clean iterator table
m_iteratorTable.erase(child->getId());
child->m_depth = 0;
child->m_parentItem.reset();
child->deregisterSelf();
ptr->notifyRowDeleted();
} else {
qDebug() << "ERROR: Something went wrong when removing child in TreeItem. Model is not available anymore";
Q_ASSERT(false);
}
}
bool TreeItem::changeParent(std::shared_ptr newParent)
{
Q_ASSERT(!m_isRoot);
if (m_isRoot) return false;
std::shared_ptr oldParent;
- if (oldParent = m_parentItem.lock()) {
+ if ((oldParent = m_parentItem.lock())) {
oldParent->removeChild(shared_from_this());
}
bool res = true;
if (newParent) {
res = newParent->appendChild(shared_from_this());
if (res) {
m_parentItem = newParent;
} else if (oldParent) {
// something went wrong, we have to reset the parent.
bool reverse = oldParent->appendChild(shared_from_this());
Q_ASSERT(reverse);
}
}
return res;
}
std::shared_ptr TreeItem::child(int row) const
{
Q_ASSERT(row >= 0 && row < (int)m_childItems.size());
auto it = m_childItems.cbegin();
std::advance(it, row);
return (*it);
}
int TreeItem::childCount() const
{
return (int)m_childItems.size();
}
int TreeItem::columnCount() const
{
return m_itemData.count();
}
QVariant TreeItem::dataColumn(int column) const
{
return m_itemData.value(column);
}
std::weak_ptr TreeItem::parentItem() const
{
return m_parentItem;
}
int TreeItem::row() const
{
if (auto ptr = m_parentItem.lock()) {
// we compute the distance in the parent's children list
auto it = ptr->m_childItems.begin();
return (int)std::distance(it, (decltype(it))ptr->m_iteratorTable.at(m_id));
}
return -1;
}
int TreeItem::depth() const
{
return m_depth;
}
int TreeItem::getId() const
{
return m_id;
}
bool TreeItem::isInModel() const
{
return m_isInModel;
}
void TreeItem::registerSelf(std::shared_ptr self)
{
for (const auto &child : self->m_childItems) {
registerSelf(child);
}
if (auto ptr = self->m_model.lock()) {
ptr->registerItem(self);
self->m_isInModel = true;
} else {
qDebug() << "Error : construction of treeItem failed because parent model is not available anymore";
Q_ASSERT(false);
}
}
void TreeItem::deregisterSelf()
{
for (const auto &child : m_childItems) {
child->deregisterSelf();
}
if (m_isInModel) {
if (auto ptr = m_model.lock()) {
ptr->deregisterItem(m_id, this);
m_isInModel = false;
}
}
}
bool TreeItem::hasAncestor(int id)
{
if (m_id == id) {
return true;
}
if (auto ptr = m_parentItem.lock()) {
return ptr->hasAncestor(id);
}
return false;
}
bool TreeItem::isRoot() const
{
return m_isRoot;
}
diff --git a/src/bin/abstractprojectitem.cpp b/src/bin/abstractprojectitem.cpp
index 493f5338f..777d4adb8 100644
--- a/src/bin/abstractprojectitem.cpp
+++ b/src/bin/abstractprojectitem.cpp
@@ -1,262 +1,285 @@
/*
Copyright (C) 2012 Till Theato
Copyright (C) 2014 Jean-Baptiste Mardelle
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 .
*/
#include "abstractprojectitem.h"
#include "bin.h"
+#include "core.h"
+#include "jobs/jobmanager.h"
#include "macros.hpp"
#include "projectitemmodel.h"
#include
#include
#include
AbstractProjectItem::AbstractProjectItem(PROJECTITEMTYPE type, const QString &id, const std::shared_ptr &model, bool isRoot)
: TreeItem(QList(), std::static_pointer_cast(model), isRoot)
, m_name()
, m_description()
, m_thumbnail(QIcon())
, m_date()
, m_binId(id)
, m_usage(0)
, m_clipStatus(StatusReady)
- , m_jobType(AbstractClipJob::NOJOBTYPE)
- , m_jobProgress(0)
, m_itemType(type)
- , m_isCurrent(false)
, m_lock(QReadWriteLock::Recursive)
-{
-}
-
-AbstractProjectItem::AbstractProjectItem(PROJECTITEMTYPE type, const QString &id, const QDomElement &description, const std::shared_ptr &model)
- : TreeItem(QList(), std::static_pointer_cast(model), false)
- , m_name()
- , m_description()
- , m_thumbnail(QIcon())
- , m_date()
- , m_binId(id)
- , m_usage(0)
- , m_clipStatus(StatusReady)
- , m_jobType(AbstractClipJob::NOJOBTYPE)
- , m_jobProgress(0)
- , m_itemType(type)
, m_isCurrent(false)
- , m_lock(QReadWriteLock::Recursive)
{
+ Q_ASSERT(!isRoot || type == FolderItem);
}
bool AbstractProjectItem::operator==(const std::shared_ptr &projectItem) const
{
// FIXME: only works for folders
bool equal = this->m_childItems == projectItem->m_childItems;
// equal = equal && (m_parentItem == projectItem->m_parentItem);
return equal;
}
std::shared_ptr AbstractProjectItem::parent() const
{
return std::static_pointer_cast(m_parentItem.lock());
}
void AbstractProjectItem::setRefCount(uint count)
{
m_usage = count;
if (auto ptr = m_model.lock())
std::static_pointer_cast(ptr)->onItemUpdated(std::static_pointer_cast(shared_from_this()));
}
uint AbstractProjectItem::refCount() const
{
return m_usage;
}
void AbstractProjectItem::addRef()
{
m_usage++;
if (auto ptr = m_model.lock())
std::static_pointer_cast(ptr)->onItemUpdated(std::static_pointer_cast(shared_from_this()));
}
void AbstractProjectItem::removeRef()
{
m_usage--;
if (auto ptr = m_model.lock())
std::static_pointer_cast(ptr)->onItemUpdated(std::static_pointer_cast(shared_from_this()));
}
const QString &AbstractProjectItem::clipId() const
{
return m_binId;
}
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;
}
AbstractProjectItem::PROJECTITEMTYPE AbstractProjectItem::itemType() const
{
return m_itemType;
}
QVariant AbstractProjectItem::getData(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 DataId:
data = QVariant(m_id);
break;
case DataDuration:
data = QVariant(m_duration);
break;
case DataDate:
data = QVariant(m_date);
break;
case UsageCount:
data = QVariant(m_usage);
break;
case ItemTypeRole:
data = QVariant(m_itemType);
break;
case JobType:
- data = QVariant(m_jobType);
+ if (itemType() == ClipItem) {
+ auto jobIds = pCore->jobManager()->getPendingJobsIds(clipId());
+ if (jobIds.empty()) {
+ jobIds = pCore->jobManager()->getFinishedJobsIds(clipId());
+ }
+ if (jobIds.size() > 0) {
+ data = QVariant(pCore->jobManager()->getJobType(jobIds[0]));
+ }
+ }
+ break;
+ case JobStatus:
+ if (itemType() == ClipItem) {
+ auto jobIds = pCore->jobManager()->getPendingJobsIds(clipId());
+ if (jobIds.empty()) {
+ jobIds = pCore->jobManager()->getFinishedJobsIds(clipId());
+ }
+ if (jobIds.size() > 0) {
+ data = QVariant(pCore->jobManager()->getJobType(jobIds[0]));
+ } else {
+ data = QVariant::fromValue(JobStatus::NoJob);
+ }
+ }
break;
case JobProgress:
- data = QVariant(m_jobProgress);
+ if (itemType() == ClipItem) {
+ auto jobIds = pCore->jobManager()->getPendingJobsIds(clipId());
+ if (jobIds.size() > 0) {
+ data = QVariant(pCore->jobManager()->getJobProgressForClip(jobIds[0], clipId()));
+ } else {
+ data = QVariant(0);
+ }
+ }
break;
case JobMessage:
- data = QVariant(m_jobMessage);
+ if (itemType() == ClipItem) {
+ QString messages;
+ auto jobIds = pCore->jobManager()->getPendingJobsIds(clipId());
+ for (int job : jobIds) {
+ messages.append(pCore->jobManager()->getJobMessageForClip(job, clipId()));
+ }
+ jobIds = pCore->jobManager()->getFinishedJobsIds(clipId());
+ for (int job : jobIds) {
+ messages.append(pCore->jobManager()->getJobMessageForClip(job, clipId()));
+ }
+ data = QVariant(messages);
+ }
break;
case ClipStatus:
data = QVariant(m_clipStatus);
break;
case ClipToolTip:
data = QVariant(getToolTip());
break;
default:
break;
}
return data;
}
int AbstractProjectItem::supportedDataCount() const
{
return 3;
}
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;
}
QPoint AbstractProjectItem::zone() const
{
return QPoint();
}
void AbstractProjectItem::setClipStatus(CLIPSTATUS status)
{
m_clipStatus = status;
}
bool AbstractProjectItem::statusReady() const
{
return m_clipStatus == StatusReady;
}
AbstractProjectItem::CLIPSTATUS AbstractProjectItem::clipStatus() const
{
return m_clipStatus;
}
std::shared_ptr AbstractProjectItem::getEnclosingFolder(bool strict)
{
if (!strict && itemType() == AbstractProjectItem::FolderItem) {
return std::static_pointer_cast(shared_from_this());
}
if (auto ptr = m_parentItem.lock()) {
return std::static_pointer_cast(ptr)->getEnclosingFolder(false);
}
return std::shared_ptr();
}
bool AbstractProjectItem::selfSoftDelete(Fun &undo, Fun &redo)
{
Fun local_undo = []() { return true; };
Fun local_redo = []() { return true; };
for (const auto &child : m_childItems) {
bool res = std::static_pointer_cast(child)->selfSoftDelete(local_undo, local_redo);
if (!res) {
bool undone = local_undo();
Q_ASSERT(undone);
return false;
}
}
UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
return true;
}
QString AbstractProjectItem::lastParentId() const
{
return m_lastParentId;
}
bool AbstractProjectItem::changeParent(std::shared_ptr newParent)
{
m_lastParentId.clear();
if (newParent) {
m_lastParentId = std::static_pointer_cast(newParent)->clipId();
}
return TreeItem::changeParent(newParent);
}
diff --git a/src/bin/abstractprojectitem.h b/src/bin/abstractprojectitem.h
index 031cd5b16..e1e7da70e 100644
--- a/src/bin/abstractprojectitem.h
+++ b/src/bin/abstractprojectitem.h
@@ -1,226 +1,214 @@
/*
Copyright (C) 2012 Till Theato
Copyright (C) 2014 Jean-Baptiste Mardelle
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 .
*/
#ifndef ABSTRACTPROJECTITEM_H
#define ABSTRACTPROJECTITEM_H
#include "abstractmodel/treeitem.hpp"
-#include "project/jobs/abstractclipjob.h"
#include "undohelper.hpp"
#include
#include
#include
class ProjectClip;
class ProjectFolder;
class Bin;
class QDomElement;
class QDomDocument;
class ProjectItemModel;
/**
* @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 TreeItem
{
Q_OBJECT
public:
enum PROJECTITEMTYPE { FolderUpItem = 0, FolderItem = 1, ClipItem = 2, SubClipItem = 3 };
/**
* @brief Constructor.
* @param type is the type of the bin item
* @param id is the binId
* @param model is the ptr to the item model
* @param isRoot is true if this is the topmost folder
*/
AbstractProjectItem(PROJECTITEMTYPE type, const QString &id, const std::shared_ptr &model, bool isRoot = false);
- /**
- * @brief Creates a project item upon project load.
- * @param description element for this item.
- * @param model pointer to the model this item is added to.
- * @param parent parent this item should be added to
- *
- * We try to read the attributes "name" and "description"
- */
- AbstractProjectItem(PROJECTITEMTYPE type, const QString &id, const QDomElement &description, const std::shared_ptr &model);
bool operator==(const std::shared_ptr &projectItem) const;
/** @brief Returns a pointer to the parent item (or NULL). */
std::shared_ptr parent() 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 std::shared_ptr clip(const QString &id) = 0;
/** @brief Used to search for a folder with a specific id. */
virtual std::shared_ptr folder(const QString &id) = 0;
virtual std::shared_ptr clipAt(int ix) = 0;
/** @brief Recursively disable/enable bin effects. */
virtual void setBinEffectsEnabled(bool enabled) = 0;
/** @brief This function executes what should be done when the item is deleted
but without deleting effectively.
For example, the item will deregister itself from the model and delete the
clips from the timeline.
However, the object is NOT actually deleted, and the tree structure is preserved.
@param Undo,Redo are the lambdas accumulating the update.
*/
virtual bool selfSoftDelete(Fun &undo, Fun &redo);
/** @brief Returns the clip's id. */
const QString &clipId() const;
virtual QPoint zone() const;
// TODO refac : these ref counting are probably deprecated by smart ptrs
/** @brief Set current usage count. */
void setRefCount(uint count);
/** @brief Returns clip's current usage count in timeline. */
uint refCount() const;
/** @brief Increase usage count. */
void addRef();
/** @brief Decrease usage count. */
void removeRef();
enum DataType {
// display name of item
DataName = Qt::DisplayRole,
// image thumbnail
DataThumbnail = Qt::DecorationRole,
// Tooltip text,usually full path
ClipToolTip = Qt::ToolTipRole,
// unique id of the project clip / folder
DataId = Qt::UserRole,
// creation date
DataDate,
// Description for item (user editable)
DataDescription,
// Number of occurrences used in timeline
UsageCount,
// Empty if clip has no effect, icon otherwise
IconOverlay,
// item type (clip, subclip, folder)
ItemTypeRole,
// Duration of the clip
DataDuration,
// If there is a running job, which type
JobType,
// Current progress of the job
JobProgress,
// error message if job crashes (not fully implemented)
JobMessage,
+ JobStatus,
// Item status (ready or not, missing, waiting, ...)
ClipStatus
};
enum CLIPSTATUS { StatusReady = 0, StatusMissing, StatusWaiting, StatusDeleting };
void setClipStatus(AbstractProjectItem::CLIPSTATUS status);
AbstractProjectItem::CLIPSTATUS clipStatus() const;
bool statusReady() const;
/** @brief Returns the data that describes this item.
* @param type type of data to return
*
* This function is necessary for interaction with ProjectItemModel.
*/
QVariant getData(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);
virtual QDomElement toXml(QDomDocument &document, bool includeMeta = false) = 0;
virtual QString getToolTip() const = 0;
virtual bool rename(const QString &name, int column) = 0;
/* @brief Return the bin id of the last parent that this element got, even if this
parent has already been destroyed.
Return the empty string if the element was parentless */
QString lastParentId() const;
/* @brief This is an overload of TreeItem::changeParent that tracks the id of the id of the parent */
bool changeParent(std::shared_ptr newParent) override;
/* Returns a ptr to the enclosing dir, and nullptr if none is found.
@param strict if set to false, the enclosing dir of a dir is itself, otherwise we try to find a "true" parent
*/
std::shared_ptr getEnclosingFolder(bool strict = false);
/** @brief Returns true if a clip corresponding to this bin is inserted in a timeline.
Note that this function does not account for children, use TreeItem::accumulate if you want to get that information as well.
*/
virtual bool isIncludedInTimeline() { return false; }
signals:
void childAdded(AbstractProjectItem *child);
void aboutToRemoveChild(AbstractProjectItem *child);
protected:
QString m_name;
QString m_description;
QIcon m_thumbnail;
QString m_duration;
QDateTime m_date;
QString m_binId;
uint m_usage;
CLIPSTATUS m_clipStatus;
- AbstractClipJob::JOBTYPE m_jobType;
- int m_jobProgress;
- QString m_jobMessage;
PROJECTITEMTYPE m_itemType;
QString m_lastParentId;
/** @brief Returns a rounded border pixmap from the @param source pixmap. */
QPixmap roundedPixmap(const QPixmap &source);
mutable QReadWriteLock m_lock; // This is a lock that ensures safety in case of concurrent access
private:
bool m_isCurrent;
};
#endif
diff --git a/src/bin/bin.cpp b/src/bin/bin.cpp
index 32e4437a9..95e4d382a 100644
--- a/src/bin/bin.cpp
+++ b/src/bin/bin.cpp
@@ -1,3463 +1,3179 @@
/*
Copyright (C) 2012 Till Theato
Copyright (C) 2014 Jean-Baptiste Mardelle
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 .
*/
#include "bin.h"
#include "bincommands.h"
+#include "clipcreator.hpp"
#include "core.h"
#include "dialogs/clipcreationdialog.h"
#include "doc/documentchecker.h"
#include "doc/docundostack.hpp"
#include "doc/kdenlivedoc.h"
+#include "jobs/jobmanager.h"
+#include "jobs/loadjob.hpp"
#include "kdenlive_debug.h"
#include "kdenlivesettings.h"
#include "mainwindow.h"
#include "mlt++/Mlt.h"
#include "mltcontroller/clipcontroller.h"
#include "mltcontroller/clippropertiescontroller.h"
#include "monitor/monitor.h"
#include "project/clipmanager.h"
#include "project/dialogs/slideshowclip.h"
#include "project/invaliddialog.h"
-#include "project/jobs/jobmanager.h"
#include "project/projectcommands.h"
#include "project/projectmanager.h"
#include "projectclip.h"
#include "projectfolder.h"
#include "projectfolderup.h"
#include "projectitemmodel.h"
#include "projectsortproxymodel.h"
#include "projectsubclip.h"
#include "titler/titlewidget.h"
#include "ui_qtextclip_ui.h"
#include "undohelper.hpp"
#include "utils/KoIconUtils.h"
#include "xml/xml.hpp"
#include
#include
#include
#include
#include "kdenlive_debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
MyListView::MyListView(QWidget *parent)
: QListView(parent)
{
setViewMode(QListView::IconMode);
setMovement(QListView::Static);
setResizeMode(QListView::Adjust);
setUniformItemSizes(true);
setDragDropMode(QAbstractItemView::DragDrop);
setAcceptDrops(true);
setDragEnabled(true);
viewport()->setAcceptDrops(true);
}
void MyListView::focusInEvent(QFocusEvent *event)
{
QListView::focusInEvent(event);
if (event->reason() == Qt::MouseFocusReason) {
emit focusView();
}
}
MyTreeView::MyTreeView(QWidget *parent)
: QTreeView(parent)
{
setEditing(false);
}
void MyTreeView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_startPos = event->pos();
}
QTreeView::mousePressEvent(event);
}
void MyTreeView::focusInEvent(QFocusEvent *event)
{
QTreeView::focusInEvent(event);
if (event->reason() == Qt::MouseFocusReason) {
emit focusView();
}
}
void MyTreeView::mouseMoveEvent(QMouseEvent *event)
{
bool dragged = false;
if ((event->buttons() & Qt::LeftButton) != 0u) {
int distance = (event->pos() - m_startPos).manhattanLength();
if (distance >= QApplication::startDragDistance()) {
dragged = performDrag();
}
}
if (!dragged) {
QTreeView::mouseMoveEvent(event);
}
}
void MyTreeView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
{
QAbstractItemView::closeEditor(editor, hint);
setEditing(false);
}
void MyTreeView::editorDestroyed(QObject *editor)
{
QAbstractItemView::editorDestroyed(editor);
setEditing(false);
}
void MyTreeView::keyPressEvent(QKeyEvent *event)
{
if (isEditing()) {
QTreeView::keyPressEvent(event);
return;
}
QModelIndex currentIndex = this->currentIndex();
if (event->key() == Qt::Key_Return && currentIndex.isValid()) {
if (this->isExpanded(currentIndex)) {
this->collapse(currentIndex);
} else {
this->expand(currentIndex);
}
}
QTreeView::keyPressEvent(event);
}
bool MyTreeView::isEditing() const
{
return state() == QAbstractItemView::EditingState;
}
void MyTreeView::setEditing(bool edit)
{
setState(edit ? QAbstractItemView::EditingState : QAbstractItemView::NoState);
}
bool MyTreeView::performDrag()
{
QModelIndexList bases = selectedIndexes();
QModelIndexList indexes;
for (int i = 0; i < bases.count(); i++) {
if (bases.at(i).column() == 0) {
indexes << bases.at(i);
}
}
if (indexes.isEmpty()) {
return false;
}
auto *drag = new QDrag(this);
drag->setMimeData(model()->mimeData(indexes));
QModelIndex ix = indexes.constFirst();
if (ix.isValid()) {
QIcon icon = ix.data(AbstractProjectItem::DataThumbnail).value();
QPixmap pix = icon.pixmap(iconSize());
QSize size = pix.size();
QImage image(size, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setOpacity(0.7);
p.drawPixmap(0, 0, pix);
p.setOpacity(1);
if (indexes.count() > 1) {
QPalette palette;
int radius = size.height() / 3;
p.setBrush(palette.highlight());
p.setPen(palette.highlightedText().color());
p.drawEllipse(QPoint(size.width() / 2, size.height() / 2), radius, radius);
p.drawText(size.width() / 2 - radius, size.height() / 2 - radius, 2 * radius, 2 * radius, Qt::AlignCenter, QString::number(indexes.count()));
}
p.end();
drag->setPixmap(QPixmap::fromImage(image));
}
drag->exec();
return true;
}
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)
, m_action(nullptr)
{
setFixedWidth(0);
setFlat(true);
m_timeLine = new QTimeLine(500, this);
QObject::connect(m_timeLine, &QTimeLine::valueChanged, this, &SmallJobLabel::slotTimeLineChanged);
QObject::connect(m_timeLine, &QTimeLine::finished, this, &SmallJobLabel::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 =
QStringLiteral("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(
QStringLiteral("\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 (style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this) != 0) {
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 (style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this) != 0) {
setFixedWidth(0);
m_action->setVisible(false);
return;
}
// hide
m_timeLine->setDirection(QTimeLine::Backward);
if (m_timeLine->state() == QTimeLine::NotRunning) {
m_timeLine->start();
}
}
}
/**
* @class BinItemDelegate
* @brief This class is responsible for drawing items in the QTreeView.
*/
class BinItemDelegate : public QStyledItemDelegate
{
public:
explicit BinItemDelegate(QObject *parent = nullptr)
: QStyledItemDelegate(parent)
{
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
if (index.column() != 0) {
return QStyledItemDelegate::updateEditorGeometry(editor, option, index);
}
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
QRect r1 = option.rect;
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
int type = index.data(AbstractProjectItem::ItemTypeRole).toInt();
double factor = (double)opt.decorationSize.height() / r1.height();
int decoWidth = 2 * textMargin;
int mid = 0;
if (factor > 0) {
decoWidth += opt.decorationSize.width() / factor;
}
if (type == AbstractProjectItem::ClipItem || type == AbstractProjectItem::SubClipItem) {
mid = (int)((r1.height() / 2));
}
r1.adjust(decoWidth, 0, 0, -mid);
QFont ft = option.font;
ft.setBold(true);
QFontMetricsF fm(ft);
QRect r2 = fm.boundingRect(r1, Qt::AlignLeft | Qt::AlignTop, index.data(AbstractProjectItem::DataName).toString()).toRect();
editor->setGeometry(r2);
}
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
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(), qMax(option.fontMetrics.lineSpacing() * 2 + 4, qMin(hint.height(), (int)(option.decorationSize.height() / 1.5))));
}
QIcon icon = qvariant_cast(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 override
{
if (index.column() == 0 && !index.data().isNull()) {
QRect r1 = option.rect;
painter->save();
painter->setClipRect(r1);
QStyleOptionViewItem 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 & static_cast((QStyle::State_Selected) != 0)) != 0) {
painter->setPen(option.palette.highlightedText().color());
} else {
painter->setPen(option.palette.text().color());
}
QRect r = r1;
QFont font = painter->font();
font.setBold(true);
painter->setFont(font);
if (type == AbstractProjectItem::ClipItem || type == AbstractProjectItem::SubClipItem) {
double factor = (double)opt.decorationSize.height() / r1.height();
int decoWidth = 2 * textMargin;
if (factor > 0) {
r.setWidth(opt.decorationSize.width() / factor);
// Draw thumbnail
opt.icon.paint(painter, r);
decoWidth += r.width();
}
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();
if (!subText.isEmpty()) {
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);
// Draw usage counter
int usage = index.data(AbstractProjectItem::UsageCount).toInt();
if (usage > 0) {
bounding.moveLeft(bounding.right() + (2 * textMargin));
QString us = QString().sprintf("[%d]", usage);
painter->drawText(bounding, Qt::AlignLeft | Qt::AlignTop, us, &bounding);
}
}
if (type == AbstractProjectItem::ClipItem) {
// 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 == JobWaiting) {
+ JobStatus status = index.data(AbstractProjectItem::JobStatus).value();
+ if (status == JobStatus::Pending || status == JobStatus::Running) {
// Draw job progress bar
int progressWidth = option.fontMetrics.averageCharWidth() * 8;
int progressHeight = option.fontMetrics.ascent() / 4;
QRect progress(r1.x() + 1, opt.rect.bottom() - progressHeight - 2, progressWidth, progressHeight);
painter->setPen(Qt::NoPen);
painter->setBrush(Qt::darkGray);
- if (jobProgress > 0) {
+ if (status == JobStatus::Running) {
painter->drawRoundedRect(progress, 2, 2);
painter->setBrush((option.state & static_cast((QStyle::State_Selected) != 0)) != 0 ? option.palette.text()
: option.palette.highlight());
progress.setWidth((progressWidth - 2) * jobProgress / 100);
painter->drawRoundedRect(progress, 2, 2);
- } else if (jobProgress == JobWaiting) {
+ } else {
// Draw kind of a pause icon
progress.setWidth(3);
painter->drawRect(progress);
progress.moveLeft(progress.right() + 3);
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);
- }
+ }
+ 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 {
// Folder or Folder Up items
double factor = (double)opt.decorationSize.height() / r1.height();
int decoWidth = 2 * textMargin;
if (factor > 0) {
r.setWidth(opt.decorationSize.width() / factor);
// Draw thumbnail
opt.icon.paint(painter, r);
decoWidth += r.width();
}
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);
}
}
};
LineEventEater::LineEventEater(QObject *parent)
: QObject(parent)
{
}
bool LineEventEater::eventFilter(QObject *obj, QEvent *event)
{
switch (event->type()) {
case QEvent::ShortcutOverride:
if (((QKeyEvent *)event)->key() == Qt::Key_Escape) {
emit clearSearchLine();
}
break;
case QEvent::Resize:
// Workaround Qt BUG 54676
emit showClearButton(((QResizeEvent *)event)->size().width() > QFontMetrics(QApplication::font()).averageCharWidth() * 8);
break;
default:
break;
}
return QObject::eventFilter(obj, event);
}
Bin::Bin(const std::shared_ptr &model, QWidget *parent)
: QWidget(parent)
, isLoading(false)
, m_itemModel(model)
, m_itemView(nullptr)
- , m_jobManager(nullptr)
, m_doc(nullptr)
, m_extractAudioAction(nullptr)
, m_transcodeAction(nullptr)
, m_clipsActionsMenu(nullptr)
, m_inTimelineAction(nullptr)
, m_listType((BinViewType)KdenliveSettings::binMode())
, m_iconSize(160, 90)
, m_propertiesPanel(nullptr)
, m_blankThumb()
, m_invalidClipDialog(nullptr)
, m_gainedFocus(false)
, m_audioDuration(0)
, m_processedAudio(0)
{
m_layout = new QVBoxLayout(this);
// Create toolbar for buttons
m_toolbar = new QToolBar(this);
m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
m_layout->addWidget(m_toolbar);
// Search line
m_proxyModel = new ProjectSortProxyModel(this);
m_proxyModel->setDynamicSortFilter(true);
m_searchLine = new QLineEdit(this);
m_searchLine->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
// m_searchLine->setClearButtonEnabled(true);
m_searchLine->setPlaceholderText(i18n("Search"));
m_searchLine->setFocusPolicy(Qt::ClickFocus);
connect(m_searchLine, &QLineEdit::textChanged, m_proxyModel, &ProjectSortProxyModel::slotSetSearchString);
auto *leventEater = new LineEventEater(this);
m_searchLine->installEventFilter(leventEater);
connect(leventEater, &LineEventEater::clearSearchLine, m_searchLine, &QLineEdit::clear);
connect(leventEater, &LineEventEater::showClearButton, this, &Bin::showClearButton);
setFocusPolicy(Qt::ClickFocus);
- connect(m_itemModel.get(), &ProjectItemModel::updateThumbProgress, this, &Bin::doUpdateThumbsProgress);
- connect(m_itemModel.get(), &ProjectItemModel::abortAudioThumb, this, &Bin::slotAbortAudioThumb);
- connect(m_itemModel.get(), &ProjectItemModel::refreshAudioThumbs, this, &Bin::refreshAudioThumbs);
- connect(m_itemModel.get(), &ProjectItemModel::reloadProducer, this, &Bin::reloadProducer);
connect(m_itemModel.get(), &ProjectItemModel::refreshPanel, this, &Bin::refreshPanel);
- connect(m_itemModel.get(), &ProjectItemModel::requestAudioThumbs, this, &Bin::requestAudioThumbs);
- connect(m_itemModel.get(), &ProjectItemModel::discardJobs, [&](const QString &id, AbstractClipJob::JOBTYPE type){
- if (hasPendingJob(id, type))
- discardJobs(id, type);
- });
- connect(m_itemModel.get(), &ProjectItemModel::startJob, this, &Bin::startJob);
+ connect(m_itemModel.get(), &ProjectItemModel::refreshAudioThumbs, this, &Bin::doRefreshAudioThumbs);
connect(m_itemModel.get(), &ProjectItemModel::refreshClip, this, &Bin::refreshClip);
connect(m_itemModel.get(), &ProjectItemModel::updateTimelineProducers, this, &Bin::updateTimelineProducers);
connect(m_itemModel.get(), &ProjectItemModel::emitMessage, this, &Bin::emitMessage);
// Connect models
m_proxyModel->setSourceModel(m_itemModel.get());
connect(m_itemModel.get(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), m_proxyModel, SLOT(slotDataChanged(const QModelIndex &, const QModelIndex &)));
connect(m_proxyModel, &ProjectSortProxyModel::selectModel, this, &Bin::selectProxyModel);
connect(m_itemModel.get(), SIGNAL(itemDropped(QStringList, QModelIndex)), this, SLOT(slotItemDropped(QStringList, QModelIndex)));
connect(m_itemModel.get(), SIGNAL(itemDropped(QList, QModelIndex)), this, SLOT(slotItemDropped(QList, QModelIndex)));
connect(m_itemModel.get(), &ProjectItemModel::effectDropped, this, &Bin::slotEffectDropped);
connect(m_itemModel.get(), &QAbstractItemModel::dataChanged, this, &Bin::slotItemEdited);
- connect(m_itemModel.get(), &ProjectItemModel::addClipCut, this, &Bin::slotAddClipCut);
connect(this, &Bin::refreshPanel, this, &Bin::doRefreshPanel);
// Zoom slider
m_slider = new QSlider(Qt::Horizontal, this);
m_slider->setMaximumWidth(100);
m_slider->setMinimumWidth(40);
m_slider->setRange(0, 10);
m_slider->setValue(KdenliveSettings::bin_zoom());
connect(m_slider, &QAbstractSlider::valueChanged, this, &Bin::slotSetIconSize);
auto *widgetslider = new QWidgetAction(this);
widgetslider->setDefaultWidget(m_slider);
// View type
KSelectAction *listType = new KSelectAction(KoIconUtils::themedIcon(QStringLiteral("view-list-tree")), i18n("View Mode"), this);
pCore->window()->actionCollection()->addAction(QStringLiteral("bin_view_mode"), listType);
QAction *treeViewAction = listType->addAction(KoIconUtils::themedIcon(QStringLiteral("view-list-tree")), i18n("Tree View"));
listType->addAction(treeViewAction);
treeViewAction->setData(BinTreeView);
if (m_listType == treeViewAction->data().toInt()) {
listType->setCurrentAction(treeViewAction);
}
pCore->window()->actionCollection()->addAction(QStringLiteral("bin_view_mode_tree"), treeViewAction);
QAction *iconViewAction = listType->addAction(KoIconUtils::themedIcon(QStringLiteral("view-list-icons")), i18n("Icon View"));
iconViewAction->setData(BinIconView);
if (m_listType == iconViewAction->data().toInt()) {
listType->setCurrentAction(iconViewAction);
}
pCore->window()->actionCollection()->addAction(QStringLiteral("bin_view_mode_icon"), iconViewAction);
QAction *disableEffects = new QAction(i18n("Disable Bin Effects"), this);
connect(disableEffects, &QAction::triggered, [this](bool disable) { this->setBinEffectsEnabled(!disable); });
disableEffects->setIcon(KoIconUtils::themedIcon(QStringLiteral("favorite")));
disableEffects->setData("disable_bin_effects");
disableEffects->setCheckable(true);
disableEffects->setChecked(false);
pCore->window()->actionCollection()->addAction(QStringLiteral("disable_bin_effects"), disableEffects);
#if KXMLGUI_VERSION_MINOR > 24 || KXMLGUI_VERSION_MAJOR > 5
m_renameAction = KStandardAction::renameFile(this, SLOT(slotRenameItem()), this);
m_renameAction->setText(i18n("Rename"));
#else
m_renameAction = new QAction(i18n("Rename"), this);
connect(m_renameAction, &QAction::triggered, this, &Bin::slotRenameItem);
m_renameAction->setShortcut(Qt::Key_F2);
#endif
m_renameAction->setData("rename");
pCore->window()->actionCollection()->addAction(QStringLiteral("rename"), m_renameAction);
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(KoIconUtils::themedIcon(QStringLiteral("zoom-in")));
sliderMenu->addAction(widgetslider);
settingsMenu->addMenu(sliderMenu);
// Column show / hide actions
m_showDate = new QAction(i18n("Show date"), this);
m_showDate->setCheckable(true);
connect(m_showDate, &QAction::triggered, this, &Bin::slotShowDateColumn);
m_showDesc = new QAction(i18n("Show description"), this);
m_showDesc->setCheckable(true);
connect(m_showDesc, &QAction::triggered, this, &Bin::slotShowDescColumn);
settingsMenu->addAction(m_showDate);
settingsMenu->addAction(m_showDesc);
settingsMenu->addAction(disableEffects);
auto *button = new QToolButton;
button->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-menu")));
button->setToolTip(i18n("Options"));
button->setMenu(settingsMenu);
button->setPopupMode(QToolButton::InstantPopup);
m_toolbar->addWidget(button);
// small info button for pending jobs
m_infoLabel = new SmallJobLabel(this);
m_infoLabel->setStyleSheet(SmallJobLabel::getStyleSheet(palette()));
+ connect(pCore->jobManager().get(), &JobManager::jobCount, m_infoLabel, &SmallJobLabel::slotSetJobCount);
QAction *infoAction = m_toolbar->addWidget(m_infoLabel);
m_jobsMenu = new QMenu(this);
- connect(m_jobsMenu, &QMenu::aboutToShow, this, &Bin::slotPrepareJobsMenu);
+ // connect(m_jobsMenu, &QMenu::aboutToShow, this, &Bin::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_discardPendingJobs = new QAction(i18n("Cancel Pending Jobs"), this);
m_discardPendingJobs->setCheckable(false);
m_jobsMenu->addAction(m_cancelJobs);
m_jobsMenu->addAction(m_discardCurrentClipJobs);
m_jobsMenu->addAction(m_discardPendingJobs);
m_infoLabel->setMenu(m_jobsMenu);
m_infoLabel->setAction(infoAction);
// Hack, create toolbar spacer
QWidget *spacer = new QWidget();
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_toolbar->addWidget(spacer);
// Add search line
m_toolbar->addWidget(m_searchLine);
m_binTreeViewDelegate = new BinItemDelegate(this);
// connect(pCore->projectManager(), SIGNAL(projectOpened(Project*)), this, SLOT(setProject(Project*)));
m_headerInfo = QByteArray::fromBase64(KdenliveSettings::treeviewheaders().toLatin1());
m_propertiesPanel = new QScrollArea(this);
m_propertiesPanel->setFrameShape(QFrame::NoFrame);
// Info widget for failed jobs, other errors
m_infoMessage = new BinMessageWidget(this);
m_layout->addWidget(m_infoMessage);
m_infoMessage->setCloseButtonVisible(false);
- connect(m_infoMessage, &KMessageWidget::linkActivated, this, &Bin::slotShowJobLog);
connect(m_infoMessage, &BinMessageWidget::messageClosing, this, &Bin::slotResetInfoMessage);
// m_infoMessage->setWordWrap(true);
m_infoMessage->hide();
connect(this, SIGNAL(requesteInvalidRemoval(QString, QString, QString)), this, SLOT(slotQueryRemoval(QString, QString, QString)));
- connect(this, &Bin::refreshAudioThumbs, this, &Bin::doRefreshAudioThumbs);
connect(this, SIGNAL(displayBinMessage(QString, KMessageWidget::MessageType)), this, SLOT(doDisplayMessage(QString, KMessageWidget::MessageType)));
}
Bin::~Bin()
{
blockSignals(true);
m_proxyModel->selectionModel()->blockSignals(true);
setEnabled(false);
abortOperations();
m_itemModel->clean();
}
QDockWidget *Bin::clipPropertiesDock()
{
return m_propertiesDock;
}
-void Bin::slotAbortAudioThumb(const QString &id, long duration)
-{
- if (!m_audioThumbsThread.isRunning()) {
- return;
- }
- QMutexLocker aMutex(&m_audioThumbMutex);
- if (m_audioThumbsList.removeAll(id) > 0) {
- m_audioDuration -= duration;
- }
-}
-
-void Bin::requestAudioThumbs(const QString &id, long duration)
-{
- if (!m_audioThumbsList.contains(id) && m_processingAudioThumb != id) {
- m_audioThumbMutex.lock();
- m_audioThumbsList.append(id);
- m_audioDuration += duration;
- m_audioThumbMutex.unlock();
- processAudioThumbs();
- }
-}
-
-void Bin::doUpdateThumbsProgress(long ms)
-{
- int progress = int(((long)m_processedAudio + (long)ms) * 100 / (long)m_audioDuration);
- emitMessage(i18n("Creating audio thumbnails"), progress, ProcessingJobMessage);
-}
-
-void Bin::processAudioThumbs()
-{
- if (m_audioThumbsThread.isRunning()) {
- return;
- }
- m_audioThumbsThread = QtConcurrent::run(this, &Bin::slotCreateAudioThumbs);
-}
-
void Bin::abortOperations()
{
blockSignals(true);
abortAudioThumbs();
if (m_propertiesPanel) {
for (QWidget *w : m_propertiesPanel->findChildren()) {
delete w;
}
}
delete m_itemView;
m_itemView = nullptr;
- delete m_jobManager;
- m_jobManager = nullptr;
blockSignals(false);
}
void Bin::abortAudioThumbs()
{
+ // TODO refac
+ /*
if (!m_audioThumbsThread.isRunning()) {
return;
}
if (!m_processingAudioThumb.isEmpty()) {
std::shared_ptr clip = m_itemModel->getClipByBinID(m_processingAudioThumb);
if (clip) {
clip->abortAudioThumbs();
}
}
m_audioThumbMutex.lock();
for (const QString &id : m_audioThumbsList) {
std::shared_ptr clip = m_itemModel->getClipByBinID(id);
if (clip) {
clip->setJobStatus(AbstractClipJob::THUMBJOB, JobDone, 0);
}
}
m_audioThumbsList.clear();
m_audioThumbMutex.unlock();
m_audioThumbsThread.waitForFinished();
+ */
}
void Bin::slotCreateAudioThumbs()
{
+ // TODO refac
+ /*
int max = m_audioThumbsList.count();
int count = 0;
m_processedAudio = 0;
while (!m_audioThumbsList.isEmpty()) {
m_audioThumbMutex.lock();
max = qMax(max, m_audioThumbsList.count());
m_processingAudioThumb = m_audioThumbsList.takeFirst();
count++;
m_audioThumbMutex.unlock();
std::shared_ptr clip = m_itemModel->getClipByBinID(m_processingAudioThumb);
if (clip) {
clip->slotCreateAudioThumbs();
m_processedAudio += (int)clip->duration().ms();
} else {
- qDebug()<<"// Trying to create audio thumbs for unknown clip: "<type() == QEvent::MouseButtonRelease) {
if (!m_monitor->isActive()) {
m_monitor->slotActivateMonitor();
}
bool success = QWidget::eventFilter(obj, event);
if (m_gainedFocus) {
QMouseEvent *mouseEvent = static_cast(event);
QAbstractItemView *view = qobject_cast(obj->parent());
if (view) {
QModelIndex idx = view->indexAt(mouseEvent->pos());
m_gainedFocus = false;
if (idx.isValid()) {
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(idx));
editMasterEffect(item);
} else {
editMasterEffect(nullptr);
}
}
// make sure we discard the focus indicator
m_gainedFocus = false;
}
return success;
}
if (event->type() == QEvent::MouseButtonDblClick) {
QMouseEvent *mouseEvent = static_cast(event);
QAbstractItemView *view = qobject_cast(obj->parent());
if (view) {
QModelIndex idx = view->indexAt(mouseEvent->pos());
if (!idx.isValid()) {
// User double clicked on empty area
slotAddClip();
} else {
slotItemDoubleClicked(idx, mouseEvent->pos());
}
} else {
qCDebug(KDENLIVE_LOG) << " +++++++ NO VIEW-------!!";
}
return true;
}
if (event->type() == QEvent::Wheel) {
QWheelEvent *e = static_cast(event);
if ((e != nullptr) && e->modifiers() == Qt::ControlModifier) {
slotZoomView(e->delta() > 0);
// emit zoomView(e->delta() > 0);
return true;
}
}
return QWidget::eventFilter(obj, event);
}
void Bin::refreshIcons()
{
QList allMenus = this->findChildren();
for (int i = 0; i < allMenus.count(); i++) {
QMenu *m = allMenus.at(i);
QIcon ic = m->icon();
if (ic.isNull() || ic.name().isEmpty()) {
continue;
}
QIcon newIcon = KoIconUtils::themedIcon(ic.name());
m->setIcon(newIcon);
}
QList allButtons = this->findChildren();
for (int i = 0; i < allButtons.count(); i++) {
QToolButton *m = allButtons.at(i);
QIcon ic = m->icon();
if (ic.isNull() || ic.name().isEmpty()) {
continue;
}
QIcon newIcon = KoIconUtils::themedIcon(ic.name());
m->setIcon(newIcon);
}
}
void Bin::slotSaveHeaders()
{
if ((m_itemView != nullptr) && m_listType == BinTreeView) {
// save current treeview state (column width)
QTreeView *view = static_cast(m_itemView);
m_headerInfo = view->header()->saveState();
KdenliveSettings::setTreeviewheaders(m_headerInfo.toBase64());
}
}
void Bin::slotZoomView(bool zoomIn)
{
if (m_itemModel->rowCount() == 0) {
// Don't zoom on empty bin
return;
}
int progress = (zoomIn) ? 1 : -1;
m_slider->setValue(m_slider->value() + progress);
}
Monitor *Bin::monitor()
{
return m_monitor;
}
const QStringList Bin::getFolderInfo(const QModelIndex &selectedIx)
{
QModelIndexList indexes;
if (selectedIx.isValid()) {
indexes << selectedIx;
} else {
indexes = m_proxyModel->selectionModel()->selectedIndexes();
}
if (indexes.isEmpty()) {
// return root folder info
QStringList folderInfo;
folderInfo << QString::number(-1);
folderInfo << QString();
return folderInfo;
}
QModelIndex ix = indexes.constFirst();
if (ix.isValid() && (m_proxyModel->selectionModel()->isSelected(ix) || selectedIx.isValid())) {
return m_itemModel->getEnclosingFolderInfo(m_proxyModel->mapToSource(ix));
}
// return root folder info
QStringList folderInfo;
folderInfo << QString::number(-1);
folderInfo << QString();
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) {
- emit openClip(std::shared_ptr());
- }
- std::shared_ptr clip = m_itemModel->getClipByBinID(id);
- if (!clip) {
- qCWarning(KDENLIVE_LOG) << "Cannot bin find clip to delete: " << id;
- return;
- }
- m_jobManager->discardJobs(id);
- ClipType type = clip->clipType();
- QString url = clip->url();
- m_fileWatcher.removeFile(id, url);
- clip->setClipStatus(AbstractProjectItem::StatusDeleting);
- if (!m_processingAudioThumb.isEmpty()) {
- clip->abortAudioThumbs();
- }
- std::shared_ptr parent = clip->parent();
- Q_ASSERT(parent);
- parent->removeChild(clip);
- m_doc->deleteClip(id, type, url);
+ QString parentFolder = getCurrentFolder();
+ ClipCreationDialog::createClipsCommand(m_doc, parentFolder, m_itemModel);
}
std::shared_ptr Bin::getFirstSelectedClip()
{
const QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
if (indexes.isEmpty()) {
return std::shared_ptr();
}
for (const QModelIndex &ix : indexes) {
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(ix));
if (item->itemType() == AbstractProjectItem::ClipItem) {
auto clip = std::static_pointer_cast(item);
if (clip) {
return clip;
}
}
}
return nullptr;
}
void Bin::slotDeleteClip()
{
const QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
std::vector> items;
bool included = false;
bool usedFolder = false;
auto checkInclusion = [](bool accum, std::shared_ptr item) {
return accum || std::static_pointer_cast(item)->isIncludedInTimeline();
};
for (const QModelIndex &ix : indexes) {
if (!ix.isValid() || ix.column() != 0) {
continue;
}
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(ix));
if (!item) {
qDebug() << "Suspicious: item not found when trying to delete";
continue;
}
included = included || item->accumulate(false, checkInclusion);
// Check if we are deleting non-empty folders:
usedFolder = usedFolder || item->childCount() > 0;
items.push_back(item);
}
if (included && (KMessageBox::warningContinueCancel(this, i18n("This will delete all selected clips from timeline")) != KMessageBox::Continue)) {
return;
}
if (usedFolder && (KMessageBox::warningContinueCancel(this, i18n("This will delete all folder content")) != KMessageBox::Continue)) {
return;
}
Fun undo = []() { return true; };
Fun redo = []() { return true; };
for (const auto &item : items) {
m_itemModel->requestBinClipDeletion(item, undo, redo);
}
pCore->pushUndo(undo, redo, i18n("Delete bin Clips"));
}
void Bin::slotReloadClip()
{
const QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
for (const QModelIndex &ix : indexes) {
if (!ix.isValid() || ix.column() != 0) {
continue;
}
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(ix));
auto currentItem = std::static_pointer_cast(item);
if (currentItem) {
emit openClip(std::shared_ptr());
- if (currentItem->clipType() == Playlist) {
+ if (currentItem->clipType() == ClipType::Playlist) {
// Check if a clip inside playlist is missing
QString path = currentItem->url();
QFile f(path);
QDomDocument doc;
doc.setContent(&f, false);
f.close();
DocumentChecker d(QUrl::fromLocalFile(path), doc);
if (!d.hasErrorInClips() && doc.documentElement().hasAttribute(QStringLiteral("modified"))) {
QString backupFile = path + QStringLiteral(".backup");
KIO::FileCopyJob *copyjob = KIO::file_copy(QUrl::fromLocalFile(path), QUrl::fromLocalFile(backupFile));
if (copyjob->exec()) {
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
KMessageBox::sorry(this, i18n("Unable to write to file %1", path));
} else {
QTextStream out(&f);
out << doc.toString();
f.close();
KMessageBox::information(
this,
i18n("Your project file was modified by Kdenlive.\nTo make sure you don't lose data, a backup copy called %1 was created.",
backupFile));
}
}
}
}
QDomDocument doc;
QDomElement xml = currentItem->toXml(doc);
qCDebug(KDENLIVE_LOG) << "*****************\n" << doc.toString() << "\n******************";
if (!xml.isNull()) {
currentItem->setClipStatus(AbstractProjectItem::StatusWaiting);
// We need to set a temporary id before all outdated producers are replaced;
- m_doc->getFileProperties(xml, currentItem->AbstractProjectItem::clipId(), 150, true);
+ // TODO refac
+ // m_doc->getFileProperties(xml, currentItem->AbstractProjectItem::clipId(), 150, true);
+ pCore->jobManager()->startJob({currentItem->AbstractProjectItem::clipId()}, {}, QString(), xml);
}
}
}
}
void Bin::slotLocateClip()
{
const QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
for (const QModelIndex &ix : indexes) {
if (!ix.isValid() || ix.column() != 0) {
continue;
}
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(ix));
auto currentItem = std::static_pointer_cast(item);
if (currentItem) {
QUrl url = QUrl::fromLocalFile(currentItem->url()).adjusted(QUrl::RemoveFilename);
bool exists = QFile(url.toLocalFile()).exists();
if (currentItem->hasUrl() && exists) {
QDesktopServices::openUrl(url);
qCDebug(KDENLIVE_LOG) << " / / " + url.toString();
} else {
if (!exists) {
emitMessage(i18n("Couldn't locate ") + QString(" (" + url.toString() + QLatin1Char(')')), 100, ErrorMessage);
}
return;
}
}
}
}
void Bin::slotDuplicateClip()
{
const QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
for (const QModelIndex &ix : indexes) {
if (!ix.isValid() || ix.column() != 0) {
continue;
}
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(ix));
auto currentItem = std::static_pointer_cast(item);
if (currentItem) {
- QStringList folderInfo = getFolderInfo(ix);
QDomDocument doc;
QDomElement xml = currentItem->toXml(doc);
if (!xml.isNull()) {
QString currentName = EffectsList::property(xml, QStringLiteral("kdenlive:clipname"));
if (currentName.isEmpty()) {
QUrl url = QUrl::fromLocalFile(EffectsList::property(xml, QStringLiteral("resource")));
if (url.isValid()) {
currentName = url.fileName();
}
}
if (!currentName.isEmpty()) {
currentName.append(i18nc("append to clip name to indicate a copied idem", " (copy)"));
EffectsList::setProperty(xml, QStringLiteral("kdenlive:clipname"), currentName);
}
- ClipCreationDialog::createClipFromXml(m_doc, xml, folderInfo, this);
+ QString id;
+ m_itemModel->requestAddBinClip(id, xml, item->parent()->clipId(), i18n("Duplicate clip"));
}
}
}
}
void Bin::setMonitor(Monitor *monitor)
{
m_monitor = monitor;
connect(m_monitor, SIGNAL(addClipToProject(QUrl)), this, SLOT(slotAddClipToProject(QUrl)));
connect(m_monitor, SIGNAL(requestAudioThumb(QString)), this, SLOT(slotSendAudioThumb(QString)));
connect(m_monitor, &Monitor::refreshCurrentClip, this, &Bin::slotOpenCurrent);
connect(this, &Bin::openClip, [&](std::shared_ptr clip) { m_monitor->slotOpenClip(clip); });
}
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
if (m_doc) {
emit openClip(std::shared_ptr());
}
m_infoMessage->hide();
blockSignals(true);
m_proxyModel->selectionModel()->blockSignals(true);
setEnabled(false);
// Cleanup previous project
m_itemModel->clean();
m_fileWatcher.clear();
delete m_itemView;
m_itemView = nullptr;
- delete m_jobManager;
m_clipCounter = 1;
m_folderCounter = 1;
m_doc = project;
int iconHeight = QFontInfo(font()).pixelSize() * 3.5;
m_iconSize = QSize(iconHeight * pCore->getCurrentDar(), iconHeight);
- m_jobManager = new JobManager(this);
setEnabled(true);
blockSignals(false);
m_proxyModel->selectionModel()->blockSignals(false);
- connect(m_jobManager, SIGNAL(addClip(QString, int)), this, SLOT(slotAddUrl(QString, int)));
connect(m_proxyAction, SIGNAL(toggled(bool)), m_doc, SLOT(slotProxyCurrentItem(bool)));
- connect(m_jobManager, &JobManager::jobCount, m_infoLabel, &SmallJobLabel::slotSetJobCount);
- connect(m_discardCurrentClipJobs, &QAction::triggered, m_jobManager, &JobManager::slotDiscardClipJobs);
- connect(m_cancelJobs, &QAction::triggered, m_jobManager, &JobManager::slotCancelJobs);
- connect(m_discardPendingJobs, &QAction::triggered, m_jobManager, &JobManager::slotCancelPendingJobs);
- connect(m_jobManager, &JobManager::updateJobStatus, this, &Bin::slotUpdateJobStatus);
-
- 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(nullptr);
bool binEffectsDisabled = getDocumentProperty(QStringLiteral("disablebineffects")).toInt() == 1;
setBinEffectsEnabled(!binEffectsDisabled);
}
-void Bin::slotAddUrl(const QString &url, int folderId, const QMap &dataMap)
-{
- const QList urls = QList() << QUrl::fromLocalFile(url);
- QStringList folderInfo;
- if (folderId >= 0) {
- QModelIndex ix = getIndexForId(QString::number(folderId), true);
- if (ix.isValid()) {
- folderInfo = getFolderInfo(m_proxyModel->mapFromSource(ix));
- } else {
- folderInfo = getFolderInfo();
- }
- }
- ClipCreationDialog::createClipsCommand(m_doc, urls, folderInfo, this, dataMap);
-}
-
-void Bin::slotAddUrl(const QString &url, const QMap &dataMap)
-{
- const QList urls = QList() << QUrl::fromLocalFile(url);
- const QStringList folderInfo = getFolderInfo();
- ClipCreationDialog::createClipsCommand(m_doc, urls, folderInfo, this, dataMap);
-}
-
void Bin::createClip(const QDomElement &xml)
{
// Check if clip should be in a folder
QString groupId = ProjectClip::getXmlProperty(xml, QStringLiteral("kdenlive:folderid"));
std::shared_ptr parentFolder = m_itemModel->getFolderByBinId(groupId);
if (!parentFolder) {
parentFolder = m_itemModel->getRootFolder();
}
QString path = EffectsList::property(xml, QStringLiteral("resource"));
if (path.endsWith(QStringLiteral(".mlt")) || path.endsWith(QStringLiteral(".kdenlive"))) {
QFile f(path);
QDomDocument doc;
doc.setContent(&f, false);
f.close();
DocumentChecker d(QUrl::fromLocalFile(path), doc);
if (!d.hasErrorInClips() && doc.documentElement().hasAttribute(QStringLiteral("modified"))) {
QString backupFile = path + QStringLiteral(".backup");
KIO::FileCopyJob *copyjob = KIO::file_copy(QUrl::fromLocalFile(path), QUrl::fromLocalFile(backupFile));
if (copyjob->exec()) {
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
KMessageBox::sorry(this, i18n("Unable to write to file %1", path));
} else {
QTextStream out(&f);
out << doc.toString();
f.close();
KMessageBox::information(
this, i18n("Your project file was modified by Kdenlive.\nTo make sure you don't lose data, a backup copy called %1 was created.",
backupFile));
}
}
}
}
QString id = Xml::getTagContentByAttribute(xml, QStringLiteral("property"), QStringLiteral("name"), QStringLiteral("kdenlive:id"));
if (id.isEmpty()) {
id = QString::number(m_itemModel->getFreeClipId());
}
auto newClip = ProjectClip::construct(id, xml, m_blankThumb, m_itemModel);
parentFolder->appendChild(newClip);
}
QString Bin::slotAddFolder(const QString &folderName)
{
auto parentFolder = m_itemModel->getFolderByBinId(getCurrentFolder());
qDebug() << "pranteforder id" << parentFolder->clipId();
QString newId;
Fun undo = []() { return true; };
Fun redo = []() { return true; };
m_itemModel->requestAddFolder(newId, folderName.isEmpty() ? i18n("Folder") : folderName, parentFolder->clipId(), undo, redo);
pCore->pushUndo(undo, redo, i18n("Create bin folder"));
// Edit folder name
if (!folderName.isEmpty()) {
// We already have a name, no need to edit
return newId;
}
auto folder = m_itemModel->getFolderByBinId(newId);
auto ix = m_itemModel->getIndexFromItem(folder);
qDebug() << "selecting" << ix;
if (ix.isValid()) {
qDebug() << "ix valid";
m_proxyModel->selectionModel()->clearSelection();
int row = ix.row();
const QModelIndex id = m_itemModel->index(row, 0, ix.parent());
const QModelIndex id2 = m_itemModel->index(row, m_itemModel->columnCount() - 1, ix.parent());
if (id.isValid() && id2.isValid()) {
m_proxyModel->selectionModel()->select(QItemSelection(m_proxyModel->mapFromSource(id), m_proxyModel->mapFromSource(id2)),
QItemSelectionModel::Select);
}
m_itemView->edit(m_proxyModel->mapFromSource(ix));
}
return newId;
}
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(items.at(i).internalPointer());
AbstractProjectItem::PROJECTITEMTYPE type = currentItem->itemType();
if (folderWanted && type == AbstractProjectItem::FolderItem) {
// We found our folder
return items.at(i);
}
if (!folderWanted && type == AbstractProjectItem::ClipItem) {
// We found our clip
return items.at(i);
}
}
return QModelIndex();
}
void Bin::selectClipById(const QString &clipId, int frame, const QPoint &zone)
{
if (m_monitor->activeClipId() == clipId) {
if (frame > -1) {
m_monitor->slotSeek(frame);
}
if (!zone.isNull()) {
m_monitor->slotLoadClipZone(zone);
}
return;
}
m_proxyModel->selectionModel()->clearSelection();
std::shared_ptr clip = getBinClip(clipId);
if (clip) {
selectClip(clip);
if (frame > -1) {
m_monitor->slotSeek(frame);
}
if (!zone.isNull()) {
m_monitor->slotLoadClipZone(zone);
}
}
}
-void Bin::removeSubClip(const QString &id, QUndoCommand *deleteCommand)
-{
- // Check parent item
- QString clipId = id;
- int in = clipId.section(QLatin1Char(':'), 1, 1).toInt();
- int out = clipId.section(QLatin1Char(':'), 2, 2).toInt();
- clipId = clipId.section(QLatin1Char(':'), 0, 0);
- new AddBinClipCutCommand(this, clipId, in, out, false, deleteCommand);
-}
-
-
void Bin::selectProxyModel(const QModelIndex &id)
{
if (isLoading) {
// return;
}
if (id.isValid()) {
if (id.column() != 0) {
return;
}
std::shared_ptr currentItem = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(id));
if (currentItem) {
// Set item as current so that it displays its content in clip monitor
setCurrent(currentItem);
if (currentItem->itemType() == AbstractProjectItem::ClipItem) {
m_reloadAction->setEnabled(true);
m_locateAction->setEnabled(true);
m_duplicateAction->setEnabled(true);
std::shared_ptr clip = std::static_pointer_cast(currentItem);
ClipType type = clip->clipType();
- m_openAction->setEnabled(type == Image || type == Audio || type == Text || type == TextTemplate);
+ m_openAction->setEnabled(type == ClipType::Image || type == ClipType::Audio || type == ClipType::Text || type == ClipType::TextTemplate);
showClipProperties(clip, false);
m_deleteAction->setText(i18n("Delete Clip"));
m_proxyAction->setText(i18n("Proxy Clip"));
emit findInTimeline(clip->clipId(), clip->timelineInstances());
} else if (currentItem->itemType() == AbstractProjectItem::FolderItem) {
// A folder was selected, disable editing clip
m_openAction->setEnabled(false);
m_reloadAction->setEnabled(false);
m_locateAction->setEnabled(false);
m_duplicateAction->setEnabled(false);
m_deleteAction->setText(i18n("Delete Folder"));
m_proxyAction->setText(i18n("Proxy Folder"));
} else if (currentItem->itemType() == AbstractProjectItem::SubClipItem) {
showClipProperties(std::static_pointer_cast(currentItem->parent()), false);
m_openAction->setEnabled(false);
m_reloadAction->setEnabled(false);
m_locateAction->setEnabled(false);
m_duplicateAction->setEnabled(false);
m_deleteAction->setText(i18n("Delete Clip"));
m_proxyAction->setText(i18n("Proxy Clip"));
}
m_deleteAction->setEnabled(true);
} else {
emit findInTimeline(QString());
m_reloadAction->setEnabled(false);
m_locateAction->setEnabled(false);
m_duplicateAction->setEnabled(false);
m_openAction->setEnabled(false);
m_deleteAction->setEnabled(false);
}
} else {
// No item selected in bin
m_openAction->setEnabled(false);
m_deleteAction->setEnabled(false);
showClipProperties(nullptr);
emit findInTimeline(QString());
emit requestClipShow(nullptr);
// clear effect stack
emit requestShowEffectStack(QString(), nullptr, QPair(), QSize(), false);
// Display black bg in clip monitor
emit openClip(std::shared_ptr());
}
}
-QList> Bin::selectedClips()
+std::vector Bin::selectedClipsIds(bool excludeFolders)
{
- // TODO: handle clips inside folders
const QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
- QList> list;
+ std::vector ids;
+ // We define the lambda that will be executed on each item of the subset of nodes of the tree that are selected
+ auto itemAdder = [excludeFolders, &ids](std::vector &ids_vec, std::shared_ptr item) {
+ auto binItem = std::static_pointer_cast(item);
+ if (!excludeFolders || (binItem->itemType() != AbstractProjectItem::FolderItem && binItem->itemType() != AbstractProjectItem::FolderUpItem)) {
+ ids.push_back(binItem->clipId());
+ }
+ return ids_vec;
+ };
for (const QModelIndex &ix : indexes) {
if (!ix.isValid() || ix.column() != 0) {
continue;
}
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(ix));
- auto currentItem = std::static_pointer_cast(item);
- if (currentItem) {
- list << currentItem;
- }
+ item->accumulate(ids, itemAdder);
}
- return list;
+ return ids;
+}
+
+QList> Bin::selectedClips()
+{
+ auto ids = selectedClipsIds(true);
+ QList> ret;
+ for (const auto &id : ids) {
+ ret.push_back(m_itemModel->getClipByBinID(id));
+ }
+ return ret;
}
void Bin::slotInitView(QAction *action)
{
if (action) {
m_proxyModel->selectionModel()->clearSelection();
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(m_itemView);
m_headerInfo = view->header()->saveState();
m_showDate->setEnabled(true);
m_showDesc->setEnabled(true);
} else {
// remove the current folderUp item if any
if (m_folderUp) {
if (m_folderUp->parent()) {
m_folderUp->parent()->removeChild(m_folderUp);
}
m_folderUp.reset();
}
}
m_listType = static_cast(viewType);
}
if (m_itemView) {
delete m_itemView;
}
switch (m_listType) {
case BinIconView:
m_itemView = new MyListView(this);
m_folderUp = ProjectFolderUp::construct(m_itemModel);
m_showDate->setEnabled(false);
m_showDesc->setEnabled(false);
break;
default:
m_itemView = new MyTreeView(this);
m_showDate->setEnabled(true);
m_showDesc->setEnabled(true);
break;
}
m_itemView->setMouseTracking(true);
m_itemView->viewport()->installEventFilter(this);
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_layout->insertWidget(1, m_itemView);
// setup some default view specific parameters
if (m_listType == BinTreeView) {
m_itemView->setItemDelegate(m_binTreeViewDelegate);
MyTreeView *view = static_cast(m_itemView);
view->setSortingEnabled(true);
view->setWordWrap(true);
connect(m_proxyModel, &QAbstractItemModel::layoutAboutToBeChanged, this, &Bin::slotSetSorting);
m_proxyModel->setDynamicSortFilter(true);
if (!m_headerInfo.isEmpty()) {
view->header()->restoreState(m_headerInfo);
} else {
view->header()->resizeSections(QHeaderView::ResizeToContents);
view->resizeColumnToContents(0);
view->setColumnHidden(1, true);
view->setColumnHidden(2, true);
}
m_showDate->setChecked(!view->isColumnHidden(1));
m_showDesc->setChecked(!view->isColumnHidden(2));
connect(view->header(), &QHeaderView::sectionResized, this, &Bin::slotSaveHeaders);
connect(view->header(), &QHeaderView::sectionClicked, this, &Bin::slotSaveHeaders);
connect(view, &MyTreeView::focusView, this, &Bin::slotGotFocus);
} else if (m_listType == BinIconView) {
MyListView *view = static_cast(m_itemView);
connect(view, &MyListView::focusView, this, &Bin::slotGotFocus);
}
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::rebuildMenu()
{
m_transcodeAction = static_cast(pCore->window()->factory()->container(QStringLiteral("transcoders"), pCore->window()));
m_extractAudioAction = static_cast(pCore->window()->factory()->container(QStringLiteral("extract_audio"), pCore->window()));
m_clipsActionsMenu = static_cast(pCore->window()->factory()->container(QStringLiteral("clip_actions"), pCore->window()));
m_menu->insertMenu(m_reloadAction, m_extractAudioAction);
m_menu->insertMenu(m_reloadAction, m_transcodeAction);
m_menu->insertMenu(m_reloadAction, m_clipsActionsMenu);
m_inTimelineAction =
m_menu->insertMenu(m_reloadAction, static_cast(pCore->window()->factory()->container(QStringLiteral("clip_in_timeline"), pCore->window())));
}
void Bin::contextMenuEvent(QContextMenuEvent *event)
{
bool enableClipActions = false;
- ClipType type = Unknown;
+ ClipType type = ClipType::Unknown;
bool isFolder = false;
bool isImported = false;
QString clipService;
QString audioCodec;
if (m_itemView) {
QModelIndex idx = m_itemView->indexAt(m_itemView->viewport()->mapFromGlobal(event->globalPos()));
if (idx.isValid()) {
// User right clicked on a clip
std::shared_ptr currentItem = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(idx));
if (currentItem) {
enableClipActions = true;
if (currentItem->itemType() == AbstractProjectItem::FolderItem) {
isFolder = true;
} else {
auto clip = std::static_pointer_cast(currentItem);
if (clip) {
m_proxyAction->blockSignals(true);
emit findInTimeline(clip->clipId(), clip->timelineInstances());
clipService = clip->getProducerProperty(QStringLiteral("mlt_service"));
m_proxyAction->setChecked(clip->hasProxy());
QList transcodeActions;
if (m_transcodeAction) {
transcodeActions = m_transcodeAction->actions();
}
QStringList dataList;
QString condition;
audioCodec = clip->codec(true);
QString videoCodec = clip->codec(false);
type = clip->clipType();
if (clip->hasUrl()) {
isImported = true;
}
bool noCodecInfo = false;
if (audioCodec.isEmpty() && videoCodec.isEmpty()) {
noCodecInfo = true;
}
for (int i = 0; i < transcodeActions.count(); ++i) {
dataList = transcodeActions.at(i)->data().toStringList();
if (dataList.count() > 4) {
condition = dataList.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(QLatin1String("vcodec"))) {
transcodeActions.at(i)->setEnabled(condition.section(QLatin1Char('='), 1, 1) == videoCodec);
} else if (condition.startsWith(QLatin1String("acodec"))) {
transcodeActions.at(i)->setEnabled(condition.section(QLatin1Char('='), 1, 1) == audioCodec);
}
}
}
}
m_proxyAction->blockSignals(false);
}
}
}
}
// Enable / disable clip actions
m_proxyAction->setEnabled((m_doc->getDocumentProperty(QStringLiteral("enableproxy")).toInt() != 0) && enableClipActions);
- m_openAction->setEnabled(type == Image || type == Audio || type == TextTemplate || type == Text);
+ m_openAction->setEnabled(type == ClipType::Image || type == ClipType::Audio || type == ClipType::TextTemplate || type == ClipType::Text);
m_reloadAction->setEnabled(enableClipActions);
m_locateAction->setEnabled(enableClipActions);
m_duplicateAction->setEnabled(enableClipActions);
m_editAction->setVisible(!isFolder);
m_clipsActionsMenu->setEnabled(enableClipActions);
m_extractAudioAction->setEnabled(enableClipActions);
m_openAction->setVisible(!isFolder);
m_reloadAction->setVisible(!isFolder);
m_duplicateAction->setVisible(!isFolder);
m_inTimelineAction->setVisible(!isFolder);
if (m_transcodeAction) {
m_transcodeAction->setEnabled(enableClipActions);
m_transcodeAction->menuAction()->setVisible(!isFolder && clipService.contains(QStringLiteral("avformat")));
}
m_clipsActionsMenu->menuAction()->setVisible(
!isFolder &&
(clipService.contains(QStringLiteral("avformat")) || clipService.contains(QStringLiteral("xml")) || clipService.contains(QStringLiteral("consumer"))));
m_extractAudioAction->menuAction()->setVisible(!isFolder && !audioCodec.isEmpty());
m_locateAction->setVisible(!isFolder && (isImported));
// Show menu
event->setAccepted(true);
if (enableClipActions) {
m_menu->exec(event->globalPos());
} else {
// Clicked in empty area
m_addButton->menu()->exec(event->globalPos());
}
}
void Bin::slotItemDoubleClicked(const QModelIndex &ix, const QPoint pos)
{
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(ix));
if (m_listType == BinIconView) {
if (item->childCount() > 0 || item->itemType() == AbstractProjectItem::FolderItem) {
m_folderUp->changeParent(std::static_pointer_cast(item));
m_itemView->setRootIndex(ix);
return;
}
if (item == m_folderUp) {
std::shared_ptr parentItem = item->parent();
QModelIndex parent = getIndexForId(parentItem->parent()->clipId(), parentItem->parent()->itemType() == AbstractProjectItem::FolderItem);
if (parentItem->parent() != m_itemModel->getRootFolder()) {
// We are entering a parent folder
m_folderUp->changeParent(std::static_pointer_cast(parentItem->parent()));
} else {
m_folderUp->changeParent(std::shared_ptr());
}
m_itemView->setRootIndex(m_proxyModel->mapFromSource(parent));
return;
}
} else {
if (item->childCount() > 0) {
QTreeView *view = static_cast(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() && ((ix.column() == 2 && item->itemType() == AbstractProjectItem::ClipItem) || !IconRect.contains(pos))) {
// User clicked outside icon, trigger rename
m_itemView->edit(ix);
return;
}
if (item->itemType() == AbstractProjectItem::ClipItem) {
std::shared_ptr clip = std::static_pointer_cast(item);
if (clip) {
- if (clip->clipType() == Text || clip->clipType() == TextTemplate) {
+ if (clip->clipType() == ClipType::Text || clip->clipType() == ClipType::TextTemplate) {
// m_propertiesPanel->setEnabled(false);
showTitleWidget(clip);
} else {
slotSwitchClipProperties(clip);
}
}
}
}
}
void Bin::slotEditClip()
{
QString panelId = m_propertiesPanel->property("clipId").toString();
QModelIndex current = m_proxyModel->selectionModel()->currentIndex();
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(current));
if (item->clipId() != panelId) {
// wrong clip
return;
}
auto clip = std::static_pointer_cast(item);
+ QString parentFolder = getCurrentFolder();
switch (clip->clipType()) {
- case Text:
- case TextTemplate:
+ case ClipType::Text:
+ case ClipType::TextTemplate:
showTitleWidget(clip);
break;
- case SlideShow:
+ case ClipType::SlideShow:
showSlideshowWidget(clip);
break;
- case QText:
- ClipCreationDialog::createQTextClip(m_doc, getFolderInfo(), this, clip.get());
+ case ClipType::QText:
+ ClipCreationDialog::createQTextClip(m_doc, parentFolder, this, clip.get());
break;
default:
break;
}
}
void Bin::slotSwitchClipProperties()
{
QModelIndex current = m_proxyModel->selectionModel()->currentIndex();
if (current.isValid()) {
// User clicked in the icon, open clip properties
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(current));
auto clip = std::static_pointer_cast(item);
if (clip) {
slotSwitchClipProperties(clip);
return;
}
}
slotSwitchClipProperties(nullptr);
}
void Bin::slotSwitchClipProperties(std::shared_ptr clip)
{
if (clip == nullptr) {
m_propertiesPanel->setEnabled(false);
return;
}
- if (clip->clipType() == SlideShow) {
+ if (clip->clipType() == ClipType::SlideShow) {
m_propertiesPanel->setEnabled(false);
showSlideshowWidget(clip);
- } else if (clip->clipType() == QText) {
+ } else if (clip->clipType() == ClipType::QText) {
m_propertiesPanel->setEnabled(false);
- ClipCreationDialog::createQTextClip(m_doc, getFolderInfo(), this, clip.get());
+ QString parentFolder = getCurrentFolder();
+ ClipCreationDialog::createQTextClip(m_doc, parentFolder, this, clip.get());
} else {
m_propertiesPanel->setEnabled(true);
showClipProperties(clip);
m_propertiesDock->show();
m_propertiesDock->raise();
}
// Check if properties panel is not tabbed under Bin
// if (!pCore->window()->isTabbedWith(m_propertiesDock, QStringLiteral("project_bin"))) {
}
void Bin::doRefreshPanel(const QString &id)
{
std::shared_ptr currentItem = getFirstSelectedClip();
if ((currentItem != nullptr) && currentItem->AbstractProjectItem::clipId() == id) {
showClipProperties(currentItem, true);
}
}
void Bin::showClipProperties(std::shared_ptr clip, bool forceRefresh)
{
if ((clip == nullptr) || !clip->isReady()) {
m_propertiesPanel->setEnabled(false);
return;
}
m_propertiesPanel->setEnabled(true);
QString panelId = m_propertiesPanel->property("clipId").toString();
if (!forceRefresh && panelId == clip->AbstractProjectItem::clipId()) {
// the properties panel is already displaying current clip, do nothing
return;
}
// Cleanup widget for new content
for (QWidget *w : m_propertiesPanel->findChildren()) {
delete w;
}
m_propertiesPanel->setProperty("clipId", clip->AbstractProjectItem::clipId());
QVBoxLayout *lay = static_cast(m_propertiesPanel->layout());
if (lay == nullptr) {
lay = new QVBoxLayout(m_propertiesPanel);
m_propertiesPanel->setLayout(lay);
}
ClipPropertiesController *panel = clip->buildProperties(m_propertiesPanel);
connect(this, &Bin::refreshTimeCode, panel, &ClipPropertiesController::slotRefreshTimeCode);
connect(panel, SIGNAL(updateClipProperties(QString, QMap, QMap)), this,
SLOT(slotEditClipCommand(QString, QMap, QMap)));
connect(panel, SIGNAL(seekToFrame(int)), m_monitor, SLOT(slotSeek(int)));
connect(panel, &ClipPropertiesController::editClip, this, &Bin::slotEditClip);
connect(panel, SIGNAL(editAnalysis(QString, QString, QString)), this, SLOT(slotAddClipExtraData(QString, QString, QString)));
lay->addWidget(panel);
}
void Bin::slotEditClipCommand(const QString &id, const QMap &oldProps, const QMap &newProps)
{
auto *command = new EditClipCommand(this, id, oldProps, newProps, true);
m_doc->commandStack()->push(command);
}
void Bin::reloadClip(const QString &id)
{
std::shared_ptr clip = m_itemModel->getClipByBinID(id);
if (!clip) {
return;
}
QDomDocument doc;
QDomElement xml = clip->toXml(doc);
if (!xml.isNull()) {
- m_doc->getFileProperties(xml, id, 150, true);
+ pCore->jobManager()->startJob({id}, {}, QString(), xml);
}
}
void Bin::slotThumbnailReady(const QString &id, const QImage &img, bool fromFile)
{
std::shared_ptr clip = m_itemModel->getClipByBinID(id);
if (clip) {
clip->setThumbnail(img);
// Save thumbnail for later reuse
bool ok = false;
if (!fromFile && clip->clipStatus() == ProjectClip::StatusReady) {
img.save(m_doc->getCacheDir(CacheThumbs, &ok).absoluteFilePath(clip->hash() + QStringLiteral(".png")));
}
}
}
QStringList Bin::getBinFolderClipIds(const QString &id) const
{
QStringList ids;
std::shared_ptr folder = m_itemModel->getFolderByBinId(id);
if (folder) {
for (int i = 0; i < folder->childCount(); i++) {
std::shared_ptr child = std::static_pointer_cast(folder->child(i));
if (child->itemType() == AbstractProjectItem::ClipItem) {
ids << child->clipId();
}
}
}
return ids;
}
std::shared_ptr Bin::getBinClip(const QString &id)
{
std::shared_ptr clip = nullptr;
if (id.contains(QLatin1Char('_'))) {
clip = m_itemModel->getClipByBinID(id.section(QLatin1Char('_'), 0, 0));
} else if (!id.isEmpty()) {
clip = m_itemModel->getClipByBinID(id);
}
return clip;
}
void Bin::setWaitingStatus(const QString &id)
{
std::shared_ptr clip = m_itemModel->getClipByBinID(id);
if (clip) {
clip->setClipStatus(AbstractProjectItem::StatusWaiting);
}
}
void Bin::slotRemoveInvalidClip(const QString &id, bool replace, const QString &errorMessage)
{
Q_UNUSED(replace);
std::shared_ptr clip = m_itemModel->getClipByBinID(id);
if (!clip) {
return;
}
emit requesteInvalidRemoval(id, clip->url(), errorMessage);
}
+// TODO refac cleanup
+/*
void Bin::slotProducerReady(const requestClipInfo &info, std::shared_ptr producer)
{
std::shared_ptr clip = m_itemModel->getClipByBinID(info.clipId);
if (clip) {
if ((producer == nullptr || clip->setProducer(producer, info.replaceProducer)) && !clip->hasProxy()) {
if (producer) {
pCore->binController()->replaceBinPlaylistClip(info.clipId, producer);
}
emit producerReady(info.clipId);
// Check for file modifications
ClipType t = clip->clipType();
if (t == AV || t == Audio || t == Image || t == Video || t == Playlist || t == TextTemplate) {
m_fileWatcher.addFile(info.clipId, clip->url());
}
if (m_doc->useProxy()) {
if (t == AV || t == Video) {
int width = clip->getProducerIntProperty(QStringLiteral("meta.media.width"));
if (m_doc->autoGenerateProxy(width)) {
// Start proxy
m_doc->slotProxyCurrentItem(true, {clip});
}
} else if (t == Playlist) {
// always proxy playlists
m_doc->slotProxyCurrentItem(true, {clip});
} else if (t == Image && m_doc->autoGenerateImageProxy(clip->getProducerIntProperty(QStringLiteral("meta.media.width")))) {
// Start proxy
m_doc->slotProxyCurrentItem(true, {clip});
}
} else {
emit producerReady(info.clipId);
}
QString currentClip = m_monitor->activeClipId();
if (currentClip.isEmpty()) {
// No clip displayed in monitor, check if item is selected
QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
if (indexes.isEmpty()) {
// No clip selected, focus this new one
selectClip(clip);
- } else {
+ } else {
for (const QModelIndex &ix : indexes) {
if (!ix.isValid() || ix.column() != 0) {
continue;
}
std::shared_ptr item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(ix));
if ((item != nullptr) && item->clipId() == info.clipId) {
// Item was selected, show it in monitor
setCurrent(item);
break;
}
}
}
} else if (currentClip == info.clipId) {
setCurrent(clip);
}
}
} else {
// Clip not found, create it
QString groupId = producer->get("kdenlive:folderid");
std::shared_ptr parentFolder;
if (!groupId.isEmpty() && groupId != QLatin1String("-1")) {
parentFolder = m_itemModel->getFolderByBinId(groupId);
if (!parentFolder) {
// parent folder does not exist, put in root folder
parentFolder = m_itemModel->getRootFolder();
}
if (groupId.toInt() >= m_folderCounter) {
m_folderCounter = groupId.toInt() + 1;
}
} else {
parentFolder = m_itemModel->getRootFolder();
}
std::shared_ptr newClip = ProjectClip::construct(info.clipId, m_blankThumb, m_itemModel, producer);
parentFolder->appendChild(newClip);
emit producerReady(info.clipId);
newClip->createAudioThumbs();
ClipType t = newClip->clipType();
if (t == AV || t == Audio || t == Image || t == Video || t == Playlist || t == TextTemplate) {
m_fileWatcher.addFile(info.clipId, newClip->url());
}
if (info.clipId.toInt() >= m_clipCounter) {
m_clipCounter = info.clipId.toInt() + 1;
}
}
}
+*/
void Bin::selectClip(const std::shared_ptr &clip)
{
QModelIndex ix = m_itemModel->getIndexFromItem(clip);
int row = ix.row();
const QModelIndex id = m_itemModel->index(row, 0, ix.parent());
const QModelIndex id2 = m_itemModel->index(row, m_itemModel->columnCount() - 1, ix.parent());
if (id.isValid() && id2.isValid()) {
m_proxyModel->selectionModel()->select(QItemSelection(m_proxyModel->mapFromSource(id), m_proxyModel->mapFromSource(id2)), QItemSelectionModel::Select);
}
selectProxyModel(m_proxyModel->mapFromSource(ix));
m_itemView->scrollTo(m_proxyModel->mapFromSource(ix));
emit openClip(clip);
}
void Bin::slotOpenCurrent()
{
std::shared_ptr currentItem = getFirstSelectedClip();
if (currentItem) {
emit openClip(currentItem);
}
}
void Bin::openProducer(std::shared_ptr controller)
{
emit openClip(controller);
}
void Bin::openProducer(std::shared_ptr controller, int in, int out)
{
emit openClip(controller, in, out);
}
void Bin::emitItemUpdated(std::shared_ptr item)
{
emit itemUpdated(item);
}
void Bin::emitRefreshPanel(const QString &id)
{
emit refreshPanel(id);
}
void Bin::setupGeneratorMenu()
{
if (!m_menu) {
qCDebug(KDENLIVE_LOG) << "Warning, menu was not created, something is wrong";
return;
}
QMenu *addMenu = qobject_cast(pCore->window()->factory()->container(QStringLiteral("generators"), pCore->window()));
if (addMenu) {
QMenu *menu = m_addButton->menu();
menu->addMenu(addMenu);
addMenu->setEnabled(!addMenu->isEmpty());
m_addButton->setMenu(menu);
}
addMenu = qobject_cast(pCore->window()->factory()->container(QStringLiteral("extract_audio"), pCore->window()));
if (addMenu) {
m_menu->addMenu(addMenu);
addMenu->setEnabled(!addMenu->isEmpty());
m_extractAudioAction = addMenu;
}
addMenu = qobject_cast(pCore->window()->factory()->container(QStringLiteral("transcoders"), pCore->window()));
if (addMenu) {
m_menu->addMenu(addMenu);
addMenu->setEnabled(!addMenu->isEmpty());
m_transcodeAction = addMenu;
}
addMenu = qobject_cast(pCore->window()->factory()->container(QStringLiteral("clip_actions"), pCore->window()));
if (addMenu) {
m_menu->addMenu(addMenu);
addMenu->setEnabled(!addMenu->isEmpty());
m_clipsActionsMenu = addMenu;
}
addMenu = qobject_cast(pCore->window()->factory()->container(QStringLiteral("clip_in_timeline"), pCore->window()));
if (addMenu) {
m_inTimelineAction = m_menu->addMenu(addMenu);
m_inTimelineAction->setEnabled(!addMenu->isEmpty());
}
if (m_locateAction) {
m_menu->addAction(m_locateAction);
}
if (m_reloadAction) {
m_menu->addAction(m_reloadAction);
}
if (m_duplicateAction) {
m_menu->addAction(m_duplicateAction);
}
if (m_proxyAction) {
m_menu->addAction(m_proxyAction);
}
addMenu = qobject_cast(pCore->window()->factory()->container(QStringLiteral("clip_timeline"), pCore->window()));
if (addMenu) {
m_menu->addMenu(addMenu);
addMenu->setEnabled(false);
}
m_menu->addAction(m_editAction);
m_menu->addAction(m_openAction);
m_menu->addAction(m_renameAction);
m_menu->addAction(m_deleteAction);
m_menu->insertSeparator(m_deleteAction);
}
void Bin::setupMenu(QMenu *addMenu, QAction *defaultAction, const QHash &actions)
{
// Setup actions
QAction *first = m_toolbar->actions().at(0);
m_deleteAction = actions.value(QStringLiteral("delete"));
m_toolbar->insertAction(first, m_deleteAction);
QAction *folder = actions.value(QStringLiteral("folder"));
m_toolbar->insertAction(m_deleteAction, folder);
m_editAction = actions.value(QStringLiteral("properties"));
m_openAction = actions.value(QStringLiteral("open"));
m_reloadAction = actions.value(QStringLiteral("reload"));
m_duplicateAction = actions.value(QStringLiteral("duplicate"));
m_locateAction = actions.value(QStringLiteral("locate"));
m_proxyAction = actions.value(QStringLiteral("proxy"));
auto *m = new QMenu(this);
m->addActions(addMenu->actions());
m_addButton = new QToolButton(this);
m_addButton->setMenu(m);
m_addButton->setDefaultAction(defaultAction);
m_addButton->setPopupMode(QToolButton::MenuButtonPopup);
m_toolbar->insertWidget(folder, m_addButton);
m_menu = new QMenu(this);
m_propertiesDock = pCore->window()->addDock(i18n("Clip Properties"), QStringLiteral("clip_properties"), m_propertiesPanel);
m_propertiesDock->close();
// m_menu->addActions(addMenu->actions());
}
const QString Bin::getDocumentProperty(const QString &key)
{
return m_doc->getDocumentProperty(key);
}
void Bin::slotUpdateJobStatus(const QString &id, int jobType, int status, const QString &label, const QString &actionName, const QString &details)
{
+ // TODO refac
+ /*
std::shared_ptr clip = m_itemModel->getClipByBinID(id);
if (clip) {
clip->setJobStatus((AbstractClipJob::JOBTYPE)jobType, (ClipJobStatus)status);
}
if (status == JobCrashed) {
QList actions = m_infoMessage->actions();
if (m_infoMessage->isHidden()) {
if (!details.isEmpty()) {
m_infoMessage->setText(label + QStringLiteral(" ") + i18n("Show log") + QStringLiteral(""));
} else {
m_infoMessage->setText(label);
}
m_infoMessage->setWordWrap(m_infoMessage->text().length() > 35);
m_infoMessage->setMessageType(KMessageWidget::Warning);
}
if (!actionName.isEmpty()) {
QAction *action = nullptr;
QList 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 != nullptr) && !actions.contains(action)) {
m_infoMessage->addAction(action);
}
}
if (!details.isEmpty()) {
m_errorLog.append(details);
}
m_infoMessage->setCloseButtonVisible(true);
m_infoMessage->animatedShow();
}
+ */
}
void Bin::doDisplayMessage(const QString &text, KMessageWidget::MessageType type, const QList &actions)
{
// Remove axisting actions if any
QList acts = m_infoMessage->actions();
while (!acts.isEmpty()) {
QAction *a = acts.takeFirst();
m_infoMessage->removeAction(a);
delete a;
}
m_infoMessage->setText(text);
m_infoMessage->setWordWrap(m_infoMessage->text().length() > 35);
for (QAction *action : actions) {
m_infoMessage->addAction(action);
connect(action, &QAction::triggered, this, &Bin::slotMessageActionTriggered);
}
m_infoMessage->setCloseButtonVisible(actions.isEmpty());
m_infoMessage->setMessageType(type);
if (m_infoMessage->isHidden()) {
m_infoMessage->animatedShow();
}
}
-void Bin::slotShowJobLog()
-{
- QDialog d(this);
- QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
- QWidget *mainWidget = new QWidget(this);
- auto *l = new QVBoxLayout;
- QTextEdit t(&d);
- for (int i = 0; i < m_errorLog.count(); ++i) {
- if (i > 0) {
- t.insertHtml(QStringLiteral("
"));
- }
- t.insertPlainText(m_errorLog.at(i));
- }
- t.setReadOnly(true);
- l->addWidget(&t);
- mainWidget->setLayout(l);
- auto *mainLayout = new QVBoxLayout;
- d.setLayout(mainLayout);
- mainLayout->addWidget(mainWidget);
- mainLayout->addWidget(buttonBox);
- d.connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::accept);
- d.exec();
-}
-
void Bin::gotProxy(const QString &id, const QString &path)
{
+ // TODO refac : delete this
std::shared_ptr clip = m_itemModel->getClipByBinID(id);
if (clip) {
QDomDocument doc;
clip->setProducerProperty(QStringLiteral("kdenlive:proxy"), path);
QDomElement xml = clip->toXml(doc, true);
if (!xml.isNull()) {
- m_doc->getFileProperties(xml, id, 150, true);
+ pCore->jobManager()->startJob({id}, {}, QString(), xml);
}
}
}
-void Bin::reloadProducer(const QString &id, const QDomElement &xml)
-{
- m_doc->getFileProperties(xml, id, 150, true);
-}
-
void Bin::refreshClip(const QString &id)
{
if (m_monitor->activeClipId() == id) {
m_monitor->refreshMonitorIfActive();
}
}
void Bin::doRefreshAudioThumbs(const QString &id)
{
if (m_monitor->activeClipId() == id) {
slotSendAudioThumb(id);
}
}
-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)
-{
- std::shared_ptr clip = getBinClip(id);
- if ((clip != nullptr) && !hasPendingJob(id, type)) {
- // Launch job
- const QList clips = {clip.get()};
- m_jobManager->prepareJobs(clips, pCore->getCurrentFps(), type);
- }
-}
-
-bool Bin::hasPendingJob(const QString &id, AbstractClipJob::JOBTYPE type)
-{
- return m_jobManager->hasPendingJob(id, type);
-}
-
void Bin::slotCreateProjectClip()
{
QAction *act = qobject_cast(sender());
if (act == nullptr) {
// Cannot access triggering action, something is wrong
qCDebug(KDENLIVE_LOG) << "// Error in clip creation action";
return;
}
ClipType type = (ClipType)act->data().toInt();
QStringList folderInfo = getFolderInfo();
QString parentFolder = getCurrentFolder();
switch (type) {
- case Color:
+ case ClipType::Color:
ClipCreationDialog::createColorClip(m_doc, parentFolder, m_itemModel);
break;
- case SlideShow:
- ClipCreationDialog::createSlideshowClip(m_doc, folderInfo, this);
+ case ClipType::SlideShow:
+ ClipCreationDialog::createSlideshowClip(m_doc, parentFolder, m_itemModel);
break;
- case Text:
+ case ClipType::Text:
ClipCreationDialog::createTitleClip(m_doc, folderInfo, QString(), this);
break;
- case TextTemplate:
- ClipCreationDialog::createTitleTemplateClip(m_doc, folderInfo, this);
+ case ClipType::TextTemplate:
+ ClipCreationDialog::createTitleTemplateClip(m_doc, parentFolder, m_itemModel);
break;
- case QText:
- ClipCreationDialog::createQTextClip(m_doc, folderInfo, this);
+ case ClipType::QText:
+ ClipCreationDialog::createQTextClip(m_doc, parentFolder, this);
break;
default:
break;
}
}
void Bin::slotItemDropped(const QStringList &ids, const QModelIndex &parent)
{
std::shared_ptr parentItem;
if (parent.isValid()) {
parentItem = m_itemModel->getBinItemByIndex(parent);
parentItem = parentItem->getEnclosingFolder(false);
} else {
parentItem = m_itemModel->getRootFolder();
}
auto *moveCommand = new QUndoCommand();
moveCommand->setText(i18np("Move Clip", "Move Clips", ids.count()));
QStringList folderIds;
for (const QString &id : ids) {
if (id.contains(QLatin1Char('/'))) {
// trying to move clip zone, not allowed. Ignore
continue;
}
if (id.startsWith(QLatin1Char('#'))) {
// moving a folder, keep it for later
folderIds << id;
continue;
}
std::shared_ptr currentItem = m_itemModel->getClipByBinID(id);
std::shared_ptr 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()) {
for (QString id : folderIds) {
id.remove(0, 1);
std::shared_ptr currentItem = m_itemModel->getFolderByBinId(id);
std::shared_ptr 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::slotAddEffect(QString id, const QStringList &effectData)
{
if (id.isEmpty()) {
id = m_monitor->activeClipId();
}
if (!id.isEmpty()) {
std::shared_ptr clip = m_itemModel->getClipByBinID(id);
if (clip) {
if (effectData.count() == 4) {
// Paste effect from another stack
std::shared_ptr sourceStack = pCore->getItemEffectStack(effectData.at(1).toInt(), effectData.at(2).toInt());
clip->copyEffect(sourceStack, effectData.at(3).toInt());
} else {
clip->addEffect(effectData.constFirst());
}
return;
}
}
pCore->displayMessage(i18n("Select a clip to apply an effect"), InformationMessage, 500);
}
void Bin::slotEffectDropped(const QStringList &effectData, const QModelIndex &parent)
{
if (parent.isValid()) {
std::shared_ptr parentItem = m_itemModel->getBinItemByIndex(parent);
if (parentItem->itemType() != AbstractProjectItem::ClipItem) {
// effect only supported on clip items
return;
}
m_proxyModel->selectionModel()->clearSelection();
int row = parent.row();
const QModelIndex id = m_itemModel->index(row, 0, parent.parent());
const QModelIndex id2 = m_itemModel->index(row, m_itemModel->columnCount() - 1, parent.parent());
if (id.isValid() && id2.isValid()) {
m_proxyModel->selectionModel()->select(QItemSelection(m_proxyModel->mapFromSource(id), m_proxyModel->mapFromSource(id2)),
QItemSelectionModel::Select);
}
setCurrent(parentItem);
if (effectData.count() == 4) {
// Paste effect from another stack
std::shared_ptr sourceStack = pCore->getItemEffectStack(effectData.at(1).toInt(), effectData.at(2).toInt());
std::static_pointer_cast(parentItem)->copyEffect(sourceStack, effectData.at(3).toInt());
} else {
std::static_pointer_cast(parentItem)->addEffect(effectData.constFirst());
}
}
}
void Bin::editMasterEffect(std::shared_ptr clip)
{
if (m_gainedFocus) {
// Widget just gained focus, updating stack is managed in the eventfilter event, not from item
return;
}
if (clip) {
if (clip->itemType() == AbstractProjectItem::ClipItem) {
std::shared_ptrclp = std::static_pointer_cast(clip);
emit requestShowEffectStack(clp->clipName(), clp->m_effectStack, QPair(0, clp->frameDuration()), clp->getFrameSize(), false);
return;
}
if (clip->itemType() == AbstractProjectItem::SubClipItem) {
if (auto ptr = clip->parentItem().lock()) {
std::shared_ptrclp = std::static_pointer_cast(ptr);
emit requestShowEffectStack(clp->clipName(), clp->m_effectStack, QPair(0, clp->frameDuration()), clp->getFrameSize(), false);
}
return;
}
}
emit requestShowEffectStack(QString(), nullptr, QPair(), QSize(), false);
}
void Bin::slotGotFocus()
{
m_gainedFocus = true;
}
void Bin::doMoveClip(const QString &id, const QString &newParentId)
{
std::shared_ptr currentItem = m_itemModel->getClipByBinID(id);
if (!currentItem) {
return;
}
std::shared_ptr currentParent = currentItem->parent();
std::shared_ptr newParent = m_itemModel->getFolderByBinId(newParentId);
currentParent->removeChild(currentItem);
currentItem->changeParent(newParent);
currentItem->updateParentInfo(newParentId, newParent->name());
}
void Bin::doMoveFolder(const QString &id, const QString &newParentId)
{
std::shared_ptr currentItem = m_itemModel->getFolderByBinId(id);
std::shared_ptr currentParent = currentItem->parent();
std::shared_ptr newParent = m_itemModel->getFolderByBinId(newParentId);
currentParent->removeChild(currentItem);
currentItem->changeParent(newParent);
emit storeFolder(id, newParent->clipId(), currentParent->clipId(), currentItem->name());
}
void Bin::droppedUrls(const QList &urls, const QStringList &folderInfo)
{
QModelIndex current;
if (folderInfo.isEmpty()) {
current = m_proxyModel->mapToSource(m_proxyModel->selectionModel()->currentIndex());
} else {
// get index for folder
current = getIndexForId(folderInfo.constFirst(), true);
}
slotItemDropped(urls, current);
}
void Bin::slotAddClipToProject(const QUrl &url)
{
QList urls;
urls << url;
QModelIndex current = m_proxyModel->mapToSource(m_proxyModel->selectionModel()->currentIndex());
slotItemDropped(urls, current);
}
void Bin::slotItemDropped(const QList &urls, const QModelIndex &parent)
{
- QStringList folderInfo;
+ QString parentFolder = m_itemModel->getRootFolder()->clipId();
if (parent.isValid()) {
// Check if drop occured on a folder
std::shared_ptr parentItem = m_itemModel->getBinItemByIndex(parent);
while (parentItem->itemType() != AbstractProjectItem::FolderItem) {
parentItem = parentItem->parent();
}
- if (parentItem != m_itemModel->getRootFolder()) {
- folderInfo << parentItem->clipId();
- }
- }
- // TODO: verify if urls exist
- QList clipsToAdd = urls;
- QMimeDatabase db;
- for (const QUrl &file : clipsToAdd) {
- // Check there is no folder here
- QMimeType type = db.mimeTypeForUrl(file);
- if (type.inherits(QStringLiteral("inode/directory"))) {
- // user dropped a folder, import its files
- clipsToAdd.removeAll(file);
- QDir dir(file.toLocalFile());
- const QStringList result = dir.entryList(QDir::Files);
- QList folderFiles;
- folderFiles.reserve(result.count());
- for (const QString &path : result) {
- folderFiles.append(QUrl::fromLocalFile(dir.absoluteFilePath(path)));
- }
- if (!folderFiles.isEmpty()) {
- QString folderId = slotAddFolder(dir.dirName());
- QModelIndex ind = getIndexForId(folderId, true);
- QStringList newFolderInfo;
- if (ind.isValid()) {
- newFolderInfo = getFolderInfo(m_proxyModel->mapFromSource(ind));
- }
- ClipCreationDialog::createClipsCommand(m_doc, folderFiles, newFolderInfo, this);
- }
- }
- }
- if (!clipsToAdd.isEmpty()) {
- ClipCreationDialog::createClipsCommand(m_doc, clipsToAdd, folderInfo, this);
+ parentFolder = parentItem->clipId();
}
+ ClipCreator::createClipsFromList(urls, true, parentFolder, m_itemModel);
}
void Bin::slotExpandUrl(const ItemInfo &info, const QString &url, QUndoCommand *command)
{
- //TODO reimplement this
+ // TODO reimplement this
/*
// Create folder to hold imported clips
QString folderName = QFileInfo(url).fileName().section(QLatin1Char('.'), 0, 0);
QString folderId = QString::number(getFreeFolderId());
new AddBinFolderCommand(this, folderId, folderName.isEmpty() ? i18n("Folder") : folderName, m_itemModel->getRootFolder()->clipId(), false, command);
// Parse playlist clips
QDomDocument doc;
QFile file(url);
doc.setContent(&file, false);
file.close();
bool invalid = false;
if (doc.documentElement().isNull()) {
invalid = true;
}
QDomNodeList producers = doc.documentElement().elementsByTagName(QStringLiteral("producer"));
QDomNodeList tracks = doc.documentElement().elementsByTagName(QStringLiteral("track"));
if (invalid || producers.isEmpty()) {
doDisplayMessage(i18n("Playlist clip %1 is invalid.", QFileInfo(url).fileName()), KMessageWidget::Warning);
delete command;
return;
}
if (tracks.count() > pCore->projectManager()->currentTimeline()->visibleTracksCount() + 1) {
doDisplayMessage(
i18n("Playlist clip %1 has too many tracks (%2) to be imported. Add new tracks to your project.", QFileInfo(url).fileName(), tracks.count()),
KMessageWidget::Warning);
delete command;
return;
}
// Maps playlist producer IDs to (project) bin producer IDs.
QMap idMap;
// Maps hash IDs to (project) first playlist producer instance ID. This is
// necessary to detect duplicate producer serializations produced by MLT.
// This covers, for instance, images and titles.
QMap