diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34466c395..290e74e22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,7 @@ jobs: cd $GITHUB_WORKSPACE/app/dialog/about python3 patreon.py if: github.event_name == 'push' + continue-on-error: true - name: Configure CMake run: | @@ -196,6 +197,7 @@ jobs: cd $GITHUB_WORKSPACE/app/dialog/about python3 patreon.py if: github.event_name == 'push' + continue-on-error: true - name: Configure CMake shell: bash @@ -283,6 +285,8 @@ jobs: os-arch: x86_64 os: macos-10.15 cmake-gen: Ninja + env: + DEP_LOCATION: /opt/olive-editor name: | ${{ matrix.os-name }} <${{ matrix.compiler-name }}, @@ -294,9 +298,6 @@ jobs: - name: Checkout Source Code uses: actions/checkout@v2 - - name: Set up Homebrew - uses: Homebrew/actions/setup-homebrew@master - - name: Automatically Generate Package Name shell: bash env: @@ -313,14 +314,8 @@ jobs: shell: bash working-directory: ${{ runner.workspace }} run: | - # HACK: Workaround for https://github.com/actions/virtual-environments/issues/4020 - rm -rf /usr/local/bin/2to3 - - brew update - brew upgrade - brew tap olive-editor/homebrew - brew install -f qt5 ffmpeg-olive openimageio-olive opencolorio opentimelineio portaudio - echo "/usr/local/opt/qt@5/bin" >> $GITHUB_PATH + $DOWNLOAD_TOOL https://github.com/olive-editor/dependencies/releases/download/continuous/olive-dep-mac-x86_64.tar.gz + sudo tar xzf olive-dep-mac-x86_64.tar.gz -C / - name: Acquire Google Crashpad shell: bash @@ -337,14 +332,16 @@ jobs: cd $GITHUB_WORKSPACE/app/dialog/about python3 patreon.py if: github.event_name == 'push' + continue-on-error: true - name: Configure CMake shell: bash working-directory: ${{ runner.workspace }}/build run: | brew install ninja - PATH=/opt/olive-editor/bin:/opt/olive-editor/crashpad:$PATH \ - cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -G "${{ matrix.cmake-gen }}" + PATH=$DEP_LOCATION:$DEP_LOCATION/bin:$DEP_LOCATION/include:$DEP_LOCATION/lib:$DEP_LOCATION/crashpad:$PATH \ + cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -G "${{ matrix.cmake-gen }}" - name: Build working-directory: ${{ runner.workspace }}/build @@ -366,9 +363,9 @@ jobs: run: | # Use macdeployqt and macdeployqt to bundle dependencies mv app/$BUNDLE_NAME . - macdeployqt $BUNDLE_NAME -executable=$BUNDLE_NAME/Contents/MacOS/olive-crashhandler + $DEP_LOCATION/bin/macdeployqt $BUNDLE_NAME -executable=$BUNDLE_NAME/Contents/MacOS/olive-crashhandler $DOWNLOAD_TOOL https://github.com/arl/macdeployqtfix/raw/master/macdeployqtfix.py - python2 macdeployqtfix.py $BUNDLE_NAME/Contents/MacOS/Olive $(dirname $(which qmake)) + python2 macdeployqtfix.py $BUNDLE_NAME/Contents/MacOS/Olive $VCPKG_ROOT/installed/x64-osx # Manual corrections for f in $BUNDLE_NAME/Contents/Frameworks/*.dylib @@ -380,7 +377,7 @@ jobs: done # Crashpad symbols - /opt/olive-editor/bin/dump_syms $BUNDLE_NAME/Contents/MacOS/Olive > Olive.sym + $DEP_LOCATION/bin/dump_syms $BUNDLE_NAME/Contents/MacOS/Olive > Olive.sym SYM_HEADER=($(head -n 1 Olive.sym)) # Read first line of symbol file SYM_DIR=$BUNDLE_NAME/Contents/Resources/symbols/Olive/${SYM_HEADER[3]} mkdir -p "$SYM_DIR" diff --git a/CMakeLists.txt b/CMakeLists.txt index 6524c5e8c..27d98682d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,9 @@ else() endif() set(OLIVE_DEFINITIONS -DQT_DEPRECATED_WARNINGS) +if (WIN32) + list(APPEND OLIVE_DEFINITIONS -DUNICODE -D_UNICODE) +endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") @@ -81,8 +84,7 @@ list(APPEND OLIVE_LIBRARIES ${OPENEXR_LIBRARIES}) list(APPEND OLIVE_INCLUDE_DIRS ${OPENEXR_INCLUDES}) # Link Qt 5 -find_package(Qt5 5.6 REQUIRED - COMPONENTS +set(QT_LIBRARIES Core Gui Widgets @@ -90,8 +92,15 @@ find_package(Qt5 5.6 REQUIRED Svg LinguistTools Concurrent +) +if (UNIX AND NOT APPLE) + list(APPEND QT_LIBRARIES DBus) +endif() +find_package(Qt5 5.6 REQUIRED + COMPONENTS + ${QT_LIBRARIES} OPTIONAL_COMPONENTS - Network + Network ) if (NOT Qt5Network_FOUND) message(" Qt5::Network module not found, crash reporting will be disabled.") @@ -156,6 +165,14 @@ else() endif() endif() +if (WIN32) + list(APPEND OLIVE_DEFINITIONS "-DUNICODE -D_UNICODE") +elseif (APPLE) + list(APPEND OLIVE_LIBRARIES "-framework IOKit") +elseif(UNIX) + list(APPEND OLIVE_LIBRARIES Qt5::DBus) +endif() + # Generate Git hash set(PROJECT_LONG_VERSION ${PROJECT_VERSION}) if(EXISTS "${CMAKE_SOURCE_DIR}/.git") diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 5d63cfa75..85ec313a6 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -66,9 +66,7 @@ add_library(olive-version-obj version.h ) target_link_libraries(olive-version-obj PRIVATE Qt5::Core) -if(DEFINED GIT_HASH) - target_compile_options(olive-version-obj PRIVATE -DAPPVERSION="${PROJECT_VERSION}" -DAPPVERSIONLONG="${PROJECT_LONG_VERSION}" ) -endif() +target_compile_options(olive-version-obj PRIVATE -DAPPVERSION="${PROJECT_VERSION}" -DAPPVERSIONLONG="${PROJECT_LONG_VERSION}" ) # Add main library add_library(libolive-editor diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 7d3a2d7e1..a6ee5c68c 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -572,7 +572,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV // Find encoder AVCodec* encoder = GetEncoder(codec); if (!encoder) { - SetError(tr("Failed to find codec for %1").arg(codec)); + SetError(tr("Failed to find codec for 0x%1").arg(codec, 16)); return false; } diff --git a/app/common/bezier.cpp b/app/common/bezier.cpp index 10fd88a2a..a1d900d57 100644 --- a/app/common/bezier.cpp +++ b/app/common/bezier.cpp @@ -58,6 +58,10 @@ double Bezier::CalculateTFromX(bool cubic, double x, double a, double b, double double top = 1.0; while (true) { + if (bottom == top) { + return bottom; + } + double mid = (bottom + top) * 0.5; double test = cubic ? CubicTtoY(a, b, c, d, mid) : QuadraticTtoY(a, b, c, mid); diff --git a/app/common/commandlineparser.cpp b/app/common/commandlineparser.cpp index 882ffcf24..150bdc244 100644 --- a/app/common/commandlineparser.cpp +++ b/app/common/commandlineparser.cpp @@ -52,16 +52,16 @@ const CommandLineParser::PositionalArgument *CommandLineParser::AddPositionalArg return a; } -void CommandLineParser::Process(int argc, char **argv) +void CommandLineParser::Process(const QVector &argv) { int positional_index = 0; - for (int i=1; iSet(); - if (o.takes_arg && i+1 < argc) { + if (o.takes_arg && i+1 < argv.size()) { o.option->SetSetting(argv[i+1]); i++; } diff --git a/app/common/commandlineparser.h b/app/common/commandlineparser.h index 7d131f357..4b32a9134 100644 --- a/app/common/commandlineparser.h +++ b/app/common/commandlineparser.h @@ -91,7 +91,7 @@ public: const PositionalArgument* AddPositionalArgument(const QString& name, const QString& description, bool required = false); - void Process(int argc, char** argv); + void Process(const QVector &argv); void PrintHelp(const char* filename); diff --git a/app/common/xmlutils.cpp b/app/common/xmlutils.cpp index f778b6440..1b7820e26 100644 --- a/app/common/xmlutils.cpp +++ b/app/common/xmlutils.cpp @@ -24,37 +24,10 @@ #include "node/factory.h" #include "widget/nodeparamview/nodeparamviewundo.h" #include "widget/nodeview/nodeviewundo.h" +//#include "widget/timelinewidget/undo/timelineundogeneral.h" namespace olive { -void XMLConnectNodes(const XMLNodeData &xml_node_data, uint version, MultiUndoCommand *command) -{ - foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { - Node *out = xml_node_data.node_ptrs.value(con.output_node); - - if (out) { - // Use output param as hint tag since we grandfathered those in - Node::ValueHint hint(con.output_param); - - if (command) { - command->add_child(new NodeEdgeAddCommand(out, con.input)); - - if (version < 210907) { - /// Deprecated: backwards compatibility only - command->add_child(new NodeSetValueHintCommand(con.input, hint)); - } - } else { - Node::ConnectEdge(out, con.input); - - if (version < 210907) { - /// Deprecated: backwards compatibility only - con.input.node()->SetValueHintForInput(con.input.input(), hint, con.input.element()); - } - } - } - } -} - bool XMLReadNextStartElement(QXmlStreamReader *reader) { QXmlStreamReader::TokenType token; @@ -71,10 +44,59 @@ bool XMLReadNextStartElement(QXmlStreamReader *reader) return false; } -void XMLLinkBlocks(const XMLNodeData &xml_node_data) +void XMLNodeData::PostConnect(uint version, MultiUndoCommand *command) const { - foreach (const XMLNodeData::BlockLink& l, xml_node_data.block_links) { - Block::Link(l.block, static_cast(xml_node_data.node_ptrs.value(l.link))); + foreach (const XMLNodeData::SerializedConnection& con, desired_connections) { + if (Node *out = node_ptrs.value(con.output_node)) { + // Use output param as hint tag since we grandfathered those in + Node::ValueHint hint(con.output_param); + + if (command) { + command->add_child(new NodeEdgeAddCommand(out, con.input)); + } else { + Node::ConnectEdge(out, con.input); + } + + if (version < 210907) { + /// Deprecated: backwards compatibility only + if (command) { + command->add_child(new NodeSetValueHintCommand(con.input, hint)); + } else { + con.input.node()->SetValueHintForInput(con.input.input(), hint, con.input.element()); + } + } + } + } + + foreach (const XMLNodeData::BlockLink& l, block_links) { + Node *a = l.block; + Node *b = node_ptrs.value(l.link); + if (command) { + command->add_child(new NodeLinkCommand(a, b, true)); + } else { + Node::Link(a, b); + } + } + + foreach (const XMLNodeData::GroupLink &l, group_input_links) { + if (Node *input_node = node_ptrs.value(l.input_node)) { + NodeInput resolved(input_node, l.input_id, l.input_element); + if (command) { + command->add_child(new NodeGroupAddInputPassthrough(l.group, resolved)); + } else { + l.group->AddInputPassthrough(resolved); + } + } + } + + for (auto it=group_output_links.cbegin(); it!=group_output_links.cend(); it++) { + if (Node *output_node = node_ptrs.value(it.value())) { + if (command) { + command->add_child(new NodeGroupSetOutputPassthrough(it.key(), output_node)); + } else { + it.key()->SetOutputPassthrough(output_node); + } + } } } diff --git a/app/common/xmlutils.h b/app/common/xmlutils.h index e8b88d6a3..d0488ccd0 100644 --- a/app/common/xmlutils.h +++ b/app/common/xmlutils.h @@ -31,6 +31,7 @@ namespace olive { class Block; class Node; class NodeInput; +class NodeGroup; #define XMLAttributeLoop(reader, item) \ foreach (const QXmlStreamAttribute& item, reader->attributes()) @@ -49,14 +50,23 @@ struct XMLNodeData { quintptr link; }; + struct GroupLink { + NodeGroup *group; + quintptr input_node; + QString input_id; + int input_element; + }; + QHash node_ptrs; QList desired_connections; QList block_links; + QVector group_input_links; + QHash group_output_links; + + void PostConnect(uint version, MultiUndoCommand *command = nullptr) const; }; -void XMLConnectNodes(const XMLNodeData& xml_node_data, uint version, MultiUndoCommand *command = nullptr); - /** * @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document * @@ -68,8 +78,6 @@ void XMLConnectNodes(const XMLNodeData& xml_node_data, uint version, MultiUndoCo */ bool XMLReadNextStartElement(QXmlStreamReader* reader); -void XMLLinkBlocks(const XMLNodeData& xml_node_data); - } #endif // XMLREADLOOP_H diff --git a/app/config/config.cpp b/app/config/config.cpp index 797cb6d9f..2f88ea1b9 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -100,6 +100,7 @@ void Config::SetDefaults() SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000); diff --git a/app/core.cpp b/app/core.cpp index 8b9859c9b..cd6f13bdb 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -78,7 +78,7 @@ namespace olive { Core* Core::instance_ = nullptr; -const uint Core::kProjectVersion = 210907; +const uint Core::kProjectVersion = 211228; Core::Core(const CoreParams& params) : main_window_(nullptr), @@ -202,10 +202,7 @@ void Core::Stop() { QFile recent_projects_file(GetRecentProjectsFilePath()); if (recent_projects_file.open(QFile::WriteOnly | QFile::Text)) { - QTextStream ts(&recent_projects_file); - - ts << recent_projects_.join('\n'); - + recent_projects_file.write(recent_projects_.join('\n').toUtf8()); recent_projects_file.close(); } } @@ -448,7 +445,7 @@ void Core::CreateNewSequence() command->add_child(new NodeAddCommand(active_project, new_sequence)); command->add_child(new FolderAddChild(GetSelectedFolderInActiveProject(), new_sequence)); - command->add_child(new NodeSetPositionCommand(new_sequence, new_sequence, QPointF(0, 0), false)); + command->add_child(new NodeSetPositionCommand(new_sequence, new_sequence, Node::Position())); // Create and connect default nodes to new sequence new_sequence->add_default_nodes(command); @@ -493,19 +490,21 @@ bool Core::AddOpenProjectFromTask(Task *task) { ProjectLoadBaseTask* load_task = static_cast(task); - Project* project = load_task->GetLoadedProject(); - MainWindowLayoutInfo layout = load_task->GetLoadedLayout(); + if (!load_task->IsCancelled()) { + Project* project = load_task->GetLoadedProject(); + MainWindowLayoutInfo layout = load_task->GetLoadedLayout(); - if (ValidateFootageInLoadedProject(project, load_task->GetFilenameProjectWasSavedAs())) { - AddOpenProject(project); - main_window_->LoadLayout(layout); + if (ValidateFootageInLoadedProject(project, load_task->GetFilenameProjectWasSavedAs())) { + AddOpenProject(project); + main_window_->LoadLayout(layout); - return true; - } else { - delete project; - - return false; + return true; + } else { + delete project; + } } + + return false; } void Core::ImportTaskComplete(Task* task) @@ -740,13 +739,10 @@ void Core::StartGUI(bool full_screen) { QFile recent_projects_file(GetRecentProjectsFilePath()); if (recent_projects_file.open(QFile::ReadOnly | QFile::Text)) { - QTextStream ts(&recent_projects_file); - - QString s; - while (!(s = ts.readLine()).isEmpty()) { - recent_projects_.append(s); + QString r = QString::fromUtf8(recent_projects_file.readAll()); + if (!r.isEmpty()) { + recent_projects_ = r.split('\n'); } - recent_projects_file.close(); } @@ -1369,10 +1365,10 @@ void Core::SetPreferenceForRenderMode(RenderMode::Mode mode, const QString &pref Config::Current()[GetRenderModePreferencePrefix(mode, preference)] = value; } -void Core::LabelNodes(const QVector &nodes) +bool Core::LabelNodes(const QVector &nodes, MultiUndoCommand *parent) { if (nodes.isEmpty()) { - return; + return false; } bool ok; @@ -1401,8 +1397,16 @@ void Core::LabelNodes(const QVector &nodes) rename_command->AddNode(n, s); } - undo_stack_.push(rename_command); + if (parent) { + parent->add_child(rename_command); + } else { + undo_stack_.push(rename_command); + } + + return true; } + + return false; } Sequence *Core::CreateNewSequenceForProject(Project* project) const diff --git a/app/core.h b/app/core.h index 5b439a8f0..01ce219b1 100644 --- a/app/core.h +++ b/app/core.h @@ -253,7 +253,7 @@ public: /** * @brief Show a dialog to the user to rename a set of nodes */ - void LabelNodes(const QVector &nodes); + bool LabelNodes(const QVector &nodes, MultiUndoCommand *parent = nullptr); /** * @brief Create a new sequence named appropriately for the active project diff --git a/app/dialog/CMakeLists.txt b/app/dialog/CMakeLists.txt index a86a301af..7692472ec 100644 --- a/app/dialog/CMakeLists.txt +++ b/app/dialog/CMakeLists.txt @@ -21,9 +21,9 @@ add_subdirectory(color) add_subdirectory(configbase) add_subdirectory(diskcache) add_subdirectory(export) +add_subdirectory(footageproperties) add_subdirectory(footagerelink) add_subdirectory(keyframeproperties) -add_subdirectory(nodeproperties) if(OpenTimelineIO_FOUND) add_subdirectory(otioproperties) endif() diff --git a/app/dialog/about/patreon.py b/app/dialog/about/patreon.py index 78fe9a059..411c8d215 100644 --- a/app/dialog/about/patreon.py +++ b/app/dialog/about/patreon.py @@ -27,6 +27,6 @@ while True: else: break -text_file = open("patreon.h", "w") +text_file = open("patreon.h", "w", encoding="utf-8") text_file.write("#ifndef PATREON_H\n#define PATREON_H\n\n#include \n\nQStringList patrons = {\n%s\n};\n\n#endif // PATREON_H\n" % name_list) text_file.close() diff --git a/app/dialog/nodeproperties/CMakeLists.txt b/app/dialog/footageproperties/CMakeLists.txt similarity index 81% rename from app/dialog/nodeproperties/CMakeLists.txt rename to app/dialog/footageproperties/CMakeLists.txt index f1c44b5af..25287c233 100644 --- a/app/dialog/nodeproperties/CMakeLists.txt +++ b/app/dialog/footageproperties/CMakeLists.txt @@ -1,5 +1,5 @@ # Olive - Non-Linear Video Editor -# Copyright (C) 2021 Olive Team +# Copyright (C) 2020 Olive Team # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(streamproperties) + set(OLIVE_SOURCES ${OLIVE_SOURCES} - dialog/nodeproperties/nodepropertiesdialog.cpp - dialog/nodeproperties/nodepropertiesdialog.h + dialog/footageproperties/footageproperties.cpp + dialog/footageproperties/footageproperties.h PARENT_SCOPE ) diff --git a/app/dialog/footageproperties/footageproperties.cpp b/app/dialog/footageproperties/footageproperties.cpp new file mode 100644 index 000000000..7cb56c63a --- /dev/null +++ b/app/dialog/footageproperties/footageproperties.cpp @@ -0,0 +1,251 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "footageproperties.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "streamproperties/audiostreamproperties.h" +#include "streamproperties/videostreamproperties.h" +#include "widget/nodeview/nodeviewundo.h" + +namespace olive { + +FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent, Footage *footage) : + QDialog(parent), + footage_(footage) +{ + QGridLayout* layout = new QGridLayout(this); + + setWindowTitle(tr("\"%1\" Properties").arg(footage_->GetLabelOrName())); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + int row = 0; + + layout->addWidget(new QLabel(tr("Name:")), row, 0); + + footage_name_field_ = new QLineEdit(footage_->GetLabel()); + layout->addWidget(footage_name_field_, row, 1); + row++; + + layout->addWidget(new QLabel(tr("Tracks:")), row, 0, 1, 2); + row++; + + track_list = new QListWidget(); + layout->addWidget(track_list, row, 0, 1, 2); + + row++; + + stacked_widget_ = new QStackedWidget(); + layout->addWidget(stacked_widget_, row, 0, 1, 2); + + int first_usable_stream = -1; + + for (int i=0; iGetTotalStreamCount(); i++) { + Track::Reference reference = footage_->GetReferenceFromRealIndex(i); + + QString description; + bool is_enabled = false; + + switch (reference.type()) { + case Track::kVideo: + { + stacked_widget_->addWidget(new VideoStreamProperties(footage_, reference.index())); + + VideoParams vp = footage_->GetVideoParams(reference.index()); + is_enabled = vp.enabled(); + description = tr("%1x%2 %3 FPS").arg(QString::number(vp.width()), QString::number(vp.height()), QString::number(vp.frame_rate().toDouble())); + break; + } + case Track::kAudio: + { + stacked_widget_->addWidget(new AudioStreamProperties(footage_, reference.index())); + + AudioParams ap = footage_->GetAudioParams(reference.index()); + is_enabled = ap.enabled(); + description = tr("%1 Hz %2 channels").arg(QString::number(ap.sample_rate()), QString::number(ap.channel_count())); + break; + } + default: + stacked_widget_->addWidget(new StreamProperties()); + description = tr("Unknown"); + break; + } + + QListWidgetItem* item = new QListWidgetItem(description, track_list); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(is_enabled ? Qt::Checked : Qt::Unchecked); + track_list->addItem(item); + + if (first_usable_stream == -1 + && (reference.type() == Track::kVideo + || reference.type() == Track::kAudio)) { + first_usable_stream = i; + } + } + + row++; + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttons->setCenterButtons(true); + layout->addWidget(buttons, row, 0, 1, 2); + + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + + connect(track_list, &QListWidget::currentRowChanged, stacked_widget_, &QStackedWidget::setCurrentIndex); + + // Auto-select first item that actually has properties + if (first_usable_stream >= 0) { + track_list->setCurrentRow(first_usable_stream); + } + track_list->setFocus(); +} + +void FootagePropertiesDialog::accept() +{ + // Perform sanity check on all pages + for (int i=0;icount();i++) { + if (!static_cast(stacked_widget_->widget(i))->SanityCheck()) { + // Switch to the failed panel in question + stacked_widget_->setCurrentIndex(i); + + // Do nothing (it's up to the property panel itself to throw the error message) + return; + } + } + + MultiUndoCommand* command = new MultiUndoCommand(); + + if (footage_->GetLabel() != footage_name_field_->text()) { + NodeRenameCommand *nrc = new NodeRenameCommand(); + nrc->AddNode(footage_, footage_name_field_->text()); + command->add_child(nrc); + } + + for (int i=0; iGetTotalStreamCount(); i++) { + Track::Reference reference = footage_->GetReferenceFromRealIndex(i); + bool new_stream_enabled = (track_list->item(i)->checkState() == Qt::Checked); + bool old_stream_enabled = new_stream_enabled; + + switch (reference.type()) { + case Track::kVideo: + old_stream_enabled = footage_->GetVideoParams(reference.index()).enabled(); + break; + case Track::kAudio: + old_stream_enabled = footage_->GetAudioParams(reference.index()).enabled(); + break; + case Track::kSubtitle: + case Track::kNone: + case Track::kCount: + break; + } + + if (old_stream_enabled != new_stream_enabled) { + command->add_child(new StreamEnableChangeCommand(footage_, + reference.type(), + reference.index(), + new_stream_enabled)); + } + } + + for (int i=0;icount();i++) { + static_cast(stacked_widget_->widget(i))->Accept(command); + } + + Core::instance()->undo_stack()->pushIfHasChildren(command); + + QDialog::accept(); +} + +FootagePropertiesDialog::StreamEnableChangeCommand::StreamEnableChangeCommand(Footage *footage, Track::Type type, int index_in_type, bool enabled) : + footage_(footage), + type_(type), + index_(index_in_type), + new_enabled_(enabled) +{ +} + +Project *FootagePropertiesDialog::StreamEnableChangeCommand::GetRelevantProject() const +{ + return footage_->project(); +} + +void FootagePropertiesDialog::StreamEnableChangeCommand::redo() +{ + switch (type_) { + case Track::kVideo: + { + VideoParams vp = footage_->GetVideoParams(index_); + old_enabled_ = vp.enabled(); + vp.set_enabled(new_enabled_); + footage_->SetVideoParams(vp, index_); + break; + } + case Track::kAudio: + { + AudioParams ap = footage_->GetAudioParams(index_); + old_enabled_ = ap.enabled(); + ap.set_enabled(new_enabled_); + footage_->SetAudioParams(ap, index_); + break; + } + case Track::kSubtitle: + case Track::kNone: + case Track::kCount: + break; + } +} + +void FootagePropertiesDialog::StreamEnableChangeCommand::undo() +{ + switch (type_) { + case Track::kVideo: + { + VideoParams vp = footage_->GetVideoParams(index_); + vp.set_enabled(old_enabled_); + footage_->SetVideoParams(vp, index_); + break; + } + case Track::kAudio: + { + AudioParams ap = footage_->GetAudioParams(index_); + ap.set_enabled(old_enabled_); + footage_->SetAudioParams(ap, index_); + break; + } + case Track::kSubtitle: + case Track::kNone: + case Track::kCount: + break; + } +} + +} diff --git a/app/dialog/footageproperties/footageproperties.h b/app/dialog/footageproperties/footageproperties.h new file mode 100644 index 000000000..aa71fa01e --- /dev/null +++ b/app/dialog/footageproperties/footageproperties.h @@ -0,0 +1,121 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef MEDIAPROPERTIESDIALOG_H +#define MEDIAPROPERTIESDIALOG_H + +#include +#include +#include +#include +#include +#include +#include + +#include "node/project/footage/footage.h" +#include "undo/undocommand.h" + +namespace olive { + +/** + * @brief The MediaPropertiesDialog class + * + * A dialog for setting properties on Media. This can be loaded from any part of the application provided it's given + * a valid Media object. + */ +class FootagePropertiesDialog : public QDialog { + Q_OBJECT +public: + /** + * @brief MediaPropertiesDialog Constructor + * + * @param parent + * + * QWidget parent. Usually MainWindow or Project panel. + * + * @param i + * + * Media object to set properties for. + */ + FootagePropertiesDialog(QWidget *parent, Footage* footage); +private: + class StreamEnableChangeCommand : public UndoCommand { + public: + StreamEnableChangeCommand(Footage *footage, + Track::Type type, + int index_in_type, + bool enabled); + + virtual Project* GetRelevantProject() const override; + + protected: + virtual void redo() override; + virtual void undo() override; + + private: + Footage *footage_; + Track::Type type_; + int index_; + + bool old_enabled_; + bool new_enabled_; + }; + + /** + * @brief Stack of widgets that changes based on whether the stream is a video or audio stream + */ + QStackedWidget* stacked_widget_; + + /** + * @brief ComboBox for interlacing setting + */ + QComboBox* interlacing_box; + + /** + * @brief Media name text field + */ + QLineEdit* footage_name_field_; + + /** + * @brief Internal pointer to Media object (set in constructor) + */ + Footage* footage_; + + /** + * @brief A list widget for listing the tracks in Media + */ + QListWidget* track_list; + + /** + * @brief Frame rate to conform to + */ + QDoubleSpinBox* conform_fr; + +private slots: + /** + * @brief Overridden accept function for saving the properties back to the Media class + */ + void accept(); + +}; + +} + +#endif // MEDIAPROPERTIESDIALOG_H diff --git a/app/dialog/footageproperties/streamproperties/CMakeLists.txt b/app/dialog/footageproperties/streamproperties/CMakeLists.txt new file mode 100644 index 000000000..3228e9520 --- /dev/null +++ b/app/dialog/footageproperties/streamproperties/CMakeLists.txt @@ -0,0 +1,26 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2020 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + dialog/footageproperties/streamproperties/streamproperties.h + dialog/footageproperties/streamproperties/streamproperties.cpp + dialog/footageproperties/streamproperties/audiostreamproperties.h + dialog/footageproperties/streamproperties/audiostreamproperties.cpp + dialog/footageproperties/streamproperties/videostreamproperties.h + dialog/footageproperties/streamproperties/videostreamproperties.cpp + PARENT_SCOPE +) diff --git a/app/dialog/footageproperties/streamproperties/audiostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/audiostreamproperties.cpp new file mode 100644 index 000000000..0359e7577 --- /dev/null +++ b/app/dialog/footageproperties/streamproperties/audiostreamproperties.cpp @@ -0,0 +1,37 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "audiostreamproperties.h" + +namespace olive { + +AudioStreamProperties::AudioStreamProperties(Footage *footage, int audio_index) : + footage_(footage), + audio_index_(audio_index) +{ +} + +void AudioStreamProperties::Accept(MultiUndoCommand*) +{ + Q_UNUSED(footage_) + Q_UNUSED(audio_index_) +} + +} diff --git a/app/dialog/footageproperties/streamproperties/audiostreamproperties.h b/app/dialog/footageproperties/streamproperties/audiostreamproperties.h new file mode 100644 index 000000000..058ff2bfc --- /dev/null +++ b/app/dialog/footageproperties/streamproperties/audiostreamproperties.h @@ -0,0 +1,45 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef AUDIOSTREAMPROPERTIES_H +#define AUDIOSTREAMPROPERTIES_H + +#include "node/project/footage/footage.h" +#include "streamproperties.h" + +namespace olive { + +class AudioStreamProperties : public StreamProperties +{ +public: + AudioStreamProperties(Footage *footage, int audio_index); + + virtual void Accept(MultiUndoCommand* parent) override; + +private: + Footage *footage_; + + int audio_index_; + +}; + +} + +#endif // AUDIOSTREAMPROPERTIES_H diff --git a/app/dialog/footageproperties/streamproperties/streamproperties.cpp b/app/dialog/footageproperties/streamproperties/streamproperties.cpp new file mode 100644 index 000000000..96f3bbd5a --- /dev/null +++ b/app/dialog/footageproperties/streamproperties/streamproperties.cpp @@ -0,0 +1,30 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "streamproperties.h" + +namespace olive { + +StreamProperties::StreamProperties(QWidget *parent) : + QWidget(parent) +{ +} + +} diff --git a/app/dialog/footageproperties/streamproperties/streamproperties.h b/app/dialog/footageproperties/streamproperties/streamproperties.h new file mode 100644 index 000000000..c8457216b --- /dev/null +++ b/app/dialog/footageproperties/streamproperties/streamproperties.h @@ -0,0 +1,44 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef STREAMPROPERTIES_H +#define STREAMPROPERTIES_H + +#include + +#include "common/define.h" +#include "undo/undocommand.h" + +namespace olive { + +class StreamProperties : public QWidget +{ +public: + StreamProperties(QWidget* parent = nullptr); + + virtual void Accept(MultiUndoCommand*){} + + virtual bool SanityCheck(){return true;} + +}; + +} + +#endif // STREAMPROPERTIES_H diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp new file mode 100644 index 000000000..6fcbdd914 --- /dev/null +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp @@ -0,0 +1,271 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "videostreamproperties.h" + +#include +#include +#include +#include +#include + +#include "common/ocioutils.h" +#include "core.h" +#include "undo/undostack.h" + +namespace olive { + +VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index) : + footage_(footage), + video_index_(video_index), + video_premultiply_alpha_(nullptr) +{ + QGridLayout* video_layout = new QGridLayout(this); + video_layout->setMargin(0); + + int row = 0; + + video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0); + + VideoParams vp = footage_->GetVideoParams(video_index_); + + pixel_aspect_combo_ = new PixelAspectRatioComboBox(); + pixel_aspect_combo_->SetPixelAspectRatio(vp.pixel_aspect_ratio()); + video_layout->addWidget(pixel_aspect_combo_, row, 1); + + row++; + + video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0); + + video_interlace_combo_ = new InterlacedComboBox(); + video_interlace_combo_->SetInterlaceMode(vp.interlacing()); + + video_layout->addWidget(video_interlace_combo_, row, 1); + + row++; + + video_layout->addWidget(new QLabel(tr("Color Space:")), row, 0); + + video_color_space_ = new QComboBox(); + OCIO::ConstConfigRcPtr config = footage_->project()->color_manager()->GetConfig(); + int number_of_colorspaces = config->getNumColorSpaces(); + + video_color_space_->addItem(tr("Default (%1)").arg(footage_->project()->color_manager()->GetDefaultInputColorSpace())); + + for (int i=0;igetColorSpaceNameByIndex(i); + + video_color_space_->addItem(colorspace); + } + + video_color_space_->setCurrentText(vp.colorspace()); + + video_layout->addWidget(video_color_space_, row, 1); + + if (vp.channel_count() == VideoParams::kRGBAChannelCount) { + row++; + + video_premultiply_alpha_ = new QCheckBox(tr("Premultiplied Alpha")); + video_premultiply_alpha_->setChecked(vp.premultiplied_alpha()); + video_layout->addWidget(video_premultiply_alpha_, row, 0, 1, 2); + } + + row++; + + if (vp.video_type() == VideoParams::kVideoTypeImageSequence) { + QGroupBox* imgseq_group = new QGroupBox(tr("Image Sequence")); + QGridLayout* imgseq_layout = new QGridLayout(imgseq_group); + + int imgseq_row = 0; + + imgseq_layout->addWidget(new QLabel(tr("Start Index:")), imgseq_row, 0); + + imgseq_start_time_ = new IntegerSlider(); + imgseq_start_time_->SetMinimum(0); + imgseq_start_time_->SetValue(vp.start_time()); + imgseq_layout->addWidget(imgseq_start_time_, imgseq_row, 1); + + imgseq_row++; + + imgseq_layout->addWidget(new QLabel(tr("End Index:")), imgseq_row, 0); + + imgseq_end_time_ = new IntegerSlider(); + imgseq_end_time_->SetMinimum(0); + imgseq_end_time_->SetValue(vp.start_time() + vp.duration() - 1); + imgseq_layout->addWidget(imgseq_end_time_, imgseq_row, 1); + + imgseq_row++; + + imgseq_layout->addWidget(new QLabel(tr("Frame Rate:")), imgseq_row, 0); + + imgseq_frame_rate_ = new FrameRateComboBox(); + imgseq_frame_rate_->SetFrameRate(vp.frame_rate()); + imgseq_layout->addWidget(imgseq_frame_rate_, imgseq_row, 1); + + video_layout->addWidget(imgseq_group, row, 0, 1, 2); + } +} + +void VideoStreamProperties::Accept(MultiUndoCommand *parent) +{ + QString set_colorspace; + + if (video_color_space_->currentIndex() > 0) { + set_colorspace = video_color_space_->currentText(); + } + + VideoParams vp = footage_->GetVideoParams(video_index_); + + if ((video_premultiply_alpha_ && video_premultiply_alpha_->isChecked() != vp.premultiplied_alpha()) + || set_colorspace != vp.colorspace() + || static_cast(video_interlace_combo_->currentIndex()) != vp.interlacing() + || pixel_aspect_combo_->GetPixelAspectRatio() != vp.pixel_aspect_ratio()) { + + parent->add_child(new VideoStreamChangeCommand(footage_, + video_index_, + video_premultiply_alpha_ ? video_premultiply_alpha_->isChecked() : vp.premultiplied_alpha(), + set_colorspace, + static_cast(video_interlace_combo_->currentIndex()), + pixel_aspect_combo_->GetPixelAspectRatio())); + } + + if (vp.video_type() == VideoParams::kVideoTypeImageSequence) { + int64_t new_dur = imgseq_end_time_->GetValue() - imgseq_start_time_->GetValue() + 1; + + if (vp.start_time() != imgseq_start_time_->GetValue() + || vp.duration() != new_dur + || vp.frame_rate() != imgseq_frame_rate_->GetFrameRate()) { + parent->add_child(new ImageSequenceChangeCommand(footage_, + video_index_, + imgseq_start_time_->GetValue(), + new_dur, + imgseq_frame_rate_->GetFrameRate())); + } + } +} + +bool VideoStreamProperties::SanityCheck() +{ + if (footage_->GetVideoParams(video_index_).video_type() == VideoParams::kVideoTypeImageSequence) { + if (imgseq_start_time_->GetValue() >= imgseq_end_time_->GetValue()) { + QMessageBox::critical(this, + tr("Invalid Configuration"), + tr("Image sequence end index must be a value higher than the start index."), + QMessageBox::Ok); + return false; + } + } + + return true; +} + +VideoStreamProperties::VideoStreamChangeCommand::VideoStreamChangeCommand(Footage *footage, + int video_index, + bool premultiplied, + QString colorspace, + VideoParams::Interlacing interlacing, + const rational &pixel_ar) : + footage_(footage), + video_index_(video_index), + new_premultiplied_(premultiplied), + new_colorspace_(colorspace), + new_interlacing_(interlacing), + new_pixel_ar_(pixel_ar) +{ +} + +Project *VideoStreamProperties::VideoStreamChangeCommand::GetRelevantProject() const +{ + return footage_->project(); +} + +void VideoStreamProperties::VideoStreamChangeCommand::redo() +{ + VideoParams vp = footage_->GetVideoParams(video_index_); + + old_premultiplied_ = vp.premultiplied_alpha(); + old_colorspace_ = vp.colorspace(); + old_interlacing_ = vp.interlacing(); + old_pixel_ar_ = vp.pixel_aspect_ratio(); + + vp.set_premultiplied_alpha(new_premultiplied_); + vp.set_colorspace(new_colorspace_); + vp.set_interlacing(new_interlacing_); + vp.set_pixel_aspect_ratio(new_pixel_ar_); + + footage_->SetVideoParams(vp, video_index_); +} + +void VideoStreamProperties::VideoStreamChangeCommand::undo() +{ + VideoParams vp = footage_->GetVideoParams(video_index_); + + vp.set_premultiplied_alpha(old_premultiplied_); + vp.set_colorspace(old_colorspace_); + vp.set_interlacing(old_interlacing_); + vp.set_pixel_aspect_ratio(old_pixel_ar_); + + footage_->SetVideoParams(vp, video_index_); +} + +VideoStreamProperties::ImageSequenceChangeCommand::ImageSequenceChangeCommand(Footage *footage, int video_index, int64_t start_index, int64_t duration, const rational &frame_rate) : + footage_(footage), + video_index_(video_index), + new_start_index_(start_index), + new_duration_(duration), + new_frame_rate_(frame_rate) +{ +} + +Project *VideoStreamProperties::ImageSequenceChangeCommand::GetRelevantProject() const +{ + return footage_->project(); +} + +void VideoStreamProperties::ImageSequenceChangeCommand::redo() +{ + VideoParams vp = footage_->GetVideoParams(video_index_); + + old_start_index_ = vp.start_time(); + vp.set_start_time(new_start_index_); + + old_duration_ = vp.duration(); + vp.set_duration(new_duration_); + + old_frame_rate_ = vp.frame_rate(); + vp.set_frame_rate(new_frame_rate_); + vp.set_time_base(new_frame_rate_.flipped()); + + footage_->SetVideoParams(vp, video_index_); +} + +void VideoStreamProperties::ImageSequenceChangeCommand::undo() +{ + VideoParams vp = footage_->GetVideoParams(video_index_); + + vp.set_start_time(old_start_index_); + vp.set_duration(old_duration_); + vp.set_frame_rate(old_frame_rate_); + vp.set_time_base(old_frame_rate_.flipped()); + + footage_->SetVideoParams(vp, video_index_); +} + +} diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.h b/app/dialog/footageproperties/streamproperties/videostreamproperties.h new file mode 100644 index 000000000..ea689e2f0 --- /dev/null +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.h @@ -0,0 +1,148 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef VIDEOSTREAMPROPERTIES_H +#define VIDEOSTREAMPROPERTIES_H + +#include +#include + +#include "node/project/footage/footage.h" +#include "streamproperties.h" +#include "widget/slider/integerslider.h" +#include "widget/standardcombos/standardcombos.h" + +namespace olive { + +class VideoStreamProperties : public StreamProperties +{ + Q_OBJECT +public: + VideoStreamProperties(Footage *footage, int video_index); + + virtual void Accept(MultiUndoCommand *parent) override; + + virtual bool SanityCheck() override; + +private: + Footage *footage_; + + int video_index_; + + /** + * @brief Setting for associated/premultiplied alpha + */ + QCheckBox* video_premultiply_alpha_; + + /** + * @brief Setting for this media's color space + */ + QComboBox* video_color_space_; + + /** + * @brief Setting for video interlacing + */ + InterlacedComboBox* video_interlace_combo_; + + /** + * @brief Sets the start index for image sequences + */ + IntegerSlider* imgseq_start_time_; + + /** + * @brief Sets the end index for image sequences + */ + IntegerSlider* imgseq_end_time_; + + /** + * @brief Sets the frame rate for image sequences + */ + FrameRateComboBox* imgseq_frame_rate_; + + /** + * @brief Sets the pixel aspect ratio of the stream + */ + PixelAspectRatioComboBox* pixel_aspect_combo_; + + class VideoStreamChangeCommand : public UndoCommand { + public: + VideoStreamChangeCommand(Footage *footage, + int video_index, + bool premultiplied, + QString colorspace, + VideoParams::Interlacing interlacing, + const rational& pixel_ar); + + virtual Project* GetRelevantProject() const override; + + protected: + virtual void redo() override; + virtual void undo() override; + + private: + Footage *footage_; + int video_index_; + + bool new_premultiplied_; + QString new_colorspace_; + VideoParams::Interlacing new_interlacing_; + rational new_pixel_ar_; + + bool old_premultiplied_; + QString old_colorspace_; + VideoParams::Interlacing old_interlacing_; + rational old_pixel_ar_; + + }; + + class ImageSequenceChangeCommand : public UndoCommand { + public: + ImageSequenceChangeCommand(Footage *footage, + int video_index, + int64_t start_index, + int64_t duration, + const rational& frame_rate); + + virtual Project* GetRelevantProject() const override; + + protected: + virtual void redo() override; + virtual void undo() override; + + private: + Footage *footage_; + int video_index_; + + int64_t new_start_index_; + int64_t old_start_index_; + + int64_t new_duration_; + int64_t old_duration_; + + rational new_frame_rate_; + rational old_frame_rate_; + + }; + +}; + +} + +#endif // VIDEOSTREAMPROPERTIES_H diff --git a/app/dialog/nodeproperties/nodepropertiesdialog.cpp b/app/dialog/nodeproperties/nodepropertiesdialog.cpp deleted file mode 100644 index 123b6c69b..000000000 --- a/app/dialog/nodeproperties/nodepropertiesdialog.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "nodepropertiesdialog.h" - -#include -#include - -#include "core.h" -#include "widget/nodeview/nodeviewundo.h" - -namespace olive { - -NodePropertiesDialog::NodePropertiesDialog(Node *node, const rational &timebase, QWidget *parent) : - QDialog(parent), - node_(node) -{ - setWindowTitle(tr("Node Properties")); - - QVBoxLayout *layout = new QVBoxLayout(this); - - QHBoxLayout *label_layout = new QHBoxLayout(); - label_layout->setMargin(0); - layout->addLayout(label_layout); - - label_layout->addWidget(new QLabel(tr("Name:"))); - - label_edit_ = new QLineEdit(); - label_edit_->setText(node->GetLabel()); - label_layout->addWidget(label_edit_); - - NodeParamViewItem *item = new NodeParamViewItem(node); - item->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - item->SetTimebase(timebase); - item->setTitleBarWidget(new QWidget()); - layout->addWidget(item); - - layout->addStretch(); - - QDialogButtonBox *btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(btns, &QDialogButtonBox::accepted, this, &NodePropertiesDialog::accept); - connect(btns, &QDialogButtonBox::rejected, this, &NodePropertiesDialog::reject); - layout->addWidget(btns); -} - -void NodePropertiesDialog::accept() -{ - if (label_edit_->text() != node_->GetLabel()) { - NodeRenameCommand* rename_command = new NodeRenameCommand(); - rename_command->AddNode(node_, label_edit_->text()); - Core::instance()->undo_stack()->push(rename_command); - } - - QDialog::accept(); -} - -} diff --git a/app/dialog/sequence/sequencedialogparametertab.cpp b/app/dialog/sequence/sequencedialogparametertab.cpp index 9d77d8bc7..00f89e002 100644 --- a/app/dialog/sequence/sequencedialogparametertab.cpp +++ b/app/dialog/sequence/sequencedialogparametertab.cpp @@ -19,11 +19,28 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg // Set up video section QGroupBox* video_group = new QGroupBox(); video_group->setTitle(tr("Video")); - QHBoxLayout* video_layout = new QHBoxLayout(video_group); - video_section_ = new VideoParamEdit(); - video_section_->SetParameterMask(Sequence::kVideoParamEditMask); - connect(video_section_, &VideoParamEdit::Changed, this, &SequenceDialogParameterTab::UpdatePreviewResolutionLabel); - video_layout->addWidget(video_section_); + QGridLayout *video_layout = new QGridLayout(video_group); + video_layout->addWidget(new QLabel(tr("Width:")), row, 0); + width_slider_ = new IntegerSlider(); + width_slider_->SetMinimum(0); + video_layout->addWidget(width_slider_, row, 1); + row++; + video_layout->addWidget(new QLabel(tr("Height:")), row, 0); + height_slider_ = new IntegerSlider(); + height_slider_->SetMinimum(0); + video_layout->addWidget(height_slider_, row, 1); + row++; + video_layout->addWidget(new QLabel(tr("Frame Rate:")), row, 0); + framerate_combo_ = new FrameRateComboBox(); + video_layout->addWidget(framerate_combo_, row, 1); + row++; + video_layout->addWidget(new QLabel(tr("Pixel Aspect Ratio:")), row, 0); + pixelaspect_combo_ = new PixelAspectRatioComboBox(); + video_layout->addWidget(pixelaspect_combo_, row, 1); + row++; + video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0); + interlacing_combo_ = new InterlacedComboBox(); + video_layout->addWidget(interlacing_combo_, row, 1); layout->addWidget(video_group); row = 0; @@ -65,7 +82,11 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg // Set values based on input sequence VideoParams vp = sequence->GetVideoParams(); AudioParams ap = sequence->GetAudioParams(); - video_section_->SetVideoParams(vp); + width_slider_->SetValue(vp.width()); + height_slider_->SetValue(vp.height()); + framerate_combo_->SetFrameRate(vp.time_base().flipped()); + pixelaspect_combo_->SetPixelAspectRatio(vp.pixel_aspect_ratio()); + interlacing_combo_->SetInterlaceMode(vp.interlacing()); preview_resolution_field_->SetDivider(vp.divider()); preview_format_field_->SetPixelFormat(vp.format()); preview_autocache_field_->setChecked(sequence->GetVideoAutoCacheEnabled()); @@ -86,11 +107,11 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg void SequenceDialogParameterTab::PresetChanged(const SequencePreset &preset) { - video_section_->SetWidth(preset.width()); - video_section_->SetHeight(preset.height()); - video_section_->SetFrameRate(preset.frame_rate()); - video_section_->SetPixelAspectRatio(preset.pixel_aspect()); - video_section_->SetInterlaceMode(preset.interlacing()); + width_slider_->SetValue(preset.width()); + height_slider_->SetValue(preset.height()); + framerate_combo_->SetFrameRate(preset.frame_rate()); + pixelaspect_combo_->SetPixelAspectRatio(preset.pixel_aspect()); + interlacing_combo_->SetInterlaceMode(preset.interlacing()); audio_sample_rate_field_->SetSampleRate(preset.sample_rate()); audio_channels_field_->SetChannelLayout(preset.channel_layout()); preview_resolution_field_->SetDivider(preset.preview_divider()); @@ -115,8 +136,8 @@ void SequenceDialogParameterTab::SavePresetClicked() void SequenceDialogParameterTab::UpdatePreviewResolutionLabel() { - VideoParams test_param(video_section_->GetWidth(), - video_section_->GetHeight(), + VideoParams test_param(GetSelectedVideoWidth(), + GetSelectedVideoHeight(), VideoParams::kFormatInvalid, VideoParams::kInternalChannelCount, rational(1), diff --git a/app/dialog/sequence/sequencedialogparametertab.h b/app/dialog/sequence/sequencedialogparametertab.h index 6d4d3bf08..e11561b2f 100644 --- a/app/dialog/sequence/sequencedialogparametertab.h +++ b/app/dialog/sequence/sequencedialogparametertab.h @@ -1,6 +1,7 @@ #ifndef SEQUENCEDIALOGPARAMETERTAB_H #define SEQUENCEDIALOGPARAMETERTAB_H +#include #include #include #include @@ -9,7 +10,6 @@ #include "sequencepreset.h" #include "widget/slider/integerslider.h" #include "widget/standardcombos/standardcombos.h" -#include "widget/videoparamedit/videoparamedit.h" namespace olive { @@ -21,27 +21,27 @@ public: int GetSelectedVideoWidth() const { - return video_section_->GetWidth(); + return width_slider_->GetValue(); } int GetSelectedVideoHeight() const { - return video_section_->GetHeight(); + return height_slider_->GetValue(); } rational GetSelectedVideoFrameRate() const { - return video_section_->GetFrameRate(); + return framerate_combo_->GetFrameRate(); } rational GetSelectedVideoPixelAspect() const { - return video_section_->GetPixelAspectRatio(); + return pixelaspect_combo_->GetPixelAspectRatio(); } VideoParams::Interlacing GetSelectedVideoInterlacingMode() const { - return video_section_->GetInterlaceMode(); + return interlacing_combo_->GetInterlaceMode(); } int GetSelectedAudioSampleRate() const @@ -76,7 +76,15 @@ signals: void SaveParametersAsPreset(const SequencePreset& preset); private: - VideoParamEdit* video_section_; + IntegerSlider *width_slider_; + + IntegerSlider *height_slider_; + + FrameRateComboBox *framerate_combo_; + + PixelAspectRatioComboBox *pixelaspect_combo_; + + InterlacedComboBox *interlacing_combo_; SampleRateComboBox* audio_sample_rate_field_; diff --git a/app/main.cpp b/app/main.cpp index 8a21126ff..7e729216f 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -71,6 +71,22 @@ int main(int argc, char *argv[]) // Parse command line arguments // + QVector args; +#if defined(_WIN32) && defined(UNICODE) + int wargc; + LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &wargc); + args.resize(wargc); + for (int i=0; i