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:
2026-08-10 20:24:25 +08:00
parent f8540e3892
commit 013a175707
4212 changed files with 8331 additions and 2274987 deletions
-4
View File
@@ -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
View File
@@ -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)
+731 -34
View File
File diff suppressed because it is too large Load Diff
+84
View File
@@ -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"
-221
View File
@@ -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 ()
-81
View File
@@ -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;
}
}
}
-75
View File
@@ -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
-213
View File
@@ -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
-87
View File
@@ -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
-98
View File
@@ -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();
}
}
-507
View File
@@ -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 &current_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;
}
}
-83
View File
@@ -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
-59
View File
@@ -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
-46
View File
@@ -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
-141
View File
@@ -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
-233
View File
@@ -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
-46
View File
@@ -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
-144
View File
@@ -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;
}
}
-76
View File
@@ -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
-87
View File
@@ -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
-168
View File
@@ -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
-220
View File
@@ -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
-60
View File
@@ -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
-47
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
-381
View File
@@ -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
-68
View File
@@ -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})
-410
View File
@@ -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();
}
-82
View File
@@ -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
-47
View File
@@ -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
)
-25
View File
@@ -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
)
-159
View File
@@ -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();
}
}
-62
View File
@@ -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
-26
View File
@@ -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
-84
View File
@@ -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()
-125
View File
@@ -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();
}
}
-72
View File
@@ -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
-22
View File
@@ -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
)
-276
View File
@@ -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();
}
}
-193
View File
@@ -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
-22
View File
@@ -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
-22
View File
@@ -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
)
-252
View File
@@ -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);
}
}
-99
View File
@@ -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
-24
View File
@@ -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
)
-102
View File
@@ -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);
}
}
}
-64
View File
@@ -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
-22
View File
@@ -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
)
-151
View File
@@ -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"));
}
}
}
}
-65
View File
@@ -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
-36
View File
@@ -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
)
-32
View File
@@ -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
)
-141
View File
@@ -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();
}
}
-73
View File
@@ -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());
}
}
}
-47
View File
@@ -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
-32
View File
@@ -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)
{
}
}
-50
View File
@@ -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
-57
View File
@@ -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();
}
}
-45
View File
@@ -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
-336
View File
@@ -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)
{
}
}
-127
View File
@@ -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
-63
View File
@@ -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);
}
}
-77
View File
@@ -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
-143
View File
@@ -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
-139
View File
@@ -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);
}
}
}
-96
View File
@@ -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
-151
View File
@@ -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;
}
}
-78
View File
@@ -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
-80
View File
@@ -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;
}
}
-81
View File
@@ -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
-314
View File
@@ -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);
}
}
}
}
-230
View File
@@ -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
-22
View File
@@ -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