R8: finish app/ pure C ABI migration (P3-P9) and make OTIO required
- app/ no longer includes engine C++ headers nor holds engine C++ types: engine access goes through the oakengine C ABI plus C++ wrappers (oakutil/oaknode.h, oakutil/oakvideo.h) and app-local mirror types (tooltypes, trackreferencehandle, timelinecommonapp, keyframetypes, subtitleapp, serializedlayoutinfoapp, nodevaluehandle, sliderdisplaytypeapp) - engine: new C ABI functions for block/track/clip/transition navigation and predicates, links, caches, waveform/playback, disk folder, sequence_track_list, node_free, footage_is_valid, block_get_track, get_brush; loadotio/saveotio ported to the current engine API - OTIO is now a required dependency: CI and CD build it on every platform, FindOpenTimelineIO fixed for OTIO 0.16/0.19 (the old deps include requirement silently disabled OTIO everywhere), runtime libraries are bundled into packages and copied next to macOS binaries (oak_copy_otio_runtime) - fix ProjectViewModel drag&drop mime read/write size mismatch (segfault) - unify color label naming (k_olive -> "Oak") in the app-side mirror - docs: OTIO required, FFmpeg minimum corrected to 6.0 (en/zh) - gtest suite: 1925 passed, 0 failed
This commit is contained in:
@@ -43,11 +43,25 @@ jobs:
|
|||||||
mingw-w64-ucrt-x86_64-nsis
|
mingw-w64-ucrt-x86_64-nsis
|
||||||
mingw-w64-ucrt-x86_64-ntldd
|
mingw-w64-ucrt-x86_64-ntldd
|
||||||
|
|
||||||
|
- name: Build OpenTimelineIO
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||||
|
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||||
|
-DOTIO_SHARED_LIBS=ON \
|
||||||
|
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||||
|
-DOTIO_FIND_IMATH=ON \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||||
|
cmake --build OpenTimelineIO/build
|
||||||
|
cmake --install OpenTimelineIO/build
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B build -G Ninja \
|
cmake -S . -B build -G Ninja \
|
||||||
-DBUILD_QT6=ON \
|
-DBUILD_QT6=ON \
|
||||||
|
-DOTIO_LOCATION="${PWD}/otio-install" \
|
||||||
-DCMAKE_BUILD_TYPE=Release
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -57,6 +71,8 @@ jobs:
|
|||||||
- name: Deploy dependencies
|
- name: Deploy dependencies
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
|
# Let ntldd discover the OTIO DLLs (no rpath on Windows)
|
||||||
|
export PATH="${GITHUB_WORKSPACE}/otio-install/bin:${PATH}"
|
||||||
mkdir -p app/packaging/windows/nsis/oak-editor
|
mkdir -p app/packaging/windows/nsis/oak-editor
|
||||||
cp build/app/oak-editor.exe app/packaging/windows/nsis/oak-editor/
|
cp build/app/oak-editor.exe app/packaging/windows/nsis/oak-editor/
|
||||||
cp build/worker/oak-render-worker.exe app/packaging/windows/nsis/oak-editor/
|
cp build/worker/oak-render-worker.exe app/packaging/windows/nsis/oak-editor/
|
||||||
@@ -116,6 +132,7 @@ jobs:
|
|||||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||||
-DOTIO_SHARED_LIBS=ON \
|
-DOTIO_SHARED_LIBS=ON \
|
||||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||||
|
-DOTIO_FIND_IMATH=ON \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||||
cmake --build OpenTimelineIO/build
|
cmake --build OpenTimelineIO/build
|
||||||
@@ -316,11 +333,24 @@ jobs:
|
|||||||
libvulkan-dev \
|
libvulkan-dev \
|
||||||
wget fuse3 desktop-file-utils
|
wget fuse3 desktop-file-utils
|
||||||
|
|
||||||
|
- name: Build OpenTimelineIO
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||||
|
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||||
|
-DOTIO_SHARED_LIBS=ON \
|
||||||
|
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||||
|
-DOTIO_FIND_IMATH=ON \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||||
|
cmake --build OpenTimelineIO/build
|
||||||
|
cmake --install OpenTimelineIO/build
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B build -G Ninja \
|
cmake -S . -B build -G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DOTIO_LOCATION="${PWD}/otio-install" \
|
||||||
-DBUILD_QT6=ON
|
-DBUILD_QT6=ON
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
@@ -366,6 +396,8 @@ jobs:
|
|||||||
ls AppDir/usr/lib/libOpenColorIO.so* || (echo "Missing libOpenColorIO" && exit 1)
|
ls AppDir/usr/lib/libOpenColorIO.so* || (echo "Missing libOpenColorIO" && exit 1)
|
||||||
ls AppDir/usr/lib/libportaudio.so* || (echo "Missing libportaudio" && exit 1)
|
ls AppDir/usr/lib/libportaudio.so* || (echo "Missing libportaudio" && exit 1)
|
||||||
ls AppDir/usr/lib/libOpenEXR*.so* || (echo "Missing libOpenEXR" && exit 1)
|
ls AppDir/usr/lib/libOpenEXR*.so* || (echo "Missing libOpenEXR" && exit 1)
|
||||||
|
ls AppDir/usr/lib/libopentimelineio.so* || (echo "Missing libopentimelineio" && exit 1)
|
||||||
|
ls AppDir/usr/lib/libopentime.so* || (echo "Missing libopentime" && exit 1)
|
||||||
# Ensure Oak's own binaries and shared libraries are present
|
# Ensure Oak's own binaries and shared libraries are present
|
||||||
ls AppDir/usr/bin/oak-editor || (echo "Missing oak-editor" && exit 1)
|
ls AppDir/usr/bin/oak-editor || (echo "Missing oak-editor" && exit 1)
|
||||||
ls AppDir/usr/bin/oak-render-worker || (echo "Missing oak-render-worker" && exit 1)
|
ls AppDir/usr/bin/oak-render-worker || (echo "Missing oak-render-worker" && exit 1)
|
||||||
@@ -410,11 +442,24 @@ jobs:
|
|||||||
portaudio19-dev libgl1-mesa-dev libxkbcommon-dev \
|
portaudio19-dev libgl1-mesa-dev libxkbcommon-dev \
|
||||||
libvulkan-dev
|
libvulkan-dev
|
||||||
|
|
||||||
|
- name: Build OpenTimelineIO
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||||
|
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||||
|
-DOTIO_SHARED_LIBS=ON \
|
||||||
|
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||||
|
-DOTIO_FIND_IMATH=ON \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||||
|
cmake --build OpenTimelineIO/build
|
||||||
|
cmake --install OpenTimelineIO/build
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B build -G Ninja \
|
cmake -S . -B build -G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DOTIO_LOCATION="${PWD}/otio-install" \
|
||||||
-DBUILD_QT6=ON
|
-DBUILD_QT6=ON
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
@@ -464,11 +509,24 @@ jobs:
|
|||||||
bzip2-devel \
|
bzip2-devel \
|
||||||
rpm-build
|
rpm-build
|
||||||
|
|
||||||
|
- name: Build OpenTimelineIO
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||||
|
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||||
|
-DOTIO_SHARED_LIBS=ON \
|
||||||
|
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||||
|
-DOTIO_FIND_IMATH=ON \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||||
|
cmake --build OpenTimelineIO/build
|
||||||
|
cmake --install OpenTimelineIO/build
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B build -G Ninja \
|
cmake -S . -B build -G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DOTIO_LOCATION="${PWD}/otio-install" \
|
||||||
-DBUILD_QT6=ON
|
-DBUILD_QT6=ON
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
@@ -522,7 +580,8 @@ jobs:
|
|||||||
pacman -S --noconfirm --needed \
|
pacman -S --noconfirm --needed \
|
||||||
cmake ninja git pkgconf \
|
cmake ninja git pkgconf \
|
||||||
qt6-base qt6-tools ffmpeg openimageio opencolorio openexr expat portaudio \
|
qt6-base qt6-tools ffmpeg openimageio opencolorio openexr expat portaudio \
|
||||||
mesa vulkan-headers vulkan-icd-loader libxkbcommon fmt
|
mesa vulkan-headers vulkan-icd-loader libxkbcommon fmt \
|
||||||
|
opentimelineio
|
||||||
useradd -m builder
|
useradd -m builder
|
||||||
chown -R builder /build
|
chown -R builder /build
|
||||||
su builder -c "makepkg -s --noconfirm"
|
su builder -c "makepkg -s --noconfirm"
|
||||||
|
|||||||
@@ -53,30 +53,33 @@ jobs:
|
|||||||
echo "$(brew --prefix qt@6)/bin" >> "$GITHUB_PATH"
|
echo "$(brew --prefix qt@6)/bin" >> "$GITHUB_PATH"
|
||||||
echo "CMAKE_PREFIX_PATH=$(brew --prefix qt@6)" >> "$GITHUB_ENV"
|
echo "CMAKE_PREFIX_PATH=$(brew --prefix qt@6)" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Cache OpenTimelineIO (macOS)
|
# ------------------------------------------------------------------
|
||||||
if: runner.os == 'macOS'
|
# OpenTimelineIO (required dependency, built from source; cached)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
- name: Cache OpenTimelineIO (Unix)
|
||||||
|
if: runner.os != 'Windows'
|
||||||
id: otio-cache
|
id: otio-cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: otio-install
|
path: otio-install
|
||||||
key: otio-0.16.0-${{ runner.os }}-v1
|
key: otio-0.16.0-${{ runner.os }}-v1
|
||||||
|
|
||||||
- name: Set OTIO location (macOS)
|
- name: Set OTIO location (Unix)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os != 'Windows'
|
||||||
run: echo "OTIO_LOCATION=${PWD}/otio-install" >> "$GITHUB_ENV"
|
run: echo "OTIO_LOCATION=${PWD}/otio-install" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Build OpenTimelineIO (macOS)
|
- name: Build OpenTimelineIO (Unix)
|
||||||
if: runner.os == 'macOS' && steps.otio-cache.outputs.cache-hit != 'true'
|
if: runner.os != 'Windows' && steps.otio-cache.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||||
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||||
-DOTIO_SHARED_LIBS=ON \
|
-DOTIO_SHARED_LIBS=ON \
|
||||||
-DOTIO_PYTHON_BINDINGS=OFF \
|
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||||
|
-DOTIO_FIND_IMATH=ON \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||||
cmake --build OpenTimelineIO/build
|
cmake --build OpenTimelineIO/build
|
||||||
cmake --install OpenTimelineIO/build
|
cmake --install OpenTimelineIO/build
|
||||||
echo "OTIO_LOCATION=${PWD}/otio-install" >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Windows dependencies (MSYS2)
|
# Windows dependencies (MSYS2)
|
||||||
@@ -106,6 +109,40 @@ jobs:
|
|||||||
mingw-w64-ucrt-x86_64-make
|
mingw-w64-ucrt-x86_64-make
|
||||||
mingw-w64-ucrt-x86_64-ccache
|
mingw-w64-ucrt-x86_64-ccache
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# OpenTimelineIO (required dependency, built from source; cached).
|
||||||
|
# Must come after Setup MSYS2 (uses the msys2 shell).
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
- name: Cache OpenTimelineIO (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
id: otio-cache-win
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: otio-install
|
||||||
|
key: otio-0.16.0-${{ runner.os }}-v1
|
||||||
|
|
||||||
|
- name: Set OTIO location (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
echo "OTIO_LOCATION=${PWD}/otio-install" >> "$GITHUB_ENV"
|
||||||
|
# No rpath on Windows: test executables need the OTIO DLLs on PATH
|
||||||
|
echo "$(cygpath -w "${PWD}/otio-install/bin")" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Build OpenTimelineIO (Windows)
|
||||||
|
if: runner.os == 'Windows' && steps.otio-cache-win.outputs.cache-hit != 'true'
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 --branch v0.16.0 https://github.com/PixarAnimationStudios/OpenTimelineIO.git
|
||||||
|
cmake -S OpenTimelineIO -B OpenTimelineIO/build -G Ninja \
|
||||||
|
-DOTIO_SHARED_LIBS=ON \
|
||||||
|
-DOTIO_PYTHON_BINDINGS=OFF \
|
||||||
|
-DOTIO_FIND_IMATH=ON \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${PWD}/otio-install"
|
||||||
|
cmake --build OpenTimelineIO/build
|
||||||
|
cmake --install OpenTimelineIO/build
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Compile cache (ccache) — restored before configure so the launcher
|
# Compile cache (ccache) — restored before configure so the launcher
|
||||||
# flags below pick it up on all three platforms
|
# flags below pick it up on all three platforms
|
||||||
@@ -130,6 +167,7 @@ jobs:
|
|||||||
-DBUILD_QT6=ON \
|
-DBUILD_QT6=ON \
|
||||||
-DCMAKE_DISABLE_FIND_PACKAGE_Vulkan=ON \
|
-DCMAKE_DISABLE_FIND_PACKAGE_Vulkan=ON \
|
||||||
-DOCIO_LOCATION=/usr \
|
-DOCIO_LOCATION=/usr \
|
||||||
|
-DOTIO_LOCATION=${OTIO_LOCATION} \
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||||
@@ -153,6 +191,7 @@ jobs:
|
|||||||
cmake -S . -B build -G Ninja \
|
cmake -S . -B build -G Ninja \
|
||||||
-DBUILD_TESTS=ON \
|
-DBUILD_TESTS=ON \
|
||||||
-DBUILD_QT6=ON \
|
-DBUILD_QT6=ON \
|
||||||
|
-DOTIO_LOCATION=${OTIO_LOCATION} \
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||||
|
|||||||
@@ -106,3 +106,6 @@ operations-log.md
|
|||||||
verification.md
|
verification.md
|
||||||
act
|
act
|
||||||
.tmp/
|
.tmp/
|
||||||
|
|
||||||
|
# Local OpenTimelineIO build (see docs/build.md)
|
||||||
|
otio-install/
|
||||||
|
|||||||
+33
-8
@@ -232,14 +232,39 @@ endif()
|
|||||||
list(APPEND OLIVE_INCLUDE_DIRS ${PORTAUDIO_INCLUDE_DIRS})
|
list(APPEND OLIVE_INCLUDE_DIRS ${PORTAUDIO_INCLUDE_DIRS})
|
||||||
list(APPEND OLIVE_LIBRARIES ${PORTAUDIO_LIBRARIES})
|
list(APPEND OLIVE_LIBRARIES ${PORTAUDIO_LIBRARIES})
|
||||||
|
|
||||||
# Optional: Link OpenTimelineIO
|
# Required: Link OpenTimelineIO
|
||||||
find_package(OpenTimelineIO)
|
find_package(OpenTimelineIO REQUIRED)
|
||||||
if (OpenTimelineIO_FOUND)
|
list(APPEND OLIVE_DEFINITIONS USE_OTIO)
|
||||||
list(APPEND OLIVE_DEFINITIONS USE_OTIO)
|
list(APPEND OLIVE_INCLUDE_DIRS ${OTIO_INCLUDE_DIRS})
|
||||||
list(APPEND OLIVE_INCLUDE_DIRS ${OTIO_INCLUDE_DIRS})
|
list(APPEND OLIVE_LIBRARIES ${OTIO_LIBRARIES})
|
||||||
list(APPEND OLIVE_LIBRARIES ${OTIO_LIBRARIES})
|
|
||||||
else()
|
# Bundle the OTIO shared libraries into our install tree so that packages
|
||||||
message(" OpenTimelineIO interchange will be disabled.")
|
# (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.
|
||||||
|
# Call oak_copy_otio_runtime(<target>) for each executable/shared library.
|
||||||
|
if (OAK_BUNDLE_OTIO AND APPLE AND OTIO_LIBRARY_DIR)
|
||||||
|
file(GLOB OAK_OTIO_DYLIBS
|
||||||
|
"${OTIO_LIBRARY_DIR}/libopentimelineio*.dylib"
|
||||||
|
"${OTIO_LIBRARY_DIR}/libopentime*.dylib")
|
||||||
|
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()
|
endif()
|
||||||
|
|
||||||
# Optional: Link Google Crashpad
|
# Optional: Link Google Crashpad
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ set(OLIVE_SOURCES
|
|||||||
engineeventbridge.h
|
engineeventbridge.h
|
||||||
engineeventbridge.cpp
|
engineeventbridge.cpp
|
||||||
common/colorcodingapp.h
|
common/colorcodingapp.h
|
||||||
|
common/colorcodingapp.cpp
|
||||||
common/nodevaluehandle.h
|
common/nodevaluehandle.h
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,6 +87,10 @@ add_executable(olive-editor
|
|||||||
$<TARGET_OBJECTS:olive-version-obj>
|
$<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)
|
target_link_libraries(olive-editor PUBLIC OfxHost)
|
||||||
add_dependencies(olive-editor oakgl-cabi-check)
|
add_dependencies(olive-editor oakgl-cabi-check)
|
||||||
if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
|
if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
QVector<Color> ColorCoding::colors = {
|
QVector<Color> AppColorCoding::colors = {
|
||||||
Color(0.545f, 0.255f, 0.255f), Color(0.412f, 0.188f, 0.259f),
|
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.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.631f, 0.612f, 0.212f), Color(0.404f, 0.478f, 0.243f),
|
||||||
@@ -36,12 +36,12 @@ QVector<Color> ColorCoding::colors = {
|
|||||||
Color(0.800f, 0.800f, 0.800f), Color(0.502f, 0.502f, 0.502f)
|
Color(0.800f, 0.800f, 0.800f), Color(0.502f, 0.502f, 0.502f)
|
||||||
};
|
};
|
||||||
|
|
||||||
const QVector<Color> &ColorCoding::standard_colors()
|
const QVector<Color> &AppColorCoding::standard_colors()
|
||||||
{
|
{
|
||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ColorCoding::get_color_name(int c)
|
QString AppColorCoding::get_color_name(int c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case k_red: return QObject::tr("Red");
|
case k_red: return QObject::tr("Red");
|
||||||
@@ -49,7 +49,7 @@ QString ColorCoding::get_color_name(int c)
|
|||||||
case k_orange: return QObject::tr("Orange");
|
case k_orange: return QObject::tr("Orange");
|
||||||
case k_brown: return QObject::tr("Brown");
|
case k_brown: return QObject::tr("Brown");
|
||||||
case k_yellow: return QObject::tr("Yellow");
|
case k_yellow: return QObject::tr("Yellow");
|
||||||
case k_olive: return QObject::tr("Olive");
|
case k_olive: return QObject::tr("Oak");
|
||||||
case k_lime: return QObject::tr("Lime");
|
case k_lime: return QObject::tr("Lime");
|
||||||
case k_green: return QObject::tr("Green");
|
case k_green: return QObject::tr("Green");
|
||||||
case k_cyan: return QObject::tr("Cyan");
|
case k_cyan: return QObject::tr("Cyan");
|
||||||
@@ -64,12 +64,12 @@ QString ColorCoding::get_color_name(int c)
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Color ColorCoding::get_color(int c)
|
Color AppColorCoding::get_color(int c)
|
||||||
{
|
{
|
||||||
return colors.at(c);
|
return colors.at(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::GlobalColor ColorCoding::get_ui_selector_color(const Color &c)
|
Qt::GlobalColor AppColorCoding::get_ui_selector_color(const Color &c)
|
||||||
{
|
{
|
||||||
if (c.get_rough_luminance() > 0.40f) {
|
if (c.get_rough_luminance() > 0.40f) {
|
||||||
return Qt::black;
|
return Qt::black;
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ namespace olive
|
|||||||
using namespace core;
|
using namespace core;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief App-side ColorCoding (moved from engine/ui/colorcoding.h)
|
* @brief App-side color label mapping (moved from engine/ui/colorcoding.h)
|
||||||
*
|
*
|
||||||
* Provides the same static color-label mapping as the engine version but
|
* Provides the same static color-label mapping as the engine version but
|
||||||
* without QObject inheritance (no moc symbols). Only the static methods
|
* without QObject inheritance (no moc symbols). Only the static methods
|
||||||
* used by app code are included.
|
* used by app code are included.
|
||||||
*/
|
*/
|
||||||
class ColorCoding {
|
class AppColorCoding {
|
||||||
public:
|
public:
|
||||||
enum Code {
|
enum Code {
|
||||||
k_red,
|
k_red,
|
||||||
|
|||||||
@@ -1,67 +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 qHash overloads and stream operators
|
|
||||||
// used by QHash containers and QDataStream serialization in app code.
|
|
||||||
// Provides local definitions so the app doesn't import these from liboakengine.
|
|
||||||
|
|
||||||
#include "node/param.h"
|
|
||||||
#include "node/output/track/track.h"
|
|
||||||
|
|
||||||
namespace olive
|
|
||||||
{
|
|
||||||
|
|
||||||
uint qHash(const NodeInput &i)
|
|
||||||
{
|
|
||||||
return qHash(i.node()) ^ qHash(i.input()) ^ ::qHash(i.element());
|
|
||||||
}
|
|
||||||
|
|
||||||
uint qHash(const NodeInputPair &i)
|
|
||||||
{
|
|
||||||
return qHash(i.node) ^ qHash(i.input);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint qHash(const NodeKeyframeTrackReference &i)
|
|
||||||
{
|
|
||||||
return qHash(i.input()) ^ ::qHash(i.track());
|
|
||||||
}
|
|
||||||
|
|
||||||
uint qHash(const Track::Reference &r, uint seed)
|
|
||||||
{
|
|
||||||
return ::qHash(QStringLiteral("%1:%2").arg(QString::number(r.type()),
|
|
||||||
QString::number(r.index())),
|
|
||||||
seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
QDataStream &operator<<(QDataStream &out, const Track::Reference &ref)
|
|
||||||
{
|
|
||||||
out << static_cast<int>(ref.type()) << ref.index();
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDataStream &operator>>(QDataStream &in, Track::Reference &ref)
|
|
||||||
{
|
|
||||||
int type, index;
|
|
||||||
in >> type >> index;
|
|
||||||
ref = Track::Reference(static_cast<Track::Type>(type), index);
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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
|
||||||
@@ -21,44 +21,117 @@
|
|||||||
#ifndef OAK_NODEVALUEHANDLE_H
|
#ifndef OAK_NODEVALUEHANDLE_H
|
||||||
#define OAK_NODEVALUEHANDLE_H
|
#define OAK_NODEVALUEHANDLE_H
|
||||||
|
|
||||||
#include "node/value.h"
|
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert engine NodeValue::Type to oak_node_value_type (app-side).
|
* @brief App-side mirror of engine NodeValue::Type (engine/node/value.h).
|
||||||
*
|
*
|
||||||
* The two enums do NOT share ordinals (e.g. k_boolean=4 vs BOOL=3), so a
|
* Ordinals MUST stay in sync with the engine enum: call sites that still
|
||||||
* plain int cast is a bug. Mirrors from_c_type() in
|
* hold an engine NodeValue::Type reach the helpers below through an
|
||||||
* engine/src/capi/node.cpp. Lives in an app header, NOT in the public
|
* implicit int conversion. The static_asserts pin the engine ordinals as
|
||||||
* facade headers — the C ABI surface stays pure C (see
|
* 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
|
* 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).
|
* facade cannot represent (caller falls back to the input's declared type).
|
||||||
*/
|
*/
|
||||||
inline int node_value_type_to_c(NodeValue::Type t)
|
inline int node_value_type_to_c(int t)
|
||||||
{
|
{
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case NodeValue::k_int: return OAK_NODE_VALUE_INT;
|
case NodeValueType::k_int: return OAK_NODE_VALUE_INT;
|
||||||
case NodeValue::k_float: return OAK_NODE_VALUE_FLOAT;
|
case NodeValueType::k_float: return OAK_NODE_VALUE_FLOAT;
|
||||||
case NodeValue::k_boolean: return OAK_NODE_VALUE_BOOL;
|
case NodeValueType::k_boolean: return OAK_NODE_VALUE_BOOL;
|
||||||
case NodeValue::k_rational: return OAK_NODE_VALUE_RATIONAL;
|
case NodeValueType::k_rational: return OAK_NODE_VALUE_RATIONAL;
|
||||||
case NodeValue::k_color: return OAK_NODE_VALUE_COLOR;
|
case NodeValueType::k_color: return OAK_NODE_VALUE_COLOR;
|
||||||
case NodeValue::k_vec2: return OAK_NODE_VALUE_VEC2;
|
case NodeValueType::k_vec2: return OAK_NODE_VALUE_VEC2;
|
||||||
case NodeValue::k_vec3: return OAK_NODE_VALUE_VEC3;
|
case NodeValueType::k_vec3: return OAK_NODE_VALUE_VEC3;
|
||||||
case NodeValue::k_vec4: return OAK_NODE_VALUE_VEC4;
|
case NodeValueType::k_vec4: return OAK_NODE_VALUE_VEC4;
|
||||||
case NodeValue::k_combo: return OAK_NODE_VALUE_COMBO;
|
case NodeValueType::k_combo: return OAK_NODE_VALUE_COMBO;
|
||||||
case NodeValue::k_file: return OAK_NODE_VALUE_STRING;
|
case NodeValueType::k_file: return OAK_NODE_VALUE_STRING;
|
||||||
case NodeValue::k_text: return OAK_NODE_VALUE_TEXT;
|
case NodeValueType::k_text: return OAK_NODE_VALUE_TEXT;
|
||||||
case NodeValue::k_font: return OAK_NODE_VALUE_FONT;
|
case NodeValueType::k_font: return OAK_NODE_VALUE_FONT;
|
||||||
case NodeValue::k_str_combo: return OAK_NODE_VALUE_STR_COMBO;
|
case NodeValueType::k_str_combo: return OAK_NODE_VALUE_STR_COMBO;
|
||||||
case NodeValue::k_binary: return OAK_NODE_VALUE_BINARY;
|
case NodeValueType::k_binary: return OAK_NODE_VALUE_BINARY;
|
||||||
case NodeValue::k_bezier: return OAK_NODE_VALUE_BEZIER;
|
case NodeValueType::k_bezier: return OAK_NODE_VALUE_BEZIER;
|
||||||
case NodeValue::k_texture: return OAK_NODE_VALUE_TEXTURE;
|
case NodeValueType::k_texture: return OAK_NODE_VALUE_TEXTURE;
|
||||||
case NodeValue::k_samples: return OAK_NODE_VALUE_SAMPLES;
|
case NodeValueType::k_samples: return OAK_NODE_VALUE_SAMPLES;
|
||||||
case NodeValue::k_video_params: return OAK_NODE_VALUE_VIDEO_PARAMS;
|
case NodeValueType::k_video_params: return OAK_NODE_VALUE_VIDEO_PARAMS;
|
||||||
case NodeValue::k_audio_params: return OAK_NODE_VALUE_AUDIO_PARAMS;
|
case NodeValueType::k_audio_params: return OAK_NODE_VALUE_AUDIO_PARAMS;
|
||||||
default: return -1;
|
default: return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-36
@@ -21,53 +21,57 @@
|
|||||||
#ifndef OAKVALUEHELPER_H
|
#ifndef OAKVALUEHELPER_H
|
||||||
#define OAKVALUEHELPER_H
|
#define OAKVALUEHELPER_H
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
#include <QVector3D>
|
#include <QVector3D>
|
||||||
#include <QVector4D>
|
#include <QVector4D>
|
||||||
|
|
||||||
#include "node/keyframe.h"
|
#include "nodevaluehandle.h"
|
||||||
#include "node/value.h"
|
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
#include "olive/core/util/color.h"
|
#include "olive/core/util/color.h"
|
||||||
|
#include "olive/core/util/rational.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert a per-track component QVariant into the C ABI oak_node_value POD.
|
* @brief Convert a per-track component QVariant into the C ABI oak_node_value POD.
|
||||||
*
|
*
|
||||||
* `type` is the declared input data type (e.g. k_float/k_color). For split-track
|
* `type` uses engine NodeValue::Type ordinals (see the NodeValueType mirror
|
||||||
* types the component is the track-0 scalar (float for k_color's red channel, etc.).
|
* in nodevaluehandle.h; an engine NodeValue::Type converts implicitly) and
|
||||||
* Returns false for types that have no POD representation.
|
* 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(NodeValue::Type type, const QVariant &v,
|
static inline bool QVariantToOakNodeValue(int type, const QVariant &v,
|
||||||
oak_node_value *out)
|
oak_node_value *out)
|
||||||
{
|
{
|
||||||
memset(out, 0, sizeof(*out));
|
memset(out, 0, sizeof(*out));
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NodeValue::k_int:
|
case NodeValueType::k_int:
|
||||||
case NodeValue::k_combo:
|
case NodeValueType::k_combo:
|
||||||
out->type = (type == NodeValue::k_combo) ? OAK_NODE_VALUE_COMBO
|
out->type = (type == NodeValueType::k_combo) ? OAK_NODE_VALUE_COMBO
|
||||||
: OAK_NODE_VALUE_INT;
|
: OAK_NODE_VALUE_INT;
|
||||||
out->num = v.toLongLong();
|
out->num = v.toLongLong();
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_float:
|
case NodeValueType::k_float:
|
||||||
out->type = OAK_NODE_VALUE_FLOAT;
|
out->type = OAK_NODE_VALUE_FLOAT;
|
||||||
out->f[0] = v.toDouble();
|
out->f[0] = v.toDouble();
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_boolean:
|
case NodeValueType::k_boolean:
|
||||||
out->type = OAK_NODE_VALUE_BOOL;
|
out->type = OAK_NODE_VALUE_BOOL;
|
||||||
out->num = v.toBool() ? 1 : 0;
|
out->num = v.toBool() ? 1 : 0;
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_rational:
|
case NodeValueType::k_rational:
|
||||||
out->type = OAK_NODE_VALUE_RATIONAL;
|
out->type = OAK_NODE_VALUE_RATIONAL;
|
||||||
{
|
{
|
||||||
const Rational r = v.value<Rational>();
|
const core::Rational r = v.value<core::Rational>();
|
||||||
out->num = r.numerator();
|
out->num = r.numerator();
|
||||||
out->den = r.denominator();
|
out->den = r.denominator();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_color:
|
case NodeValueType::k_color:
|
||||||
out->type = OAK_NODE_VALUE_COLOR;
|
out->type = OAK_NODE_VALUE_COLOR;
|
||||||
{
|
{
|
||||||
const core::Color c = v.value<core::Color>();
|
const core::Color c = v.value<core::Color>();
|
||||||
@@ -77,7 +81,7 @@ static inline bool QVariantToOakNodeValue(NodeValue::Type type, const QVariant &
|
|||||||
out->f[3] = c.alpha();
|
out->f[3] = c.alpha();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_vec2:
|
case NodeValueType::k_vec2:
|
||||||
out->type = OAK_NODE_VALUE_VEC2;
|
out->type = OAK_NODE_VALUE_VEC2;
|
||||||
{
|
{
|
||||||
const QVector2D vec = v.value<QVector2D>();
|
const QVector2D vec = v.value<QVector2D>();
|
||||||
@@ -85,7 +89,7 @@ static inline bool QVariantToOakNodeValue(NodeValue::Type type, const QVariant &
|
|||||||
out->f[1] = vec.y();
|
out->f[1] = vec.y();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_vec3:
|
case NodeValueType::k_vec3:
|
||||||
out->type = OAK_NODE_VALUE_VEC3;
|
out->type = OAK_NODE_VALUE_VEC3;
|
||||||
{
|
{
|
||||||
const QVector3D vec = v.value<QVector3D>();
|
const QVector3D vec = v.value<QVector3D>();
|
||||||
@@ -94,7 +98,7 @@ static inline bool QVariantToOakNodeValue(NodeValue::Type type, const QVariant &
|
|||||||
out->f[2] = vec.z();
|
out->f[2] = vec.z();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_vec4:
|
case NodeValueType::k_vec4:
|
||||||
out->type = OAK_NODE_VALUE_VEC4;
|
out->type = OAK_NODE_VALUE_VEC4;
|
||||||
{
|
{
|
||||||
const QVector4D vec = v.value<QVector4D>();
|
const QVector4D vec = v.value<QVector4D>();
|
||||||
@@ -116,49 +120,51 @@ static inline bool QVariantToOakNodeValue(NodeValue::Type type, const QVariant &
|
|||||||
* single track's component (e.g. one float for a k_color channel). The resulting
|
* 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
|
* POD has the input's declared type with the component in f[0]/num, exactly what
|
||||||
* the per-track facade commands expect.
|
* the per-track facade commands expect.
|
||||||
|
*
|
||||||
|
* `type` uses engine NodeValue::Type ordinals (NodeValueType mirror).
|
||||||
*/
|
*/
|
||||||
static inline bool NodeTrackComponentToOakNodeValue(NodeValue::Type type,
|
static inline bool NodeTrackComponentToOakNodeValue(int type,
|
||||||
const QVariant &v,
|
const QVariant &v,
|
||||||
oak_node_value *out)
|
oak_node_value *out)
|
||||||
{
|
{
|
||||||
memset(out, 0, sizeof(*out));
|
memset(out, 0, sizeof(*out));
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NodeValue::k_int:
|
case NodeValueType::k_int:
|
||||||
case NodeValue::k_combo:
|
case NodeValueType::k_combo:
|
||||||
out->type = (type == NodeValue::k_combo) ? OAK_NODE_VALUE_COMBO
|
out->type = (type == NodeValueType::k_combo) ? OAK_NODE_VALUE_COMBO
|
||||||
: OAK_NODE_VALUE_INT;
|
: OAK_NODE_VALUE_INT;
|
||||||
out->num = v.toLongLong();
|
out->num = v.toLongLong();
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_float:
|
case NodeValueType::k_float:
|
||||||
case NodeValue::k_bezier:
|
case NodeValueType::k_bezier:
|
||||||
out->type = OAK_NODE_VALUE_FLOAT;
|
out->type = OAK_NODE_VALUE_FLOAT;
|
||||||
out->f[0] = v.toDouble();
|
out->f[0] = v.toDouble();
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_boolean:
|
case NodeValueType::k_boolean:
|
||||||
out->type = OAK_NODE_VALUE_BOOL;
|
out->type = OAK_NODE_VALUE_BOOL;
|
||||||
out->num = v.toBool() ? 1 : 0;
|
out->num = v.toBool() ? 1 : 0;
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_rational:
|
case NodeValueType::k_rational:
|
||||||
out->type = OAK_NODE_VALUE_RATIONAL;
|
out->type = OAK_NODE_VALUE_RATIONAL;
|
||||||
{
|
{
|
||||||
const Rational r = v.value<Rational>();
|
const core::Rational r = v.value<core::Rational>();
|
||||||
out->num = r.numerator();
|
out->num = r.numerator();
|
||||||
out->den = r.denominator();
|
out->den = r.denominator();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_color:
|
case NodeValueType::k_color:
|
||||||
out->type = OAK_NODE_VALUE_COLOR;
|
out->type = OAK_NODE_VALUE_COLOR;
|
||||||
out->f[0] = v.toFloat();
|
out->f[0] = v.toFloat();
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_vec2:
|
case NodeValueType::k_vec2:
|
||||||
out->type = OAK_NODE_VALUE_VEC2;
|
out->type = OAK_NODE_VALUE_VEC2;
|
||||||
out->f[0] = v.toFloat();
|
out->f[0] = v.toFloat();
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_vec3:
|
case NodeValueType::k_vec3:
|
||||||
out->type = OAK_NODE_VALUE_VEC3;
|
out->type = OAK_NODE_VALUE_VEC3;
|
||||||
out->f[0] = v.toFloat();
|
out->f[0] = v.toFloat();
|
||||||
return true;
|
return true;
|
||||||
case NodeValue::k_vec4:
|
case NodeValueType::k_vec4:
|
||||||
out->type = OAK_NODE_VALUE_VEC4;
|
out->type = OAK_NODE_VALUE_VEC4;
|
||||||
out->f[0] = v.toFloat();
|
out->f[0] = v.toFloat();
|
||||||
return true;
|
return true;
|
||||||
@@ -185,7 +191,7 @@ static inline QVariant OakNodeValueToQVariant(const oak_node_value &v)
|
|||||||
return QVariant::fromValue(v.num != 0);
|
return QVariant::fromValue(v.num != 0);
|
||||||
case OAK_NODE_VALUE_RATIONAL:
|
case OAK_NODE_VALUE_RATIONAL:
|
||||||
return QVariant::fromValue(
|
return QVariant::fromValue(
|
||||||
Rational(int(v.num), int(v.den)));
|
core::Rational(int(v.num), int(v.den)));
|
||||||
case OAK_NODE_VALUE_COLOR:
|
case OAK_NODE_VALUE_COLOR:
|
||||||
return QVariant::fromValue(core::Color(
|
return QVariant::fromValue(core::Color(
|
||||||
float(v.f[0]), float(v.f[1]), float(v.f[2]), float(v.f[3])));
|
float(v.f[0]), float(v.f[1]), float(v.f[2]), float(v.f[3])));
|
||||||
@@ -206,16 +212,17 @@ static inline QVariant OakNodeValueToQVariant(const oak_node_value &v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Map an engine NodeKeyframe::Type to the facade easing type.
|
* @brief Map engine NodeKeyframe::Type ordinals (NodeKeyframeType mirror)
|
||||||
|
* to the facade easing type (0=linear, 1=bezier, 2=hold).
|
||||||
*/
|
*/
|
||||||
static inline int NodeKeyframeTypeToFacade(NodeKeyframe::Type type)
|
static inline int NodeKeyframeTypeToFacade(int type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NodeKeyframe::k_bezier:
|
case NodeKeyframeType::k_bezier:
|
||||||
return 1;
|
return 1;
|
||||||
case NodeKeyframe::k_hold:
|
case NodeKeyframeType::k_hold:
|
||||||
return 2;
|
return 2;
|
||||||
case NodeKeyframe::k_linear:
|
case NodeKeyframeType::k_linear:
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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
|
||||||
+38
-36
@@ -67,9 +67,6 @@
|
|||||||
#include "panel/timebased/timebased.h"
|
#include "panel/timebased/timebased.h"
|
||||||
#include "panel/timeline/timeline.h"
|
#include "panel/timeline/timeline.h"
|
||||||
#include "panel/viewer/viewer.h"
|
#include "panel/viewer/viewer.h"
|
||||||
#include "pluginSupport/oliveplugininstance.h"
|
|
||||||
#include "pluginSupport/pluginprogressreporter.h"
|
|
||||||
#include "render/diskmanager.h"
|
|
||||||
#include "dialog/projectimport/projectimporterrordialog.h"
|
#include "dialog/projectimport/projectimporterrordialog.h"
|
||||||
#include "ui/style/style.h"
|
#include "ui/style/style.h"
|
||||||
#include "widget/menu/menushared.h"
|
#include "widget/menu/menushared.h"
|
||||||
@@ -118,10 +115,13 @@ Core::Core(const OakEngineAppParams *params)
|
|||||||
};
|
};
|
||||||
cb.relink_footage = [](OakEngineFootage **footage, int count,
|
cb.relink_footage = [](OakEngineFootage **footage, int count,
|
||||||
void *userdata) -> int {
|
void *userdata) -> int {
|
||||||
QVector<Footage *> fv;
|
// The engine hands over the footage node pointers themselves
|
||||||
|
// (see install_handlers() in the engine), so they can be used
|
||||||
|
// directly as node handles.
|
||||||
|
QVector<OakEngineNode *> fv;
|
||||||
fv.reserve(count);
|
fv.reserve(count);
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
fv.append(reinterpret_cast<Footage *>(footage[i]));
|
fv.append(reinterpret_cast<OakEngineNode *>(footage[i]));
|
||||||
}
|
}
|
||||||
FootageRelinkDialog frd(fv,
|
FootageRelinkDialog frd(fv,
|
||||||
static_cast<Core *>(userdata)->main_window_);
|
static_cast<Core *>(userdata)->main_window_);
|
||||||
@@ -158,9 +158,7 @@ Core::Core(const OakEngineAppParams *params)
|
|||||||
oakengine_disk_set_settings_handler(
|
oakengine_disk_set_settings_handler(
|
||||||
[](const char *folder_path, void *parent_window, void *userdata) {
|
[](const char *folder_path, void *parent_window, void *userdata) {
|
||||||
Q_UNUSED(userdata)
|
Q_UNUSED(userdata)
|
||||||
DiskCacheDialog d(
|
DiskCacheDialog d(oakengine_disk_get_open_folder(folder_path),
|
||||||
reinterpret_cast<DiskCacheFolder *>(
|
|
||||||
oakengine_disk_get_open_folder(folder_path)),
|
|
||||||
reinterpret_cast<QWidget *>(parent_window));
|
reinterpret_cast<QWidget *>(parent_window));
|
||||||
d.exec();
|
d.exec();
|
||||||
}, nullptr);
|
}, nullptr);
|
||||||
@@ -345,7 +343,7 @@ void Core::dialog_import_show()
|
|||||||
|
|
||||||
if (active_project_panel ==
|
if (active_project_panel ==
|
||||||
nullptr // Check that we found a Project panel
|
nullptr // Check that we found a Project panel
|
||||||
|| (active_project = reinterpret_cast<OakEngineProject *>(active_project_panel->project())) ==
|
|| (active_project = active_project_panel->project().handle()) ==
|
||||||
nullptr) { // and that we could find an active Project
|
nullptr) { // and that we could find an active Project
|
||||||
QMessageBox::critical(main_window_, tr("Failed to import footage"),
|
QMessageBox::critical(main_window_, tr("Failed to import footage"),
|
||||||
tr("Failed to find active Project panel"));
|
tr("Failed to find active Project panel"));
|
||||||
@@ -353,7 +351,7 @@ void Core::dialog_import_show()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the selected folder in this panel
|
// Get the selected folder in this panel
|
||||||
OakEngineNode *folder = reinterpret_cast<OakEngineNode *>(active_project_panel->get_selected_folder());
|
OakEngineNode *folder = active_project_panel->get_selected_folder().handle();
|
||||||
|
|
||||||
import_files(files, folder);
|
import_files(files, folder);
|
||||||
}
|
}
|
||||||
@@ -370,7 +368,7 @@ void Core::dialog_project_properties_show()
|
|||||||
OakEngineProject *proj = get_active_project();
|
OakEngineProject *proj = get_active_project();
|
||||||
|
|
||||||
if (proj) {
|
if (proj) {
|
||||||
ProjectPropertiesDialog ppd(reinterpret_cast<Project *>(proj), main_window_);
|
ProjectPropertiesDialog ppd(proj, main_window_);
|
||||||
ppd.exec();
|
ppd.exec();
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
@@ -391,12 +389,7 @@ void Core::dialog_export_show()
|
|||||||
bool Core::DialogImportOTIOShow(const QList<OakEngineSequence *> &sequences)
|
bool Core::DialogImportOTIOShow(const QList<OakEngineSequence *> &sequences)
|
||||||
{
|
{
|
||||||
OakEngineProject *active_project = get_active_project();
|
OakEngineProject *active_project = get_active_project();
|
||||||
QList<Sequence *> sq;
|
OTIOPropertiesDialog opd(sequences, active_project);
|
||||||
sq.reserve(sequences.size());
|
|
||||||
for (auto *s : sequences) {
|
|
||||||
sq.append(reinterpret_cast<Sequence *>(s));
|
|
||||||
}
|
|
||||||
OTIOPropertiesDialog opd(sq, reinterpret_cast<Project *>(active_project));
|
|
||||||
return opd.exec() == QDialog::Accepted;
|
return opd.exec() == QDialog::Accepted;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -409,7 +402,7 @@ void Core::create_new_folder()
|
|||||||
OakEngineProject *active_project;
|
OakEngineProject *active_project;
|
||||||
|
|
||||||
if (active_project_panel == nullptr // Check that we found a Project panel
|
if (active_project_panel == nullptr // Check that we found a Project panel
|
||||||
|| (active_project = reinterpret_cast<OakEngineProject *>(active_project_panel->project())) ==
|
|| (active_project = active_project_panel->project().handle()) ==
|
||||||
nullptr) { // and that we could find an active Project
|
nullptr) { // and that we could find an active Project
|
||||||
QMessageBox::critical(main_window_, tr("Failed to create new folder"),
|
QMessageBox::critical(main_window_, tr("Failed to create new folder"),
|
||||||
tr("Failed to find active project"));
|
tr("Failed to find active project"));
|
||||||
@@ -417,7 +410,7 @@ void Core::create_new_folder()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the selected folder in this panel
|
// Get the selected folder in this panel
|
||||||
OakEngineNode *folder = reinterpret_cast<OakEngineNode *>(active_project_panel->get_selected_folder());
|
OakEngineNode *folder = active_project_panel->get_selected_folder().handle();
|
||||||
|
|
||||||
// Group the three facade edits into a single undo entry.
|
// Group the three facade edits into a single undo entry.
|
||||||
oakengine_undo_group_begin(tr("Create New Folder").toUtf8().constData());
|
oakengine_undo_group_begin(tr("Create New Folder").toUtf8().constData());
|
||||||
@@ -455,7 +448,7 @@ void Core::create_new_sequence()
|
|||||||
// Create new sequence
|
// Create new sequence
|
||||||
OakEngineSequence *new_sequence = create_new_sequence_for_project(active_project);
|
OakEngineSequence *new_sequence = create_new_sequence_for_project(active_project);
|
||||||
|
|
||||||
SequenceDialog sd(reinterpret_cast<Sequence *>(new_sequence), SequenceDialog::k_new, main_window_);
|
SequenceDialog sd(reinterpret_cast<OakEngineNode *>(new_sequence), SequenceDialog::k_new, main_window_);
|
||||||
|
|
||||||
// Make sure SequenceDialog doesn't make an undo command for editing the sequence, since we make an undo command for
|
// Make sure SequenceDialog doesn't make an undo command for editing the sequence, since we make an undo command for
|
||||||
// adding it later on
|
// adding it later on
|
||||||
@@ -473,7 +466,7 @@ void Core::create_new_sequence()
|
|||||||
get_selected_folder_in_active_project(),
|
get_selected_folder_in_active_project(),
|
||||||
reinterpret_cast<OakEngineNode *>(new_sequence));
|
reinterpret_cast<OakEngineNode *>(new_sequence));
|
||||||
oakengine_undo_command_multi_add_child(command, oakengine_node_set_position_command(reinterpret_cast<void *>(new_sequence), reinterpret_cast<void *>(new_sequence), 0.0, 0.0, 0));
|
oakengine_undo_command_multi_add_child(command, oakengine_node_set_position_command(reinterpret_cast<void *>(new_sequence), reinterpret_cast<void *>(new_sequence), 0.0, 0.0, 0));
|
||||||
oakengine_undo_command_multi_add_child(command, make_open_sequence_command(reinterpret_cast<Sequence *>(new_sequence)));
|
oakengine_undo_command_multi_add_child(command, make_open_sequence_command(reinterpret_cast<OakEngineNode *>(new_sequence)));
|
||||||
|
|
||||||
// Create and connect default nodes to new sequence
|
// Create and connect default nodes to new sequence
|
||||||
oakengine_sequence_add_default_nodes(new_sequence);
|
oakengine_sequence_add_default_nodes(new_sequence);
|
||||||
@@ -509,9 +502,17 @@ void Core::import_task_complete(OakEngineTask *task)
|
|||||||
bool all_stills = true;
|
bool all_stills = true;
|
||||||
|
|
||||||
for (int j = 0; j < vid_count; j++) {
|
for (int j = 0; j < vid_count; j++) {
|
||||||
const VideoParams &vs = viewer_output_video_params(f, j);
|
const oak::VideoParams vs = viewer_output_video_params(f, j);
|
||||||
|
// Stream enabled state is not part of the POD; query it
|
||||||
|
// through the C ABI (video_type 1 ==
|
||||||
|
// olive::VideoParams::k_video_type_still, keep the ordinal
|
||||||
|
// in sync with the engine enum).
|
||||||
|
const bool stream_enabled =
|
||||||
|
oakengine_viewer_get_stream_enabled(
|
||||||
|
reinterpret_cast<const OakEngineNode *>(f),
|
||||||
|
OAKENGINE_TRACK_TYPE_VIDEO, j) == 1;
|
||||||
if (!(vs.video_type() == 1 &&
|
if (!(vs.video_type() == 1 &&
|
||||||
vs.enabled() == (j == 0))) {
|
stream_enabled == (j == 0))) {
|
||||||
all_stills = false;
|
all_stills = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -579,7 +580,13 @@ void Core::import_task_complete(OakEngineTask *task)
|
|||||||
command,
|
command,
|
||||||
tr("Imported %1 File(s)").arg(imported_footage.size()).toUtf8().constData());
|
tr("Imported %1 File(s)").arg(imported_footage.size()).toUtf8().constData());
|
||||||
|
|
||||||
main_window_->select_footage(reinterpret_cast<const QVector<Footage *> &>(imported_footage));
|
// select_footage() takes node handles, so unwrap the footage handles here
|
||||||
|
QVector<OakEngineNode *> imported_nodes;
|
||||||
|
imported_nodes.reserve(imported_footage.size());
|
||||||
|
for (OakEngineFootage *f : imported_footage) {
|
||||||
|
imported_nodes.append(reinterpret_cast<OakEngineNode *>(f));
|
||||||
|
}
|
||||||
|
main_window_->select_footage(imported_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::confirm_image_sequence(const QString &filename)
|
bool Core::confirm_image_sequence(const QString &filename)
|
||||||
@@ -636,7 +643,7 @@ bool Core::start_headless_export()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sequence* sequence = nullptr;
|
OakEngineSequence* sequence = nullptr;
|
||||||
|
|
||||||
// Check if this project contains multiple sequences
|
// Check if this project contains multiple sequences
|
||||||
if (items.size() > 1) {
|
if (items.size() > 1) {
|
||||||
@@ -669,9 +676,9 @@ bool Core::start_headless_export()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sequence = static_cast<Sequence*>(items.at(sequence_index));
|
sequence = reinterpret_cast<OakEngineSequence*>(items.at(sequence_index));
|
||||||
} else {
|
} else {
|
||||||
sequence = static_cast<Sequence*>(items.first());
|
sequence = reinterpret_cast<OakEngineSequence*>(items.first());
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportParams params;
|
ExportParams params;
|
||||||
@@ -953,7 +960,7 @@ OakEngineNode *Core::get_selected_folder_in_active_project() const
|
|||||||
PanelManager::instance()->most_recently_focused<ProjectPanel>();
|
PanelManager::instance()->most_recently_focused<ProjectPanel>();
|
||||||
|
|
||||||
if (active_project_panel) {
|
if (active_project_panel) {
|
||||||
return reinterpret_cast<OakEngineNode *>(active_project_panel->get_selected_folder());
|
return active_project_panel->get_selected_folder().handle();
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -1014,14 +1021,14 @@ void Core::open_recovery_project(const QString &filename)
|
|||||||
|
|
||||||
void Core::open_node_in_viewer(OakEngineNode *viewer)
|
void Core::open_node_in_viewer(OakEngineNode *viewer)
|
||||||
{
|
{
|
||||||
main_window_->open_node_in_viewer(reinterpret_cast<ViewerOutput *>(viewer));
|
main_window_->open_node_in_viewer(viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::open_export_dialog_for_viewer(OakEngineNode *viewer,
|
void Core::open_export_dialog_for_viewer(OakEngineNode *viewer,
|
||||||
bool start_still_image)
|
bool start_still_image)
|
||||||
{
|
{
|
||||||
ExportDialog *ed =
|
ExportDialog *ed =
|
||||||
new ExportDialog(reinterpret_cast<ViewerOutput *>(viewer), start_still_image, main_window_);
|
new ExportDialog(viewer, start_still_image, main_window_);
|
||||||
connect(ed, &ExportDialog::finished, ed, &ExportDialog::deleteLater);
|
connect(ed, &ExportDialog::finished, ed, &ExportDialog::deleteLater);
|
||||||
ed->open();
|
ed->open();
|
||||||
connect(ed, &ExportDialog::request_import_file, this,
|
connect(ed, &ExportDialog::request_import_file, this,
|
||||||
@@ -1093,7 +1100,7 @@ void Core::show_cache_full_warning()
|
|||||||
|
|
||||||
void Core::on_active_project_changed(OakEngineProject *p)
|
void Core::on_active_project_changed(OakEngineProject *p)
|
||||||
{
|
{
|
||||||
main_window_->set_project(reinterpret_cast<Project *>(p));
|
main_window_->set_project(p);
|
||||||
|
|
||||||
if (p) {
|
if (p) {
|
||||||
// Keep the window's modified state in sync via event subscription
|
// Keep the window's modified state in sync via event subscription
|
||||||
@@ -1362,11 +1369,6 @@ void Core::open_project()
|
|||||||
|
|
||||||
// ---- Facade-wrapping method implementations ----
|
// ---- Facade-wrapping method implementations ----
|
||||||
|
|
||||||
UndoStack *Core::undo_stack() const
|
|
||||||
{
|
|
||||||
return reinterpret_cast<UndoStack *>(oakengine_undo_handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
Tool::Item Core::tool() const
|
Tool::Item Core::tool() const
|
||||||
{
|
{
|
||||||
return static_cast<Tool::Item>(oakengine_app_tool());
|
return static_cast<Tool::Item>(oakengine_app_tool());
|
||||||
|
|||||||
+8
-9
@@ -22,8 +22,10 @@
|
|||||||
#ifndef OAK_CORE_H
|
#ifndef OAK_CORE_H
|
||||||
#define OAK_CORE_H
|
#define OAK_CORE_H
|
||||||
|
|
||||||
#include "coreengine.h"
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <olive/core/core.h>
|
||||||
|
#include "common/tooltypes.h"
|
||||||
|
#include "oakutil/qtutils.h"
|
||||||
#include "oakengine/app.h"
|
#include "oakengine/app.h"
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
#include "oakengine/project.h"
|
#include "oakengine/project.h"
|
||||||
@@ -35,6 +37,8 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using namespace core;
|
||||||
|
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,12 +216,9 @@ public slots:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// The following methods are ordinary member functions, NOT slots. They are
|
// The following methods are ordinary member functions, NOT slots. They are
|
||||||
// deliberately kept out of the `public slots:` section because their
|
// deliberately kept out of the `public slots:` section; none of them are
|
||||||
// signatures reference engine C++ types (Project*, Sequence*, UndoStack*).
|
// connect() targets: every connection involving Core uses the new-style
|
||||||
// If MOC processed them as slots it would instantiate QMetaType for those
|
// member-function syntax, which works with plain methods.
|
||||||
// types and pull their staticMetaObject symbols across the ABI boundary.
|
|
||||||
// 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
|
* @brief Show OTIO import dialog
|
||||||
@@ -228,8 +229,6 @@ public:
|
|||||||
|
|
||||||
// ---- Facade-wrapping methods (shadow EngineCore to avoid symbol refs) ----
|
// ---- Facade-wrapping methods (shadow EngineCore to avoid symbol refs) ----
|
||||||
|
|
||||||
UndoStack *undo_stack() const;
|
|
||||||
|
|
||||||
Tool::Item tool() const;
|
Tool::Item tool() const;
|
||||||
void set_tool(const Tool::Item &tool);
|
void set_tool(const Tool::Item &tool);
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "oakutil/filefunctions.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -187,13 +187,13 @@ QString ColorDialog::get_color_space_input() const
|
|||||||
return chooser_->input();
|
return chooser_->input();
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorTransform ColorDialog::get_color_space_output() const
|
oak::ColorTransform ColorDialog::get_color_space_output() const
|
||||||
{
|
{
|
||||||
return chooser_->output();
|
return chooser_->output();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorDialog::color_space_changed(const QString &input,
|
void ColorDialog::color_space_changed(const QString &input,
|
||||||
const ColorTransform &output)
|
const oak::ColorTransform &output)
|
||||||
{
|
{
|
||||||
QByteArray ref_cs = oak_query_string([this](char *buf, int size) {
|
QByteArray ref_cs = oak_query_string([this](char *buf, int size) {
|
||||||
return oakengine_color_manager_reference_color_space(
|
return oakengine_color_manager_reference_color_space(
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public:
|
|||||||
|
|
||||||
QString get_color_space_input() const;
|
QString get_color_space_input() const;
|
||||||
|
|
||||||
ColorTransform get_color_space_output() const;
|
oak::ColorTransform get_color_space_output() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void set_color(const ManagedColor &c);
|
void set_color(const ManagedColor &c);
|
||||||
@@ -91,7 +91,7 @@ private:
|
|||||||
ColorSwatchChooser *swatch_;
|
ColorSwatchChooser *swatch_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void color_space_changed(const QString &input, const ColorTransform &output);
|
void color_space_changed(const QString &input, const oak::ColorTransform &output);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,26 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "oakengine/disk.h"
|
#include "oakengine/disk.h"
|
||||||
|
#include "oakutil/define.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
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)
|
: QDialog(parent)
|
||||||
, folder_(folder)
|
, folder_(folder)
|
||||||
{
|
{
|
||||||
@@ -39,8 +54,9 @@ DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
|||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->get_path())),
|
layout->addWidget(
|
||||||
row, 0, 1, 2);
|
new QLabel(tr("Disk Cache: %1").arg(disk_folder_path(folder))), row,
|
||||||
|
0, 1, 2);
|
||||||
setWindowTitle(tr("Disk Cache Settings"));
|
setWindowTitle(tr("Disk Cache Settings"));
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
@@ -50,7 +66,8 @@ DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
|||||||
maximum_cache_slider_ = new FloatSlider();
|
maximum_cache_slider_ = new FloatSlider();
|
||||||
maximum_cache_slider_->set_format(tr("%1 GB"));
|
maximum_cache_slider_->set_format(tr("%1 GB"));
|
||||||
maximum_cache_slider_->set_minimum(1.0);
|
maximum_cache_slider_->set_minimum(1.0);
|
||||||
maximum_cache_slider_->set_value(static_cast<double>(folder->get_limit()) /
|
// 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));
|
static_cast<double>(k_bytes_in_gigabyte));
|
||||||
layout->addWidget(maximum_cache_slider_, row, 1);
|
layout->addWidget(maximum_cache_slider_, row, 1);
|
||||||
|
|
||||||
@@ -66,7 +83,8 @@ DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
|||||||
|
|
||||||
clear_disk_cache_ =
|
clear_disk_cache_ =
|
||||||
new QCheckBox(tr("Automatically clear disk cache on close"));
|
new QCheckBox(tr("Automatically clear disk cache on close"));
|
||||||
clear_disk_cache_->setChecked(folder->get_clear_on_close());
|
clear_disk_cache_->setChecked(
|
||||||
|
oakengine_disk_folder_get_clear_on_close(folder) != 0);
|
||||||
layout->addWidget(clear_disk_cache_, row, 1);
|
layout->addWidget(clear_disk_cache_, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
@@ -84,12 +102,17 @@ void DiskCacheDialog::accept()
|
|||||||
{
|
{
|
||||||
qint64 new_disk_cache_limit =
|
qint64 new_disk_cache_limit =
|
||||||
qRound64(maximum_cache_slider_->get_value() * k_bytes_in_gigabyte);
|
qRound64(maximum_cache_slider_->get_value() * k_bytes_in_gigabyte);
|
||||||
if (new_disk_cache_limit != folder_->get_limit()) {
|
if (static_cast<double>(new_disk_cache_limit) !=
|
||||||
folder_->set_limit(new_disk_cache_limit);
|
oakengine_disk_folder_get_limit(folder_)) {
|
||||||
|
oakengine_disk_folder_set_limit(
|
||||||
|
folder_, static_cast<double>(new_disk_cache_limit));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (folder_->get_clear_on_close() != clear_disk_cache_->isChecked()) {
|
const bool clear_on_close = clear_disk_cache_->isChecked();
|
||||||
folder_->set_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();
|
QDialog::accept();
|
||||||
@@ -97,7 +120,7 @@ void DiskCacheDialog::accept()
|
|||||||
|
|
||||||
void DiskCacheDialog::clear_disk_cache()
|
void DiskCacheDialog::clear_disk_cache()
|
||||||
{
|
{
|
||||||
clear_disk_cache(folder_->get_path(), this, clear_cache_btn_);
|
clear_disk_cache(disk_folder_path(folder_), this, clear_cache_btn_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskCacheDialog::clear_disk_cache(const QString &path, QWidget *parent,
|
void DiskCacheDialog::clear_disk_cache(const QString &path, QWidget *parent,
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
#include "render/diskmanager.h"
|
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -35,7 +34,12 @@ namespace olive
|
|||||||
class DiskCacheDialog : public QDialog {
|
class DiskCacheDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr);
|
/**
|
||||||
|
* @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,
|
static void clear_disk_cache(const QString &path, QWidget *parent,
|
||||||
QPushButton *clear_btn = nullptr);
|
QPushButton *clear_btn = nullptr);
|
||||||
@@ -44,7 +48,7 @@ public slots:
|
|||||||
virtual void accept() override;
|
virtual void accept() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DiskCacheFolder *folder_;
|
void *folder_;
|
||||||
|
|
||||||
FloatSlider *maximum_cache_slider_;
|
FloatSlider *maximum_cache_slider_;
|
||||||
|
|
||||||
|
|||||||
@@ -33,13 +33,11 @@
|
|||||||
|
|
||||||
#include "oakutil/digit.h"
|
#include "oakutil/digit.h"
|
||||||
#include "oakutil/qtutils.h"
|
#include "oakutil/qtutils.h"
|
||||||
#include "codec/exportcodec.h"
|
#include "oakutil/oaknode.h"
|
||||||
#include "codec/exportformat.h"
|
|
||||||
#include "dialog/msgbox.h"
|
#include "dialog/msgbox.h"
|
||||||
#include "dialog/task/task.h"
|
#include "dialog/task/task.h"
|
||||||
#include "exportsavepresetdialog.h"
|
#include "exportsavepresetdialog.h"
|
||||||
#include "node/project.h"
|
#include "oakengine/color.h"
|
||||||
#include "node/project/sequence/sequence.h"
|
|
||||||
#include "oakengine/events.h"
|
#include "oakengine/events.h"
|
||||||
#include "widget/manageddisplay/colorprocessorhandle.h"
|
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||||
#include "widget/viewer/vieweroutpututils.h"
|
#include "widget/viewer/vieweroutpututils.h"
|
||||||
@@ -47,6 +45,7 @@
|
|||||||
#include "oakengine/project.h"
|
#include "oakengine/project.h"
|
||||||
#include "oakengine/task.h"
|
#include "oakengine/task.h"
|
||||||
#include "oakengine/encoding.h"
|
#include "oakengine/encoding.h"
|
||||||
|
#include "oakengine/timeline.h"
|
||||||
#include "oakengine/viewer.h"
|
#include "oakengine/viewer.h"
|
||||||
#include "ui/icons/icons.h"
|
#include "ui/icons/icons.h"
|
||||||
#include "widget/timeruler/timeruler.h"
|
#include "widget/timeruler/timeruler.h"
|
||||||
@@ -70,6 +69,22 @@ int pix_fmt_index(int codec, const QString &pix_fmt)
|
|||||||
return oakengine_encoding_pix_fmt_index(codec, pix_fmt.toUtf8().constData());
|
return oakengine_encoding_pix_fmt_index(codec, pix_fmt.toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ViewerOutput::get_work_area() snapshot through the C ABI.
|
||||||
|
oakengine_viewer_workarea export_viewer_workarea(const OakEngineNode *viewer)
|
||||||
|
{
|
||||||
|
oakengine_viewer_workarea wa = {};
|
||||||
|
oakengine_viewer_get_workarea(viewer, &wa);
|
||||||
|
return wa;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewerOutput::get_length() as a Rational through the C ABI.
|
||||||
|
Rational export_viewer_length(const OakEngineNode *viewer)
|
||||||
|
{
|
||||||
|
int64_t num = 0, den = 1;
|
||||||
|
oakengine_viewer_get_length(viewer, &num, &den);
|
||||||
|
return Rational(num, den);
|
||||||
|
}
|
||||||
|
|
||||||
// OakEngineEncodingParams (assembled by the dialog) -> facade POD. One-to-one with
|
// OakEngineEncodingParams (assembled by the dialog) -> facade POD. One-to-one with
|
||||||
// oak_export_options_ex; see oakengine/exporter.h for the field docs.
|
// oak_export_options_ex; see oakengine/exporter.h for the field docs.
|
||||||
oak_export_options_ex params_to_ex(const OakEngineEncodingParams *p)
|
oak_export_options_ex params_to_ex(const OakEngineEncodingParams *p)
|
||||||
@@ -182,7 +197,7 @@ oak_export_options_ex params_to_ex(const OakEngineEncodingParams *p)
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
ExportDialog::ExportDialog(OakEngineNode *viewer_node, bool stills_only_mode,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: super(parent)
|
: super(parent)
|
||||||
, viewer_node_(viewer_node)
|
, viewer_node_(viewer_node)
|
||||||
@@ -253,7 +268,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
|||||||
range_combobox_ = new QComboBox();
|
range_combobox_ = new QComboBox();
|
||||||
range_combobox_->addItem(tr("Entire Sequence"));
|
range_combobox_->addItem(tr("Entire Sequence"));
|
||||||
range_combobox_->addItem(tr("In to Out"));
|
range_combobox_->addItem(tr("In to Out"));
|
||||||
range_combobox_->setEnabled(viewer_node_->get_work_area()->enabled());
|
range_combobox_->setEnabled(export_viewer_workarea(viewer_node_).enabled);
|
||||||
|
|
||||||
preferences_layout->addWidget(range_combobox_, row, 1, 1, 3);
|
preferences_layout->addWidget(range_combobox_, row, 1, 1, 3);
|
||||||
|
|
||||||
@@ -287,13 +302,14 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
|||||||
|
|
||||||
preferences_tabs_ = new QTabWidget();
|
preferences_tabs_ = new QTabWidget();
|
||||||
|
|
||||||
color_manager_ = oak_color_manager(viewer_node_->project()->color_manager());
|
color_manager_ = oakengine_color_manager_from_project(
|
||||||
|
oakengine_node_get_project(viewer_node_));
|
||||||
video_tab_ = new ExportVideoTab(color_manager_);
|
video_tab_ = new ExportVideoTab(color_manager_);
|
||||||
add_preferences_tab(video_tab_, tr("Video"));
|
add_preferences_tab(video_tab_, tr("Video"));
|
||||||
|
|
||||||
// Set video tab time and make connections
|
// Set video tab time and make connections
|
||||||
viewer_sub_ = oakengine_event_subscribe(
|
viewer_sub_ = oakengine_event_subscribe(
|
||||||
reinterpret_cast<OakEngineNode *>(viewer_node),
|
viewer_node,
|
||||||
OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED,
|
OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED,
|
||||||
[](const oakengine_event *event, void *userdata) {
|
[](const oakengine_event *event, void *userdata) {
|
||||||
auto *dlg = static_cast<ExportDialog *>(userdata);
|
auto *dlg = static_cast<ExportDialog *>(userdata);
|
||||||
@@ -304,13 +320,12 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
|||||||
connect(video_tab_, &ExportVideoTab::time_changed, this,
|
connect(video_tab_, &ExportVideoTab::time_changed, this,
|
||||||
[viewer_node](const Rational &time) {
|
[viewer_node](const Rational &time) {
|
||||||
oakengine_viewer_set_playhead(
|
oakengine_viewer_set_playhead(
|
||||||
reinterpret_cast<OakEngineNode *>(viewer_node),
|
viewer_node,
|
||||||
time.numerator(), time.denominator());
|
time.numerator(), time.denominator());
|
||||||
});
|
});
|
||||||
{
|
{
|
||||||
int64_t pn, pd;
|
int64_t pn, pd;
|
||||||
oakengine_viewer_get_playhead(
|
oakengine_viewer_get_playhead(viewer_node, &pn, &pd);
|
||||||
reinterpret_cast<OakEngineNode *>(viewer_node), &pn, &pd);
|
|
||||||
video_tab_->set_time(Rational(pn, pd));
|
video_tab_->set_time(Rational(pn, pd));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,7 +404,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
|||||||
connect(format_combobox_, &ExportFormatComboBox::format_changed, this,
|
connect(format_combobox_, &ExportFormatComboBox::format_changed, this,
|
||||||
&ExportDialog::format_changed);
|
&ExportDialog::format_changed);
|
||||||
|
|
||||||
VideoParams vp = viewer_output_video_params(viewer_node_);
|
oak::VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||||
video_aspect_ratio_ =
|
video_aspect_ratio_ =
|
||||||
static_cast<double>(vp.width()) / static_cast<double>(vp.height());
|
static_cast<double>(vp.width()) / static_cast<double>(vp.height());
|
||||||
|
|
||||||
@@ -408,7 +423,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
|||||||
&ExportDialog::resolution_changed);
|
&ExportDialog::resolution_changed);
|
||||||
|
|
||||||
connect(video_tab_, &ExportVideoTab::color_space_changed, preview_viewer_,
|
connect(video_tab_, &ExportVideoTab::color_space_changed, preview_viewer_,
|
||||||
static_cast<void (ViewerWidget::*)(const ColorTransform &)>(
|
static_cast<void (ViewerWidget::*)(const oak::ColorTransform &)>(
|
||||||
&ViewerWidget::set_color_transform));
|
&ViewerWidget::set_color_transform));
|
||||||
connect(video_tab_, &ExportVideoTab::image_sequence_check_box_changed, this,
|
connect(video_tab_, &ExportVideoTab::image_sequence_check_box_changed, this,
|
||||||
&ExportDialog::image_sequence_check_box_changed);
|
&ExportDialog::image_sequence_check_box_changed);
|
||||||
@@ -800,12 +815,10 @@ void ExportDialog::load_presets()
|
|||||||
|
|
||||||
void ExportDialog::set_default_filename()
|
void ExportDialog::set_default_filename()
|
||||||
{
|
{
|
||||||
Project *p = viewer_node_->project();
|
OakEngineProject *p = oakengine_node_get_project(viewer_node_);
|
||||||
|
|
||||||
char fn_buf[512];
|
char fn_buf[512];
|
||||||
oakengine_project_filename(
|
oakengine_project_filename(p, fn_buf, sizeof(fn_buf));
|
||||||
reinterpret_cast<OakEngineProject *>(p),
|
|
||||||
fn_buf, sizeof(fn_buf));
|
|
||||||
QDir doc_location;
|
QDir doc_location;
|
||||||
|
|
||||||
if (fn_buf[0] == '\0') {
|
if (fn_buf[0] == '\0') {
|
||||||
@@ -815,16 +828,25 @@ void ExportDialog::set_default_filename()
|
|||||||
doc_location = QFileInfo(fn_buf).dir();
|
doc_location = QFileInfo(fn_buf).dir();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString file_location = doc_location.filePath(viewer_node_->get_label());
|
QString file_location = doc_location.filePath(
|
||||||
|
oak::Node(viewer_node_).get_label());
|
||||||
filename_edit_->setText(file_location);
|
filename_edit_->setText(file_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportDialog::sequence_has_subtitles() const
|
bool ExportDialog::sequence_has_subtitles() const
|
||||||
{
|
{
|
||||||
if (Sequence *s = dynamic_cast<Sequence *>(viewer_node_)) {
|
OakEngineNode *node = viewer_node_;
|
||||||
TrackList *tl = s->track_list(Track::k_subtitle);
|
if (oakengine_node_is_sequence(node)) {
|
||||||
for (Track *t : tl->get_tracks()) {
|
OakEngineSequence *seq = reinterpret_cast<OakEngineSequence *>(node);
|
||||||
if (!t->is_muted() && !t->blocks().empty()) {
|
int track_count = 0;
|
||||||
|
oakengine_sequence_track_count(seq, nullptr, nullptr, &track_count);
|
||||||
|
for (int i = 0; i < track_count; i++) {
|
||||||
|
OakEngineTrack *t = oakengine_sequence_track_at(
|
||||||
|
seq, OAKENGINE_TRACK_TYPE_SUBTITLE, i);
|
||||||
|
if (t &&
|
||||||
|
!oakengine_track_is_muted(seq, OAKENGINE_TRACK_TYPE_SUBTITLE,
|
||||||
|
i) &&
|
||||||
|
oakengine_track_block_count(t) > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -842,7 +864,7 @@ void ExportDialog::set_defaults()
|
|||||||
}
|
}
|
||||||
format_changed(format_combobox_->get_format());
|
format_changed(format_combobox_->get_format());
|
||||||
|
|
||||||
VideoParams vp = viewer_output_video_params(viewer_node_);
|
oak::VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||||
AudioParams ap = viewer_output_audio_params(viewer_node_);
|
AudioParams ap = viewer_output_audio_params(viewer_node_);
|
||||||
|
|
||||||
video_tab_->width_slider()->set_value(vp.width());
|
video_tab_->width_slider()->set_value(vp.width());
|
||||||
@@ -873,7 +895,7 @@ OakEngineEncodingParams *ExportDialog::generate_params() const
|
|||||||
oakengine_encoding_params_set_filename(
|
oakengine_encoding_params_set_filename(
|
||||||
params, filename_edit_->text().trimmed().toUtf8().constData());
|
params, filename_edit_->text().trimmed().toUtf8().constData());
|
||||||
|
|
||||||
const Rational export_len = viewer_node_->get_length();
|
const Rational export_len = export_viewer_length(viewer_node_);
|
||||||
oakengine_encoding_params_set_export_length(
|
oakengine_encoding_params_set_export_length(
|
||||||
params, export_len.numerator(), export_len.denominator());
|
params, export_len.numerator(), export_len.denominator());
|
||||||
|
|
||||||
@@ -887,10 +909,10 @@ OakEngineEncodingParams *ExportDialog::generate_params() const
|
|||||||
(export_time + tb).numerator(),
|
(export_time + tb).numerator(),
|
||||||
(export_time + tb).denominator());
|
(export_time + tb).denominator());
|
||||||
} else if (range_combobox_->currentIndex() == k_range_in_to_out) {
|
} else if (range_combobox_->currentIndex() == k_range_in_to_out) {
|
||||||
const TimeRange &r = viewer_node_->get_work_area()->range();
|
const oakengine_viewer_workarea wa =
|
||||||
|
export_viewer_workarea(viewer_node_);
|
||||||
oakengine_encoding_params_set_custom_range(
|
oakengine_encoding_params_set_custom_range(
|
||||||
params, r.in().numerator(), r.in().denominator(),
|
params, wa.in_num, wa.in_den, wa.out_num, wa.out_den);
|
||||||
r.out().numerator(), r.out().denominator());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video_tab_->scaling_method_combobox()->isEnabled()) {
|
if (video_tab_->scaling_method_combobox()->isEnabled()) {
|
||||||
@@ -985,7 +1007,7 @@ void ExportDialog::set_params(const OakEngineEncodingParams *e)
|
|||||||
format_changed(format_combobox_->get_format());
|
format_changed(format_combobox_->get_format());
|
||||||
|
|
||||||
if (oakengine_encoding_params_has_custom_range(e) &&
|
if (oakengine_encoding_params_has_custom_range(e) &&
|
||||||
viewer_node_->get_work_area()->enabled()) {
|
export_viewer_workarea(viewer_node_).enabled) {
|
||||||
range_combobox_->setCurrentIndex(k_range_in_to_out);
|
range_combobox_->setCurrentIndex(k_range_in_to_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1110,9 +1132,12 @@ void ExportDialog::done(int r)
|
|||||||
Rational ExportDialog::get_export_length() const
|
Rational ExportDialog::get_export_length() const
|
||||||
{
|
{
|
||||||
if (range_combobox_->currentIndex() == k_range_in_to_out) {
|
if (range_combobox_->currentIndex() == k_range_in_to_out) {
|
||||||
return viewer_node_->get_work_area()->range().length();
|
const oakengine_viewer_workarea wa =
|
||||||
|
export_viewer_workarea(viewer_node_);
|
||||||
|
return Rational(wa.out_num, wa.out_den) -
|
||||||
|
Rational(wa.in_num, wa.in_den);
|
||||||
} else {
|
} else {
|
||||||
return viewer_node_->get_length();
|
return export_viewer_length(viewer_node_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1128,7 +1153,7 @@ void ExportDialog::update_viewer_dimensions()
|
|||||||
static_cast<int>(video_tab_->width_slider()->get_value()),
|
static_cast<int>(video_tab_->width_slider()->get_value()),
|
||||||
static_cast<int>(video_tab_->height_slider()->get_value()));
|
static_cast<int>(video_tab_->height_slider()->get_value()));
|
||||||
|
|
||||||
VideoParams vp = viewer_output_video_params(viewer_node_);
|
oak::VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||||
|
|
||||||
float mat16[16];
|
float mat16[16];
|
||||||
oakengine_encoding_generate_matrix(
|
oakengine_encoding_generate_matrix(
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
|
|
||||||
#include "codec/encoder.h"
|
|
||||||
#include "dialog/export/exportformatcombobox.h"
|
#include "dialog/export/exportformatcombobox.h"
|
||||||
#include "exportaudiotab.h"
|
#include "exportaudiotab.h"
|
||||||
#include "exportsubtitlestab.h"
|
#include "exportsubtitlestab.h"
|
||||||
@@ -44,9 +43,9 @@ namespace olive
|
|||||||
class ExportDialog : public QDialog {
|
class ExportDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
ExportDialog(OakEngineNode *viewer_node, bool stills_only_mode,
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
ExportDialog(ViewerOutput *viewer_node, QWidget *parent = nullptr)
|
ExportDialog(OakEngineNode *viewer_node, QWidget *parent = nullptr)
|
||||||
: ExportDialog(viewer_node, false, parent)
|
: ExportDialog(viewer_node, false, parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -75,7 +74,7 @@ private:
|
|||||||
|
|
||||||
void set_defaults();
|
void set_defaults();
|
||||||
|
|
||||||
ViewerOutput *viewer_node_;
|
OakEngineNode *viewer_node_;
|
||||||
|
|
||||||
int64_t viewer_sub_ = 0;
|
int64_t viewer_sub_ = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
#include "codec/encoder.h"
|
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
|
|||||||
@@ -43,24 +43,24 @@ ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent)
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case k_show_all_formats:
|
case k_show_all_formats:
|
||||||
custom_menu_->addAction(create_header(icon::video, tr("Video")));
|
custom_menu_->addAction(create_header(icon::video, tr("Video")));
|
||||||
populate_type(Track::k_video);
|
populate_type(TrackReference::k_video);
|
||||||
custom_menu_->addSeparator();
|
custom_menu_->addSeparator();
|
||||||
|
|
||||||
custom_menu_->addAction(create_header(icon::audio, tr("Audio")));
|
custom_menu_->addAction(create_header(icon::audio, tr("Audio")));
|
||||||
populate_type(Track::k_audio);
|
populate_type(TrackReference::k_audio);
|
||||||
custom_menu_->addSeparator();
|
custom_menu_->addSeparator();
|
||||||
|
|
||||||
custom_menu_->addAction(create_header(icon::subtitles, tr("Subtitle")));
|
custom_menu_->addAction(create_header(icon::subtitles, tr("Subtitle")));
|
||||||
populate_type(Track::k_subtitle);
|
populate_type(TrackReference::k_subtitle);
|
||||||
break;
|
break;
|
||||||
case k_show_audio_only:
|
case k_show_audio_only:
|
||||||
populate_type(Track::k_audio);
|
populate_type(TrackReference::k_audio);
|
||||||
break;
|
break;
|
||||||
case k_show_video_only:
|
case k_show_video_only:
|
||||||
populate_type(Track::k_video);
|
populate_type(TrackReference::k_video);
|
||||||
break;
|
break;
|
||||||
case k_show_subtitles_only:
|
case k_show_subtitles_only:
|
||||||
populate_type(Track::k_subtitle);
|
populate_type(TrackReference::k_subtitle);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ void ExportFormatComboBox::handle_index_change(QAction *a)
|
|||||||
emit format_changed(f);
|
emit format_changed(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportFormatComboBox::populate_type(Track::Type type)
|
void ExportFormatComboBox::populate_type(TrackReference::Type type)
|
||||||
{
|
{
|
||||||
const int fmt_count = oakengine_encoding_format_count();
|
const int fmt_count = oakengine_encoding_format_count();
|
||||||
for (int i = 0; i < fmt_count; i++) {
|
for (int i = 0; i < fmt_count; i++) {
|
||||||
@@ -101,11 +101,11 @@ void ExportFormatComboBox::populate_type(Track::Type type)
|
|||||||
bool has_audio = oakengine_encoding_format_audio_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;
|
bool has_sub = oakengine_encoding_format_subtitle_codec_count(f) > 0;
|
||||||
|
|
||||||
if (type == Track::k_video && has_video) {
|
if (type == TrackReference::k_video && has_video) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else if (type == Track::k_audio && !has_video && has_audio) {
|
} else if (type == TrackReference::k_audio && !has_video && has_audio) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else if (type == Track::k_subtitle && !has_video && !has_audio && has_sub) {
|
} else if (type == TrackReference::k_subtitle && !has_video && !has_audio && has_sub) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QWidgetAction>
|
#include <QWidgetAction>
|
||||||
|
|
||||||
#include "node/output/track/track.h"
|
#include "common/trackreferencehandle.h"
|
||||||
#include "widget/menu/menu.h"
|
#include "widget/menu/menu.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -64,7 +64,7 @@ private slots:
|
|||||||
void handle_index_change(QAction *a);
|
void handle_index_change(QAction *a);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void populate_type(Track::Type type);
|
void populate_type(TrackReference::Type type);
|
||||||
|
|
||||||
QWidgetAction *create_header(const QIcon &icon, const QString &title);
|
QWidgetAction *create_header(const QIcon &icon, const QString &title);
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
#include "exportadvancedvideodialog.h"
|
#include "exportadvancedvideodialog.h"
|
||||||
#include "node/color/colormanager/colormanager.h"
|
|
||||||
#include "oakengine/encoding.h"
|
#include "oakengine/encoding.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
#include "oakengine/timeline.h"
|
#include "oakengine/timeline.h"
|
||||||
#include "oakengine/undo.h"
|
#include "oakengine/undo.h"
|
||||||
|
#include "oakutil/oaknode.h"
|
||||||
#include "streamproperties/audiostreamproperties.h"
|
#include "streamproperties/audiostreamproperties.h"
|
||||||
#include "streamproperties/videostreamproperties.h"
|
#include "streamproperties/videostreamproperties.h"
|
||||||
#include "widget/viewer/vieweroutpututils.h"
|
#include "widget/viewer/vieweroutpututils.h"
|
||||||
@@ -46,20 +47,26 @@ namespace olive
|
|||||||
{
|
{
|
||||||
|
|
||||||
FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
||||||
Footage *footage)
|
OakEngineNode *footage)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, footage_(footage)
|
, footage_(footage)
|
||||||
{
|
{
|
||||||
QGridLayout *layout = new QGridLayout(this);
|
QGridLayout *layout = new QGridLayout(this);
|
||||||
|
|
||||||
setWindowTitle(tr("\"%1\" Properties").arg(footage_->get_label_or_name()));
|
// 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);
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("Name:")), row, 0);
|
layout->addWidget(new QLabel(tr("Name:")), row, 0);
|
||||||
|
|
||||||
footage_name_field_ = new QLineEdit(footage_->get_label());
|
footage_name_field_ = new QLineEdit(footage_label);
|
||||||
layout->addWidget(footage_name_field_, row, 1);
|
layout->addWidget(footage_name_field_, row, 1);
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
@@ -70,8 +77,15 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
|||||||
{
|
{
|
||||||
QHBoxLayout *start_time_layout = new QHBoxLayout();
|
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_ = new QCheckBox(tr("Set"));
|
||||||
source_start_time_enable_->setChecked(footage_->has_source_start_time());
|
source_start_time_enable_->setChecked(has_sst);
|
||||||
start_time_layout->addWidget(source_start_time_enable_);
|
start_time_layout->addWidget(source_start_time_enable_);
|
||||||
|
|
||||||
source_start_time_spin_ = new QDoubleSpinBox();
|
source_start_time_spin_ = new QDoubleSpinBox();
|
||||||
@@ -79,9 +93,7 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
|||||||
source_start_time_spin_->setDecimals(3);
|
source_start_time_spin_->setDecimals(3);
|
||||||
source_start_time_spin_->setSuffix(QStringLiteral(" s"));
|
source_start_time_spin_->setSuffix(QStringLiteral(" s"));
|
||||||
source_start_time_spin_->setValue(
|
source_start_time_spin_->setValue(
|
||||||
footage_->has_source_start_time() ?
|
has_sst ? double(sst_num) / double(sst_den) : 0.0);
|
||||||
footage_->source_start_time().to_double() :
|
|
||||||
0.0);
|
|
||||||
source_start_time_spin_->setEnabled(
|
source_start_time_spin_->setEnabled(
|
||||||
source_start_time_enable_->isChecked());
|
source_start_time_enable_->isChecked());
|
||||||
start_time_layout->addWidget(source_start_time_spin_, 1);
|
start_time_layout->addWidget(source_start_time_spin_, 1);
|
||||||
@@ -89,14 +101,11 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
|||||||
QString detection_note;
|
QString detection_note;
|
||||||
// Detection source comes through the facade (auto-detected field or
|
// Detection source comes through the facade (auto-detected field or
|
||||||
// "manual"), matching the engine's stored value.
|
// "manual"), matching the engine's stored value.
|
||||||
if (footage_->has_source_start_time()) {
|
if (has_sst) {
|
||||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
|
||||||
reinterpret_cast<OakEngineNode *>(footage_));
|
|
||||||
char source_buf[64];
|
char source_buf[64];
|
||||||
source_buf[0] = '\0';
|
source_buf[0] = '\0';
|
||||||
oakengine_footage_get_source_start_time_source(
|
oakengine_footage_get_source_start_time_source(
|
||||||
facade_handle, source_buf, sizeof(source_buf));
|
start_time_handle, source_buf, sizeof(source_buf));
|
||||||
oakengine_footage_free(facade_handle);
|
|
||||||
const QString source = QString::fromUtf8(source_buf);
|
const QString source = QString::fromUtf8(source_buf);
|
||||||
detection_note =
|
detection_note =
|
||||||
(source == QStringLiteral("manual")) ?
|
(source == QStringLiteral("manual")) ?
|
||||||
@@ -105,6 +114,7 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
|||||||
} else {
|
} else {
|
||||||
detection_note = tr("(not detected)");
|
detection_note = tr("(not detected)");
|
||||||
}
|
}
|
||||||
|
oakengine_footage_free(start_time_handle);
|
||||||
start_time_layout->addWidget(new QLabel(detection_note));
|
start_time_layout->addWidget(new QLabel(detection_note));
|
||||||
|
|
||||||
connect(source_start_time_enable_, &QCheckBox::toggled,
|
connect(source_start_time_enable_, &QCheckBox::toggled,
|
||||||
@@ -127,49 +137,66 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
|||||||
|
|
||||||
int first_usable_stream = -1;
|
int first_usable_stream = -1;
|
||||||
|
|
||||||
for (int i = 0; i < footage_->get_total_stream_count(); i++) {
|
int total_stream_count = 0;
|
||||||
Track::Reference reference = footage_->get_reference_from_real_index(i);
|
{
|
||||||
|
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;
|
QString description;
|
||||||
bool is_enabled = false;
|
bool is_enabled = false;
|
||||||
|
|
||||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||||
reinterpret_cast<OakEngineNode *>(footage_));
|
|
||||||
|
|
||||||
switch (reference.type()) {
|
// (track_type, stream_index) pair for this real stream index;
|
||||||
case Track::k_video: {
|
// 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(
|
stacked_widget_->addWidget(
|
||||||
new VideoStreamProperties(footage_, reference.index()));
|
new VideoStreamProperties(footage_, reference_index));
|
||||||
|
|
||||||
VideoParams vp = viewer_output_video_params(footage_, reference.index());
|
is_enabled = oakengine_viewer_get_stream_enabled(
|
||||||
is_enabled = vp.enabled();
|
reinterpret_cast<const OakEngineNode *>(footage_),
|
||||||
|
OAKENGINE_TRACK_TYPE_VIDEO, reference_index) == 1;
|
||||||
{
|
{
|
||||||
char desc_buf[256];
|
char desc_buf[256];
|
||||||
oakengine_footage_describe_video_stream(
|
oakengine_footage_describe_video_stream(
|
||||||
facade_handle, reference.index(), desc_buf,
|
facade_handle, reference_index, desc_buf,
|
||||||
sizeof(desc_buf));
|
sizeof(desc_buf));
|
||||||
description = QString::fromUtf8(desc_buf);
|
description = QString::fromUtf8(desc_buf);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Track::k_audio: {
|
case OAKENGINE_TRACK_TYPE_AUDIO: {
|
||||||
stacked_widget_->addWidget(
|
stacked_widget_->addWidget(
|
||||||
new AudioStreamProperties(footage_, reference.index()));
|
new AudioStreamProperties(footage_, reference_index));
|
||||||
|
|
||||||
AudioParams ap = viewer_output_audio_params(footage_, reference.index());
|
AudioParams ap = viewer_output_audio_params(footage_, reference_index);
|
||||||
is_enabled = ap.enabled();
|
is_enabled = ap.enabled();
|
||||||
{
|
{
|
||||||
char desc_buf[256];
|
char desc_buf[256];
|
||||||
oakengine_footage_describe_audio_stream(
|
oakengine_footage_describe_audio_stream(
|
||||||
facade_handle, reference.index(), desc_buf,
|
facade_handle, reference_index, desc_buf,
|
||||||
sizeof(desc_buf));
|
sizeof(desc_buf));
|
||||||
description = QString::fromUtf8(desc_buf);
|
description = QString::fromUtf8(desc_buf);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Track::k_subtitle: {
|
case OAKENGINE_TRACK_TYPE_SUBTITLE: {
|
||||||
is_enabled = oakengine_footage_get_stream_enabled(
|
is_enabled = oakengine_footage_get_stream_enabled(
|
||||||
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference.index());
|
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference_index);
|
||||||
|
|
||||||
// FIXME: Language?
|
// FIXME: Language?
|
||||||
description = tr("Subtitles");
|
description = tr("Subtitles");
|
||||||
@@ -189,9 +216,9 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
|||||||
track_list_->addItem(item);
|
track_list_->addItem(item);
|
||||||
|
|
||||||
if (first_usable_stream == -1 &&
|
if (first_usable_stream == -1 &&
|
||||||
(reference.type() == Track::k_video ||
|
(reference_type == OAKENGINE_TRACK_TYPE_VIDEO ||
|
||||||
reference.type() == Track::k_audio ||
|
reference_type == OAKENGINE_TRACK_TYPE_AUDIO ||
|
||||||
reference.type() == Track::k_subtitle)) {
|
reference_type == OAKENGINE_TRACK_TYPE_SUBTITLE)) {
|
||||||
first_usable_stream = i;
|
first_usable_stream = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,16 +257,14 @@ void FootagePropertiesDialog::accept()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||||
reinterpret_cast<OakEngineNode *>(footage_));
|
|
||||||
|
|
||||||
// All writes go through the liboakengine C ABI facade; each call lands
|
// All writes go through the liboakengine C ABI facade; each call lands
|
||||||
// on the shared undo stack as an undoable command (replacing this
|
// on the shared undo stack as an undoable command (replacing this
|
||||||
// dialog's own undo command classes with identical semantics).
|
// dialog's own undo command classes with identical semantics).
|
||||||
if (footage_->get_label() != footage_name_field_->text()) {
|
if (oak::Node(footage_).get_label() != footage_name_field_->text()) {
|
||||||
oakengine_node_set_label(
|
oakengine_node_set_label(
|
||||||
reinterpret_cast<OakEngineNode *>(footage_),
|
footage_, footage_name_field_->text().toUtf8().constData());
|
||||||
footage_name_field_->text().toUtf8().constData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply source start time changes
|
// Apply source start time changes
|
||||||
@@ -247,41 +272,55 @@ void FootagePropertiesDialog::accept()
|
|||||||
const bool new_enabled = source_start_time_enable_->isChecked();
|
const bool new_enabled = source_start_time_enable_->isChecked();
|
||||||
const Rational new_time =
|
const Rational new_time =
|
||||||
Rational::from_double(source_start_time_spin_->value());
|
Rational::from_double(source_start_time_spin_->value());
|
||||||
if (new_enabled != footage_->has_source_start_time() ||
|
int cur_sst_num = 0, cur_sst_den = 1;
|
||||||
(new_enabled && new_time != footage_->source_start_time())) {
|
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(
|
oakengine_footage_set_source_start_time(
|
||||||
facade_handle, new_enabled ? 1 : 0, new_time.numerator(),
|
facade_handle, new_enabled ? 1 : 0, new_time.numerator(),
|
||||||
new_time.denominator());
|
new_time.denominator());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < footage_->get_total_stream_count(); i++) {
|
int total_stream_count =
|
||||||
Track::Reference reference = footage_->get_reference_from_real_index(i);
|
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 =
|
bool new_stream_enabled =
|
||||||
(track_list_->item(i)->checkState() == Qt::Checked);
|
(track_list_->item(i)->checkState() == Qt::Checked);
|
||||||
bool old_stream_enabled = new_stream_enabled;
|
bool old_stream_enabled = new_stream_enabled;
|
||||||
|
|
||||||
switch (reference.type()) {
|
switch (reference_type) {
|
||||||
case Track::k_video:
|
case OAKENGINE_TRACK_TYPE_VIDEO:
|
||||||
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||||
facade_handle, OAKENGINE_TRACK_TYPE_VIDEO, reference.index());
|
facade_handle, OAKENGINE_TRACK_TYPE_VIDEO, reference_index);
|
||||||
break;
|
break;
|
||||||
case Track::k_audio:
|
case OAKENGINE_TRACK_TYPE_AUDIO:
|
||||||
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||||
facade_handle, OAKENGINE_TRACK_TYPE_AUDIO, reference.index());
|
facade_handle, OAKENGINE_TRACK_TYPE_AUDIO, reference_index);
|
||||||
break;
|
break;
|
||||||
case Track::k_subtitle:
|
case OAKENGINE_TRACK_TYPE_SUBTITLE:
|
||||||
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||||
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference.index());
|
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference_index);
|
||||||
break;
|
break;
|
||||||
case Track::k_none:
|
default:
|
||||||
case Track::k_count:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old_stream_enabled != new_stream_enabled) {
|
if (old_stream_enabled != new_stream_enabled) {
|
||||||
oakengine_footage_set_stream_enabled(
|
oakengine_footage_set_stream_enabled(
|
||||||
facade_handle, int(reference.type()), reference.index(),
|
facade_handle, reference_type, reference_index,
|
||||||
new_stream_enabled ? 1 : 0);
|
new_stream_enabled ? 1 : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
|
|
||||||
#include "node/project/footage/footage.h"
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -55,7 +55,7 @@ public:
|
|||||||
*
|
*
|
||||||
* Media object to set properties for.
|
* Media object to set properties for.
|
||||||
*/
|
*/
|
||||||
FootagePropertiesDialog(QWidget *parent, Footage *footage);
|
FootagePropertiesDialog(QWidget *parent, OakEngineNode *footage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
@@ -79,9 +79,9 @@ private:
|
|||||||
QDoubleSpinBox *source_start_time_spin_;
|
QDoubleSpinBox *source_start_time_spin_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal pointer to Media object (set in constructor)
|
* @brief Internal handle to the footage node (set in constructor)
|
||||||
*/
|
*/
|
||||||
Footage *footage_;
|
OakEngineNode *footage_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A list widget for listing the tracks in Media
|
* @brief A list widget for listing the tracks in Media
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
AudioStreamProperties::AudioStreamProperties(Footage *footage, int audio_index)
|
AudioStreamProperties::AudioStreamProperties(OakEngineNode *footage,
|
||||||
|
int audio_index)
|
||||||
: footage_(footage)
|
: footage_(footage)
|
||||||
, audio_index_(audio_index)
|
, audio_index_(audio_index)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,20 +22,21 @@
|
|||||||
#ifndef OAK_AUDIOSTREAMPROPERTIES_H
|
#ifndef OAK_AUDIOSTREAMPROPERTIES_H
|
||||||
#define OAK_AUDIOSTREAMPROPERTIES_H
|
#define OAK_AUDIOSTREAMPROPERTIES_H
|
||||||
|
|
||||||
#include "node/project/footage/footage.h"
|
|
||||||
#include "streamproperties.h"
|
#include "streamproperties.h"
|
||||||
|
|
||||||
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
class AudioStreamProperties : public StreamProperties {
|
class AudioStreamProperties : public StreamProperties {
|
||||||
public:
|
public:
|
||||||
AudioStreamProperties(Footage *footage, int audio_index);
|
AudioStreamProperties(OakEngineNode *footage, int audio_index);
|
||||||
|
|
||||||
virtual void accept(void *parent) override;
|
virtual void accept(void *parent) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Footage *footage_;
|
OakEngineNode *footage_;
|
||||||
|
|
||||||
int audio_index_;
|
int audio_index_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "node/project.h"
|
|
||||||
#include "oakengine/color.h"
|
#include "oakengine/color.h"
|
||||||
#include "widget/manageddisplay/colorprocessorhandle.h"
|
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||||
#include "oakengine/footage.h"
|
#include "oakengine/footage.h"
|
||||||
@@ -38,7 +37,8 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
VideoStreamProperties::VideoStreamProperties(OakEngineNode *footage,
|
||||||
|
int video_index)
|
||||||
: footage_(footage)
|
: footage_(footage)
|
||||||
, video_index_(video_index)
|
, video_index_(video_index)
|
||||||
, video_premultiply_alpha_(nullptr)
|
, video_premultiply_alpha_(nullptr)
|
||||||
@@ -51,14 +51,11 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
|||||||
video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0);
|
video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0);
|
||||||
|
|
||||||
oak_video_params vpod;
|
oak_video_params vpod;
|
||||||
oakengine_viewer_get_video_params(
|
oakengine_viewer_get_video_params(footage_, video_index_, &vpod);
|
||||||
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
|
|
||||||
&vpod);
|
|
||||||
|
|
||||||
// Stream override values come through the liboakengine C ABI facade;
|
// Stream override values come through the liboakengine C ABI facade;
|
||||||
// layout-only conditions (channel count, video type) stay direct reads.
|
// layout-only conditions (channel count, video type) stay direct reads.
|
||||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||||
reinterpret_cast<OakEngineNode *>(footage_));
|
|
||||||
char colorspace_buf[256];
|
char colorspace_buf[256];
|
||||||
int color_range = 0;
|
int color_range = 0;
|
||||||
int interlacing = 0;
|
int interlacing = 0;
|
||||||
@@ -79,8 +76,7 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
|||||||
video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0);
|
video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0);
|
||||||
|
|
||||||
video_interlace_combo_ = new InterlacedComboBox();
|
video_interlace_combo_ = new InterlacedComboBox();
|
||||||
video_interlace_combo_->set_interlace_mode(
|
video_interlace_combo_->set_interlace_mode(interlacing);
|
||||||
static_cast<VideoParams::Interlacing>(interlacing));
|
|
||||||
|
|
||||||
video_layout->addWidget(video_interlace_combo_, row, 1);
|
video_layout->addWidget(video_interlace_combo_, row, 1);
|
||||||
|
|
||||||
@@ -93,7 +89,7 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
|||||||
// The dropdown's color space list comes through the facade (same list
|
// The dropdown's color space list comes through the facade (same list
|
||||||
// the engine's color config reports).
|
// the engine's color config reports).
|
||||||
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||||
reinterpret_cast<OakEngineProject *>(footage_->project()));
|
oakengine_node_get_project(footage_));
|
||||||
video_color_space_->addItem(tr("Default (%1)")
|
video_color_space_->addItem(tr("Default (%1)")
|
||||||
.arg(oak_query_string([cm](char *buf, int size) {
|
.arg(oak_query_string([cm](char *buf, int size) {
|
||||||
return oakengine_color_manager_default_input_color_space(
|
return oakengine_color_manager_default_input_color_space(
|
||||||
@@ -183,8 +179,7 @@ void VideoStreamProperties::accept(void *parent)
|
|||||||
{
|
{
|
||||||
Q_UNUSED(parent)
|
Q_UNUSED(parent)
|
||||||
|
|
||||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||||
reinterpret_cast<OakEngineNode *>(footage_));
|
|
||||||
|
|
||||||
QString set_colorspace;
|
QString set_colorspace;
|
||||||
|
|
||||||
@@ -206,9 +201,7 @@ void VideoStreamProperties::accept(void *parent)
|
|||||||
&vp_par_num, &vp_par_den);
|
&vp_par_num, &vp_par_den);
|
||||||
|
|
||||||
oak_video_params vpod;
|
oak_video_params vpod;
|
||||||
oakengine_viewer_get_video_params(
|
oakengine_viewer_get_video_params(footage_, video_index_, &vpod);
|
||||||
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
|
|
||||||
&vpod);
|
|
||||||
|
|
||||||
int64_t vp_start_time = 0, vp_duration = 0;
|
int64_t vp_start_time = 0, vp_duration = 0;
|
||||||
int vp_fr_num = 0, vp_fr_den = 1;
|
int vp_fr_num = 0, vp_fr_den = 1;
|
||||||
@@ -222,9 +215,7 @@ void VideoStreamProperties::accept(void *parent)
|
|||||||
if ((video_premultiply_alpha_ &&
|
if ((video_premultiply_alpha_ &&
|
||||||
video_premultiply_alpha_->isChecked() != (vp_premultiplied != 0)) ||
|
video_premultiply_alpha_->isChecked() != (vp_premultiplied != 0)) ||
|
||||||
set_colorspace != QString::fromUtf8(vp_colorspace) ||
|
set_colorspace != QString::fromUtf8(vp_colorspace) ||
|
||||||
static_cast<VideoParams::Interlacing>(
|
video_interlace_combo_->currentIndex() != vp_interlacing ||
|
||||||
video_interlace_combo_->currentIndex()) !=
|
|
||||||
static_cast<VideoParams::Interlacing>(vp_interlacing) ||
|
|
||||||
color_range_combo_->currentData().toInt() != vp_color_range) {
|
color_range_combo_->currentData().toInt() != vp_color_range) {
|
||||||
oakengine_footage_set_video_stream_overrides(
|
oakengine_footage_set_video_stream_overrides(
|
||||||
facade_handle, video_index_,
|
facade_handle, video_index_,
|
||||||
@@ -264,9 +255,7 @@ void VideoStreamProperties::accept(void *parent)
|
|||||||
bool VideoStreamProperties::sanity_check()
|
bool VideoStreamProperties::sanity_check()
|
||||||
{
|
{
|
||||||
oak_video_params vpod;
|
oak_video_params vpod;
|
||||||
oakengine_viewer_get_video_params(
|
oakengine_viewer_get_video_params(footage_, video_index_, &vpod);
|
||||||
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
|
|
||||||
&vpod);
|
|
||||||
if (vpod.video_type == 2) {
|
if (vpod.video_type == 2) {
|
||||||
if (imgseq_start_time_->get_value() >= imgseq_end_time_->get_value()) {
|
if (imgseq_start_time_->get_value() >= imgseq_end_time_->get_value()) {
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
|
|||||||
@@ -25,25 +25,26 @@
|
|||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
|
||||||
#include "node/project/footage/footage.h"
|
|
||||||
#include "streamproperties.h"
|
#include "streamproperties.h"
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
#include "widget/standardcombos/standardcombos.h"
|
#include "widget/standardcombos/standardcombos.h"
|
||||||
|
|
||||||
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
class VideoStreamProperties : public StreamProperties {
|
class VideoStreamProperties : public StreamProperties {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
VideoStreamProperties(Footage *footage, int video_index);
|
VideoStreamProperties(OakEngineNode *footage, int video_index);
|
||||||
|
|
||||||
virtual void accept(void *parent) override;
|
virtual void accept(void *parent) override;
|
||||||
|
|
||||||
virtual bool sanity_check() override;
|
virtual bool sanity_check() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Footage *footage_;
|
OakEngineNode *footage_;
|
||||||
|
|
||||||
int video_index_;
|
int video_index_;
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,11 @@
|
|||||||
#include "footagerelinkdialog.h"
|
#include "footagerelinkdialog.h"
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "common/nodedatatypes.h"
|
||||||
#include "oakengine/footage.h"
|
#include "oakengine/footage.h"
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
#include "oakengine/project.h"
|
#include "oakengine/project.h"
|
||||||
|
#include "oakutil/oaknode.h"
|
||||||
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@@ -39,7 +41,7 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
FootageRelinkDialog::FootageRelinkDialog(const QVector<Footage *> &footage,
|
FootageRelinkDialog::FootageRelinkDialog(const QVector<OakEngineNode *> &footage,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, footage_(footage)
|
, footage_(footage)
|
||||||
@@ -64,7 +66,7 @@ FootageRelinkDialog::FootageRelinkDialog(const QVector<Footage *> &footage,
|
|||||||
table_->header()->setStretchLastSection(false);
|
table_->header()->setStretchLastSection(false);
|
||||||
|
|
||||||
for (int i = 0; i < footage.size(); i++) {
|
for (int i = 0; i < footage.size(); i++) {
|
||||||
Footage *f = footage.at(i);
|
OakEngineNode *f = footage.at(i);
|
||||||
QTreeWidgetItem *item = new QTreeWidgetItem();
|
QTreeWidgetItem *item = new QTreeWidgetItem();
|
||||||
|
|
||||||
QWidget *item_actions = new QWidget();
|
QWidget *item_actions = new QWidget();
|
||||||
@@ -75,9 +77,10 @@ FootageRelinkDialog::FootageRelinkDialog(const QVector<Footage *> &footage,
|
|||||||
&FootageRelinkDialog::browse_for_footage);
|
&FootageRelinkDialog::browse_for_footage);
|
||||||
item_actions_layout->addWidget(item_browse_btn);
|
item_actions_layout->addWidget(item_browse_btn);
|
||||||
|
|
||||||
item->setIcon(0, f->data(Node::icon).value<QIcon>());
|
item->setIcon(
|
||||||
item->setText(0, f->get_label());
|
0, oak::Node(f).data(k_node_data_icon).value<QIcon>());
|
||||||
item->setText(1, f->filename());
|
item->setText(0, oak::Node(f).get_label());
|
||||||
|
item->setText(1, oak::Footage::borrow(f).filename());
|
||||||
|
|
||||||
table_->addTopLevelItem(item);
|
table_->addTopLevelItem(item);
|
||||||
|
|
||||||
@@ -99,21 +102,22 @@ FootageRelinkDialog::FootageRelinkDialog(const QVector<Footage *> &footage,
|
|||||||
|
|
||||||
void FootageRelinkDialog::update_footage_item(int index)
|
void FootageRelinkDialog::update_footage_item(int index)
|
||||||
{
|
{
|
||||||
Footage *f = footage_.at(index);
|
OakEngineNode *f = footage_.at(index);
|
||||||
QTreeWidgetItem *item = table_->topLevelItem(index);
|
QTreeWidgetItem *item = table_->topLevelItem(index);
|
||||||
item->setIcon(0, f->data(Node::icon).value<QIcon>());
|
item->setIcon(0, oak::Node(f).data(k_node_data_icon).value<QIcon>());
|
||||||
item->setText(1, f->filename());
|
item->setText(1, oak::Footage::borrow(f).filename());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FootageRelinkDialog::browse_for_footage()
|
void FootageRelinkDialog::browse_for_footage()
|
||||||
{
|
{
|
||||||
int index = sender()->property("index").toInt();
|
int index = sender()->property("index").toInt();
|
||||||
Footage *f = footage_.at(index);
|
OakEngineNode *f = footage_.at(index);
|
||||||
|
|
||||||
QFileInfo info(f->filename());
|
QFileInfo info(oak::Footage::borrow(f).filename());
|
||||||
|
|
||||||
QString new_fn = QFileDialog::getOpenFileName(
|
QString new_fn = QFileDialog::getOpenFileName(
|
||||||
this, tr("Relink \"%1\"").arg(f->get_label()), info.absolutePath(),
|
this, tr("Relink \"%1\"").arg(oak::Node(f).get_label()),
|
||||||
|
info.absolutePath(),
|
||||||
Core::footage_file_dialog_filter());
|
Core::footage_file_dialog_filter());
|
||||||
|
|
||||||
// Originally, this function would attempt to filter to the exact filename of the missing file.
|
// Originally, this function would attempt to filter to the exact filename of the missing file.
|
||||||
@@ -141,8 +145,7 @@ void FootageRelinkDialog::browse_for_footage()
|
|||||||
// Relink through the facade (reprobes the file and resets stream /
|
// Relink through the facade (reprobes the file and resets stream /
|
||||||
// proxy state; relinked footage becomes valid when the probe
|
// proxy state; relinked footage becomes valid when the probe
|
||||||
// succeeds).
|
// succeeds).
|
||||||
OakEngineFootage *relink_handle = oakengine_footage_borrow(
|
OakEngineFootage *relink_handle = oakengine_footage_borrow(f);
|
||||||
reinterpret_cast<OakEngineNode *>(f));
|
|
||||||
const int relink_rc = oakengine_footage_relink(
|
const int relink_rc = oakengine_footage_relink(
|
||||||
relink_handle, new_fn.toUtf8().constData());
|
relink_handle, new_fn.toUtf8().constData());
|
||||||
oakengine_footage_free(relink_handle);
|
oakengine_footage_free(relink_handle);
|
||||||
@@ -162,30 +165,33 @@ void FootageRelinkDialog::browse_for_footage()
|
|||||||
// Check all other footage files for matches in the new directory
|
// Check all other footage files for matches in the new directory
|
||||||
// (facade's exact file-name matching, mirroring the second attempt
|
// (facade's exact file-name matching, mirroring the second attempt
|
||||||
// of the old per-footage loop).
|
// of the old per-footage loop).
|
||||||
Project *project = f->project();
|
OakEngineProject *project = oakengine_node_get_project(f);
|
||||||
if (project) {
|
if (project) {
|
||||||
oakengine_project_find_offline_footage(
|
oakengine_project_find_offline_footage(
|
||||||
reinterpret_cast<OakEngineProject *>(project),
|
project, new_dir.absolutePath().toUtf8().constData());
|
||||||
new_dir.absolutePath().toUtf8().constData());
|
|
||||||
|
|
||||||
// The old dialog also tried the original directory's relative
|
// The old dialog also tried the original directory's relative
|
||||||
// paths, which the facade's exact-name matching does not cover;
|
// paths, which the facade's exact-name matching does not cover;
|
||||||
// keep that pass here.
|
// keep that pass here.
|
||||||
for (int it = 0; it < footage_.size(); it++) {
|
for (int it = 0; it < footage_.size(); it++) {
|
||||||
Footage *other_footage = footage_.at(it);
|
OakEngineNode *other_footage = footage_.at(it);
|
||||||
|
|
||||||
// Ignore footage that's already valid of course
|
// Ignore footage that's already valid of course
|
||||||
if (!other_footage->is_valid()) {
|
if (!oakengine_footage_is_valid(other_footage)) {
|
||||||
// Get footage path relative to original directory
|
// Get footage path relative to original directory
|
||||||
QString relative_to_original =
|
QString relative_to_original =
|
||||||
original_dir.relativeFilePath(other_footage->filename());
|
original_dir.relativeFilePath(
|
||||||
|
oak::Footage::borrow(other_footage).filename());
|
||||||
QString absolute_to_new =
|
QString absolute_to_new =
|
||||||
new_dir.filePath(relative_to_original);
|
new_dir.filePath(relative_to_original);
|
||||||
|
|
||||||
if (QFileInfo::exists(absolute_to_new)) {
|
if (QFileInfo::exists(absolute_to_new)) {
|
||||||
|
OakEngineFootage *other_handle =
|
||||||
|
oakengine_footage_borrow(other_footage);
|
||||||
oakengine_footage_relink(
|
oakengine_footage_relink(
|
||||||
reinterpret_cast<OakEngineFootage *>(other_footage),
|
other_handle,
|
||||||
absolute_to_new.toUtf8().constData());
|
absolute_to_new.toUtf8().constData());
|
||||||
|
oakengine_footage_free(other_handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,7 +207,7 @@ void FootageRelinkDialog::browse_for_footage()
|
|||||||
// jump to that footage so the user knows where it is.
|
// jump to that footage so the user knows where it is.
|
||||||
int next_invalid = -1;
|
int next_invalid = -1;
|
||||||
for (int i = 0; i < footage_.size(); i++) {
|
for (int i = 0; i < footage_.size(); i++) {
|
||||||
if (!footage_.at(i)->is_valid()) {
|
if (!oakengine_footage_is_valid(footage_.at(i))) {
|
||||||
next_invalid = i;
|
next_invalid = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,9 @@
|
|||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "node/project/footage/footage.h"
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -33,7 +34,7 @@ namespace olive
|
|||||||
class FootageRelinkDialog : public QDialog {
|
class FootageRelinkDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
FootageRelinkDialog(const QVector<Footage *> &footage,
|
FootageRelinkDialog(const QVector<OakEngineNode *> &footage,
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -41,7 +42,7 @@ private:
|
|||||||
|
|
||||||
QTreeWidget *table_;
|
QTreeWidget *table_;
|
||||||
|
|
||||||
QVector<Footage *> footage_;
|
QVector<OakEngineNode *> footage_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void browse_for_footage();
|
void browse_for_footage();
|
||||||
|
|||||||
@@ -24,7 +24,9 @@
|
|||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
|
|
||||||
|
#include "common/keyframetypes.h"
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
|
#include "olive/core/util/timecodefunctions.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -32,41 +34,47 @@ namespace olive
|
|||||||
namespace
|
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
|
// Selected keyframes grouped by owning input (node/id/element), with
|
||||||
// times converted to the facade's frame timestamps. The dialog's
|
// times converted to the facade's frame timestamps. The dialog's
|
||||||
// per-property writes go through the facade as ONE undoable command per
|
// per-property writes go through the facade as ONE undoable command per
|
||||||
// group (usually just one).
|
// group (usually just one).
|
||||||
struct KeyGroup {
|
struct KeyGroup {
|
||||||
Node *node;
|
OakEngineNode *node;
|
||||||
QString input;
|
QString input;
|
||||||
int element;
|
int element;
|
||||||
QVector<int64_t> times;
|
QVector<int64_t> times;
|
||||||
QVector<int> tracks;
|
QVector<int> tracks;
|
||||||
};
|
};
|
||||||
|
|
||||||
QVector<KeyGroup> group_keys(const std::vector<NodeKeyframe *> &keys)
|
QVector<KeyGroup> group_keys(const QVector<oak::Keyframe> &keys)
|
||||||
{
|
{
|
||||||
QVector<KeyGroup> groups;
|
QVector<KeyGroup> groups;
|
||||||
for (NodeKeyframe *item : keys) {
|
for (const oak::Keyframe &item : keys) {
|
||||||
|
OakEngineNode *node = item.node().handle();
|
||||||
int g = 0;
|
int g = 0;
|
||||||
for (; g < groups.size(); g++) {
|
for (; g < groups.size(); g++) {
|
||||||
if (groups.at(g).node == item->parent() &&
|
if (groups.at(g).node == node &&
|
||||||
groups.at(g).input == item->input() &&
|
groups.at(g).input == item.input_id() &&
|
||||||
groups.at(g).element == item->element()) {
|
groups.at(g).element == item.element()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (g == groups.size()) {
|
if (g == groups.size()) {
|
||||||
groups.append({ item->parent(), item->input(), item->element(),
|
groups.append({ node, item.input_id(), item.element(),
|
||||||
{}, {} });
|
{}, {} });
|
||||||
}
|
}
|
||||||
OakEngineNode *handle =
|
|
||||||
reinterpret_cast<OakEngineNode *>(item->parent());
|
|
||||||
int tbn = 0, tbd = 0;
|
int tbn = 0, tbd = 0;
|
||||||
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
oakengine_node_frame_time_base(node, &tbn, &tbd);
|
||||||
groups[g].times.append(Timecode::time_to_timestamp(
|
groups[g].times.append(Timecode::time_to_timestamp(
|
||||||
item->time(), Rational(tbn, tbd), Timecode::k_round));
|
keyframe_rational_time(item), Rational(tbn, tbd), Timecode::k_round));
|
||||||
groups[g].tracks.append(item->track());
|
groups[g].tracks.append(item.track());
|
||||||
}
|
}
|
||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
@@ -74,7 +82,7 @@ QVector<KeyGroup> group_keys(const std::vector<NodeKeyframe *> &keys)
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
KeyframePropertiesDialog::KeyframePropertiesDialog(
|
KeyframePropertiesDialog::KeyframePropertiesDialog(
|
||||||
const std::vector<NodeKeyframe *> &keys, const Rational &timebase,
|
const QVector<oak::Keyframe> &keys, const Rational &timebase,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, keys_(keys)
|
, keys_(keys)
|
||||||
@@ -137,47 +145,48 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(
|
|||||||
bool all_same_bezier_out_x = true;
|
bool all_same_bezier_out_x = true;
|
||||||
bool all_same_bezier_out_y = true;
|
bool all_same_bezier_out_y = true;
|
||||||
|
|
||||||
for (size_t i = 0; i < keys_.size(); i++) {
|
for (int i = 0; i < keys_.size(); i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
NodeKeyframe *prev_key = keys_.at(i - 1);
|
const oak::Keyframe &prev_key = keys_.at(i - 1);
|
||||||
NodeKeyframe *this_key = keys_.at(i);
|
const oak::Keyframe &this_key = keys_.at(i);
|
||||||
|
|
||||||
// Determine if the keyframes are all the same time or not
|
// Determine if the keyframes are all the same time or not
|
||||||
if (all_same_time) {
|
if (all_same_time) {
|
||||||
all_same_time = (prev_key->time() == this_key->time());
|
all_same_time = (keyframe_rational_time(prev_key) ==
|
||||||
|
keyframe_rational_time(this_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if the keyframes are all the same type
|
// Determine if the keyframes are all the same type
|
||||||
if (all_same_type) {
|
if (all_same_type) {
|
||||||
all_same_type = (prev_key->type() == this_key->type());
|
all_same_type = (prev_key.type() == this_key.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check all four bezier control points
|
// Check all four bezier control points
|
||||||
if (all_same_bezier_in_x) {
|
if (all_same_bezier_in_x) {
|
||||||
all_same_bezier_in_x = (prev_key->bezier_control_in().x() ==
|
all_same_bezier_in_x = (prev_key.bezier_point(0).x() ==
|
||||||
this_key->bezier_control_in().x());
|
this_key.bezier_point(0).x());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_same_bezier_in_y) {
|
if (all_same_bezier_in_y) {
|
||||||
all_same_bezier_in_y = (prev_key->bezier_control_in().y() ==
|
all_same_bezier_in_y = (prev_key.bezier_point(0).y() ==
|
||||||
this_key->bezier_control_in().y());
|
this_key.bezier_point(0).y());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_same_bezier_out_x) {
|
if (all_same_bezier_out_x) {
|
||||||
all_same_bezier_out_x = (prev_key->bezier_control_out().x() ==
|
all_same_bezier_out_x = (prev_key.bezier_point(1).x() ==
|
||||||
this_key->bezier_control_out().x());
|
this_key.bezier_point(1).x());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_same_bezier_out_y) {
|
if (all_same_bezier_out_y) {
|
||||||
all_same_bezier_out_y = (prev_key->bezier_control_out().y() ==
|
all_same_bezier_out_y = (prev_key.bezier_point(1).y() ==
|
||||||
this_key->bezier_control_out().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)
|
// Determine if any keyframes are on the same track (in which case we can't set the time)
|
||||||
if (can_set_time) {
|
if (can_set_time) {
|
||||||
for (size_t j = 0; j < keys_.size(); j++) {
|
for (int j = 0; j < keys_.size(); j++) {
|
||||||
if (i != j && keys_.at(j)->track() == keys_.at(i)->track()) {
|
if (i != j && keys_.at(j).track() == keys_.at(i).track()) {
|
||||||
can_set_time = false;
|
can_set_time = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -192,7 +201,7 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (all_same_time) {
|
if (all_same_time) {
|
||||||
time_slider_->set_value(keys_.front()->time());
|
time_slider_->set_value(keyframe_rational_time(keys_.front()));
|
||||||
} else {
|
} else {
|
||||||
time_slider_->set_tristate();
|
time_slider_->set_tristate();
|
||||||
}
|
}
|
||||||
@@ -207,14 +216,16 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(
|
|||||||
key_type_changed(0);
|
key_type_changed(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_select_->addItem(tr("Linear"), NodeKeyframe::k_linear);
|
// Item data uses the facade easing order (oak::Keyframe::type()):
|
||||||
type_select_->addItem(tr("Hold"), NodeKeyframe::k_hold);
|
// 0 = linear, 1 = bezier, 2 = hold.
|
||||||
type_select_->addItem(tr("Bezier"), NodeKeyframe::k_bezier);
|
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_same_type) {
|
||||||
// If all keyframes are the same type, set it here
|
// If all keyframes are the same type, set it here
|
||||||
for (int i = 0; i < type_select_->count(); i++) {
|
for (int i = 0; i < type_select_->count(); i++) {
|
||||||
if (type_select_->itemData(i).toInt() == keys_.front()->type()) {
|
if (type_select_->itemData(i).toInt() == keys_.front().type()) {
|
||||||
type_select_->setCurrentIndex(i);
|
type_select_->setCurrentIndex(i);
|
||||||
|
|
||||||
// Ensure UI updates for this index
|
// Ensure UI updates for this index
|
||||||
@@ -225,13 +236,13 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
set_up_bezier_slider(bezier_in_x_slider_, all_same_bezier_in_x,
|
set_up_bezier_slider(bezier_in_x_slider_, all_same_bezier_in_x,
|
||||||
keys_.front()->bezier_control_in().x());
|
keys_.front().bezier_point(0).x());
|
||||||
set_up_bezier_slider(bezier_in_y_slider_, all_same_bezier_in_y,
|
set_up_bezier_slider(bezier_in_y_slider_, all_same_bezier_in_y,
|
||||||
keys_.front()->bezier_control_in().y());
|
keys_.front().bezier_point(0).y());
|
||||||
set_up_bezier_slider(bezier_out_x_slider_, all_same_bezier_out_x,
|
set_up_bezier_slider(bezier_out_x_slider_, all_same_bezier_out_x,
|
||||||
keys_.front()->bezier_control_out().x());
|
keys_.front().bezier_point(1).x());
|
||||||
set_up_bezier_slider(bezier_out_y_slider_, all_same_bezier_out_y,
|
set_up_bezier_slider(bezier_out_y_slider_, all_same_bezier_out_y,
|
||||||
keys_.front()->bezier_control_out().y());
|
keys_.front().bezier_point(1).y());
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
@@ -251,26 +262,20 @@ void KeyframePropertiesDialog::accept()
|
|||||||
const QVector<KeyGroup> groups = group_keys(keys_);
|
const QVector<KeyGroup> groups = group_keys(keys_);
|
||||||
|
|
||||||
if (new_type > -1) {
|
if (new_type > -1) {
|
||||||
// Engine type (linear 0 / hold 1 / bezier 2) to the facade's
|
// new_type is already in the facade's easing order (linear 0 /
|
||||||
// easing order (linear 0 / bezier 1 / hold 2).
|
// bezier 1 / hold 2), so it can be passed straight through.
|
||||||
int facade_type = 0;
|
|
||||||
if (new_type == NodeKeyframe::k_bezier) {
|
|
||||||
facade_type = 1;
|
|
||||||
} else if (new_type == NodeKeyframe::k_hold) {
|
|
||||||
facade_type = 2;
|
|
||||||
}
|
|
||||||
foreach (const KeyGroup &g, groups) {
|
foreach (const KeyGroup &g, groups) {
|
||||||
oakengine_node_keyframes_set_type_many(
|
oakengine_node_keyframes_set_type_many(
|
||||||
reinterpret_cast<OakEngineNode *>(g.node),
|
g.node,
|
||||||
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
||||||
g.tracks.data(), g.times.size(), facade_type);
|
g.tracks.data(), g.times.size(), new_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bezier_group_->isEnabled()) {
|
if (bezier_group_->isEnabled()) {
|
||||||
foreach (const KeyGroup &g, groups) {
|
foreach (const KeyGroup &g, groups) {
|
||||||
oakengine_node_keyframes_set_bezier_many(
|
oakengine_node_keyframes_set_bezier_many(
|
||||||
reinterpret_cast<OakEngineNode *>(g.node),
|
g.node,
|
||||||
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
||||||
g.tracks.data(), g.times.size(),
|
g.tracks.data(), g.times.size(),
|
||||||
bezier_in_x_slider_->get_value(),
|
bezier_in_x_slider_->get_value(),
|
||||||
@@ -285,8 +290,7 @@ void KeyframePropertiesDialog::accept()
|
|||||||
// the times the groups were built from.
|
// the times the groups were built from.
|
||||||
if (time_slider_->isEnabled() && !time_slider_->is_tristate()) {
|
if (time_slider_->isEnabled() && !time_slider_->is_tristate()) {
|
||||||
foreach (const KeyGroup &g, groups) {
|
foreach (const KeyGroup &g, groups) {
|
||||||
OakEngineNode *handle =
|
OakEngineNode *handle = g.node;
|
||||||
reinterpret_cast<OakEngineNode *>(g.node);
|
|
||||||
int tbn = 0, tbd = 0;
|
int tbn = 0, tbd = 0;
|
||||||
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
||||||
oakengine_node_keyframes_set_time_many(
|
oakengine_node_keyframes_set_time_many(
|
||||||
@@ -313,7 +317,7 @@ void KeyframePropertiesDialog::set_up_bezier_slider(FloatSlider *slider,
|
|||||||
void KeyframePropertiesDialog::key_type_changed(int index)
|
void KeyframePropertiesDialog::key_type_changed(int index)
|
||||||
{
|
{
|
||||||
bezier_group_->setEnabled(type_select_->itemData(index) ==
|
bezier_group_->setEnabled(type_select_->itemData(index) ==
|
||||||
NodeKeyframe::k_bezier);
|
KeyframeTypes::k_facade_bezier);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
|
|
||||||
#include "node/keyframe.h"
|
#include "oakutil/oaknode.h"
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
#include "widget/slider/rationalslider.h"
|
#include "widget/slider/rationalslider.h"
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ namespace olive
|
|||||||
class KeyframePropertiesDialog : public QDialog {
|
class KeyframePropertiesDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
KeyframePropertiesDialog(const std::vector<NodeKeyframe *> &keys,
|
KeyframePropertiesDialog(const QVector<oak::Keyframe> &keys,
|
||||||
const Rational &timebase,
|
const Rational &timebase,
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ public slots:
|
|||||||
private:
|
private:
|
||||||
void set_up_bezier_slider(FloatSlider *slider, bool all_same, double value);
|
void set_up_bezier_slider(FloatSlider *slider, bool all_same, double value);
|
||||||
|
|
||||||
const std::vector<NodeKeyframe *> &keys_;
|
const QVector<oak::Keyframe> &keys_;
|
||||||
|
|
||||||
Rational timebase_;
|
Rational timebase_;
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
#include "oakengine/timeline.h"
|
#include "oakengine/timeline.h"
|
||||||
|
#include "widget/timeruler/markerhandle.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -37,7 +38,7 @@ namespace olive
|
|||||||
#define super QDialog
|
#define super QDialog
|
||||||
|
|
||||||
MarkerPropertiesDialog::MarkerPropertiesDialog(
|
MarkerPropertiesDialog::MarkerPropertiesDialog(
|
||||||
const std::vector<TimelineMarker *> &markers, const Rational &timebase,
|
const std::vector<OakEngineMarker *> &markers, const Rational &timebase,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: super(parent)
|
: super(parent)
|
||||||
, markers_(markers)
|
, markers_(markers)
|
||||||
@@ -66,10 +67,11 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (markers.size() == 1) {
|
if (markers.size() == 1) {
|
||||||
in_slider_->set_value(markers.front()->time().in());
|
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_display_type(slider::k_time);
|
||||||
in_slider_->set_timebase(timebase);
|
in_slider_->set_timebase(timebase);
|
||||||
out_slider_->set_value(markers.front()->time().out());
|
out_slider_->set_value(marker_range.out());
|
||||||
out_slider_->set_display_type(slider::k_time);
|
out_slider_->set_display_type(slider::k_time);
|
||||||
out_slider_->set_timebase(timebase);
|
out_slider_->set_timebase(timebase);
|
||||||
} else {
|
} else {
|
||||||
@@ -89,9 +91,9 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(
|
|||||||
color_menu_ = new ColorCodingComboBox();
|
color_menu_ = new ColorCodingComboBox();
|
||||||
layout->addWidget(color_menu_, row, 1);
|
layout->addWidget(color_menu_, row, 1);
|
||||||
|
|
||||||
color_menu_->set_color(markers.front()->color());
|
color_menu_->set_color(marker_color(markers.front()));
|
||||||
for (size_t i = 1; i < markers.size(); i++) {
|
for (size_t i = 1; i < markers.size(); i++) {
|
||||||
if (markers.at(i)->color() != color_menu_->get_selected_color()) {
|
if (marker_color(markers.at(i)) != color_menu_->get_selected_color()) {
|
||||||
color_menu_->set_color(-1);
|
color_menu_->set_color(-1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -107,9 +109,9 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(
|
|||||||
layout->addWidget(label_edit_, row, 1);
|
layout->addWidget(label_edit_, row, 1);
|
||||||
|
|
||||||
// Determine what the startup label text should be
|
// Determine what the startup label text should be
|
||||||
label_edit_->setText(markers.front()->name());
|
label_edit_->setText(marker_name(markers.front()));
|
||||||
for (size_t i = 1; i < markers.size(); i++) {
|
for (size_t i = 1; i < markers.size(); i++) {
|
||||||
if (markers.at(i)->name() != label_edit_->text()) {
|
if (marker_name(markers.at(i)) != label_edit_->text()) {
|
||||||
label_edit_->clear();
|
label_edit_->clear();
|
||||||
label_edit_->setPlaceholderText(tr("(multiple)"));
|
label_edit_->setPlaceholderText(tr("(multiple)"));
|
||||||
break;
|
break;
|
||||||
@@ -143,8 +145,8 @@ void MarkerPropertiesDialog::accept()
|
|||||||
// Batch-set properties via facade (one undoable command)
|
// Batch-set properties via facade (one undoable command)
|
||||||
{
|
{
|
||||||
QVector<OakEngineMarker *> oak_markers;
|
QVector<OakEngineMarker *> oak_markers;
|
||||||
foreach (TimelineMarker *m, markers_) {
|
foreach (OakEngineMarker *m, markers_) {
|
||||||
oak_markers.append(reinterpret_cast<OakEngineMarker *>(m));
|
oak_markers.append(m);
|
||||||
}
|
}
|
||||||
int color = color_menu_->get_selected_color();
|
int color = color_menu_->get_selected_color();
|
||||||
QByteArray name_ba;
|
QByteArray name_ba;
|
||||||
|
|||||||
@@ -22,10 +22,12 @@
|
|||||||
#ifndef OAK_MARKERPROPERTIESDIALOG_H
|
#ifndef OAK_MARKERPROPERTIESDIALOG_H
|
||||||
#define OAK_MARKERPROPERTIESDIALOG_H
|
#define OAK_MARKERPROPERTIESDIALOG_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
|
|
||||||
#include "timeline/timelinemarker.h"
|
#include "oakengine/timeline.h"
|
||||||
#include "widget/colorlabelmenu/colorcodingcombobox.h"
|
#include "widget/colorlabelmenu/colorcodingcombobox.h"
|
||||||
#include "widget/slider/rationalslider.h"
|
#include "widget/slider/rationalslider.h"
|
||||||
|
|
||||||
@@ -54,14 +56,14 @@ signals:
|
|||||||
class MarkerPropertiesDialog : public QDialog {
|
class MarkerPropertiesDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
MarkerPropertiesDialog(const std::vector<TimelineMarker *> &markers,
|
MarkerPropertiesDialog(const std::vector<OakEngineMarker *> &markers,
|
||||||
const Rational &timebase, QWidget *parent = nullptr);
|
const Rational &timebase, QWidget *parent = nullptr);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void accept() override;
|
virtual void accept() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<TimelineMarker *> markers_;
|
std::vector<OakEngineMarker *> markers_;
|
||||||
|
|
||||||
LineEditWithFocusSignal *label_edit_;
|
LineEditWithFocusSignal *label_edit_;
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,14 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "dialog/sequence/sequence.h"
|
#include "dialog/sequence/sequence.h"
|
||||||
|
#include "oakutil/oaknode.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
OTIOPropertiesDialog::OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
OTIOPropertiesDialog::OTIOPropertiesDialog(
|
||||||
Project *active_project,
|
const QList<OakEngineSequence *> &sequences,
|
||||||
QWidget *parent)
|
OakEngineProject *active_project, QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, sequences_(sequences)
|
, sequences_(sequences)
|
||||||
{
|
{
|
||||||
@@ -51,7 +52,7 @@ OTIOPropertiesDialog::OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
|||||||
|
|
||||||
for (int i = 0; i < sequences.size(); i++) {
|
for (int i = 0; i < sequences.size(); i++) {
|
||||||
QTreeWidgetItem *item = new QTreeWidgetItem();
|
QTreeWidgetItem *item = new QTreeWidgetItem();
|
||||||
Sequence *s = sequences.at(i);
|
OakEngineSequence *s = sequences.at(i);
|
||||||
|
|
||||||
QWidget *item_actions = new QWidget();
|
QWidget *item_actions = new QWidget();
|
||||||
QHBoxLayout *item_actions_layout = new QHBoxLayout(item_actions);
|
QHBoxLayout *item_actions_layout = new QHBoxLayout(item_actions);
|
||||||
@@ -61,7 +62,8 @@ OTIOPropertiesDialog::OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
|||||||
&OTIOPropertiesDialog::SetupSequence);
|
&OTIOPropertiesDialog::SetupSequence);
|
||||||
item_actions_layout->addWidget(item_settings_btn);
|
item_actions_layout->addWidget(item_settings_btn);
|
||||||
|
|
||||||
item->setText(0, s->GetLabel());
|
item->setText(
|
||||||
|
0, oak::Node(reinterpret_cast<OakEngineNode *>(s)).get_label());
|
||||||
|
|
||||||
table_->addTopLevelItem(item);
|
table_->addTopLevelItem(item);
|
||||||
|
|
||||||
@@ -89,9 +91,10 @@ OTIOPropertiesDialog::OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
|||||||
void OTIOPropertiesDialog::SetupSequence()
|
void OTIOPropertiesDialog::SetupSequence()
|
||||||
{
|
{
|
||||||
int index = sender()->property("index").toInt();
|
int index = sender()->property("index").toInt();
|
||||||
Sequence *s = sequences_.at(index);
|
OakEngineSequence *s = sequences_.at(index);
|
||||||
SequenceDialog sd(s, SequenceDialog::kNew);
|
SequenceDialog sd(reinterpret_cast<OakEngineNode *>(s),
|
||||||
sd.SetUndoable(false);
|
SequenceDialog::k_new);
|
||||||
|
sd.set_undoable(false);
|
||||||
sd.exec();
|
sd.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,9 @@
|
|||||||
|
|
||||||
#include "oakutil/define.h"
|
#include "oakutil/define.h"
|
||||||
#include "opentimelineio/timeline.h"
|
#include "opentimelineio/timeline.h"
|
||||||
#include "node/project/sequence/sequence.h"
|
|
||||||
#include "node/project.h"
|
struct OakEngineProject;
|
||||||
|
struct OakEngineSequence;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -39,13 +40,14 @@ namespace olive
|
|||||||
class OTIOPropertiesDialog : public QDialog {
|
class OTIOPropertiesDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
OTIOPropertiesDialog(const QList<OakEngineSequence *> &sequences,
|
||||||
Project *active_project, QWidget *parent = nullptr);
|
OakEngineProject *active_project,
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTreeWidget *table_;
|
QTreeWidget *table_;
|
||||||
|
|
||||||
const QList<Sequence *> sequences_;
|
const QList<OakEngineSequence *> sequences_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -27,8 +27,7 @@
|
|||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
#include "node/node.h"
|
#include "oakutil/oaknode.h"
|
||||||
#include "oakengine/node.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -69,10 +68,8 @@ PreferencesAppearanceTab::PreferencesAppearanceTab()
|
|||||||
|
|
||||||
QGridLayout *color_layout = new QGridLayout(color_group);
|
QGridLayout *color_layout = new QGridLayout(color_group);
|
||||||
|
|
||||||
for (int i = 0; i < Node::k_category_count; i++) {
|
for (int i = 0; i < oak::k_category_count; i++) {
|
||||||
char cat_buf[256];
|
QString cat_name = oak::Node::category_name(i);
|
||||||
oakengine_node_category_name(i, cat_buf, sizeof(cat_buf));
|
|
||||||
QString cat_name = QString::fromUtf8(cat_buf);
|
|
||||||
color_layout->addWidget(new QLabel(cat_name), i, 0);
|
color_layout->addWidget(new QLabel(cat_name), i, 0);
|
||||||
|
|
||||||
ColorCodingComboBox *ccc = new ColorCodingComboBox();
|
ColorCodingComboBox *ccc = new ColorCodingComboBox();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "preferencesgeneraltab.h"
|
#include "preferencesgeneraltab.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
|
|
||||||
#include "dialog/configbase/configdialogbase.h"
|
#include "dialog/configbase/configdialogbase.h"
|
||||||
#include "node/project/sequence/sequence.h"
|
|
||||||
#include "widget/slider/rationalslider.h"
|
#include "widget/slider/rationalslider.h"
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ PluginProgressDialogReporter::PluginProgressDialogReporter(
|
|||||||
dialog_->setAttribute(Qt::WA_DeleteOnClose);
|
dialog_->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
QObject::connect(dialog_, &ProgressDialog::cancelled, dialog_, [this]() {
|
QObject::connect(dialog_, &ProgressDialog::cancelled, dialog_, [this]() {
|
||||||
cancelled_ = true;
|
cancelled_ = true;
|
||||||
set_cancelled();
|
|
||||||
});
|
});
|
||||||
|
// The engine's C adapter treats show() as a no-op, so the dialog is
|
||||||
|
// shown here at create time.
|
||||||
|
dialog_->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginProgressDialogReporter::~PluginProgressDialogReporter()
|
PluginProgressDialogReporter::~PluginProgressDialogReporter()
|
||||||
@@ -49,18 +51,4 @@ void PluginProgressDialogReporter::set_progress(double value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginProgressDialogReporter::show()
|
|
||||||
{
|
|
||||||
if (dialog_) {
|
|
||||||
dialog_->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PluginProgressDialogReporter::close()
|
|
||||||
{
|
|
||||||
if (dialog_) {
|
|
||||||
dialog_->close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,32 +24,27 @@
|
|||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
|
||||||
#include "pluginSupport/pluginprogressreporter.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
class ProgressDialog;
|
class ProgressDialog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief PluginProgressReporter implementation wrapping a ProgressDialog
|
* @brief ProgressDialog wrapper created through the plugin progress
|
||||||
|
* reporter factory
|
||||||
*
|
*
|
||||||
* UI-side reporter created through the plugin progress reporter factory.
|
* Plain UI-side class; the engine only ever talks to it through the C
|
||||||
* Forwards progress calls to the dialog and the dialog's cancelled() signal
|
* callbacks registered with oakengine_plugin_set_progress_reporter_factory()
|
||||||
* back to the engine. The dialog is deleted on close; the reporter itself
|
* (create/destroy/is_cancelled/set_progress, see engine/src/capi/plugin.cpp).
|
||||||
* is destroyed by the engine with deleteLater().
|
* The dialog is shown on construction and deleted with the reporter.
|
||||||
*/
|
*/
|
||||||
class PluginProgressDialogReporter : public plugin::PluginProgressReporter {
|
class PluginProgressDialogReporter {
|
||||||
public:
|
public:
|
||||||
PluginProgressDialogReporter(const QString &message, const QString &title);
|
PluginProgressDialogReporter(const QString &message, const QString &title);
|
||||||
|
|
||||||
virtual ~PluginProgressDialogReporter() override;
|
~PluginProgressDialogReporter();
|
||||||
|
|
||||||
virtual void set_progress(double value) override;
|
void set_progress(double value);
|
||||||
|
|
||||||
virtual void show() override;
|
|
||||||
|
|
||||||
virtual void close() override;
|
|
||||||
|
|
||||||
bool was_cancelled() const { return cancelled_; }
|
bool was_cancelled() const { return cancelled_; }
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
#include "oakutil/filefunctions.h"
|
#include "oakutil/filefunctions.h"
|
||||||
|
#include "oakutil/qtutils.h"
|
||||||
|
#include "common/projecttypes.h"
|
||||||
#include "oakengine/color.h"
|
#include "oakengine/color.h"
|
||||||
#include "oakengine/disk.h"
|
#include "oakengine/disk.h"
|
||||||
#include "oakengine/project.h"
|
#include "oakengine/project.h"
|
||||||
@@ -40,7 +42,8 @@ namespace olive
|
|||||||
|
|
||||||
#define super QDialog
|
#define super QDialog
|
||||||
|
|
||||||
ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
ProjectPropertiesDialog::ProjectPropertiesDialog(OakEngineProject *p,
|
||||||
|
QWidget *parent)
|
||||||
: super(parent)
|
: super(parent)
|
||||||
, working_project_(p)
|
, working_project_(p)
|
||||||
, ocio_config_is_valid_(true)
|
, ocio_config_is_valid_(true)
|
||||||
@@ -49,7 +52,7 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
|
|
||||||
char name_buf[256];
|
char name_buf[256];
|
||||||
oakengine_project_name(
|
oakengine_project_name(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
working_project_,
|
||||||
name_buf, sizeof(name_buf));
|
name_buf, sizeof(name_buf));
|
||||||
setWindowTitle(
|
setWindowTitle(
|
||||||
tr("Project Properties for '%1'").arg(name_buf));
|
tr("Project Properties for '%1'").arg(name_buf));
|
||||||
@@ -88,15 +91,18 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
color_layout->addWidget(new QLabel(tr("Reference Space:")), row, 0);
|
color_layout->addWidget(new QLabel(tr("Reference Space:")), row, 0);
|
||||||
|
|
||||||
reference_space_ = new QComboBox(this);
|
reference_space_ = new QComboBox(this);
|
||||||
reference_space_->addItem(tr("Scene Linear"), ocio::ROLE_SCENE_LINEAR);
|
// OpenColorIO role names (ocio::ROLE_SCENE_LINEAR /
|
||||||
|
// ocio::ROLE_COMPOSITING_LOG); mirrored as literals because the OCIO
|
||||||
|
// headers only reached this file transitively through the engine.
|
||||||
|
reference_space_->addItem(tr("Scene Linear"),
|
||||||
|
QStringLiteral("scene_linear"));
|
||||||
reference_space_->addItem(tr("Compositing Log"),
|
reference_space_->addItem(tr("Compositing Log"),
|
||||||
ocio::ROLE_COMPOSITING_LOG);
|
QStringLiteral("compositing_log"));
|
||||||
QtUtils::set_combo_box_data(reference_space_,
|
QtUtils::set_combo_box_data(reference_space_,
|
||||||
[p]() -> QString {
|
[p]() -> QString {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
oakengine_project_get_color_reference_space(
|
oakengine_project_get_color_reference_space(
|
||||||
reinterpret_cast<OakEngineProject *>(p),
|
p, buf, sizeof(buf));
|
||||||
buf, sizeof(buf));
|
|
||||||
return QString::fromUtf8(buf);
|
return QString::fromUtf8(buf);
|
||||||
}());
|
}());
|
||||||
color_layout->addWidget(reference_space_, row, 1, 1, 2);
|
color_layout->addWidget(reference_space_, row, 1, 1, 2);
|
||||||
@@ -109,7 +115,7 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
&ProjectPropertiesDialog::browse_for_ocio_config);
|
&ProjectPropertiesDialog::browse_for_ocio_config);
|
||||||
|
|
||||||
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_));
|
working_project_);
|
||||||
ocio_filename_->setText(oak_query_string([cm](char *buf, int size) {
|
ocio_filename_->setText(oak_query_string([cm](char *buf, int size) {
|
||||||
return oakengine_color_manager_get_config_filename(cm, buf, size);
|
return oakengine_color_manager_get_config_filename(cm, buf, size);
|
||||||
}));
|
}));
|
||||||
@@ -149,7 +155,7 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
[this]() -> QString {
|
[this]() -> QString {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
oakengine_project_get_custom_cache_path(
|
oakengine_project_get_custom_cache_path(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
working_project_,
|
||||||
buf, sizeof(buf));
|
buf, sizeof(buf));
|
||||||
return QString::fromUtf8(buf);
|
return QString::fromUtf8(buf);
|
||||||
}(), this);
|
}(), this);
|
||||||
@@ -163,7 +169,7 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
|
|
||||||
// Check the radio button that should currently be active
|
// Check the radio button that should currently be active
|
||||||
disk_cache_radios_[oakengine_project_get_cache_location_setting(
|
disk_cache_radios_[oakengine_project_get_cache_location_setting(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_))]
|
working_project_)]
|
||||||
->setChecked(true);
|
->setChecked(true);
|
||||||
|
|
||||||
// Add disk cache settings button
|
// Add disk cache settings button
|
||||||
@@ -208,7 +214,7 @@ void ProjectPropertiesDialog::accept()
|
|||||||
[this]() -> QString {
|
[this]() -> QString {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
oakengine_project_cache_alongside_path(
|
oakengine_project_cache_alongside_path(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
working_project_,
|
||||||
buf, sizeof(buf));
|
buf, sizeof(buf));
|
||||||
return QString::fromUtf8(buf);
|
return QString::fromUtf8(buf);
|
||||||
}())) {
|
}())) {
|
||||||
@@ -225,7 +231,7 @@ void ProjectPropertiesDialog::accept()
|
|||||||
[this]() -> QString {
|
[this]() -> QString {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
oakengine_project_get_custom_cache_path(
|
oakengine_project_get_custom_cache_path(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
working_project_,
|
||||||
buf, sizeof(buf));
|
buf, sizeof(buf));
|
||||||
return QString::fromUtf8(buf);
|
return QString::fromUtf8(buf);
|
||||||
}()) {
|
}()) {
|
||||||
@@ -235,17 +241,17 @@ void ProjectPropertiesDialog::accept()
|
|||||||
}
|
}
|
||||||
|
|
||||||
oakengine_project_set_custom_cache_path(
|
oakengine_project_set_custom_cache_path(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
working_project_,
|
||||||
custom_cache_path_->text().toUtf8().constData());
|
custom_cache_path_->text().toUtf8().constData());
|
||||||
|
|
||||||
oakengine_disk_invalidate_project(
|
oakengine_disk_invalidate_project(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_));
|
working_project_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should ripple changes throughout the graph/cache that the color config has changed, and
|
// This should ripple changes throughout the graph/cache that the color config has changed, and
|
||||||
// therefore should be done after the cache path is changed
|
// therefore should be done after the cache path is changed
|
||||||
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_));
|
working_project_);
|
||||||
QString old_config = oak_query_string([cm](char *buf, int size) {
|
QString old_config = oak_query_string([cm](char *buf, int size) {
|
||||||
return oakengine_color_manager_get_config_filename(cm, buf, size);
|
return oakengine_color_manager_get_config_filename(cm, buf, size);
|
||||||
});
|
});
|
||||||
@@ -263,12 +269,12 @@ void ProjectPropertiesDialog::accept()
|
|||||||
if ([this]() -> QString {
|
if ([this]() -> QString {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
oakengine_project_get_color_reference_space(
|
oakengine_project_get_color_reference_space(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
working_project_,
|
||||||
buf, sizeof(buf));
|
buf, sizeof(buf));
|
||||||
return QString::fromUtf8(buf);
|
return QString::fromUtf8(buf);
|
||||||
}() != reference_space_->currentData().toString()) {
|
}() != reference_space_->currentData().toString()) {
|
||||||
oakengine_project_set_color_reference_space(
|
oakengine_project_set_color_reference_space(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
working_project_,
|
||||||
reference_space_->currentData().toString().toUtf8().constData());
|
reference_space_->currentData().toString().toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +327,7 @@ void ProjectPropertiesDialog::ocio_filename_updated()
|
|||||||
// List input color spaces
|
// List input color spaces
|
||||||
int cs_count = oakengine_color_config_colorspace_count(config);
|
int cs_count = oakengine_color_config_colorspace_count(config);
|
||||||
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_));
|
working_project_);
|
||||||
QString default_cs = oak_query_string([cm](char *buf, int size) {
|
QString default_cs = oak_query_string([cm](char *buf, int size) {
|
||||||
return oakengine_color_manager_default_input_color_space(cm, buf,
|
return oakengine_color_manager_default_input_color_space(cm, buf,
|
||||||
size);
|
size);
|
||||||
@@ -361,7 +367,7 @@ void ProjectPropertiesDialog::open_disk_cache_settings()
|
|||||||
[this]() -> QString {
|
[this]() -> QString {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
oakengine_project_cache_alongside_path(
|
oakengine_project_cache_alongside_path(
|
||||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
working_project_,
|
||||||
buf, sizeof(buf));
|
buf, sizeof(buf));
|
||||||
return QString::fromUtf8(buf);
|
return QString::fromUtf8(buf);
|
||||||
}().toUtf8().constData(), this);
|
}().toUtf8().constData(), this);
|
||||||
|
|||||||
@@ -29,16 +29,17 @@
|
|||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
|
|
||||||
#include "node/project.h"
|
|
||||||
#include "widget/path/pathwidget.h"
|
#include "widget/path/pathwidget.h"
|
||||||
|
|
||||||
|
struct OakEngineProject;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
class ProjectPropertiesDialog : public QDialog {
|
class ProjectPropertiesDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ProjectPropertiesDialog(Project *p, QWidget *parent);
|
ProjectPropertiesDialog(OakEngineProject *p, QWidget *parent);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void accept() override;
|
virtual void accept() override;
|
||||||
@@ -46,7 +47,7 @@ public slots:
|
|||||||
private:
|
private:
|
||||||
bool verify_path_and_warn_if_bad(const QString &path);
|
bool verify_path_and_warn_if_bad(const QString &path);
|
||||||
|
|
||||||
Project *working_project_;
|
OakEngineProject *working_project_;
|
||||||
|
|
||||||
QLineEdit *ocio_filename_;
|
QLineEdit *ocio_filename_;
|
||||||
|
|
||||||
|
|||||||
@@ -30,13 +30,14 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "common/configwrapper.h"
|
#include "common/configwrapper.h"
|
||||||
#include "node/project.h"
|
|
||||||
#include "oakengine/project.h"
|
#include "oakengine/project.h"
|
||||||
|
#include "oakengine/timeline.h"
|
||||||
|
#include "oakutil/oaknode.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
ProxyDialog::ProxyDialog(QWidget *parent, const QVector<Footage *> &footage)
|
ProxyDialog::ProxyDialog(QWidget *parent, const QVector<OakEngineNode *> &footage)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, footage_(footage)
|
, footage_(footage)
|
||||||
, footage_tree_(nullptr)
|
, footage_tree_(nullptr)
|
||||||
@@ -63,8 +64,10 @@ ProxyDialog::ProxyDialog(QWidget *parent, const QVector<Footage *> &footage)
|
|||||||
custom_params_checkbox_ =
|
custom_params_checkbox_ =
|
||||||
new QCheckBox(tr("Use custom settings for selected footage"));
|
new QCheckBox(tr("Use custom settings for selected footage"));
|
||||||
bool any_custom = false;
|
bool any_custom = false;
|
||||||
for (const Footage *item : footage_) {
|
for (OakEngineNode *item : footage_) {
|
||||||
if (item->has_custom_proxy_params()) {
|
// WRAPPER-GAP: oak::Footage::has_custom_proxy_params
|
||||||
|
oak::Footage f = oak::Footage::borrow(item);
|
||||||
|
if (f && oakengine_footage_has_custom_proxy_params(f.handle())) {
|
||||||
any_custom = true;
|
any_custom = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -186,14 +189,17 @@ void ProxyDialog::accept()
|
|||||||
save_global_settings();
|
save_global_settings();
|
||||||
|
|
||||||
if (!footage_.isEmpty()) {
|
if (!footage_.isEmpty()) {
|
||||||
for (Footage *item : footage_) {
|
for (OakEngineNode *item : footage_) {
|
||||||
|
// WRAPPER-GAP: oak::Footage::{set,clear}_custom_proxy_params
|
||||||
|
oak::Footage f = oak::Footage::borrow(item);
|
||||||
|
if (!f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (custom_params_checkbox_->isChecked()) {
|
if (custom_params_checkbox_->isChecked()) {
|
||||||
oak_proxy_params p = current_params();
|
oak_proxy_params p = current_params();
|
||||||
oakengine_footage_set_custom_proxy_params(
|
oakengine_footage_set_custom_proxy_params(f.handle(), &p);
|
||||||
reinterpret_cast<OakEngineFootage *>(item), &p);
|
|
||||||
} else {
|
} else {
|
||||||
oakengine_footage_clear_custom_proxy_params(
|
oakengine_footage_clear_custom_proxy_params(f.handle());
|
||||||
reinterpret_cast<OakEngineFootage *>(item));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,17 +313,20 @@ void ProxyDialog::refresh_footage_list()
|
|||||||
}
|
}
|
||||||
|
|
||||||
footage_tree_->clear();
|
footage_tree_->clear();
|
||||||
for (const Footage *item : footage_) {
|
for (OakEngineNode *item : footage_) {
|
||||||
|
// WRAPPER-GAP: oak::Footage::{proxy_state,has_custom_proxy_params}
|
||||||
|
oak::Footage f = oak::Footage::borrow(item);
|
||||||
QTreeWidgetItem *tree_item = new QTreeWidgetItem(footage_tree_);
|
QTreeWidgetItem *tree_item = new QTreeWidgetItem(footage_tree_);
|
||||||
tree_item->setText(0, item->filename());
|
tree_item->setText(0, f.filename());
|
||||||
{
|
{
|
||||||
char state_buf[256];
|
char state_buf[256];
|
||||||
int state_len = oakengine_proxy_state_to_string(
|
int state_len = oakengine_proxy_state_to_string(
|
||||||
item->proxy_state(), state_buf, sizeof(state_buf));
|
oakengine_footage_proxy_get_state(f.handle()), state_buf,
|
||||||
|
sizeof(state_buf));
|
||||||
QString state = (state_len > 0)
|
QString state = (state_len > 0)
|
||||||
? QString::fromUtf8(state_buf, state_len)
|
? QString::fromUtf8(state_buf, state_len)
|
||||||
: QString();
|
: QString();
|
||||||
if (item->has_custom_proxy_params()) {
|
if (oakengine_footage_has_custom_proxy_params(f.handle())) {
|
||||||
state = tr("%1 (custom settings)").arg(state);
|
state = tr("%1 (custom settings)").arg(state);
|
||||||
}
|
}
|
||||||
tree_item->setText(1, state);
|
tree_item->setText(1, state);
|
||||||
@@ -327,15 +336,30 @@ void ProxyDialog::refresh_footage_list()
|
|||||||
|
|
||||||
void ProxyDialog::generate_proxies()
|
void ProxyDialog::generate_proxies()
|
||||||
{
|
{
|
||||||
for (Footage *item : footage_) {
|
for (OakEngineNode *item : footage_) {
|
||||||
const VideoParams video = item->get_first_enabled_video_stream();
|
// WRAPPER-GAP: oak::Footage::{get_effective_proxy_params,set_proxy}
|
||||||
|
oak::Footage f = oak::Footage::borrow(item);
|
||||||
oak_video_params _vp;
|
oak_video_params _vp;
|
||||||
oakengine_viewer_get_first_enabled_video_stream(
|
oakengine_viewer_get_first_enabled_video_stream(item, &_vp);
|
||||||
reinterpret_cast<OakEngineNode *>(item), &_vp);
|
|
||||||
if (!oakengine_video_params_is_valid(&_vp)) {
|
if (!oakengine_video_params_is_valid(&_vp)) {
|
||||||
qWarning()
|
qWarning()
|
||||||
<< "ProxyDialog::GenerateProxies: skipping item with no valid video stream"
|
<< "ProxyDialog::GenerateProxies: skipping item with no valid video stream"
|
||||||
<< item->filename();
|
<< f.filename();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index of the first enabled video stream
|
||||||
|
// (VideoParams::stream_index() has no facade equivalent).
|
||||||
|
int stream_index = -1;
|
||||||
|
const QVector<QPair<int, int>> streams =
|
||||||
|
oak::Node(item).enabled_streams();
|
||||||
|
for (const QPair<int, int> &s : streams) {
|
||||||
|
if (s.first == OAKENGINE_TRACK_TYPE_VIDEO) {
|
||||||
|
stream_index = s.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stream_index < 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,27 +367,23 @@ void ProxyDialog::generate_proxies()
|
|||||||
if (custom_params_checkbox_->isChecked()) {
|
if (custom_params_checkbox_->isChecked()) {
|
||||||
params = current_params();
|
params = current_params();
|
||||||
} else {
|
} else {
|
||||||
oakengine_footage_get_effective_proxy_params(
|
oakengine_footage_get_effective_proxy_params(f.handle(), ¶ms);
|
||||||
reinterpret_cast<OakEngineFootage *>(item), ¶ms);
|
|
||||||
}
|
}
|
||||||
oak_proxy_result proxy;
|
oak_proxy_result proxy;
|
||||||
char cache_buf[512];
|
char cache_buf[512];
|
||||||
oakengine_project_cache_path(
|
oakengine_project_cache_path(oakengine_node_get_project(item),
|
||||||
reinterpret_cast<OakEngineProject *>(item->project()),
|
|
||||||
cache_buf, sizeof(cache_buf));
|
cache_buf, sizeof(cache_buf));
|
||||||
int ret = oakengine_proxy_get_or_start(
|
int ret = oakengine_proxy_get_or_start(
|
||||||
cache_buf,
|
cache_buf, f.filename().toUtf8().constData(), stream_index,
|
||||||
item->filename().toUtf8().constData(),
|
¶ms, &proxy);
|
||||||
video.stream_index(), ¶ms, &proxy);
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
qWarning() << "ProxyDialog::GenerateProxies: failed to get/start proxy for"
|
qWarning() << "ProxyDialog::GenerateProxies: failed to get/start proxy for"
|
||||||
<< item->filename();
|
<< f.filename();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
oakengine_footage_set_proxy(reinterpret_cast<OakEngineFootage *>(item),
|
oakengine_footage_set_proxy(f.handle(), proxy.filename, proxy.state,
|
||||||
proxy.filename, proxy.state,
|
stream_index, 1, params.version);
|
||||||
video.stream_index(), 1, params.version);
|
f.invalidate();
|
||||||
oakengine_footage_invalidate(reinterpret_cast<OakEngineFootage *>(item));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh_footage_list();
|
refresh_footage_list();
|
||||||
@@ -371,22 +391,24 @@ void ProxyDialog::generate_proxies()
|
|||||||
|
|
||||||
void ProxyDialog::delete_proxies()
|
void ProxyDialog::delete_proxies()
|
||||||
{
|
{
|
||||||
for (Footage *item : footage_) {
|
for (OakEngineNode *item : footage_) {
|
||||||
if (item->proxy_path().isEmpty()) {
|
// WRAPPER-GAP: oak::Footage::clear_proxy
|
||||||
|
oak::Footage f = oak::Footage::borrow(item);
|
||||||
|
if (f.proxy_path().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile::remove(item->proxy_path());
|
QFile::remove(f.proxy_path());
|
||||||
{
|
{
|
||||||
char wbuf[4096];
|
char wbuf[4096];
|
||||||
int wlen = oakengine_proxy_get_working_filename(
|
int wlen = oakengine_proxy_get_working_filename(
|
||||||
item->proxy_path().toUtf8().constData(), wbuf, sizeof(wbuf));
|
f.proxy_path().toUtf8().constData(), wbuf, sizeof(wbuf));
|
||||||
if (wlen > 0) {
|
if (wlen > 0) {
|
||||||
QFile::remove(QString::fromUtf8(wbuf, wlen));
|
QFile::remove(QString::fromUtf8(wbuf, wlen));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
oakengine_footage_clear_proxy(reinterpret_cast<OakEngineFootage *>(item));
|
oakengine_footage_clear_proxy(f.handle());
|
||||||
oakengine_footage_invalidate(reinterpret_cast<OakEngineFootage *>(item));
|
f.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh_footage_list();
|
refresh_footage_list();
|
||||||
|
|||||||
@@ -29,16 +29,17 @@
|
|||||||
#include "oakengine/proxy.h"
|
#include "oakengine/proxy.h"
|
||||||
#include "oakengine/videoparams.h"
|
#include "oakengine/videoparams.h"
|
||||||
#include "oakengine/viewer.h"
|
#include "oakengine/viewer.h"
|
||||||
#include "node/project/footage/footage.h"
|
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
|
|
||||||
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
class ProxyDialog : public QDialog {
|
class ProxyDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ProxyDialog(QWidget *parent, const QVector<Footage *> &footage = {});
|
ProxyDialog(QWidget *parent, const QVector<OakEngineNode *> &footage = {});
|
||||||
|
|
||||||
virtual void accept() override;
|
virtual void accept() override;
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ private:
|
|||||||
|
|
||||||
void refresh_footage_list();
|
void refresh_footage_list();
|
||||||
|
|
||||||
QVector<Footage *> footage_;
|
QVector<OakEngineNode *> footage_;
|
||||||
|
|
||||||
QTreeWidget *footage_tree_;
|
QTreeWidget *footage_tree_;
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,12 @@
|
|||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
#include "oakengine/timeline.h"
|
#include "oakengine/timeline.h"
|
||||||
#include "oakengine/videoparams.h"
|
#include "oakengine/videoparams.h"
|
||||||
|
#include "oakutil/oaknode.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
SequenceDialog::SequenceDialog(Sequence *s, Type t, QWidget *parent)
|
SequenceDialog::SequenceDialog(OakEngineNode *s, Type t, QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, sequence_(s)
|
, sequence_(s)
|
||||||
, make_undoable_(true)
|
, make_undoable_(true)
|
||||||
@@ -89,11 +90,12 @@ SequenceDialog::SequenceDialog(Sequence *s, Type t, QWidget *parent)
|
|||||||
setWindowTitle(tr("New Sequence"));
|
setWindowTitle(tr("New Sequence"));
|
||||||
break;
|
break;
|
||||||
case k_existing:
|
case k_existing:
|
||||||
setWindowTitle(tr("Editing \"%1\"").arg(sequence_->get_label()));
|
setWindowTitle(
|
||||||
|
tr("Editing \"%1\"").arg(oak::Node(sequence_).get_label()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
name_field_->setText(sequence_->get_label());
|
name_field_->setText(oak::Node(sequence_).get_label());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SequenceDialog::set_undoable(bool u)
|
void SequenceDialog::set_undoable(bool u)
|
||||||
|
|||||||
@@ -26,10 +26,11 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
|
|
||||||
#include "node/project/sequence/sequence.h"
|
|
||||||
#include "sequencedialogparametertab.h"
|
#include "sequencedialogparametertab.h"
|
||||||
#include "sequencedialogpresettab.h"
|
#include "sequencedialogpresettab.h"
|
||||||
|
|
||||||
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ public:
|
|||||||
* @param parent
|
* @param parent
|
||||||
* QWidget parent
|
* QWidget parent
|
||||||
*/
|
*/
|
||||||
SequenceDialog(Sequence *s, Type t = k_existing, QWidget *parent = nullptr);
|
SequenceDialog(OakEngineNode *s, Type t = k_existing, QWidget *parent = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set whether the parameter changes should be made into an undo command or not
|
* @brief Set whether the parameter changes should be made into an undo command or not
|
||||||
@@ -90,7 +91,7 @@ public slots:
|
|||||||
virtual void accept() override;
|
virtual void accept() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Sequence *sequence_;
|
OakEngineNode *sequence_;
|
||||||
|
|
||||||
SequenceDialogPresetTab *preset_tab_;
|
SequenceDialogPresetTab *preset_tab_;
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence *sequence,
|
SequenceDialogParameterTab::SequenceDialogParameterTab(OakEngineNode *sequence,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,18 +24,20 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
|
|
||||||
#include "node/project/sequence/sequence.h"
|
|
||||||
#include "sequencepreset.h"
|
#include "sequencepreset.h"
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
#include "widget/standardcombos/standardcombos.h"
|
#include "widget/standardcombos/standardcombos.h"
|
||||||
|
|
||||||
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
class SequenceDialogParameterTab : public QWidget {
|
class SequenceDialogParameterTab : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
SequenceDialogParameterTab(Sequence *sequence, QWidget *parent = nullptr);
|
SequenceDialogParameterTab(OakEngineNode *sequence,
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
int get_selected_video_width() const
|
int get_selected_video_width() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,11 +27,14 @@
|
|||||||
|
|
||||||
#include "oakutil/xmlutils.h"
|
#include "oakutil/xmlutils.h"
|
||||||
#include "dialog/sequence/presetmanager.h"
|
#include "dialog/sequence/presetmanager.h"
|
||||||
#include "render/videoparams.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Same namespace bridge the engine's render/videoparams.h used to provide
|
||||||
|
// (unqualified Rational/PixelFormat inside namespace olive).
|
||||||
|
using namespace core;
|
||||||
|
|
||||||
class SequencePreset : public Preset {
|
class SequencePreset : public Preset {
|
||||||
public:
|
public:
|
||||||
SequencePreset() = default;
|
SequencePreset() = default;
|
||||||
|
|||||||
@@ -27,22 +27,63 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "node/block/clip/clip.h"
|
#include "oakengine/preview.h"
|
||||||
#include "oakengine/timeline.h"
|
#include "oakengine/timeline.h"
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
#include "oakengine/undo.h"
|
#include "oakengine/undo.h"
|
||||||
|
#include "timeline/timelinecommonapp.h"
|
||||||
#include "widget/timelinewidget/cliphandle.h"
|
#include "widget/timelinewidget/cliphandle.h"
|
||||||
#include "timeline/timelineundopointer.h"
|
|
||||||
#include "timeline/timelinecommon.h"
|
|
||||||
#include "timeline/timelineundopointer.h"
|
|
||||||
#include "timeline/timelineundoripple.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
#define super QDialog
|
#define super QDialog
|
||||||
|
|
||||||
SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Block::length() as rational seconds (C ABI facade).
|
||||||
|
inline Rational sdd_block_length(const OakEngineBlock *block)
|
||||||
|
{
|
||||||
|
int num = 0, den = 1;
|
||||||
|
oakengine_block_get_length_rational(
|
||||||
|
reinterpret_cast<const OakEngineNode *>(block), &num, &den);
|
||||||
|
return Rational(num, den);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block::in() as rational seconds.
|
||||||
|
inline Rational sdd_block_in(const OakEngineBlock *block)
|
||||||
|
{
|
||||||
|
int num = 0, den = 1;
|
||||||
|
oakengine_block_get_in_rational(
|
||||||
|
reinterpret_cast<const OakEngineNode *>(block), &num, &den);
|
||||||
|
return Rational(num, den);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block::out() as rational seconds.
|
||||||
|
inline Rational sdd_block_out(const OakEngineBlock *block)
|
||||||
|
{
|
||||||
|
int num = 0, den = 1;
|
||||||
|
oakengine_block_get_out_rational(
|
||||||
|
reinterpret_cast<const OakEngineNode *>(block), &num, &den);
|
||||||
|
return Rational(num, den);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block::next() as a block handle.
|
||||||
|
inline OakEngineBlock *sdd_block_next(OakEngineBlock *block)
|
||||||
|
{
|
||||||
|
return oakengine_block_next(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ClipBlock::track() as an opaque track handle.
|
||||||
|
inline OakEngineTrack *sdd_clip_track(OakEngineBlock *clip)
|
||||||
|
{
|
||||||
|
return oakengine_block_get_track(clip);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SpeedDurationDialog::SpeedDurationDialog(const QVector<OakEngineBlock *> &clips,
|
||||||
const Rational &timebase,
|
const Rational &timebase,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: super(parent)
|
: super(parent)
|
||||||
@@ -113,9 +154,9 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
|||||||
loop_layout->addWidget(new QLabel(tr("Loop:")), row, 0);
|
loop_layout->addWidget(new QLabel(tr("Loop:")), row, 0);
|
||||||
|
|
||||||
loop_combo_ = new QComboBox();
|
loop_combo_ = new QComboBox();
|
||||||
loop_combo_->addItem(tr("None"), int(LoopMode::k_loop_mode_off));
|
loop_combo_->addItem(tr("None"), OAKENGINE_LOOP_MODE_OFF);
|
||||||
loop_combo_->addItem(tr("Loop"), int(LoopMode::k_loop_mode_loop));
|
loop_combo_->addItem(tr("Loop"), OAKENGINE_LOOP_MODE_LOOP);
|
||||||
loop_combo_->addItem(tr("Clamp"), int(LoopMode::k_loop_mode_clamp));
|
loop_combo_->addItem(tr("Clamp"), OAKENGINE_LOOP_MODE_CLAMP);
|
||||||
loop_layout->addWidget(loop_combo_, row, 1);
|
loop_layout->addWidget(loop_combo_, row, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,19 +171,19 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
|||||||
|
|
||||||
// Determine which speed value to use
|
// Determine which speed value to use
|
||||||
start_speed_ = clip_speed(clips.first());
|
start_speed_ = clip_speed(clips.first());
|
||||||
start_duration_ = clips.first()->length();
|
start_duration_ = sdd_block_length(clips.first());
|
||||||
start_reverse_ = clip_is_reversed(clips.first());
|
start_reverse_ = clip_is_reversed(clips.first());
|
||||||
start_maintain_audio_pitch_ = clip_maintain_audio_pitch(clips.first());
|
start_maintain_audio_pitch_ = clip_maintain_audio_pitch(clips.first());
|
||||||
start_loop_ = clip_loop_mode(clips.first());
|
start_loop_ = clip_loop_mode(clips.first());
|
||||||
for (int i = 1; i < clips.size(); i++) {
|
for (int i = 1; i < clips.size(); i++) {
|
||||||
ClipBlock *c = clips.at(i);
|
OakEngineBlock *c = clips.at(i);
|
||||||
|
|
||||||
if (!qIsNaN(start_speed_) && !qFuzzyCompare(start_speed_, clip_speed(c))) {
|
if (!qIsNaN(start_speed_) && !qFuzzyCompare(start_speed_, clip_speed(c))) {
|
||||||
// Speed differs per clip
|
// Speed differs per clip
|
||||||
start_speed_ = qSNaN();
|
start_speed_ = qSNaN();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start_duration_ != -1 && c->length() != start_duration_) {
|
if (start_duration_ != -1 && sdd_block_length(c) != start_duration_) {
|
||||||
start_duration_ = -1;
|
start_duration_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,8 +245,9 @@ void SpeedDurationDialog::accept()
|
|||||||
if (speed_slider_->is_tristate()) {
|
if (speed_slider_->is_tristate()) {
|
||||||
if (link_box_->isChecked() && !dur_slider_->is_tristate()) {
|
if (link_box_->isChecked() && !dur_slider_->is_tristate()) {
|
||||||
// Automatically determine speed from duration
|
// Automatically determine speed from duration
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (OakEngineBlock *c, clips_) {
|
||||||
double speed = get_speed_adjustment(clip_speed(c), c->length(),
|
double speed = get_speed_adjustment(clip_speed(c),
|
||||||
|
sdd_block_length(c),
|
||||||
dur_slider_->get_value());
|
dur_slider_->get_value());
|
||||||
oak_node_value val;
|
oak_node_value val;
|
||||||
memset(&val, 0, sizeof(val));
|
memset(&val, 0, sizeof(val));
|
||||||
@@ -218,7 +260,7 @@ void SpeedDurationDialog::accept()
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Set speeds to value of slider
|
// Set speeds to value of slider
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (OakEngineBlock *c, clips_) {
|
||||||
oak_node_value val;
|
oak_node_value val;
|
||||||
memset(&val, 0, sizeof(val));
|
memset(&val, 0, sizeof(val));
|
||||||
val.type = OAK_NODE_VALUE_FLOAT;
|
val.type = OAK_NODE_VALUE_FLOAT;
|
||||||
@@ -230,14 +272,18 @@ void SpeedDurationDialog::accept()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set duration values (undoable via facade)
|
// Set duration values (undoable via facade)
|
||||||
TimelineRippleDeleteGapsAtRegionsCommand::RangeList ripple_ranges;
|
// (track handle, range) pairs fed to
|
||||||
|
// oakengine_timeline_ripple_delete_gaps_command below; was
|
||||||
|
// TimelineRippleDeleteGapsAtRegionsCommand::RangeList.
|
||||||
|
QVector<QPair<void *, TimeRange>> ripple_ranges;
|
||||||
|
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (OakEngineBlock *c, clips_) {
|
||||||
Rational proposed_length = c->length();
|
Rational proposed_length = sdd_block_length(c);
|
||||||
|
|
||||||
if (dur_slider_->is_tristate()) {
|
if (dur_slider_->is_tristate()) {
|
||||||
if (link_box_->isChecked() && !speed_slider_->is_tristate()) {
|
if (link_box_->isChecked() && !speed_slider_->is_tristate()) {
|
||||||
proposed_length = get_length_adjustment(c->length(), clip_speed(c),
|
proposed_length = get_length_adjustment(sdd_block_length(c),
|
||||||
|
clip_speed(c),
|
||||||
speed_slider_->get_value(),
|
speed_slider_->get_value(),
|
||||||
timebase_);
|
timebase_);
|
||||||
}
|
}
|
||||||
@@ -245,40 +291,44 @@ void SpeedDurationDialog::accept()
|
|||||||
proposed_length = dur_slider_->get_value();
|
proposed_length = dur_slider_->get_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proposed_length != c->length()) {
|
if (proposed_length != sdd_block_length(c)) {
|
||||||
// Clip length should ideally change, but check if there's "room" to do so
|
// Clip length should ideally change, but check if there's "room" to do so
|
||||||
if (proposed_length > c->length() && c->next()) {
|
if (proposed_length > sdd_block_length(c) && sdd_block_next(c)) {
|
||||||
if (GapBlock *gap = dynamic_cast<GapBlock *>(c->next())) {
|
// Gap check via the C ABI (GapBlock's type used to ride in
|
||||||
|
// with the removed timelineundo headers)
|
||||||
|
if (oakengine_block_is_gap(sdd_block_next(c))) {
|
||||||
proposed_length =
|
proposed_length =
|
||||||
qMin(proposed_length, gap->out() - c->in());
|
qMin(proposed_length,
|
||||||
|
sdd_block_out(sdd_block_next(c)) -
|
||||||
|
sdd_block_in(c));
|
||||||
} else {
|
} else {
|
||||||
proposed_length = c->length();
|
proposed_length = sdd_block_length(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proposed_length != c->length()) {
|
if (proposed_length != sdd_block_length(c)) {
|
||||||
// Trim the clip's out-point to the new length (one undoable child
|
// Trim the clip's out-point to the new length (one undoable child
|
||||||
// inside the group, kept as a direct C++ command because the dialog
|
// inside the group, kept as a direct C++ command because the dialog
|
||||||
// already works in Rational time and has the track available).
|
// already works in Rational time and has the track available).
|
||||||
oakengine_undo_push(
|
oakengine_undo_push(
|
||||||
oakengine_block_trim_command(
|
oakengine_block_trim_command(
|
||||||
reinterpret_cast<void *>(c->track()),
|
reinterpret_cast<void *>(sdd_clip_track(c)),
|
||||||
reinterpret_cast<void *>(c),
|
reinterpret_cast<void *>(c),
|
||||||
proposed_length.numerator(),
|
proposed_length.numerator(),
|
||||||
proposed_length.denominator(),
|
proposed_length.denominator(),
|
||||||
olive::Timeline::k_trim_out, 0),
|
TimelineApp::k_trim_out, 0),
|
||||||
tr("Trim Clip").toUtf8().constData());
|
tr("Trim Clip").toUtf8().constData());
|
||||||
ripple_ranges.append(
|
ripple_ranges.append(
|
||||||
{ c->track(),
|
{ reinterpret_cast<void *>(sdd_clip_track(c)),
|
||||||
TimeRange(c->in() + proposed_length, c->out()) });
|
TimeRange(sdd_block_in(c) + proposed_length,
|
||||||
|
sdd_block_out(c)) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ripple_box_->isChecked() && !ripple_ranges.isEmpty()) {
|
if (ripple_box_->isChecked() && !ripple_ranges.isEmpty()) {
|
||||||
Sequence *seq = reinterpret_cast<Sequence *>(
|
OakEngineSequence *seq = oakengine_clip_get_sequence(
|
||||||
oakengine_clip_get_sequence(
|
reinterpret_cast<OakEngineClip *>(clips_.first()));
|
||||||
reinterpret_cast<OakEngineClip *>(clips_.first())));
|
|
||||||
if (seq) {
|
if (seq) {
|
||||||
QVector<int64_t> range_in_ts;
|
QVector<int64_t> range_in_ts;
|
||||||
QVector<int64_t> range_out_ts;
|
QVector<int64_t> range_out_ts;
|
||||||
@@ -292,8 +342,10 @@ void SpeedDurationDialog::accept()
|
|||||||
oakengine_node_frame_time_base(
|
oakengine_node_frame_time_base(
|
||||||
reinterpret_cast<OakEngineNode *>(seq), &tbn, &tbd);
|
reinterpret_cast<OakEngineNode *>(seq), &tbn, &tbd);
|
||||||
for (const auto &range : ripple_ranges) {
|
for (const auto &range : ripple_ranges) {
|
||||||
range_track_types.append(range.first->type());
|
range_track_types.append(oakengine_track_get_type(
|
||||||
range_track_indexes.append(range.first->index());
|
reinterpret_cast<const OakEngineNode *>(range.first)));
|
||||||
|
range_track_indexes.append(oakengine_track_get_index(
|
||||||
|
reinterpret_cast<const OakEngineNode *>(range.first)));
|
||||||
range_in_ts.append(olive::core::Timecode::time_to_timestamp(
|
range_in_ts.append(olive::core::Timecode::time_to_timestamp(
|
||||||
range.second.in(), olive::Rational(tbn, tbd),
|
range.second.in(), olive::Rational(tbn, tbd),
|
||||||
olive::core::Timecode::k_round));
|
olive::core::Timecode::k_round));
|
||||||
@@ -314,7 +366,7 @@ void SpeedDurationDialog::accept()
|
|||||||
|
|
||||||
// Set reverse values
|
// Set reverse values
|
||||||
if (!reverse_box_->isTristate()) {
|
if (!reverse_box_->isTristate()) {
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (OakEngineBlock *c, clips_) {
|
||||||
oak_node_value val;
|
oak_node_value val;
|
||||||
memset(&val, 0, sizeof(val));
|
memset(&val, 0, sizeof(val));
|
||||||
val.type = OAK_NODE_VALUE_BOOL;
|
val.type = OAK_NODE_VALUE_BOOL;
|
||||||
@@ -327,7 +379,7 @@ void SpeedDurationDialog::accept()
|
|||||||
|
|
||||||
// Set maintain audio pitch values
|
// Set maintain audio pitch values
|
||||||
if (!maintain_audio_pitch_box_->isTristate()) {
|
if (!maintain_audio_pitch_box_->isTristate()) {
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (OakEngineBlock *c, clips_) {
|
||||||
oak_node_value val;
|
oak_node_value val;
|
||||||
memset(&val, 0, sizeof(val));
|
memset(&val, 0, sizeof(val));
|
||||||
val.type = OAK_NODE_VALUE_BOOL;
|
val.type = OAK_NODE_VALUE_BOOL;
|
||||||
@@ -339,7 +391,7 @@ void SpeedDurationDialog::accept()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loop_combo_->currentIndex() != -1) {
|
if (loop_combo_->currentIndex() != -1) {
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (OakEngineBlock *c, clips_) {
|
||||||
oak_node_value val;
|
oak_node_value val;
|
||||||
memset(&val, 0, sizeof(val));
|
memset(&val, 0, sizeof(val));
|
||||||
val.type = OAK_NODE_VALUE_INT;
|
val.type = OAK_NODE_VALUE_INT;
|
||||||
|
|||||||
@@ -26,18 +26,16 @@
|
|||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
#include "node/block/gap/gap.h"
|
#include "oakengine/timeline.h"
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
#include "widget/slider/rationalslider.h"
|
#include "widget/slider/rationalslider.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
class ClipBlock;
|
|
||||||
|
|
||||||
class SpeedDurationDialog : public QDialog {
|
class SpeedDurationDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
explicit SpeedDurationDialog(const QVector<OakEngineBlock *> &clips,
|
||||||
const Rational &timebase,
|
const Rational &timebase,
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
@@ -55,7 +53,7 @@ private:
|
|||||||
const Rational &original_length,
|
const Rational &original_length,
|
||||||
const Rational &new_length);
|
const Rational &new_length);
|
||||||
|
|
||||||
QVector<ClipBlock *> clips_;
|
QVector<OakEngineBlock *> clips_;
|
||||||
|
|
||||||
FloatSlider *speed_slider_;
|
FloatSlider *speed_slider_;
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "oakengine/plugin.h"
|
#include "oakengine/plugin.h"
|
||||||
#include "pluginSupport/olivehost.h"
|
|
||||||
|
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ pkgdesc="Oak - Non-linear video editor"
|
|||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
url="https://oakvideoeditor.org"
|
url="https://oakvideoeditor.org"
|
||||||
license=('GPL3')
|
license=('GPL3')
|
||||||
depends=('qt6-base' 'qt6-tools' 'ffmpeg' 'openimageio' 'opencolorio' 'openexr' 'expat' 'portaudio' 'mesa' 'vulkan-icd-loader' 'libxkbcommon' 'fmt')
|
depends=('qt6-base' 'qt6-tools' 'ffmpeg' 'openimageio' 'opencolorio' 'openexr' 'expat' 'portaudio' 'mesa' 'vulkan-icd-loader' 'libxkbcommon' 'fmt' 'opentimelineio')
|
||||||
makedepends=('cmake' 'ninja' 'git' 'pkgconf' 'vulkan-headers')
|
makedepends=('cmake' 'ninja' 'git' 'pkgconf' 'vulkan-headers')
|
||||||
source=("${pkgname}-${pkgver}.tar.gz")
|
source=("${pkgname}-${pkgver}.tar.gz")
|
||||||
md5sums=('SKIP')
|
md5sums=('SKIP')
|
||||||
@@ -16,6 +16,7 @@ build() {
|
|||||||
cmake -S . -B build -G Ninja \
|
cmake -S . -B build -G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DOAK_BUNDLE_OTIO=OFF \
|
||||||
-DBUILD_QT6=ON
|
-DBUILD_QT6=ON
|
||||||
cmake --build build
|
cmake --build build
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,9 +49,15 @@ void CurvePanel::deselect_all()
|
|||||||
static_cast<CurveWidget *>(get_time_based_widget())->deselect_all();
|
static_cast<CurveWidget *>(get_time_based_widget())->deselect_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurvePanel::set_nodes(const QVector<Node *> &nodes)
|
void CurvePanel::set_nodes(const QVector<OakEngineNode *> &nodes)
|
||||||
{
|
{
|
||||||
static_cast<CurveWidget *>(get_time_based_widget())->set_nodes(nodes);
|
// Convert to oak::Node for the curve widget (C ABI wrapper layer)
|
||||||
|
QVector<oak::Node> oak_nodes;
|
||||||
|
oak_nodes.reserve(nodes.size());
|
||||||
|
foreach (OakEngineNode *n, nodes) {
|
||||||
|
oak_nodes.append(oak::Node(n));
|
||||||
|
}
|
||||||
|
static_cast<CurveWidget *>(get_time_based_widget())->set_nodes(oak_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurvePanel::increase_track_height()
|
void CurvePanel::increase_track_height()
|
||||||
|
|||||||
@@ -43,20 +43,17 @@ public slots:
|
|||||||
void set_node(OakEngineNode *node)
|
void set_node(OakEngineNode *node)
|
||||||
{
|
{
|
||||||
// Convert single pointer to either an empty vector or a vector of one
|
// Convert single pointer to either an empty vector or a vector of one
|
||||||
QVector<Node *> nodes;
|
QVector<OakEngineNode *> nodes;
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
nodes.append(reinterpret_cast<Node *>(node));
|
nodes.append(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_nodes(nodes);
|
set_nodes(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Not a slot: signature uses the engine C++ type Node*, which must not be
|
void set_nodes(const QVector<OakEngineNode *> &nodes);
|
||||||
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
|
||||||
// boundary). All connections use new-style member-function syntax.
|
|
||||||
void set_nodes(const QVector<Node *> &nodes);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void increase_track_height() override;
|
virtual void increase_track_height() override;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public:
|
|||||||
return node_widget_;
|
return node_widget_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVector<Node *> &get_contexts() const
|
const QVector<oak::Node> &get_contexts() const
|
||||||
{
|
{
|
||||||
return node_widget_->view()->get_contexts();
|
return node_widget_->view()->get_contexts();
|
||||||
}
|
}
|
||||||
@@ -53,12 +53,12 @@ public:
|
|||||||
return node_widget_->view()->is_group_overlay();
|
return node_widget_->view()->is_group_overlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_contexts(const QVector<Node *> &nodes)
|
void set_contexts(const QVector<oak::Node> &nodes)
|
||||||
{
|
{
|
||||||
node_widget_->set_contexts(nodes);
|
node_widget_->set_contexts(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_contexts_belonging_to_project(Project *project)
|
void close_contexts_belonging_to_project(oak::Project project)
|
||||||
{
|
{
|
||||||
node_widget_->view()->close_contexts_belonging_to_project(project);
|
node_widget_->view()->close_contexts_belonging_to_project(project);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ void ParamPanel::deselect_all()
|
|||||||
static_cast<NodeParamView *>(get_time_based_widget())->deselect_all();
|
static_cast<NodeParamView *>(get_time_based_widget())->deselect_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParamPanel::set_contexts(const QVector<Node *> &contexts)
|
void ParamPanel::set_contexts(const QVector<oak::Node> &contexts)
|
||||||
{
|
{
|
||||||
static_cast<NodeParamView *>(get_time_based_widget())->set_contexts(contexts);
|
static_cast<NodeParamView *>(get_time_based_widget())->set_contexts(contexts);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ public:
|
|||||||
return static_cast<NodeParamView *>(get_time_based_widget());
|
return static_cast<NodeParamView *>(get_time_based_widget());
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVector<Node *> &get_contexts() const
|
const QVector<oak::Node> &get_contexts() const
|
||||||
{
|
{
|
||||||
return get_param_view()->get_contexts();
|
return get_param_view()->get_contexts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_contexts_belonging_to_project(Project *p)
|
void close_contexts_belonging_to_project(oak::Project p)
|
||||||
{
|
{
|
||||||
get_param_view()->close_contexts_belonging_to_project(p);
|
get_param_view()->close_contexts_belonging_to_project(p);
|
||||||
}
|
}
|
||||||
@@ -63,10 +63,9 @@ public slots:
|
|||||||
virtual void deselect_all() override;
|
virtual void deselect_all() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Not a slot: signature uses the engine C++ type Node*, which must not be
|
// Not a slot: mirrors NodePanel::set_contexts(), which is also a plain
|
||||||
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
// member function. All connections use new-style member-function syntax.
|
||||||
// boundary). All connections use new-style member-function syntax.
|
void set_contexts(const QVector<oak::Node> &contexts);
|
||||||
void set_contexts(const QVector<Node *> &contexts);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void focused_node_changed(OakEngineNode *n);
|
void focused_node_changed(OakEngineNode *n);
|
||||||
|
|||||||
@@ -24,8 +24,6 @@
|
|||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
|
||||||
#include "node/project/footage/footage.h"
|
|
||||||
|
|
||||||
struct OakEngineNode;
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "oakengine/events.h"
|
#include "oakengine/events.h"
|
||||||
#include "oakengine/project.h"
|
#include "oakengine/project.h"
|
||||||
#include "node/project/sequence/sequence.h"
|
|
||||||
#include "panel/footageviewer/footageviewer.h"
|
#include "panel/footageviewer/footageviewer.h"
|
||||||
#include "panel/timeline/timeline.h"
|
#include "panel/timeline/timeline.h"
|
||||||
#include "panel/panelmanager.h"
|
#include "panel/panelmanager.h"
|
||||||
@@ -89,12 +88,12 @@ ProjectPanel::ProjectPanel(const QString &unique_name)
|
|||||||
retranslate();
|
retranslate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Project *ProjectPanel::project() const
|
oak::Project ProjectPanel::project() const
|
||||||
{
|
{
|
||||||
return explorer_->project();
|
return explorer_->project();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectPanel::set_project(Project *p)
|
void ProjectPanel::set_project(oak::Project p)
|
||||||
{
|
{
|
||||||
if (project()) {
|
if (project()) {
|
||||||
if (project_name_sub_ > 0) {
|
if (project_name_sub_ > 0) {
|
||||||
@@ -106,9 +105,8 @@ void ProjectPanel::set_project(Project *p)
|
|||||||
explorer_->set_project(p);
|
explorer_->set_project(p);
|
||||||
|
|
||||||
if (project()) {
|
if (project()) {
|
||||||
auto *ph = reinterpret_cast<OakEngineProject *>(project());
|
|
||||||
project_name_sub_ = oakengine_event_subscribe(
|
project_name_sub_ = oakengine_event_subscribe(
|
||||||
ph, OAKENGINE_EVENT_PROJECT_NAME_CHANGED,
|
project().handle(), OAKENGINE_EVENT_PROJECT_NAME_CHANGED,
|
||||||
[](const oakengine_event *, void *userdata) {
|
[](const oakengine_event *, void *userdata) {
|
||||||
auto *s = static_cast<ProjectPanel *>(userdata);
|
auto *s = static_cast<ProjectPanel *>(userdata);
|
||||||
s->update_subtitle();
|
s->update_subtitle();
|
||||||
@@ -122,24 +120,24 @@ void ProjectPanel::set_project(Project *p)
|
|||||||
emit project_name_changed();
|
emit project_name_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
Folder *ProjectPanel::get_root() const
|
oak::Node ProjectPanel::get_root() const
|
||||||
{
|
{
|
||||||
return explorer_->get_root();
|
return explorer_->get_root();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectPanel::set_root(Folder *item)
|
void ProjectPanel::set_root(oak::Node item)
|
||||||
{
|
{
|
||||||
explorer_->set_root(item);
|
explorer_->set_root(item);
|
||||||
|
|
||||||
retranslate();
|
retranslate();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<Node *> ProjectPanel::selected_items() const
|
QVector<oak::Node> ProjectPanel::selected_items() const
|
||||||
{
|
{
|
||||||
return explorer_->selected_items();
|
return explorer_->selected_items();
|
||||||
}
|
}
|
||||||
|
|
||||||
Folder *ProjectPanel::get_selected_folder() const
|
oak::Node ProjectPanel::get_selected_folder() const
|
||||||
{
|
{
|
||||||
return explorer_->get_selected_folder();
|
return explorer_->get_selected_folder();
|
||||||
}
|
}
|
||||||
@@ -176,7 +174,7 @@ void ProjectPanel::edit(OakEngineNode *item)
|
|||||||
|
|
||||||
void ProjectPanel::retranslate()
|
void ProjectPanel::retranslate()
|
||||||
{
|
{
|
||||||
if (project() && explorer_->get_root() != project()->root()) {
|
if (project() && explorer_->get_root() != project().root()) {
|
||||||
set_title(tr("Folder"));
|
set_title(tr("Folder"));
|
||||||
} else {
|
} else {
|
||||||
set_title(tr("Project"));
|
set_title(tr("Project"));
|
||||||
@@ -187,21 +185,20 @@ void ProjectPanel::retranslate()
|
|||||||
|
|
||||||
void ProjectPanel::item_double_click_slot(OakEngineNode *item_handle)
|
void ProjectPanel::item_double_click_slot(OakEngineNode *item_handle)
|
||||||
{
|
{
|
||||||
Node *item = reinterpret_cast<Node *>(item_handle);
|
oak::Node item(item_handle);
|
||||||
if (item == nullptr) {
|
if (item == nullptr) {
|
||||||
// If the user double clicks on empty space, show the import dialog
|
// If the user double clicks on empty space, show the import dialog
|
||||||
Core::instance()->dialog_import_show();
|
Core::instance()->dialog_import_show();
|
||||||
} else if (dynamic_cast<Footage *>(item)) {
|
} else if (item.is_footage()) {
|
||||||
// Open this footage in a FootageViewer
|
// Open this footage in a FootageViewer
|
||||||
auto panel =
|
auto panel =
|
||||||
PanelManager::instance()->most_recently_focused<FootageViewerPanel>();
|
PanelManager::instance()->most_recently_focused<FootageViewerPanel>();
|
||||||
panel->connect_viewer_node(static_cast<Footage *>(item));
|
panel->connect_viewer_node(item.handle());
|
||||||
panel->raise();
|
panel->raise();
|
||||||
panel->setFocus(Qt::FocusReason::MouseFocusReason);
|
panel->setFocus(Qt::FocusReason::MouseFocusReason);
|
||||||
} else if (dynamic_cast<Sequence *>(item)) {
|
} else if (item.is_sequence()) {
|
||||||
// Open this sequence in the Timeline
|
// Open this sequence in the Timeline
|
||||||
Core::instance()->main_window()->open_sequence(
|
Core::instance()->main_window()->open_sequence(item.handle());
|
||||||
static_cast<Sequence *>(item));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,23 +214,19 @@ void ProjectPanel::show_new_menu()
|
|||||||
void ProjectPanel::update_subtitle()
|
void ProjectPanel::update_subtitle()
|
||||||
{
|
{
|
||||||
if (project()) {
|
if (project()) {
|
||||||
char name_buf[256];
|
QString project_title = project().name();
|
||||||
oakengine_project_name(
|
|
||||||
reinterpret_cast<OakEngineProject *>(project()),
|
|
||||||
name_buf, sizeof(name_buf));
|
|
||||||
QString project_title = QString::fromUtf8(name_buf);
|
|
||||||
|
|
||||||
if (explorer_->get_root() != project()->root()) {
|
if (explorer_->get_root() != project().root()) {
|
||||||
QString folder_path;
|
QString folder_path;
|
||||||
|
|
||||||
Folder *item = explorer_->get_root();
|
oak::Node item = explorer_->get_root();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
folder_path.prepend(
|
folder_path.prepend(
|
||||||
QStringLiteral("/%1").arg(item->get_label()));
|
QStringLiteral("/%1").arg(item.get_label()));
|
||||||
|
|
||||||
item = item->folder();
|
item = item.folder();
|
||||||
} while (item != project()->root());
|
} while (item != project().root());
|
||||||
|
|
||||||
project_title.append(folder_path);
|
project_title.append(folder_path);
|
||||||
}
|
}
|
||||||
@@ -251,12 +244,12 @@ void ProjectPanel::save_connected_project()
|
|||||||
|
|
||||||
QVector<OakEngineNode *> ProjectPanel::get_selected_footage() const
|
QVector<OakEngineNode *> ProjectPanel::get_selected_footage() const
|
||||||
{
|
{
|
||||||
QVector<Node *> items = selected_items();
|
QVector<oak::Node> items = selected_items();
|
||||||
QVector<OakEngineNode *> footage;
|
QVector<OakEngineNode *> footage;
|
||||||
|
|
||||||
foreach (Node *i, items) {
|
foreach (const oak::Node &i, items) {
|
||||||
if (dynamic_cast<ViewerOutput *>(i)) {
|
if (i.is_viewer_output()) {
|
||||||
footage.append(reinterpret_cast<OakEngineNode *>(i));
|
footage.append(i.handle());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#define OAK_PROJECT_PANEL_H
|
#define OAK_PROJECT_PANEL_H
|
||||||
|
|
||||||
#include "footagemanagementpanel.h"
|
#include "footagemanagementpanel.h"
|
||||||
#include "node/project.h"
|
|
||||||
#include "panel/panel.h"
|
#include "panel/panel.h"
|
||||||
#include "widget/projectexplorer/projectexplorer.h"
|
#include "widget/projectexplorer/projectexplorer.h"
|
||||||
|
|
||||||
@@ -41,22 +40,22 @@ public:
|
|||||||
ProjectPanel(const QString &unique_name);
|
ProjectPanel(const QString &unique_name);
|
||||||
~ProjectPanel() override;
|
~ProjectPanel() override;
|
||||||
|
|
||||||
Project *project() const;
|
oak::Project project() const;
|
||||||
void set_project(Project *p);
|
void set_project(oak::Project p);
|
||||||
|
|
||||||
Folder *get_root() const;
|
oak::Node get_root() const;
|
||||||
|
|
||||||
void set_root(Folder *item);
|
void set_root(oak::Node item);
|
||||||
|
|
||||||
QVector<Node *> selected_items() const;
|
QVector<oak::Node> selected_items() const;
|
||||||
|
|
||||||
Folder *get_selected_folder() const;
|
oak::Node get_selected_folder() const;
|
||||||
|
|
||||||
virtual QVector<OakEngineNode *> get_selected_footage() const override;
|
virtual QVector<OakEngineNode *> get_selected_footage() const override;
|
||||||
|
|
||||||
ProjectViewModel *model() const;
|
ProjectViewModel *model() const;
|
||||||
|
|
||||||
bool select_item(Node *n, bool deselect_all_first = true)
|
bool select_item(oak::Node n, bool deselect_all_first = true)
|
||||||
{
|
{
|
||||||
return explorer_->select_item(n, deselect_all_first);
|
return explorer_->select_item(n, deselect_all_first);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ SequenceViewerPanel::SequenceViewerPanel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SequenceViewerPanel::start_capture(const TimeRange &time,
|
void SequenceViewerPanel::start_capture(const TimeRange &time,
|
||||||
const Track::Reference &track)
|
const TrackReference &track)
|
||||||
{
|
{
|
||||||
TimelinePanel *tp = static_cast<TimelinePanel *>(sender());
|
TimelinePanel *tp = static_cast<TimelinePanel *>(sender());
|
||||||
static_cast<ViewerWidget *>(get_time_based_widget())
|
static_cast<ViewerWidget *>(get_time_based_widget())
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#ifndef OAK_SEQUENCEVIEWERPANEL_H
|
#ifndef OAK_SEQUENCEVIEWERPANEL_H
|
||||||
#define OAK_SEQUENCEVIEWERPANEL_H
|
#define OAK_SEQUENCEVIEWERPANEL_H
|
||||||
|
|
||||||
|
#include "common/trackreferencehandle.h"
|
||||||
#include "panel/viewer/viewer.h"
|
#include "panel/viewer/viewer.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -33,7 +34,7 @@ public:
|
|||||||
SequenceViewerPanel();
|
SequenceViewerPanel();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void start_capture(const TimeRange &time, const Track::Reference &track);
|
void start_capture(const TimeRange &time, const TrackReference &track);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void retranslate() override;
|
virtual void retranslate() override;
|
||||||
|
|||||||
@@ -34,15 +34,12 @@ public:
|
|||||||
NodeTablePanel();
|
NodeTablePanel();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Not slots: signatures use the engine C++ type Node*, which must not be
|
void select_nodes(const QVector<OakEngineNode *> &nodes)
|
||||||
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
|
||||||
// boundary). All connections use new-style member-function syntax.
|
|
||||||
void select_nodes(const QVector<Node *> &nodes)
|
|
||||||
{
|
{
|
||||||
static_cast<NodeTableWidget *>(get_time_based_widget())->select_nodes(nodes);
|
static_cast<NodeTableWidget *>(get_time_based_widget())->select_nodes(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deselect_nodes(const QVector<Node *> &nodes)
|
void deselect_nodes(const QVector<OakEngineNode *> &nodes)
|
||||||
{
|
{
|
||||||
static_cast<NodeTableWidget *>(get_time_based_widget())
|
static_cast<NodeTableWidget *>(get_time_based_widget())
|
||||||
->deselect_nodes(nodes);
|
->deselect_nodes(nodes);
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include "timebased.h"
|
#include "timebased.h"
|
||||||
|
|
||||||
|
#include "oakutil/oaknode.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -116,7 +118,7 @@ void TimeBasedPanel::shuttle_right()
|
|||||||
emit shuttle_right_requested();
|
emit shuttle_right_requested();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeBasedPanel::connect_viewer_node(ViewerOutput *node)
|
void TimeBasedPanel::connect_viewer_node(OakEngineNode *node)
|
||||||
{
|
{
|
||||||
widget_->connect_viewer_node(node);
|
widget_->connect_viewer_node(node);
|
||||||
}
|
}
|
||||||
@@ -140,8 +142,8 @@ void TimeBasedPanel::set_time_based_widget(TimeBasedWidget *widget)
|
|||||||
|
|
||||||
void TimeBasedPanel::retranslate()
|
void TimeBasedPanel::retranslate()
|
||||||
{
|
{
|
||||||
if (get_time_based_widget()->get_connected_node()) {
|
if (get_connected_viewer()) {
|
||||||
set_subtitle(get_time_based_widget()->get_connected_node()->get_label());
|
set_subtitle(oak::Node(get_connected_viewer()).get_label());
|
||||||
} else {
|
} else {
|
||||||
set_subtitle(tr("(none)"));
|
set_subtitle(tr("(none)"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public:
|
|||||||
|
|
||||||
virtual ~TimeBasedPanel() override;
|
virtual ~TimeBasedPanel() override;
|
||||||
|
|
||||||
void connect_viewer_node(ViewerOutput *node);
|
void connect_viewer_node(OakEngineNode *node);
|
||||||
|
|
||||||
void disconnect_viewer_node()
|
void disconnect_viewer_node()
|
||||||
{
|
{
|
||||||
@@ -46,7 +46,7 @@ public:
|
|||||||
// Get the timebase of this panels widget
|
// Get the timebase of this panels widget
|
||||||
const Rational &timebase();
|
const Rational &timebase();
|
||||||
|
|
||||||
ViewerOutput *get_connected_viewer() const
|
OakEngineNode *get_connected_viewer() const
|
||||||
{
|
{
|
||||||
return widget_->get_connected_node();
|
return widget_->get_connected_node();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#ifndef OAK_TIMELINE_PANEL_H
|
#ifndef OAK_TIMELINE_PANEL_H
|
||||||
#define OAK_TIMELINE_PANEL_H
|
#define OAK_TIMELINE_PANEL_H
|
||||||
|
|
||||||
|
#include "common/trackreferencehandle.h"
|
||||||
#include "panel/timebased/timebased.h"
|
#include "panel/timebased/timebased.h"
|
||||||
#include "widget/timelinewidget/timelinewidget.h"
|
#include "widget/timelinewidget/timelinewidget.h"
|
||||||
|
|
||||||
@@ -107,14 +108,29 @@ public:
|
|||||||
|
|
||||||
void overwrite_footage_at_playhead(const QVector<OakEngineNode *> &footage);
|
void overwrite_footage_at_playhead(const QVector<OakEngineNode *> &footage);
|
||||||
|
|
||||||
const QVector<Block *> &get_selected_blocks() const
|
QVector<OakEngineBlock *> get_selected_blocks() const
|
||||||
{
|
{
|
||||||
return timeline_widget()->get_selected_blocks();
|
// TimelineWidget still exposes engine Block* (its own wave converts
|
||||||
|
// it); re-wrap into the handle family here so callers never see the
|
||||||
|
// engine type.
|
||||||
|
const auto &selected = timeline_widget()->get_selected_blocks();
|
||||||
|
QVector<OakEngineBlock *> handles;
|
||||||
|
handles.reserve(selected.size());
|
||||||
|
for (auto *b : selected) {
|
||||||
|
handles.append(reinterpret_cast<OakEngineBlock *>(b));
|
||||||
|
}
|
||||||
|
return handles;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sequence *get_sequence() const
|
OakEngineSequence *get_sequence() const
|
||||||
{
|
{
|
||||||
return dynamic_cast<Sequence *>(get_connected_viewer());
|
// R8: type check via the C ABI facade predicate (replaces
|
||||||
|
// dynamic_cast<Sequence*>, which needed the complete engine type);
|
||||||
|
// the handle is shared, so the cast is a reinterpret.
|
||||||
|
OakEngineNode *connected = get_connected_viewer();
|
||||||
|
return (connected && oakengine_node_is_sequence(connected))
|
||||||
|
? reinterpret_cast<OakEngineSequence *>(connected)
|
||||||
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -124,7 +140,7 @@ signals:
|
|||||||
void block_selection_changed(const QVector<OakEngineBlock *> &selected_blocks);
|
void block_selection_changed(const QVector<OakEngineBlock *> &selected_blocks);
|
||||||
|
|
||||||
void request_capture_start(const TimeRange &time,
|
void request_capture_start(const TimeRange &time,
|
||||||
const Track::Reference &track);
|
const TrackReference &track);
|
||||||
|
|
||||||
void reveal_viewer_in_project(OakEngineNode *r);
|
void reveal_viewer_in_project(OakEngineNode *r);
|
||||||
void reveal_viewer_in_footage_viewer(OakEngineNode *r, const TimeRange &range);
|
void reveal_viewer_in_footage_viewer(OakEngineNode *r, const TimeRange &range);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ void ViewerPanelBase::set_full_screen(QScreen *screen)
|
|||||||
|
|
||||||
void ViewerPanelBase::set_gizmos(OakEngineNode *node)
|
void ViewerPanelBase::set_gizmos(OakEngineNode *node)
|
||||||
{
|
{
|
||||||
get_viewer_widget()->set_gizmos(reinterpret_cast<Node *>(node));
|
get_viewer_widget()->set_gizmos(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerPanelBase::cache_entire_sequence()
|
void ViewerPanelBase::cache_entire_sequence()
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public:
|
|||||||
get_viewer_widget()->add_playback_device(vw);
|
get_viewer_widget()->add_playback_device(vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_timeline_selected_blocks(const QVector<Block *> &b)
|
void set_timeline_selected_blocks(const QVector<OakEngineBlock *> &b)
|
||||||
{
|
{
|
||||||
get_viewer_widget()->set_timeline_selected_blocks(b);
|
get_viewer_widget()->set_timeline_selected_blocks(b);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
|
timeline/timelinecommonapp.h
|
||||||
timeline/timelinecoordinate.h
|
timeline/timelinecoordinate.h
|
||||||
timeline/timelinecoordinate.cpp
|
timeline/timelinecoordinate.cpp
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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_TIMELINECOMMONAPP_H
|
||||||
|
#define OAK_TIMELINECOMMONAPP_H
|
||||||
|
|
||||||
|
#include <olive/core/core.h>
|
||||||
|
|
||||||
|
typedef struct OakEngineTrack OakEngineTrack;
|
||||||
|
typedef struct OakEngineBlock OakEngineBlock;
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
using olive::core::Rational;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief App-side mirror of engine's olive::Timeline
|
||||||
|
* (engine/timeline/timelinecommon.h).
|
||||||
|
*
|
||||||
|
* Pure enums + a POD struct, identical to the engine version. It is named
|
||||||
|
* TimelineApp (not Timeline) because the engine class still reaches some
|
||||||
|
* app translation units transitively (e.g. via engine/node/block/block.h)
|
||||||
|
* and an identical name would be an ODR redefinition there.
|
||||||
|
*
|
||||||
|
* Enumerator ordinals must stay in sync with the engine enums: the C ABI
|
||||||
|
* transports these values as ints (OAKENGINE_MOVEMENT_MODE_* in
|
||||||
|
* oakengine/timeline.h, the `movement_mode` parameter of
|
||||||
|
* oakengine_block_trim_command(), etc.).
|
||||||
|
*/
|
||||||
|
class TimelineApp {
|
||||||
|
public:
|
||||||
|
enum MovementMode { k_none, k_move, k_trim_in, k_trim_out };
|
||||||
|
|
||||||
|
enum ThumbnailMode { k_thumbnail_off, k_thumbnail_in_out, k_thumbnail_on };
|
||||||
|
|
||||||
|
enum WaveformMode { k_waveforms_disabled, k_waveforms_enabled };
|
||||||
|
|
||||||
|
static bool is_a_trim_mode(MovementMode mode)
|
||||||
|
{
|
||||||
|
return mode == k_trim_in || mode == k_trim_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EditToInfo {
|
||||||
|
OakEngineTrack *track;
|
||||||
|
Rational nearest_time;
|
||||||
|
OakEngineBlock *nearest_block;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OAK_TIMELINECOMMONAPP_H
|
||||||
@@ -25,19 +25,19 @@ namespace olive
|
|||||||
{
|
{
|
||||||
|
|
||||||
TimelineCoordinate::TimelineCoordinate()
|
TimelineCoordinate::TimelineCoordinate()
|
||||||
: track_(Track::k_none, 0)
|
: track_(TrackReference::k_none, 0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineCoordinate::TimelineCoordinate(const Rational &frame,
|
TimelineCoordinate::TimelineCoordinate(const Rational &frame,
|
||||||
const Track::Reference &track)
|
const TrackReference &track)
|
||||||
: frame_(frame)
|
: frame_(frame)
|
||||||
, track_(track)
|
, track_(track)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineCoordinate::TimelineCoordinate(const Rational &frame,
|
TimelineCoordinate::TimelineCoordinate(const Rational &frame,
|
||||||
const Track::Type &track_type,
|
const TrackReference::Type &track_type,
|
||||||
const int &track_index)
|
const int &track_index)
|
||||||
: frame_(frame)
|
: frame_(frame)
|
||||||
, track_(track_type, track_index)
|
, track_(track_type, track_index)
|
||||||
@@ -49,7 +49,7 @@ const Rational &TimelineCoordinate::get_frame() const
|
|||||||
return frame_;
|
return frame_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Track::Reference &TimelineCoordinate::get_track() const
|
const TrackReference &TimelineCoordinate::get_track() const
|
||||||
{
|
{
|
||||||
return track_;
|
return track_;
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ void TimelineCoordinate::set_frame(const Rational &frame)
|
|||||||
frame_ = frame;
|
frame_ = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimelineCoordinate::set_track(const Track::Reference &track)
|
void TimelineCoordinate::set_track(const TrackReference &track)
|
||||||
{
|
{
|
||||||
track_ = track;
|
track_ = track;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,28 +22,32 @@
|
|||||||
#ifndef OAK_TIMELINECOORDINATE_H
|
#ifndef OAK_TIMELINECOORDINATE_H
|
||||||
#define OAK_TIMELINECOORDINATE_H
|
#define OAK_TIMELINECOORDINATE_H
|
||||||
|
|
||||||
#include "node/output/track/track.h"
|
#include <olive/core/util/rational.h>
|
||||||
|
|
||||||
|
#include "common/trackreferencehandle.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using namespace core;
|
||||||
|
|
||||||
class TimelineCoordinate {
|
class TimelineCoordinate {
|
||||||
public:
|
public:
|
||||||
TimelineCoordinate();
|
TimelineCoordinate();
|
||||||
TimelineCoordinate(const Rational &frame, const Track::Reference &track);
|
TimelineCoordinate(const Rational &frame, const TrackReference &track);
|
||||||
TimelineCoordinate(const Rational &frame, const Track::Type &track_type,
|
TimelineCoordinate(const Rational &frame, const TrackReference::Type &track_type,
|
||||||
const int &track_index);
|
const int &track_index);
|
||||||
|
|
||||||
const Rational &get_frame() const;
|
const Rational &get_frame() const;
|
||||||
const Track::Reference &get_track() const;
|
const TrackReference &get_track() const;
|
||||||
|
|
||||||
void set_frame(const Rational &frame);
|
void set_frame(const Rational &frame);
|
||||||
void set_track(const Track::Reference &track);
|
void set_track(const TrackReference &track);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Rational frame_;
|
Rational frame_;
|
||||||
|
|
||||||
Track::Reference track_;
|
TrackReference track_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,9 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
#include "audio/audiolevelmeter.h"
|
|
||||||
#include "audio/audiomanager.h"
|
|
||||||
#include "oakutil/decibel.h"
|
#include "oakutil/decibel.h"
|
||||||
#include "oakengine/preview.h"
|
#include "oakengine/preview.h"
|
||||||
|
#include "oakengine/viewer.h"
|
||||||
#include "oakutil/qtutils.h"
|
#include "oakutil/qtutils.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -39,6 +38,10 @@ const int k_decibel_minimum =
|
|||||||
-198; // Must be divisible by kDecibelStep for infinity to appear
|
-198; // Must be divisible by kDecibelStep for infinity to appear
|
||||||
const int k_maximum_smoothness = 8;
|
const int k_maximum_smoothness = 8;
|
||||||
|
|
||||||
|
/// Channel capacity of the summary buffers passed to
|
||||||
|
/// oakengine_waveform_cache_get_summary()
|
||||||
|
const int k_max_summary_channels = 64;
|
||||||
|
|
||||||
QVector<AudioMonitor *> AudioMonitor::instances;
|
QVector<AudioMonitor *> AudioMonitor::instances;
|
||||||
|
|
||||||
AudioMonitor::AudioMonitor(QWidget *parent)
|
AudioMonitor::AudioMonitor(QWidget *parent)
|
||||||
@@ -118,9 +121,15 @@ void AudioMonitor::start_waveform(const void *waveform,
|
|||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
const AudioWaveformCache *cache =
|
// The waveform cache C ABI reports the length in sample frames at the
|
||||||
static_cast<const AudioWaveformCache *>(waveform);
|
// cache's own sample rate; convert to seconds (AudioWaveformCache::length())
|
||||||
waveform_length_ = cache->length();
|
const int sample_rate = oakengine_waveform_cache_sample_rate(waveform);
|
||||||
|
if (sample_rate <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
waveform_length_ =
|
||||||
|
Rational(static_cast<int>(oakengine_waveform_cache_length(waveform)),
|
||||||
|
sample_rate);
|
||||||
if (start >= waveform_length_) {
|
if (start >= waveform_length_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -455,21 +464,39 @@ void AudioMonitor::update_values_from_waveform(QVector<double> &v,
|
|||||||
// Delta time is provided in milliseconds, so we convert to seconds in Rational
|
// Delta time is provided in milliseconds, so we convert to seconds in Rational
|
||||||
Rational length(delta_time, 1000);
|
Rational length(delta_time, 1000);
|
||||||
|
|
||||||
const AudioWaveformCache *cache =
|
const int sample_rate = oakengine_waveform_cache_sample_rate(waveform_);
|
||||||
static_cast<const AudioWaveformCache *>(waveform_);
|
if (sample_rate <= 0) {
|
||||||
AudioVisualWaveform::Sample sum =
|
return;
|
||||||
cache->get_summary_from_time(waveform_time_, length);
|
}
|
||||||
|
|
||||||
audio_visual_waveform_sample_to_internal_values(sum, v);
|
// The summary C ABI works in sample frames at the cache's sample rate
|
||||||
|
// (AudioWaveformCache::get_summary_from_time() equivalent)
|
||||||
|
const Rational sample_tb(1, sample_rate);
|
||||||
|
const int64_t start_ts = core::Timecode::time_to_timestamp(
|
||||||
|
waveform_time_, sample_tb, core::Timecode::k_round);
|
||||||
|
const int64_t end_ts = core::Timecode::time_to_timestamp(
|
||||||
|
waveform_time_ + length, sample_tb, core::Timecode::k_round);
|
||||||
|
|
||||||
|
double min_vals[k_max_summary_channels];
|
||||||
|
double max_vals[k_max_summary_channels];
|
||||||
|
int channels = 0;
|
||||||
|
if (oakengine_waveform_cache_get_summary(waveform_, start_ts, end_ts,
|
||||||
|
min_vals, max_vals,
|
||||||
|
k_max_summary_channels,
|
||||||
|
&channels) == OAKENGINE_OK) {
|
||||||
|
audio_visual_waveform_sample_to_internal_values(min_vals, max_vals,
|
||||||
|
channels, v);
|
||||||
|
}
|
||||||
|
|
||||||
waveform_time_ += length;
|
waveform_time_ += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMonitor::audio_visual_waveform_sample_to_internal_values(
|
void AudioMonitor::audio_visual_waveform_sample_to_internal_values(
|
||||||
const AudioVisualWaveform::Sample &in, QVector<double> &out)
|
const double *min_vals, const double *max_vals, int channels,
|
||||||
|
QVector<double> &out)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < in.size(); i++) {
|
for (int i = 0; i < channels; i++) {
|
||||||
float max = qMax(qAbs(in.at(i).min), qAbs(in.at(i).max));
|
double max = qMax(qAbs(min_vals[i]), qAbs(max_vals[i]));
|
||||||
|
|
||||||
int output_index = i % out.size();
|
int output_index = i % out.size();
|
||||||
if (max > out.at(output_index)) {
|
if (max > out.at(output_index)) {
|
||||||
|
|||||||
@@ -26,13 +26,17 @@
|
|||||||
#include <QOpenGLWidget>
|
#include <QOpenGLWidget>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "audio/audiovisualwaveform.h"
|
#include <olive/core/core.h>
|
||||||
|
|
||||||
#include "oakutil/define.h"
|
#include "oakutil/define.h"
|
||||||
#include "render/audiowaveformcache.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Same namespace bridge the engine audio headers used to provide
|
||||||
|
// (unqualified Rational/AudioParams/SampleBuffer inside namespace olive).
|
||||||
|
using namespace core;
|
||||||
|
|
||||||
class AudioMonitor : public QOpenGLWidget {
|
class AudioMonitor : public QOpenGLWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
@@ -87,8 +91,13 @@ private:
|
|||||||
|
|
||||||
void update_values_from_waveform(QVector<double> &v, qint64 delta_time);
|
void update_values_from_waveform(QVector<double> &v, qint64 delta_time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fold a per-channel min/max summary (POD arrays from
|
||||||
|
* oakengine_waveform_cache_get_summary()) into the internal values.
|
||||||
|
*/
|
||||||
void audio_visual_waveform_sample_to_internal_values(
|
void audio_visual_waveform_sample_to_internal_values(
|
||||||
const AudioVisualWaveform::Sample &in, QVector<double> &out);
|
const double *min_vals, const double *max_vals, int channels,
|
||||||
|
QVector<double> &out);
|
||||||
|
|
||||||
void push_value(const QVector<double> &v);
|
void push_value(const QVector<double> &v);
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ void ColorButton::set_color(const ManagedColor &c)
|
|||||||
color_.set_color_input(compliant_in);
|
color_.set_color_input(compliant_in);
|
||||||
|
|
||||||
QByteArray out_name = color_.color_output().output().toUtf8();
|
QByteArray out_name = color_.color_output().output().toUtf8();
|
||||||
ColorTransform cs_out = color_.color_output();
|
oak::ColorTransform cs_out = color_.color_output();
|
||||||
QByteArray o, v, l;
|
QByteArray o, v, l;
|
||||||
oak_color_transform pod = oak_to_transform(cs_out, &o, &v, &l);
|
oak_color_transform pod = oak_to_transform(cs_out, &o, &v, &l);
|
||||||
int out_is_display = 0;
|
int out_is_display = 0;
|
||||||
@@ -70,11 +70,11 @@ void ColorButton::set_color(const ManagedColor &c)
|
|||||||
color_manager_, &pod, 0, &out_is_display, out_buf, sizeof(out_buf),
|
color_manager_, &pod, 0, &out_is_display, out_buf, sizeof(out_buf),
|
||||||
view_buf, sizeof(view_buf), look_buf, sizeof(look_buf));
|
view_buf, sizeof(view_buf), look_buf, sizeof(look_buf));
|
||||||
if (out_is_display) {
|
if (out_is_display) {
|
||||||
color_.set_color_output(ColorTransform(QString::fromUtf8(out_buf),
|
color_.set_color_output(oak::ColorTransform(QString::fromUtf8(out_buf),
|
||||||
QString::fromUtf8(view_buf),
|
QString::fromUtf8(view_buf),
|
||||||
QString::fromUtf8(look_buf)));
|
QString::fromUtf8(look_buf)));
|
||||||
} else {
|
} else {
|
||||||
color_.set_color_output(ColorTransform(QString::fromUtf8(out_buf)));
|
color_.set_color_output(oak::ColorTransform(QString::fromUtf8(out_buf)));
|
||||||
}
|
}
|
||||||
|
|
||||||
update_color();
|
update_color();
|
||||||
@@ -113,7 +113,7 @@ void ColorButton::color_dialog_finished(int e)
|
|||||||
void ColorButton::update_color()
|
void ColorButton::update_color()
|
||||||
{
|
{
|
||||||
QByteArray in_cs = color_.color_input().toUtf8();
|
QByteArray in_cs = color_.color_input().toUtf8();
|
||||||
ColorTransform out = color_.color_output();
|
oak::ColorTransform out = color_.color_output();
|
||||||
QByteArray o, v, l;
|
QByteArray o, v, l;
|
||||||
oak_color_transform out_pod = oak_to_transform(out, &o, &v, &l);
|
oak_color_transform out_pod = oak_to_transform(out, &o, &v, &l);
|
||||||
color_processor_ = ColorProcessorHandlePtr(
|
color_processor_ = ColorProcessorHandlePtr(
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include "colorcodingcombobox.h"
|
#include "colorcodingcombobox.h"
|
||||||
|
|
||||||
#include "ui/colorcoding.h"
|
#include "common/colorcodingapp.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -48,7 +48,7 @@ void ColorCodingComboBox::showPopup()
|
|||||||
void ColorCodingComboBox::set_color(int index)
|
void ColorCodingComboBox::set_color(int index)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
addItem(ColorCoding::get_color_name(index));
|
addItem(AppColorCoding::get_color_name(index));
|
||||||
index_ = index;
|
index_ = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <QWidgetAction>
|
#include <QWidgetAction>
|
||||||
|
|
||||||
#include "oakutil/qtutils.h"
|
#include "oakutil/qtutils.h"
|
||||||
#include "ui/colorcoding.h"
|
#include "common/colorcodingapp.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -37,14 +37,14 @@ ColorLabelMenu::ColorLabelMenu(QWidget *parent)
|
|||||||
// Used for size calculations
|
// Used for size calculations
|
||||||
int box_size = fontMetrics().height();
|
int box_size = fontMetrics().height();
|
||||||
|
|
||||||
color_items_.resize(ColorCoding::standard_colors().size());
|
color_items_.resize(AppColorCoding::standard_colors().size());
|
||||||
for (int i = 0; i < ColorCoding::standard_colors().size(); i++) {
|
for (int i = 0; i < AppColorCoding::standard_colors().size(); i++) {
|
||||||
QPixmap p(box_size, box_size);
|
QPixmap p(box_size, box_size);
|
||||||
|
|
||||||
QPainter painter(&p);
|
QPainter painter(&p);
|
||||||
painter.setPen(Qt::black);
|
painter.setPen(Qt::black);
|
||||||
painter.setBrush(
|
painter.setBrush(
|
||||||
QtUtils::to_q_color(ColorCoding::standard_colors().at(i)));
|
QtUtils::to_q_color(AppColorCoding::standard_colors().at(i)));
|
||||||
painter.drawRect(p.rect().adjusted(0, 0, -1, -1));
|
painter.drawRect(p.rect().adjusted(0, 0, -1, -1));
|
||||||
|
|
||||||
QAction *a = add_item(QStringLiteral("colorlabel%1").arg(i), this,
|
QAction *a = add_item(QStringLiteral("colorlabel%1").arg(i), this,
|
||||||
@@ -71,7 +71,7 @@ void ColorLabelMenu::retranslate()
|
|||||||
this->setTitle(tr("Color"));
|
this->setTitle(tr("Color"));
|
||||||
|
|
||||||
for (int i = 0; i < color_items_.size(); i++) {
|
for (int i = 0; i < color_items_.size(); i++) {
|
||||||
color_items_.at(i)->setText(ColorCoding::get_color_name(i));
|
color_items_.at(i)->setText(AppColorCoding::get_color_name(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
#include "oakutil/lerp.h"
|
#include "oakutil/lerp.h"
|
||||||
#include "node/node.h"
|
#include "oakutil/qtutils.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -173,9 +173,9 @@ QString ColorSpaceChooser::input() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorTransform ColorSpaceChooser::output() const
|
oak::ColorTransform ColorSpaceChooser::output() const
|
||||||
{
|
{
|
||||||
return ColorTransform(display_combobox_->currentText(),
|
return oak::ColorTransform(display_combobox_->currentText(),
|
||||||
view_combobox_->currentText(),
|
view_combobox_->currentText(),
|
||||||
look_combobox_->currentIndex() == 0 ?
|
look_combobox_->currentIndex() == 0 ?
|
||||||
QString() :
|
QString() :
|
||||||
@@ -192,7 +192,7 @@ void ColorSpaceChooser::set_input(const QString &s)
|
|||||||
input_combobox_->setCurrentText(compliant);
|
input_combobox_->setCurrentText(compliant);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorSpaceChooser::set_output(const ColorTransform &out)
|
void ColorSpaceChooser::set_output(const oak::ColorTransform &out)
|
||||||
{
|
{
|
||||||
QByteArray o, v, l;
|
QByteArray o, v, l;
|
||||||
oak_color_transform pod = oak_to_transform(out, &o, &v, &l);
|
oak_color_transform pod = oak_to_transform(out, &o, &v, &l);
|
||||||
|
|||||||
@@ -40,17 +40,17 @@ public:
|
|||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
QString input() const;
|
QString input() const;
|
||||||
ColorTransform output() const;
|
oak::ColorTransform output() const;
|
||||||
|
|
||||||
void set_input(const QString &s);
|
void set_input(const QString &s);
|
||||||
void set_output(const ColorTransform &out);
|
void set_output(const oak::ColorTransform &out);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void input_color_space_changed(const QString &input);
|
void input_color_space_changed(const QString &input);
|
||||||
|
|
||||||
void output_color_space_changed(const ColorTransform &out);
|
void output_color_space_changed(const oak::ColorTransform &out);
|
||||||
|
|
||||||
void color_space_changed(const QString &input, const ColorTransform &out);
|
void color_space_changed(const QString &input, const oak::ColorTransform &out);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void update_views(const QString &display);
|
void update_views(const QString &display);
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ void ColorSwatchChooser::load_swatches()
|
|||||||
Color::DataType r;
|
Color::DataType r;
|
||||||
QString s;
|
QString s;
|
||||||
ManagedColor c;
|
ManagedColor c;
|
||||||
ColorTransform t;
|
oak::ColorTransform t;
|
||||||
bool is_display;
|
bool is_display;
|
||||||
|
|
||||||
c.set_alpha(1.0);
|
c.set_alpha(1.0);
|
||||||
@@ -167,10 +167,10 @@ void ColorSwatchChooser::load_swatches()
|
|||||||
d >> display;
|
d >> display;
|
||||||
d >> view;
|
d >> view;
|
||||||
d >> look;
|
d >> look;
|
||||||
c.set_color_output(ColorTransform(display, view, look));
|
c.set_color_output(oak::ColorTransform(display, view, look));
|
||||||
} else {
|
} else {
|
||||||
d >> s;
|
d >> s;
|
||||||
c.set_color_output(ColorTransform(s));
|
c.set_color_output(oak::ColorTransform(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons_[index]->set_color(c);
|
buttons_[index]->set_color(c);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
|
||||||
#include "ui/colorcoding.h"
|
#include "common/colorcodingapp.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -80,7 +80,7 @@ void ColorSwatchWidget::SelectedColorChangedEvent(const Color &, bool)
|
|||||||
|
|
||||||
Qt::GlobalColor ColorSwatchWidget::get_ui_selector_color() const
|
Qt::GlobalColor ColorSwatchWidget::get_ui_selector_color() const
|
||||||
{
|
{
|
||||||
return ColorCoding::get_ui_selector_color(get_selected_color());
|
return AppColorCoding::get_ui_selector_color(get_selected_color());
|
||||||
}
|
}
|
||||||
|
|
||||||
Color ColorSwatchWidget::get_managed_color(const Color &input) const
|
Color ColorSwatchWidget::get_managed_color(const Color &input) const
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
#include "node/node.h"
|
#include "oakutil/qtutils.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
#include "oakutil/decibel.h"
|
#include "oakutil/decibel.h"
|
||||||
#include "common/nodevaluehandle.h"
|
|
||||||
#include "common/oakvaluehelper.h"
|
#include "common/oakvaluehelper.h"
|
||||||
#include "oakutil/qtutils.h"
|
#include "oakutil/qtutils.h"
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
@@ -44,44 +43,45 @@ namespace
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Map a keyframe track's scalar QVariant into the facade POD for the
|
// Map a keyframe track's scalar QVariant into the facade POD for the
|
||||||
// input's declared type (the curve view drags numeric tracks).
|
// input's declared type (the curve view drags numeric tracks). `c_type`
|
||||||
void track_value_to_c(NodeValue::Type declared, const QVariant &value,
|
// is the oak_node_value_type of the input (oakengine_node_input_get_type()).
|
||||||
|
void track_value_to_c(int c_type, const QVariant &value,
|
||||||
oak_node_value *out)
|
oak_node_value *out)
|
||||||
{
|
{
|
||||||
memset(out, 0, sizeof(*out));
|
memset(out, 0, sizeof(*out));
|
||||||
switch (declared) {
|
switch (c_type) {
|
||||||
case NodeValue::k_int:
|
case OAK_NODE_VALUE_INT:
|
||||||
out->type = OAK_NODE_VALUE_INT;
|
out->type = OAK_NODE_VALUE_INT;
|
||||||
out->num = value.toLongLong();
|
out->num = value.toLongLong();
|
||||||
break;
|
break;
|
||||||
case NodeValue::k_combo:
|
case OAK_NODE_VALUE_COMBO:
|
||||||
out->type = OAK_NODE_VALUE_COMBO;
|
out->type = OAK_NODE_VALUE_COMBO;
|
||||||
out->num = value.toLongLong();
|
out->num = value.toLongLong();
|
||||||
break;
|
break;
|
||||||
case NodeValue::k_boolean:
|
case OAK_NODE_VALUE_BOOL:
|
||||||
out->type = OAK_NODE_VALUE_BOOL;
|
out->type = OAK_NODE_VALUE_BOOL;
|
||||||
out->num = value.toBool() ? 1 : 0;
|
out->num = value.toBool() ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
case NodeValue::k_rational: {
|
case OAK_NODE_VALUE_RATIONAL: {
|
||||||
const Rational r = value.value<Rational>();
|
const Rational r = value.value<Rational>();
|
||||||
out->type = OAK_NODE_VALUE_RATIONAL;
|
out->type = OAK_NODE_VALUE_RATIONAL;
|
||||||
out->num = r.numerator();
|
out->num = r.numerator();
|
||||||
out->den = r.denominator();
|
out->den = r.denominator();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NodeValue::k_color:
|
case OAK_NODE_VALUE_COLOR:
|
||||||
out->type = OAK_NODE_VALUE_COLOR;
|
out->type = OAK_NODE_VALUE_COLOR;
|
||||||
out->f[0] = value.toDouble();
|
out->f[0] = value.toDouble();
|
||||||
break;
|
break;
|
||||||
case NodeValue::k_vec2:
|
case OAK_NODE_VALUE_VEC2:
|
||||||
out->type = OAK_NODE_VALUE_VEC2;
|
out->type = OAK_NODE_VALUE_VEC2;
|
||||||
out->f[0] = value.toDouble();
|
out->f[0] = value.toDouble();
|
||||||
break;
|
break;
|
||||||
case NodeValue::k_vec3:
|
case OAK_NODE_VALUE_VEC3:
|
||||||
out->type = OAK_NODE_VALUE_VEC3;
|
out->type = OAK_NODE_VALUE_VEC3;
|
||||||
out->f[0] = value.toDouble();
|
out->f[0] = value.toDouble();
|
||||||
break;
|
break;
|
||||||
case NodeValue::k_vec4:
|
case OAK_NODE_VALUE_VEC4:
|
||||||
out->type = OAK_NODE_VALUE_VEC4;
|
out->type = OAK_NODE_VALUE_VEC4;
|
||||||
out->f[0] = value.toDouble();
|
out->f[0] = value.toDouble();
|
||||||
break;
|
break;
|
||||||
@@ -92,6 +92,14 @@ void track_value_to_c(NodeValue::Type declared, const QVariant &value,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The oak_node_value_type of the input that owns `key`.
|
||||||
|
int key_input_c_type(OakEngineKeyframe *key)
|
||||||
|
{
|
||||||
|
const QByteArray input = key_input_id(key).toUtf8();
|
||||||
|
return oakengine_node_input_get_type(oakengine_keyframe_get_node(key),
|
||||||
|
input.constData());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CurveView::CurveView(QWidget *parent)
|
CurveView::CurveView(QWidget *parent)
|
||||||
@@ -109,7 +117,7 @@ CurveView::CurveView(QWidget *parent)
|
|||||||
QtUtils::q_font_metrics_width(fontMetrics(), QStringLiteral("00000"));
|
QtUtils::q_font_metrics_width(fontMetrics(), QStringLiteral("00000"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::connect_input(const NodeKeyframeTrackReference &ref)
|
void CurveView::connect_input(const oak::KeyframeTrackRef &ref)
|
||||||
{
|
{
|
||||||
if (connected_inputs_.contains(ref)) {
|
if (connected_inputs_.contains(ref)) {
|
||||||
// Input wasn't connected, do nothing
|
// Input wasn't connected, do nothing
|
||||||
@@ -129,7 +137,7 @@ void CurveView::connect_input(const NodeKeyframeTrackReference &ref)
|
|||||||
connected_inputs_.append(ref);
|
connected_inputs_.append(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::disconnect_input(const NodeKeyframeTrackReference &ref)
|
void CurveView::disconnect_input(const oak::KeyframeTrackRef &ref)
|
||||||
{
|
{
|
||||||
if (!connected_inputs_.contains(ref)) {
|
if (!connected_inputs_.contains(ref)) {
|
||||||
// Input wasn't connected, do nothing
|
// Input wasn't connected, do nothing
|
||||||
@@ -143,18 +151,18 @@ void CurveView::disconnect_input(const NodeKeyframeTrackReference &ref)
|
|||||||
connected_inputs_.removeOne(ref);
|
connected_inputs_.removeOne(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::select_keyframes_of_input(const NodeKeyframeTrackReference &ref)
|
void CurveView::select_keyframes_of_input(const oak::KeyframeTrackRef &ref)
|
||||||
{
|
{
|
||||||
deselect_all();
|
deselect_all();
|
||||||
|
|
||||||
if (KeyframeViewInputConnection *con = track_connections_.value(ref)) {
|
if (KeyframeViewInputConnection *con = track_connections_.value(ref)) {
|
||||||
foreach (NodeKeyframe *key, con->get_keyframes()) {
|
foreach (const oak::Keyframe &key, con->get_keyframes()) {
|
||||||
select_keyframe(key);
|
select_keyframe(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::set_keyframe_track_color(const NodeKeyframeTrackReference &ref,
|
void CurveView::set_keyframe_track_color(const oak::KeyframeTrackRef &ref,
|
||||||
const QColor &color)
|
const QColor &color)
|
||||||
{
|
{
|
||||||
// Insert color into hashmap
|
// Insert color into hashmap
|
||||||
@@ -221,15 +229,9 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
|||||||
painter->drawLines(lines);
|
painter->drawLines(lines);
|
||||||
|
|
||||||
// Draw keyframe lines
|
// Draw keyframe lines
|
||||||
foreach (const NodeKeyframeTrackReference &ref, connected_inputs_) {
|
foreach (const oak::KeyframeTrackRef &ref, connected_inputs_) {
|
||||||
Node *node = ref.input().node();
|
if (ref.input().is_keyframing()) {
|
||||||
const QString &input = ref.input().input();
|
const QVector<oak::Keyframe> track = ref.keyframes();
|
||||||
|
|
||||||
if (node->is_input_keyframing(input, ref.input().element())) {
|
|
||||||
const QVector<NodeKeyframeTrack> &tracks =
|
|
||||||
node->get_keyframe_tracks(ref.input());
|
|
||||||
|
|
||||||
const NodeKeyframeTrack &track = tracks.at(ref.track());
|
|
||||||
|
|
||||||
if (!track.isEmpty()) {
|
if (!track.isEmpty()) {
|
||||||
painter->setPen(QPen(keyframe_colors_.value(ref),
|
painter->setPen(QPen(keyframe_colors_.value(ref),
|
||||||
@@ -245,46 +247,46 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
|||||||
|
|
||||||
// Draw lines between each keyframe
|
// Draw lines between each keyframe
|
||||||
for (int i = 1; i < track.size(); i++) {
|
for (int i = 1; i < track.size(); i++) {
|
||||||
NodeKeyframe *before = track.at(i - 1);
|
const oak::Keyframe &before = track.at(i - 1);
|
||||||
NodeKeyframe *after = track.at(i);
|
const oak::Keyframe &after = track.at(i);
|
||||||
|
|
||||||
QPointF before_pos = get_keyframe_position(before);
|
QPointF before_pos = get_keyframe_position(before);
|
||||||
QPointF after_pos = get_keyframe_position(after);
|
QPointF after_pos = get_keyframe_position(after);
|
||||||
|
|
||||||
if (before->type() == NodeKeyframe::k_hold) {
|
if (before.type() == KeyframeTypes::k_facade_hold) {
|
||||||
// Draw a hold keyframe (basically a right angle)
|
// Draw a hold keyframe (basically a right angle)
|
||||||
path.lineTo(after_pos.x(), before_pos.y());
|
path.lineTo(after_pos.x(), before_pos.y());
|
||||||
path.lineTo(after_pos.x(), after_pos.y());
|
path.lineTo(after_pos.x(), after_pos.y());
|
||||||
|
|
||||||
} else if (before->type() == NodeKeyframe::k_bezier &&
|
} else if (before.type() == KeyframeTypes::k_facade_bezier &&
|
||||||
after->type() == NodeKeyframe::k_bezier) {
|
after.type() == KeyframeTypes::k_facade_bezier) {
|
||||||
// Draw a cubic bezier
|
// Draw a cubic bezier
|
||||||
|
|
||||||
// Cubic beziers have two control points, so we can just use both
|
// Cubic beziers have two control points, so we can just use both
|
||||||
QPointF before_control_point =
|
QPointF before_control_point =
|
||||||
before_pos +
|
before_pos +
|
||||||
ScalePoint(before->valid_bezier_control_out());
|
ScalePoint(before.valid_bezier_point(1));
|
||||||
QPointF after_control_point =
|
QPointF after_control_point =
|
||||||
after_pos +
|
after_pos +
|
||||||
ScalePoint(after->valid_bezier_control_in());
|
ScalePoint(after.valid_bezier_point(0));
|
||||||
|
|
||||||
path.cubicTo(before_control_point, after_control_point,
|
path.cubicTo(before_control_point, after_control_point,
|
||||||
after_pos);
|
after_pos);
|
||||||
|
|
||||||
} else if (before->type() == NodeKeyframe::k_bezier ||
|
} else if (before.type() == KeyframeTypes::k_facade_bezier ||
|
||||||
after->type() == NodeKeyframe::k_bezier) {
|
after.type() == KeyframeTypes::k_facade_bezier) {
|
||||||
// Draw a quadratic bezier
|
// Draw a quadratic bezier
|
||||||
|
|
||||||
// Quadratic beziers have a single control point, we just have to determine which it is
|
// Quadratic beziers have a single control point, we just have to determine which it is
|
||||||
QPointF key_anchor;
|
QPointF key_anchor;
|
||||||
QPointF control_point;
|
QPointF control_point;
|
||||||
|
|
||||||
if (before->type() == NodeKeyframe::k_bezier) {
|
if (before.type() == KeyframeTypes::k_facade_bezier) {
|
||||||
key_anchor = before_pos;
|
key_anchor = before_pos;
|
||||||
control_point = before->valid_bezier_control_out();
|
control_point = before.valid_bezier_point(1);
|
||||||
} else {
|
} else {
|
||||||
key_anchor = after_pos;
|
key_anchor = after_pos;
|
||||||
control_point = after->valid_bezier_control_in();
|
control_point = after.valid_bezier_point(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale control point
|
// Scale control point
|
||||||
@@ -327,8 +329,7 @@ void CurveView::ContextMenuEvent(Menu &m)
|
|||||||
&CurveView::zoom_to_fit_selected);
|
&CurveView::zoom_to_fit_selected);
|
||||||
|
|
||||||
QAction *reset_zoom_action = m.addAction(tr("Reset Zoom"));
|
QAction *reset_zoom_action = m.addAction(tr("Reset Zoom"));
|
||||||
connect(reset_zoom_action, &QAction::triggered, this,
|
connect(reset_zoom_action, &QAction::triggered, this, &CurveView::reset_zoom);
|
||||||
&CurveView::reset_zoom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::SceneRectUpdateEvent(QRectF &r)
|
void CurveView::SceneRectUpdateEvent(QRectF &r)
|
||||||
@@ -337,7 +338,7 @@ void CurveView::SceneRectUpdateEvent(QRectF &r)
|
|||||||
bool got_val = false;
|
bool got_val = false;
|
||||||
|
|
||||||
foreach (KeyframeViewInputConnection *con, track_connections_) {
|
foreach (KeyframeViewInputConnection *con, track_connections_) {
|
||||||
foreach (NodeKeyframe *key, con->get_keyframes()) {
|
foreach (const oak::Keyframe &key, con->get_keyframes()) {
|
||||||
qreal key_y = get_item_y_from_keyframe_value(key);
|
qreal key_y = get_item_y_from_keyframe_value(key);
|
||||||
|
|
||||||
if (got_val) {
|
if (got_val) {
|
||||||
@@ -358,16 +359,17 @@ void CurveView::SceneRectUpdateEvent(QRectF &r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qreal CurveView::get_keyframe_scene_y(KeyframeViewInputConnection *track,
|
qreal CurveView::get_keyframe_scene_y(KeyframeViewInputConnection *track,
|
||||||
NodeKeyframe *key)
|
const oak::Keyframe &key)
|
||||||
{
|
{
|
||||||
return get_item_y_from_keyframe_value(key);
|
return get_item_y_from_keyframe_value(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::draw_keyframe(QPainter *painter, NodeKeyframe *key,
|
void CurveView::draw_keyframe(QPainter *painter, const oak::Keyframe &key,
|
||||||
KeyframeViewInputConnection *track,
|
KeyframeViewInputConnection *track,
|
||||||
const QRectF &key_rect)
|
const QRectF &key_rect)
|
||||||
{
|
{
|
||||||
if (is_keyframe_selected(key) && key->type() == NodeKeyframe::k_bezier) {
|
if (is_keyframe_selected(key) &&
|
||||||
|
key.type() == KeyframeTypes::k_facade_bezier) {
|
||||||
// Draw bezier control points if keyframe is selected
|
// Draw bezier control points if keyframe is selected
|
||||||
int control_point_size = QtUtils::q_font_metrics_width(fontMetrics(), "o");
|
int control_point_size = QtUtils::q_font_metrics_width(fontMetrics(), "o");
|
||||||
int half_sz = control_point_size / 2;
|
int half_sz = control_point_size / 2;
|
||||||
@@ -378,9 +380,9 @@ void CurveView::draw_keyframe(QPainter *painter, NodeKeyframe *key,
|
|||||||
painter->setBrush(Qt::NoBrush);
|
painter->setBrush(Qt::NoBrush);
|
||||||
|
|
||||||
QRectF cp_in = control_point_rect.translated(
|
QRectF cp_in = control_point_rect.translated(
|
||||||
key_rect.center() + ScalePoint(key->bezier_control_in()));
|
key_rect.center() + ScalePoint(key.bezier_point(0)));
|
||||||
QRectF cp_out = control_point_rect.translated(
|
QRectF cp_out = control_point_rect.translated(
|
||||||
key_rect.center() + ScalePoint(key->bezier_control_out()));
|
key_rect.center() + ScalePoint(key.bezier_point(1)));
|
||||||
|
|
||||||
painter->drawLine(key_rect.center(), cp_in.center());
|
painter->drawLine(key_rect.center(), cp_in.center());
|
||||||
painter->drawLine(key_rect.center(), cp_out.center());
|
painter->drawLine(key_rect.center(), cp_out.center());
|
||||||
@@ -388,8 +390,8 @@ void CurveView::draw_keyframe(QPainter *painter, NodeKeyframe *key,
|
|||||||
painter->drawEllipse(cp_in);
|
painter->drawEllipse(cp_in);
|
||||||
painter->drawEllipse(cp_out);
|
painter->drawEllipse(cp_out);
|
||||||
|
|
||||||
bezier_pts_.append({ cp_in, key, NodeKeyframe::k_in_handle });
|
bezier_pts_.append({ cp_in, key.handle(), KeyframeTypes::k_in_handle });
|
||||||
bezier_pts_.append({ cp_out, key, NodeKeyframe::k_out_handle });
|
bezier_pts_.append({ cp_out, key.handle(), KeyframeTypes::k_out_handle });
|
||||||
}
|
}
|
||||||
|
|
||||||
super::draw_keyframe(painter, key, track, key_rect);
|
super::draw_keyframe(painter, key, track, key_rect);
|
||||||
@@ -407,15 +409,15 @@ bool CurveView::first_chance_mouse_press(QMouseEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dragging_bezier_pt_) {
|
if (dragging_bezier_pt_) {
|
||||||
NodeKeyframe *key = dragging_bezier_pt_->keyframe;
|
OakEngineKeyframe *key = dragging_bezier_pt_->keyframe;
|
||||||
dragging_bezier_point_start_ =
|
dragging_bezier_point_start_ =
|
||||||
(dragging_bezier_pt_->type == NodeKeyframe::k_in_handle) ?
|
(dragging_bezier_pt_->type == KeyframeTypes::k_in_handle) ?
|
||||||
key->bezier_control_in() :
|
key_bezier_point(key, 0) :
|
||||||
key->bezier_control_out();
|
key_bezier_point(key, 1);
|
||||||
dragging_bezier_point_opposing_start_ =
|
dragging_bezier_point_opposing_start_ =
|
||||||
(dragging_bezier_pt_->type == NodeKeyframe::k_in_handle) ?
|
(dragging_bezier_pt_->type == KeyframeTypes::k_in_handle) ?
|
||||||
key->bezier_control_out() :
|
key_bezier_point(key, 1) :
|
||||||
key->bezier_control_in();
|
key_bezier_point(key, 0);
|
||||||
|
|
||||||
drag_start_ = mapToScene(event->pos());
|
drag_start_ = mapToScene(event->pos());
|
||||||
return true;
|
return true;
|
||||||
@@ -449,7 +451,7 @@ void CurveView::first_chance_mouse_move(QMouseEvent *event)
|
|||||||
|
|
||||||
if (!(event->modifiers() & Qt::ControlModifier)) {
|
if (!(event->modifiers() & Qt::ControlModifier)) {
|
||||||
new_opposing_pos = generate_bezier_control_position(
|
new_opposing_pos = generate_bezier_control_position(
|
||||||
static_cast<NodeKeyframe::BezierType>(opposing_type),
|
static_cast<KeyframeTypes::BezierType>(opposing_type),
|
||||||
dragging_bezier_point_opposing_start_,
|
dragging_bezier_point_opposing_start_,
|
||||||
-mouse_diff_scaled);
|
-mouse_diff_scaled);
|
||||||
} else {
|
} else {
|
||||||
@@ -457,12 +459,12 @@ void CurveView::first_chance_mouse_move(QMouseEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
oakengine_keyframe_set_bezier_point_live(
|
oakengine_keyframe_set_bezier_point_live(
|
||||||
reinterpret_cast<OakEngineKeyframe *>(dragging_bezier_pt_->keyframe),
|
dragging_bezier_pt_->keyframe,
|
||||||
dragging_bezier_pt_->type,
|
dragging_bezier_pt_->type,
|
||||||
new_bezier_pos.x(), new_bezier_pos.y());
|
new_bezier_pos.x(), new_bezier_pos.y());
|
||||||
|
|
||||||
oakengine_keyframe_set_bezier_point_live(
|
oakengine_keyframe_set_bezier_point_live(
|
||||||
reinterpret_cast<OakEngineKeyframe *>(dragging_bezier_pt_->keyframe),
|
dragging_bezier_pt_->keyframe,
|
||||||
opposing_type,
|
opposing_type,
|
||||||
new_opposing_pos.x(), new_opposing_pos.y());
|
new_opposing_pos.x(), new_opposing_pos.y());
|
||||||
|
|
||||||
@@ -475,29 +477,29 @@ void CurveView::first_chance_mouse_release(QMouseEvent *event)
|
|||||||
// as the explicit old values (the drag already live-set the new
|
// as the explicit old values (the drag already live-set the new
|
||||||
// ones); one undoable command per handle, same as the old
|
// ones); one undoable command per handle, same as the old
|
||||||
// KeyframeSetBezierControlPoint children.
|
// KeyframeSetBezierControlPoint children.
|
||||||
NodeKeyframe *key = dragging_bezier_pt_->keyframe;
|
OakEngineKeyframe *key = dragging_bezier_pt_->keyframe;
|
||||||
OakEngineNode *handle =
|
OakEngineNode *handle = oakengine_keyframe_get_node(key);
|
||||||
reinterpret_cast<OakEngineNode *>(key->parent());
|
|
||||||
int tbn = 0, tbd = 0;
|
int tbn = 0, tbd = 0;
|
||||||
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
||||||
const int64_t ts = Timecode::time_to_timestamp(
|
const int64_t ts = Timecode::time_to_timestamp(
|
||||||
key->time(), Rational(tbn, tbd), Timecode::k_round);
|
key_time(key), Rational(tbn, tbd), Timecode::k_round);
|
||||||
const QPointF current =
|
const QPointF current =
|
||||||
key->bezier_control(dragging_bezier_pt_->type);
|
key_bezier_point(key, dragging_bezier_pt_->type);
|
||||||
|
const QByteArray input = key_input_id(key).toUtf8();
|
||||||
oakengine_node_keyframe_set_bezier_point(
|
oakengine_node_keyframe_set_bezier_point(
|
||||||
handle, key->input().toUtf8().constData(), key->element(), ts,
|
handle, input.constData(), key_element(key), ts,
|
||||||
key->track(),
|
key_track(key),
|
||||||
(dragging_bezier_pt_->type == NodeKeyframe::k_in_handle) ? 0 : 1,
|
(dragging_bezier_pt_->type == KeyframeTypes::k_in_handle) ? 0 : 1,
|
||||||
current.x(), current.y(), dragging_bezier_point_start_.x(),
|
current.x(), current.y(), dragging_bezier_point_start_.x(),
|
||||||
dragging_bezier_point_start_.y());
|
dragging_bezier_point_start_.y());
|
||||||
|
|
||||||
if (!(event->modifiers() & Qt::ControlModifier)) {
|
if (!(event->modifiers() & Qt::ControlModifier)) {
|
||||||
int opposing_type =
|
int opposing_type =
|
||||||
oakengine_keyframe_opposing_bezier_type(dragging_bezier_pt_->type);
|
oakengine_keyframe_opposing_bezier_type(dragging_bezier_pt_->type);
|
||||||
const QPointF opposing_current = key->bezier_control(static_cast<NodeKeyframe::BezierType>(opposing_type));
|
const QPointF opposing_current = key_bezier_point(key, opposing_type);
|
||||||
oakengine_node_keyframe_set_bezier_point(
|
oakengine_node_keyframe_set_bezier_point(
|
||||||
handle, key->input().toUtf8().constData(), key->element(), ts,
|
handle, input.constData(), key_element(key), ts,
|
||||||
key->track(),
|
key_track(key),
|
||||||
opposing_type,
|
opposing_type,
|
||||||
opposing_current.x(), opposing_current.y(),
|
opposing_current.x(), opposing_current.y(),
|
||||||
dragging_bezier_point_opposing_start_.x(),
|
dragging_bezier_point_opposing_start_.x(),
|
||||||
@@ -511,8 +513,8 @@ void CurveView::keyframe_drag_start(QMouseEvent *event)
|
|||||||
{
|
{
|
||||||
drag_keyframe_values_.resize(get_selected_keyframes().size());
|
drag_keyframe_values_.resize(get_selected_keyframes().size());
|
||||||
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
||||||
NodeKeyframe *key = get_selected_keyframes().at(i);
|
OakEngineKeyframe *key = get_selected_keyframes().at(i);
|
||||||
drag_keyframe_values_[i] = key->value();
|
drag_keyframe_values_[i] = OakNodeValueToQVariant(key_value(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
drag_start_ = mapToScene(event->pos());
|
drag_start_ = mapToScene(event->pos());
|
||||||
@@ -523,9 +525,9 @@ void CurveView::keyframe_drag_move(QMouseEvent *event, QString &tip)
|
|||||||
if (event->modifiers() & Qt::ShiftModifier) {
|
if (event->modifiers() & Qt::ShiftModifier) {
|
||||||
// Lock to X axis only and set original values on all keys
|
// Lock to X axis only and set original values on all keys
|
||||||
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
||||||
NodeKeyframe *key = get_selected_keyframes().at(i);
|
OakEngineKeyframe *key = get_selected_keyframes().at(i);
|
||||||
oak_node_value v;
|
oak_node_value v;
|
||||||
track_value_to_c(key->parent()->get_input_data_type(key->input()),
|
track_value_to_c(key_input_c_type(key),
|
||||||
drag_keyframe_values_.at(i), &v);
|
drag_keyframe_values_.at(i), &v);
|
||||||
key_set_value_live(key, v);
|
key_set_value_live(key, v);
|
||||||
}
|
}
|
||||||
@@ -538,27 +540,26 @@ void CurveView::keyframe_drag_move(QMouseEvent *event, QString &tip)
|
|||||||
|
|
||||||
// Validate movement - ensure no keyframe goes above its max point or below its min point
|
// Validate movement - ensure no keyframe goes above its max point or below its min point
|
||||||
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
||||||
NodeKeyframe *key = get_selected_keyframes().at(i);
|
OakEngineKeyframe *key = get_selected_keyframes().at(i);
|
||||||
|
|
||||||
FloatSlider::DisplayType display = get_float_display_type_from_keyframe(key);
|
FloatSlider::DisplayType display = get_float_display_type_from_keyframe(oak::Keyframe(key));
|
||||||
Node *node = key->parent();
|
OakEngineNode *node = oakengine_keyframe_get_node(key);
|
||||||
|
const QByteArray input = key_input_id(key).toUtf8();
|
||||||
double original_val = FloatSlider::transform_value_to_display(
|
double original_val = FloatSlider::transform_value_to_display(
|
||||||
drag_keyframe_values_.at(i).toDouble(), display);
|
drag_keyframe_values_.at(i).toDouble(), display);
|
||||||
const QString &input = key->input();
|
|
||||||
double new_val = FloatSlider::transform_display_to_value(
|
double new_val = FloatSlider::transform_display_to_value(
|
||||||
original_val - scaled_diff, display);
|
original_val - scaled_diff, display);
|
||||||
double limited = new_val;
|
double limited = new_val;
|
||||||
|
|
||||||
if (node->has_input_property(input, QStringLiteral("min"))) {
|
double prop = 0;
|
||||||
limited = qMax(
|
if (oakengine_node_input_get_property_number(
|
||||||
limited,
|
node, input.constData(), "min", -1, &prop) == OAKENGINE_OK) {
|
||||||
node->get_input_property(input, QStringLiteral("min")).toDouble());
|
limited = qMax(limited, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->has_input_property(input, QStringLiteral("max"))) {
|
if (oakengine_node_input_get_property_number(
|
||||||
limited = qMin(
|
node, input.constData(), "max", -1, &prop) == OAKENGINE_OK) {
|
||||||
limited,
|
limited = qMin(limited, prop);
|
||||||
node->get_input_property(input, QStringLiteral("max")).toDouble());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (limited != new_val) {
|
if (limited != new_val) {
|
||||||
@@ -568,11 +569,11 @@ void CurveView::keyframe_drag_move(QMouseEvent *event, QString &tip)
|
|||||||
|
|
||||||
// Set values
|
// Set values
|
||||||
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
||||||
NodeKeyframe *key = get_selected_keyframes().at(i);
|
OakEngineKeyframe *key = get_selected_keyframes().at(i);
|
||||||
FloatSlider::DisplayType display = get_float_display_type_from_keyframe(key);
|
FloatSlider::DisplayType display = get_float_display_type_from_keyframe(oak::Keyframe(key));
|
||||||
oak_node_value v;
|
oak_node_value v;
|
||||||
track_value_to_c(
|
track_value_to_c(
|
||||||
key->parent()->get_input_data_type(key->input()),
|
key_input_c_type(key),
|
||||||
FloatSlider::transform_display_to_value(
|
FloatSlider::transform_display_to_value(
|
||||||
FloatSlider::transform_value_to_display(
|
FloatSlider::transform_value_to_display(
|
||||||
drag_keyframe_values_.at(i).toDouble(), display) -
|
drag_keyframe_values_.at(i).toDouble(), display) -
|
||||||
@@ -582,16 +583,16 @@ void CurveView::keyframe_drag_move(QMouseEvent *event, QString &tip)
|
|||||||
key_set_value_live(key, v);
|
key_set_value_live(key, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeKeyframe *tip_item = get_selected_keyframes().front();
|
OakEngineKeyframe *tip_item = get_selected_keyframes().front();
|
||||||
|
|
||||||
bool ok;
|
bool ok;
|
||||||
double num_value = tip_item->value().toDouble(&ok);
|
double num_value = OakNodeValueToQVariant(key_value(tip_item)).toDouble(&ok);
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
tip = QStringLiteral("%1\n");
|
tip = QStringLiteral("%1\n");
|
||||||
tip.append(FloatSlider::value_to_string(
|
tip.append(FloatSlider::value_to_string(
|
||||||
num_value + get_offset_from_keyframe(tip_item),
|
num_value + get_offset_from_keyframe(oak::Keyframe(tip_item)),
|
||||||
get_float_display_type_from_keyframe(tip_item), 2, true));
|
get_float_display_type_from_keyframe(oak::Keyframe(tip_item)), 2, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +606,7 @@ void CurveView::keyframe_drag_release(QMouseEvent *event,
|
|||||||
// drag-start values as the explicit undo values (the drag already
|
// drag-start values as the explicit undo values (the drag already
|
||||||
// live-set the new ones).
|
// live-set the new ones).
|
||||||
struct ValueGroup {
|
struct ValueGroup {
|
||||||
Node *node;
|
OakEngineNode *node;
|
||||||
QString input;
|
QString input;
|
||||||
int element;
|
int element;
|
||||||
QVector<int64_t> times;
|
QVector<int64_t> times;
|
||||||
@@ -615,45 +616,45 @@ void CurveView::keyframe_drag_release(QMouseEvent *event,
|
|||||||
};
|
};
|
||||||
QVector<ValueGroup> groups;
|
QVector<ValueGroup> groups;
|
||||||
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
|
||||||
NodeKeyframe *k = get_selected_keyframes().at(i);
|
OakEngineKeyframe *k = get_selected_keyframes().at(i);
|
||||||
if (qFuzzyCompare(k->value().toDouble(),
|
if (qFuzzyCompare(key_value_as_double(k),
|
||||||
drag_keyframe_values_.at(i).toDouble())) {
|
drag_keyframe_values_.at(i).toDouble())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OakEngineNode *node = oakengine_keyframe_get_node(k);
|
||||||
|
const QString input = key_input_id(k);
|
||||||
|
const int element = key_element(k);
|
||||||
|
|
||||||
int g = 0;
|
int g = 0;
|
||||||
for (; g < groups.size(); g++) {
|
for (; g < groups.size(); g++) {
|
||||||
if (groups.at(g).node == k->parent() &&
|
if (groups.at(g).node == node &&
|
||||||
groups.at(g).input == k->input() &&
|
groups.at(g).input == input &&
|
||||||
groups.at(g).element == k->element()) {
|
groups.at(g).element == element) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (g == groups.size()) {
|
if (g == groups.size()) {
|
||||||
groups.append(
|
groups.append(
|
||||||
{ k->parent(), k->input(), k->element(), {}, {}, {}, {} });
|
{ node, input, element, {}, {}, {}, {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
OakEngineNode *handle =
|
|
||||||
reinterpret_cast<OakEngineNode *>(k->parent());
|
|
||||||
int tbn = 0, tbd = 0;
|
int tbn = 0, tbd = 0;
|
||||||
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
oakengine_node_frame_time_base(node, &tbn, &tbd);
|
||||||
groups[g].times.append(Timecode::time_to_timestamp(
|
groups[g].times.append(Timecode::time_to_timestamp(
|
||||||
k->time(), Rational(tbn, tbd), Timecode::k_round));
|
key_time(k), Rational(tbn, tbd), Timecode::k_round));
|
||||||
groups[g].tracks.append(k->track());
|
groups[g].tracks.append(key_track(k));
|
||||||
|
|
||||||
const NodeValue::Type declared =
|
oak_node_value old_v;
|
||||||
k->parent()->get_input_data_type(k->input());
|
track_value_to_c(key_input_c_type(k), drag_keyframe_values_.at(i),
|
||||||
oak_node_value new_v, old_v;
|
&old_v);
|
||||||
track_value_to_c(declared, k->value(), &new_v);
|
groups[g].values.push_back(key_value(k));
|
||||||
track_value_to_c(declared, drag_keyframe_values_.at(i), &old_v);
|
|
||||||
groups[g].values.push_back(new_v);
|
|
||||||
groups[g].olds.push_back(old_v);
|
groups[g].olds.push_back(old_v);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (const ValueGroup &g, groups) {
|
foreach (const ValueGroup &g, groups) {
|
||||||
oakengine_node_keyframes_set_value_many(
|
oakengine_node_keyframes_set_value_many(
|
||||||
reinterpret_cast<OakEngineNode *>(g.node),
|
g.node,
|
||||||
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
||||||
g.tracks.data(), g.times.size(), g.values.data(),
|
g.tracks.data(), g.times.size(), g.values.data(),
|
||||||
g.olds.data());
|
g.olds.data());
|
||||||
@@ -661,7 +662,7 @@ void CurveView::keyframe_drag_release(QMouseEvent *event,
|
|||||||
}
|
}
|
||||||
|
|
||||||
QPointF
|
QPointF
|
||||||
CurveView::generate_bezier_control_position(const NodeKeyframe::BezierType mode,
|
CurveView::generate_bezier_control_position(const KeyframeTypes::BezierType mode,
|
||||||
const QPointF &start_point,
|
const QPointF &start_point,
|
||||||
const QPointF &scaled_cursor_diff)
|
const QPointF &scaled_cursor_diff)
|
||||||
{
|
{
|
||||||
@@ -670,7 +671,7 @@ CurveView::generate_bezier_control_position(const NodeKeyframe::BezierType mode,
|
|||||||
new_bezier_pos += scaled_cursor_diff;
|
new_bezier_pos += scaled_cursor_diff;
|
||||||
|
|
||||||
// LIMIT bezier handles from overlapping each other
|
// LIMIT bezier handles from overlapping each other
|
||||||
if (mode == NodeKeyframe::k_in_handle) {
|
if (mode == KeyframeTypes::k_in_handle) {
|
||||||
if (new_bezier_pos.x() > 0) {
|
if (new_bezier_pos.x() > 0) {
|
||||||
new_bezier_pos.setX(0);
|
new_bezier_pos.setX(0);
|
||||||
}
|
}
|
||||||
@@ -696,11 +697,12 @@ void CurveView::zoom_to_fit_internal(bool selected_only)
|
|||||||
double min_val, max_val;
|
double min_val, max_val;
|
||||||
|
|
||||||
foreach (KeyframeViewInputConnection *con, track_connections_) {
|
foreach (KeyframeViewInputConnection *con, track_connections_) {
|
||||||
foreach (NodeKeyframe *key, con->get_keyframes()) {
|
foreach (const oak::Keyframe &key, con->get_keyframes()) {
|
||||||
if (!selected_only || is_keyframe_selected(key)) {
|
if (!selected_only || is_keyframe_selected(key)) {
|
||||||
Rational transformed_time =
|
Rational transformed_time =
|
||||||
get_adjusted_time(key->parent(), get_time_target(), key->time(),
|
get_adjusted_time(key.node().handle(),
|
||||||
Node::k_transform_towards_output);
|
get_time_target(), key_time(key.handle()),
|
||||||
|
k_transform_towards_output);
|
||||||
|
|
||||||
qreal key_y = get_unscaled_item_y_from_keyframe_value(key);
|
qreal key_y = get_unscaled_item_y_from_keyframe_value(key);
|
||||||
|
|
||||||
@@ -762,14 +764,14 @@ void CurveView::zoom_to_fit_internal(bool selected_only)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal CurveView::get_item_y_from_keyframe_value(NodeKeyframe *key)
|
qreal CurveView::get_item_y_from_keyframe_value(const oak::Keyframe &key)
|
||||||
{
|
{
|
||||||
return get_unscaled_item_y_from_keyframe_value(key) * get_y_scale();
|
return get_unscaled_item_y_from_keyframe_value(key) * get_y_scale();
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal CurveView::get_unscaled_item_y_from_keyframe_value(NodeKeyframe *key)
|
qreal CurveView::get_unscaled_item_y_from_keyframe_value(const oak::Keyframe &key)
|
||||||
{
|
{
|
||||||
double val = key->value().toDouble();
|
double val = key_value_as_double(key.handle());
|
||||||
|
|
||||||
val = FloatSlider::transform_value_to_display(
|
val = FloatSlider::transform_value_to_display(
|
||||||
val, get_float_display_type_from_keyframe(key));
|
val, get_float_display_type_from_keyframe(key));
|
||||||
@@ -786,45 +788,35 @@ QPointF CurveView::ScalePoint(const QPointF &point)
|
|||||||
}
|
}
|
||||||
|
|
||||||
FloatSlider::DisplayType
|
FloatSlider::DisplayType
|
||||||
CurveView::get_float_display_type_from_keyframe(NodeKeyframe *key)
|
CurveView::get_float_display_type_from_keyframe(const oak::Keyframe &key)
|
||||||
{
|
{
|
||||||
Node *node = key->parent();
|
|
||||||
const QString &input = key->input();
|
|
||||||
if (node->has_input_property(input, QStringLiteral("view"))) {
|
|
||||||
// Try to get view from input (which will be normal if unset)
|
// Try to get view from input (which will be normal if unset)
|
||||||
return static_cast<FloatSlider::DisplayType>(
|
const QByteArray input = key.input_id().toUtf8();
|
||||||
node->get_input_property(input, QStringLiteral("view")).toInt());
|
double view_type = 0;
|
||||||
|
if (oakengine_node_input_get_property_number(
|
||||||
|
key.node().handle(), input.constData(), "view", -1,
|
||||||
|
&view_type) == OAKENGINE_OK) {
|
||||||
|
return static_cast<FloatSlider::DisplayType>(int(view_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to normal
|
// Fallback to normal
|
||||||
return slider::k_normal;
|
return slider::k_normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
double CurveView::get_offset_from_keyframe(NodeKeyframe *key)
|
double CurveView::get_offset_from_keyframe(const oak::Keyframe &key)
|
||||||
{
|
{
|
||||||
Node *node = key->parent();
|
const QByteArray input = key.input_id().toUtf8();
|
||||||
const QString &input = key->input();
|
double offset = 0;
|
||||||
if (node->has_input_property(input, QStringLiteral("offset"))) {
|
if (oakengine_node_input_get_property_number(
|
||||||
QVariant v = node->get_input_property(input, QStringLiteral("offset"));
|
key.node().handle(), input.constData(), "offset", -1,
|
||||||
|
&offset) == OAKENGINE_OK) {
|
||||||
const NodeValue::Type dt = node->get_input_data_type(input);
|
return offset;
|
||||||
const int c_type = node_value_type_to_c(dt);
|
|
||||||
oak_node_value normal;
|
|
||||||
const int tc = oakengine_node_value_keyframe_track_count(c_type);
|
|
||||||
QVector<oak_node_value> track_vals(tc);
|
|
||||||
if (QVariantToOakNodeValue(dt, v, &normal) &&
|
|
||||||
oakengine_node_value_split_to_tracks(
|
|
||||||
c_type, &normal, track_vals.data(), tc) == OAKENGINE_OK &&
|
|
||||||
key->track() >= 0 && key->track() < tc) {
|
|
||||||
return track_vals.at(key->track()).f[0];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointF CurveView::get_keyframe_position(NodeKeyframe *key)
|
QPointF CurveView::get_keyframe_position(const oak::Keyframe &key)
|
||||||
{
|
{
|
||||||
return QPointF(get_keyframe_scene_x(key), get_item_y_from_keyframe_value(key));
|
return QPointF(get_keyframe_scene_x(key), get_item_y_from_keyframe_value(key));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
#ifndef OAK_CURVEVIEW_H
|
#ifndef OAK_CURVEVIEW_H
|
||||||
#define OAK_CURVEVIEW_H
|
#define OAK_CURVEVIEW_H
|
||||||
|
|
||||||
#include "node/keyframe.h"
|
#include "common/keyframetypes.h"
|
||||||
#include "widget/keyframeview/keyframeview.h"
|
#include "widget/keyframeview/keyframeview.h"
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
|
|
||||||
@@ -34,16 +34,16 @@ class CurveView : public KeyframeView {
|
|||||||
public:
|
public:
|
||||||
CurveView(QWidget *parent = nullptr);
|
CurveView(QWidget *parent = nullptr);
|
||||||
|
|
||||||
void connect_input(const NodeKeyframeTrackReference &ref);
|
void connect_input(const oak::KeyframeTrackRef &ref);
|
||||||
|
|
||||||
void disconnect_input(const NodeKeyframeTrackReference &ref);
|
void disconnect_input(const oak::KeyframeTrackRef &ref);
|
||||||
|
|
||||||
void select_keyframes_of_input(const NodeKeyframeTrackReference &ref);
|
void select_keyframes_of_input(const oak::KeyframeTrackRef &ref);
|
||||||
|
|
||||||
void set_keyframe_track_color(const NodeKeyframeTrackReference &ref,
|
void set_keyframe_track_color(const oak::KeyframeTrackRef &ref,
|
||||||
const QColor &color);
|
const QColor &color);
|
||||||
|
|
||||||
const QHash<NodeKeyframeTrackReference, KeyframeViewInputConnection *> &
|
const QHash<oak::KeyframeTrackRef, KeyframeViewInputConnection *> &
|
||||||
get_connections() const
|
get_connections() const
|
||||||
{
|
{
|
||||||
return track_connections_;
|
return track_connections_;
|
||||||
@@ -65,9 +65,9 @@ protected:
|
|||||||
virtual void SceneRectUpdateEvent(QRectF &r) override;
|
virtual void SceneRectUpdateEvent(QRectF &r) override;
|
||||||
|
|
||||||
virtual qreal get_keyframe_scene_y(KeyframeViewInputConnection *track,
|
virtual qreal get_keyframe_scene_y(KeyframeViewInputConnection *track,
|
||||||
NodeKeyframe *key) override;
|
const oak::Keyframe &key) override;
|
||||||
|
|
||||||
virtual void draw_keyframe(QPainter *painter, NodeKeyframe *key,
|
virtual void draw_keyframe(QPainter *painter, const oak::Keyframe &key,
|
||||||
KeyframeViewInputConnection *track,
|
KeyframeViewInputConnection *track,
|
||||||
const QRectF &key_rect) override;
|
const QRectF &key_rect) override;
|
||||||
|
|
||||||
@@ -83,41 +83,41 @@ protected:
|
|||||||
private:
|
private:
|
||||||
void zoom_to_fit_internal(bool selected_only);
|
void zoom_to_fit_internal(bool selected_only);
|
||||||
|
|
||||||
qreal get_item_y_from_keyframe_value(NodeKeyframe *key);
|
qreal get_item_y_from_keyframe_value(const oak::Keyframe &key);
|
||||||
qreal get_unscaled_item_y_from_keyframe_value(NodeKeyframe *key);
|
qreal get_unscaled_item_y_from_keyframe_value(const oak::Keyframe &key);
|
||||||
|
|
||||||
QPointF ScalePoint(const QPointF &point);
|
QPointF ScalePoint(const QPointF &point);
|
||||||
|
|
||||||
static FloatSlider::DisplayType
|
static FloatSlider::DisplayType
|
||||||
get_float_display_type_from_keyframe(NodeKeyframe *key);
|
get_float_display_type_from_keyframe(const oak::Keyframe &key);
|
||||||
|
|
||||||
static double get_offset_from_keyframe(NodeKeyframe *key);
|
static double get_offset_from_keyframe(const oak::Keyframe &key);
|
||||||
|
|
||||||
void adjust_lines();
|
void adjust_lines();
|
||||||
|
|
||||||
QPointF get_keyframe_position(NodeKeyframe *key);
|
QPointF get_keyframe_position(const oak::Keyframe &key);
|
||||||
|
|
||||||
static QPointF
|
static QPointF
|
||||||
generate_bezier_control_position(const NodeKeyframe::BezierType mode,
|
generate_bezier_control_position(const KeyframeTypes::BezierType mode,
|
||||||
const QPointF &start_point,
|
const QPointF &start_point,
|
||||||
const QPointF &scaled_cursor_diff);
|
const QPointF &scaled_cursor_diff);
|
||||||
|
|
||||||
QPointF get_scaled_cursor_pos(const QPointF &cursor_pos);
|
QPointF get_scaled_cursor_pos(const QPointF &cursor_pos);
|
||||||
|
|
||||||
QHash<NodeKeyframeTrackReference, QColor> keyframe_colors_;
|
QHash<oak::KeyframeTrackRef, QColor> keyframe_colors_;
|
||||||
QHash<NodeKeyframeTrackReference, KeyframeViewInputConnection *>
|
QHash<oak::KeyframeTrackRef, KeyframeViewInputConnection *>
|
||||||
track_connections_;
|
track_connections_;
|
||||||
|
|
||||||
int text_padding_;
|
int text_padding_;
|
||||||
|
|
||||||
int minimum_grid_space_;
|
int minimum_grid_space_;
|
||||||
|
|
||||||
QVector<NodeKeyframeTrackReference> connected_inputs_;
|
QVector<oak::KeyframeTrackRef> connected_inputs_;
|
||||||
|
|
||||||
struct BezierPoint {
|
struct BezierPoint {
|
||||||
QRectF rect;
|
QRectF rect;
|
||||||
NodeKeyframe *keyframe;
|
OakEngineKeyframe *keyframe;
|
||||||
NodeKeyframe::BezierType type;
|
KeyframeTypes::BezierType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
QVector<BezierPoint> bezier_pts_;
|
QVector<BezierPoint> bezier_pts_;
|
||||||
|
|||||||
@@ -29,9 +29,9 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "oakutil/qtutils.h"
|
#include "oakutil/qtutils.h"
|
||||||
#include "node/node.h"
|
#include "common/keyframetypes.h"
|
||||||
#include "common/nodevaluehandle.h"
|
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
|
#include "olive/core/util/timecodefunctions.h"
|
||||||
#include "widget/timeruler/timeruler.h"
|
#include "widget/timeruler/timeruler.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -135,17 +135,17 @@ void CurveWidget::DeleteSelected()
|
|||||||
view_->delete_selected();
|
view_->delete_selected();
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *CurveWidget::get_selected_node_with_id(const QString &id)
|
oak::Node CurveWidget::get_selected_node_with_id(const QString &id)
|
||||||
{
|
{
|
||||||
for (auto it = view_->get_connections().cbegin();
|
for (auto it = view_->get_connections().cbegin();
|
||||||
it != view_->get_connections().cend(); it++) {
|
it != view_->get_connections().cend(); it++) {
|
||||||
Node *n = it.key().input().node();
|
oak::Node n = it.key().input().node();
|
||||||
if (n->id() == id) {
|
if (n.id() == id) {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return oak::Node();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CurveWidget::copy_selected(bool cut)
|
bool CurveWidget::copy_selected(bool cut)
|
||||||
@@ -167,7 +167,7 @@ bool CurveWidget::paste()
|
|||||||
std::placeholders::_1));
|
std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::set_nodes(const QVector<Node *> &nodes)
|
void CurveWidget::set_nodes(const QVector<oak::Node> &nodes)
|
||||||
{
|
{
|
||||||
tree_view_->set_nodes(nodes);
|
tree_view_->set_nodes(nodes);
|
||||||
|
|
||||||
@@ -175,19 +175,19 @@ void CurveWidget::set_nodes(const QVector<Node *> &nodes)
|
|||||||
nodes_ = nodes;
|
nodes_ = nodes;
|
||||||
|
|
||||||
// Generate colors
|
// Generate colors
|
||||||
foreach (Node *node, nodes_) {
|
foreach (const oak::Node &node, nodes_) {
|
||||||
foreach (const QString &input, node->inputs()) {
|
foreach (const oak::Input &input, node.inputs()) {
|
||||||
if (node->is_input_keyframable(input) &&
|
if (input.is_keyframable() && !input.is_hidden()) {
|
||||||
!node->is_input_hidden(input)) {
|
const int arr_sz = input.array_size();
|
||||||
int arr_sz = node->input_array_size(input);
|
|
||||||
for (int i = -1; i < arr_sz; i++) {
|
for (int i = -1; i < arr_sz; i++) {
|
||||||
// Generate a random color for this input
|
// Generate a random color for this input
|
||||||
const QVector<NodeKeyframeTrack> &tracks =
|
const oak::Input element_input(node.handle(),
|
||||||
node->get_keyframe_tracks(input, i);
|
input.input_id(), i);
|
||||||
|
const int track_count =
|
||||||
|
element_input.keyframe_track_count();
|
||||||
|
|
||||||
for (int j = 0; j < tracks.size(); j++) {
|
for (int j = 0; j < track_count; j++) {
|
||||||
NodeKeyframeTrackReference ref(
|
oak::KeyframeTrackRef ref(element_input, j);
|
||||||
NodeInput(node, input, i), j);
|
|
||||||
|
|
||||||
if (!keyframe_colors_.contains(ref)) {
|
if (!keyframe_colors_.contains(ref)) {
|
||||||
QColor c =
|
QColor c =
|
||||||
@@ -218,7 +218,7 @@ void CurveWidget::ScaleChangedEvent(const double &scale)
|
|||||||
view_->set_scale(scale);
|
view_->set_scale(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::TimeTargetChangedEvent(ViewerOutput *target)
|
void CurveWidget::TimeTargetChangedEvent(OakEngineNode *target)
|
||||||
{
|
{
|
||||||
TimeTargetObject::TimeTargetChangedEvent(target);
|
TimeTargetObject::TimeTargetChangedEvent(target);
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ void CurveWidget::TimeTargetChangedEvent(ViewerOutput *target)
|
|||||||
view_->set_time_target(target);
|
view_->set_time_target(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::ConnectedNodeChangeEvent(ViewerOutput *n)
|
void CurveWidget::ConnectedNodeChangeEvent(OakEngineNode *n)
|
||||||
{
|
{
|
||||||
super::ConnectedNodeChangeEvent(n);
|
super::ConnectedNodeChangeEvent(n);
|
||||||
|
|
||||||
@@ -250,18 +250,20 @@ void CurveWidget::set_keyframe_button_checked(bool checked)
|
|||||||
hold_button_->setChecked(checked);
|
hold_button_->setChecked(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::set_keyframe_button_checked_from_type(NodeKeyframe::Type type)
|
void CurveWidget::set_keyframe_button_checked_from_type(int facade_type)
|
||||||
{
|
{
|
||||||
linear_button_->setChecked(type == NodeKeyframe::k_linear);
|
linear_button_->setChecked(facade_type == KeyframeTypes::k_facade_linear);
|
||||||
bezier_button_->setChecked(type == NodeKeyframe::k_bezier);
|
bezier_button_->setChecked(facade_type == KeyframeTypes::k_facade_bezier);
|
||||||
hold_button_->setChecked(type == NodeKeyframe::k_hold);
|
hold_button_->setChecked(facade_type == KeyframeTypes::k_facade_hold);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::connect_input(Node *node, const QString &input, int element)
|
void CurveWidget::connect_input(const oak::Node &node, const QString &input,
|
||||||
|
int element)
|
||||||
{
|
{
|
||||||
if (element == -1 && node->input_is_array(input)) {
|
const oak::Input root_input(node.handle(), input);
|
||||||
|
if (element == -1 && root_input.is_array()) {
|
||||||
// This is the root element, connect all elements (if applicable)
|
// This is the root element, connect all elements (if applicable)
|
||||||
int arr_sz = node->input_array_size(input);
|
int arr_sz = root_input.array_size();
|
||||||
for (int i = -1; i < arr_sz; i++) {
|
for (int i = -1; i < arr_sz; i++) {
|
||||||
connect_input_internal(node, input, i);
|
connect_input_internal(node, input, i);
|
||||||
}
|
}
|
||||||
@@ -271,14 +273,13 @@ void CurveWidget::connect_input(Node *node, const QString &input, int element)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::connect_input_internal(Node *node, const QString &input,
|
void CurveWidget::connect_input_internal(const oak::Node &node,
|
||||||
int element)
|
const QString &input, int element)
|
||||||
{
|
{
|
||||||
NodeInput input_ref(node, input, element);
|
const oak::Input input_ref(node.handle(), input, element);
|
||||||
int track_count =
|
const int track_count = input_ref.keyframe_track_count();
|
||||||
oakengine_node_value_keyframe_track_count(node_value_type_to_c(input_ref.get_data_type()));
|
|
||||||
for (int i = 0; i < track_count; i++) {
|
for (int i = 0; i < track_count; i++) {
|
||||||
NodeKeyframeTrackReference track_ref(input_ref, i);
|
oak::KeyframeTrackRef track_ref(input_ref, i);
|
||||||
view_->connect_input(track_ref);
|
view_->connect_input(track_ref);
|
||||||
selected_tracks_.append(track_ref);
|
selected_tracks_.append(track_ref);
|
||||||
}
|
}
|
||||||
@@ -286,20 +287,21 @@ void CurveWidget::connect_input_internal(Node *node, const QString &input,
|
|||||||
|
|
||||||
void CurveWidget::selection_changed()
|
void CurveWidget::selection_changed()
|
||||||
{
|
{
|
||||||
const std::vector<NodeKeyframe *> &selected = view_->get_selected_keyframes();
|
const std::vector<OakEngineKeyframe *> &selected = view_->get_selected_keyframes();
|
||||||
|
|
||||||
set_keyframe_button_checked(false);
|
set_keyframe_button_checked(false);
|
||||||
set_keyframe_button_enabled(!selected.empty());
|
set_keyframe_button_enabled(!selected.empty());
|
||||||
|
|
||||||
if (!selected.empty()) {
|
if (!selected.empty()) {
|
||||||
bool all_same_type = true;
|
bool all_same_type = true;
|
||||||
NodeKeyframe::Type type = selected.front()->type();
|
const int type = oakengine_keyframe_get_type(selected.front());
|
||||||
|
|
||||||
for (size_t i = 1; i < selected.size(); i++) {
|
for (size_t i = 1; i < selected.size(); i++) {
|
||||||
NodeKeyframe *prev_item = selected.at(i - 1);
|
OakEngineKeyframe *prev_item = selected.at(i - 1);
|
||||||
NodeKeyframe *this_item = selected.at(i);
|
OakEngineKeyframe *this_item = selected.at(i);
|
||||||
|
|
||||||
if (prev_item->type() != this_item->type()) {
|
if (oakengine_keyframe_get_type(prev_item) !=
|
||||||
|
oakengine_keyframe_get_type(this_item)) {
|
||||||
all_same_type = false;
|
all_same_type = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -322,21 +324,21 @@ void CurveWidget::keyframe_type_button_triggered(bool checked)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get selected items and do nothing if there are none
|
// Get selected items and do nothing if there are none
|
||||||
const std::vector<NodeKeyframe *> &selected = view_->get_selected_keyframes();
|
const std::vector<OakEngineKeyframe *> &selected = view_->get_selected_keyframes();
|
||||||
if (selected.empty()) {
|
if (selected.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set all selected keyframes to this type
|
// Set all selected keyframes to this type
|
||||||
NodeKeyframe::Type new_type;
|
int new_type;
|
||||||
|
|
||||||
// Determine which type to set
|
// Determine which type to set
|
||||||
if (key_btn == bezier_button_) {
|
if (key_btn == bezier_button_) {
|
||||||
new_type = NodeKeyframe::k_bezier;
|
new_type = KeyframeTypes::k_facade_bezier;
|
||||||
} else if (key_btn == hold_button_) {
|
} else if (key_btn == hold_button_) {
|
||||||
new_type = NodeKeyframe::k_hold;
|
new_type = KeyframeTypes::k_facade_hold;
|
||||||
} else {
|
} else {
|
||||||
new_type = NodeKeyframe::k_linear;
|
new_type = KeyframeTypes::k_facade_linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure only the appropriate button is checked
|
// Ensure only the appropriate button is checked
|
||||||
@@ -346,53 +348,49 @@ void CurveWidget::keyframe_type_button_triggered(bool checked)
|
|||||||
// distinct input (usually just one), with the same batch semantics as
|
// distinct input (usually just one), with the same batch semantics as
|
||||||
// the old per-keyframe commands.
|
// the old per-keyframe commands.
|
||||||
struct TypeGroup {
|
struct TypeGroup {
|
||||||
Node *node;
|
OakEngineNode *node;
|
||||||
QString input;
|
QString input;
|
||||||
int element;
|
int element;
|
||||||
QVector<int64_t> times;
|
QVector<int64_t> times;
|
||||||
QVector<int> tracks;
|
QVector<int> tracks;
|
||||||
};
|
};
|
||||||
QVector<TypeGroup> groups;
|
QVector<TypeGroup> groups;
|
||||||
foreach (NodeKeyframe *item, selected) {
|
foreach (OakEngineKeyframe *item, selected) {
|
||||||
|
const oak::Keyframe key(item);
|
||||||
|
OakEngineNode *node = key.node().handle();
|
||||||
int g = 0;
|
int g = 0;
|
||||||
for (; g < groups.size(); g++) {
|
for (; g < groups.size(); g++) {
|
||||||
if (groups.at(g).node == item->parent() &&
|
if (groups.at(g).node == node &&
|
||||||
groups.at(g).input == item->input() &&
|
groups.at(g).input == key.input_id() &&
|
||||||
groups.at(g).element == item->element()) {
|
groups.at(g).element == key.element()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (g == groups.size()) {
|
if (g == groups.size()) {
|
||||||
groups.append({ item->parent(), item->input(), item->element(),
|
groups.append({ node, key.input_id(), key.element(),
|
||||||
{}, {} });
|
{}, {} });
|
||||||
}
|
}
|
||||||
OakEngineNode *handle =
|
|
||||||
reinterpret_cast<OakEngineNode *>(item->parent());
|
|
||||||
int tbn = 0, tbd = 0;
|
int tbn = 0, tbd = 0;
|
||||||
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
oakengine_node_frame_time_base(node, &tbn, &tbd);
|
||||||
|
int64_t num = 0, den = 1;
|
||||||
|
key.time(&num, &den);
|
||||||
groups[g].times.append(Timecode::time_to_timestamp(
|
groups[g].times.append(Timecode::time_to_timestamp(
|
||||||
item->time(), Rational(tbn, tbd), Timecode::k_round));
|
Rational(int(num), int(den)), Rational(tbn, tbd), Timecode::k_round));
|
||||||
groups[g].tracks.append(item->track());
|
groups[g].tracks.append(key.track());
|
||||||
}
|
|
||||||
int facade_type = 0;
|
|
||||||
if (new_type == NodeKeyframe::k_bezier) {
|
|
||||||
facade_type = 1;
|
|
||||||
} else if (new_type == NodeKeyframe::k_hold) {
|
|
||||||
facade_type = 2;
|
|
||||||
}
|
}
|
||||||
foreach (const TypeGroup &g, groups) {
|
foreach (const TypeGroup &g, groups) {
|
||||||
oakengine_node_keyframes_set_type_many(
|
oakengine_node_keyframes_set_type_many(
|
||||||
reinterpret_cast<OakEngineNode *>(g.node),
|
g.node,
|
||||||
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
||||||
g.tracks.data(), g.times.size(), facade_type);
|
g.tracks.data(), g.times.size(), new_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveWidget::input_selection_changed(const NodeKeyframeTrackReference &ref)
|
void CurveWidget::input_selection_changed(const oak::KeyframeTrackRef &ref)
|
||||||
{
|
{
|
||||||
key_control_->set_input(ref.input());
|
key_control_->set_input(ref.input());
|
||||||
|
|
||||||
foreach (const NodeKeyframeTrackReference &c, selected_tracks_) {
|
foreach (const oak::KeyframeTrackRef &c, selected_tracks_) {
|
||||||
view_->disconnect_input(c);
|
view_->disconnect_input(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,14 +402,14 @@ void CurveWidget::input_selection_changed(const NodeKeyframeTrackReference &ref)
|
|||||||
selected_tracks_.append(ref);
|
selected_tracks_.append(ref);
|
||||||
} else if (ref.input().is_valid()) {
|
} else if (ref.input().is_valid()) {
|
||||||
// This reference is a input, connect all tracks
|
// This reference is a input, connect all tracks
|
||||||
connect_input(ref.input().node(), ref.input().input(),
|
connect_input(ref.input().node(), ref.input().input_id(),
|
||||||
ref.input().element());
|
ref.input().element());
|
||||||
} else if (Node *node = ref.input().node()) {
|
} else if (!ref.input().node().is_null()) {
|
||||||
// This is a node, add all inputs
|
// This is a node, add all inputs
|
||||||
foreach (const QString &input, node->inputs()) {
|
const oak::Node node = ref.input().node();
|
||||||
if (node->is_input_keyframable(input) &&
|
foreach (const oak::Input &input, node.inputs()) {
|
||||||
!node->is_input_hidden(input)) {
|
if (input.is_keyframable() && !input.is_hidden()) {
|
||||||
connect_input(node, input, -1);
|
connect_input(node, input.input_id(), -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,25 +56,22 @@ public:
|
|||||||
view_->deselect_all();
|
view_->deselect_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *get_selected_node_with_id(const QString &id);
|
oak::Node get_selected_node_with_id(const QString &id);
|
||||||
|
|
||||||
virtual bool copy_selected(bool cut) override;
|
virtual bool copy_selected(bool cut) override;
|
||||||
|
|
||||||
virtual bool paste() override;
|
virtual bool paste() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Not a slot: signature uses the engine C++ type Node*, which must not be
|
void set_nodes(const QVector<oak::Node> &nodes);
|
||||||
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
|
||||||
// boundary). All connections use new-style member-function syntax.
|
|
||||||
void set_nodes(const QVector<Node *> &nodes);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void TimebaseChangedEvent(const Rational &) override;
|
virtual void TimebaseChangedEvent(const Rational &) override;
|
||||||
virtual void ScaleChangedEvent(const double &) override;
|
virtual void ScaleChangedEvent(const double &) override;
|
||||||
|
|
||||||
virtual void TimeTargetChangedEvent(ViewerOutput *target) override;
|
virtual void TimeTargetChangedEvent(OakEngineNode *target) override;
|
||||||
|
|
||||||
virtual void ConnectedNodeChangeEvent(ViewerOutput *n) override;
|
virtual void ConnectedNodeChangeEvent(OakEngineNode *n) override;
|
||||||
|
|
||||||
virtual const QVector<KeyframeViewInputConnection *> *
|
virtual const QVector<KeyframeViewInputConnection *> *
|
||||||
get_snap_keyframes() const override
|
get_snap_keyframes() const override
|
||||||
@@ -87,7 +84,7 @@ protected:
|
|||||||
return view_;
|
return view_;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual const std::vector<NodeKeyframe *> *
|
virtual const std::vector<OakEngineKeyframe *> *
|
||||||
get_snap_ignore_keyframes() const override
|
get_snap_ignore_keyframes() const override
|
||||||
{
|
{
|
||||||
return &view_->get_selected_keyframes();
|
return &view_->get_selected_keyframes();
|
||||||
@@ -98,13 +95,13 @@ private:
|
|||||||
|
|
||||||
void set_keyframe_button_checked(bool checked);
|
void set_keyframe_button_checked(bool checked);
|
||||||
|
|
||||||
void set_keyframe_button_checked_from_type(NodeKeyframe::Type type);
|
void set_keyframe_button_checked_from_type(int facade_type);
|
||||||
|
|
||||||
void connect_input(Node *node, const QString &input, int element);
|
void connect_input(const oak::Node &node, const QString &input, int element);
|
||||||
|
|
||||||
void connect_input_internal(Node *node, const QString &input, int element);
|
void connect_input_internal(const oak::Node &node, const QString &input, int element);
|
||||||
|
|
||||||
QHash<NodeKeyframeTrackReference, QColor> keyframe_colors_;
|
QHash<oak::KeyframeTrackRef, QColor> keyframe_colors_;
|
||||||
|
|
||||||
NodeTreeView *tree_view_;
|
NodeTreeView *tree_view_;
|
||||||
|
|
||||||
@@ -118,16 +115,16 @@ private:
|
|||||||
|
|
||||||
NodeParamViewKeyframeControl *key_control_;
|
NodeParamViewKeyframeControl *key_control_;
|
||||||
|
|
||||||
QVector<Node *> nodes_;
|
QVector<oak::Node> nodes_;
|
||||||
|
|
||||||
QVector<NodeKeyframeTrackReference> selected_tracks_;
|
QVector<oak::KeyframeTrackRef> selected_tracks_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void selection_changed();
|
void selection_changed();
|
||||||
|
|
||||||
void keyframe_type_button_triggered(bool checked);
|
void keyframe_type_button_triggered(bool checked);
|
||||||
|
|
||||||
void input_selection_changed(const NodeKeyframeTrackReference &ref);
|
void input_selection_changed(const oak::KeyframeTrackRef &ref);
|
||||||
|
|
||||||
void keyframe_view_dragged(int x, int y);
|
void keyframe_view_dragged(int x, int y);
|
||||||
void keyframe_view_released();
|
void keyframe_view_released();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
|
||||||
#include "tool/tool.h"
|
#include "common/tooltypes.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,19 +21,108 @@
|
|||||||
|
|
||||||
#include "historywidget.h"
|
#include "historywidget.h"
|
||||||
|
|
||||||
#include "core.h"
|
|
||||||
#include "oakengine/events.h"
|
#include "oakengine/events.h"
|
||||||
#include "oakengine/undo.h"
|
#include "oakengine/undo.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
|
HistoryModel::HistoryModel(QObject *parent)
|
||||||
|
: QAbstractItemModel(parent)
|
||||||
|
{
|
||||||
|
sub_ = oakengine_event_subscribe(
|
||||||
|
oakengine_undo_handle(), OAKENGINE_EVENT_UNDO_INDEX_CHANGED,
|
||||||
|
[](const oakengine_event *event, void *userdata) {
|
||||||
|
Q_UNUSED(event)
|
||||||
|
auto *self = static_cast<HistoryModel *>(userdata);
|
||||||
|
self->beginResetModel();
|
||||||
|
self->endResetModel();
|
||||||
|
},
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryModel::~HistoryModel()
|
||||||
|
{
|
||||||
|
if (sub_ > 0) {
|
||||||
|
oakengine_event_unsubscribe(sub_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex HistoryModel::index(int row, int column,
|
||||||
|
const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
return createIndex(row, column, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex HistoryModel::parent(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(index)
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
int HistoryModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return static_cast<int>(oakengine_undo_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
int HistoryModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant HistoryModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
switch (index.column()) {
|
||||||
|
case 0:
|
||||||
|
return index.row() + 1;
|
||||||
|
case 1: {
|
||||||
|
char buf[1024];
|
||||||
|
buf[0] = '\0';
|
||||||
|
oakengine_undo_command_text(index.row(), buf, sizeof(buf));
|
||||||
|
const QString name = QString::fromUtf8(buf);
|
||||||
|
return name.isEmpty() ? tr("Command") : name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (role == Qt::ForegroundRole) {
|
||||||
|
// Rows at/after the current stack index are undone commands
|
||||||
|
if (index.row() >= oakengine_undo_index()) {
|
||||||
|
return QVariant(QColor(Qt::gray));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant HistoryModel::headerData(int section, Qt::Orientation orientation,
|
||||||
|
int role) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(orientation)
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
switch (section) {
|
||||||
|
case 0:
|
||||||
|
return QStringLiteral("Number");
|
||||||
|
case 1:
|
||||||
|
return QStringLiteral("Action");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
HistoryWidget::HistoryWidget(QWidget *parent)
|
HistoryWidget::HistoryWidget(QWidget *parent)
|
||||||
: QTreeView(parent)
|
: QTreeView(parent)
|
||||||
{
|
{
|
||||||
stack_ = Core::instance()->undo_stack();
|
model_ = new HistoryModel(this);
|
||||||
|
|
||||||
this->setModel(stack_);
|
this->setModel(model_);
|
||||||
this->setRootIsDecorated(false);
|
this->setRootIsDecorated(false);
|
||||||
undo_sub_ = oakengine_event_subscribe(
|
undo_sub_ = oakengine_event_subscribe(
|
||||||
oakengine_undo_handle(), OAKENGINE_EVENT_UNDO_INDEX_CHANGED,
|
oakengine_undo_handle(), OAKENGINE_EVENT_UNDO_INDEX_CHANGED,
|
||||||
|
|||||||
@@ -22,15 +22,42 @@
|
|||||||
#ifndef OAK_HISTORYWIDGET_H
|
#ifndef OAK_HISTORYWIDGET_H
|
||||||
#define OAK_HISTORYWIDGET_H
|
#define OAK_HISTORYWIDGET_H
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "undo/undostack.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief App-side undo history model over the engine C ABI.
|
||||||
|
*
|
||||||
|
* Replaces the direct use of the engine's UndoStack as a Qt item model.
|
||||||
|
* Semantics mirror engine/undo/undostack.cpp: two columns (Number, Action),
|
||||||
|
* rows are all commands on the stack (done first, then undone), undone rows
|
||||||
|
* are shown gray. Refreshes itself on OAKENGINE_EVENT_UNDO_INDEX_CHANGED.
|
||||||
|
*/
|
||||||
|
class HistoryModel : public QAbstractItemModel {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit HistoryModel(QObject *parent = nullptr);
|
||||||
|
~HistoryModel() override;
|
||||||
|
|
||||||
|
QModelIndex index(int row, int column,
|
||||||
|
const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QModelIndex parent(const QModelIndex &index) const override;
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index,
|
||||||
|
int role = Qt::DisplayRole) const override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation,
|
||||||
|
int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int64_t sub_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class HistoryWidget : public QTreeView {
|
class HistoryWidget : public QTreeView {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
@@ -38,7 +65,7 @@ public:
|
|||||||
~HistoryWidget() override;
|
~HistoryWidget() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UndoStack *stack_;
|
HistoryModel *model_;
|
||||||
|
|
||||||
int64_t undo_sub_ = 0;
|
int64_t undo_sub_ = 0;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user