refactor: workspace layout — crates/, app at root, legacy C++ removed
Single mechanical restructure commit: - root Cargo.toml = oakapp bin + workspace; one cargo build produces oakapp, oak-cli, oak-worker, liboakengine.dylib - app/rust/src -> src/ (app at repo root, no rust/ nesting) - src/<mod>/rust -> crates/oak<mod>; src/oakcore-rs -> crates/oakcore; src/bindings/oakotio -> crates/oakotio; src/engine/rust -> crates/oakengine (keeps cdylib+staticlib+rlib) - public C headers include/<mod>/ -> crates/oakengine/include/<mod>/ - OFX SDK headers vendored into crates/oakplugin/ofx/ (HostSupport gone) - legacy deleted: old src/ C++ modules, engine/, core/, ffmpeg_bridge/, app/ (Qt), cli/worker C++, root CMakeLists, third_party/KDDockWidgets submodule, otio-install, all build-* output (~40GB) - oakstorage kept but excluded from the workspace (skeleton w/ todos); gpui excluded (own workspace) - verified: cargo build green, cargo test --workspace 1845/0 (with the documented OCIO_RS_* env override for the homebrew OCIO)
This commit is contained in:
@@ -1,7 +1,3 @@
|
||||
[submodule "third_party/KDDockWidgets"]
|
||||
path = third_party/KDDockWidgets
|
||||
url = https://github.com/OliveCommunity/KDDockWidgets.git
|
||||
branch = main
|
||||
[submodule "gpui"]
|
||||
path = gpui
|
||||
url = https://github.com/OakVideoEditorCommunity/oak-gpui.git
|
||||
|
||||
-406
@@ -1,406 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2023 Olive Studios LLC
|
||||
# Modifications Copyright (C) 2025 mikesolar
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
||||
|
||||
project(olive-editor VERSION 0.4.1 LANGUAGES CXX)
|
||||
# Fallback version used only when git is unavailable (e.g. source tarball);
|
||||
# normally overridden by the git tag / commit hash logic further below.
|
||||
# Edit version.txt to change it.
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/version.txt")
|
||||
file(STRINGS "${CMAKE_SOURCE_DIR}/version.txt" PROJECT_VERSION LIMIT_COUNT 1)
|
||||
string(STRIP "${PROJECT_VERSION}" PROJECT_VERSION)
|
||||
else()
|
||||
set(PROJECT_VERSION "0.0.0-unknown")
|
||||
endif()
|
||||
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DOFX_SUPPORTS_OPENGLRENDER")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOFX_SUPPORTS_OPENGLRENDER")
|
||||
option(BUILD_QT6 "Build with Qt 6 over 5 (experimental)" ON)
|
||||
option(BUILD_DOXYGEN "Build Doxygen documentation" OFF)
|
||||
option(BUILD_TESTS "Build unit tests" OFF)
|
||||
option(USE_WERROR "Error on compile warning" OFF)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
# Generates a compile_commands.json in the build dir, link that to the repo
|
||||
# root to enrich your IDE with clangd language server protocol functionalities
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
include_directories(include/)
|
||||
# Sanitizers
|
||||
add_library(olive-sanitizers INTERFACE
|
||||
src/common/c_api/commandlineparser.cpp
|
||||
include/common/commandlineparser.h)
|
||||
include(cmake/Sanitizers.cmake)
|
||||
enable_sanitizers(olive-sanitizers)
|
||||
list(APPEND OLIVE_LIBRARIES olive-sanitizers)
|
||||
|
||||
# Set compiler options
|
||||
if(MSVC)
|
||||
set(OLIVE_COMPILE_OPTIONS
|
||||
/wd4267
|
||||
/wd4244
|
||||
/experimental:external
|
||||
/external:anglebrackets
|
||||
/external:W0
|
||||
"$<$<CONFIG:RELEASE>:/O2>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:/MP>"
|
||||
/DOFX_SUPPORTS_OPENGLRENDER
|
||||
)
|
||||
if (USE_WERROR)
|
||||
list(APPEND OLIVE_COMPILE_OPTIONS "/WX")
|
||||
endif()
|
||||
else()
|
||||
set(OLIVE_COMPILE_OPTIONS
|
||||
"$<$<CONFIG:RELEASE>:-O2>"
|
||||
-Wuninitialized
|
||||
-pedantic-errors
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
-Wshadow
|
||||
-DOFX_SUPPORTS_OPENGLRENDER
|
||||
|
||||
)
|
||||
if (USE_WERROR)
|
||||
list(APPEND OLIVE_COMPILE_OPTIONS "-Werror")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# MinGW does not define WIN32/WINDOWS the way OpenFX HostSupport expects.
|
||||
# Ensure these are visible so ofxhBinary.h et al. pick the Windows code path.
|
||||
if(MINGW)
|
||||
add_compile_definitions(WIN32 WINDOWS)
|
||||
endif()
|
||||
|
||||
set(OLIVE_DEFINITIONS -DQT_DEPRECATED_WARNINGS)
|
||||
if (WIN32)
|
||||
list(APPEND OLIVE_DEFINITIONS -DUNICODE -D_UNICODE)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
# OFX HostSupport requires expat::expat
|
||||
find_package(EXPAT REQUIRED)
|
||||
if (TARGET EXPAT::EXPAT AND NOT TARGET expat::expat)
|
||||
add_library(expat::expat ALIAS EXPAT::EXPAT)
|
||||
endif()
|
||||
|
||||
# Link OpenGL
|
||||
if(UNIX AND NOT APPLE AND NOT DEFINED OpenGL_GL_PREFERENCE)
|
||||
set(OpenGL_GL_PREFERENCE LEGACY)
|
||||
endif()
|
||||
find_package(OpenGL REQUIRED)
|
||||
list(APPEND OLIVE_LIBRARIES OpenGL::GL)
|
||||
|
||||
# Optional: Link Vulkan and shaderc (for the Vulkan render backend)
|
||||
find_package(Vulkan)
|
||||
find_package(PkgConfig)
|
||||
if(PkgConfig_FOUND)
|
||||
pkg_check_modules(SHADERC shaderc)
|
||||
endif()
|
||||
if(Vulkan_FOUND)
|
||||
message(STATUS "Vulkan found: ${Vulkan_LIBRARIES}")
|
||||
else()
|
||||
message(STATUS " Vulkan not found. The Vulkan render backend will be disabled.")
|
||||
endif()
|
||||
|
||||
# Link OpenColorIO
|
||||
find_package(OpenColorIO 2.1.1 REQUIRED)
|
||||
list(APPEND OLIVE_LIBRARIES ${OCIO_LIBRARIES})
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${OCIO_INCLUDE_DIRS})
|
||||
|
||||
# Link OpenImageIO
|
||||
find_package(OpenImageIO 2.1.12 REQUIRED)
|
||||
list(APPEND OLIVE_LIBRARIES ${OIIO_LIBRARIES})
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${OIIO_INCLUDE_DIRS})
|
||||
|
||||
# Link OpenEXR
|
||||
find_package(OpenEXR REQUIRED)
|
||||
list(APPEND OLIVE_LIBRARIES ${OPENEXR_LIBRARIES})
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${OPENEXR_INCLUDES})
|
||||
|
||||
# Link Olive
|
||||
list(APPEND OLIVE_LIBRARIES olivecore)
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/core/include)
|
||||
|
||||
# Shared header-only utilities (used by both engine/ and app/)
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/shared/include)
|
||||
|
||||
|
||||
# Link Qt
|
||||
set(QT_LIBRARIES
|
||||
Core
|
||||
Gui
|
||||
Widgets
|
||||
OpenGL
|
||||
LinguistTools
|
||||
Concurrent
|
||||
)
|
||||
if (UNIX AND NOT APPLE)
|
||||
list(APPEND QT_LIBRARIES DBus)
|
||||
endif()
|
||||
if (BUILD_QT6)
|
||||
set(QT_NAME Qt6)
|
||||
else()
|
||||
set(QT_NAME Qt5)
|
||||
endif()
|
||||
find_package(QT
|
||||
NAMES
|
||||
${QT_NAME}
|
||||
REQUIRED
|
||||
COMPONENTS
|
||||
${QT_LIBRARIES}
|
||||
OPTIONAL_COMPONENTS
|
||||
Network
|
||||
)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED
|
||||
COMPONENTS
|
||||
${QT_LIBRARIES}
|
||||
OPTIONAL_COMPONENTS
|
||||
Network
|
||||
)
|
||||
if (NOT Qt${QT_VERSION_MAJOR}Network_FOUND)
|
||||
message(" Qt${QT_VERSION_MAJOR}::Network module not found, crash reporting will be disabled.")
|
||||
endif()
|
||||
list(APPEND OLIVE_LIBRARIES
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
Qt${QT_VERSION_MAJOR}::OpenGL
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
)
|
||||
|
||||
if (${QT_VERSION_MAJOR} EQUAL "6")
|
||||
find_package(Qt${QT_VERSION_MAJOR}
|
||||
REQUIRED
|
||||
OpenGLWidgets
|
||||
)
|
||||
|
||||
list(APPEND OLIVE_LIBRARIES
|
||||
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
|
||||
)
|
||||
|
||||
# Link KDDockWidgets
|
||||
#find_package(KDDockWidgets-qt6 CONFIG REQUIRED)
|
||||
else()
|
||||
# Link KDDockWidgets
|
||||
#find_package(KDDockWidgets CONFIG REQUIRED)
|
||||
endif()
|
||||
|
||||
list(APPEND OLIVE_LIBRARIES
|
||||
KDAB::kddockwidgets
|
||||
)
|
||||
|
||||
# Link OFX HostSupport wherever libolive-editor objects are used.
|
||||
list(APPEND OLIVE_LIBRARIES OfxHost)
|
||||
|
||||
# FFmpeg isolation: all FFmpeg access in the editor goes through this shared
|
||||
# library's pure C API. It is the only component that links FFmpeg.
|
||||
add_subdirectory(ffmpeg_bridge)
|
||||
list(APPEND OLIVE_LIBRARIES ffmpeg_bridge)
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_bridge/include)
|
||||
|
||||
# Link PortAudio
|
||||
find_package(PortAudio REQUIRED)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${PORTAUDIO_INCLUDE_DIRS})
|
||||
include(CheckIncludeFileCXX)
|
||||
check_include_file_cxx( "pa_jack.h" PA_HAS_JACK)
|
||||
if (PA_HAS_JACK)
|
||||
list(APPEND OLIVE_DEFINITIONS PA_HAS_JACK)
|
||||
endif()
|
||||
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${PORTAUDIO_INCLUDE_DIRS})
|
||||
list(APPEND OLIVE_LIBRARIES ${PORTAUDIO_LIBRARIES})
|
||||
|
||||
# Required: Link OpenTimelineIO
|
||||
find_package(OpenTimelineIO REQUIRED)
|
||||
list(APPEND OLIVE_DEFINITIONS USE_OTIO)
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${OTIO_INCLUDE_DIRS})
|
||||
list(APPEND OLIVE_LIBRARIES ${OTIO_LIBRARIES})
|
||||
|
||||
# Bundle the OTIO shared libraries into our install tree so that packages
|
||||
# (deb/rpm/AppImage/Windows installer) ship them — most distros have no OTIO
|
||||
# package to depend on. Distro-native packaging (e.g. Arch PKGBUILD, where
|
||||
# opentimelineio is a proper depends entry) should pass -DOAK_BUNDLE_OTIO=OFF.
|
||||
option(OAK_BUNDLE_OTIO "Install OTIO runtime libraries alongside Oak" ON)
|
||||
if (OAK_BUNDLE_OTIO AND UNIX AND NOT APPLE)
|
||||
include(GNUInstallDirs)
|
||||
file(GLOB _otio_runtime_libs
|
||||
"${OTIO_LIBRARY_DIR}/libopentimelineio.so*"
|
||||
"${OTIO_LIBRARY_DIR}/libopentime.so*")
|
||||
if (_otio_runtime_libs)
|
||||
install(FILES ${_otio_runtime_libs} DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# OTIO's macOS dylibs use @loader_path install names: every binary that
|
||||
# (transitively) links them needs a copy of the dylibs next to itself.
|
||||
# Windows has no rpath either, so its DLLs must also sit next to each
|
||||
# executable (relying on PATH is fragile in CI shells).
|
||||
# Call oak_copy_otio_runtime(<target>) for each executable/shared library.
|
||||
if (OAK_BUNDLE_OTIO AND (APPLE OR WIN32) AND OTIO_LIBRARY_DIR)
|
||||
if (APPLE)
|
||||
file(GLOB OAK_OTIO_DYLIBS
|
||||
"${OTIO_LIBRARY_DIR}/libopentimelineio*.dylib"
|
||||
"${OTIO_LIBRARY_DIR}/libopentime*.dylib")
|
||||
else()
|
||||
file(GLOB OAK_OTIO_DYLIBS
|
||||
"${OTIO_LIBRARY_DIR}/libopentimelineio*.dll"
|
||||
"${OTIO_LIBRARY_DIR}/libopentime*.dll")
|
||||
endif()
|
||||
function(oak_copy_otio_runtime target)
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${OAK_OTIO_DYLIBS} $<TARGET_FILE_DIR:${target}>)
|
||||
endfunction()
|
||||
endif()
|
||||
|
||||
# Optional: Link Google Crashpad
|
||||
find_package(GoogleCrashpad)
|
||||
if (GoogleCrashpad_FOUND)
|
||||
list(APPEND OLIVE_DEFINITIONS USE_CRASHPAD)
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${CRASHPAD_INCLUDE_DIRS})
|
||||
list(APPEND OLIVE_LIBRARIES ${CRASHPAD_LIBRARIES})
|
||||
else()
|
||||
message(" Automatic crash reporting will be disabled.")
|
||||
|
||||
if (APPLE)
|
||||
# Enables use of special functions for slider dragging, only linked if Crashpad isn't found
|
||||
# because Crashpad links it itself and will cause duplicate references if we also link it
|
||||
list(APPEND OLIVE_LIBRARIES "-framework ApplicationServices")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND OLIVE_LIBRARIES "-framework IOKit")
|
||||
elseif(UNIX)
|
||||
list(APPEND OLIVE_LIBRARIES Qt${QT_VERSION_MAJOR}::DBus)
|
||||
endif()
|
||||
|
||||
# Determine version from git: the tag name if HEAD is exactly on a tag,
|
||||
# otherwise the first 8 hex digits of the commit hash. Falls back to
|
||||
# version.txt (read above) when git is unavailable (e.g. tarball).
|
||||
set(PROJECT_LONG_VERSION ${PROJECT_VERSION})
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
find_package(Git)
|
||||
if(GIT_FOUND)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --exact-match --tags HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
if(GIT_TAG)
|
||||
# Tags are named like "v0.4.1-alpha"; drop the leading "v"
|
||||
string(REGEX REPLACE "^v" "" PROJECT_VERSION "${GIT_TAG}")
|
||||
else()
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short=8 HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE PROJECT_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
endif()
|
||||
|
||||
set(PROJECT_LONG_VERSION ${PROJECT_VERSION})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Optional: Find Doxygen if requested
|
||||
if(BUILD_DOXYGEN)
|
||||
find_package(Doxygen)
|
||||
endif()
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/third_party)
|
||||
|
||||
# Google Test discovery (shared by core/tests, engine/tests, tests/)
|
||||
if (BUILD_TESTS)
|
||||
include(FetchContent)
|
||||
find_package(GTest QUIET)
|
||||
if (NOT GTest_FOUND)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip
|
||||
)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(core)
|
||||
|
||||
set(KDDockWidgets_STATIC ON CACHE INTERNAL "Force KDDockWidgets to build statically")
|
||||
set(KDDockWidgets_QT6 ${BUILD_QT6} CACHE INTERNAL "Conform KDDockWidgets' Qt 6 setting to ours")
|
||||
# Oak only uses the QtWidgets frontend; building the QtQuick frontend causes
|
||||
# duplicate QML module registration on macOS and pulls in unused dependencies.
|
||||
set(KDDockWidgets_FRONTENDS "qtwidgets" CACHE INTERNAL "Only build the QtWidgets frontend for Oak")
|
||||
add_subdirectory(third_party/KDDockWidgets EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(third_party/openfx/HostSupport)
|
||||
add_subdirectory(engine)
|
||||
add_subdirectory(cli)
|
||||
|
||||
# Internal consumers (app, tests) link oakengine-obj directly instead of the
|
||||
# oakengine shared library (see app/CMakeLists.txt). Linking both breaks the
|
||||
# Windows build: the DLL import library re-defines every symbol already
|
||||
# provided by the object files (multiple definition errors at link time).
|
||||
|
||||
add_subdirectory(app)
|
||||
add_subdirectory(worker)
|
||||
add_subdirectory(src)
|
||||
if (BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# CPack / system package configuration
|
||||
# ------------------------------------------------------------------------------
|
||||
set(CPACK_PACKAGE_NAME "oak-video-editor")
|
||||
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
|
||||
set(CPACK_PACKAGE_VENDOR "Oak Video Editor Team")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Oak - Non-linear video editor")
|
||||
set(CPACK_PACKAGE_HOMEPAGE_URL "https://oakvideoeditor.org")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
|
||||
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")
|
||||
|
||||
# Debian
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Oak Video Editor Team")
|
||||
set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS
|
||||
"libqt6core6, libqt6gui6, libqt6widgets6, libqt6opengl6, libqt6openglwidgets6, libqt6network6, libqt6concurrent6, libavcodec60, libavformat60, libavutil58, libswscale7, libswresample4, libavfilter9, libopenimageio2.4, libopencolorio2, libopenexr-3-1-30, libexpat1, libportaudio2, libgl1, libvulkan1, libxkbcommon0")
|
||||
|
||||
# RPM
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "GPLv3")
|
||||
set(CPACK_RPM_PACKAGE_GROUP "Applications/Multimedia")
|
||||
set(CPACK_RPM_PACKAGE_URL "https://oakvideoeditor.org")
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES
|
||||
"qt6-qtbase >= 6.0, qt6-qtbase-gui >= 6.0, qt6-qttools, ffmpeg-libs >= 6.0, OpenImageIO >= 2.4, OpenColorIO >= 2.0, openexr >= 3.1, expat, portaudio, mesa-libGL, vulkan-loader, libxkbcommon")
|
||||
|
||||
include(CPack)
|
||||
|
||||
Generated
+731
-34
File diff suppressed because it is too large
Load Diff
+84
@@ -0,0 +1,84 @@
|
||||
# Oak Video Editor - Non-Linear Video Editor
|
||||
# Copyright (C) 2026 Oak Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Root manifest: the `oakapp` package (the gpui-based application) plus the
|
||||
# Cargo workspace over every crate under crates/.
|
||||
#
|
||||
# oakstorage (crates/oakstorage) is deliberately excluded: it is a work in
|
||||
# progress whose contract tests are still `todo!()` stubs (they cannot pass
|
||||
# yet), and it pulls heavy database backends (sea-orm). It stays a
|
||||
# standalone crate with its own Cargo.lock; build it with
|
||||
# `cd crates/oakstorage && cargo build`.
|
||||
#
|
||||
# gpui (the oak-gpui fork at gpui/) is excluded too: it is a separate git
|
||||
# repository with its own workspace (resolver 3, edition 2024,
|
||||
# workspace.package/workspace.dependencies). Without the exclusion its
|
||||
# crates would be auto-included here via oakapp's path dependencies and
|
||||
# would inherit from THIS workspace's [workspace.package] (which lacks the
|
||||
# keys gpui expects). Excluded, each gpui crate resolves against gpui's own
|
||||
# workspace root, exactly as before the monorepo workspace existed.
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
exclude = ["crates/oakstorage", "gpui"]
|
||||
default-members = [".", "crates/oak-cli", "crates/oak-worker", "crates/oakengine"]
|
||||
resolver = "2"
|
||||
|
||||
[profile.release]
|
||||
# FFI discipline: every module crate exports an `extern "C"` ABI whose
|
||||
# entry points must never unwind/abort across the boundary; panics are
|
||||
# caught by catch_unwind and mapped to error codes instead. `unwind` is
|
||||
# also rustc's default, but this makes the project-wide policy explicit
|
||||
# (it used to live in each member's Cargo.toml, which a workspace root
|
||||
# ignores).
|
||||
panic = "unwind"
|
||||
|
||||
[package]
|
||||
name = "oakapp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Oak Video Editor application layer (Rust, gpui-based)"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lib]
|
||||
name = "oakapp"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "oakapp"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# gpui: the GPU-accelerated UI framework (oak-gpui fork, git submodule at gpui/).
|
||||
gpui = { path = "gpui/crates/gpui" }
|
||||
# Convenience entry point: `gpui_platform::application()` picks the platform
|
||||
# backend. font-kit enables text shaping/rendering on macOS.
|
||||
gpui_platform = { path = "gpui/crates/gpui_platform", features = ["font-kit"] }
|
||||
# Oak's widget library: menus, viewer, form controls, project explorer.
|
||||
gpui_widgets = { path = "gpui/crates/gpui_widgets" }
|
||||
# The mock engine's synthetic viewer frames (`image::Frame` in a
|
||||
# `RenderImage`), matching the versions gpui itself uses.
|
||||
image = "0.25"
|
||||
smallvec = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
# `#[gpui::test]` harness for engine-seam smoke tests (test-support feature).
|
||||
gpui = { path = "gpui/crates/gpui", features = ["test-support"] }
|
||||
# `test-support` also enables `gpui_macos/test-support`, which is what makes
|
||||
# `render_to_image` (the screenshot example) available.
|
||||
gpui_platform = { path = "gpui/crates/gpui_platform", features = ["test-support"] }
|
||||
# Screenshot capture: `examples/screenshot.rs` saves the rendered window PNG
|
||||
# (the `image` crate is already in the lockfile through gpui).
|
||||
image = "0.25"
|
||||
@@ -1,221 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
# Modifications Copyright (C) 2025 mikesolar
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Set Olive sources and resources
|
||||
set(OLIVE_SOURCES
|
||||
core.h
|
||||
core.cpp
|
||||
engineeventbridge.h
|
||||
engineeventbridge.cpp
|
||||
common/colorcodingapp.h
|
||||
common/colorcodingapp.cpp
|
||||
common/nodevaluehandle.h
|
||||
playback/playbackcontroller.h
|
||||
playback/playbackcontroller.cpp
|
||||
)
|
||||
|
||||
#set(OLIVE_RESOURCES)
|
||||
|
||||
# Add subdirectories, which will populate the above variables
|
||||
add_subdirectory(dialog)
|
||||
add_subdirectory(packaging)
|
||||
add_subdirectory(panel)
|
||||
add_subdirectory(timeline)
|
||||
add_subdirectory(ts)
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(widget)
|
||||
add_subdirectory(window)
|
||||
|
||||
# Add translations
|
||||
qt_add_translation(OLIVE_QM_FILES ${OLIVE_TS_FILES})
|
||||
|
||||
set(QRC_BODY "")
|
||||
foreach (QM_FILE ${OLIVE_QM_FILES})
|
||||
get_filename_component(QM_FILENAME_COMPONENT ${QM_FILE} NAME_WE)
|
||||
string(APPEND QRC_BODY "<file alias=\"${QM_FILENAME_COMPONENT}\">${QM_FILE}</file>\n")
|
||||
endforeach ()
|
||||
configure_file(ts/translations.qrc.in ts/translations.qrc @ONLY)
|
||||
|
||||
set(OLIVE_RESOURCES
|
||||
${OLIVE_RESOURCES}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ts/translations.qrc
|
||||
widget/nodeparamview/nodeparambutton.cpp
|
||||
widget/nodeparamview/nodeparambutton.h
|
||||
)
|
||||
|
||||
# Add main library
|
||||
add_library(libolive-editor
|
||||
OBJECT
|
||||
${OLIVE_SOURCES}
|
||||
${OLIVE_RESOURCES}
|
||||
)
|
||||
|
||||
target_compile_features(libolive-editor PUBLIC cxx_std_23)
|
||||
include_directories(../third_party/openfx/include
|
||||
../third_party/openfx/HostSupport/include)
|
||||
# Remove prefix - prevents CMake calling it "liblibolive-editor"
|
||||
set_target_properties(libolive-editor PROPERTIES PREFIX "")
|
||||
|
||||
option(OAK_ENABLE_DYNAMIC_RENDER_BACKEND "Build and use the dynamic render backend adapter" ON)
|
||||
if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
|
||||
set_target_properties(libolive-editor PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_definitions(libolive-editor PRIVATE OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
|
||||
|
||||
foreach (target olivecore kddockwidgets)
|
||||
if (TARGET ${target})
|
||||
set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
endif ()
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
# Add application
|
||||
add_executable(olive-editor
|
||||
main.cpp
|
||||
$<TARGET_OBJECTS:libolive-editor>
|
||||
$<TARGET_OBJECTS:olive-version-obj>
|
||||
)
|
||||
|
||||
if (COMMAND oak_copy_otio_runtime)
|
||||
oak_copy_otio_runtime(olive-editor)
|
||||
endif()
|
||||
|
||||
target_link_libraries(olive-editor PUBLIC OfxHost)
|
||||
add_dependencies(olive-editor oakgl-cabi-check)
|
||||
if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
|
||||
add_dependencies(olive-editor oakgl)
|
||||
if (TARGET oakvulkan)
|
||||
add_dependencies(olive-editor oakvulkan)
|
||||
endif ()
|
||||
endif ()
|
||||
set_target_properties(olive-editor PROPERTIES OUTPUT_NAME "oak-editor")
|
||||
|
||||
# Create docs if doxygen was found
|
||||
if (DOXYGEN_FOUND)
|
||||
set(DOXYGEN_PROJECT_NAME "Oak Video Editor")
|
||||
set(DOXYGEN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/docs")
|
||||
set(DOXYGEN_EXTRACT_ALL "YES")
|
||||
set(DOXYGEN_EXTRACT_PRIVATE "YES")
|
||||
doxygen_add_docs(docs ALL ${OLIVE_SOURCES})
|
||||
endif ()
|
||||
|
||||
# Platform-specific deployment preferences
|
||||
if (WIN32)
|
||||
# Set Windows application icon
|
||||
target_sources(olive-editor PRIVATE packaging/windows/resources.rc)
|
||||
|
||||
# Preserve folder structure in visual studio
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${OLIVE_SOURCES})
|
||||
|
||||
elseif (APPLE)
|
||||
# Set Mac application icon
|
||||
set(OLIVE_ICON packaging/macos/oak.icns)
|
||||
target_sources(olive-editor PRIVATE ${OLIVE_ICON})
|
||||
|
||||
# Set Mac bundle properties
|
||||
set_target_properties(olive-editor PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/packaging/macos/MacOSXBundleInfo.plist.in
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER org.oakvideoeditor.Oak
|
||||
MACOSX_BUNDLE_ICON_FILE oak.icns
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Oak Video Editor"
|
||||
MACOSX_BUNDLE_INFO_STRING "Oak Video Editor ${PROJECT_LONG_VERSION}"
|
||||
MACOSX_BUNDLE_COPYRIGHT "©2018-2021 Olive Studios LLC and others. Fork maintained by Oak Video Editor Team."
|
||||
RESOURCE "${OLIVE_ICON}"
|
||||
OUTPUT_NAME "Oak"
|
||||
)
|
||||
|
||||
# Copy the render worker, dynamic render backends, and the FFmpeg bridge
|
||||
# library into the app bundle. They are looked up in
|
||||
# QCoreApplication::applicationDirPath(), which on macOS points to
|
||||
# Oak.app/Contents/MacOS.
|
||||
add_custom_command(TARGET olive-editor POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olive-render-worker> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:oakgl> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:ffmpeg_bridge> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olivecore> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:oakengine> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
COMMENT "Copying oak-render-worker, render backends, ffmpeg_bridge, liboakcore and liboakengine into Oak.app"
|
||||
)
|
||||
if (TARGET oakvulkan)
|
||||
add_custom_command(TARGET olive-editor POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:oakvulkan> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
|
||||
)
|
||||
endif ()
|
||||
elseif (UNIX)
|
||||
# Set Linux-specific properties for application
|
||||
install(TARGETS olive-editor RUNTIME DESTINATION bin)
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
# Windows has no RPATH: shared libraries must sit next to the executable
|
||||
add_custom_command(TARGET olive-editor POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:ffmpeg_bridge> $<TARGET_FILE_DIR:olive-editor>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olivecore> $<TARGET_FILE_DIR:olive-editor>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:oakengine> $<TARGET_FILE_DIR:olive-editor>
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Set link libraries
|
||||
target_link_libraries(olive-editor PRIVATE ${OLIVE_LIBRARIES})
|
||||
target_link_libraries(libolive-editor PRIVATE ${OLIVE_LIBRARIES})
|
||||
# The app is an internal consumer that still uses engine C++ classes directly.
|
||||
# Link the object library to bypass the version-script restrictions on
|
||||
# liboakengine.so (which only exposes the C ABI for external consumers).
|
||||
target_link_libraries(olive-editor PRIVATE oakengine-obj)
|
||||
target_link_libraries(libolive-editor PRIVATE oakengine-obj)
|
||||
|
||||
# The ffmpeg_bridge shared library ships next to the binaries: inside the
|
||||
# macOS app bundle (Contents/MacOS, resolved via @loader_path), and in the
|
||||
# standard lib directory on other platforms.
|
||||
if (APPLE)
|
||||
# macOS bundles are distributed via POST_BUILD copies (not install()), so
|
||||
# @loader_path must already be in the build-tree binaries' RPATH.
|
||||
set(OLIVE_FB_RPATH "@loader_path")
|
||||
set_target_properties(olive-editor PROPERTIES
|
||||
BUILD_RPATH "@loader_path")
|
||||
elseif (UNIX)
|
||||
set(OLIVE_FB_RPATH "$ORIGIN/../lib")
|
||||
endif ()
|
||||
if (OLIVE_FB_RPATH)
|
||||
set_target_properties(olive-editor PROPERTIES INSTALL_RPATH "${OLIVE_FB_RPATH}")
|
||||
if (TARGET oakgl)
|
||||
set_target_properties(oakgl PROPERTIES INSTALL_RPATH "${OLIVE_FB_RPATH}")
|
||||
endif ()
|
||||
if (TARGET oakvulkan)
|
||||
set_target_properties(oakvulkan PROPERTIES INSTALL_RPATH "${OLIVE_FB_RPATH}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Set compile options
|
||||
target_compile_options(olive-editor PRIVATE ${OLIVE_COMPILE_OPTIONS})
|
||||
target_compile_options(libolive-editor PRIVATE ${OLIVE_COMPILE_OPTIONS})
|
||||
|
||||
# Set global definitions
|
||||
target_compile_definitions(olive-editor PRIVATE ${OLIVE_DEFINITIONS})
|
||||
target_compile_definitions(libolive-editor PRIVATE ${OLIVE_DEFINITIONS})
|
||||
|
||||
# Set include dirs
|
||||
target_include_directories(olive-editor PRIVATE ${OLIVE_INCLUDE_DIRS})
|
||||
target_include_directories(libolive-editor PRIVATE ${OLIVE_INCLUDE_DIRS})
|
||||
|
||||
# Add crash handler
|
||||
if (GoogleCrashpad_FOUND AND Qt${QT_VERSION_MAJOR}Network_FOUND)
|
||||
add_subdirectory(crashhandler)
|
||||
endif ()
|
||||
@@ -1,81 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/colorcodingapp.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
QVector<Color> AppColorCoding::colors = {
|
||||
Color(0.545f, 0.255f, 0.255f), Color(0.412f, 0.188f, 0.259f),
|
||||
Color(0.561f, 0.427f, 0.239f), Color(0.486f, 0.306f, 0.235f),
|
||||
Color(0.631f, 0.612f, 0.212f), Color(0.404f, 0.478f, 0.243f),
|
||||
Color(0.349f, 0.576f, 0.275f), Color(0.224f, 0.459f, 0.251f),
|
||||
Color(0.259f, 0.471f, 0.541f), Color(0.184f, 0.376f, 0.329f),
|
||||
Color(0.259f, 0.365f, 0.541f), Color(0.196f, 0.216f, 0.412f),
|
||||
Color(0.612f, 0.294f, 0.502f), Color(0.404f, 0.220f, 0.459f),
|
||||
Color(0.800f, 0.800f, 0.800f), Color(0.502f, 0.502f, 0.502f)
|
||||
};
|
||||
|
||||
const QVector<Color> &AppColorCoding::standard_colors()
|
||||
{
|
||||
return colors;
|
||||
}
|
||||
|
||||
QString AppColorCoding::get_color_name(int c)
|
||||
{
|
||||
switch (c) {
|
||||
case k_red: return QObject::tr("Red");
|
||||
case k_maroon: return QObject::tr("Maroon");
|
||||
case k_orange: return QObject::tr("Orange");
|
||||
case k_brown: return QObject::tr("Brown");
|
||||
case k_yellow: return QObject::tr("Yellow");
|
||||
case k_olive: return QObject::tr("Oak");
|
||||
case k_lime: return QObject::tr("Lime");
|
||||
case k_green: return QObject::tr("Green");
|
||||
case k_cyan: return QObject::tr("Cyan");
|
||||
case k_teal: return QObject::tr("Teal");
|
||||
case k_blue: return QObject::tr("Blue");
|
||||
case k_navy: return QObject::tr("Navy");
|
||||
case k_pink: return QObject::tr("Pink");
|
||||
case k_purple: return QObject::tr("Purple");
|
||||
case k_silver: return QObject::tr("Silver");
|
||||
case k_gray: return QObject::tr("Gray");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
Color AppColorCoding::get_color(int c)
|
||||
{
|
||||
return colors.at(c);
|
||||
}
|
||||
|
||||
Qt::GlobalColor AppColorCoding::get_ui_selector_color(const Color &c)
|
||||
{
|
||||
if (c.get_rough_luminance() > 0.40f) {
|
||||
return Qt::black;
|
||||
} else {
|
||||
return Qt::white;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_COLORCODINGAPP_H
|
||||
#define OAK_COLORCODINGAPP_H
|
||||
|
||||
#include <olive/core/core.h>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using namespace core;
|
||||
|
||||
/**
|
||||
* @brief App-side color label mapping (moved from engine/ui/colorcoding.h)
|
||||
*
|
||||
* Provides the same static color-label mapping as the engine version but
|
||||
* without QObject inheritance (no moc symbols). Only the static methods
|
||||
* used by app code are included.
|
||||
*/
|
||||
class AppColorCoding {
|
||||
public:
|
||||
enum Code {
|
||||
k_red,
|
||||
k_maroon,
|
||||
k_orange,
|
||||
k_brown,
|
||||
k_yellow,
|
||||
k_olive,
|
||||
k_lime,
|
||||
k_green,
|
||||
k_cyan,
|
||||
k_teal,
|
||||
k_blue,
|
||||
k_navy,
|
||||
k_pink,
|
||||
k_purple,
|
||||
k_silver,
|
||||
k_gray
|
||||
};
|
||||
|
||||
static QString get_color_name(int c);
|
||||
|
||||
static Color get_color(int c);
|
||||
|
||||
static Qt::GlobalColor get_ui_selector_color(const Color &c);
|
||||
|
||||
static const QVector<Color> &standard_colors();
|
||||
|
||||
private:
|
||||
static QVector<Color> colors;
|
||||
};
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // OAK_COLORCODINGAPP_H
|
||||
@@ -1,213 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CONFIGWRAPPER_H
|
||||
#define OAK_CONFIGWRAPPER_H
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include "olive/core/util/rational.h"
|
||||
#include "oakengine/config.h"
|
||||
|
||||
// Facade migration B9b: replace the engine's OAK_CONFIG macro (which
|
||||
// references olive::Config::current()/operator[] and brings C++ symbols into
|
||||
// the editor binary) with a thin header-only wrapper around the C ABI.
|
||||
//
|
||||
// Include this header instead of "config/config.h" in app code. It undefines
|
||||
// the engine macros and redefines them to return an inline OakConfigValue that
|
||||
// forwards reads/writes to oakengine_config_*().
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class OakConfigValue {
|
||||
public:
|
||||
explicit OakConfigValue(const QString &key) : key_(key) {}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return oakengine_config_get_int(key_utf8(), 0) != 0;
|
||||
}
|
||||
operator int() const
|
||||
{
|
||||
return static_cast<int>(oakengine_config_get_int(key_utf8(), 0));
|
||||
}
|
||||
operator qint64() const
|
||||
{
|
||||
return static_cast<qint64>(oakengine_config_get_int(key_utf8(), 0));
|
||||
}
|
||||
operator quint64() const
|
||||
{
|
||||
return static_cast<quint64>(oakengine_config_get_int(key_utf8(), 0));
|
||||
}
|
||||
// int64_t/uint64_t overloads only exist where they differ from
|
||||
// qint64/quint64 (Linux LP64: int64_t is long; on macOS/Windows both are
|
||||
// long long, where declaring them would be a redeclaration).
|
||||
#if defined(__linux__)
|
||||
operator int64_t() const
|
||||
{
|
||||
return oakengine_config_get_int(key_utf8(), 0);
|
||||
}
|
||||
operator uint64_t() const
|
||||
{
|
||||
return static_cast<uint64_t>(oakengine_config_get_int(key_utf8(), 0));
|
||||
}
|
||||
#endif
|
||||
operator QString() const
|
||||
{
|
||||
char buf[1024];
|
||||
const int len = oakengine_config_get_string(key_utf8(), buf,
|
||||
sizeof(buf));
|
||||
return QString::fromUtf8(buf, len);
|
||||
}
|
||||
operator QVariant() const
|
||||
{
|
||||
return QVariant(static_cast<QString>(*this));
|
||||
}
|
||||
|
||||
OakConfigValue &operator=(bool v)
|
||||
{
|
||||
oakengine_config_set_int(key_utf8(), v ? 1 : 0);
|
||||
return *this;
|
||||
}
|
||||
OakConfigValue &operator=(int v)
|
||||
{
|
||||
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||
return *this;
|
||||
}
|
||||
OakConfigValue &operator=(uint v)
|
||||
{
|
||||
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||
return *this;
|
||||
}
|
||||
OakConfigValue &operator=(qint64 v)
|
||||
{
|
||||
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||
return *this;
|
||||
}
|
||||
OakConfigValue &operator=(quint64 v)
|
||||
{
|
||||
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||
return *this;
|
||||
}
|
||||
#if defined(__linux__)
|
||||
OakConfigValue &operator=(int64_t v)
|
||||
{
|
||||
oakengine_config_set_int(key_utf8(), v);
|
||||
return *this;
|
||||
}
|
||||
OakConfigValue &operator=(uint64_t v)
|
||||
{
|
||||
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
OakConfigValue &operator=(const QString &v)
|
||||
{
|
||||
const QByteArray utf8 = v.toUtf8();
|
||||
oakengine_config_set_string(key_utf8(), utf8.constData());
|
||||
return *this;
|
||||
}
|
||||
OakConfigValue &operator=(const char *v)
|
||||
{
|
||||
oakengine_config_set_string(key_utf8(), v ? v : "");
|
||||
return *this;
|
||||
}
|
||||
OakConfigValue &operator=(const QVariant &v)
|
||||
{
|
||||
switch (v.typeId()) {
|
||||
case QMetaType::Bool:
|
||||
*this = v.toBool();
|
||||
break;
|
||||
case QMetaType::Int:
|
||||
case QMetaType::UInt:
|
||||
case QMetaType::LongLong:
|
||||
case QMetaType::ULongLong:
|
||||
case QMetaType::Long:
|
||||
case QMetaType::Short:
|
||||
case QMetaType::Char:
|
||||
case QMetaType::ULong:
|
||||
case QMetaType::UShort:
|
||||
case QMetaType::UChar:
|
||||
*this = v.toLongLong();
|
||||
break;
|
||||
case QMetaType::Double:
|
||||
case QMetaType::Float:
|
||||
*this = static_cast<int64_t>(v.toDouble());
|
||||
break;
|
||||
default:
|
||||
*this = v.toString();
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool toBool() const { return static_cast<bool>(*this); }
|
||||
int toInt() const { return static_cast<int>(*this); }
|
||||
qint64 toLongLong() const { return static_cast<qint64>(*this); }
|
||||
quint64 toULongLong() const { return static_cast<quint64>(*this); }
|
||||
QString toString() const { return static_cast<QString>(*this); }
|
||||
|
||||
bool operator==(int rhs) const { return toInt() == rhs; }
|
||||
bool operator!=(int rhs) const { return toInt() != rhs; }
|
||||
bool operator==(qint64 rhs) const { return toLongLong() == rhs; }
|
||||
bool operator!=(qint64 rhs) const { return toLongLong() != rhs; }
|
||||
bool operator==(const QString &rhs) const { return toString() == rhs; }
|
||||
bool operator!=(const QString &rhs) const { return toString() != rhs; }
|
||||
bool operator==(const char *rhs) const { return toString() == QString::fromUtf8(rhs); }
|
||||
bool operator!=(const char *rhs) const { return toString() != QString::fromUtf8(rhs); }
|
||||
|
||||
template <typename T> T value() const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, olive::core::Rational>) {
|
||||
const QString s = static_cast<QString>(*this);
|
||||
const QByteArray utf8 = s.toUtf8();
|
||||
return olive::core::Rational::from_string(
|
||||
std::string(utf8.constData(), size_t(utf8.size())));
|
||||
} else {
|
||||
return static_cast<T>(*this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const char *key_utf8() const
|
||||
{
|
||||
key_utf8_ = key_.toUtf8();
|
||||
return key_utf8_.constData();
|
||||
}
|
||||
|
||||
QString key_;
|
||||
mutable QByteArray key_utf8_;
|
||||
};
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#ifdef OAK_CONFIG
|
||||
#undef OAK_CONFIG
|
||||
#endif
|
||||
#ifdef OAK_CONFIG_STR
|
||||
#undef OAK_CONFIG_STR
|
||||
#endif
|
||||
|
||||
#define OAK_CONFIG(x) olive::OakConfigValue(QStringLiteral(x))
|
||||
#define OAK_CONFIG_STR(x) olive::OakConfigValue(x)
|
||||
|
||||
#endif // OAK_CONFIGWRAPPER_H
|
||||
@@ -1,87 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_DEBUGAPP_H
|
||||
#define OAK_DEBUGAPP_H
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDateTime>
|
||||
#include <QMutex>
|
||||
#include <QTextStream>
|
||||
#include <iostream>
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief App-side debug handler (moved from engine/common/debug.cpp)
|
||||
*
|
||||
* Replaces engine's olive::debug_handler so oak-editor doesn't import
|
||||
* that symbol. Only used in main.cpp's qInstallMessageHandler.
|
||||
*/
|
||||
[[maybe_unused]] [[maybe_unused]] static void debug_handler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
{
|
||||
// Suppress noisy warnings from Qt's QXcbIntegration
|
||||
if (type == QtWarningMsg && msg.contains("QXcbIntegration")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Suppress all Qt warnings during automated testing
|
||||
static const bool is_testing = qEnvironmentVariableIsSet("OAK_TESTING");
|
||||
if (is_testing && type == QtWarningMsg) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString log_line;
|
||||
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
log_line = QStringLiteral("Debug: %1 (%2:%3, %4)\n");
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
log_line = QStringLiteral("Info: %1 (%2:%3, %4)\n");
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
log_line = QStringLiteral("Warning: %1 (%2:%3, %4)\n");
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
log_line = QStringLiteral("Critical: %1 (%2:%3, %4)\n");
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
log_line = QStringLiteral("Fatal: %1 (%2:%3, %4)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
log_line = log_line.arg(msg, context.file != nullptr ? context.file : "<null>",
|
||||
QString::number(context.line), context.function != nullptr ?
|
||||
context.function : "<null>");
|
||||
|
||||
std::cerr << log_line.toUtf8().constData();
|
||||
|
||||
if (type == QtFatalMsg) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // OAK_DEBUGAPP_H
|
||||
@@ -1,98 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
// App-side implementations of FileFunctions methods that would otherwise
|
||||
// be imported from liboakengine. The declarations live in the engine header
|
||||
// (common/filefunctions.h) which is on the public include path; these
|
||||
// definitions resolve the symbols locally in the app binary.
|
||||
|
||||
#include "oakutil/filefunctions.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
bool FileFunctions::directory_is_valid(const QDir &d,
|
||||
bool try_to_create_if_not_exists)
|
||||
{
|
||||
return d.exists() ||
|
||||
(try_to_create_if_not_exists && d.mkpath(QStringLiteral(".")));
|
||||
}
|
||||
|
||||
QString FileFunctions::read_file_as_string(const QString &filename)
|
||||
{
|
||||
QFile f(filename);
|
||||
QString file_data;
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream text_stream(&f);
|
||||
file_data = text_stream.readAll();
|
||||
f.close();
|
||||
}
|
||||
return file_data;
|
||||
}
|
||||
|
||||
QString FileFunctions::get_auto_recovery_root()
|
||||
{
|
||||
return QDir(QStandardPaths::writableLocation(
|
||||
QStandardPaths::AppLocalDataLocation))
|
||||
.filePath(QStringLiteral("autorecovery"));
|
||||
}
|
||||
|
||||
QString FileFunctions::ensure_filename_extension(QString fn,
|
||||
const QString &extension)
|
||||
{
|
||||
if (!fn.isEmpty() && !extension.isEmpty()) {
|
||||
QString extension_with_dot;
|
||||
extension_with_dot.append('.');
|
||||
extension_with_dot.append(extension);
|
||||
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
|
||||
fn.append(extension_with_dot);
|
||||
}
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
QString FileFunctions::get_configuration_location()
|
||||
{
|
||||
if (is_portable()) {
|
||||
return get_application_path();
|
||||
} else {
|
||||
QString s = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
QDir(s).mkpath(".");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileFunctions::is_portable()
|
||||
{
|
||||
return QFileInfo::exists(QDir(get_application_path()).filePath("portable"));
|
||||
}
|
||||
|
||||
QString FileFunctions::get_application_path()
|
||||
{
|
||||
return QCoreApplication::applicationDirPath();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,507 +0,0 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "htmlapp.h"
|
||||
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QFont>
|
||||
#include <QTextBlock>
|
||||
#include <QTextBlockFormat>
|
||||
#include <QTextCharFormat>
|
||||
#include <QTextDocument>
|
||||
#include <QTextFragment>
|
||||
#include <QTextList>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "oakutil/xmlutils.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextBlock>
|
||||
|
||||
#include "oakutil/xmlutils.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QVector<QString> Html::k_block_tags = { QStringLiteral("p"),
|
||||
QStringLiteral("div") };
|
||||
|
||||
inline bool str_equals(const QStringView &a, const QStringView &b)
|
||||
{
|
||||
return !a.compare(b, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
QString Html::doc_to_html(const QTextDocument *doc)
|
||||
{
|
||||
QString html;
|
||||
QXmlStreamWriter writer(&html);
|
||||
|
||||
//writer.setAutoFormatting(true);
|
||||
|
||||
for (auto it = doc->begin(); it != doc->end(); it = it.next()) {
|
||||
write_block(&writer, it);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
struct HtmlNode {
|
||||
QString tag;
|
||||
QTextCharFormat format;
|
||||
};
|
||||
|
||||
QTextCharFormat merge_html_formats(const QVector<HtmlNode> &stack)
|
||||
{
|
||||
QTextCharFormat f;
|
||||
|
||||
for (int i = 0; i < stack.size(); i++) {
|
||||
f.merge(stack.at(i).format);
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void Html::html_to_doc(QTextDocument *doc, const QString &html)
|
||||
{
|
||||
// Empty doc
|
||||
doc->clear();
|
||||
bool inside_block = true;
|
||||
|
||||
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
|
||||
QTextCursor c(doc);
|
||||
|
||||
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
|
||||
QXmlStreamReader reader(wrapped);
|
||||
|
||||
QVector<HtmlNode> fmt_stack;
|
||||
|
||||
QTextCharFormat default_fmt;
|
||||
default_fmt.setFontWeight(QFont::Normal);
|
||||
fmt_stack.append({ QStringLiteral("html"), default_fmt });
|
||||
|
||||
QTextCharFormat current_fmt;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
reader.readNext();
|
||||
|
||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
fmt_stack.append({ tag, read_char_format(reader.attributes()) });
|
||||
current_fmt = merge_html_formats(fmt_stack);
|
||||
|
||||
if (k_block_tags.contains(tag)) {
|
||||
QTextBlockFormat block_fmt =
|
||||
read_block_format(reader.attributes());
|
||||
if (inside_block) {
|
||||
c.setBlockFormat(block_fmt);
|
||||
c.setBlockCharFormat(current_fmt);
|
||||
} else {
|
||||
c.insertBlock(block_fmt, current_fmt);
|
||||
inside_block = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
||||
QString characters = reader.text().toString();
|
||||
c.insertText(characters, current_fmt);
|
||||
|
||||
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
for (int i = fmt_stack.size() - 1; i >= 0; i--) {
|
||||
if (fmt_stack.at(i).tag == tag) {
|
||||
fmt_stack.removeAt(i);
|
||||
current_fmt = merge_html_formats(fmt_stack);
|
||||
|
||||
if (k_block_tags.contains(tag)) {
|
||||
inside_block = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.error()) {
|
||||
qCritical() << "Failed to parse HTML:" << reader.errorString();
|
||||
}
|
||||
}
|
||||
|
||||
void Html::write_block(QXmlStreamWriter *writer, const QTextBlock &block)
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("p"));
|
||||
|
||||
const QTextBlockFormat &fmt = block.blockFormat();
|
||||
|
||||
// Write block alignment
|
||||
if (!(fmt.alignment() & Qt::AlignLeft)) {
|
||||
if (fmt.alignment() & Qt::AlignRight) {
|
||||
writer->writeAttribute(QStringLiteral("align"),
|
||||
QStringLiteral("right"));
|
||||
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
||||
writer->writeAttribute(QStringLiteral("align"),
|
||||
QStringLiteral("center"));
|
||||
} else if (fmt.alignment() & Qt::AlignJustify) {
|
||||
writer->writeAttribute(QStringLiteral("align"),
|
||||
QStringLiteral("justify"));
|
||||
}
|
||||
}
|
||||
|
||||
// RTL support
|
||||
if (block.textDirection() == Qt::RightToLeft) {
|
||||
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
|
||||
}
|
||||
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
|
||||
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
|
||||
write_css_property(&style, QStringLiteral("line-height"),
|
||||
QStringLiteral("%1%").arg(fmt.lineHeight()));
|
||||
}
|
||||
|
||||
write_char_format(&style, block.charFormat());
|
||||
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
|
||||
auto it = block.begin();
|
||||
|
||||
if (it != block.end()) {
|
||||
for (; it != block.end(); it++) {
|
||||
write_fragment(writer, it.fragment());
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // p
|
||||
}
|
||||
|
||||
void Html::write_fragment(QXmlStreamWriter *writer,
|
||||
const QTextFragment &fragment)
|
||||
{
|
||||
const QTextCharFormat &fmt = fragment.charFormat();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("span"));
|
||||
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
|
||||
write_char_format(&style, fmt);
|
||||
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
|
||||
QStringList lines = fragment.text().split(QChar::LineSeparator);
|
||||
bool first_line = true;
|
||||
foreach (const QString &l, lines) {
|
||||
if (first_line) {
|
||||
first_line = false;
|
||||
} else {
|
||||
writer->writeEmptyElement(QStringLiteral("br"));
|
||||
}
|
||||
writer->writeCharacters(l);
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // span
|
||||
}
|
||||
|
||||
void Html::write_css_property(QString *style, const QString &key,
|
||||
const QStringList &values)
|
||||
{
|
||||
QString value;
|
||||
foreach (QString v, values) {
|
||||
if (v.contains(' ')) {
|
||||
v = QStringLiteral("'%1'").arg(v);
|
||||
}
|
||||
|
||||
append_string_auto_space(&value, v);
|
||||
}
|
||||
|
||||
append_string_auto_space(style, QStringLiteral("%1: %2;").arg(key, value));
|
||||
}
|
||||
|
||||
void Html::write_char_format(QString *style, const QTextCharFormat &fmt)
|
||||
{
|
||||
QStringList families = fmt.fontFamilies().toStringList();
|
||||
if (!families.isEmpty()) {
|
||||
write_css_property(style, QStringLiteral("font-family"),
|
||||
families.first());
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
|
||||
write_css_property(
|
||||
style, QStringLiteral("font-size"),
|
||||
QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontWeight)) {
|
||||
write_css_property(style, QStringLiteral("font-weight"),
|
||||
QString::number(fmt.fontWeight() * 8));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontItalic)) {
|
||||
write_css_property(style, QStringLiteral("font-style"),
|
||||
fmt.fontItalic() ? QStringLiteral("italic") :
|
||||
QStringLiteral("normal"));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
|
||||
write_css_property(style, QStringLiteral("-ove-font-style"),
|
||||
fmt.fontStyleName().toString());
|
||||
}
|
||||
|
||||
QStringList deco;
|
||||
|
||||
if (fmt.fontUnderline()) {
|
||||
deco.append(QStringLiteral("underline"));
|
||||
}
|
||||
|
||||
if (fmt.fontStrikeOut()) {
|
||||
deco.append(QStringLiteral("line-through"));
|
||||
}
|
||||
|
||||
if (fmt.fontOverline()) {
|
||||
deco.append(QStringLiteral("overline"));
|
||||
}
|
||||
|
||||
if (!deco.isEmpty()) {
|
||||
write_css_property(style, QStringLiteral("text-decoration"), deco);
|
||||
}
|
||||
|
||||
if (fmt.foreground().style() != Qt::NoBrush) {
|
||||
const QColor &color = fmt.foreground().color();
|
||||
QString cs;
|
||||
|
||||
if (color.alpha() == 255) {
|
||||
cs = color.name();
|
||||
} else if (color.alpha()) {
|
||||
cs = QStringLiteral("rgba(%1, %2, %3, %4)")
|
||||
.arg(QString::number(color.red()),
|
||||
QString::number(color.green()),
|
||||
QString::number(color.blue()),
|
||||
QString::number(color.alphaF()));
|
||||
}
|
||||
|
||||
write_css_property(style, QStringLiteral("color"), cs);
|
||||
}
|
||||
|
||||
if (fmt.fontCapitalization() != QFont::MixedCase) {
|
||||
if (fmt.fontCapitalization() == QFont::SmallCaps) {
|
||||
write_css_property(style, QStringLiteral("font-variant"),
|
||||
QStringLiteral("small-caps"));
|
||||
// TODO: Add others
|
||||
}
|
||||
}
|
||||
|
||||
if (fmt.fontLetterSpacing() != 0.0) {
|
||||
write_css_property(style, QStringLiteral("letter-spacing"),
|
||||
QStringLiteral("%1%").arg(
|
||||
QString::number(fmt.fontLetterSpacing())));
|
||||
}
|
||||
|
||||
if (fmt.fontStretch() != 0) {
|
||||
write_css_property(
|
||||
style, QStringLiteral("font-stretch"),
|
||||
QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
|
||||
}
|
||||
}
|
||||
|
||||
QTextCharFormat Html::read_char_format(const QXmlStreamAttributes &attributes)
|
||||
{
|
||||
QTextCharFormat fmt;
|
||||
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (str_equals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = get_css_from_style(attr.value().toString());
|
||||
|
||||
for (auto it = css.begin(); it != css.end(); it++) {
|
||||
const QString &first_val = it.value().first();
|
||||
|
||||
if (it.key() == QStringLiteral("font-family")) {
|
||||
fmt.setFontFamilies({ first_val });
|
||||
} else if (it.key() == QStringLiteral("font-size")) {
|
||||
if (first_val.endsWith(QStringLiteral("pt"),
|
||||
Qt::CaseInsensitive)) {
|
||||
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-weight")) {
|
||||
fmt.setFontWeight(first_val.toInt() / 8);
|
||||
} else if (it.key() == QStringLiteral("font-style")) {
|
||||
fmt.setFontItalic(
|
||||
str_equals(first_val, QStringLiteral("italic")));
|
||||
} else if (it.key() == QStringLiteral("text-decoration")) {
|
||||
foreach (const QString &v, it.value()) {
|
||||
if (str_equals(v, QStringLiteral("underline"))) {
|
||||
fmt.setFontUnderline(true);
|
||||
} else if (str_equals(v,
|
||||
QStringLiteral("line-through"))) {
|
||||
fmt.setFontStrikeOut(true);
|
||||
} else if (str_equals(v, QStringLiteral("overline"))) {
|
||||
fmt.setFontOverline(true);
|
||||
}
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("color")) {
|
||||
if (first_val.startsWith(QStringLiteral("rgba"),
|
||||
Qt::CaseInsensitive)) {
|
||||
QString vals_only = first_val;
|
||||
vals_only.remove(QStringLiteral("rgba"));
|
||||
vals_only.remove(QStringLiteral("("));
|
||||
vals_only.remove(QStringLiteral(")"));
|
||||
QStringList rgba = vals_only.split(',');
|
||||
if (rgba.size() == 4) {
|
||||
QColor c;
|
||||
c.setRed(rgba.at(0).toInt()); // Writer emits 0-255 RGB (CSS rgba() convention)
|
||||
c.setGreen(rgba.at(1).toInt());
|
||||
c.setBlue(rgba.at(2).toInt());
|
||||
c.setAlphaF(rgba.at(3).toDouble());
|
||||
fmt.setForeground(c);
|
||||
}
|
||||
} else {
|
||||
fmt.setForeground(QColor(first_val));
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-variant")) {
|
||||
if (str_equals(first_val, QStringLiteral("small-caps"))) {
|
||||
fmt.setFontCapitalization(QFont::SmallCaps);
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontLetterSpacing(
|
||||
first_val.chopped(1).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-stretch")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontStretch(first_val.chopped(1).toInt());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("-ove-font-style")) {
|
||||
fmt.setFontStyleName(first_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
QTextBlockFormat Html::read_block_format(const QXmlStreamAttributes &attributes)
|
||||
{
|
||||
QTextBlockFormat block_fmt;
|
||||
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (str_equals(attr.name(), QStringLiteral("align"))) {
|
||||
if (str_equals(attr.value(), QStringLiteral("right"))) {
|
||||
block_fmt.setAlignment(Qt::AlignRight);
|
||||
} else if (str_equals(attr.value(), QStringLiteral("center"))) {
|
||||
block_fmt.setAlignment(Qt::AlignHCenter);
|
||||
} else if (str_equals(attr.value(), QStringLiteral("justify"))) {
|
||||
block_fmt.setAlignment(Qt::AlignJustify);
|
||||
}
|
||||
} else if (str_equals(attr.name(), QStringLiteral("dir"))) {
|
||||
if (str_equals(attr.value(), QStringLiteral("rtl"))) {
|
||||
block_fmt.setLayoutDirection(Qt::RightToLeft);
|
||||
}
|
||||
} else if (str_equals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = get_css_from_style(attr.value().toString());
|
||||
|
||||
for (auto it = css.begin(); it != css.end(); it++) {
|
||||
if (it.key() == QStringLiteral("line-height")) {
|
||||
const QString &first_val = it.value().constFirst();
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
block_fmt.setLineHeight(
|
||||
first_val.chopped(1).toDouble(),
|
||||
QTextBlockFormat::ProportionalHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return block_fmt;
|
||||
}
|
||||
|
||||
void Html::append_string_auto_space(QString *s, const QString &append)
|
||||
{
|
||||
if (!s->isEmpty()) {
|
||||
s->append(QChar(' '));
|
||||
}
|
||||
|
||||
s->append(append);
|
||||
}
|
||||
|
||||
QMap<QString, QStringList> Html::get_css_from_style(const QString &s)
|
||||
{
|
||||
QMap<QString, QStringList> map;
|
||||
|
||||
QStringList list = s.split(QChar(';'));
|
||||
|
||||
foreach (const QString &a, list) {
|
||||
QStringList kv = a.split(QChar(':'));
|
||||
|
||||
if (kv.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
|
||||
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
|
||||
// match. Also commas should be filtered out.
|
||||
QStringList values;
|
||||
const QString &val = kv.at(1);
|
||||
QChar in_quote(0);
|
||||
QString current_str;
|
||||
for (int i = 0; i < val.size(); i++) {
|
||||
const QChar ¤t_char = val.at(i);
|
||||
|
||||
if (!in_quote.isNull()) {
|
||||
// If inside quotes and character isn't quote, indiscriminately append char
|
||||
if (current_char == in_quote) {
|
||||
in_quote = QChar(0);
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
} else if (current_char.isSpace() || current_char == QChar(',')) {
|
||||
// Dump current
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
current_str.clear();
|
||||
}
|
||||
} else if (in_quote.isNull() && (current_char == QChar('\'') ||
|
||||
current_char == QChar('"'))) {
|
||||
in_quote = current_char;
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
}
|
||||
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
}
|
||||
|
||||
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
|
||||
if (values.isEmpty()) {
|
||||
values.append(QString());
|
||||
}
|
||||
|
||||
map[kv.at(0).trimmed().toLower()] = values;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OAK_HTMLAPP_H
|
||||
#define OAK_HTMLAPP_H
|
||||
|
||||
#include <QTextDocument>
|
||||
#include <QTextFragment>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Functions for converting HTML to QTextDocument and vice versa
|
||||
*
|
||||
* Qt does contain its own functions for this, however they have some limitations. Some things that
|
||||
* we want to support (e.g. kerning/spacing and font stretch) are not implemented in Qt's
|
||||
* QTextHtmlExporter and QTextHtmlParser. Additionally, since these functions are not part of Qt's
|
||||
* public API, and make many references to other parts of Qt that are not part of the public API,
|
||||
* there is no way to subclass or extend their functionality without forking Qt as a whole.
|
||||
*
|
||||
* Therefore, it became necessary to write a custom class for the conversion so that we can
|
||||
* ensure support for the features we need.
|
||||
*
|
||||
* If someone wishes to extend this class for more feature support, feel free to open a pull
|
||||
* request. But this is NOT intended to be an exhaustive HTML implementation, and is primarily
|
||||
* designed to store rich text in a standard format for the purpose of text formatting for video.
|
||||
*/
|
||||
class Html {
|
||||
public:
|
||||
static QString doc_to_html(const QTextDocument *doc);
|
||||
|
||||
static void html_to_doc(QTextDocument *doc, const QString &html);
|
||||
|
||||
private:
|
||||
static void write_block(QXmlStreamWriter *writer, const QTextBlock &block);
|
||||
|
||||
static void write_fragment(QXmlStreamWriter *writer,
|
||||
const QTextFragment &fragment);
|
||||
|
||||
static void write_css_property(QString *style, const QString &key,
|
||||
const QStringList &value);
|
||||
static void write_css_property(QString *style, const QString &key,
|
||||
const QString &value)
|
||||
{
|
||||
write_css_property(style, key, QStringList({ value }));
|
||||
}
|
||||
|
||||
static void write_char_format(QString *style, const QTextCharFormat &fmt);
|
||||
|
||||
static QTextCharFormat
|
||||
read_char_format(const QXmlStreamAttributes &attributes);
|
||||
|
||||
static QTextBlockFormat
|
||||
read_block_format(const QXmlStreamAttributes &attributes);
|
||||
|
||||
static void append_string_auto_space(QString *s, const QString &append);
|
||||
|
||||
static QMap<QString, QStringList> get_css_from_style(const QString &s);
|
||||
|
||||
static const QVector<QString> k_block_tags;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_HTML_H
|
||||
@@ -1,59 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_KEYFRAMETYPES_H
|
||||
#define OAK_KEYFRAMETYPES_H
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief App-side keyframe enum mirrors.
|
||||
*
|
||||
* The engine olive::NodeKeyframe::Type mirror lives in
|
||||
* app/common/nodevaluehandle.h as NodeKeyframeType (kept with the
|
||||
* NodeValueType mirror); the enums below cover the remaining keyframe
|
||||
* domains. Enumerator ordinals must stay in sync with the engine / facade:
|
||||
* the C ABI transports these as ints.
|
||||
*/
|
||||
class KeyframeTypes {
|
||||
public:
|
||||
/// Mirror of engine's olive::NodeKeyframe::BezierType
|
||||
/// (engine/node/keyframe.h; oakengine_keyframe_opposing_bezier_type()
|
||||
/// transports these ordinals).
|
||||
enum BezierType { k_in_handle, k_out_handle };
|
||||
|
||||
/**
|
||||
* @brief Facade easing order used by the C ABI
|
||||
* (oakengine_keyframe_get_type(), oak::Keyframe::type()): NOT the same
|
||||
* order as the engine Type enum (see NodeKeyframeType in
|
||||
* common/nodevaluehandle.h).
|
||||
*/
|
||||
enum FacadeType {
|
||||
k_facade_invalid = -1,
|
||||
k_facade_linear = 0,
|
||||
k_facade_bezier = 1,
|
||||
k_facade_hold = 2
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_KEYFRAMETYPES_H
|
||||
@@ -1,46 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_NODEDATATYPES_H
|
||||
#define OAK_NODEDATATYPES_H
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief App-side mirror of engine's olive::Node::DataType
|
||||
* (engine/node/node.h).
|
||||
*
|
||||
* Enumerator ordinals must stay in sync with the engine enum: the C ABI
|
||||
* oakengine_node_get_data() (and the oak::Node::data() wrapper) takes the
|
||||
* `role` argument as a plain int carrying these ordinals.
|
||||
*/
|
||||
enum NodeDataType {
|
||||
k_node_data_icon,
|
||||
k_node_data_duration,
|
||||
k_node_data_created_time,
|
||||
k_node_data_modified_time,
|
||||
k_node_data_frequency_rate,
|
||||
k_node_data_tooltip
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_NODEDATATYPES_H
|
||||
@@ -1,141 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_NODEVALUEHANDLE_H
|
||||
#define OAK_NODEVALUEHANDLE_H
|
||||
|
||||
#include "oakengine/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief App-side mirror of engine NodeValue::Type (engine/node/value.h).
|
||||
*
|
||||
* Ordinals MUST stay in sync with the engine enum: call sites that still
|
||||
* hold an engine NodeValue::Type reach the helpers below through an
|
||||
* implicit int conversion. The static_asserts pin the engine ordinals as
|
||||
* of the R8 wave 1 migration; update both sides together.
|
||||
*
|
||||
* NOTE: the C ABI oak_node_value_type (OAK_NODE_VALUE_*) does NOT share
|
||||
* these ordinals (e.g. k_boolean=4 vs OAK_NODE_VALUE_BOOL=3), so a plain
|
||||
* int cast between the two domains is a bug. Convert with
|
||||
* node_value_type_to_c().
|
||||
*/
|
||||
class NodeValueType
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
k_none = 0,
|
||||
k_int,
|
||||
k_float,
|
||||
k_rational,
|
||||
k_boolean,
|
||||
k_color,
|
||||
k_matrix,
|
||||
k_text,
|
||||
k_font,
|
||||
k_file,
|
||||
k_texture,
|
||||
k_samples,
|
||||
k_vec2,
|
||||
k_vec3,
|
||||
k_vec4,
|
||||
k_bezier,
|
||||
k_combo,
|
||||
k_str_combo,
|
||||
k_video_params,
|
||||
k_audio_params,
|
||||
k_subtitle_params,
|
||||
k_binary,
|
||||
k_push_button,
|
||||
k_data_type_count
|
||||
};
|
||||
};
|
||||
|
||||
// Ordinal sync guards against engine/node/value.h.
|
||||
static_assert(NodeValueType::k_boolean == 4,
|
||||
"NodeValueType out of sync with engine NodeValue::Type");
|
||||
static_assert(NodeValueType::k_vec2 == 12,
|
||||
"NodeValueType out of sync with engine NodeValue::Type");
|
||||
static_assert(NodeValueType::k_data_type_count == 23,
|
||||
"NodeValueType out of sync with engine NodeValue::Type");
|
||||
|
||||
/**
|
||||
* @brief App-side mirror of engine NodeKeyframe::Type
|
||||
* (engine/node/keyframe.h).
|
||||
*
|
||||
* Ordinals MUST stay in sync with the engine enum (k_invalid=-1,
|
||||
* k_linear=0, k_hold=1, k_bezier=2). The facade easing type transported
|
||||
* over the C ABI is a DIFFERENT numbering: 0=linear, 1=bezier, 2=hold
|
||||
* (see oakengine/node.h) — convert with NodeKeyframeTypeToFacade() in
|
||||
* oakvaluehelper.h, never with a plain cast.
|
||||
*/
|
||||
class NodeKeyframeType
|
||||
{
|
||||
public:
|
||||
enum Type { k_invalid = -1, k_linear = 0, k_hold = 1, k_bezier = 2 };
|
||||
};
|
||||
|
||||
// Ordinal sync guard against engine/node/keyframe.h.
|
||||
static_assert(NodeKeyframeType::k_bezier == 2,
|
||||
"NodeKeyframeType out of sync with engine NodeKeyframe::Type");
|
||||
|
||||
/**
|
||||
* @brief Convert engine NodeValue::Type ordinals to oak_node_value_type (app-side).
|
||||
*
|
||||
* `t` uses engine NodeValue::Type ordinals (see the NodeValueType mirror
|
||||
* above); callers holding an engine NodeValue::Type pass it through an
|
||||
* implicit int conversion. The two enums do NOT share ordinals (e.g.
|
||||
* k_boolean=4 vs BOOL=3), so a plain int cast is a bug. Mirrors
|
||||
* from_c_type() in engine/src/capi/node.cpp. Lives in an app header, NOT
|
||||
* in the public facade headers — the C ABI surface stays pure C (see
|
||||
* docs/zh/r6-cleanup-plan.md red line 3 context). Returns -1 for types the
|
||||
* facade cannot represent (caller falls back to the input's declared type).
|
||||
*/
|
||||
inline int node_value_type_to_c(int t)
|
||||
{
|
||||
switch (t) {
|
||||
case NodeValueType::k_int: return OAK_NODE_VALUE_INT;
|
||||
case NodeValueType::k_float: return OAK_NODE_VALUE_FLOAT;
|
||||
case NodeValueType::k_boolean: return OAK_NODE_VALUE_BOOL;
|
||||
case NodeValueType::k_rational: return OAK_NODE_VALUE_RATIONAL;
|
||||
case NodeValueType::k_color: return OAK_NODE_VALUE_COLOR;
|
||||
case NodeValueType::k_vec2: return OAK_NODE_VALUE_VEC2;
|
||||
case NodeValueType::k_vec3: return OAK_NODE_VALUE_VEC3;
|
||||
case NodeValueType::k_vec4: return OAK_NODE_VALUE_VEC4;
|
||||
case NodeValueType::k_combo: return OAK_NODE_VALUE_COMBO;
|
||||
case NodeValueType::k_file: return OAK_NODE_VALUE_STRING;
|
||||
case NodeValueType::k_text: return OAK_NODE_VALUE_TEXT;
|
||||
case NodeValueType::k_font: return OAK_NODE_VALUE_FONT;
|
||||
case NodeValueType::k_str_combo: return OAK_NODE_VALUE_STR_COMBO;
|
||||
case NodeValueType::k_binary: return OAK_NODE_VALUE_BINARY;
|
||||
case NodeValueType::k_bezier: return OAK_NODE_VALUE_BEZIER;
|
||||
case NodeValueType::k_texture: return OAK_NODE_VALUE_TEXTURE;
|
||||
case NodeValueType::k_samples: return OAK_NODE_VALUE_SAMPLES;
|
||||
case NodeValueType::k_video_params: return OAK_NODE_VALUE_VIDEO_PARAMS;
|
||||
case NodeValueType::k_audio_params: return OAK_NODE_VALUE_AUDIO_PARAMS;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // OAK_NODEVALUEHANDLE_H
|
||||
@@ -1,233 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAKVALUEHELPER_H
|
||||
#define OAKVALUEHELPER_H
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <QVariant>
|
||||
#include <QVector2D>
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
|
||||
#include "nodevaluehandle.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "olive/core/util/color.h"
|
||||
#include "olive/core/util/rational.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief Convert a per-track component QVariant into the C ABI oak_node_value POD.
|
||||
*
|
||||
* `type` uses engine NodeValue::Type ordinals (see the NodeValueType mirror
|
||||
* in nodevaluehandle.h; an engine NodeValue::Type converts implicitly) and
|
||||
* is the declared input data type (e.g. k_float/k_color). For split-track
|
||||
* types the component is the track-0 scalar (float for k_color's red
|
||||
* channel, etc.). Returns false for types that have no POD representation.
|
||||
*/
|
||||
static inline bool QVariantToOakNodeValue(int type, const QVariant &v,
|
||||
oak_node_value *out)
|
||||
{
|
||||
memset(out, 0, sizeof(*out));
|
||||
switch (type) {
|
||||
case NodeValueType::k_int:
|
||||
case NodeValueType::k_combo:
|
||||
out->type = (type == NodeValueType::k_combo) ? OAK_NODE_VALUE_COMBO
|
||||
: OAK_NODE_VALUE_INT;
|
||||
out->num = v.toLongLong();
|
||||
return true;
|
||||
case NodeValueType::k_float:
|
||||
out->type = OAK_NODE_VALUE_FLOAT;
|
||||
out->f[0] = v.toDouble();
|
||||
return true;
|
||||
case NodeValueType::k_boolean:
|
||||
out->type = OAK_NODE_VALUE_BOOL;
|
||||
out->num = v.toBool() ? 1 : 0;
|
||||
return true;
|
||||
case NodeValueType::k_rational:
|
||||
out->type = OAK_NODE_VALUE_RATIONAL;
|
||||
{
|
||||
const core::Rational r = v.value<core::Rational>();
|
||||
out->num = r.numerator();
|
||||
out->den = r.denominator();
|
||||
}
|
||||
return true;
|
||||
case NodeValueType::k_color:
|
||||
out->type = OAK_NODE_VALUE_COLOR;
|
||||
{
|
||||
const core::Color c = v.value<core::Color>();
|
||||
out->f[0] = c.red();
|
||||
out->f[1] = c.green();
|
||||
out->f[2] = c.blue();
|
||||
out->f[3] = c.alpha();
|
||||
}
|
||||
return true;
|
||||
case NodeValueType::k_vec2:
|
||||
out->type = OAK_NODE_VALUE_VEC2;
|
||||
{
|
||||
const QVector2D vec = v.value<QVector2D>();
|
||||
out->f[0] = vec.x();
|
||||
out->f[1] = vec.y();
|
||||
}
|
||||
return true;
|
||||
case NodeValueType::k_vec3:
|
||||
out->type = OAK_NODE_VALUE_VEC3;
|
||||
{
|
||||
const QVector3D vec = v.value<QVector3D>();
|
||||
out->f[0] = vec.x();
|
||||
out->f[1] = vec.y();
|
||||
out->f[2] = vec.z();
|
||||
}
|
||||
return true;
|
||||
case NodeValueType::k_vec4:
|
||||
out->type = OAK_NODE_VALUE_VEC4;
|
||||
{
|
||||
const QVector4D vec = v.value<QVector4D>();
|
||||
out->f[0] = vec.x();
|
||||
out->f[1] = vec.y();
|
||||
out->f[2] = vec.z();
|
||||
out->f[3] = vec.w();
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a per-track component QVariant into the C ABI oak_node_value POD.
|
||||
*
|
||||
* Unlike QVariantToOakNodeValue() which takes a full normal value, this takes a
|
||||
* single track's component (e.g. one float for a k_color channel). The resulting
|
||||
* POD has the input's declared type with the component in f[0]/num, exactly what
|
||||
* the per-track facade commands expect.
|
||||
*
|
||||
* `type` uses engine NodeValue::Type ordinals (NodeValueType mirror).
|
||||
*/
|
||||
static inline bool NodeTrackComponentToOakNodeValue(int type,
|
||||
const QVariant &v,
|
||||
oak_node_value *out)
|
||||
{
|
||||
memset(out, 0, sizeof(*out));
|
||||
switch (type) {
|
||||
case NodeValueType::k_int:
|
||||
case NodeValueType::k_combo:
|
||||
out->type = (type == NodeValueType::k_combo) ? OAK_NODE_VALUE_COMBO
|
||||
: OAK_NODE_VALUE_INT;
|
||||
out->num = v.toLongLong();
|
||||
return true;
|
||||
case NodeValueType::k_float:
|
||||
case NodeValueType::k_bezier:
|
||||
out->type = OAK_NODE_VALUE_FLOAT;
|
||||
out->f[0] = v.toDouble();
|
||||
return true;
|
||||
case NodeValueType::k_boolean:
|
||||
out->type = OAK_NODE_VALUE_BOOL;
|
||||
out->num = v.toBool() ? 1 : 0;
|
||||
return true;
|
||||
case NodeValueType::k_rational:
|
||||
out->type = OAK_NODE_VALUE_RATIONAL;
|
||||
{
|
||||
const core::Rational r = v.value<core::Rational>();
|
||||
out->num = r.numerator();
|
||||
out->den = r.denominator();
|
||||
}
|
||||
return true;
|
||||
case NodeValueType::k_color:
|
||||
out->type = OAK_NODE_VALUE_COLOR;
|
||||
out->f[0] = v.toFloat();
|
||||
return true;
|
||||
case NodeValueType::k_vec2:
|
||||
out->type = OAK_NODE_VALUE_VEC2;
|
||||
out->f[0] = v.toFloat();
|
||||
return true;
|
||||
case NodeValueType::k_vec3:
|
||||
out->type = OAK_NODE_VALUE_VEC3;
|
||||
out->f[0] = v.toFloat();
|
||||
return true;
|
||||
case NodeValueType::k_vec4:
|
||||
out->type = OAK_NODE_VALUE_VEC4;
|
||||
out->f[0] = v.toFloat();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a full C ABI oak_node_value POD back into a QVariant.
|
||||
*
|
||||
* Mirrors QVariantToOakNodeValue(). String/binary/bezier are not represented
|
||||
* in the POD and return an invalid QVariant; use the dedicated string/binary/
|
||||
* bezier facade getters for those.
|
||||
*/
|
||||
static inline QVariant OakNodeValueToQVariant(const oak_node_value &v)
|
||||
{
|
||||
switch (v.type) {
|
||||
case OAK_NODE_VALUE_INT:
|
||||
return QVariant::fromValue<qlonglong>(v.num);
|
||||
case OAK_NODE_VALUE_FLOAT:
|
||||
return QVariant::fromValue(v.f[0]);
|
||||
case OAK_NODE_VALUE_BOOL:
|
||||
return QVariant::fromValue(v.num != 0);
|
||||
case OAK_NODE_VALUE_RATIONAL:
|
||||
return QVariant::fromValue(
|
||||
core::Rational(int(v.num), int(v.den)));
|
||||
case OAK_NODE_VALUE_COLOR:
|
||||
return QVariant::fromValue(core::Color(
|
||||
float(v.f[0]), float(v.f[1]), float(v.f[2]), float(v.f[3])));
|
||||
case OAK_NODE_VALUE_VEC2:
|
||||
return QVariant::fromValue(
|
||||
QVector2D(float(v.f[0]), float(v.f[1])));
|
||||
case OAK_NODE_VALUE_VEC3:
|
||||
return QVariant::fromValue(
|
||||
QVector3D(float(v.f[0]), float(v.f[1]), float(v.f[2])));
|
||||
case OAK_NODE_VALUE_VEC4:
|
||||
return QVariant::fromValue(
|
||||
QVector4D(float(v.f[0]), float(v.f[1]), float(v.f[2]), float(v.f[3])));
|
||||
case OAK_NODE_VALUE_COMBO:
|
||||
return QVariant::fromValue<int>(int(v.num));
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Map engine NodeKeyframe::Type ordinals (NodeKeyframeType mirror)
|
||||
* to the facade easing type (0=linear, 1=bezier, 2=hold).
|
||||
*/
|
||||
static inline int NodeKeyframeTypeToFacade(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case NodeKeyframeType::k_bezier:
|
||||
return 1;
|
||||
case NodeKeyframeType::k_hold:
|
||||
return 2;
|
||||
case NodeKeyframeType::k_linear:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // OAKVALUEHELPER_H
|
||||
@@ -1,46 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_PROJECTTYPES_H
|
||||
#define OAK_PROJECTTYPES_H
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief App-side mirror of the engine's olive::Project enum(s)
|
||||
* (engine/node/project.h).
|
||||
*
|
||||
* Enumerator ordinals must stay in sync with the engine enum: the C ABI
|
||||
* (oakengine_project_get_cache_location_setting() etc.) transports these
|
||||
* values as plain ints.
|
||||
*/
|
||||
class Project {
|
||||
public:
|
||||
enum CacheSetting {
|
||||
k_cache_use_default_location,
|
||||
k_cache_store_alongside_project,
|
||||
k_cache_custom_path
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_PROJECTTYPES_H
|
||||
@@ -1,144 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakutil/qtutils.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
int QtUtils::q_font_metrics_width(QFontMetrics fm, const QString &s)
|
||||
{
|
||||
return fm.horizontalAdvance(s);
|
||||
}
|
||||
|
||||
QFrame *QtUtils::create_horizontal_line()
|
||||
{
|
||||
QFrame *horizontal_line = new QFrame();
|
||||
horizontal_line->setFrameShape(QFrame::HLine);
|
||||
horizontal_line->setFrameShadow(QFrame::Sunken);
|
||||
return horizontal_line;
|
||||
}
|
||||
|
||||
QFrame *QtUtils::create_vertical_line()
|
||||
{
|
||||
QFrame *l = create_horizontal_line();
|
||||
l->setFrameShape(QFrame::VLine);
|
||||
return l;
|
||||
}
|
||||
|
||||
QString QtUtils::get_formatted_date_time(const QDateTime &dt)
|
||||
{
|
||||
// ISO-style date/time (e.g. "2026-06-03 20:25") per the UI design reference
|
||||
return dt.toString(QStringLiteral("yyyy-MM-dd HH:mm"));
|
||||
}
|
||||
|
||||
QStringList QtUtils::word_wrap_string(const QString &s, const QFontMetrics &fm,
|
||||
int bounding_width)
|
||||
{
|
||||
QStringList list;
|
||||
QStringList lines = s.split('\n');
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
QString this_line = lines.at(i);
|
||||
while (this_line.size() > 1 &&
|
||||
q_font_metrics_width(fm, this_line) >= bounding_width) {
|
||||
int old_size = this_line.size();
|
||||
int hard_break = -1;
|
||||
for (int j = this_line.size() - 1; j >= 0; j--) {
|
||||
const QChar &char_test = this_line.at(j);
|
||||
if (char_test.isSpace() || char_test == '-') {
|
||||
if (q_font_metrics_width(fm, this_line.left(j)) <
|
||||
bounding_width) {
|
||||
if (!char_test.isSpace()) j++;
|
||||
list.append(this_line.left(j));
|
||||
while (j < this_line.size() &&
|
||||
this_line.at(j).isSpace()) j++;
|
||||
this_line.remove(0, j);
|
||||
break;
|
||||
}
|
||||
} else if (hard_break == -1 &&
|
||||
q_font_metrics_width(fm, this_line.left(j)) <
|
||||
bounding_width) {
|
||||
hard_break = j;
|
||||
}
|
||||
}
|
||||
if (old_size == this_line.size()) {
|
||||
if (hard_break != -1) {
|
||||
list.append(this_line.left(hard_break));
|
||||
this_line.remove(0, hard_break);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this_line.isEmpty()) {
|
||||
list.append(this_line);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers
|
||||
QtUtils::flip_control_and_shift_modifiers(Qt::KeyboardModifiers e)
|
||||
{
|
||||
if (e & Qt::ControlModifier & Qt::ShiftModifier) return e;
|
||||
if (e & Qt::ShiftModifier) {
|
||||
e |= Qt::ControlModifier;
|
||||
e &= ~Qt::ShiftModifier;
|
||||
} else if (e & Qt::ControlModifier) {
|
||||
e |= Qt::ShiftModifier;
|
||||
e &= ~Qt::ControlModifier;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
void QtUtils::set_combo_box_data(QComboBox *cb, int data)
|
||||
{
|
||||
for (int i = 0; i < cb->count(); i++) {
|
||||
if (cb->itemData(i).toInt() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtUtils::set_combo_box_data(QComboBox *cb, const QString &data)
|
||||
{
|
||||
for (int i = 0; i < cb->count(); i++) {
|
||||
if (cb->itemData(i).toString() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QColor QtUtils::to_q_color(const core::Color &i)
|
||||
{
|
||||
QColor c;
|
||||
|
||||
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
|
||||
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
|
||||
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
|
||||
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
|
||||
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_SERIALIZEDLAYOUTINFOAPP_H
|
||||
#define OAK_SERIALIZEDLAYOUTINFOAPP_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
#include "oakengine/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief App-side mirror of the engine's olive::SerializedLayoutInfo
|
||||
* (engine/node/project/serializer/serializedlayoutinfo.h).
|
||||
*
|
||||
* SYNC OBLIGATION: the data member layout (types AND order) must stay
|
||||
* identical to the engine type. Instances of this struct cross the engine
|
||||
* boundary as `void *` (Core::save_project_internal() passes one to
|
||||
* oakengine_task_create_project_save(), and the engine hands one back
|
||||
* through the load_layout callback); the engine side reinterprets the
|
||||
* pointer as its own olive::SerializedLayoutInfo and copies the members,
|
||||
* so any layout divergence is silent memory corruption.
|
||||
*
|
||||
* The engine's std::vector<Folder*> / std::vector<Sequence*> /
|
||||
* std::vector<ViewerOutput*> members are mirrored as
|
||||
* std::vector<OakEngineNode*> (same pointer size and semantics: borrowed
|
||||
* node handles). panel_data mirrors
|
||||
* std::map<QString, PanelLayoutInfo> where PanelLayoutInfo is
|
||||
* std::map<QString, QString> (identical to PanelWidget::Info).
|
||||
*
|
||||
* Only the data members are mirrored; the engine type's XML
|
||||
* (de)serialization methods (to_xml/from_xml) stay engine-side.
|
||||
*/
|
||||
class SerializedLayoutInfo {
|
||||
public:
|
||||
SerializedLayoutInfo() = default;
|
||||
|
||||
QByteArray state;
|
||||
|
||||
std::vector<OakEngineNode *> open_folders;
|
||||
|
||||
std::vector<OakEngineNode *> open_sequences;
|
||||
|
||||
std::vector<OakEngineNode *> open_viewers;
|
||||
|
||||
std::map<QString, std::map<QString, QString>> panel_data;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::SerializedLayoutInfo)
|
||||
|
||||
#endif // OAK_SERIALIZEDLAYOUTINFOAPP_H
|
||||
@@ -1,87 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_SUBTITLEAPP_H
|
||||
#define OAK_SUBTITLEAPP_H
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#include <olive/core/util/timerange.h>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using olive::core::TimeRange;
|
||||
|
||||
/**
|
||||
* @brief App-side mirror of the engine Subtitle value class
|
||||
* (engine/render/subtitleparams.h).
|
||||
*
|
||||
* Pure value type (time range + text), identical semantics to the engine
|
||||
* version. It is named SubtitleApp (not Subtitle) because the engine class
|
||||
* still reaches some app translation units transitively (e.g. via
|
||||
* engine/node/output/viewer/viewer.h) and an identical name would be an
|
||||
* ODR redefinition there.
|
||||
*
|
||||
* The member layout MUST stay in sync with the engine class:
|
||||
* oakengine_viewer_get_subtitle_at() returns pointers to engine Subtitle
|
||||
* objects which the app reads through this mirror. Update both sides
|
||||
* together.
|
||||
*/
|
||||
class SubtitleApp {
|
||||
public:
|
||||
SubtitleApp() = default;
|
||||
|
||||
SubtitleApp(const TimeRange &time, const QString &text)
|
||||
: range_(time)
|
||||
, text_(text)
|
||||
{
|
||||
}
|
||||
|
||||
const TimeRange &time() const
|
||||
{
|
||||
return range_;
|
||||
}
|
||||
void set_time(const TimeRange &t)
|
||||
{
|
||||
range_ = t;
|
||||
}
|
||||
|
||||
const QString &text() const
|
||||
{
|
||||
return text_;
|
||||
}
|
||||
void set_text(const QString &t)
|
||||
{
|
||||
text_ = t;
|
||||
}
|
||||
|
||||
private:
|
||||
TimeRange range_;
|
||||
|
||||
QString text_;
|
||||
};
|
||||
|
||||
} // namespace olive
|
||||
|
||||
Q_DECLARE_METATYPE(olive::SubtitleApp)
|
||||
|
||||
#endif // OAK_SUBTITLEAPP_H
|
||||
@@ -1,168 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_TOOLTYPES_H
|
||||
#define OAK_TOOLTYPES_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QString>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief App-side mirror of engine's olive::Tool (engine/tool/tool.h).
|
||||
*
|
||||
* Pure enum + static string mapping, identical to the engine version.
|
||||
* Enumerator ordinals must stay in sync with the engine enum: the C ABI
|
||||
* (oakengine_app_tool() etc.) transports these as ints.
|
||||
*/
|
||||
class Tool {
|
||||
public:
|
||||
/**
|
||||
* @brief A list of tools that can be used throughout the application
|
||||
*/
|
||||
enum Item {
|
||||
/// No tool. This should never be set as the application tool, its only real purpose is to indicate the lack of
|
||||
/// a tool somewhere similar to nullptr.
|
||||
k_none,
|
||||
|
||||
/// Pointer tool
|
||||
k_pointer,
|
||||
|
||||
/// Edit tool
|
||||
k_edit,
|
||||
|
||||
/// Ripple tool
|
||||
k_ripple,
|
||||
|
||||
/// Rolling tool
|
||||
k_rolling,
|
||||
|
||||
/// Razor tool
|
||||
k_razor,
|
||||
|
||||
/// Slip tool
|
||||
k_slip,
|
||||
|
||||
/// Slide tool
|
||||
k_slide,
|
||||
|
||||
/// Hand tool
|
||||
k_hand,
|
||||
|
||||
/// Zoom tool
|
||||
k_zoom,
|
||||
|
||||
/// Transition tool
|
||||
k_transition,
|
||||
|
||||
/// Record tool
|
||||
k_record,
|
||||
|
||||
/// Add tool
|
||||
k_add,
|
||||
|
||||
/// Track select tool
|
||||
k_track_select,
|
||||
|
||||
k_count
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Tools that can be added using the kAdd tool
|
||||
*/
|
||||
enum AddableObject {
|
||||
/// An empty clip
|
||||
k_addable_empty,
|
||||
|
||||
/// A video clip showing a generic video placeholder
|
||||
k_addable_bars,
|
||||
|
||||
/// A video clip showing a primitive shape
|
||||
k_addable_shape,
|
||||
|
||||
/// A video clip with a solid connected
|
||||
k_addable_solid,
|
||||
|
||||
/// A video clip with a title connected
|
||||
k_addable_title,
|
||||
|
||||
/// An audio clip with a sine connected to it
|
||||
k_addable_tone,
|
||||
|
||||
/// A subtitle clip
|
||||
k_addable_subtitle,
|
||||
|
||||
k_addable_count
|
||||
};
|
||||
|
||||
static QString get_addable_object_name(const AddableObject &a)
|
||||
{
|
||||
switch (a) {
|
||||
case k_addable_empty:
|
||||
return QCoreApplication::translate("Tool", "Empty");
|
||||
case k_addable_bars:
|
||||
return QCoreApplication::translate("Tool", "Bars");
|
||||
case k_addable_shape:
|
||||
return QCoreApplication::translate("Tool", "Shape");
|
||||
case k_addable_solid:
|
||||
return QCoreApplication::translate("Tool", "Solid");
|
||||
case k_addable_title:
|
||||
return QCoreApplication::translate("Tool", "Title");
|
||||
case k_addable_tone:
|
||||
return QCoreApplication::translate("Tool", "Tone");
|
||||
case k_addable_subtitle:
|
||||
return QCoreApplication::translate("Tool", "Subtitle");
|
||||
case k_addable_count:
|
||||
break;
|
||||
}
|
||||
|
||||
return QCoreApplication::translate("Tool", "Unknown");
|
||||
}
|
||||
|
||||
static QString get_addable_object_id(const AddableObject &a)
|
||||
{
|
||||
switch (a) {
|
||||
case k_addable_empty:
|
||||
return QStringLiteral("empty");
|
||||
case k_addable_bars:
|
||||
return QStringLiteral("bars");
|
||||
case k_addable_shape:
|
||||
return QStringLiteral("shape");
|
||||
case k_addable_solid:
|
||||
return QStringLiteral("solid");
|
||||
case k_addable_title:
|
||||
return QStringLiteral("title");
|
||||
case k_addable_tone:
|
||||
return QStringLiteral("tone");
|
||||
case k_addable_subtitle:
|
||||
return QStringLiteral("subtitle");
|
||||
case k_addable_count:
|
||||
break;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_TOOLTYPES_H
|
||||
@@ -1,220 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_TRACKREFERENCEHANDLE_H
|
||||
#define OAK_TRACKREFERENCEHANDLE_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDataStream>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
#include "oakengine/timeline.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief App-side mirror of the engine Track::Reference value class
|
||||
* (engine/node/output/track/track.h).
|
||||
*
|
||||
* Pure value type (track type + index) used throughout timeline UI code.
|
||||
* Semantics are identical to the engine version. The nested Type enum
|
||||
* mirrors engine Track::Type; ordinals MUST stay in sync with the engine
|
||||
* enum — the C ABI transports track types as ints
|
||||
* (OAKENGINE_TRACK_TYPE_* in oakengine/timeline.h), pinned by the
|
||||
* static_asserts below. Update both sides together.
|
||||
*/
|
||||
class TrackReference
|
||||
{
|
||||
public:
|
||||
enum Type { k_none = -1, k_video, k_audio, k_subtitle, k_count };
|
||||
|
||||
TrackReference()
|
||||
: type_(k_none)
|
||||
, index_(-1)
|
||||
{
|
||||
}
|
||||
|
||||
TrackReference(const Type &type, const int &index)
|
||||
: type_(type)
|
||||
, index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
const Type &type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
const int &index() const
|
||||
{
|
||||
return index_;
|
||||
}
|
||||
|
||||
bool operator==(const TrackReference &ref) const
|
||||
{
|
||||
return type_ == ref.type_ && index_ == ref.index_;
|
||||
}
|
||||
|
||||
bool operator!=(const TrackReference &ref) const
|
||||
{
|
||||
return !(*this == ref);
|
||||
}
|
||||
|
||||
bool operator<(const TrackReference &rhs) const
|
||||
{
|
||||
if (type_ != rhs.type_) {
|
||||
return type_ < rhs.type_;
|
||||
}
|
||||
|
||||
return index_ < rhs.index_;
|
||||
}
|
||||
|
||||
QString to_string() const
|
||||
{
|
||||
QString type_string = type_to_string(type_);
|
||||
if (type_string.isEmpty()) {
|
||||
return QString();
|
||||
} else {
|
||||
return QStringLiteral("%1:%2").arg(type_string,
|
||||
QString::number(index_));
|
||||
}
|
||||
}
|
||||
|
||||
/// For IDs that shouldn't change between localizations
|
||||
static QString type_to_string(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case k_video:
|
||||
return QStringLiteral("v");
|
||||
case k_audio:
|
||||
return QStringLiteral("a");
|
||||
case k_subtitle:
|
||||
return QStringLiteral("s");
|
||||
case k_count:
|
||||
case k_none:
|
||||
break;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
/// For human-facing strings (translation context "Track" kept
|
||||
/// identical to the engine version)
|
||||
static QString type_to_translated_string(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case k_video:
|
||||
return QCoreApplication::translate("Track", "V");
|
||||
case k_audio:
|
||||
return QCoreApplication::translate("Track", "A");
|
||||
case k_subtitle:
|
||||
return QCoreApplication::translate("Track", "S");
|
||||
case k_count:
|
||||
case k_none:
|
||||
break;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
static Type type_from_string(const QString &s)
|
||||
{
|
||||
if (s.size() >= 3) {
|
||||
if (s.at(1) == ':') {
|
||||
if (s.at(0) == 'v') {
|
||||
// Video stream
|
||||
return k_video;
|
||||
} else if (s.at(0) == 'a') {
|
||||
// Audio stream
|
||||
return k_audio;
|
||||
} else if (s.at(0) == 's') {
|
||||
// Subtitle stream
|
||||
return k_subtitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return k_none;
|
||||
}
|
||||
|
||||
static TrackReference from_string(const QString &s)
|
||||
{
|
||||
TrackReference ref;
|
||||
Type parse_type = type_from_string(s);
|
||||
|
||||
if (parse_type != k_none) {
|
||||
bool ok;
|
||||
int parse_index = s.mid(2).toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
ref.type_ = parse_type;
|
||||
ref.index_ = parse_index;
|
||||
}
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return type_ > k_none && type_ < k_count && index_ >= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
|
||||
int index_;
|
||||
};
|
||||
|
||||
// Ordinal sync guards: C ABI OAKENGINE_TRACK_TYPE_* (oakengine/timeline.h)
|
||||
// carry the same values as engine Track::Type, and this mirror matches both.
|
||||
static_assert(TrackReference::k_video == OAKENGINE_TRACK_TYPE_VIDEO,
|
||||
"TrackReference::Type out of sync with C ABI track types");
|
||||
static_assert(TrackReference::k_audio == OAKENGINE_TRACK_TYPE_AUDIO,
|
||||
"TrackReference::Type out of sync with C ABI track types");
|
||||
static_assert(TrackReference::k_subtitle == OAKENGINE_TRACK_TYPE_SUBTITLE,
|
||||
"TrackReference::Type out of sync with C ABI track types");
|
||||
|
||||
inline uint qHash(const TrackReference &r, uint seed = 0)
|
||||
{
|
||||
return ::qHash(QStringLiteral("%1:%2").arg(QString::number(r.type()),
|
||||
QString::number(r.index())),
|
||||
seed);
|
||||
}
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &out, const TrackReference &ref)
|
||||
{
|
||||
out << static_cast<int>(ref.type()) << ref.index();
|
||||
return out;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &in, TrackReference &ref)
|
||||
{
|
||||
int type, index;
|
||||
in >> type >> index;
|
||||
ref = TrackReference(static_cast<TrackReference::Type>(type), index);
|
||||
return in;
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // OAK_TRACKREFERENCEHANDLE_H
|
||||
@@ -1,60 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_UNDOWRAPPER_H
|
||||
#define OAK_UNDOWRAPPER_H
|
||||
|
||||
#include "oakengine/undo.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* Wrap an app-side undo command object in the facade custom-command API.
|
||||
*
|
||||
* `Cmd` must provide public `redo()` and `undo()` methods. Ownership of `cmd`
|
||||
* is transferred to the returned opaque command pointer; the wrapper deletes
|
||||
* `cmd` when the engine command is destroyed.
|
||||
*
|
||||
* This helper lets app code keep small app-state undo commands (selections,
|
||||
* splitter sizes, etc.) without defining new subclasses of olive::UndoCommand,
|
||||
* which would keep olive::UndoCommand symbols in the editor binary.
|
||||
*/
|
||||
template <typename Cmd>
|
||||
void *wrap_app_undo_command(const char *name, Cmd *cmd)
|
||||
{
|
||||
return oakengine_undo_command_create(
|
||||
name,
|
||||
[](void *userdata) {
|
||||
static_cast<Cmd *>(userdata)->redo();
|
||||
},
|
||||
[](void *userdata) {
|
||||
static_cast<Cmd *>(userdata)->undo();
|
||||
},
|
||||
[](void *userdata) {
|
||||
delete static_cast<Cmd *>(userdata);
|
||||
},
|
||||
cmd);
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // OAK_UNDOWRAPPER_H
|
||||
@@ -1,47 +0,0 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
// App-side implementation of xml_read_next_start_element
|
||||
// Provides a local definition so the app doesn't import this from liboakengine.
|
||||
|
||||
#include "oakutil/xmlutils.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
bool xml_read_next_start_element(QXmlStreamReader *reader,
|
||||
void *cancel_atom)
|
||||
{
|
||||
QXmlStreamReader::TokenType token;
|
||||
|
||||
while ((token = reader->readNext()) != QXmlStreamReader::Invalid &&
|
||||
token != QXmlStreamReader::EndDocument &&
|
||||
(!cancel_atom)) {
|
||||
if (reader->isEndElement()) {
|
||||
return false;
|
||||
} else if (reader->isStartElement()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
-1589
File diff suppressed because it is too large
Load Diff
-381
@@ -1,381 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CORE_H
|
||||
#define OAK_CORE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <olive/core/core.h>
|
||||
#include "common/tooltypes.h"
|
||||
#include "oakutil/qtutils.h"
|
||||
#include "oakengine/app.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "oakengine/project.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "oakengine/undo.h"
|
||||
#include "oakengine/init.h"
|
||||
#include "oakengine/task.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using namespace core;
|
||||
|
||||
class MainWindow;
|
||||
|
||||
/**
|
||||
* @brief The main central Olive application instance_
|
||||
*
|
||||
* This is the UI-facing application controller. It holds an EngineCore
|
||||
* member for UI-independent engine state and adds the main window, dialogs
|
||||
* and other user interaction on top of it.
|
||||
*
|
||||
* EngineCore is NOT a base class — it is a member, so the MOC-generated
|
||||
* code for Core does not pull in EngineCore's Q_OBJECT symbols.
|
||||
*
|
||||
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
|
||||
* opening the import dialog, etc.)
|
||||
*/
|
||||
class Core : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief Core Constructor
|
||||
*
|
||||
* Creates the EngineCore engine instance and registers the UI handlers
|
||||
* that the engine uses to request user interaction.
|
||||
*/
|
||||
Core(const OakEngineAppParams *params = nullptr);
|
||||
|
||||
~Core()
|
||||
{
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Core object accessible from anywhere in the code
|
||||
*
|
||||
* Returns the application Core singleton (no EngineCore::instance() call).
|
||||
*/
|
||||
static Core *instance()
|
||||
{
|
||||
return instance_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start Olive Core
|
||||
*
|
||||
* Main application launcher. Starts the engine first, then the GUI (if entering a GUI mode).
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* @brief Stop Olive Core
|
||||
*
|
||||
* Tears down the UI services first, then the engine, ready for the application to exit.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* @brief Retrieve main window instance_
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* Pointer to the olive::MainWindow object, or nullptr if running in CLI mode.
|
||||
*/
|
||||
MainWindow *main_window();
|
||||
|
||||
/**
|
||||
* @brief Import a list of files
|
||||
*
|
||||
* FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to
|
||||
* make passing references to the model unnecessary?
|
||||
*
|
||||
* @param urls
|
||||
*/
|
||||
void import_files(const QStringList &urls, OakEngineNode *parent);
|
||||
|
||||
/**
|
||||
* @brief Get the currently active project
|
||||
*
|
||||
* Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project
|
||||
* that the user wishes to work on.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The active Project file, or nullptr if the heuristic couldn't find one.
|
||||
*/
|
||||
OakEngineProject *get_active_project() const;
|
||||
OakEngineNode *get_selected_folder_in_active_project() const;
|
||||
|
||||
/**
|
||||
* @brief Show a dialog to the user to rename a set of nodes
|
||||
*/
|
||||
bool label_nodes(const QVector<OakEngineNode *> &nodes,
|
||||
void *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Opens a project from the recently opened list
|
||||
*/
|
||||
void open_project_from_recent_list(int index);
|
||||
|
||||
/**
|
||||
* @brief Closes a project
|
||||
*/
|
||||
bool close_project(bool auto_open_new, bool ignore_modified = false);
|
||||
|
||||
/**
|
||||
* @brief Runs a modal cache task on the currently active sequence
|
||||
*/
|
||||
void cache_active_sequence(bool in_out_only);
|
||||
|
||||
void open_recovery_project(const QString &filename);
|
||||
|
||||
void open_node_in_viewer(OakEngineNode *viewer);
|
||||
|
||||
void open_export_dialog_for_viewer(OakEngineNode *viewer,
|
||||
bool start_still_image);
|
||||
|
||||
bool add_open_project_from_task(OakEngineTask *task, bool add_to_recents);
|
||||
bool add_recovery_project_from_task(OakEngineTask *task);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Starts an open file dialog to load a project from file
|
||||
*/
|
||||
void open_project();
|
||||
|
||||
/**
|
||||
* @brief Saves the current project
|
||||
*/
|
||||
bool save_project();
|
||||
|
||||
/**
|
||||
* @brief Performs a "save as" on the current project
|
||||
*/
|
||||
bool save_project_as();
|
||||
|
||||
void revert_project();
|
||||
|
||||
/**
|
||||
* @brief Show an About dialog
|
||||
*/
|
||||
void dialog_about_show();
|
||||
|
||||
/**
|
||||
* @brief Open the import footage dialog and import the files selected (runs ImportFiles())
|
||||
*/
|
||||
void dialog_import_show();
|
||||
|
||||
/**
|
||||
* @brief Show Preferences dialog
|
||||
*/
|
||||
void dialog_preferences_show(int start_tab = 0);
|
||||
|
||||
/**
|
||||
* @brief Show Project Properties dialog
|
||||
*/
|
||||
void dialog_project_properties_show();
|
||||
|
||||
/**
|
||||
* @brief Show Export dialog
|
||||
*/
|
||||
void dialog_export_show();
|
||||
|
||||
/**
|
||||
* @brief Create a new folder in the currently active project
|
||||
*/
|
||||
void create_new_folder();
|
||||
|
||||
/**
|
||||
* @brief Create a new sequence in the currently active project
|
||||
*/
|
||||
void create_new_sequence();
|
||||
|
||||
void check_for_auto_recoveries();
|
||||
|
||||
void browse_auto_recoveries();
|
||||
|
||||
public:
|
||||
// The following methods are ordinary member functions, NOT slots. They are
|
||||
// deliberately kept out of the `public slots:` section; none of them are
|
||||
// connect() targets: every connection involving Core uses the new-style
|
||||
// member-function syntax, which works with plain methods.
|
||||
|
||||
/**
|
||||
* @brief Show OTIO import dialog
|
||||
*/
|
||||
#ifdef USE_OTIO
|
||||
bool DialogImportOTIOShow(const QList<OakEngineSequence *> &sequences);
|
||||
#endif
|
||||
|
||||
// ---- Facade-wrapping methods (shadow EngineCore to avoid symbol refs) ----
|
||||
|
||||
Tool::Item tool() const;
|
||||
void set_tool(const Tool::Item &tool);
|
||||
|
||||
bool snapping() const;
|
||||
void set_snapping(const bool &b);
|
||||
|
||||
Timecode::Display get_timecode_display() const;
|
||||
void set_timecode_display(Timecode::Display d);
|
||||
|
||||
void show_status_bar_message(const QString &s, int timeout = 0);
|
||||
void clear_status_bar_message();
|
||||
|
||||
static QString footage_file_dialog_filter();
|
||||
static bool is_footage_extension_allowed(const QString &path);
|
||||
|
||||
void create_new_project();
|
||||
OakEngineSequence *create_new_sequence_for_project(const QString &format,
|
||||
OakEngineProject *project);
|
||||
static OakEngineSequence *create_new_sequence_for_project(OakEngineProject *project);
|
||||
|
||||
void clear_open_recent_list();
|
||||
void set_use_proxy_media(bool enabled);
|
||||
|
||||
void request_pixel_sampling_in_viewers(bool e);
|
||||
|
||||
Tool::AddableObject get_selected_addable_object() const;
|
||||
void set_selected_addable_object(const Tool::AddableObject &obj);
|
||||
void set_selected_transition_object(const QString &obj);
|
||||
|
||||
static void copy_string_to_clipboard(const QString &s);
|
||||
|
||||
void set_magic(bool e);
|
||||
|
||||
// Recent project list accessors (replaces EngineCore::get_recent_projects())
|
||||
int get_recent_project_count() const;
|
||||
QString get_recent_project_at(int index) const;
|
||||
|
||||
// Facade-wrapping methods (delegate through the C ABI)
|
||||
|
||||
bool set_language(const QString &locale);
|
||||
void set_autorecovery_interval(int minutes);
|
||||
|
||||
void on_project_saved(OakEngineProject *p);
|
||||
static QString get_auto_recovery_index_filename();
|
||||
void add_open_project(OakEngineProject *p, bool add_to_recents = false);
|
||||
void remove_recently_opened_project(int index);
|
||||
void set_active_project(OakEngineProject *p);
|
||||
QString get_selected_transition() const;
|
||||
|
||||
signals:
|
||||
// Forwarding signals (shadow EngineCore signals so connect() resolves here)
|
||||
void tool_changed(const Tool::Item &tool);
|
||||
void addable_object_changed(Tool::AddableObject o);
|
||||
void snapping_changed(const bool &b);
|
||||
void timecode_display_changed(Timecode::Display d);
|
||||
void open_recent_list_changed();
|
||||
void color_picker_enabled(bool e);
|
||||
void color_picker_color_emitted(const Color &reference, const Color &display);
|
||||
|
||||
/**
|
||||
* @brief App-internal re-broadcast of the engine undo-stack index change
|
||||
* (issue 7 of the EventBridge elimination plan). Widgets connect to this
|
||||
* instead of raw oakengine_event_subscribe callbacks on
|
||||
* OAKENGINE_EVENT_UNDO_INDEX_CHANGED. The argument is the new stack index.
|
||||
*/
|
||||
void undo_index_changed(int index);
|
||||
|
||||
/**
|
||||
* @brief App-internal re-broadcast of the active project's modified-flag
|
||||
* change (issue 8 of the EventBridge elimination plan). The main window
|
||||
* drives setWindowModified from this instead of a raw
|
||||
* oakengine_event_subscribe callback on
|
||||
* OAKENGINE_EVENT_PROJECT_MODIFIED_CHANGED.
|
||||
*/
|
||||
void project_modified_changed(bool modified);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
|
||||
*/
|
||||
static QString get_project_filter(bool include_any_filter);
|
||||
|
||||
/**
|
||||
* @brief Start GUI portion of Olive
|
||||
*
|
||||
* Starts services and objects required for the GUI of Olive. It's guaranteed that running without this function will
|
||||
* create an application instance_ that is completely valid minus the UI (e.g. for CLI modes).
|
||||
*/
|
||||
void start_gui(bool full_screen);
|
||||
|
||||
/**
|
||||
* @brief Internal function for saving a project to a file
|
||||
*/
|
||||
void save_project_internal(const QString &override_filename = QString());
|
||||
|
||||
/**
|
||||
* @brief Retrieves the currently most active sequence for exporting
|
||||
*/
|
||||
OakEngineNode *get_sequence_to_export();
|
||||
|
||||
bool revert_project_internal(bool by_opening_existing);
|
||||
|
||||
/**
|
||||
* @brief Shows the "disk cache full" warning (connected to EngineCore::cache_full_warning_requested)
|
||||
*/
|
||||
void show_cache_full_warning();
|
||||
|
||||
/**
|
||||
* @brief Applies a new active project to the main window (connected to EngineCore::active_project_changed)
|
||||
*/
|
||||
void on_active_project_changed(OakEngineProject *p);
|
||||
|
||||
/**
|
||||
* @brief Internal main window object
|
||||
*/
|
||||
MainWindow *main_window_;
|
||||
|
||||
/**
|
||||
* @brief Cached Core* singleton
|
||||
*/
|
||||
static Core *instance_;
|
||||
|
||||
private slots:
|
||||
void project_save_succeeded(OakEngineTask *task);
|
||||
|
||||
bool add_open_project_from_task_and_add_to_recents(OakEngineTask *task)
|
||||
{
|
||||
return instance()->add_open_project_from_task(task, true);
|
||||
}
|
||||
|
||||
void import_task_complete(OakEngineTask *task);
|
||||
|
||||
bool confirm_image_sequence(const QString &filename);
|
||||
|
||||
bool start_headless_export();
|
||||
|
||||
void open_startup_project();
|
||||
|
||||
/**
|
||||
* @brief Internal project open
|
||||
*/
|
||||
void open_project_internal(const QString &filename,
|
||||
bool recovery_project = false);
|
||||
|
||||
void import_single_file(const QString &f);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_CORE_H
|
||||
@@ -1,68 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Create crash handler executable
|
||||
add_executable(
|
||||
olive-crashhandler
|
||||
crashhandler.cpp
|
||||
crashhandler.h
|
||||
$<TARGET_OBJECTS:olive-version-obj>
|
||||
)
|
||||
|
||||
# Rename the generated binary to oak-crashhandler so it matches the Oak branding.
|
||||
set_target_properties(olive-crashhandler PROPERTIES OUTPUT_NAME "oak-crashhandler")
|
||||
|
||||
# Disable console appearing on crash handler dialog
|
||||
set_target_properties(olive-crashhandler PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
)
|
||||
|
||||
# Set crash handler includes
|
||||
target_include_directories(
|
||||
olive-crashhandler
|
||||
PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/app
|
||||
${CRASHPAD_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# Set crash handler libs
|
||||
target_link_libraries(
|
||||
olive-crashhandler
|
||||
PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
${CRASHPAD_LIBRARIES}
|
||||
)
|
||||
|
||||
set(CRASHPAD_HANDLER "crashpad_handler${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
set(MINIDUMP_STACKWALK "minidump_stackwalk${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
|
||||
if (APPLE)
|
||||
# Move crash handler executables inside Mac app bundle
|
||||
add_custom_command(TARGET olive-crashhandler POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olive-crashhandler> $<TARGET_FILE_DIR:olive-editor>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} $<TARGET_FILE_DIR:olive-editor>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} $<TARGET_FILE_DIR:olive-editor>
|
||||
)
|
||||
elseif (UNIX)
|
||||
install(TARGETS olive-crashhandler RUNTIME DESTINATION bin)
|
||||
install(PROGRAMS ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} DESTINATION bin)
|
||||
install(PROGRAMS ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} DESTINATION bin)
|
||||
endif ()
|
||||
|
||||
target_compile_definitions(olive-crashhandler PRIVATE ${OLIVE_DEFINITIONS})
|
||||
@@ -1,410 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "crashhandler.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFontDatabase>
|
||||
#include <QLabel>
|
||||
#include <QHttpMultiPart>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QProcess>
|
||||
#include <QScrollBar>
|
||||
#include <QSplitter>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "oakutil/crashpadutils.h"
|
||||
#include "oakutil/filefunctions.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
CrashHandlerDialog::CrashHandlerDialog(const QString &report_path)
|
||||
{
|
||||
setWindowTitle(tr("Oak Video Editor"));
|
||||
setWindowFlags(Qt::WindowStaysOnTopHint);
|
||||
|
||||
report_filename_ = report_path;
|
||||
waiting_for_upload_ = false;
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
layout->addWidget(new QLabel(tr(
|
||||
"We're sorry, Oak Video Editor has crashed. Please help us fix it by "
|
||||
"sending an error report.")));
|
||||
|
||||
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
|
||||
summary_edit_ = new QTextEdit();
|
||||
summary_edit_->setPlaceholderText(
|
||||
tr("Describe what you were doing in as much detail as "
|
||||
"possible. If you can, provide steps to reproduce this crash."));
|
||||
|
||||
splitter->addWidget(summary_edit_);
|
||||
|
||||
QWidget *crash_widget = new QWidget();
|
||||
QVBoxLayout *crash_widget_layout = new QVBoxLayout(crash_widget);
|
||||
crash_widget_layout->setMargin(0);
|
||||
|
||||
crash_widget_layout->addWidget(new QLabel(tr("Crash Report:")));
|
||||
|
||||
crash_report_ = new QTextEdit();
|
||||
crash_report_->setReadOnly(true);
|
||||
crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
crash_widget_layout->addWidget(crash_report_);
|
||||
|
||||
splitter->addWidget(crash_widget);
|
||||
|
||||
QHBoxLayout *btn_layout = new QHBoxLayout();
|
||||
btn_layout->setMargin(0);
|
||||
btn_layout->addStretch();
|
||||
|
||||
send_report_btn_ = new QPushButton(tr("Send Error Report"));
|
||||
connect(send_report_btn_, &QPushButton::clicked, this,
|
||||
&CrashHandlerDialog::SendErrorReport);
|
||||
btn_layout->addWidget(send_report_btn_);
|
||||
|
||||
dont_send_btn_ = new QPushButton(tr("Don't Send"));
|
||||
connect(dont_send_btn_, &QPushButton::clicked, this,
|
||||
&CrashHandlerDialog::reject);
|
||||
btn_layout->addWidget(dont_send_btn_);
|
||||
|
||||
layout->addLayout(btn_layout);
|
||||
|
||||
crash_report_->setEnabled(false);
|
||||
send_report_btn_->setEnabled(false);
|
||||
|
||||
crash_report_->setText(tr("Waiting for crash report to be generated..."));
|
||||
|
||||
GenerateReport();
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::SetGUIObjectsEnabled(bool e)
|
||||
{
|
||||
summary_edit_->setEnabled(e);
|
||||
crash_report_->setEnabled(e);
|
||||
send_report_btn_->setEnabled(e);
|
||||
dont_send_btn_->setEnabled(e);
|
||||
}
|
||||
|
||||
QString CrashHandlerDialog::GetSymbolPath()
|
||||
{
|
||||
QDir app_path(qApp->applicationDirPath());
|
||||
QString symbols_path;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
symbols_path = app_path.filePath(QStringLiteral("symbols"));
|
||||
#elif BUILDFLAG(IS_LINUX)
|
||||
app_path.cdUp();
|
||||
symbols_path =
|
||||
app_path.filePath(QStringLiteral("share/oak-editor/symbols"));
|
||||
#elif BUILDFLAG(IS_APPLE)
|
||||
app_path.cdUp();
|
||||
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
|
||||
#endif
|
||||
|
||||
return symbols_path;
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::GenerateReport()
|
||||
{
|
||||
QProcess *p = new QProcess();
|
||||
|
||||
connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this, &CrashHandlerDialog::ReadProcessFinished);
|
||||
connect(p, &QProcess::readyReadStandardOutput, this,
|
||||
&CrashHandlerDialog::ReadProcessHasData);
|
||||
|
||||
QString stackwalk_filename =
|
||||
FileFunctions::GetFormattedExecutableForPlatform(
|
||||
QStringLiteral("minidump_stackwalk"));
|
||||
|
||||
QString stackwalk_bin =
|
||||
QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
|
||||
p->start(stackwalk_bin, { report_filename_, GetSymbolPath() });
|
||||
crash_report_->setText(
|
||||
QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::ReplyFinished(QNetworkReply *reply)
|
||||
{
|
||||
waiting_for_upload_ = false;
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
// Close dialog
|
||||
QDialog::accept();
|
||||
} else {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Upload Failed"));
|
||||
b.setText(
|
||||
tr("Failed to send error report (%1). Please try again later.")
|
||||
.arg(QString::number(reply->error())));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
|
||||
SetGUIObjectsEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::HandleSslErrors(QNetworkReply *reply,
|
||||
const QList<QSslError> &se)
|
||||
{
|
||||
QStringList errors;
|
||||
for (const QSslError &err : se) {
|
||||
errors.append(err.errorString());
|
||||
}
|
||||
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("SSL Error"));
|
||||
b.setText(tr("Encountered the following SSL errors:\n\n%1")
|
||||
.arg(errors.join('\n')));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::AttemptToFindReport()
|
||||
{
|
||||
// If we found it, use it, otherwise wait a second and try again
|
||||
if (report_filename_.isEmpty()) {
|
||||
// Couldn't find report, try again in one second
|
||||
QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport);
|
||||
} else {
|
||||
GenerateReport();
|
||||
}
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::ReadProcessHasData()
|
||||
{
|
||||
report_data_.append(
|
||||
static_cast<QProcess *>(sender())->readAllStandardOutput());
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::ReadProcessFinished()
|
||||
{
|
||||
SetGUIObjectsEnabled(true);
|
||||
crash_report_->setText(report_data_);
|
||||
delete sender();
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::SendErrorReport()
|
||||
{
|
||||
if (summary_edit_->document()->isEmpty()) {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Question);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setText(
|
||||
tr("You must write a description to submit this crash report."));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
connect(manager, &QNetworkAccessManager::finished, this,
|
||||
&CrashHandlerDialog::ReplyFinished);
|
||||
connect(manager, &QNetworkAccessManager::sslErrors, this,
|
||||
&CrashHandlerDialog::HandleSslErrors);
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
|
||||
request.setUrl(
|
||||
QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
|
||||
|
||||
// Create HTTP form
|
||||
QHttpMultiPart *multipart =
|
||||
new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
|
||||
// Create description section
|
||||
QHttpPart desc_part;
|
||||
desc_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("text/plain; charset=UTF-8"));
|
||||
desc_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"description\""));
|
||||
desc_part.setBody(summary_edit_->toPlainText().toUtf8());
|
||||
multipart->append(desc_part);
|
||||
|
||||
// Create report section
|
||||
QHttpPart report_part;
|
||||
report_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("text/plain; charset=UTF-8"));
|
||||
report_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"report\""));
|
||||
report_part.setBody(report_data_);
|
||||
multipart->append(report_part);
|
||||
|
||||
// Create commit section
|
||||
QHttpPart commit_part;
|
||||
commit_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("text/plain; charset=UTF-8"));
|
||||
commit_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"commit\""));
|
||||
commit_part.setBody(kAppVersionLong.toUtf8());
|
||||
multipart->append(commit_part);
|
||||
|
||||
// Create dump section
|
||||
QHttpPart dump_part;
|
||||
dump_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("application/octet-stream"));
|
||||
dump_part.setHeader(
|
||||
QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"dump\"; filename=\"%1\"")
|
||||
.arg(QFileInfo(report_filename_).fileName()));
|
||||
QFile *dump_file = new QFile(report_filename_);
|
||||
dump_file->open(QFile::ReadOnly);
|
||||
dump_part.setBodyDevice(dump_file);
|
||||
dump_file->setParent(multipart); // Delete file with multipart
|
||||
multipart->append(dump_part);
|
||||
|
||||
// Find symbol file
|
||||
QDir symbol_dir(GetSymbolPath());
|
||||
|
||||
QString symbol_bin_name;
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
symbol_bin_name = QStringLiteral("oak-editor.pdb");
|
||||
#elif BUILDFLAG(IS_APPLE)
|
||||
symbol_bin_name = QStringLiteral("Oak");
|
||||
#else
|
||||
symbol_bin_name = QStringLiteral("oak-editor");
|
||||
#endif
|
||||
symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name));
|
||||
|
||||
QStringList folders_in_symbol_path =
|
||||
symbol_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
if (folders_in_symbol_path.size() > 0) {
|
||||
symbol_dir = QDir(symbol_dir.filePath(folders_in_symbol_path.first()));
|
||||
} else {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Failed to send report"));
|
||||
b.setText(tr("Failed to find symbols necessary to send report. "
|
||||
"This is a packaging issue. Please notify "
|
||||
"the maintainers of this package."));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create sym section
|
||||
QString symbol_filename;
|
||||
#if BUILDFLAG(IS_APPLE)
|
||||
symbol_filename = QStringLiteral("Oak.sym");
|
||||
#else
|
||||
symbol_filename = QStringLiteral("oak-editor.sym");
|
||||
#endif
|
||||
QString symbol_full_path = symbol_dir.filePath(symbol_filename);
|
||||
QHttpPart sym_part;
|
||||
sym_part.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||
QStringLiteral("application/octet-stream"));
|
||||
sym_part.setHeader(
|
||||
QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"sym\"; filename=\"%1\"")
|
||||
.arg(symbol_filename));
|
||||
QFile sym_file(symbol_full_path);
|
||||
|
||||
if (!sym_file.open(QFile::ReadOnly)) {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Failed to send report"));
|
||||
b.setText(tr("Failed to open symbol file. You may not have "
|
||||
"permission to access it."));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray symbol_data = qCompress(sym_file.readAll(), 9);
|
||||
sym_file.close();
|
||||
|
||||
sym_part.setBody(symbol_data);
|
||||
multipart->append(sym_part);
|
||||
|
||||
manager->post(request, multipart);
|
||||
|
||||
SetGUIObjectsEnabled(false);
|
||||
waiting_for_upload_ = true;
|
||||
}
|
||||
|
||||
void CrashHandlerDialog::closeEvent(QCloseEvent *e)
|
||||
{
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Warning);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Confirm Close"));
|
||||
b.setText(
|
||||
tr("Crash report is still uploading. Closing now may result in no "
|
||||
"report being sent. Are you sure you wish to close?"));
|
||||
b.addButton(QMessageBox::Ok);
|
||||
b.addButton(QMessageBox::Cancel);
|
||||
|
||||
if (waiting_for_upload_ && b.exec() == QMessageBox::Cancel) {
|
||||
e->ignore();
|
||||
} else {
|
||||
e->accept();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QString report;
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
int num_args;
|
||||
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &num_args);
|
||||
if (num_args < 2) {
|
||||
LocalFree(args);
|
||||
return 1;
|
||||
}
|
||||
|
||||
report = QString::fromWCharArray(args[1]);
|
||||
LocalFree(args);
|
||||
#else
|
||||
if (argc < 2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
report = argv[1];
|
||||
#endif
|
||||
|
||||
QApplication a(argc, argv);
|
||||
|
||||
olive::CrashHandlerDialog chd(report);
|
||||
chd.open();
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CRASHHANDLERDIALOG_H
|
||||
#define OAK_CRASHHANDLERDIALOG_H
|
||||
|
||||
#include <client/crash_report_database.h>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QNetworkReply>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
|
||||
#include "oakutil/define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CrashHandlerDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CrashHandlerDialog(const QString &report_path);
|
||||
|
||||
private:
|
||||
void SetGUIObjectsEnabled(bool e);
|
||||
|
||||
void GenerateReport();
|
||||
|
||||
static QString GetSymbolPath();
|
||||
|
||||
QTextEdit *summary_edit_;
|
||||
|
||||
QTextEdit *crash_report_;
|
||||
|
||||
QPushButton *send_report_btn_;
|
||||
|
||||
QPushButton *dont_send_btn_;
|
||||
|
||||
QString report_filename_;
|
||||
|
||||
QByteArray report_data_;
|
||||
|
||||
bool waiting_for_upload_;
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void ReplyFinished(QNetworkReply *reply);
|
||||
|
||||
void HandleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
||||
|
||||
void AttemptToFindReport();
|
||||
|
||||
void ReadProcessHasData();
|
||||
|
||||
void ReadProcessFinished();
|
||||
|
||||
void SendErrorReport();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_CRASHHANDLERDIALOG_H
|
||||
@@ -1,47 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(about)
|
||||
add_subdirectory(actionsearch)
|
||||
add_subdirectory(autorecovery)
|
||||
add_subdirectory(color)
|
||||
add_subdirectory(configbase)
|
||||
add_subdirectory(diskcache)
|
||||
add_subdirectory(export)
|
||||
add_subdirectory(footageproperties)
|
||||
add_subdirectory(footagerelink)
|
||||
add_subdirectory(keyframeproperties)
|
||||
add_subdirectory(markerproperties)
|
||||
if (OpenTimelineIO_FOUND)
|
||||
add_subdirectory(otioproperties)
|
||||
endif ()
|
||||
add_subdirectory(preferences)
|
||||
add_subdirectory(progress)
|
||||
add_subdirectory(projectimport)
|
||||
add_subdirectory(proxy)
|
||||
add_subdirectory(projectproperties)
|
||||
add_subdirectory(rendercancel)
|
||||
add_subdirectory(sequence)
|
||||
add_subdirectory(speedduration)
|
||||
add_subdirectory(task)
|
||||
add_subdirectory(text)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/ratiodialog.cpp
|
||||
dialog/ratiodialog.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,25 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/about/about.cpp
|
||||
dialog/about/about.h
|
||||
dialog/about/patreon.h
|
||||
dialog/about/scrollinglabel.cpp
|
||||
dialog/about/scrollinglabel.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,159 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "about.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "common/configwrapper.h"
|
||||
#include "patreon.h"
|
||||
#include "scrollinglabel.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
if (welcome_dialog) {
|
||||
setWindowTitle(
|
||||
tr("Welcome to %1").arg(QApplication::applicationName()));
|
||||
} else {
|
||||
setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
|
||||
}
|
||||
|
||||
QFontMetrics fm = fontMetrics();
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
|
||||
fm.height());
|
||||
|
||||
QHBoxLayout *horiz_layout = new QHBoxLayout();
|
||||
horiz_layout->setContentsMargins(fm.height(), fm.height(), fm.height(),
|
||||
fm.height());
|
||||
horiz_layout->setSpacing(fm.height() * 2);
|
||||
|
||||
QLabel *icon = new QLabel(
|
||||
QStringLiteral("<html><img src=':/graphics/oak-logo.png'></html>"));
|
||||
icon->setAlignment(Qt::AlignCenter);
|
||||
horiz_layout->addWidget(icon);
|
||||
|
||||
// Construct About text
|
||||
QLabel *label = new QLabel(
|
||||
QStringLiteral("<html><head/><body>"
|
||||
"<p><b>%1</b> %2</p>" // AppName (version identifier)
|
||||
"<p>%3</p>" // Description
|
||||
"<p>%4</p>" // Fork notice
|
||||
"<p>%5</p>" // Special thanks
|
||||
"</body></html>")
|
||||
.arg(
|
||||
QApplication::applicationName(),
|
||||
QApplication::applicationVersion(),
|
||||
tr("Oak Video Editor is a free open source non-linear video editor. "
|
||||
"This software is licensed under the GNU GPL Version 3."),
|
||||
tr("This project is a fork of "
|
||||
"<a href=\"https://github.com/olive-editor/olive\">Olive Video Editor</a>."),
|
||||
tr("Special thanks to Enzo GD, administrator of the Olive "
|
||||
"Facebook user group, for his generous support in spreading "
|
||||
"the word about this project in its early days.")));
|
||||
|
||||
// Set text formatting
|
||||
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
label->setWordWrap(true);
|
||||
label->setOpenExternalLinks(true);
|
||||
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse |
|
||||
Qt::LinksAccessibleByMouse);
|
||||
label->setCursor(Qt::IBeamCursor);
|
||||
horiz_layout->addWidget(label);
|
||||
|
||||
layout->addLayout(horiz_layout);
|
||||
|
||||
// Patrons where possible
|
||||
layout->addWidget(new QLabel());
|
||||
|
||||
QString opening_statement;
|
||||
|
||||
if (welcome_dialog || patrons.isEmpty()) {
|
||||
opening_statement = tr(
|
||||
"<b>Oak Video Editor relies on support from the community to continue its development.</b>");
|
||||
} else {
|
||||
opening_statement = tr(
|
||||
"Oak Video Editor wouldn't be possible without the support of gracious donations from the following people.");
|
||||
}
|
||||
|
||||
QLabel *support_lbl = new QLabel(
|
||||
tr("<html>%1 "
|
||||
"If you like this project, please consider making a "
|
||||
"one-time donation or pledging monthly to support its development.</html>")
|
||||
.arg(opening_statement));
|
||||
support_lbl->setWordWrap(true);
|
||||
support_lbl->setAlignment(Qt::AlignCenter);
|
||||
support_lbl->setOpenExternalLinks(true);
|
||||
layout->addWidget(support_lbl);
|
||||
|
||||
if (!patrons.isEmpty()) {
|
||||
ScrollingLabel *scroll = new ScrollingLabel(patrons);
|
||||
scroll->start_animating();
|
||||
layout->addWidget(scroll);
|
||||
}
|
||||
|
||||
layout->addWidget(new QLabel());
|
||||
|
||||
QHBoxLayout *btn_layout = new QHBoxLayout();
|
||||
btn_layout->setContentsMargins(0, 0, 0, 0);
|
||||
btn_layout->setSpacing(0);
|
||||
|
||||
if (welcome_dialog) {
|
||||
dont_show_again_checkbox_ =
|
||||
new QCheckBox(tr("Don't show this message again"));
|
||||
btn_layout->addWidget(dont_show_again_checkbox_);
|
||||
} else {
|
||||
dont_show_again_checkbox_ = nullptr;
|
||||
}
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok, this);
|
||||
if (!welcome_dialog) {
|
||||
buttons->setCenterButtons(true);
|
||||
}
|
||||
btn_layout->addWidget(buttons);
|
||||
|
||||
layout->addLayout(btn_layout);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
|
||||
setFixedSize(sizeHint());
|
||||
}
|
||||
|
||||
void AboutDialog::accept()
|
||||
{
|
||||
if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) {
|
||||
OAK_CONFIG("ShowWelcomeDialog") = false;
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_ABOUTDIALOG_H
|
||||
#define OAK_ABOUTDIALOG_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
|
||||
#include "oakutil/define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The AboutDialog class
|
||||
*
|
||||
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from
|
||||
* anywhere
|
||||
*/
|
||||
class AboutDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief AboutDialog Constructor
|
||||
*
|
||||
* Creates About dialog.
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* QWidget parent object. Usually this will be MainWindow.
|
||||
*/
|
||||
explicit AboutDialog(bool welcome_dialog, QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
QCheckBox *dont_show_again_checkbox_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_ABOUTDIALOG_H
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OAK_PATREON_H
|
||||
#define OAK_PATREON_H
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
QStringList patrons;
|
||||
|
||||
#endif // OAK_PATREON_H
|
||||
@@ -1,84 +0,0 @@
|
||||
# Oak Video Editor - Non-Linear Video Editor
|
||||
# Copyright (C) 2025 Olive CE Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
# /***
|
||||
#
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ***/
|
||||
#
|
||||
|
||||
import json
|
||||
import requests
|
||||
import os
|
||||
|
||||
url = 'https://www.patreon.com/api/oauth2/v2/campaigns/1478705/members?include=currently_entitled_tiers&fields%5Bmember%5D=full_name'
|
||||
name_list = ''
|
||||
|
||||
while True:
|
||||
member_data = requests.get(url, headers={"authorization": "Bearer " + os.environ.get('PATREON_KEY')})
|
||||
member_data_decoded = json.loads(member_data.text)
|
||||
|
||||
for member in member_data_decoded["data"]:
|
||||
if len(member["relationships"]["currently_entitled_tiers"]["data"]) > 0:
|
||||
if member["relationships"]["currently_entitled_tiers"]["data"][0]["id"] == "3952333":
|
||||
if len(name_list) > 0:
|
||||
name_list += ',\n'
|
||||
name = member["attributes"]["full_name"]
|
||||
name_list += " QStringLiteral(\""
|
||||
name_list += name.translate(str.maketrans({
|
||||
"\"": "\\\"",
|
||||
"\\": "\\\\"
|
||||
}))
|
||||
name_list += "\")"
|
||||
|
||||
if "links" in member_data_decoded:
|
||||
url = member_data_decoded["links"]["next"]
|
||||
else:
|
||||
break
|
||||
|
||||
text_file = open("patreon.h", "w", encoding="utf-8")
|
||||
text_file.write(
|
||||
"#ifndef PATREON_H\n#define PATREON_H\n\n#include <QStringList>\n\nQStringList patrons = {\n%s\n};\n\n#endif // PATREON_H\n" % name_list)
|
||||
text_file.close()
|
||||
@@ -1,125 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "scrollinglabel.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#include "oakutil/qtutils.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const int ScrollingLabel::k_min_line_height = 10;
|
||||
|
||||
ScrollingLabel::ScrollingLabel(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, animate_(0)
|
||||
{
|
||||
timer_.setInterval(50);
|
||||
connect(&timer_, &QTimer::timeout, this, &ScrollingLabel::animation_update);
|
||||
}
|
||||
|
||||
ScrollingLabel::ScrollingLabel(const QStringList &text, QWidget *parent)
|
||||
: ScrollingLabel(parent)
|
||||
{
|
||||
set_text(text);
|
||||
}
|
||||
|
||||
void ScrollingLabel::set_text(const QStringList &text)
|
||||
{
|
||||
text_ = text;
|
||||
|
||||
QFontMetrics fm = fontMetrics();
|
||||
text_height_ = fm.height();
|
||||
|
||||
int width = 0;
|
||||
foreach (const QString &s, text_) {
|
||||
width = qMax(width, QtUtils::q_font_metrics_width(fm, s));
|
||||
}
|
||||
|
||||
setMinimumSize(width, text_height_ * k_min_line_height);
|
||||
}
|
||||
|
||||
void ScrollingLabel::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
QImage map(width(), height(), QImage::Format_RGBA8888_Premultiplied);
|
||||
map.fill(Qt::transparent);
|
||||
|
||||
{
|
||||
QPainter p(&map);
|
||||
p.setPen(palette().text().color());
|
||||
|
||||
QFontMetrics fm = p.fontMetrics();
|
||||
int half_width = width();
|
||||
|
||||
for (int i = 0; i < text_.size(); i++) {
|
||||
int text_y = fm.ascent() + height() - animate_ + (fm.height() * i);
|
||||
int text_bottom = text_y + fm.descent();
|
||||
int text_top = text_y - fm.ascent();
|
||||
|
||||
if (text_bottom < 0 || text_top >= height()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QString &s = text_.at(i);
|
||||
|
||||
int width = QtUtils::q_font_metrics_width(fm, s);
|
||||
p.drawText(half_width / 2 - width / 2, text_y, s);
|
||||
}
|
||||
|
||||
for (int y = 0; y < text_height_; y++) {
|
||||
double mul = double(y) / double(text_height_);
|
||||
|
||||
set_opacity_of_scan_line(map.scanLine(y), map.width(), 4, mul);
|
||||
set_opacity_of_scan_line(map.scanLine(map.height() - 1 - y),
|
||||
map.width(), 4, mul);
|
||||
}
|
||||
}
|
||||
|
||||
QPainter wp(this);
|
||||
wp.drawImage(0, 0, map);
|
||||
}
|
||||
|
||||
void ScrollingLabel::set_opacity_of_scan_line(uchar *scan_line, int width,
|
||||
int channels, double mul)
|
||||
{
|
||||
for (int x = 0; x < width; x++) {
|
||||
uchar *pixel = &scan_line[x * 4];
|
||||
|
||||
for (int c = 0; c < channels; c++) {
|
||||
pixel[c] *= mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollingLabel::animation_update()
|
||||
{
|
||||
animate_++;
|
||||
|
||||
if (animate_ >= height() + text_.size() * text_height_) {
|
||||
animate_ = 0;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_SCROLLINGLABEL_H
|
||||
#define OAK_SCROLLINGLABEL_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ScrollingLabel : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ScrollingLabel(QWidget *parent = nullptr);
|
||||
ScrollingLabel(const QStringList &text, QWidget *parent = nullptr);
|
||||
|
||||
void set_text(const QStringList &text);
|
||||
|
||||
void start_animating()
|
||||
{
|
||||
timer_.start();
|
||||
}
|
||||
|
||||
void stop_animating()
|
||||
{
|
||||
timer_.stop();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
static void set_opacity_of_scan_line(uchar *scan_line, int width, int channels,
|
||||
double mul);
|
||||
|
||||
static const int k_min_line_height;
|
||||
|
||||
QStringList text_;
|
||||
|
||||
int text_height_;
|
||||
|
||||
QTimer timer_;
|
||||
|
||||
int animate_;
|
||||
|
||||
private slots:
|
||||
void animation_update();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_SCROLLINGLABEL_H
|
||||
@@ -1,22 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/actionsearch/actionsearch.h
|
||||
dialog/actionsearch/actionsearch.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,276 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "actionsearch.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QMenuBar>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ActionSearch::ActionSearch(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, menu_bar_(nullptr)
|
||||
{
|
||||
// ActionSearch requires a parent widget
|
||||
Q_ASSERT(parent != nullptr);
|
||||
|
||||
// Set styling (object name is required for CSS specific to this object)
|
||||
setObjectName("ASDiag");
|
||||
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
|
||||
|
||||
// Size proportionally to the parent (usually MainWindow).
|
||||
resize(parent->width() / 3, parent->height() / 3);
|
||||
|
||||
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
|
||||
setWindowFlags(Qt::Popup);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
// Construct the main entry text field.
|
||||
ActionSearchEntry *entry_field = new ActionSearchEntry(this);
|
||||
|
||||
// Set the main entry field font size to 1.2x its standard font size.
|
||||
QFont entry_field_font = entry_field->font();
|
||||
entry_field_font.setPointSize(qRound(entry_field_font.pointSize() * 1.2));
|
||||
entry_field->setFont(entry_field_font);
|
||||
|
||||
// Set placeholder text for the main entry field
|
||||
entry_field->setPlaceholderText(tr("Search for action..."));
|
||||
|
||||
// Connect signals/slots
|
||||
connect(entry_field, SIGNAL(textChanged(const QString &)), this,
|
||||
SLOT(search_update(const QString &)));
|
||||
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
|
||||
|
||||
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
|
||||
// We override it here to select the upper or lower item in the list.
|
||||
connect(entry_field, SIGNAL(move_selection_up()), this,
|
||||
SLOT(move_selection_up()));
|
||||
connect(entry_field, SIGNAL(move_selection_down()), this,
|
||||
SLOT(move_selection_down()));
|
||||
layout->addWidget(entry_field);
|
||||
|
||||
// Construct list of actions
|
||||
list_widget_ = new ActionSearchList(this);
|
||||
|
||||
// Set list's font to 1.2x its standard font size
|
||||
QFont list_widget_font = list_widget_->font();
|
||||
list_widget_font.setPointSize(qRound(list_widget_font.pointSize() * 1.2));
|
||||
list_widget_->setFont(list_widget_font);
|
||||
|
||||
layout->addWidget(list_widget_);
|
||||
|
||||
connect(list_widget_, SIGNAL(dbl_click()), this, SLOT(perform_action()));
|
||||
|
||||
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
|
||||
// shortcut for example).
|
||||
entry_field->setFocus();
|
||||
}
|
||||
|
||||
void ActionSearch::set_menu_bar(QMenuBar *menu_bar)
|
||||
{
|
||||
menu_bar_ = menu_bar;
|
||||
}
|
||||
|
||||
void ActionSearch::search_update(const QString &s, const QString &p,
|
||||
QMenu *parent)
|
||||
{
|
||||
// Do nothing if there's no menu bar to work with
|
||||
if (menu_bar_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
|
||||
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
|
||||
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
|
||||
// submenus).
|
||||
|
||||
if (parent == nullptr) {
|
||||
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
|
||||
// (and their submenus).
|
||||
|
||||
// We'll clear all the current items in the list since if we're here, we're just starting.
|
||||
list_widget_->clear();
|
||||
|
||||
QList<QAction *> menus = menu_bar_->actions();
|
||||
|
||||
// Loop through all menus from the menubar and run this function on each one.
|
||||
for (int i = 0; i < menus.size(); i++) {
|
||||
QMenu *menu = menus.at(i)->menu();
|
||||
|
||||
search_update(s, p, menu);
|
||||
}
|
||||
|
||||
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
|
||||
// keyboard-exclusive functionality.
|
||||
if (list_widget_->count() > 0) {
|
||||
list_widget_->item(0)->setSelected(true);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
|
||||
|
||||
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
|
||||
// adding the current menu's text to the existing hierarchy (passed in `p`).
|
||||
QString menu_text;
|
||||
if (!p.isEmpty())
|
||||
menu_text += p + " > ";
|
||||
menu_text += parent->title().replace(
|
||||
"&", ""); // Strip out any &s used in menu action names
|
||||
|
||||
// Loop over the menu's actions
|
||||
QList<QAction *> actions = parent->actions();
|
||||
for (int i = 0; i < actions.size(); i++) {
|
||||
QAction *a = actions.at(i);
|
||||
|
||||
// Ignore separator actions
|
||||
if (!a->isSeparator()) {
|
||||
if (a->menu() != nullptr) {
|
||||
// If the action is a menu, run this function recursively on it
|
||||
search_update(s, menu_text, a->menu());
|
||||
|
||||
} else {
|
||||
// This is a valid non-separator non-menu action, so check it against the currently entered string.
|
||||
|
||||
// Strip out all &s from the action's name
|
||||
QString comp = a->text().replace("&", "");
|
||||
|
||||
// See if the action's name contains any of the currently entered string
|
||||
if (comp.contains(s, Qt::CaseInsensitive)) {
|
||||
// If so, we add it to the list widget.
|
||||
QListWidgetItem *item = new QListWidgetItem(
|
||||
QStringLiteral("%1\n(%2)").arg(comp, menu_text),
|
||||
list_widget_);
|
||||
|
||||
// Add a pointer to the original QAction in the item's data
|
||||
item->setData(Qt::UserRole + 1,
|
||||
reinterpret_cast<quintptr>(a));
|
||||
|
||||
list_widget_->addItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionSearch::perform_action()
|
||||
{
|
||||
// Loop over all the items in the list and if we find one that's selected, we trigger it.
|
||||
QList<QListWidgetItem *> selected_items = list_widget_->selectedItems();
|
||||
if (list_widget_->count() > 0 && selected_items.size() > 0) {
|
||||
QListWidgetItem *item = selected_items.at(0);
|
||||
|
||||
// Get QAction pointer from item's data
|
||||
QAction *a = reinterpret_cast<QAction *>(
|
||||
item->data(Qt::UserRole + 1).value<quintptr>());
|
||||
|
||||
a->trigger();
|
||||
}
|
||||
|
||||
// Close this popup
|
||||
accept();
|
||||
}
|
||||
|
||||
void ActionSearch::move_selection_up()
|
||||
{
|
||||
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
|
||||
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
|
||||
// bottom item).
|
||||
|
||||
int lim = list_widget_->count();
|
||||
for (int i = 1; i < lim; i++) {
|
||||
if (list_widget_->item(i)->isSelected()) {
|
||||
list_widget_->item(i - 1)->setSelected(true);
|
||||
list_widget_->scrollToItem(list_widget_->item(i - 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionSearch::move_selection_down()
|
||||
{
|
||||
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
|
||||
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
|
||||
// bottom item).
|
||||
|
||||
int lim = list_widget_->count() - 1;
|
||||
for (int i = 0; i < lim; i++) {
|
||||
if (list_widget_->item(i)->isSelected()) {
|
||||
list_widget_->item(i + 1)->setSelected(true);
|
||||
list_widget_->scrollToItem(list_widget_->item(i + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionSearchEntry::ActionSearchEntry(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool ActionSearchEntry::event(QEvent *e)
|
||||
{
|
||||
switch (e->type()) {
|
||||
case QEvent::ShortcutOverride:
|
||||
switch (static_cast<QKeyEvent *>(e)->key()) {
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_Down:
|
||||
e->accept();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::KeyPress:
|
||||
// Listen for up/down, otherwise pass the key event to the base class.
|
||||
switch (static_cast<QKeyEvent *>(e)->key()) {
|
||||
case Qt::Key_Up:
|
||||
e->accept();
|
||||
emit move_selection_up();
|
||||
return true;
|
||||
case Qt::Key_Down:
|
||||
e->accept();
|
||||
emit move_selection_down();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QLineEdit::event(e);
|
||||
}
|
||||
|
||||
ActionSearchList::ActionSearchList(QWidget *parent)
|
||||
: QListWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *)
|
||||
{
|
||||
// Indiscriminately emit a signal on any double click
|
||||
emit dbl_click();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_ACTIONSEARCH_H
|
||||
#define OAK_ACTIONSEARCH_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
|
||||
#include "oakutil/define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ActionSearchList;
|
||||
|
||||
/**
|
||||
* @brief The ActionSearch class
|
||||
*
|
||||
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
|
||||
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
|
||||
*/
|
||||
class ActionSearch : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief ActionSearch Constructor
|
||||
*
|
||||
* Create ActionSearch popup.
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* QWidget parent. Usually MainWindow.
|
||||
*/
|
||||
ActionSearch(QWidget *parent);
|
||||
|
||||
/**
|
||||
* @brief Set the menu bar to use in this action search
|
||||
*/
|
||||
void set_menu_bar(QMenuBar *menu_bar);
|
||||
private slots:
|
||||
/**
|
||||
* @brief Update the list of actions according to a search query
|
||||
*
|
||||
* This function adds/removes actions in the action list according to a given search query entered by the user.
|
||||
*
|
||||
* To loop over the menubar and all of its menus and submenus, this function will call itself recursively. As such
|
||||
* some of its parameters do not need to be set externally, as these will be set by the function itself as it calls
|
||||
* itself.
|
||||
*
|
||||
* @param s
|
||||
*
|
||||
* The search text. This is the only parameter that should be set externally.
|
||||
*
|
||||
* @param p
|
||||
*
|
||||
* The current parent hierarchy. In most cases, this should be left as nullptr when called externally.
|
||||
* search_update() will fill this automatically as it needs while calling itself recursively.
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* The current menu to loop over. In most cases, this should be left as nullptr when called externally.
|
||||
* search_update() will fill this automatically as it needs while calling itself recursively.
|
||||
*/
|
||||
void search_update(const QString &s, const QString &p = nullptr,
|
||||
QMenu *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Perform the currently selected action
|
||||
*
|
||||
* Usually triggered by pressing Enter on the ActionSearchEntry field, this will trigger whatever action is currently
|
||||
* highlighted and then close this popup. If no entries are highlighted (i.e. the list is empty), no action is
|
||||
* triggered and the popup closes anyway.
|
||||
*/
|
||||
void perform_action();
|
||||
|
||||
/**
|
||||
* @brief Move selection up
|
||||
*
|
||||
* A slot for pressing up on the ActionSearchEntry field. Moves the selection in the list up once. If the
|
||||
* selection is already at the top of the list, this is a no-op.
|
||||
*/
|
||||
void move_selection_up();
|
||||
|
||||
/**
|
||||
* @brief Move selection down
|
||||
*
|
||||
* A slot for pressing down on the ActionSearchEntry field. Moves the selection in the list down once. If the
|
||||
* selection is already at the bottom of the list, this is a no-op.
|
||||
*/
|
||||
void move_selection_down();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Main widget that shows the list of commands
|
||||
*/
|
||||
ActionSearchList *list_widget_;
|
||||
|
||||
/**
|
||||
* @brief Attached menu bar object
|
||||
*/
|
||||
QMenuBar *menu_bar_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The ActionSearchList class
|
||||
*
|
||||
* Simple wrapper around QListWidget that emits a signal when an item is double clicked that ActionSearch connects
|
||||
* to a slot that triggers the currently selected action.
|
||||
*/
|
||||
class ActionSearchList : public QListWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief ActionSearchList Constructor
|
||||
* @param parent
|
||||
*
|
||||
* Usually ActionSearch.
|
||||
*/
|
||||
ActionSearchList(QWidget *parent);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Override of QListWidget's double click event that emits a signal.
|
||||
*/
|
||||
void mouseDoubleClickEvent(QMouseEvent *);
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when a QListWidget item is double clicked.
|
||||
*/
|
||||
void dbl_click();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The ActionSearchEntry class
|
||||
*
|
||||
* Simple wrapper around QLineEdit that emits signals when the up or down arrow keys are pressed so that ActionSearch
|
||||
* can connect them to moving the current selection up or down.
|
||||
*/
|
||||
class ActionSearchEntry : public QLineEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief ActionSearchEntry
|
||||
* @param parent
|
||||
*
|
||||
* Usually ActionSearch.
|
||||
*/
|
||||
ActionSearchEntry(QWidget *parent);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Override of QLineEdit's key press event that listens for up/down key presses.
|
||||
* @param event
|
||||
*/
|
||||
virtual bool event(QEvent *e) override;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Emitted when the user presses the up arrow key.
|
||||
*/
|
||||
void move_selection_up();
|
||||
|
||||
/**
|
||||
* @brief Emitted when the user presses the down arrow key.
|
||||
*/
|
||||
void move_selection_down();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_ACTIONSEARCH_H
|
||||
@@ -1,22 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/autorecovery/autorecoverydialog.h
|
||||
dialog/autorecovery/autorecoverydialog.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,157 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "autorecoverydialog.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDir>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "core.h"
|
||||
#include "oakutil/filefunctions.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super QDialog
|
||||
|
||||
AutoRecoveryDialog::AutoRecoveryDialog(const QString &message,
|
||||
const QStringList &recoveries,
|
||||
bool autocheck_latest, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
init(message);
|
||||
|
||||
populate_tree(recoveries, autocheck_latest);
|
||||
}
|
||||
|
||||
void AutoRecoveryDialog::accept()
|
||||
{
|
||||
foreach (QTreeWidgetItem *checkable, checkable_items_) {
|
||||
if (checkable->checkState(0) == Qt::Checked) {
|
||||
QString filename = checkable->data(0, k_filename_role).toString();
|
||||
Core::instance()->open_recovery_project(filename);
|
||||
}
|
||||
}
|
||||
|
||||
super::accept();
|
||||
}
|
||||
|
||||
void AutoRecoveryDialog::init(const QString &header_text)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
setWindowTitle(tr("Auto-Recovery"));
|
||||
|
||||
layout->addWidget(new QLabel(header_text));
|
||||
|
||||
tree_widget_ = new QTreeWidget();
|
||||
tree_widget_->setHeaderHidden(true);
|
||||
layout->addWidget(tree_widget_);
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttons->button(QDialogButtonBox::Ok)->setText(tr("Load"));
|
||||
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||
&AutoRecoveryDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||
&AutoRecoveryDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
}
|
||||
|
||||
void AutoRecoveryDialog::populate_tree(const QStringList &recoveries,
|
||||
bool autocheck_latest)
|
||||
{
|
||||
// Each entry in `recoveries` is a directory with 1+ recovery projects in it
|
||||
QDir autorecovery_root(FileFunctions::get_auto_recovery_root());
|
||||
|
||||
foreach (const QString &recovery_folder, recoveries) {
|
||||
QDir recovery_dir(autorecovery_root.filePath(recovery_folder));
|
||||
|
||||
QString pretty_name;
|
||||
{
|
||||
// Retrieve pretty name
|
||||
QFile pretty_name_file(
|
||||
recovery_dir.filePath(QStringLiteral("realname.txt")));
|
||||
if (pretty_name_file.open(QFile::ReadOnly)) {
|
||||
// Read pretty name that we should have written in the autorecovery process
|
||||
pretty_name = QString::fromUtf8(pretty_name_file.readAll());
|
||||
pretty_name_file.close();
|
||||
}
|
||||
|
||||
if (pretty_name.isEmpty()) {
|
||||
// Fallback to just the UUID. While it won't mean much to the user, it's better than nothing.
|
||||
pretty_name = recovery_dir.dirName();
|
||||
}
|
||||
}
|
||||
|
||||
QTreeWidgetItem *top_level = new QTreeWidgetItem(tree_widget_);
|
||||
top_level->setText(0, pretty_name);
|
||||
|
||||
{
|
||||
// Populate with recoveries
|
||||
QStringList entries =
|
||||
recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot,
|
||||
QDir::Name | QDir::Reversed);
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
const QString &entry = entries.at(i);
|
||||
|
||||
if (entry.endsWith(QStringLiteral(".ove"),
|
||||
Qt::CaseInsensitive)) {
|
||||
QTreeWidgetItem *entry_item =
|
||||
new QTreeWidgetItem(top_level);
|
||||
|
||||
bool ok;
|
||||
qint64 recovery_time =
|
||||
entry.left(entry.indexOf('.')).toLongLong(&ok);
|
||||
QString entry_name;
|
||||
|
||||
if (ok) {
|
||||
// Set as time/date of recovery
|
||||
entry_name =
|
||||
QDateTime::fromSecsSinceEpoch(recovery_time)
|
||||
.toString();
|
||||
} else {
|
||||
// Fallback if we couldn't discern a date from this
|
||||
entry_name = entry;
|
||||
}
|
||||
|
||||
entry_item->setText(0, entry_name);
|
||||
entry_item->setData(0, k_filename_role,
|
||||
recovery_dir.filePath(entry));
|
||||
|
||||
// Allow to be checked, auto-checking the first entry
|
||||
entry_item->setCheckState(
|
||||
0, (autocheck_latest && top_level->childCount() == 1) ?
|
||||
Qt::Checked :
|
||||
Qt::Unchecked);
|
||||
|
||||
checkable_items_.append(entry_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_AUTORECOVERYDIALOG_H
|
||||
#define OAK_AUTORECOVERYDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTreeWidget>
|
||||
|
||||
#include "oakutil/define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class AutoRecoveryDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AutoRecoveryDialog(const QString &message, const QStringList &recoveries,
|
||||
bool autocheck_latest, QWidget *parent);
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
void init(const QString &header_text);
|
||||
|
||||
void populate_tree(const QStringList &recoveries, bool autocheck);
|
||||
|
||||
QTreeWidget *tree_widget_;
|
||||
|
||||
QVector<QTreeWidgetItem *> checkable_items_;
|
||||
|
||||
enum DataRole { k_filename_role = Qt::UserRole };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_AUTORECOVERYDIALOG_H
|
||||
@@ -1,22 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/color/colordialog.h
|
||||
dialog/color/colordialog.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,252 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "colordialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QSplitter>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "oakutil/qtutils.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ColorDialog::ColorDialog(OakEngineColorManager *color_manager, const ManagedColor &start,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, color_manager_(color_manager)
|
||||
{
|
||||
setWindowTitle(tr("Select Color"));
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
QSplitter *splitter = new QSplitter(Qt::Horizontal);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
|
||||
QWidget *graphics_area = new QWidget();
|
||||
splitter->addWidget(graphics_area);
|
||||
|
||||
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
|
||||
|
||||
QHBoxLayout *wheel_layout = new QHBoxLayout();
|
||||
graphics_layout->addLayout(wheel_layout);
|
||||
|
||||
color_wheel_ = new ColorWheelWidget();
|
||||
wheel_layout->addWidget(color_wheel_);
|
||||
|
||||
hsv_value_gradient_ = new ColorGradientWidget(Qt::Vertical);
|
||||
hsv_value_gradient_->setFixedWidth(
|
||||
QtUtils::q_font_metrics_width(fontMetrics(), QStringLiteral("HHH")));
|
||||
wheel_layout->addWidget(hsv_value_gradient_);
|
||||
|
||||
QHBoxLayout *swatch_layout = new QHBoxLayout();
|
||||
graphics_layout->addLayout(swatch_layout);
|
||||
|
||||
swatch_layout->addStretch();
|
||||
|
||||
swatch_ = new ColorSwatchChooser(color_manager_);
|
||||
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
swatch_layout->addWidget(swatch_);
|
||||
|
||||
swatch_layout->addStretch();
|
||||
|
||||
QWidget *value_area = new QWidget();
|
||||
QVBoxLayout *value_layout = new QVBoxLayout(value_area);
|
||||
value_layout->setSpacing(0);
|
||||
splitter->addWidget(value_area);
|
||||
|
||||
color_values_widget_ = new ColorValuesWidget(color_manager_);
|
||||
color_values_widget_->ignore_pick_from(this);
|
||||
value_layout->addWidget(color_values_widget_);
|
||||
|
||||
chooser_ = new ColorSpaceChooser(color_manager_);
|
||||
|
||||
value_layout->addWidget(chooser_);
|
||||
|
||||
// Split window 50/50
|
||||
splitter->setSizes({ INT_MAX, INT_MAX });
|
||||
|
||||
connect(color_wheel_, &ColorWheelWidget::selected_color_changed,
|
||||
color_values_widget_, &ColorValuesWidget::set_color);
|
||||
connect(color_wheel_, &ColorWheelWidget::selected_color_changed,
|
||||
hsv_value_gradient_, &ColorGradientWidget::set_selected_color);
|
||||
connect(color_wheel_, &ColorWheelWidget::selected_color_changed, swatch_,
|
||||
&ColorSwatchChooser::set_current_color);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::selected_color_changed,
|
||||
color_values_widget_, &ColorValuesWidget::set_color);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::selected_color_changed,
|
||||
color_wheel_, &ColorWheelWidget::set_selected_color);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::selected_color_changed,
|
||||
swatch_, &ColorSwatchChooser::set_current_color);
|
||||
connect(color_values_widget_, &ColorValuesWidget::color_changed,
|
||||
hsv_value_gradient_, &ColorGradientWidget::set_selected_color);
|
||||
connect(color_values_widget_, &ColorValuesWidget::color_changed,
|
||||
color_wheel_, &ColorWheelWidget::set_selected_color);
|
||||
connect(color_values_widget_, &ColorValuesWidget::color_changed, swatch_,
|
||||
&ColorSwatchChooser::set_current_color);
|
||||
connect(swatch_, &ColorSwatchChooser::color_clicked, hsv_value_gradient_,
|
||||
&ColorGradientWidget::set_selected_color);
|
||||
connect(swatch_, &ColorSwatchChooser::color_clicked, color_wheel_,
|
||||
&ColorWheelWidget::set_selected_color);
|
||||
connect(swatch_, &ColorSwatchChooser::color_clicked, color_values_widget_,
|
||||
&ColorValuesWidget::set_color);
|
||||
|
||||
connect(color_wheel_, &ColorWheelWidget::diameter_changed,
|
||||
hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
set_color(start);
|
||||
|
||||
connect(chooser_, &ColorSpaceChooser::color_space_changed, this,
|
||||
&ColorDialog::color_space_changed);
|
||||
color_space_changed(chooser_->input(), chooser_->output());
|
||||
|
||||
// Set default size ratio to 2:1
|
||||
resize(sizeHint().height() * 2, sizeHint().height());
|
||||
}
|
||||
|
||||
void ColorDialog::set_color(const ManagedColor &start)
|
||||
{
|
||||
chooser_->set_input(start.color_input());
|
||||
chooser_->set_output(start.color_output());
|
||||
|
||||
Color managed_start;
|
||||
|
||||
if (start.color_input().isEmpty()) {
|
||||
managed_start = start;
|
||||
|
||||
} else {
|
||||
// Convert reference color to the input space
|
||||
QByteArray ref_cs = oak_query_string([this](char *buf, int size) {
|
||||
return oakengine_color_manager_reference_color_space(
|
||||
color_manager_, buf, size);
|
||||
}).toUtf8();
|
||||
QByteArray in_cs = start.color_input().toUtf8();
|
||||
oak_color_transform in_pod;
|
||||
in_pod.is_display = 0;
|
||||
in_pod.output = in_cs.constData();
|
||||
in_pod.view = nullptr;
|
||||
in_pod.look = nullptr;
|
||||
ColorProcessorHandlePtr linear_to_input(
|
||||
oakengine_color_processor_create(color_manager_, ref_cs.constData(),
|
||||
&in_pod,
|
||||
OAKENGINE_COLOR_PROCESSOR_NORMAL),
|
||||
ColorProcessorHandleDeleter());
|
||||
|
||||
managed_start = oak_convert_color(linear_to_input, start);
|
||||
}
|
||||
|
||||
color_wheel_->set_selected_color(managed_start);
|
||||
hsv_value_gradient_->set_selected_color(managed_start);
|
||||
color_values_widget_->set_color(managed_start);
|
||||
swatch_->set_current_color(managed_start);
|
||||
}
|
||||
|
||||
ManagedColor ColorDialog::get_selected_color() const
|
||||
{
|
||||
ManagedColor selected = color_wheel_->get_selected_color();
|
||||
|
||||
// Convert to linear and return a linear color
|
||||
if (input_to_ref_processor_) {
|
||||
selected = oak_convert_color(input_to_ref_processor_, selected);
|
||||
}
|
||||
|
||||
selected.set_color_input(get_color_space_input());
|
||||
selected.set_color_output(get_color_space_output());
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
QString ColorDialog::get_color_space_input() const
|
||||
{
|
||||
return chooser_->input();
|
||||
}
|
||||
|
||||
oak::ColorTransform ColorDialog::get_color_space_output() const
|
||||
{
|
||||
return chooser_->output();
|
||||
}
|
||||
|
||||
void ColorDialog::color_space_changed(const QString &input,
|
||||
const oak::ColorTransform &output)
|
||||
{
|
||||
QByteArray ref_cs = oak_query_string([this](char *buf, int size) {
|
||||
return oakengine_color_manager_reference_color_space(
|
||||
color_manager_, buf, size);
|
||||
}).toUtf8();
|
||||
QByteArray in = input.toUtf8();
|
||||
QByteArray o, v, l;
|
||||
oak_color_transform out_pod = oak_to_transform(output, &o, &v, &l);
|
||||
|
||||
auto make_proc = [&](const char *input_cs, const oak_color_transform *dest,
|
||||
int dir) -> ColorProcessorHandlePtr {
|
||||
return ColorProcessorHandlePtr(
|
||||
oakengine_color_processor_create(color_manager_, input_cs, dest,
|
||||
dir),
|
||||
ColorProcessorHandleDeleter());
|
||||
};
|
||||
|
||||
input_to_ref_processor_ = make_proc(in.constData(), &out_pod,
|
||||
OAKENGINE_COLOR_PROCESSOR_NORMAL);
|
||||
|
||||
oak_color_transform ref_display_pod;
|
||||
ref_display_pod.is_display = out_pod.is_display;
|
||||
ref_display_pod.output = out_pod.output;
|
||||
ref_display_pod.view = out_pod.view;
|
||||
ref_display_pod.look = out_pod.look;
|
||||
ColorProcessorHandlePtr ref_to_display = make_proc(
|
||||
ref_cs.constData(), &ref_display_pod,
|
||||
OAKENGINE_COLOR_PROCESSOR_NORMAL);
|
||||
|
||||
oak_color_transform ref_input_pod;
|
||||
ref_input_pod.is_display = 0;
|
||||
ref_input_pod.output = in.constData();
|
||||
ref_input_pod.view = nullptr;
|
||||
ref_input_pod.look = nullptr;
|
||||
ColorProcessorHandlePtr ref_to_input = make_proc(
|
||||
ref_cs.constData(), &ref_input_pod,
|
||||
OAKENGINE_COLOR_PROCESSOR_NORMAL);
|
||||
|
||||
// Display -> reference is the inverse of the display transform. Older OCIO
|
||||
// versions crashed on TRANSFORM_DIR_INVERSE; guard by requiring a valid
|
||||
// processor and fall back to disabling the display tab if creation fails.
|
||||
ColorProcessorHandlePtr display_to_ref = make_proc(
|
||||
ref_cs.constData(), &ref_display_pod,
|
||||
OAKENGINE_COLOR_PROCESSOR_INVERSE);
|
||||
if (display_to_ref && !oakengine_color_processor_is_valid(display_to_ref.get())) {
|
||||
display_to_ref = nullptr;
|
||||
}
|
||||
|
||||
color_wheel_->set_color_processor(input_to_ref_processor_, ref_to_display);
|
||||
hsv_value_gradient_->set_color_processor(input_to_ref_processor_,
|
||||
ref_to_display);
|
||||
color_values_widget_->set_color_processor(
|
||||
input_to_ref_processor_, ref_to_display, display_to_ref, ref_to_input);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_COLORDIALOG_H
|
||||
#define OAK_COLORDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "oakengine/color.h"
|
||||
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||
#include "widget/colorwheel/colorgradientwidget.h"
|
||||
#include "widget/colorwheel/colorspacechooser.h"
|
||||
#include "widget/colorwheel/colorswatchchooser.h"
|
||||
#include "widget/colorwheel/colorvalueswidget.h"
|
||||
#include "widget/colorwheel/colorwheelwidget.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ColorDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief ColorDialog Constructor
|
||||
*
|
||||
* @param color_manager
|
||||
*
|
||||
* The ColorManager to use for color management. This must be valid.
|
||||
*
|
||||
* @param start
|
||||
*
|
||||
* The color to start with. This must be in the color_manager's reference space
|
||||
*
|
||||
* @param input_cs
|
||||
*
|
||||
* The input range that the user should see. The start color will be converted to this for UI object.
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* QWidget parent.
|
||||
*/
|
||||
ColorDialog(OakEngineColorManager *color_manager,
|
||||
const ManagedColor &start = Color(1.0f, 1.0f, 1.0f),
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the color selected by the user
|
||||
*
|
||||
* The color is always returned in the ColorManager's reference space (usually scene linear).
|
||||
*/
|
||||
ManagedColor get_selected_color() const;
|
||||
|
||||
QString get_color_space_input() const;
|
||||
|
||||
oak::ColorTransform get_color_space_output() const;
|
||||
|
||||
public slots:
|
||||
void set_color(const ManagedColor &c);
|
||||
|
||||
private:
|
||||
OakEngineColorManager *color_manager_;
|
||||
|
||||
ColorWheelWidget *color_wheel_;
|
||||
|
||||
ColorValuesWidget *color_values_widget_;
|
||||
|
||||
ColorGradientWidget *hsv_value_gradient_;
|
||||
|
||||
ColorProcessorHandlePtr input_to_ref_processor_;
|
||||
|
||||
ColorSpaceChooser *chooser_;
|
||||
|
||||
ColorSwatchChooser *swatch_;
|
||||
|
||||
private slots:
|
||||
void color_space_changed(const QString &input, const oak::ColorTransform &output);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_COLORDIALOG_H
|
||||
@@ -1,24 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/configbase/configdialogbase.cpp
|
||||
dialog/configbase/configdialogbase.h
|
||||
dialog/configbase/configdialogbasetab.cpp
|
||||
dialog/configbase/configdialogbasetab.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,102 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "configdialogbase.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QSplitter>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#include "oakengine/undo.h"
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ConfigDialogBase::ConfigDialogBase(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
QSplitter *splitter = new QSplitter();
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
|
||||
list_widget_ = new QListWidget();
|
||||
|
||||
preference_pane_stack_ = new QStackedWidget(this);
|
||||
|
||||
splitter->addWidget(list_widget_);
|
||||
splitter->addWidget(preference_pane_stack_);
|
||||
|
||||
QDialogButtonBox *button_box = new QDialogButtonBox(this);
|
||||
button_box->setOrientation(Qt::Horizontal);
|
||||
button_box->setStandardButtons(QDialogButtonBox::Cancel |
|
||||
QDialogButtonBox::Ok);
|
||||
|
||||
layout->addWidget(button_box);
|
||||
|
||||
connect(button_box, &QDialogButtonBox::accepted, this,
|
||||
&ConfigDialogBase::accept);
|
||||
connect(button_box, &QDialogButtonBox::rejected, this,
|
||||
&ConfigDialogBase::reject);
|
||||
|
||||
connect(list_widget_, &QListWidget::currentRowChanged,
|
||||
preference_pane_stack_, &QStackedWidget::setCurrentIndex);
|
||||
}
|
||||
|
||||
void ConfigDialogBase::accept()
|
||||
{
|
||||
foreach (ConfigDialogBaseTab *tab, tabs_) {
|
||||
if (!tab->validate()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void *command = oakengine_undo_command_create_multi();
|
||||
|
||||
foreach (ConfigDialogBaseTab *tab, tabs_) {
|
||||
tab->accept(command);
|
||||
}
|
||||
|
||||
oakengine_undo_push(command, tr("Set Configuration").toUtf8().constData());
|
||||
|
||||
AcceptEvent();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void ConfigDialogBase::add_tab(ConfigDialogBaseTab *tab, const QString &title)
|
||||
{
|
||||
list_widget_->addItem(title);
|
||||
preference_pane_stack_->addWidget(tab);
|
||||
|
||||
tabs_.append(tab);
|
||||
}
|
||||
|
||||
void ConfigDialogBase::set_current_tab(int index)
|
||||
{
|
||||
if (index >= 0 && index < list_widget_->count()) {
|
||||
list_widget_->setCurrentRow(index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CONFIGBASE_H
|
||||
#define OAK_CONFIGBASE_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QListWidget>
|
||||
#include <QStackedWidget>
|
||||
|
||||
#include "configdialogbasetab.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ConfigDialogBase : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConfigDialogBase(QWidget *parent = nullptr);
|
||||
|
||||
void set_current_tab(int index);
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Override of accept to save preferences to Config.
|
||||
*/
|
||||
virtual void accept() override;
|
||||
|
||||
protected:
|
||||
void add_tab(ConfigDialogBaseTab *tab, const QString &title);
|
||||
|
||||
virtual void AcceptEvent()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
QListWidget *list_widget_;
|
||||
|
||||
QStackedWidget *preference_pane_stack_;
|
||||
|
||||
QList<ConfigDialogBaseTab *> tabs_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_CONFIGBASE_H
|
||||
@@ -1,32 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "configdialogbasetab.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
bool ConfigDialogBaseTab::validate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_PREFERENCESTAB_H
|
||||
#define OAK_PREFERENCESTAB_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "common/configwrapper.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ConfigDialogBaseTab : public QWidget {
|
||||
public:
|
||||
ConfigDialogBaseTab() = default;
|
||||
|
||||
virtual bool validate();
|
||||
|
||||
virtual void accept(void *parent) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_PREFERENCESTAB_H
|
||||
@@ -1,22 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/diskcache/diskcachedialog.h
|
||||
dialog/diskcache/diskcachedialog.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,151 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "diskcachedialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "oakengine/disk.h"
|
||||
#include "oakutil/define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/// DiskCacheFolder::get_path() through the C ABI (buf/size convention)
|
||||
QString disk_folder_path(const void *folder)
|
||||
{
|
||||
char buf[1024];
|
||||
buf[0] = '\0';
|
||||
oakengine_disk_folder_get_path(folder, buf, sizeof(buf));
|
||||
return QString::fromUtf8(buf);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DiskCacheDialog::DiskCacheDialog(void *folder, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, folder_(folder)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(
|
||||
new QLabel(tr("Disk Cache: %1").arg(disk_folder_path(folder))), row,
|
||||
0, 1, 2);
|
||||
setWindowTitle(tr("Disk Cache Settings"));
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0);
|
||||
|
||||
maximum_cache_slider_ = new FloatSlider();
|
||||
maximum_cache_slider_->set_format(tr("%1 GB"));
|
||||
maximum_cache_slider_->set_minimum(1.0);
|
||||
// The folder limit is a byte count; the slider works in GB
|
||||
maximum_cache_slider_->set_value(oakengine_disk_folder_get_limit(folder) /
|
||||
static_cast<double>(k_bytes_in_gigabyte));
|
||||
layout->addWidget(maximum_cache_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
clear_cache_btn_ = new QPushButton(tr("Clear Disk Cache"));
|
||||
connect(clear_cache_btn_, &QPushButton::clicked, this,
|
||||
static_cast<void (DiskCacheDialog::*)()>(
|
||||
&DiskCacheDialog::clear_disk_cache));
|
||||
layout->addWidget(clear_cache_btn_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
clear_disk_cache_ =
|
||||
new QCheckBox(tr("Automatically clear disk cache on close"));
|
||||
clear_disk_cache_->setChecked(
|
||||
oakengine_disk_folder_get_clear_on_close(folder) != 0);
|
||||
layout->addWidget(clear_disk_cache_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||
&DiskCacheDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||
&DiskCacheDialog::reject);
|
||||
layout->addWidget(buttons, row, 0, 1, 2);
|
||||
}
|
||||
|
||||
void DiskCacheDialog::accept()
|
||||
{
|
||||
qint64 new_disk_cache_limit =
|
||||
qRound64(maximum_cache_slider_->get_value() * k_bytes_in_gigabyte);
|
||||
if (static_cast<double>(new_disk_cache_limit) !=
|
||||
oakengine_disk_folder_get_limit(folder_)) {
|
||||
oakengine_disk_folder_set_limit(
|
||||
folder_, static_cast<double>(new_disk_cache_limit));
|
||||
}
|
||||
|
||||
const bool clear_on_close = clear_disk_cache_->isChecked();
|
||||
if ((oakengine_disk_folder_get_clear_on_close(folder_) != 0) !=
|
||||
clear_on_close) {
|
||||
oakengine_disk_folder_set_clear_on_close(folder_,
|
||||
clear_on_close ? 1 : 0);
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void DiskCacheDialog::clear_disk_cache()
|
||||
{
|
||||
clear_disk_cache(disk_folder_path(folder_), this, clear_cache_btn_);
|
||||
}
|
||||
|
||||
void DiskCacheDialog::clear_disk_cache(const QString &path, QWidget *parent,
|
||||
QPushButton *clear_btn)
|
||||
{
|
||||
if (QMessageBox::question(
|
||||
parent, tr("Clear Disk Cache"),
|
||||
tr("Are you sure you want to clear the disk cache in '%1'?")
|
||||
.arg(path),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
if (clear_btn)
|
||||
clear_btn->setEnabled(false);
|
||||
|
||||
if (oakengine_disk_clear_cache(path.toUtf8().constData())) {
|
||||
if (clear_btn)
|
||||
clear_btn->setText(tr("Disk Cache Cleared"));
|
||||
} else {
|
||||
QMessageBox::information(
|
||||
parent, tr("Clear Disk Cache"),
|
||||
tr("Disk cache failed to fully clear. You may have to delete the cache files manually."),
|
||||
QMessageBox::Ok);
|
||||
if (clear_btn)
|
||||
clear_btn->setText(tr("Disk Cache Partially Cleared"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_DISKCACHEDIALOG_H
|
||||
#define OAK_DISKCACHEDIALOG_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class DiskCacheDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @param folder Opaque engine DiskCacheFolder handle (from
|
||||
* oakengine_disk_get_open_folder()), accessed through the
|
||||
* oakengine_disk_folder_* C ABI.
|
||||
*/
|
||||
DiskCacheDialog(void *folder, QWidget *parent = nullptr);
|
||||
|
||||
static void clear_disk_cache(const QString &path, QWidget *parent,
|
||||
QPushButton *clear_btn = nullptr);
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
void *folder_;
|
||||
|
||||
FloatSlider *maximum_cache_slider_;
|
||||
|
||||
QCheckBox *clear_disk_cache_;
|
||||
|
||||
QPushButton *clear_cache_btn_;
|
||||
|
||||
private slots:
|
||||
void clear_disk_cache();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_DISKCACHEDIALOG_H
|
||||
@@ -1,36 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(codec)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/export/export.cpp
|
||||
dialog/export/export.h
|
||||
dialog/export/exportadvancedvideodialog.cpp
|
||||
dialog/export/exportadvancedvideodialog.h
|
||||
dialog/export/exportaudiotab.cpp
|
||||
dialog/export/exportaudiotab.h
|
||||
dialog/export/exportformatcombobox.cpp
|
||||
dialog/export/exportformatcombobox.h
|
||||
dialog/export/exportsavepresetdialog.cpp
|
||||
dialog/export/exportsavepresetdialog.h
|
||||
dialog/export/exportsubtitlestab.cpp
|
||||
dialog/export/exportsubtitlestab.h
|
||||
dialog/export/exportvideotab.cpp
|
||||
dialog/export/exportvideotab.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,32 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/export/codec/av1section.cpp
|
||||
dialog/export/codec/av1section.h
|
||||
dialog/export/codec/cineformsection.cpp
|
||||
dialog/export/codec/cineformsection.h
|
||||
dialog/export/codec/codecsection.cpp
|
||||
dialog/export/codec/codecsection.h
|
||||
dialog/export/codec/codecstack.cpp
|
||||
dialog/export/codec/codecstack.h
|
||||
dialog/export/codec/h264section.cpp
|
||||
dialog/export/codec/h264section.h
|
||||
dialog/export/codec/imagesection.cpp
|
||||
dialog/export/codec/imagesection.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,141 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "av1section.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "oakutil/qtutils.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
AV1Section::AV1Section(QWidget *parent)
|
||||
: AV1Section(AV1CRFSection::k_default_a_v1_crf, parent)
|
||||
{
|
||||
}
|
||||
|
||||
AV1Section::AV1Section(int default_crf, QWidget *parent)
|
||||
: CodecSection(parent)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
int row = 0;
|
||||
layout->addWidget(new QLabel(tr("Preset:")), row, 0);
|
||||
|
||||
preset_combobox_ = new QComboBox();
|
||||
preset_combobox_->setToolTip(tr(
|
||||
"This parameter governs the efficiency/encode-time trade-off.\n"
|
||||
"Lower presets will result in an output with better quality for a given file size, but will take longer to encode.\n"
|
||||
"Higher presets can result in a very fast encode, but will make some compromises on visual quality for a given crf value."));
|
||||
|
||||
for (int i = 0; i <= 13; i++)
|
||||
preset_combobox_->addItem(QString::number(i));
|
||||
|
||||
preset_combobox_->setCurrentIndex(8);
|
||||
|
||||
layout->addWidget(preset_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
|
||||
|
||||
QComboBox *compression_box = new QComboBox();
|
||||
compression_box->setToolTip(tr(
|
||||
"This parameter governs the quality/size trade-off.\n"
|
||||
"Higher CRF values will result in a final output that takes less space, but begins to lose detail.\n"
|
||||
"Lower CRF values retain more detail at the cost of larger file sizes.\n"
|
||||
"The possible range of CRF in SVT-AV1 is 1-63."));
|
||||
|
||||
// These items must correspond to the CompressionMethod enum
|
||||
compression_box->addItem(tr("Constant Rate Factor"));
|
||||
|
||||
layout->addWidget(compression_box, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
compression_method_stack_ = new QStackedWidget();
|
||||
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
|
||||
|
||||
crf_section_ = new AV1CRFSection(default_crf);
|
||||
compression_method_stack_->addWidget(crf_section_);
|
||||
|
||||
connect(
|
||||
compression_box,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
compression_method_stack_, &QStackedWidget::setCurrentIndex);
|
||||
}
|
||||
|
||||
void AV1Section::add_opts(OakEngineEncodingParams *params)
|
||||
{
|
||||
CompressionMethod method = static_cast<CompressionMethod>(
|
||||
compression_method_stack_->currentIndex());
|
||||
|
||||
if (method == k_constant_rate_factor) {
|
||||
// Set Quantizer value
|
||||
oakengine_encoding_params_set_video_option(
|
||||
params, "qp",
|
||||
QByteArray::number(crf_section_->get_value()).constData());
|
||||
}
|
||||
|
||||
oakengine_encoding_params_set_video_option(
|
||||
params, "preset",
|
||||
QByteArray::number(preset_combobox_->currentIndex()).constData());
|
||||
}
|
||||
|
||||
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
crf_slider_ = new QSlider(Qt::Horizontal);
|
||||
crf_slider_->setMinimum(k_minimum_crf);
|
||||
crf_slider_->setMaximum(k_maximum_crf);
|
||||
crf_slider_->setValue(default_crf);
|
||||
layout->addWidget(crf_slider_);
|
||||
|
||||
IntegerSlider *crf_input = new IntegerSlider();
|
||||
crf_input->setMaximumWidth(QtUtils::q_font_metrics_width(
|
||||
crf_input->fontMetrics(), QStringLiteral("HHHH")));
|
||||
crf_input->set_minimum(k_minimum_crf);
|
||||
crf_input->set_maximum(k_maximum_crf);
|
||||
crf_input->set_value(default_crf);
|
||||
crf_input->SetDefaultValue(default_crf);
|
||||
layout->addWidget(crf_input);
|
||||
|
||||
connect(crf_slider_, &QSlider::valueChanged, crf_input,
|
||||
&IntegerSlider::set_value);
|
||||
connect(crf_input, &IntegerSlider::value_changed, crf_slider_,
|
||||
&QSlider::setValue);
|
||||
}
|
||||
|
||||
int AV1CRFSection::get_value() const
|
||||
{
|
||||
return crf_slider_->value();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_AV1SECTION_H
|
||||
#define OAK_AV1SECTION_H
|
||||
|
||||
#include <QSlider>
|
||||
#include <QStackedWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "codecsection.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class AV1CRFSection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AV1CRFSection(int default_crf, QWidget *parent = nullptr);
|
||||
|
||||
int get_value() const;
|
||||
|
||||
static const int k_default_a_v1_crf = 30;
|
||||
|
||||
private:
|
||||
static const int k_minimum_crf = 0;
|
||||
static const int k_maximum_crf = 63;
|
||||
|
||||
QSlider *crf_slider_;
|
||||
};
|
||||
|
||||
class AV1Section : public CodecSection {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum CompressionMethod {
|
||||
k_constant_rate_factor,
|
||||
};
|
||||
|
||||
AV1Section(QWidget *parent = nullptr);
|
||||
AV1Section(int default_crf, QWidget *parent);
|
||||
|
||||
virtual void add_opts(OakEngineEncodingParams *params) override;
|
||||
|
||||
private:
|
||||
QStackedWidget *compression_method_stack_;
|
||||
|
||||
AV1CRFSection *crf_section_;
|
||||
|
||||
QComboBox *preset_combobox_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_AV1SECTION_H
|
||||
@@ -1,99 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "cineformsection.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
CineformSection::CineformSection(QWidget *parent)
|
||||
: CodecSection(parent)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
|
||||
|
||||
quality_combobox_ = new QComboBox();
|
||||
|
||||
/* Correspond to the following indexes for FFmpeg
|
||||
*
|
||||
* -quality <int> E..V....... set quality (from 0 to 12) (default film3+)
|
||||
* film3+ 0 E..V.......
|
||||
* film3 1 E..V.......
|
||||
* film2+ 2 E..V.......
|
||||
* film2 3 E..V.......
|
||||
* film1.5 4 E..V.......
|
||||
* film1+ 5 E..V.......
|
||||
* film1 6 E..V.......
|
||||
* high+ 7 E..V.......
|
||||
* high 8 E..V.......
|
||||
* medium+ 9 E..V.......
|
||||
* medium 10 E..V.......
|
||||
* low+ 11 E..V.......
|
||||
* low 12 E..V.......
|
||||
*
|
||||
*/
|
||||
|
||||
quality_combobox_->addItem(tr("Film Scan 3+"));
|
||||
quality_combobox_->addItem(tr("Film Scan 3"));
|
||||
quality_combobox_->addItem(tr("Film Scan 2+"));
|
||||
quality_combobox_->addItem(tr("Film Scan 2"));
|
||||
quality_combobox_->addItem(tr("Film Scan 1.5"));
|
||||
quality_combobox_->addItem(tr("Film Scan 1+"));
|
||||
quality_combobox_->addItem(tr("Film Scan 1"));
|
||||
quality_combobox_->addItem(tr("High+"));
|
||||
quality_combobox_->addItem(tr("High"));
|
||||
quality_combobox_->addItem(tr("Medium+"));
|
||||
quality_combobox_->addItem(tr("Medium"));
|
||||
quality_combobox_->addItem(tr("Low+"));
|
||||
quality_combobox_->addItem(tr("Low"));
|
||||
|
||||
// Default to "medium"
|
||||
quality_combobox_->setCurrentIndex(10);
|
||||
|
||||
layout->addWidget(quality_combobox_, row, 1);
|
||||
}
|
||||
|
||||
void CineformSection::add_opts(OakEngineEncodingParams *params)
|
||||
{
|
||||
oakengine_encoding_params_set_video_option(
|
||||
params, "quality",
|
||||
QByteArray::number(quality_combobox_->currentIndex()).constData());
|
||||
}
|
||||
|
||||
void CineformSection::set_opts(const OakEngineEncodingParams *p)
|
||||
{
|
||||
char buf[64];
|
||||
const int ret = oakengine_encoding_params_video_option(
|
||||
p, "quality", buf, static_cast<int>(sizeof(buf)));
|
||||
if (ret > 0) {
|
||||
quality_combobox_->setCurrentIndex(QString::fromUtf8(buf).toInt());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CINEFORMSECTION_H
|
||||
#define OAK_CINEFORMSECTION_H
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
#include "codecsection.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CineformSection : public CodecSection {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CineformSection(QWidget *parent = nullptr);
|
||||
|
||||
virtual void add_opts(OakEngineEncodingParams *params) override;
|
||||
|
||||
virtual void set_opts(const OakEngineEncodingParams *p) override;
|
||||
|
||||
private:
|
||||
QComboBox *quality_combobox_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_CINEFORMSECTION_H
|
||||
@@ -1,32 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "codecsection.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
CodecSection::CodecSection(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CODECSECTION_H
|
||||
#define OAK_CODECSECTION_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "oakengine/encoding.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CodecSection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CodecSection(QWidget *parent = nullptr);
|
||||
|
||||
virtual void add_opts(OakEngineEncodingParams *params)
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
}
|
||||
|
||||
virtual void set_opts(const OakEngineEncodingParams *p)
|
||||
{
|
||||
Q_UNUSED(p)
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_CODECSECTION_H
|
||||
@@ -1,57 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "codecstack.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super QStackedWidget
|
||||
|
||||
CodecStack::CodecStack(QWidget *parent)
|
||||
: super{ parent }
|
||||
{
|
||||
connect(this, &CodecStack::currentChanged, this, &CodecStack::on_change);
|
||||
}
|
||||
|
||||
void CodecStack::addWidget(QWidget *widget)
|
||||
{
|
||||
super::addWidget(widget);
|
||||
|
||||
on_change(currentIndex());
|
||||
}
|
||||
|
||||
void CodecStack::on_change(int index)
|
||||
{
|
||||
for (int i = 0; i < count(); i++) {
|
||||
if (i == index) {
|
||||
widget(i)->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Expanding);
|
||||
} else {
|
||||
widget(i)->setSizePolicy(QSizePolicy::Ignored,
|
||||
QSizePolicy::Ignored);
|
||||
}
|
||||
widget(i)->adjustSize();
|
||||
}
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CODECSTACK_H
|
||||
#define OAK_CODECSTACK_H
|
||||
|
||||
#include <QStackedWidget>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CodecStack : public QStackedWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CodecStack(QWidget *parent = nullptr);
|
||||
|
||||
void addWidget(QWidget *widget);
|
||||
|
||||
signals:
|
||||
|
||||
private slots:
|
||||
void on_change(int index);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_CODECSTACK_H
|
||||
@@ -1,336 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "h264section.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "oakutil/qtutils.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
H264Section::H264Section(QWidget *parent)
|
||||
: H264Section(H264CRFSection::k_default_h264_crf, parent)
|
||||
{
|
||||
}
|
||||
|
||||
H264Section::H264Section(int default_crf, QWidget *parent)
|
||||
: CodecSection(parent)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
int row = 0;
|
||||
layout->addWidget(new QLabel(tr("Encode Speed:")), row, 0);
|
||||
|
||||
preset_combobox_ = new QComboBox();
|
||||
preset_combobox_->setToolTip(tr(
|
||||
"This setting allows you to tweak the ratio of export speed to compression quality. \n\n"
|
||||
"If using Constant Rate Factor, slower speeds will result in smaller file sizes for the same quality. \n\n"
|
||||
"If using Target Bit Rate or Target File Size, slower speeds will result in higher quality for the same bitrate/filesize. \n\n"
|
||||
"This setting is equivalent to the `preset` setting in libx264."));
|
||||
|
||||
preset_combobox_->addItem(tr("Ultra Fast"));
|
||||
preset_combobox_->addItem(tr("Super Fast"));
|
||||
preset_combobox_->addItem(tr("Very Fast"));
|
||||
preset_combobox_->addItem(tr("Faster"));
|
||||
preset_combobox_->addItem(tr("Fast"));
|
||||
preset_combobox_->addItem(tr("Medium"));
|
||||
preset_combobox_->addItem(tr("Slow"));
|
||||
preset_combobox_->addItem(tr("Slower"));
|
||||
preset_combobox_->addItem(tr("Very Slow"));
|
||||
|
||||
//Default to "medium"
|
||||
preset_combobox_->setCurrentIndex(5);
|
||||
|
||||
layout->addWidget(preset_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Compression Method:")), row, 0);
|
||||
|
||||
QComboBox *compression_box = new QComboBox();
|
||||
|
||||
// These items must correspond to the CompressionMethod enum
|
||||
compression_box->addItem(tr("Constant Rate Factor"));
|
||||
compression_box->addItem(tr("Target Bit Rate"));
|
||||
compression_box->addItem(tr("Target File Size"));
|
||||
|
||||
layout->addWidget(compression_box, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
compression_method_stack_ = new QStackedWidget();
|
||||
layout->addWidget(compression_method_stack_, row, 0, 1, 2);
|
||||
|
||||
crf_section_ = new H264CRFSection(default_crf);
|
||||
compression_method_stack_->addWidget(crf_section_);
|
||||
|
||||
bitrate_section_ = new H264BitRateSection();
|
||||
compression_method_stack_->addWidget(bitrate_section_);
|
||||
|
||||
filesize_section_ = new H264FileSizeSection();
|
||||
compression_method_stack_->addWidget(filesize_section_);
|
||||
|
||||
connect(
|
||||
compression_box,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
compression_method_stack_, &QStackedWidget::setCurrentIndex);
|
||||
}
|
||||
|
||||
void H264Section::add_opts(OakEngineEncodingParams *params)
|
||||
{
|
||||
// FIXME: Implement two-pass
|
||||
|
||||
CompressionMethod method = static_cast<CompressionMethod>(
|
||||
compression_method_stack_->currentIndex());
|
||||
|
||||
// This option is not used by the encoder (nor is anything with the ove_ prefix), it's to help us
|
||||
// identify which option was chosen when params are restored
|
||||
oakengine_encoding_params_set_video_option(
|
||||
params, "ove_compressionmethod",
|
||||
QByteArray::number(method).constData());
|
||||
|
||||
if (method == k_constant_rate_factor) {
|
||||
// Simply set CRF value
|
||||
oakengine_encoding_params_set_video_option(
|
||||
params, "crf",
|
||||
QByteArray::number(crf_section_->get_value()).constData());
|
||||
|
||||
} else {
|
||||
int64_t target_rate, max_rate, min_rate;
|
||||
|
||||
if (method == k_target_bit_rate) {
|
||||
// Use user-supplied values for the bit rate
|
||||
target_rate = bitrate_section_->get_target_bit_rate();
|
||||
min_rate = 0;
|
||||
max_rate = bitrate_section_->get_maximum_bit_rate();
|
||||
} else {
|
||||
// Calculate the bit rate from the file size divided by the sequence length in seconds (bits per second)
|
||||
int64_t target_fs = filesize_section_->get_file_size();
|
||||
int export_len_num = 0, export_len_den = 1;
|
||||
oakengine_encoding_params_get_export_length(
|
||||
params, &export_len_num, &export_len_den);
|
||||
const double export_len_sec =
|
||||
(export_len_den > 0)
|
||||
? static_cast<double>(export_len_num)
|
||||
/ static_cast<double>(export_len_den)
|
||||
: 1.0;
|
||||
target_rate = qRound64(static_cast<double>(target_fs) / export_len_sec);
|
||||
min_rate = target_rate;
|
||||
max_rate = target_rate;
|
||||
|
||||
oakengine_encoding_params_set_video_option(
|
||||
params, "ove_targetfilesize",
|
||||
QByteArray::number(target_fs).constData());
|
||||
}
|
||||
|
||||
// Disable CRF encoding
|
||||
oakengine_encoding_params_set_video_option(params, "crf", "-1");
|
||||
|
||||
oakengine_encoding_params_set_video_bit_rate(params, target_rate);
|
||||
oakengine_encoding_params_set_video_min_bit_rate(params, min_rate);
|
||||
oakengine_encoding_params_set_video_max_bit_rate(params, max_rate);
|
||||
oakengine_encoding_params_set_video_buffer_size(params, 2000000);
|
||||
}
|
||||
|
||||
oakengine_encoding_params_set_video_option(
|
||||
params, "preset",
|
||||
QByteArray::number(preset_combobox_->currentIndex()).constData());
|
||||
}
|
||||
|
||||
void H264Section::set_opts(const OakEngineEncodingParams *p)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
CompressionMethod method = k_constant_rate_factor;
|
||||
if (oakengine_encoding_params_video_option(
|
||||
p, "ove_compressionmethod", buf,
|
||||
static_cast<int>(sizeof(buf))) > 0) {
|
||||
method = static_cast<CompressionMethod>(QString::fromUtf8(buf).toInt());
|
||||
}
|
||||
|
||||
compression_method_stack_->setCurrentIndex(method);
|
||||
|
||||
if (method == k_constant_rate_factor) {
|
||||
if (oakengine_encoding_params_video_option(
|
||||
p, "crf", buf, static_cast<int>(sizeof(buf))) > 0) {
|
||||
crf_section_->set_value(QString::fromUtf8(buf).toInt());
|
||||
}
|
||||
} else {
|
||||
int64_t target_rate = oakengine_encoding_params_video_bit_rate(p);
|
||||
int64_t max_rate = oakengine_encoding_params_video_max_bit_rate(p);
|
||||
|
||||
if (method == k_target_bit_rate) {
|
||||
// Use user-supplied values for the bit rate
|
||||
bitrate_section_->set_target_bit_rate(target_rate);
|
||||
bitrate_section_->set_maximum_bit_rate(max_rate);
|
||||
} else {
|
||||
// Calculate the bit rate from the file size divided by the sequence length in seconds (bits per second)
|
||||
if (oakengine_encoding_params_video_option(
|
||||
p, "ove_targetfilesize", buf,
|
||||
static_cast<int>(sizeof(buf))) > 0) {
|
||||
filesize_section_->set_file_size(
|
||||
QString::fromUtf8(buf).toLongLong());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
H264CRFSection::H264CRFSection(int default_crf, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
crf_slider_ = new QSlider(Qt::Horizontal);
|
||||
crf_slider_->setMinimum(k_minimum_crf);
|
||||
crf_slider_->setMaximum(k_maximum_crf);
|
||||
crf_slider_->setValue(default_crf);
|
||||
layout->addWidget(crf_slider_);
|
||||
|
||||
IntegerSlider *crf_input = new IntegerSlider();
|
||||
crf_input->setMaximumWidth(QtUtils::q_font_metrics_width(
|
||||
crf_input->fontMetrics(), QStringLiteral("HHHH")));
|
||||
crf_input->set_minimum(k_minimum_crf);
|
||||
crf_input->set_maximum(k_maximum_crf);
|
||||
crf_input->set_value(default_crf);
|
||||
crf_input->SetDefaultValue(default_crf);
|
||||
layout->addWidget(crf_input);
|
||||
|
||||
connect(crf_slider_, &QSlider::valueChanged, crf_input,
|
||||
&IntegerSlider::set_value);
|
||||
connect(crf_input, &IntegerSlider::value_changed, crf_slider_,
|
||||
&QSlider::setValue);
|
||||
}
|
||||
|
||||
int H264CRFSection::get_value() const
|
||||
{
|
||||
return crf_slider_->value();
|
||||
}
|
||||
|
||||
void H264CRFSection::set_value(int c)
|
||||
{
|
||||
crf_slider_->setValue(c);
|
||||
}
|
||||
|
||||
H264BitRateSection::H264BitRateSection(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Target Bit Rate (Mbps):")), row, 0);
|
||||
|
||||
target_rate_ = new FloatSlider();
|
||||
target_rate_->set_minimum(0);
|
||||
layout->addWidget(target_rate_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Maximum Bit Rate (Mbps):")), row, 0);
|
||||
|
||||
max_rate_ = new FloatSlider();
|
||||
max_rate_->set_minimum(0);
|
||||
layout->addWidget(max_rate_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Two-Pass")), row, 0);
|
||||
|
||||
QCheckBox *two_pass_box = new QCheckBox();
|
||||
layout->addWidget(two_pass_box, row, 1);
|
||||
|
||||
// Bit rate defaults
|
||||
target_rate_->set_value(16.0);
|
||||
max_rate_->set_value(32.0);
|
||||
}
|
||||
|
||||
int64_t H264BitRateSection::get_target_bit_rate() const
|
||||
{
|
||||
return qRound64(target_rate_->get_value() * 1000000.0);
|
||||
}
|
||||
|
||||
void H264BitRateSection::set_target_bit_rate(int64_t b)
|
||||
{
|
||||
target_rate_->set_value(double(b) * 0.000001);
|
||||
}
|
||||
|
||||
int64_t H264BitRateSection::get_maximum_bit_rate() const
|
||||
{
|
||||
return qRound64(max_rate_->get_value() * 1000000.0);
|
||||
}
|
||||
|
||||
void H264BitRateSection::set_maximum_bit_rate(int64_t b)
|
||||
{
|
||||
max_rate_->set_value(double(b) * 0.000001);
|
||||
}
|
||||
|
||||
H264FileSizeSection::H264FileSizeSection(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Target File Size (MB):")), row, 0);
|
||||
|
||||
file_size_ = new FloatSlider();
|
||||
file_size_->set_minimum(0);
|
||||
layout->addWidget(file_size_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Two-Pass")), row, 0);
|
||||
|
||||
QCheckBox *two_pass_box = new QCheckBox();
|
||||
layout->addWidget(two_pass_box, row, 1);
|
||||
|
||||
// File size defaults
|
||||
file_size_->set_value(700.0);
|
||||
}
|
||||
|
||||
int64_t H264FileSizeSection::get_file_size() const
|
||||
{
|
||||
// Convert megabytes to BITS
|
||||
return qRound64(file_size_->get_value() * 1024.0 * 1024.0 * 8.0);
|
||||
}
|
||||
|
||||
void H264FileSizeSection::set_file_size(int64_t f)
|
||||
{
|
||||
// Convert bits back to megabytes
|
||||
file_size_->set_value(double(f) / 8.0 / 1024.0 / 1024.0);
|
||||
}
|
||||
|
||||
H265Section::H265Section(QWidget *parent)
|
||||
: H264Section(H264CRFSection::k_default_h265_crf, parent)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_H264SECTION_H
|
||||
#define OAK_H264SECTION_H
|
||||
|
||||
#include <QSlider>
|
||||
#include <QStackedWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "codecsection.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class H264CRFSection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
H264CRFSection(int default_crf, QWidget *parent = nullptr);
|
||||
|
||||
int get_value() const;
|
||||
void set_value(int c);
|
||||
|
||||
static constexpr int k_default_h264_crf = 18;
|
||||
static constexpr int k_default_h265_crf = 23;
|
||||
|
||||
private:
|
||||
static constexpr int k_minimum_crf = 0;
|
||||
static constexpr int k_maximum_crf = 51;
|
||||
|
||||
QSlider *crf_slider_;
|
||||
};
|
||||
|
||||
class H264BitRateSection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
H264BitRateSection(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Get user-selected target bit rate (returns in BITS)
|
||||
*/
|
||||
int64_t get_target_bit_rate() const;
|
||||
void set_target_bit_rate(int64_t b);
|
||||
|
||||
/**
|
||||
* @brief Get user-selected maximum bit rate (returns in BITS)
|
||||
*/
|
||||
int64_t get_maximum_bit_rate() const;
|
||||
void set_maximum_bit_rate(int64_t b);
|
||||
|
||||
private:
|
||||
FloatSlider *target_rate_;
|
||||
|
||||
FloatSlider *max_rate_;
|
||||
};
|
||||
|
||||
class H264FileSizeSection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
H264FileSizeSection(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Returns file size in BITS
|
||||
*/
|
||||
int64_t get_file_size() const;
|
||||
void set_file_size(int64_t f);
|
||||
|
||||
private:
|
||||
FloatSlider *file_size_;
|
||||
};
|
||||
|
||||
class H264Section : public CodecSection {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum CompressionMethod {
|
||||
k_constant_rate_factor,
|
||||
k_target_bit_rate,
|
||||
k_target_file_size
|
||||
};
|
||||
|
||||
H264Section(QWidget *parent = nullptr);
|
||||
H264Section(int default_crf, QWidget *parent);
|
||||
|
||||
virtual void add_opts(OakEngineEncodingParams *params) override;
|
||||
|
||||
virtual void set_opts(const OakEngineEncodingParams *p) override;
|
||||
|
||||
private:
|
||||
QStackedWidget *compression_method_stack_;
|
||||
|
||||
H264CRFSection *crf_section_;
|
||||
|
||||
H264BitRateSection *bitrate_section_;
|
||||
|
||||
H264FileSizeSection *filesize_section_;
|
||||
|
||||
QComboBox *preset_combobox_;
|
||||
};
|
||||
|
||||
class H265Section : public H264Section {
|
||||
Q_OBJECT
|
||||
public:
|
||||
H265Section(QWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_H264SECTION_H
|
||||
@@ -1,63 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "imagesection.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ImageSection::ImageSection(QWidget *parent)
|
||||
: CodecSection(parent)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Image Sequence:")), row, 0);
|
||||
|
||||
image_sequence_checkbox_ = new QCheckBox();
|
||||
connect(image_sequence_checkbox_, &QCheckBox::toggled, this,
|
||||
&ImageSection::image_sequence_check_box_toggled);
|
||||
layout->addWidget(image_sequence_checkbox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Frame to Export:")), row, 0);
|
||||
|
||||
frame_slider_ = new RationalSlider();
|
||||
frame_slider_->set_minimum(0);
|
||||
frame_slider_->set_value(0);
|
||||
frame_slider_->set_display_type(slider::k_time);
|
||||
connect(frame_slider_, &RationalSlider::value_changed, this,
|
||||
&ImageSection::time_changed);
|
||||
layout->addWidget(frame_slider_, row, 1);
|
||||
}
|
||||
|
||||
void ImageSection::image_sequence_check_box_toggled(bool e)
|
||||
{
|
||||
frame_slider_->setEnabled(!e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_IMAGESECTION_H
|
||||
#define OAK_IMAGESECTION_H
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "codecsection.h"
|
||||
#include "widget/slider/rationalslider.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ImageSection : public CodecSection {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ImageSection(QWidget *parent = nullptr);
|
||||
|
||||
bool is_image_sequence_checked() const
|
||||
{
|
||||
return image_sequence_checkbox_->isChecked();
|
||||
}
|
||||
|
||||
void set_image_sequence_checked(bool e)
|
||||
{
|
||||
image_sequence_checkbox_->setChecked(e);
|
||||
}
|
||||
|
||||
void set_timebase(const Rational &r)
|
||||
{
|
||||
frame_slider_->set_timebase(r);
|
||||
}
|
||||
|
||||
Rational get_time() const
|
||||
{
|
||||
return frame_slider_->get_value();
|
||||
}
|
||||
|
||||
void set_time(const Rational &t)
|
||||
{
|
||||
frame_slider_->set_value(t);
|
||||
}
|
||||
|
||||
signals:
|
||||
void time_changed(const Rational &t);
|
||||
|
||||
private:
|
||||
QCheckBox *image_sequence_checkbox_;
|
||||
|
||||
RationalSlider *frame_slider_;
|
||||
|
||||
private slots:
|
||||
void image_sequence_check_box_toggled(bool e);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_IMAGESECTION_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,143 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EXPORTDIALOG_H
|
||||
#define OAK_EXPORTDIALOG_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
#include <cstdint>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLineEdit>
|
||||
#include <QProgressBar>
|
||||
|
||||
#include "dialog/export/exportformatcombobox.h"
|
||||
#include "exportaudiotab.h"
|
||||
#include "exportsubtitlestab.h"
|
||||
#include "exportvideotab.h"
|
||||
#include "oakengine/encoding.h"
|
||||
#include "widget/nodeparamview/nodeparamviewwidgetbridge.h"
|
||||
#include "widget/viewer/viewer.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ExportDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportDialog(OakEngineNode *viewer_node, bool stills_only_mode,
|
||||
QWidget *parent = nullptr);
|
||||
ExportDialog(OakEngineNode *viewer_node, QWidget *parent = nullptr)
|
||||
: ExportDialog(viewer_node, false, parent)
|
||||
{
|
||||
}
|
||||
|
||||
Rational get_selected_timebase() const;
|
||||
void set_selected_timebase(const Rational &r);
|
||||
|
||||
OakEngineEncodingParams *generate_params() const;
|
||||
void set_params(const OakEngineEncodingParams *e);
|
||||
|
||||
virtual bool eventFilter(QObject *o, QEvent *e) override;
|
||||
|
||||
public slots:
|
||||
virtual void done(int r) override;
|
||||
|
||||
signals:
|
||||
void request_import_file(const QString &s);
|
||||
|
||||
private:
|
||||
void add_preferences_tab(QWidget *inner_widget, const QString &title);
|
||||
|
||||
void load_presets();
|
||||
void set_default_filename();
|
||||
|
||||
bool sequence_has_subtitles() const;
|
||||
|
||||
void set_defaults();
|
||||
|
||||
OakEngineNode *viewer_node_;
|
||||
|
||||
int previously_selected_format_;
|
||||
|
||||
Rational get_export_length() const;
|
||||
int64_t get_export_length_in_timebase_units() const;
|
||||
|
||||
enum RangeSelection { k_range_entire_sequence, k_range_in_to_out };
|
||||
|
||||
enum AutoPreset {
|
||||
k_preset_default = -1,
|
||||
k_preset_last_used = -2,
|
||||
};
|
||||
|
||||
QTabWidget *preferences_tabs_;
|
||||
|
||||
QComboBox *preset_combobox_;
|
||||
QComboBox *range_combobox_;
|
||||
std::vector<OakEngineEncodingParams *> presets_;
|
||||
|
||||
QCheckBox *video_enabled_;
|
||||
QCheckBox *audio_enabled_;
|
||||
QCheckBox *subtitles_enabled_;
|
||||
|
||||
ViewerWidget *preview_viewer_;
|
||||
QLineEdit *filename_edit_;
|
||||
ExportFormatComboBox *format_combobox_;
|
||||
|
||||
ExportVideoTab *video_tab_;
|
||||
ExportAudioTab *audio_tab_;
|
||||
ExportSubtitlesTab *subtitle_tab_;
|
||||
|
||||
double video_aspect_ratio_;
|
||||
|
||||
OakEngineColorManager *color_manager_;
|
||||
|
||||
QWidget *preferences_area_;
|
||||
QCheckBox *export_bkg_box_;
|
||||
QCheckBox *import_file_after_export_;
|
||||
|
||||
bool stills_only_mode_;
|
||||
|
||||
bool loading_presets_;
|
||||
|
||||
private slots:
|
||||
void browse_filename();
|
||||
|
||||
void format_changed(int current_format);
|
||||
|
||||
void resolution_changed();
|
||||
|
||||
void update_viewer_dimensions();
|
||||
|
||||
void start_export();
|
||||
|
||||
void export_finished();
|
||||
|
||||
void image_sequence_check_box_changed(bool e);
|
||||
|
||||
void save_preset();
|
||||
|
||||
void preset_combo_box_changed();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_EXPORTDIALOG_H
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "exportadvancedvideodialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ExportAdvancedVideoDialog::ExportAdvancedVideoDialog(
|
||||
const QList<QString> &pix_fmts, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Advanced"));
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
{
|
||||
// Pixel Settings
|
||||
QGroupBox *pixel_group = new QGroupBox();
|
||||
layout->addWidget(pixel_group);
|
||||
pixel_group->setTitle(tr("Pixel"));
|
||||
|
||||
QGridLayout *pixel_layout = new QGridLayout(pixel_group);
|
||||
|
||||
int row = 0;
|
||||
|
||||
pixel_layout->addWidget(new QLabel(tr("Pixel Format:")), row, 0);
|
||||
|
||||
pixel_format_combobox_ = new QComboBox();
|
||||
pixel_format_combobox_->addItems(pix_fmts);
|
||||
pixel_layout->addWidget(pixel_format_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
pixel_layout->addWidget(new QLabel(tr("YUV Color Range:")), row, 0);
|
||||
|
||||
yuv_color_range_combobox_ = new QComboBox();
|
||||
yuv_color_range_combobox_->addItems(
|
||||
{ tr("Limited (16-235)"), tr("Full (0-255)") });
|
||||
pixel_layout->addWidget(yuv_color_range_combobox_, row, 1);
|
||||
}
|
||||
|
||||
{
|
||||
// Performance Settings
|
||||
QGroupBox *performance_group = new QGroupBox();
|
||||
layout->addWidget(performance_group);
|
||||
performance_group->setTitle(tr("Performance"));
|
||||
|
||||
QGridLayout *performance_layout = new QGridLayout(performance_group);
|
||||
|
||||
int row = 0;
|
||||
|
||||
performance_layout->addWidget(new QLabel(tr("Threads:")), row, 0);
|
||||
|
||||
thread_slider_ = new IntegerSlider();
|
||||
thread_slider_->set_minimum(0);
|
||||
thread_slider_->SetDefaultValue(0);
|
||||
thread_slider_->insert_label_substitution(0, tr("Auto"));
|
||||
performance_layout->addWidget(thread_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
}
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||
&ExportAdvancedVideoDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||
&ExportAdvancedVideoDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OAK_EXPORTADVANCEDVIDEODIALOG_H
|
||||
#define OAK_EXPORTADVANCEDVIDEODIALOG_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
|
||||
#include "widget/slider/integerslider.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ExportAdvancedVideoDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportAdvancedVideoDialog(const QList<QString> &pix_fmts,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
int threads() const
|
||||
{
|
||||
return static_cast<int>(thread_slider_->get_value());
|
||||
}
|
||||
|
||||
void set_threads(int t)
|
||||
{
|
||||
thread_slider_->set_value(t);
|
||||
}
|
||||
|
||||
QString pix_fmt() const
|
||||
{
|
||||
return pixel_format_combobox_->currentText();
|
||||
}
|
||||
|
||||
void set_pix_fmt(const QString &s)
|
||||
{
|
||||
pixel_format_combobox_->setCurrentText(s);
|
||||
}
|
||||
|
||||
int yuv_range() const
|
||||
{
|
||||
return static_cast<int>(
|
||||
yuv_color_range_combobox_->currentIndex());
|
||||
}
|
||||
|
||||
void set_yuv_range(int i)
|
||||
{
|
||||
yuv_color_range_combobox_->setCurrentIndex(i);
|
||||
}
|
||||
|
||||
private:
|
||||
IntegerSlider *thread_slider_;
|
||||
|
||||
QComboBox *pixel_format_combobox_;
|
||||
|
||||
QComboBox *yuv_color_range_combobox_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_EXPORTADVANCEDVIDEODIALOG_H
|
||||
@@ -1,139 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "exportaudiotab.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include <olive/core/core.h>
|
||||
#include "oakengine/encoding.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const int ExportAudioTab::k_default_bit_rate = 320;
|
||||
|
||||
ExportAudioTab::ExportAudioTab(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
||||
|
||||
QGridLayout *layout = new QGridLayout();
|
||||
outer_layout->addLayout(layout);
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Codec:")), row, 0);
|
||||
|
||||
codec_combobox_ = new QComboBox();
|
||||
connect(
|
||||
codec_combobox_,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &ExportAudioTab::update_sample_formats);
|
||||
connect(
|
||||
codec_combobox_,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &ExportAudioTab::update_bit_rate_enabled);
|
||||
layout->addWidget(codec_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Sample Rate:")), row, 0);
|
||||
|
||||
sample_rate_combobox_ = new SampleRateComboBox();
|
||||
layout->addWidget(sample_rate_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Channel Layout:")), row, 0);
|
||||
|
||||
channel_layout_combobox_ = new ChannelLayoutComboBox();
|
||||
layout->addWidget(channel_layout_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Format:")), row, 0);
|
||||
|
||||
sample_format_combobox_ = new SampleFormatComboBox();
|
||||
layout->addWidget(sample_format_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Bit Rate:")), row, 0);
|
||||
|
||||
bit_rate_slider_ = new IntegerSlider();
|
||||
bit_rate_slider_->set_minimum(32);
|
||||
bit_rate_slider_->set_maximum(320);
|
||||
bit_rate_slider_->set_value(k_default_bit_rate);
|
||||
bit_rate_slider_->set_format(tr("%1 kbps"));
|
||||
layout->addWidget(bit_rate_slider_, row, 1);
|
||||
|
||||
outer_layout->addStretch();
|
||||
}
|
||||
|
||||
int ExportAudioTab::set_format(int format)
|
||||
{
|
||||
const int acodec_count = oakengine_encoding_format_audio_codec_count(format);
|
||||
setEnabled(acodec_count > 0);
|
||||
codec_combobox_->blockSignals(true);
|
||||
codec_combobox_->clear();
|
||||
for (int i = 0; i < acodec_count; i++) {
|
||||
int codec = oakengine_encoding_format_audio_codec_at(format, i);
|
||||
char buf[256];
|
||||
oakengine_encoding_codec_name(codec, buf, sizeof(buf));
|
||||
codec_combobox_->addItem(QString::fromUtf8(buf), codec);
|
||||
}
|
||||
codec_combobox_->blockSignals(false);
|
||||
fmt_ = format;
|
||||
|
||||
update_sample_formats();
|
||||
update_bit_rate_enabled();
|
||||
|
||||
return acodec_count;
|
||||
}
|
||||
|
||||
void ExportAudioTab::update_sample_formats()
|
||||
{
|
||||
// Use oakengine to get sample format values and build the vector
|
||||
const int count = oakengine_encoding_sample_format_count(fmt_, get_codec());
|
||||
std::vector<olive::core::SampleFormat> fmts;
|
||||
fmts.reserve(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
int val = oakengine_encoding_sample_format_at(fmt_, get_codec(), i);
|
||||
fmts.push_back(olive::core::SampleFormat(static_cast<olive::core::SampleFormat::Format>(val)));
|
||||
}
|
||||
sample_format_combobox_->set_available_formats(fmts);
|
||||
}
|
||||
|
||||
void ExportAudioTab::update_bit_rate_enabled()
|
||||
{
|
||||
bool uses_bitrate = !oakengine_encoding_codec_is_lossless(get_codec());
|
||||
bit_rate_slider_->setEnabled(uses_bitrate);
|
||||
|
||||
if (!uses_bitrate) {
|
||||
bit_rate_slider_->set_tristate();
|
||||
} else {
|
||||
bit_rate_slider_->set_value(k_default_bit_rate);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EXPORTAUDIOTAB_H
|
||||
#define OAK_EXPORTAUDIOTAB_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QWidget>
|
||||
|
||||
#include "oakutil/define.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
#include "widget/standardcombos/standardcombos.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ExportAudioTab : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportAudioTab(QWidget *parent = nullptr);
|
||||
|
||||
int get_codec() const
|
||||
{
|
||||
return codec_combobox_->currentData().toInt();
|
||||
}
|
||||
|
||||
void set_codec(int c)
|
||||
{
|
||||
for (int i = 0; i < codec_combobox_->count(); i++) {
|
||||
if (codec_combobox_->itemData(i) == c) {
|
||||
codec_combobox_->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SampleRateComboBox *sample_rate_combobox() const
|
||||
{
|
||||
return sample_rate_combobox_;
|
||||
}
|
||||
|
||||
SampleFormatComboBox *sample_format_combobox() const
|
||||
{
|
||||
return sample_format_combobox_;
|
||||
}
|
||||
|
||||
ChannelLayoutComboBox *channel_layout_combobox() const
|
||||
{
|
||||
return channel_layout_combobox_;
|
||||
}
|
||||
|
||||
IntegerSlider *bit_rate_slider() const
|
||||
{
|
||||
return bit_rate_slider_;
|
||||
}
|
||||
|
||||
public slots:
|
||||
int set_format(int format);
|
||||
|
||||
private:
|
||||
int fmt_;
|
||||
QComboBox *codec_combobox_;
|
||||
SampleRateComboBox *sample_rate_combobox_;
|
||||
ChannelLayoutComboBox *channel_layout_combobox_;
|
||||
SampleFormatComboBox *sample_format_combobox_;
|
||||
IntegerSlider *bit_rate_slider_;
|
||||
|
||||
static const int k_default_bit_rate;
|
||||
|
||||
private slots:
|
||||
void update_sample_formats();
|
||||
|
||||
void update_bit_rate_enabled();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_EXPORTAUDIOTAB_H
|
||||
@@ -1,151 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "exportformatcombobox.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "oakengine/encoding.h"
|
||||
#include "ui/icons/icons.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent)
|
||||
: QComboBox(parent)
|
||||
{
|
||||
// The invalid placeholder format is the format count itself
|
||||
// (ExportFormat::k_format_count), not -1.
|
||||
current_ = oakengine_encoding_format_count();
|
||||
|
||||
custom_menu_ = new Menu(this);
|
||||
|
||||
// Populate combobox formats
|
||||
switch (mode) {
|
||||
case k_show_all_formats:
|
||||
custom_menu_->addAction(create_header(icon::video, tr("Video")));
|
||||
populate_type(TrackReference::k_video);
|
||||
custom_menu_->addSeparator();
|
||||
|
||||
custom_menu_->addAction(create_header(icon::audio, tr("Audio")));
|
||||
populate_type(TrackReference::k_audio);
|
||||
custom_menu_->addSeparator();
|
||||
|
||||
custom_menu_->addAction(create_header(icon::subtitles, tr("Subtitle")));
|
||||
populate_type(TrackReference::k_subtitle);
|
||||
break;
|
||||
case k_show_audio_only:
|
||||
populate_type(TrackReference::k_audio);
|
||||
break;
|
||||
case k_show_video_only:
|
||||
populate_type(TrackReference::k_video);
|
||||
break;
|
||||
case k_show_subtitles_only:
|
||||
populate_type(TrackReference::k_subtitle);
|
||||
break;
|
||||
}
|
||||
|
||||
connect(custom_menu_, &Menu::triggered, this,
|
||||
&ExportFormatComboBox::handle_index_change);
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::showPopup()
|
||||
{
|
||||
custom_menu_->setMinimumWidth(this->width());
|
||||
custom_menu_->exec(mapToGlobal(QPoint(0, 0)));
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::set_format(int fmt)
|
||||
{
|
||||
current_ = fmt;
|
||||
clear();
|
||||
char buf[256];
|
||||
oakengine_encoding_format_name(fmt, buf, sizeof(buf));
|
||||
addItem(QString::fromUtf8(buf));
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::handle_index_change(QAction *a)
|
||||
{
|
||||
int f = a->data().toInt();
|
||||
set_format(f);
|
||||
emit format_changed(f);
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::populate_type(TrackReference::Type type)
|
||||
{
|
||||
const int fmt_count = oakengine_encoding_format_count();
|
||||
for (int i = 0; i < fmt_count; i++) {
|
||||
int f = i;
|
||||
char buf[256];
|
||||
|
||||
bool has_video = oakengine_encoding_format_video_codec_count(f) > 0;
|
||||
bool has_audio = oakengine_encoding_format_audio_codec_count(f) > 0;
|
||||
bool has_sub = oakengine_encoding_format_subtitle_codec_count(f) > 0;
|
||||
|
||||
if (type == TrackReference::k_video && has_video) {
|
||||
// Do nothing
|
||||
} else if (type == TrackReference::k_audio && !has_video && has_audio) {
|
||||
// Do nothing
|
||||
} else if (type == TrackReference::k_subtitle && !has_video && !has_audio && has_sub) {
|
||||
// Do nothing
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
oakengine_encoding_format_name(f, buf, sizeof(buf));
|
||||
QString format_name = QString::fromUtf8(buf);
|
||||
|
||||
QAction *a = custom_menu_->addAction(format_name);
|
||||
a->setData(i);
|
||||
a->setIconVisibleInMenu(false);
|
||||
}
|
||||
}
|
||||
|
||||
QWidgetAction *ExportFormatComboBox::create_header(const QIcon &icon,
|
||||
const QString &title)
|
||||
{
|
||||
QWidgetAction *a = new QWidgetAction(this);
|
||||
|
||||
QWidget *w = new QWidget();
|
||||
QHBoxLayout *layout = new QHBoxLayout(w);
|
||||
|
||||
QLabel *icon_lbl = new QLabel();
|
||||
|
||||
QLabel *text_lbl = new QLabel(title);
|
||||
text_lbl->setAlignment(Qt::AlignCenter);
|
||||
QFont f = text_lbl->font();
|
||||
f.setWeight(QFont::Bold);
|
||||
text_lbl->setFont(f);
|
||||
|
||||
icon_lbl->setPixmap(icon.pixmap(text_lbl->sizeHint()));
|
||||
|
||||
layout->addStretch();
|
||||
layout->addWidget(icon_lbl);
|
||||
layout->addWidget(text_lbl);
|
||||
layout->addStretch();
|
||||
|
||||
a->setDefaultWidget(w);
|
||||
a->setEnabled(false);
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EXPORTFORMATCOMBOBOX_H
|
||||
#define OAK_EXPORTFORMATCOMBOBOX_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QWidgetAction>
|
||||
|
||||
#include "common/trackreferencehandle.h"
|
||||
#include "widget/menu/menu.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ExportFormatComboBox : public QComboBox {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Mode {
|
||||
k_show_all_formats,
|
||||
k_show_audio_only,
|
||||
k_show_video_only,
|
||||
k_show_subtitles_only
|
||||
};
|
||||
|
||||
ExportFormatComboBox(Mode mode, QWidget *parent = nullptr);
|
||||
ExportFormatComboBox(QWidget *parent = nullptr)
|
||||
: ExportFormatComboBox(k_show_all_formats, parent)
|
||||
{
|
||||
}
|
||||
|
||||
int get_format() const
|
||||
{
|
||||
return current_;
|
||||
}
|
||||
|
||||
void showPopup();
|
||||
|
||||
signals:
|
||||
void format_changed(int fmt);
|
||||
|
||||
public slots:
|
||||
void set_format(int fmt);
|
||||
|
||||
private slots:
|
||||
void handle_index_change(QAction *a);
|
||||
|
||||
private:
|
||||
void populate_type(TrackReference::Type type);
|
||||
|
||||
QWidgetAction *create_header(const QIcon &icon, const QString &title);
|
||||
|
||||
Menu *custom_menu_;
|
||||
|
||||
int current_ = -1; // was ExportFormat::k_format_count
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_EXPORTFORMATCOMBOBOX_H
|
||||
@@ -1,127 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "exportsavepresetdialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDir>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ExportSavePresetDialog::ExportSavePresetDialog(const OakEngineEncodingParams *p,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, params_(p)
|
||||
{
|
||||
auto layout = new QVBoxLayout(this);
|
||||
|
||||
name_edit_ = new QLineEdit();
|
||||
|
||||
// Populate existing list
|
||||
QStringList l;
|
||||
{
|
||||
const int n = oakengine_encoding_preset_count();
|
||||
for (int i = 0; i < n; i++) {
|
||||
char name_buf[256];
|
||||
if (oakengine_encoding_preset_name(
|
||||
i, name_buf, static_cast<int>(sizeof(name_buf))) > 0) {
|
||||
l.append(QString::fromUtf8(name_buf));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!l.empty()) {
|
||||
auto list_widget = new QListWidget();
|
||||
for (const QString &f : l) {
|
||||
list_widget->addItem(f);
|
||||
}
|
||||
connect(list_widget, &QListWidget::currentTextChanged, name_edit_,
|
||||
&QLineEdit::setText);
|
||||
layout->addWidget(list_widget);
|
||||
}
|
||||
|
||||
auto name_layout = new QHBoxLayout();
|
||||
layout->addLayout(name_layout);
|
||||
|
||||
name_layout->addWidget(new QLabel(tr("Name:")));
|
||||
|
||||
name_edit_->setFocus();
|
||||
name_layout->addWidget(name_edit_);
|
||||
|
||||
auto btns =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(btns, &QDialogButtonBox::accepted, this,
|
||||
&ExportSavePresetDialog::accept);
|
||||
connect(btns, &QDialogButtonBox::rejected, this,
|
||||
&ExportSavePresetDialog::reject);
|
||||
layout->addWidget(btns);
|
||||
|
||||
setWindowTitle(tr("Save Export Preset"));
|
||||
}
|
||||
|
||||
void ExportSavePresetDialog::accept()
|
||||
{
|
||||
if (name_edit_->text().isEmpty()) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Invalid Name"),
|
||||
tr("You must enter a name to save an export preset."));
|
||||
return;
|
||||
}
|
||||
|
||||
char preset_path_buf[1024];
|
||||
preset_path_buf[0] = '\0';
|
||||
oakengine_encoding_preset_path(
|
||||
preset_path_buf, static_cast<int>(sizeof(preset_path_buf)));
|
||||
QDir d(QString::fromUtf8(preset_path_buf));
|
||||
|
||||
if (!d.exists()) {
|
||||
d.mkpath(QStringLiteral("."));
|
||||
}
|
||||
|
||||
if (d.exists(name_edit_->text())) {
|
||||
if (QMessageBox::question(
|
||||
this, tr("Overwrite Preset"),
|
||||
tr("A preset with the name \"%1\" already exists. Do you wish to overwrite it?")
|
||||
.arg(name_edit_->text()),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const QByteArray full_path =
|
||||
d.filePath(name_edit_->text()).toUtf8();
|
||||
const int rc = oakengine_encoding_params_save_file(
|
||||
params_, full_path.constData());
|
||||
if (rc != OAKENGINE_OK) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Write Error"),
|
||||
tr("Failed to save preset to \"%1\".").arg(
|
||||
QString::fromUtf8(full_path)));
|
||||
return;
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EXPORTSAVEPRESETDIALOG_H
|
||||
#define OAK_EXPORTSAVEPRESETDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
|
||||
#include "oakengine/encoding.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ExportSavePresetDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportSavePresetDialog(const OakEngineEncodingParams *p, QWidget *parent = nullptr);
|
||||
|
||||
QString get_selected_preset_name() const
|
||||
{
|
||||
return name_edit_->text();
|
||||
}
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
QLineEdit *name_edit_;
|
||||
|
||||
const OakEngineEncodingParams *params_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_EXPORTSAVEPRESETDIALOG_H
|
||||
@@ -1,80 +0,0 @@
|
||||
#include "exportsubtitlestab.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
|
||||
#include "oakengine/encoding.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ExportSubtitlesTab::ExportSubtitlesTab(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
||||
|
||||
QGridLayout *layout = new QGridLayout();
|
||||
outer_layout->addLayout(layout);
|
||||
|
||||
int row = 0;
|
||||
|
||||
sidecar_checkbox_ = new QCheckBox(tr("Export to sidecar file"));
|
||||
layout->addWidget(sidecar_checkbox_, row, 0, 1, 2);
|
||||
|
||||
row++;
|
||||
|
||||
sidecar_format_label_ = new QLabel(tr("Sidecar Format:"));
|
||||
sidecar_format_label_->setVisible(false);
|
||||
layout->addWidget(sidecar_format_label_, row, 0);
|
||||
|
||||
sidecar_format_combobox_ =
|
||||
new ExportFormatComboBox(ExportFormatComboBox::k_show_subtitles_only);
|
||||
sidecar_format_combobox_->setVisible(true);
|
||||
layout->addWidget(sidecar_format_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Codec:")), row, 0);
|
||||
|
||||
codec_combobox_ = new QComboBox();
|
||||
layout->addWidget(codec_combobox_, row, 1);
|
||||
|
||||
outer_layout->addStretch();
|
||||
|
||||
connect(sidecar_checkbox_, &QCheckBox::toggled, sidecar_format_label_,
|
||||
&QWidget::setVisible);
|
||||
connect(sidecar_checkbox_, &QCheckBox::toggled, sidecar_format_combobox_,
|
||||
&QWidget::setVisible);
|
||||
}
|
||||
|
||||
int ExportSubtitlesTab::set_format(int format)
|
||||
{
|
||||
const bool has_video = oakengine_encoding_format_video_codec_count(format) > 0;
|
||||
const bool has_audio = oakengine_encoding_format_audio_codec_count(format) > 0;
|
||||
int scodec_count = oakengine_encoding_format_subtitle_codec_count(format);
|
||||
|
||||
if (scodec_count > 0 && !has_video && !has_audio) {
|
||||
// If format supports ONLY scodecs, default this to off and disable it
|
||||
sidecar_checkbox_->setChecked(false);
|
||||
sidecar_checkbox_->setEnabled(false);
|
||||
} else {
|
||||
// If format does not support scodecs, default this to checked and disable it
|
||||
sidecar_checkbox_->setChecked(scodec_count == 0);
|
||||
sidecar_checkbox_->setEnabled(scodec_count > 0);
|
||||
}
|
||||
|
||||
// Refresh for sidecar format
|
||||
int sidecar_fmt = sidecar_format_combobox_->get_format();
|
||||
scodec_count = oakengine_encoding_format_subtitle_codec_count(sidecar_fmt);
|
||||
|
||||
codec_combobox_->clear();
|
||||
for (int i = 0; i < scodec_count; i++) {
|
||||
int scodec = oakengine_encoding_format_subtitle_codec_at(sidecar_fmt, i);
|
||||
char buf[256];
|
||||
oakengine_encoding_codec_name(scodec, buf, sizeof(buf));
|
||||
codec_combobox_->addItem(QString::fromUtf8(buf), scodec);
|
||||
}
|
||||
|
||||
return scodec_count;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EXPORTSUBTITLESTAB_H
|
||||
#define OAK_EXPORTSUBTITLESTAB_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
|
||||
#include "oakutil/qtutils.h"
|
||||
#include "dialog/export/exportformatcombobox.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ExportSubtitlesTab : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportSubtitlesTab(QWidget *parent = nullptr);
|
||||
|
||||
bool get_sidecar_enabled() const
|
||||
{
|
||||
return sidecar_checkbox_->isChecked();
|
||||
}
|
||||
void set_sidecar_enabled(bool e)
|
||||
{
|
||||
sidecar_checkbox_->setChecked(e);
|
||||
}
|
||||
|
||||
int get_sidecar_format() const
|
||||
{
|
||||
return sidecar_format_combobox_->get_format();
|
||||
}
|
||||
void set_sidecar_format(int f)
|
||||
{
|
||||
sidecar_format_combobox_->set_format(f);
|
||||
}
|
||||
|
||||
int set_format(int format);
|
||||
|
||||
int get_subtitle_codec()
|
||||
{
|
||||
return codec_combobox_->currentData().toInt();
|
||||
}
|
||||
|
||||
void set_subtitle_codec(int c)
|
||||
{
|
||||
QtUtils::set_combo_box_data(codec_combobox_, c);
|
||||
}
|
||||
|
||||
private:
|
||||
QCheckBox *sidecar_checkbox_;
|
||||
|
||||
QLabel *sidecar_format_label_;
|
||||
ExportFormatComboBox *sidecar_format_combobox_;
|
||||
|
||||
QComboBox *codec_combobox_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_EXPORTSUBTITLESTAB_H
|
||||
@@ -1,314 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "exportvideotab.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "exportadvancedvideodialog.h"
|
||||
#include "oakengine/encoding.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ExportVideoTab::ExportVideoTab(OakEngineColorManager *color_manager, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, color_manager_(color_manager)
|
||||
, threads_(0)
|
||||
, color_range_(0) // k_color_range_default
|
||||
{
|
||||
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
||||
|
||||
outer_layout->addWidget(setup_resolution_section());
|
||||
|
||||
outer_layout->addWidget(setup_codec_section());
|
||||
|
||||
outer_layout->addWidget(setup_color_section());
|
||||
|
||||
outer_layout->addStretch();
|
||||
}
|
||||
|
||||
int ExportVideoTab::set_format(int format)
|
||||
{
|
||||
format_ = format;
|
||||
|
||||
const int vcodec_count = oakengine_encoding_format_video_codec_count(format);
|
||||
setEnabled(vcodec_count > 0);
|
||||
codec_combobox()->clear();
|
||||
for (int i = 0; i < vcodec_count; i++) {
|
||||
int vcodec = oakengine_encoding_format_video_codec_at(format, i);
|
||||
char buf[256];
|
||||
oakengine_encoding_codec_name(vcodec, buf, sizeof(buf));
|
||||
codec_combobox()->addItem(QString::fromUtf8(buf), vcodec);
|
||||
}
|
||||
return vcodec_count;
|
||||
}
|
||||
|
||||
bool ExportVideoTab::is_image_sequence_set() const
|
||||
{
|
||||
ImageSection *img_section =
|
||||
dynamic_cast<ImageSection *>(codec_stack_->currentWidget());
|
||||
|
||||
return (img_section && img_section->is_image_sequence_checked());
|
||||
}
|
||||
|
||||
void ExportVideoTab::set_image_sequence(bool e) const
|
||||
{
|
||||
if (ImageSection *img_section =
|
||||
dynamic_cast<ImageSection *>(codec_stack_->currentWidget())) {
|
||||
img_section->set_image_sequence_checked(e);
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *ExportVideoTab::setup_resolution_section()
|
||||
{
|
||||
int row = 0;
|
||||
|
||||
QGroupBox *resolution_group = new QGroupBox();
|
||||
resolution_group->setTitle(tr("General"));
|
||||
|
||||
QGridLayout *layout = new QGridLayout(resolution_group);
|
||||
|
||||
layout->addWidget(new QLabel(tr("Width:")), row, 0);
|
||||
|
||||
width_slider_ = new IntegerSlider();
|
||||
width_slider_->set_minimum(1);
|
||||
layout->addWidget(width_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Height:")), row, 0);
|
||||
|
||||
height_slider_ = new IntegerSlider();
|
||||
height_slider_->set_minimum(1);
|
||||
layout->addWidget(height_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Maintain Aspect Ratio:")), row, 0);
|
||||
|
||||
maintain_aspect_checkbox_ = new QCheckBox();
|
||||
maintain_aspect_checkbox_->setChecked(true);
|
||||
layout->addWidget(maintain_aspect_checkbox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Scaling Method:")), row, 0);
|
||||
|
||||
scaling_method_combobox_ = new QComboBox();
|
||||
scaling_method_combobox_->setEnabled(false);
|
||||
scaling_method_combobox_->addItem(tr("Fit"), OAKENGINE_ENCODING_SCALING_FIT);
|
||||
scaling_method_combobox_->addItem(tr("Stretch"), OAKENGINE_ENCODING_SCALING_STRETCH);
|
||||
scaling_method_combobox_->addItem(tr("Crop"), OAKENGINE_ENCODING_SCALING_CROP);
|
||||
layout->addWidget(scaling_method_combobox_, row, 1);
|
||||
|
||||
// Automatically enable/disable the scaling method depending on maintain aspect ratio
|
||||
connect(maintain_aspect_checkbox_, &QCheckBox::toggled, this,
|
||||
&ExportVideoTab::maintain_aspect_ratio_changed);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Frame Rate:")), row, 0);
|
||||
|
||||
frame_rate_combobox_ = new FrameRateComboBox();
|
||||
connect(frame_rate_combobox_, &FrameRateComboBox::frame_rate_changed, this,
|
||||
&ExportVideoTab::update_frame_rate);
|
||||
layout->addWidget(frame_rate_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Pixel Aspect Ratio:")), row, 0);
|
||||
|
||||
pixel_aspect_combobox_ = new PixelAspectRatioComboBox();
|
||||
layout->addWidget(pixel_aspect_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Interlacing:")), row, 0);
|
||||
|
||||
interlaced_combobox_ = new InterlacedComboBox();
|
||||
layout->addWidget(interlaced_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
|
||||
|
||||
pixel_format_field_ = new PixelFormatComboBox(false);
|
||||
layout->addWidget(pixel_format_field_, row, 1);
|
||||
|
||||
return resolution_group;
|
||||
}
|
||||
|
||||
QWidget *ExportVideoTab::setup_color_section()
|
||||
{
|
||||
color_space_chooser_ = new ColorSpaceChooser(color_manager_, true, false);
|
||||
connect(color_space_chooser_, &ColorSpaceChooser::input_color_space_changed,
|
||||
this, &ExportVideoTab::color_space_changed);
|
||||
return color_space_chooser_;
|
||||
}
|
||||
|
||||
QWidget *ExportVideoTab::setup_codec_section()
|
||||
{
|
||||
int row = 0;
|
||||
|
||||
QGroupBox *codec_group = new QGroupBox();
|
||||
codec_group->setTitle(tr("Codec"));
|
||||
|
||||
QGridLayout *codec_layout = new QGridLayout(codec_group);
|
||||
|
||||
codec_layout->addWidget(new QLabel(tr("Codec:")), row, 0);
|
||||
|
||||
codec_combobox_ = new QComboBox();
|
||||
codec_layout->addWidget(codec_combobox_, row, 1);
|
||||
connect(
|
||||
codec_combobox_,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &ExportVideoTab::video_codec_changed);
|
||||
|
||||
row++;
|
||||
|
||||
codec_stack_ = new CodecStack();
|
||||
codec_layout->addWidget(codec_stack_, row, 0, 1, 2);
|
||||
|
||||
image_section_ = new ImageSection();
|
||||
connect(image_section_, &ImageSection::time_changed, this,
|
||||
&ExportVideoTab::time_changed);
|
||||
codec_stack_->addWidget(image_section_);
|
||||
|
||||
h264_section_ = new H264Section();
|
||||
codec_stack_->addWidget(h264_section_);
|
||||
|
||||
h265_section_ = new H265Section();
|
||||
codec_stack_->addWidget(h265_section_);
|
||||
|
||||
av1_section_ = new AV1Section();
|
||||
codec_stack_->addWidget(av1_section_);
|
||||
|
||||
cineform_section_ = new CineformSection();
|
||||
codec_stack_->addWidget(cineform_section_);
|
||||
|
||||
row++;
|
||||
|
||||
QPushButton *advanced_btn = new QPushButton(tr("Advanced"));
|
||||
connect(advanced_btn, &QPushButton::clicked, this,
|
||||
&ExportVideoTab::open_advanced_dialog);
|
||||
codec_layout->addWidget(advanced_btn, row, 1);
|
||||
|
||||
return codec_group;
|
||||
}
|
||||
|
||||
void ExportVideoTab::maintain_aspect_ratio_changed(bool val)
|
||||
{
|
||||
scaling_method_combobox_->setEnabled(!val);
|
||||
}
|
||||
|
||||
void ExportVideoTab::open_advanced_dialog()
|
||||
{
|
||||
// Find pixel formats compatible with this encoder
|
||||
QStringList pixel_formats;
|
||||
const int pix_count = oakengine_encoding_pix_fmt_count(format_, get_selected_codec());
|
||||
for (int i = 0; i < pix_count; i++) {
|
||||
char buf[64];
|
||||
oakengine_encoding_pix_fmt_at(format_, get_selected_codec(), i, buf, sizeof(buf));
|
||||
pixel_formats.append(QString::fromUtf8(buf));
|
||||
}
|
||||
|
||||
ExportAdvancedVideoDialog d(pixel_formats, this);
|
||||
|
||||
d.set_threads(threads_);
|
||||
d.set_pix_fmt(pix_fmt_);
|
||||
d.set_yuv_range(color_range_);
|
||||
|
||||
if (d.exec() == QDialog::Accepted) {
|
||||
threads_ = d.threads();
|
||||
pix_fmt_ = d.pix_fmt();
|
||||
color_range_ = d.yuv_range();
|
||||
}
|
||||
}
|
||||
|
||||
void ExportVideoTab::update_frame_rate(Rational r)
|
||||
{
|
||||
// Convert frame rate to timebase
|
||||
r.flip();
|
||||
|
||||
for (int i = 0; i < codec_stack_->count(); i++) {
|
||||
ImageSection *img =
|
||||
dynamic_cast<ImageSection *>(codec_stack_->widget(i));
|
||||
if (img) {
|
||||
img->set_timebase(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExportVideoTab::video_codec_changed()
|
||||
{
|
||||
int codec = get_selected_codec();
|
||||
|
||||
switch (codec) {
|
||||
case OAKENGINE_ENCODING_CODEC_H264:
|
||||
case OAKENGINE_ENCODING_CODEC_H264RGB:
|
||||
set_codec_section(h264_section_);
|
||||
break;
|
||||
case OAKENGINE_ENCODING_CODEC_H265:
|
||||
set_codec_section(h265_section_);
|
||||
break;
|
||||
case OAKENGINE_ENCODING_CODEC_AV1:
|
||||
set_codec_section(av1_section_);
|
||||
break;
|
||||
case OAKENGINE_ENCODING_CODEC_CINEFORM:
|
||||
set_codec_section(cineform_section_);
|
||||
break;
|
||||
default:
|
||||
set_codec_section(
|
||||
oakengine_encoding_codec_is_still_image(codec) ? image_section_ : nullptr);
|
||||
}
|
||||
|
||||
// Set default pixel format
|
||||
QStringList pix_fmts;
|
||||
const int pix_count = oakengine_encoding_pix_fmt_count(format_, codec);
|
||||
for (int i = 0; i < pix_count; i++) {
|
||||
char buf[64];
|
||||
oakengine_encoding_pix_fmt_at(format_, codec, i, buf, sizeof(buf));
|
||||
pix_fmts.append(QString::fromUtf8(buf));
|
||||
}
|
||||
if (!pix_fmts.isEmpty()) {
|
||||
pix_fmt_ = pix_fmts.first();
|
||||
} else {
|
||||
pix_fmt_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ExportVideoTab::set_time(const Rational &time)
|
||||
{
|
||||
for (int i = 0; i < codec_stack_->count(); i++) {
|
||||
ImageSection *img =
|
||||
dynamic_cast<ImageSection *>(codec_stack_->widget(i));
|
||||
if (img) {
|
||||
img->set_time(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EXPORTVIDEOTAB_H
|
||||
#define OAK_EXPORTVIDEOTAB_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QWidget>
|
||||
|
||||
#include "oakutil/qtutils.h"
|
||||
#include "dialog/export/codec/av1section.h"
|
||||
#include "dialog/export/codec/cineformsection.h"
|
||||
#include "dialog/export/codec/codecstack.h"
|
||||
#include "dialog/export/codec/h264section.h"
|
||||
#include "dialog/export/codec/imagesection.h"
|
||||
#include "oakengine/color.h"
|
||||
#include "widget/colorwheel/colorspacechooser.h"
|
||||
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
#include "widget/standardcombos/standardcombos.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ExportVideoTab : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportVideoTab(OakEngineColorManager *color_manager, QWidget *parent = nullptr);
|
||||
|
||||
int set_format(int format);
|
||||
|
||||
bool is_image_sequence_set() const;
|
||||
void set_image_sequence(bool e) const;
|
||||
|
||||
Rational get_still_image_time() const
|
||||
{
|
||||
return image_section_->get_time();
|
||||
}
|
||||
|
||||
int get_selected_codec() const
|
||||
{
|
||||
return codec_combobox()->currentData().toInt();
|
||||
}
|
||||
|
||||
void set_selected_codec(int c)
|
||||
{
|
||||
QtUtils::set_combo_box_data(codec_combobox(), c);
|
||||
}
|
||||
|
||||
QComboBox *codec_combobox() const
|
||||
{
|
||||
return codec_combobox_;
|
||||
}
|
||||
|
||||
IntegerSlider *width_slider() const
|
||||
{
|
||||
return width_slider_;
|
||||
}
|
||||
|
||||
IntegerSlider *height_slider() const
|
||||
{
|
||||
return height_slider_;
|
||||
}
|
||||
|
||||
QCheckBox *maintain_aspect_checkbox() const
|
||||
{
|
||||
return maintain_aspect_checkbox_;
|
||||
}
|
||||
|
||||
QComboBox *scaling_method_combobox() const
|
||||
{
|
||||
return scaling_method_combobox_;
|
||||
}
|
||||
|
||||
Rational get_selected_frame_rate() const
|
||||
{
|
||||
return frame_rate_combobox_->get_frame_rate();
|
||||
}
|
||||
|
||||
void set_selected_frame_rate(const Rational &fr)
|
||||
{
|
||||
frame_rate_combobox_->set_frame_rate(fr);
|
||||
update_frame_rate(fr);
|
||||
}
|
||||
|
||||
QString current_ocio_color_space()
|
||||
{
|
||||
return color_space_chooser_->input();
|
||||
}
|
||||
|
||||
void set_ocio_color_space(const QString &s)
|
||||
{
|
||||
color_space_chooser_->set_input(s);
|
||||
}
|
||||
|
||||
CodecSection *get_codec_section() const
|
||||
{
|
||||
return static_cast<CodecSection *>(codec_stack_->currentWidget());
|
||||
}
|
||||
|
||||
void set_codec_section(CodecSection *section)
|
||||
{
|
||||
if (section) {
|
||||
codec_stack_->setVisible(true);
|
||||
codec_stack_->setCurrentWidget(section);
|
||||
} else {
|
||||
codec_stack_->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
InterlacedComboBox *interlaced_combobox() const
|
||||
{
|
||||
return interlaced_combobox_;
|
||||
}
|
||||
|
||||
PixelAspectRatioComboBox *pixel_aspect_combobox() const
|
||||
{
|
||||
return pixel_aspect_combobox_;
|
||||
}
|
||||
|
||||
PixelFormatComboBox *pixel_format_field() const
|
||||
{
|
||||
return pixel_format_field_;
|
||||
}
|
||||
|
||||
const int &threads() const
|
||||
{
|
||||
return threads_;
|
||||
}
|
||||
|
||||
void set_threads(int t)
|
||||
{
|
||||
threads_ = t;
|
||||
}
|
||||
|
||||
const QString &pix_fmt() const
|
||||
{
|
||||
return pix_fmt_;
|
||||
}
|
||||
void set_pix_fmt(const QString &s)
|
||||
{
|
||||
pix_fmt_ = s;
|
||||
}
|
||||
|
||||
int color_range() const
|
||||
{
|
||||
return color_range_;
|
||||
}
|
||||
void set_color_range(int c)
|
||||
{
|
||||
color_range_ = c;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void video_codec_changed();
|
||||
|
||||
void set_time(const Rational &time);
|
||||
|
||||
signals:
|
||||
void color_space_changed(const QString &colorspace);
|
||||
|
||||
void image_sequence_check_box_changed(bool e);
|
||||
|
||||
void time_changed(const Rational &time);
|
||||
|
||||
private:
|
||||
QWidget *setup_resolution_section();
|
||||
QWidget *setup_color_section();
|
||||
QWidget *setup_codec_section();
|
||||
|
||||
QComboBox *codec_combobox_;
|
||||
FrameRateComboBox *frame_rate_combobox_;
|
||||
QCheckBox *maintain_aspect_checkbox_;
|
||||
QComboBox *scaling_method_combobox_;
|
||||
|
||||
CodecStack *codec_stack_;
|
||||
ImageSection *image_section_;
|
||||
H264Section *h264_section_;
|
||||
H264Section *h265_section_;
|
||||
AV1Section *av1_section_;
|
||||
CineformSection *cineform_section_;
|
||||
|
||||
ColorSpaceChooser *color_space_chooser_;
|
||||
|
||||
IntegerSlider *width_slider_;
|
||||
IntegerSlider *height_slider_;
|
||||
|
||||
OakEngineColorManager *color_manager_;
|
||||
|
||||
InterlacedComboBox *interlaced_combobox_;
|
||||
PixelAspectRatioComboBox *pixel_aspect_combobox_;
|
||||
PixelFormatComboBox *pixel_format_field_;
|
||||
|
||||
int threads_;
|
||||
|
||||
QString pix_fmt_;
|
||||
int color_range_;
|
||||
|
||||
int format_;
|
||||
|
||||
private slots:
|
||||
void maintain_aspect_ratio_changed(bool val);
|
||||
|
||||
void open_advanced_dialog();
|
||||
|
||||
void update_frame_rate(Rational r);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_EXPORTVIDEOTAB_H
|
||||
@@ -1,24 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2020 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(streamproperties)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/footageproperties/footageproperties.cpp
|
||||
dialog/footageproperties/footageproperties.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,340 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "footageproperties.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QGroupBox>
|
||||
#include <QListWidget>
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "core.h"
|
||||
#include "oakengine/footage.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "oakengine/undo.h"
|
||||
#include "oakutil/oaknode.h"
|
||||
#include "streamproperties/audiostreamproperties.h"
|
||||
#include "streamproperties/videostreamproperties.h"
|
||||
#include "widget/viewer/vieweroutpututils.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
||||
OakEngineNode *footage)
|
||||
: QDialog(parent)
|
||||
, footage_(footage)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
// WRAPPER-GAP: oakengine_node_get_label_or_name -- emulate inline
|
||||
// (Node::get_label_or_name(): the label, falling back to the name).
|
||||
const QString footage_label = oak::Node(footage_).get_label();
|
||||
setWindowTitle(
|
||||
tr("\"%1\" Properties")
|
||||
.arg(footage_label.isEmpty() ? oak::Node(footage_).name() :
|
||||
footage_label));
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Name:")), row, 0);
|
||||
|
||||
footage_name_field_ = new QLineEdit(footage_label);
|
||||
layout->addWidget(footage_name_field_, row, 1);
|
||||
row++;
|
||||
|
||||
// Manual source start time: audio/timecode sync relies on this value,
|
||||
// which is otherwise only auto-detected from file metadata
|
||||
layout->addWidget(new QLabel(tr("Source Start Time:")), row, 0);
|
||||
|
||||
{
|
||||
QHBoxLayout *start_time_layout = new QHBoxLayout();
|
||||
|
||||
OakEngineFootage *start_time_handle = oakengine_footage_borrow(footage_);
|
||||
int sst_num = 0, sst_den = 1;
|
||||
const bool has_sst =
|
||||
oakengine_footage_get_source_start_time(start_time_handle,
|
||||
&sst_num,
|
||||
&sst_den) == 1;
|
||||
|
||||
source_start_time_enable_ = new QCheckBox(tr("Set"));
|
||||
source_start_time_enable_->setChecked(has_sst);
|
||||
start_time_layout->addWidget(source_start_time_enable_);
|
||||
|
||||
source_start_time_spin_ = new QDoubleSpinBox();
|
||||
source_start_time_spin_->setRange(-86400.0, 86400.0);
|
||||
source_start_time_spin_->setDecimals(3);
|
||||
source_start_time_spin_->setSuffix(QStringLiteral(" s"));
|
||||
source_start_time_spin_->setValue(
|
||||
has_sst ? double(sst_num) / double(sst_den) : 0.0);
|
||||
source_start_time_spin_->setEnabled(
|
||||
source_start_time_enable_->isChecked());
|
||||
start_time_layout->addWidget(source_start_time_spin_, 1);
|
||||
|
||||
QString detection_note;
|
||||
// Detection source comes through the facade (auto-detected field or
|
||||
// "manual"), matching the engine's stored value.
|
||||
if (has_sst) {
|
||||
char source_buf[64];
|
||||
source_buf[0] = '\0';
|
||||
oakengine_footage_get_source_start_time_source(
|
||||
start_time_handle, source_buf, sizeof(source_buf));
|
||||
const QString source = QString::fromUtf8(source_buf);
|
||||
detection_note =
|
||||
(source == QStringLiteral("manual")) ?
|
||||
tr("(set manually)") :
|
||||
tr("(auto-detected: %1)").arg(source);
|
||||
} else {
|
||||
detection_note = tr("(not detected)");
|
||||
}
|
||||
oakengine_footage_free(start_time_handle);
|
||||
start_time_layout->addWidget(new QLabel(detection_note));
|
||||
|
||||
connect(source_start_time_enable_, &QCheckBox::toggled,
|
||||
source_start_time_spin_, &QDoubleSpinBox::setEnabled);
|
||||
|
||||
layout->addLayout(start_time_layout, row, 1);
|
||||
}
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Tracks:")), row, 0, 1, 2);
|
||||
row++;
|
||||
|
||||
track_list_ = new QListWidget();
|
||||
layout->addWidget(track_list_, row, 0, 1, 2);
|
||||
|
||||
row++;
|
||||
|
||||
stacked_widget_ = new QStackedWidget();
|
||||
layout->addWidget(stacked_widget_, row, 0, 1, 2);
|
||||
|
||||
int first_usable_stream = -1;
|
||||
|
||||
int total_stream_count = 0;
|
||||
{
|
||||
OakEngineFootage *count_handle = oakengine_footage_borrow(footage_);
|
||||
total_stream_count =
|
||||
oakengine_footage_get_video_stream_count(count_handle) +
|
||||
oakengine_footage_get_audio_stream_count(count_handle) +
|
||||
oakengine_footage_get_subtitle_stream_count(count_handle);
|
||||
oakengine_footage_free(count_handle);
|
||||
}
|
||||
|
||||
for (int i = 0; i < total_stream_count; i++) {
|
||||
QString description;
|
||||
bool is_enabled = false;
|
||||
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||
|
||||
// (track_type, stream_index) pair for this real stream index;
|
||||
// track types are OAKENGINE_TRACK_TYPE_* ordinals (identical to
|
||||
// engine Track::Type).
|
||||
int reference_type = -1;
|
||||
int reference_index = -1;
|
||||
oakengine_footage_get_stream_reference(facade_handle, i,
|
||||
&reference_type,
|
||||
&reference_index);
|
||||
|
||||
switch (reference_type) {
|
||||
case OAKENGINE_TRACK_TYPE_VIDEO: {
|
||||
stacked_widget_->addWidget(
|
||||
new VideoStreamProperties(footage_, reference_index));
|
||||
|
||||
is_enabled = oakengine_viewer_get_stream_enabled(
|
||||
reinterpret_cast<const OakEngineNode *>(footage_),
|
||||
OAKENGINE_TRACK_TYPE_VIDEO, reference_index) == 1;
|
||||
{
|
||||
char desc_buf[256];
|
||||
oakengine_footage_describe_video_stream(
|
||||
facade_handle, reference_index, desc_buf,
|
||||
sizeof(desc_buf));
|
||||
description = QString::fromUtf8(desc_buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OAKENGINE_TRACK_TYPE_AUDIO: {
|
||||
stacked_widget_->addWidget(
|
||||
new AudioStreamProperties(footage_, reference_index));
|
||||
|
||||
AudioParams ap = viewer_output_audio_params(footage_, reference_index);
|
||||
is_enabled = ap.enabled();
|
||||
{
|
||||
char desc_buf[256];
|
||||
oakengine_footage_describe_audio_stream(
|
||||
facade_handle, reference_index, desc_buf,
|
||||
sizeof(desc_buf));
|
||||
description = QString::fromUtf8(desc_buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OAKENGINE_TRACK_TYPE_SUBTITLE: {
|
||||
is_enabled = oakengine_footage_get_stream_enabled(
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference_index);
|
||||
|
||||
// FIXME: Language?
|
||||
description = tr("Subtitles");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
stacked_widget_->addWidget(new StreamProperties());
|
||||
description = tr("Unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
oakengine_footage_free(facade_handle);
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(description, track_list_);
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(is_enabled ? Qt::Checked : Qt::Unchecked);
|
||||
track_list_->addItem(item);
|
||||
|
||||
if (first_usable_stream == -1 &&
|
||||
(reference_type == OAKENGINE_TRACK_TYPE_VIDEO ||
|
||||
reference_type == OAKENGINE_TRACK_TYPE_AUDIO ||
|
||||
reference_type == OAKENGINE_TRACK_TYPE_SUBTITLE)) {
|
||||
first_usable_stream = i;
|
||||
}
|
||||
}
|
||||
|
||||
row++;
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttons->setCenterButtons(true);
|
||||
layout->addWidget(buttons, row, 0, 1, 2);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
connect(track_list_, &QListWidget::currentRowChanged, stacked_widget_,
|
||||
&QStackedWidget::setCurrentIndex);
|
||||
|
||||
// Auto-select first item that actually has properties
|
||||
if (first_usable_stream >= 0) {
|
||||
track_list_->setCurrentRow(first_usable_stream);
|
||||
}
|
||||
track_list_->setFocus();
|
||||
}
|
||||
|
||||
void FootagePropertiesDialog::accept()
|
||||
{
|
||||
// Perform sanity check on all pages
|
||||
for (int i = 0; i < stacked_widget_->count(); i++) {
|
||||
if (!static_cast<StreamProperties *>(stacked_widget_->widget(i))
|
||||
->sanity_check()) {
|
||||
// Switch to the failed panel in question
|
||||
stacked_widget_->setCurrentIndex(i);
|
||||
|
||||
// Do nothing (it's up to the property panel itself to throw the error message)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||
|
||||
// All writes go through the liboakengine C ABI facade; each call lands
|
||||
// on the shared undo stack as an undoable command (replacing this
|
||||
// dialog's own undo command classes with identical semantics).
|
||||
if (oak::Node(footage_).get_label() != footage_name_field_->text()) {
|
||||
oakengine_node_set_label(
|
||||
footage_, footage_name_field_->text().toUtf8().constData());
|
||||
}
|
||||
|
||||
// Apply source start time changes
|
||||
{
|
||||
const bool new_enabled = source_start_time_enable_->isChecked();
|
||||
const Rational new_time =
|
||||
Rational::from_double(source_start_time_spin_->value());
|
||||
int cur_sst_num = 0, cur_sst_den = 1;
|
||||
const bool cur_has_sst =
|
||||
oakengine_footage_get_source_start_time(facade_handle,
|
||||
&cur_sst_num,
|
||||
&cur_sst_den) == 1;
|
||||
if (new_enabled != cur_has_sst ||
|
||||
(new_enabled &&
|
||||
new_time != Rational(cur_sst_num, cur_sst_den))) {
|
||||
oakengine_footage_set_source_start_time(
|
||||
facade_handle, new_enabled ? 1 : 0, new_time.numerator(),
|
||||
new_time.denominator());
|
||||
}
|
||||
}
|
||||
|
||||
int total_stream_count =
|
||||
oakengine_footage_get_video_stream_count(facade_handle) +
|
||||
oakengine_footage_get_audio_stream_count(facade_handle) +
|
||||
oakengine_footage_get_subtitle_stream_count(facade_handle);
|
||||
|
||||
for (int i = 0; i < total_stream_count; i++) {
|
||||
int reference_type = -1;
|
||||
int reference_index = -1;
|
||||
oakengine_footage_get_stream_reference(facade_handle, i,
|
||||
&reference_type,
|
||||
&reference_index);
|
||||
bool new_stream_enabled =
|
||||
(track_list_->item(i)->checkState() == Qt::Checked);
|
||||
bool old_stream_enabled = new_stream_enabled;
|
||||
|
||||
switch (reference_type) {
|
||||
case OAKENGINE_TRACK_TYPE_VIDEO:
|
||||
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_VIDEO, reference_index);
|
||||
break;
|
||||
case OAKENGINE_TRACK_TYPE_AUDIO:
|
||||
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_AUDIO, reference_index);
|
||||
break;
|
||||
case OAKENGINE_TRACK_TYPE_SUBTITLE:
|
||||
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference_index);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (old_stream_enabled != new_stream_enabled) {
|
||||
oakengine_footage_set_stream_enabled(
|
||||
facade_handle, reference_type, reference_index,
|
||||
new_stream_enabled ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
oakengine_footage_free(facade_handle);
|
||||
|
||||
void *command = oakengine_undo_command_create_multi();
|
||||
for (int i = 0; i < stacked_widget_->count(); i++) {
|
||||
static_cast<StreamProperties *>(stacked_widget_->widget(i))
|
||||
->accept(command);
|
||||
}
|
||||
oakengine_undo_command_free(command); // stream pages write through the facade directly
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_MEDIAPROPERTIESDIALOG_H
|
||||
#define OAK_MEDIAPROPERTIESDIALOG_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QStackedWidget>
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The MediaPropertiesDialog class
|
||||
*
|
||||
* A dialog for setting properties on Media. This can be loaded from any part of the application provided it's given
|
||||
* a valid Media object.
|
||||
*/
|
||||
class FootagePropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief MediaPropertiesDialog Constructor
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* QWidget parent. Usually MainWindow or Project panel.
|
||||
*
|
||||
* @param i
|
||||
*
|
||||
* Media object to set properties for.
|
||||
*/
|
||||
FootagePropertiesDialog(QWidget *parent, OakEngineNode *footage);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Stack of widgets that changes based on whether the stream is a video or audio stream
|
||||
*/
|
||||
QStackedWidget *stacked_widget_;
|
||||
|
||||
/**
|
||||
* @brief Media name text field
|
||||
*/
|
||||
QLineEdit *footage_name_field_;
|
||||
|
||||
/**
|
||||
* @brief Whether a manual source start time should be used
|
||||
*/
|
||||
QCheckBox *source_start_time_enable_;
|
||||
|
||||
/**
|
||||
* @brief Source start time in seconds
|
||||
*/
|
||||
QDoubleSpinBox *source_start_time_spin_;
|
||||
|
||||
/**
|
||||
* @brief Internal handle to the footage node (set in constructor)
|
||||
*/
|
||||
OakEngineNode *footage_;
|
||||
|
||||
/**
|
||||
* @brief A list widget for listing the tracks in Media
|
||||
*/
|
||||
QListWidget *track_list_;
|
||||
|
||||
/**
|
||||
* @brief Frame rate to conform to
|
||||
*/
|
||||
QDoubleSpinBox *conform_fr_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Overridden accept function for saving the properties back to the Media class
|
||||
*/
|
||||
void accept();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_MEDIAPROPERTIESDIALOG_H
|
||||
@@ -1,26 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2020 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/footageproperties/streamproperties/streamproperties.h
|
||||
dialog/footageproperties/streamproperties/streamproperties.cpp
|
||||
dialog/footageproperties/streamproperties/audiostreamproperties.h
|
||||
dialog/footageproperties/streamproperties/audiostreamproperties.cpp
|
||||
dialog/footageproperties/streamproperties/videostreamproperties.h
|
||||
dialog/footageproperties/streamproperties/videostreamproperties.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,40 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "audiostreamproperties.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
AudioStreamProperties::AudioStreamProperties(OakEngineNode *footage,
|
||||
int audio_index)
|
||||
: footage_(footage)
|
||||
, audio_index_(audio_index)
|
||||
{
|
||||
}
|
||||
|
||||
void AudioStreamProperties::accept(void *)
|
||||
{
|
||||
Q_UNUSED(footage_)
|
||||
Q_UNUSED(audio_index_)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_AUDIOSTREAMPROPERTIES_H
|
||||
#define OAK_AUDIOSTREAMPROPERTIES_H
|
||||
|
||||
#include "streamproperties.h"
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class AudioStreamProperties : public StreamProperties {
|
||||
public:
|
||||
AudioStreamProperties(OakEngineNode *footage, int audio_index);
|
||||
|
||||
virtual void accept(void *parent) override;
|
||||
|
||||
private:
|
||||
OakEngineNode *footage_;
|
||||
|
||||
int audio_index_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_AUDIOSTREAMPROPERTIES_H
|
||||
@@ -1,32 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "streamproperties.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
StreamProperties::StreamProperties(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_STREAMPROPERTIES_H
|
||||
#define OAK_STREAMPROPERTIES_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "oakutil/define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class StreamProperties : public QWidget {
|
||||
public:
|
||||
StreamProperties(QWidget *parent = nullptr);
|
||||
|
||||
virtual void accept(void *)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool sanity_check()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_STREAMPROPERTIES_H
|
||||
@@ -1,272 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "videostreamproperties.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QInputDialog>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "oakengine/color.h"
|
||||
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||
#include "oakengine/footage.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "oakengine/viewer.h"
|
||||
#include "oakengine/videoparams.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
VideoStreamProperties::VideoStreamProperties(OakEngineNode *footage,
|
||||
int video_index)
|
||||
: footage_(footage)
|
||||
, video_index_(video_index)
|
||||
, video_premultiply_alpha_(nullptr)
|
||||
{
|
||||
QGridLayout *video_layout = new QGridLayout(this);
|
||||
video_layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
int row = 0;
|
||||
|
||||
video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0);
|
||||
|
||||
oak_video_params vpod;
|
||||
oakengine_viewer_get_video_params(footage_, video_index_, &vpod);
|
||||
|
||||
// Stream override values come through the liboakengine C ABI facade;
|
||||
// layout-only conditions (channel count, video type) stay direct reads.
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||
char colorspace_buf[256];
|
||||
int color_range = 0;
|
||||
int interlacing = 0;
|
||||
int premultiplied = 0;
|
||||
oakengine_footage_get_video_stream_overrides(
|
||||
facade_handle, video_index_, colorspace_buf, sizeof(colorspace_buf),
|
||||
&color_range, &interlacing, &premultiplied);
|
||||
int par_num = 1, par_den = 1;
|
||||
oakengine_footage_get_pixel_aspect(facade_handle, video_index_, &par_num,
|
||||
&par_den);
|
||||
|
||||
pixel_aspect_combo_ = new PixelAspectRatioComboBox();
|
||||
pixel_aspect_combo_->set_pixel_aspect_ratio(Rational(par_num, par_den));
|
||||
video_layout->addWidget(pixel_aspect_combo_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0);
|
||||
|
||||
video_interlace_combo_ = new InterlacedComboBox();
|
||||
video_interlace_combo_->set_interlace_mode(interlacing);
|
||||
|
||||
video_layout->addWidget(video_interlace_combo_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
video_layout->addWidget(new QLabel(tr("Color Space:")), row, 0);
|
||||
|
||||
video_color_space_ = new QComboBox();
|
||||
|
||||
// The dropdown's color space list comes through the facade (same list
|
||||
// the engine's color config reports).
|
||||
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||
oakengine_node_get_project(footage_));
|
||||
video_color_space_->addItem(tr("Default (%1)")
|
||||
.arg(oak_query_string([cm](char *buf, int size) {
|
||||
return oakengine_color_manager_default_input_color_space(
|
||||
cm, buf, size);
|
||||
})));
|
||||
|
||||
const int colorspace_count =
|
||||
oakengine_footage_colorspace_count(facade_handle);
|
||||
for (int i = 0; i < colorspace_count; i++) {
|
||||
char name_buf[256];
|
||||
if (oakengine_footage_colorspace_at(facade_handle, i, name_buf,
|
||||
sizeof(name_buf)) > 0) {
|
||||
video_color_space_->addItem(QString::fromUtf8(name_buf));
|
||||
}
|
||||
}
|
||||
|
||||
video_color_space_->setCurrentText(QString::fromUtf8(colorspace_buf));
|
||||
|
||||
video_layout->addWidget(video_color_space_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
video_layout->addWidget(new QLabel(tr("Color Range:")), row, 0);
|
||||
|
||||
color_range_combo_ = new QComboBox();
|
||||
color_range_combo_->addItem(tr("Limited (16-235)"),
|
||||
0);
|
||||
color_range_combo_->addItem(tr("Full (0-255)"),
|
||||
1);
|
||||
color_range_combo_->setCurrentIndex(color_range);
|
||||
|
||||
video_layout->addWidget(color_range_combo_, row, 1);
|
||||
|
||||
if (oakengine_video_params_internal_channel_count() == 4) {
|
||||
row++;
|
||||
|
||||
video_premultiply_alpha_ = new QCheckBox(tr("Premultiplied Alpha"));
|
||||
video_premultiply_alpha_->setChecked(premultiplied != 0);
|
||||
video_layout->addWidget(video_premultiply_alpha_, row, 0, 1, 2);
|
||||
}
|
||||
|
||||
row++;
|
||||
|
||||
if (vpod.video_type == 2) {
|
||||
QGroupBox *imgseq_group = new QGroupBox(tr("Image Sequence"));
|
||||
QGridLayout *imgseq_layout = new QGridLayout(imgseq_group);
|
||||
|
||||
int imgseq_row = 0;
|
||||
|
||||
imgseq_layout->addWidget(new QLabel(tr("Start Index:")), imgseq_row, 0);
|
||||
|
||||
int64_t seq_start = 0, seq_duration = 0;
|
||||
int fr_num = 0, fr_den = 1;
|
||||
oakengine_footage_get_image_sequence_params(
|
||||
facade_handle, video_index_, &seq_start, &seq_duration, &fr_num,
|
||||
&fr_den);
|
||||
|
||||
imgseq_start_time_ = new IntegerSlider();
|
||||
imgseq_start_time_->set_minimum(0);
|
||||
imgseq_start_time_->set_value(seq_start);
|
||||
imgseq_layout->addWidget(imgseq_start_time_, imgseq_row, 1);
|
||||
|
||||
imgseq_row++;
|
||||
|
||||
imgseq_layout->addWidget(new QLabel(tr("End Index:")), imgseq_row, 0);
|
||||
|
||||
imgseq_end_time_ = new IntegerSlider();
|
||||
imgseq_end_time_->set_minimum(0);
|
||||
imgseq_end_time_->set_value(seq_start + seq_duration - 1);
|
||||
imgseq_layout->addWidget(imgseq_end_time_, imgseq_row, 1);
|
||||
|
||||
imgseq_row++;
|
||||
|
||||
imgseq_layout->addWidget(new QLabel(tr("Frame Rate:")), imgseq_row, 0);
|
||||
|
||||
imgseq_frame_rate_ = new FrameRateComboBox();
|
||||
imgseq_frame_rate_->set_frame_rate(Rational(fr_num, fr_den));
|
||||
imgseq_layout->addWidget(imgseq_frame_rate_, imgseq_row, 1);
|
||||
|
||||
video_layout->addWidget(imgseq_group, row, 0, 1, 2);
|
||||
}
|
||||
|
||||
oakengine_footage_free(facade_handle);
|
||||
}
|
||||
|
||||
void VideoStreamProperties::accept(void *parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||
|
||||
QString set_colorspace;
|
||||
|
||||
if (video_color_space_->currentIndex() > 0) {
|
||||
set_colorspace = video_color_space_->currentText();
|
||||
}
|
||||
|
||||
// Fetch current values through the facade (avoids the inline
|
||||
// ViewerOutput::get_video_params() which references k_video_params_input).
|
||||
char vp_colorspace[256];
|
||||
vp_colorspace[0] = '\0';
|
||||
int vp_color_range = 0, vp_interlacing = 0, vp_premultiplied = 0;
|
||||
oakengine_footage_get_video_stream_overrides(
|
||||
facade_handle, video_index_, vp_colorspace, sizeof(vp_colorspace),
|
||||
&vp_color_range, &vp_interlacing, &vp_premultiplied);
|
||||
|
||||
int vp_par_num = 1, vp_par_den = 1;
|
||||
oakengine_footage_get_pixel_aspect(facade_handle, video_index_,
|
||||
&vp_par_num, &vp_par_den);
|
||||
|
||||
oak_video_params vpod;
|
||||
oakengine_viewer_get_video_params(footage_, video_index_, &vpod);
|
||||
|
||||
int64_t vp_start_time = 0, vp_duration = 0;
|
||||
int vp_fr_num = 0, vp_fr_den = 1;
|
||||
oakengine_footage_get_image_sequence_params(
|
||||
facade_handle, video_index_, &vp_start_time, &vp_duration,
|
||||
&vp_fr_num, &vp_fr_den);
|
||||
|
||||
// Write every override through the facade (each call is one undoable
|
||||
// command on the shared undo stack, replacing this dialog's own undo
|
||||
// command classes with identical semantics).
|
||||
if ((video_premultiply_alpha_ &&
|
||||
video_premultiply_alpha_->isChecked() != (vp_premultiplied != 0)) ||
|
||||
set_colorspace != QString::fromUtf8(vp_colorspace) ||
|
||||
video_interlace_combo_->currentIndex() != vp_interlacing ||
|
||||
color_range_combo_->currentData().toInt() != vp_color_range) {
|
||||
oakengine_footage_set_video_stream_overrides(
|
||||
facade_handle, video_index_,
|
||||
set_colorspace.toUtf8().constData(),
|
||||
color_range_combo_->currentData().toInt(),
|
||||
video_interlace_combo_->currentIndex(),
|
||||
video_premultiply_alpha_ ?
|
||||
(video_premultiply_alpha_->isChecked() ? 1 : 0) :
|
||||
-1);
|
||||
}
|
||||
|
||||
const Rational new_par = pixel_aspect_combo_->get_pixel_aspect_ratio();
|
||||
if (new_par != Rational(vp_par_num, vp_par_den)) {
|
||||
oakengine_footage_set_pixel_aspect(facade_handle, video_index_,
|
||||
new_par.numerator(),
|
||||
new_par.denominator());
|
||||
}
|
||||
|
||||
if (vpod.video_type == 2) {
|
||||
int64_t new_dur =
|
||||
imgseq_end_time_->get_value() - imgseq_start_time_->get_value() + 1;
|
||||
|
||||
if (vp_start_time != imgseq_start_time_->get_value() ||
|
||||
vp_duration != new_dur ||
|
||||
Rational(vp_fr_num, vp_fr_den) != imgseq_frame_rate_->get_frame_rate()) {
|
||||
const Rational fr = imgseq_frame_rate_->get_frame_rate();
|
||||
oakengine_footage_set_image_sequence_params(
|
||||
facade_handle, video_index_,
|
||||
imgseq_start_time_->get_value(), new_dur, fr.numerator(),
|
||||
fr.denominator());
|
||||
}
|
||||
}
|
||||
|
||||
oakengine_footage_free(facade_handle);
|
||||
}
|
||||
|
||||
bool VideoStreamProperties::sanity_check()
|
||||
{
|
||||
oak_video_params vpod;
|
||||
oakengine_viewer_get_video_params(footage_, video_index_, &vpod);
|
||||
if (vpod.video_type == 2) {
|
||||
if (imgseq_start_time_->get_value() >= imgseq_end_time_->get_value()) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Invalid Configuration"),
|
||||
tr("Image sequence end index must be a value higher than the start index."),
|
||||
QMessageBox::Ok);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_VIDEOSTREAMPROPERTIES_H
|
||||
#define OAK_VIDEOSTREAMPROPERTIES_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "streamproperties.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
#include "widget/standardcombos/standardcombos.h"
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class VideoStreamProperties : public StreamProperties {
|
||||
Q_OBJECT
|
||||
public:
|
||||
VideoStreamProperties(OakEngineNode *footage, int video_index);
|
||||
|
||||
virtual void accept(void *parent) override;
|
||||
|
||||
virtual bool sanity_check() override;
|
||||
|
||||
private:
|
||||
OakEngineNode *footage_;
|
||||
|
||||
int video_index_;
|
||||
|
||||
/**
|
||||
* @brief Setting for associated/premultiplied alpha
|
||||
*/
|
||||
QCheckBox *video_premultiply_alpha_;
|
||||
|
||||
/**
|
||||
* @brief Setting for this media's color space
|
||||
*/
|
||||
QComboBox *video_color_space_;
|
||||
|
||||
/**
|
||||
* @brief Setting for this streams's color range
|
||||
*/
|
||||
QComboBox *color_range_combo_;
|
||||
|
||||
/**
|
||||
* @brief Setting for video interlacing
|
||||
*/
|
||||
InterlacedComboBox *video_interlace_combo_;
|
||||
|
||||
/**
|
||||
* @brief Sets the start index for image sequences
|
||||
*/
|
||||
IntegerSlider *imgseq_start_time_;
|
||||
|
||||
/**
|
||||
* @brief Sets the end index for image sequences
|
||||
*/
|
||||
IntegerSlider *imgseq_end_time_;
|
||||
|
||||
/**
|
||||
* @brief Sets the frame rate for image sequences
|
||||
*/
|
||||
FrameRateComboBox *imgseq_frame_rate_;
|
||||
|
||||
/**
|
||||
* @brief Sets the pixel aspect ratio of the stream
|
||||
*/
|
||||
PixelAspectRatioComboBox *pixel_aspect_combo_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_VIDEOSTREAMPROPERTIES_H
|
||||
@@ -1,22 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/footagerelink/footagerelinkdialog.h
|
||||
dialog/footagerelink/footagerelinkdialog.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,228 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "footagerelinkdialog.h"
|
||||
|
||||
#include "core.h"
|
||||
#include "common/nodedatatypes.h"
|
||||
#include "oakengine/footage.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "oakengine/project.h"
|
||||
#include "oakutil/oaknode.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QScrollBar>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
FootageRelinkDialog::FootageRelinkDialog(const QVector<OakEngineNode *> &footage,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, footage_(footage)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
layout->addWidget(new QLabel(
|
||||
"The following files couldn't be found. Clips using them will be "
|
||||
"unplayable until they're relinked."));
|
||||
|
||||
table_ = new QTreeWidget();
|
||||
|
||||
table_->setColumnCount(3);
|
||||
table_->setHeaderLabels({ tr("Footage"), tr("Filename"), tr("Actions") });
|
||||
table_->setRootIsDecorated(false);
|
||||
table_->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
table_->header()->setSectionsMovable(false);
|
||||
|
||||
// Prefer stretching URL column (QHeaderView defaults to stretching the last column, which in
|
||||
// our case is just a browse button)
|
||||
table_->header()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
table_->header()->setStretchLastSection(false);
|
||||
|
||||
for (int i = 0; i < footage.size(); i++) {
|
||||
OakEngineNode *f = footage.at(i);
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem();
|
||||
|
||||
QWidget *item_actions = new QWidget();
|
||||
QHBoxLayout *item_actions_layout = new QHBoxLayout(item_actions);
|
||||
QPushButton *item_browse_btn = new QPushButton(tr("Browse"));
|
||||
item_browse_btn->setProperty("index", i);
|
||||
connect(item_browse_btn, &QPushButton::clicked, this,
|
||||
&FootageRelinkDialog::browse_for_footage);
|
||||
item_actions_layout->addWidget(item_browse_btn);
|
||||
|
||||
item->setIcon(
|
||||
0, oak::Node(f).data(k_node_data_icon).value<QIcon>());
|
||||
item->setText(0, oak::Node(f).get_label());
|
||||
item->setText(1, oak::Footage::borrow(f).filename());
|
||||
|
||||
table_->addTopLevelItem(item);
|
||||
|
||||
table_->setItemWidget(item, 2, item_actions);
|
||||
}
|
||||
|
||||
layout->addWidget(table_);
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||
&FootageRelinkDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||
&FootageRelinkDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
setWindowTitle(tr("Relink Footage"));
|
||||
}
|
||||
|
||||
void FootageRelinkDialog::update_footage_item(int index)
|
||||
{
|
||||
OakEngineNode *f = footage_.at(index);
|
||||
QTreeWidgetItem *item = table_->topLevelItem(index);
|
||||
item->setIcon(0, oak::Node(f).data(k_node_data_icon).value<QIcon>());
|
||||
item->setText(1, oak::Footage::borrow(f).filename());
|
||||
}
|
||||
|
||||
void FootageRelinkDialog::browse_for_footage()
|
||||
{
|
||||
int index = sender()->property("index").toInt();
|
||||
OakEngineNode *f = footage_.at(index);
|
||||
|
||||
QFileInfo info(oak::Footage::borrow(f).filename());
|
||||
|
||||
QString new_fn = QFileDialog::getOpenFileName(
|
||||
this, tr("Relink \"%1\"").arg(oak::Node(f).get_label()),
|
||||
info.absolutePath(),
|
||||
Core::footage_file_dialog_filter());
|
||||
|
||||
// Originally, this function would attempt to filter to the exact filename of the missing file.
|
||||
// However, this would break on Windows if the filename had any spaces in it. The reason is
|
||||
// Windows separates its extensions with ';' while Qt separates them with ' '. Qt isn't
|
||||
// intelligent enough to determine whether it's a list of extensions or a single filename with a
|
||||
// space in it, it just does a global replace of ' ' to ';'. There's no way around it, outside of
|
||||
// bypassing Qt entirely and using Win32's GetOpenFileName() directly. As annoying as it is, I've
|
||||
// just disabled it for now.
|
||||
//QStringLiteral("%1 (\"%1\");;%2 (*)").arg(info.fileName(), tr("All Files")));
|
||||
|
||||
// We received a new filename
|
||||
if (!new_fn.isEmpty()) {
|
||||
if (!Core::is_footage_extension_allowed(new_fn)) {
|
||||
QMessageBox::warning(
|
||||
this, tr("Unsupported media"),
|
||||
tr("This file type is not allowed by the current media type "
|
||||
"filter."));
|
||||
return;
|
||||
}
|
||||
// Store original dir since we might be able to use this to find other files
|
||||
QDir original_dir = info.dir();
|
||||
QDir new_dir = QFileInfo(new_fn).dir();
|
||||
|
||||
// Relink through the facade (reprobes the file and resets stream /
|
||||
// proxy state; relinked footage becomes valid when the probe
|
||||
// succeeds).
|
||||
OakEngineFootage *relink_handle = oakengine_footage_borrow(f);
|
||||
const int relink_rc = oakengine_footage_relink(
|
||||
relink_handle, new_fn.toUtf8().constData());
|
||||
oakengine_footage_free(relink_handle);
|
||||
if (relink_rc != OAKENGINE_OK) {
|
||||
char err[512];
|
||||
err[0] = '\0';
|
||||
oakengine_footage_last_error(err, sizeof(err));
|
||||
QMessageBox::warning(this, tr("Cannot relink footage"),
|
||||
err[0] ? QString::fromUtf8(err) :
|
||||
tr("The file could not be used as media."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Update item visually
|
||||
update_footage_item(index);
|
||||
|
||||
// Check all other footage files for matches in the new directory
|
||||
// (facade's exact file-name matching, mirroring the second attempt
|
||||
// of the old per-footage loop).
|
||||
OakEngineProject *project = oakengine_node_get_project(f);
|
||||
if (project) {
|
||||
oakengine_project_find_offline_footage(
|
||||
project, new_dir.absolutePath().toUtf8().constData());
|
||||
|
||||
// The old dialog also tried the original directory's relative
|
||||
// paths, which the facade's exact-name matching does not cover;
|
||||
// keep that pass here.
|
||||
for (int it = 0; it < footage_.size(); it++) {
|
||||
OakEngineNode *other_footage = footage_.at(it);
|
||||
|
||||
// Ignore footage that's already valid of course
|
||||
if (!oakengine_footage_is_valid(other_footage)) {
|
||||
// Get footage path relative to original directory
|
||||
QString relative_to_original =
|
||||
original_dir.relativeFilePath(
|
||||
oak::Footage::borrow(other_footage).filename());
|
||||
QString absolute_to_new =
|
||||
new_dir.filePath(relative_to_original);
|
||||
|
||||
if (QFileInfo::exists(absolute_to_new)) {
|
||||
OakEngineFootage *other_handle =
|
||||
oakengine_footage_borrow(other_footage);
|
||||
oakengine_footage_relink(
|
||||
other_handle,
|
||||
absolute_to_new.toUtf8().constData());
|
||||
oakengine_footage_free(other_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh every row whose validity may have changed.
|
||||
for (int it = 0; it < footage_.size(); it++) {
|
||||
update_footage_item(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check where the next invalid footage is. If there is none, accept automatically. Otherwise,
|
||||
// jump to that footage so the user knows where it is.
|
||||
int next_invalid = -1;
|
||||
for (int i = 0; i < footage_.size(); i++) {
|
||||
if (!oakengine_footage_is_valid(footage_.at(i))) {
|
||||
next_invalid = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (next_invalid == -1) {
|
||||
// No more invalid footage, just accept
|
||||
this->accept();
|
||||
} else {
|
||||
// Jump to next invalid footage
|
||||
QModelIndex idx = table_->model()->index(next_invalid, 0);
|
||||
table_->selectionModel()->select(idx, QItemSelectionModel::Select |
|
||||
QItemSelectionModel::Rows);
|
||||
table_->scrollTo(idx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_FOOTAGERELINKDIALOG_H
|
||||
#define OAK_FOOTAGERELINKDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTreeWidget>
|
||||
#include <QVector>
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class FootageRelinkDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FootageRelinkDialog(const QVector<OakEngineNode *> &footage,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void update_footage_item(int index);
|
||||
|
||||
QTreeWidget *table_;
|
||||
|
||||
QVector<OakEngineNode *> footage_;
|
||||
|
||||
private slots:
|
||||
void browse_for_footage();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_FOOTAGERELINKDIALOG_H
|
||||
@@ -1,22 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/keyframeproperties/keyframeproperties.h
|
||||
dialog/keyframeproperties/keyframeproperties.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,323 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "keyframeproperties.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
|
||||
#include "common/keyframetypes.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "olive/core/util/timecodefunctions.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
Rational keyframe_rational_time(const oak::Keyframe &key)
|
||||
{
|
||||
int64_t num = 0, den = 1;
|
||||
key.time(&num, &den);
|
||||
return Rational(int(num), int(den));
|
||||
}
|
||||
|
||||
// Selected keyframes grouped by owning input (node/id/element), with
|
||||
// times converted to the facade's frame timestamps. The dialog's
|
||||
// per-property writes go through the facade as ONE undoable command per
|
||||
// group (usually just one).
|
||||
struct KeyGroup {
|
||||
OakEngineNode *node;
|
||||
QString input;
|
||||
int element;
|
||||
QVector<int64_t> times;
|
||||
QVector<int> tracks;
|
||||
};
|
||||
|
||||
QVector<KeyGroup> group_keys(const QVector<oak::Keyframe> &keys)
|
||||
{
|
||||
QVector<KeyGroup> groups;
|
||||
for (const oak::Keyframe &item : keys) {
|
||||
OakEngineNode *node = item.node().handle();
|
||||
int g = 0;
|
||||
for (; g < groups.size(); g++) {
|
||||
if (groups.at(g).node == node &&
|
||||
groups.at(g).input == item.input_id() &&
|
||||
groups.at(g).element == item.element()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (g == groups.size()) {
|
||||
groups.append({ node, item.input_id(), item.element(),
|
||||
{}, {} });
|
||||
}
|
||||
int tbn = 0, tbd = 0;
|
||||
oakengine_node_frame_time_base(node, &tbn, &tbd);
|
||||
groups[g].times.append(Timecode::time_to_timestamp(
|
||||
keyframe_rational_time(item), Rational(tbn, tbd), Timecode::k_round));
|
||||
groups[g].tracks.append(item.track());
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
KeyframePropertiesDialog::KeyframePropertiesDialog(
|
||||
const QVector<oak::Keyframe> &keys, const Rational &timebase,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, keys_(keys)
|
||||
, timebase_(timebase)
|
||||
{
|
||||
setWindowTitle(tr("Keyframe Properties"));
|
||||
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(new QLabel("Time:"), row, 0);
|
||||
|
||||
time_slider_ = new RationalSlider();
|
||||
time_slider_->set_display_type(slider::k_time);
|
||||
time_slider_->set_timebase(timebase_);
|
||||
layout->addWidget(time_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel("Type:"), row, 0);
|
||||
|
||||
type_select_ = new QComboBox();
|
||||
connect(type_select_, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(key_type_changed(int)));
|
||||
layout->addWidget(type_select_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Bezier handles
|
||||
bezier_group_ = new QGroupBox();
|
||||
|
||||
QGridLayout *bezier_group_layout = new QGridLayout(bezier_group_);
|
||||
|
||||
bezier_group_layout->addWidget(new QLabel(tr("In:")), 0, 0);
|
||||
|
||||
bezier_in_x_slider_ = new FloatSlider();
|
||||
bezier_group_layout->addWidget(bezier_in_x_slider_, 0, 1);
|
||||
|
||||
bezier_in_y_slider_ = new FloatSlider();
|
||||
bezier_group_layout->addWidget(bezier_in_y_slider_, 0, 2);
|
||||
|
||||
bezier_group_layout->addWidget(new QLabel(tr("Out:")), 1, 0);
|
||||
|
||||
bezier_out_x_slider_ = new FloatSlider();
|
||||
bezier_group_layout->addWidget(bezier_out_x_slider_, 1, 1);
|
||||
|
||||
bezier_out_y_slider_ = new FloatSlider();
|
||||
bezier_group_layout->addWidget(bezier_out_y_slider_, 1, 2);
|
||||
|
||||
layout->addWidget(bezier_group_, row, 0, 1, 2);
|
||||
|
||||
bool all_same_time = true;
|
||||
bool can_set_time = true;
|
||||
|
||||
bool all_same_type = true;
|
||||
|
||||
bool all_same_bezier_in_x = true;
|
||||
bool all_same_bezier_in_y = true;
|
||||
bool all_same_bezier_out_x = true;
|
||||
bool all_same_bezier_out_y = true;
|
||||
|
||||
for (int i = 0; i < keys_.size(); i++) {
|
||||
if (i > 0) {
|
||||
const oak::Keyframe &prev_key = keys_.at(i - 1);
|
||||
const oak::Keyframe &this_key = keys_.at(i);
|
||||
|
||||
// Determine if the keyframes are all the same time or not
|
||||
if (all_same_time) {
|
||||
all_same_time = (keyframe_rational_time(prev_key) ==
|
||||
keyframe_rational_time(this_key));
|
||||
}
|
||||
|
||||
// Determine if the keyframes are all the same type
|
||||
if (all_same_type) {
|
||||
all_same_type = (prev_key.type() == this_key.type());
|
||||
}
|
||||
|
||||
// Check all four bezier control points
|
||||
if (all_same_bezier_in_x) {
|
||||
all_same_bezier_in_x = (prev_key.bezier_point(0).x() ==
|
||||
this_key.bezier_point(0).x());
|
||||
}
|
||||
|
||||
if (all_same_bezier_in_y) {
|
||||
all_same_bezier_in_y = (prev_key.bezier_point(0).y() ==
|
||||
this_key.bezier_point(0).y());
|
||||
}
|
||||
|
||||
if (all_same_bezier_out_x) {
|
||||
all_same_bezier_out_x = (prev_key.bezier_point(1).x() ==
|
||||
this_key.bezier_point(1).x());
|
||||
}
|
||||
|
||||
if (all_same_bezier_out_y) {
|
||||
all_same_bezier_out_y = (prev_key.bezier_point(1).y() ==
|
||||
this_key.bezier_point(1).y());
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if any keyframes are on the same track (in which case we can't set the time)
|
||||
if (can_set_time) {
|
||||
for (int j = 0; j < keys_.size(); j++) {
|
||||
if (i != j && keys_.at(j).track() == keys_.at(i).track()) {
|
||||
can_set_time = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!all_same_time && !all_same_type && !can_set_time &&
|
||||
!all_same_bezier_in_x && !all_same_bezier_in_y &&
|
||||
!all_same_bezier_out_x && !all_same_bezier_out_y) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_same_time) {
|
||||
time_slider_->set_value(keyframe_rational_time(keys_.front()));
|
||||
} else {
|
||||
time_slider_->set_tristate();
|
||||
}
|
||||
|
||||
time_slider_->setEnabled(can_set_time);
|
||||
|
||||
if (!all_same_type) {
|
||||
// If all keyframes aren't the same type, add an empty item
|
||||
type_select_->addItem(QStringLiteral("--"), -1);
|
||||
|
||||
// Ensure UI updates for the index being 0
|
||||
key_type_changed(0);
|
||||
}
|
||||
|
||||
// Item data uses the facade easing order (oak::Keyframe::type()):
|
||||
// 0 = linear, 1 = bezier, 2 = hold.
|
||||
type_select_->addItem(tr("Linear"), KeyframeTypes::k_facade_linear);
|
||||
type_select_->addItem(tr("Hold"), KeyframeTypes::k_facade_hold);
|
||||
type_select_->addItem(tr("Bezier"), KeyframeTypes::k_facade_bezier);
|
||||
|
||||
if (all_same_type) {
|
||||
// If all keyframes are the same type, set it here
|
||||
for (int i = 0; i < type_select_->count(); i++) {
|
||||
if (type_select_->itemData(i).toInt() == keys_.front().type()) {
|
||||
type_select_->setCurrentIndex(i);
|
||||
|
||||
// Ensure UI updates for this index
|
||||
key_type_changed(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_up_bezier_slider(bezier_in_x_slider_, all_same_bezier_in_x,
|
||||
keys_.front().bezier_point(0).x());
|
||||
set_up_bezier_slider(bezier_in_y_slider_, all_same_bezier_in_y,
|
||||
keys_.front().bezier_point(0).y());
|
||||
set_up_bezier_slider(bezier_out_x_slider_, all_same_bezier_out_x,
|
||||
keys_.front().bezier_point(1).x());
|
||||
set_up_bezier_slider(bezier_out_y_slider_, all_same_bezier_out_y,
|
||||
keys_.front().bezier_point(1).y());
|
||||
|
||||
row++;
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttons->setCenterButtons(true);
|
||||
layout->addWidget(buttons, row, 0, 1, 2);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
void KeyframePropertiesDialog::accept()
|
||||
{
|
||||
const Rational new_time = time_slider_->get_value();
|
||||
const int new_type = type_select_->currentData().toInt();
|
||||
|
||||
const QVector<KeyGroup> groups = group_keys(keys_);
|
||||
|
||||
if (new_type > -1) {
|
||||
// new_type is already in the facade's easing order (linear 0 /
|
||||
// bezier 1 / hold 2), so it can be passed straight through.
|
||||
foreach (const KeyGroup &g, groups) {
|
||||
oakengine_node_keyframes_set_type_many(
|
||||
g.node,
|
||||
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
||||
g.tracks.data(), g.times.size(), new_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (bezier_group_->isEnabled()) {
|
||||
foreach (const KeyGroup &g, groups) {
|
||||
oakengine_node_keyframes_set_bezier_many(
|
||||
g.node,
|
||||
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
||||
g.tracks.data(), g.times.size(),
|
||||
bezier_in_x_slider_->get_value(),
|
||||
bezier_in_y_slider_->get_value(),
|
||||
bezier_out_x_slider_->get_value(),
|
||||
bezier_out_y_slider_->get_value());
|
||||
}
|
||||
}
|
||||
|
||||
// Time moves go LAST: the facade addresses keyframes by time, so the
|
||||
// type/bezier writes above must happen while the keys still sit at
|
||||
// the times the groups were built from.
|
||||
if (time_slider_->isEnabled() && !time_slider_->is_tristate()) {
|
||||
foreach (const KeyGroup &g, groups) {
|
||||
OakEngineNode *handle = g.node;
|
||||
int tbn = 0, tbd = 0;
|
||||
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
||||
oakengine_node_keyframes_set_time_many(
|
||||
handle, g.input.toUtf8().constData(), g.element,
|
||||
g.times.constData(), g.tracks.data(), g.times.size(),
|
||||
Timecode::time_to_timestamp(new_time, Rational(tbn, tbd),
|
||||
Timecode::k_round));
|
||||
}
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void KeyframePropertiesDialog::set_up_bezier_slider(FloatSlider *slider,
|
||||
bool all_same, double value)
|
||||
{
|
||||
if (all_same) {
|
||||
slider->set_value(value);
|
||||
} else {
|
||||
slider->set_tristate();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframePropertiesDialog::key_type_changed(int index)
|
||||
{
|
||||
bezier_group_->setEnabled(type_select_->itemData(index) ==
|
||||
KeyframeTypes::k_facade_bezier);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_KEYFRAMEPROPERTIESDIALOG_H
|
||||
#define OAK_KEYFRAMEPROPERTIESDIALOG_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
#include <QGroupBox>
|
||||
|
||||
#include "oakutil/oaknode.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
#include "widget/slider/rationalslider.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class KeyframePropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframePropertiesDialog(const QVector<oak::Keyframe> &keys,
|
||||
const Rational &timebase,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
void set_up_bezier_slider(FloatSlider *slider, bool all_same, double value);
|
||||
|
||||
const QVector<oak::Keyframe> &keys_;
|
||||
|
||||
Rational timebase_;
|
||||
|
||||
RationalSlider *time_slider_;
|
||||
|
||||
QComboBox *type_select_;
|
||||
|
||||
QGroupBox *bezier_group_;
|
||||
|
||||
FloatSlider *bezier_in_x_slider_;
|
||||
|
||||
FloatSlider *bezier_in_y_slider_;
|
||||
|
||||
FloatSlider *bezier_out_x_slider_;
|
||||
|
||||
FloatSlider *bezier_out_y_slider_;
|
||||
|
||||
private slots:
|
||||
void key_type_changed(int index);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_KEYFRAMEPROPERTIESDIALOG_H
|
||||
@@ -1,22 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/markerproperties/markerpropertiesdialog.h
|
||||
dialog/markerproperties/markerpropertiesdialog.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,171 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "markerpropertiesdialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#include "oakengine/timeline.h"
|
||||
#include "widget/timeruler/markerhandle.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super QDialog
|
||||
|
||||
MarkerPropertiesDialog::MarkerPropertiesDialog(
|
||||
const std::vector<OakEngineMarker *> &markers, const Rational &timebase,
|
||||
QWidget *parent)
|
||||
: super(parent)
|
||||
, markers_(markers)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
int row = 0;
|
||||
|
||||
QGroupBox *time_group = new QGroupBox(tr("Time"));
|
||||
QGridLayout *time_layout = new QGridLayout(time_group);
|
||||
|
||||
{
|
||||
int time_row = 0;
|
||||
|
||||
time_layout->addWidget(new QLabel(tr("In:")), time_row, 0);
|
||||
|
||||
in_slider_ = new RationalSlider();
|
||||
time_layout->addWidget(in_slider_, time_row, 1);
|
||||
|
||||
time_row++;
|
||||
|
||||
time_layout->addWidget(new QLabel(tr("Out:")), time_row, 0);
|
||||
|
||||
out_slider_ = new RationalSlider();
|
||||
time_layout->addWidget(out_slider_, time_row, 1);
|
||||
}
|
||||
|
||||
if (markers.size() == 1) {
|
||||
const TimeRange marker_range = marker_time(markers.front());
|
||||
in_slider_->set_value(marker_range.in());
|
||||
in_slider_->set_display_type(slider::k_time);
|
||||
in_slider_->set_timebase(timebase);
|
||||
out_slider_->set_value(marker_range.out());
|
||||
out_slider_->set_display_type(slider::k_time);
|
||||
out_slider_->set_timebase(timebase);
|
||||
} else {
|
||||
// Markers cannot be on the same time, so we disable setting time if multiple markers are selected
|
||||
in_slider_->setEnabled(false);
|
||||
in_slider_->set_tristate();
|
||||
out_slider_->setEnabled(false);
|
||||
out_slider_->set_tristate();
|
||||
}
|
||||
|
||||
layout->addWidget(time_group, row, 0, 1, 2);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Color:")), row, 0);
|
||||
|
||||
color_menu_ = new ColorCodingComboBox();
|
||||
layout->addWidget(color_menu_, row, 1);
|
||||
|
||||
color_menu_->set_color(marker_color(markers.front()));
|
||||
for (size_t i = 1; i < markers.size(); i++) {
|
||||
if (marker_color(markers.at(i)) != color_menu_->get_selected_color()) {
|
||||
color_menu_->set_color(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Name:")), row, 0);
|
||||
|
||||
label_edit_ = new LineEditWithFocusSignal();
|
||||
connect(label_edit_, &LineEditWithFocusSignal::focused, this,
|
||||
[this] { label_edit_->setPlaceholderText(QString()); });
|
||||
layout->addWidget(label_edit_, row, 1);
|
||||
|
||||
// Determine what the startup label text should be
|
||||
label_edit_->setText(marker_name(markers.front()));
|
||||
for (size_t i = 1; i < markers.size(); i++) {
|
||||
if (marker_name(markers.at(i)) != label_edit_->text()) {
|
||||
label_edit_->clear();
|
||||
label_edit_->setPlaceholderText(tr("(multiple)"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
row++;
|
||||
|
||||
QDialogButtonBox *buttons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this,
|
||||
&MarkerPropertiesDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this,
|
||||
&MarkerPropertiesDialog::reject);
|
||||
layout->addWidget(buttons, row, 0, 1, 2);
|
||||
|
||||
setWindowTitle(tr("Edit Markers"));
|
||||
label_edit_->setFocus();
|
||||
}
|
||||
|
||||
void MarkerPropertiesDialog::accept()
|
||||
{
|
||||
if (in_slider_->isEnabled() &&
|
||||
in_slider_->get_value() > out_slider_->get_value()) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Invalid Values"),
|
||||
tr("In point must be less than or equal to out point."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Batch-set properties via facade (one undoable command)
|
||||
{
|
||||
QVector<OakEngineMarker *> oak_markers;
|
||||
for (OakEngineMarker *m : markers_) {
|
||||
oak_markers.append(m);
|
||||
}
|
||||
int color = color_menu_->get_selected_color();
|
||||
QByteArray name_ba;
|
||||
const char *name = nullptr;
|
||||
if (label_edit_->placeholderText().isEmpty()) {
|
||||
name_ba = label_edit_->text().toUtf8();
|
||||
name = name_ba.constData();
|
||||
}
|
||||
oakengine_marker_set_properties(
|
||||
oak_markers.data(), oak_markers.size(), color, name,
|
||||
(markers_.size() == 1) ? 1 : 0,
|
||||
in_slider_->get_value().numerator(),
|
||||
in_slider_->get_value().denominator(),
|
||||
out_slider_->get_value().numerator(),
|
||||
out_slider_->get_value().denominator(),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
super::accept();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user