From 4378fb74232052d9da5bcb1440dad92ba7264ef9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 27 Dec 2018 09:47:08 +1100 Subject: [PATCH 01/65] changed img seq debug message --- io/loadthread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/loadthread.cpp b/io/loadthread.cpp index b7688a1a6..65bdc50f4 100644 --- a/io/loadthread.cpp +++ b/io/loadthread.cpp @@ -237,7 +237,7 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { } else if (m->url.contains('%')) { // hack for image sequences (qt won't be able to find the URL with %, but ffmpeg may) m->url = internal_proj_dir_test; - dout << "[INFO] Guess image sequence" << attr.value().toString() << "path to project's current directory"; + dout << "[INFO] Guess image sequence" << attr.value().toString() << "path to project's internal directory"; } else { dout << "[INFO] Failed to match" << attr.value().toString() << "to file"; } From 1ee9451343313e38b199b2a21d14df8815f9e706 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 27 Dec 2018 10:29:02 +1100 Subject: [PATCH 02/65] implemented hold keyframe and began new panel --- mainwindow.cpp | 21 +++++---------------- olive.pro | 8 ++++++-- panels/grapheditor.cpp | 31 +++++++++++++++++++++++++++++++ panels/grapheditor.h | 16 ++++++++++++++++ panels/panels.cpp | 35 ++++++++++++++++++++++++++++++----- panels/panels.h | 6 ++++++ project/effect.h | 4 ++-- project/effectfield.cpp | 13 +++++++++---- ui/graphview.cpp | 13 +++++++++++++ ui/graphview.h | 13 +++++++++++++ ui/keyframeview.cpp | 31 ++++++++++++++++++++++--------- 11 files changed, 153 insertions(+), 38 deletions(-) create mode 100644 panels/grapheditor.cpp create mode 100644 panels/grapheditor.h create mode 100644 ui/graphview.cpp create mode 100644 ui/graphview.h diff --git a/mainwindow.cpp b/mainwindow.cpp index 738e7c9b6..8cd2f0972 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -18,6 +18,7 @@ #include "panels/effectcontrols.h" #include "panels/viewer.h" #include "panels/timeline.h" +#include "panels/grapheditor.h" #include "dialogs/aboutdialog.h" #include "dialogs/newsequencedialog.h" @@ -57,6 +58,7 @@ void MainWindow::setup_layout(bool reset) { panel_footage_viewer->show(); panel_sequence_viewer->show(); panel_timeline->show(); + panel_graph_editor->hide(); bool load_default = true; @@ -77,6 +79,7 @@ void MainWindow::setup_layout(bool reset) { panel_footage_viewer->raise(); addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer); addDockWidget(Qt::BottomDockWidgetArea, panel_timeline); + panel_graph_editor->setFloating(true); // workaround for strange Qt dock bug (see https://bugreports.qt.io/browse/QTBUG-65592) #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) @@ -194,12 +197,7 @@ MainWindow::MainWindow(QWidget *parent) : } } - // TODO maybe replace these with non-pointers later on? - panel_sequence_viewer = new Viewer(this); - panel_footage_viewer = new Viewer(this); - panel_project = new Project(this); - panel_effect_controls = new EffectControls(this); - panel_timeline = new Timeline(this); + alloc_panels(this); if (!data_dir.isEmpty()) { // detect auto-recovery file @@ -259,16 +257,7 @@ MainWindow::~MainWindow() { delete ui; - delete panel_sequence_viewer; - panel_sequence_viewer = NULL; - delete panel_footage_viewer; - panel_footage_viewer = NULL; - delete panel_project; - panel_project = NULL; - delete panel_effect_controls; - panel_effect_controls = NULL; - delete panel_timeline; - panel_timeline = NULL; + free_panels(); close_debug(); } diff --git a/olive.pro b/olive.pro index 650aedb28..eab6b3b19 100644 --- a/olive.pro +++ b/olive.pro @@ -105,7 +105,9 @@ SOURCES += \ ui/resizablescrollbar.cpp \ ui/sourceiconview.cpp \ project/sourcescommon.cpp \ - ui/keyframenavigator.cpp + ui/keyframenavigator.cpp \ + panels/grapheditor.cpp \ + ui/graphview.cpp HEADERS += \ mainwindow.h \ @@ -185,7 +187,9 @@ HEADERS += \ ui/resizablescrollbar.h \ ui/sourceiconview.h \ project/sourcescommon.h \ - ui/keyframenavigator.h + ui/keyframenavigator.h \ + panels/grapheditor.h \ + ui/graphview.h FORMS += \ mainwindow.ui \ diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp new file mode 100644 index 000000000..ef2f1fdd2 --- /dev/null +++ b/panels/grapheditor.cpp @@ -0,0 +1,31 @@ +#include "grapheditor.h" + +#include +#include +#include + +#include "ui/graphview.h" + +GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent) { + setWindowTitle("Graph Editor"); + + QWidget* central_widget = new QWidget(); + setWidget(central_widget); + + QVBoxLayout* layout = new QVBoxLayout(); + central_widget->setLayout(layout); + + QHBoxLayout* tools = new QHBoxLayout(); + tools->addWidget(new QPushButton("HECK!")); + layout->addLayout(tools); + + view = new GraphView(); + layout->addWidget(view); + + QHBoxLayout* values = new QHBoxLayout(); + values->addStretch(); + values->addWidget(new QLabel("1920.0")); + values->addWidget(new QLabel("1080.0")); + values->addStretch(); + layout->addLayout(values); +} diff --git a/panels/grapheditor.h b/panels/grapheditor.h new file mode 100644 index 000000000..5e4a919cf --- /dev/null +++ b/panels/grapheditor.h @@ -0,0 +1,16 @@ +#ifndef GRAPHEDITOR_H +#define GRAPHEDITOR_H + +#include + +class GraphView; + +class GraphEditor : public QDockWidget { + Q_OBJECT +public: + GraphEditor(QWidget* parent = 0); +private: + GraphView* view; +}; + +#endif // GRAPHEDITOR_H diff --git a/panels/panels.cpp b/panels/panels.cpp index dd8206ccc..fcc9e0008 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -8,6 +8,7 @@ #include "project/clip.h" #include "project/transition.h" #include "io/config.h" +#include "grapheditor.h" #include "debug.h" Project* panel_project = 0; @@ -15,6 +16,7 @@ EffectControls* panel_effect_controls = 0; Viewer* panel_sequence_viewer = 0; Viewer* panel_footage_viewer = 0; Timeline* panel_timeline = 0; +GraphEditor* panel_graph_editor = 0; void update_effect_controls() { // SEND CLIPS TO EFFECT CONTROLS @@ -110,15 +112,15 @@ void update_ui(bool modified) { QDockWidget *get_focused_panel() { QDockWidget* w = NULL; if (config.hover_focus) { - if (panel_project->rect().contains(panel_project->mapFromGlobal(QCursor::pos()))) { + if (panel_project->underMouse()) { w = panel_project; - } else if (panel_effect_controls->rect().contains(panel_effect_controls->mapFromGlobal(QCursor::pos()))) { + } else if (panel_effect_controls->underMouse()) { w = panel_effect_controls; - } else if (panel_sequence_viewer->rect().contains(panel_sequence_viewer->mapFromGlobal(QCursor::pos()))) { + } else if (panel_sequence_viewer->underMouse()) { w = panel_sequence_viewer; - } else if (panel_footage_viewer->rect().contains(panel_footage_viewer->mapFromGlobal(QCursor::pos()))) { + } else if (panel_footage_viewer->underMouse()) { w = panel_footage_viewer; - } else if (panel_timeline->rect().contains(panel_timeline->mapFromGlobal(QCursor::pos()))) { + } else if (panel_timeline->underMouse()) { w = panel_timeline; } } @@ -137,3 +139,26 @@ QDockWidget *get_focused_panel() { } return w; } + +void alloc_panels(QWidget* parent) { + // TODO maybe replace these with non-pointers later on? + panel_sequence_viewer = new Viewer(parent); + panel_footage_viewer = new Viewer(parent); + panel_project = new Project(parent); + panel_effect_controls = new EffectControls(parent); + panel_timeline = new Timeline(parent); + panel_graph_editor = new GraphEditor(parent); +} + +void free_panels() { + delete panel_sequence_viewer; + panel_sequence_viewer = NULL; + delete panel_footage_viewer; + panel_footage_viewer = NULL; + delete panel_project; + panel_project = NULL; + delete panel_effect_controls; + panel_effect_controls = NULL; + delete panel_timeline; + panel_timeline = NULL; +} diff --git a/panels/panels.h b/panels/panels.h index 042a652d8..691736b82 100644 --- a/panels/panels.h +++ b/panels/panels.h @@ -5,6 +5,9 @@ class Project; class EffectControls; class Viewer; class Timeline; +class GraphEditor; + +class QWidget; class QDockWidget; extern Project* panel_project; @@ -12,8 +15,11 @@ extern EffectControls* panel_effect_controls; extern Viewer* panel_sequence_viewer; extern Viewer* panel_footage_viewer; extern Timeline* panel_timeline; +extern GraphEditor* panel_graph_editor; void update_ui(bool modified); QDockWidget* get_focused_panel(); +void alloc_panels(QWidget *parent); +void free_panels(); #endif // PANELS_H diff --git a/project/effect.h b/project/effect.h index 08fcc13b4..572baf7a9 100644 --- a/project/effect.h +++ b/project/effect.h @@ -69,8 +69,8 @@ extern QMutex effects_loaded; #define EFFECT_INTERNAL_COUNT 13 #define KEYFRAME_TYPE_LINEAR 0 -#define KEYFRAME_TYPE_SMOOTH 1 -#define KEYFRAME_TYPE_BEZIER 2 +#define KEYFRAME_TYPE_BEZIER 1 +#define KEYFRAME_TYPE_HOLD 2 struct GLTextureCoords { int grid_size; diff --git a/project/effectfield.cpp b/project/effectfield.cpp index d2166a835..2caaf2991 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -140,7 +140,12 @@ void EffectField::get_keyframe_data(double timecode, int &before, int &after, do before = before_keyframe_index; after = after_keyframe_index; - progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time)); + if (parent_row->keyframe_types.at(before) == KEYFRAME_TYPE_HOLD) { + progress = 0; + } else { + // TODO replace with bezier function + progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time)); + } } else if (before_keyframe_index > -1) { before = before_keyframe_index; after = before_keyframe_index; @@ -161,13 +166,13 @@ QVariant EffectField::validate_keyframe_data(double timecode, bool async) { double progress; get_keyframe_data(timecode, before_keyframe, after_keyframe, progress); - int kf_type = (progress < 0.5) ? parent_row->keyframe_types.at(before_keyframe) : parent_row->keyframe_types.at(after_keyframe); - if (kf_type == KEYFRAME_TYPE_SMOOTH) { + /*int kf_type = (progress < 0.5) ? parent_row->keyframe_types.at(before_keyframe) : parent_row->keyframe_types.at(after_keyframe); + if (kf_type == KEYFRAME_TYPE_BEZIER) { double x = (8.0 * progress) - 4.0; progress = 1.0 / (1.0 + qPow(M_E, -x)); progress *= 1.0373; progress -= 0.01865; - } + }*/ const QVariant& before_data = keyframe_data.at(before_keyframe); switch (type) { diff --git a/ui/graphview.cpp b/ui/graphview.cpp new file mode 100644 index 000000000..405663889 --- /dev/null +++ b/ui/graphview.cpp @@ -0,0 +1,13 @@ +#include "graphview.h" + +#include + +GraphView::GraphView(QWidget* parent) {} + +void GraphView::paintEvent(QPaintEvent *event) { + QPainter p(this); + + p.setPen(Qt::white); + + p.drawRect(rect()); +} diff --git a/ui/graphview.h b/ui/graphview.h new file mode 100644 index 000000000..b28d80bf9 --- /dev/null +++ b/ui/graphview.h @@ -0,0 +1,13 @@ +#ifndef GRAPHVIEW_H +#define GRAPHVIEW_H + +#include + +class GraphView : public QWidget { +public: + GraphView(QWidget* parent = 0); + + void paintEvent(QPaintEvent *event); +}; + +#endif // GRAPHVIEW_H diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index c46dcb0c2..431d9ab03 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -11,6 +11,7 @@ #include "panels/viewer.h" #include "ui/viewerwidget.h" #include "project/sequence.h" +#include "panels/grapheditor.h" #include "ui_effectcontrols.h" #include @@ -47,24 +48,33 @@ KeyframeView::KeyframeView(QWidget *parent) : void KeyframeView::show_context_menu(const QPoint& pos) { if (selected_rows.size() > 0) { QMenu menu(this); + QAction* linear = menu.addAction("Linear"); linear->setData(KEYFRAME_TYPE_LINEAR); QAction* smooth = menu.addAction("Smooth"); - smooth->setData(KEYFRAME_TYPE_SMOOTH); - /*QAction* bezier = menu.addAction("Bezier"); - bezier->setData(KEYFRAME_TYPE_BEZIER);*/ + smooth->setData(KEYFRAME_TYPE_BEZIER); + QAction* hold = menu.addAction("Hold"); + hold->setData(KEYFRAME_TYPE_HOLD); + menu.addSeparator(); + menu.addAction("Graph Editor"); + connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_set_key_type(QAction*))); menu.exec(mapToGlobal(pos)); } } void KeyframeView::menu_set_key_type(QAction* a) { - ComboAction* ca = new ComboAction(); - for (int i=0;iappend(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt())); + if (a->data().isNull()) { + // load graph editor + panel_graph_editor->show(); + } else { + ComboAction* ca = new ComboAction(); + for (int i=0;iappend(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt())); + } + undo_stack.push(ca); + update(); } - undo_stack.push(ca); - update(); } void KeyframeView::paintEvent(QPaintEvent*) { @@ -184,9 +194,12 @@ void KeyframeView::draw_keyframe(QPainter &p, int type, int x, int y, bool darke p.drawPolygon(points, KEYFRAME_POINT_COUNT); } break; - case KEYFRAME_TYPE_SMOOTH: + case KEYFRAME_TYPE_BEZIER: p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE); break; + case KEYFRAME_TYPE_HOLD: + p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2)); + break; } } From 8a6242e36fde034d77b2bd5f8b4b6b836af0f760 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 27 Dec 2018 11:51:35 +1100 Subject: [PATCH 03/65] reimplemented panel state restoration --- mainwindow.cpp | 42 +++++++++++++++++++++--------------------- mainwindow.h | 2 ++ mainwindow.ui | 9 +++++++++ panels/grapheditor.cpp | 21 +++++++++++++++++++-- panels/panels.cpp | 6 ++++++ playback/audio.cpp | 6 ++++-- ui/graphview.cpp | 5 ++++- 7 files changed, 65 insertions(+), 26 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 8cd2f0972..f8be11aff 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -60,31 +60,26 @@ void MainWindow::setup_layout(bool reset) { panel_timeline->show(); panel_graph_editor->hide(); - bool load_default = true; - - /*if (!reset) { - QFile panel_config(get_data_path() + "/layout"); - if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { - if (restoreState(panel_config.readAll(), 0)) { - load_default = false; - } - panel_config.close(); - } - }*/ - - if (load_default) { - addDockWidget(Qt::TopDockWidgetArea, panel_project); - addDockWidget(Qt::TopDockWidgetArea, panel_footage_viewer); - tabifyDockWidget(panel_footage_viewer, panel_effect_controls); - panel_footage_viewer->raise(); - addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer); - addDockWidget(Qt::BottomDockWidgetArea, panel_timeline); - panel_graph_editor->setFloating(true); + addDockWidget(Qt::TopDockWidgetArea, panel_project); + addDockWidget(Qt::TopDockWidgetArea, panel_footage_viewer); + tabifyDockWidget(panel_footage_viewer, panel_effect_controls); + panel_footage_viewer->raise(); + addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer); + addDockWidget(Qt::BottomDockWidgetArea, panel_timeline); + panel_graph_editor->setFloating(true); // workaround for strange Qt dock bug (see https://bugreports.qt.io/browse/QTBUG-65592) #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) - resizeDocks({panel_project}, {40}, Qt::Horizontal); + resizeDocks({panel_project}, {40}, Qt::Horizontal); #endif + + // load panels from file + if (!reset) { + QFile panel_config(get_data_path() + "/layout"); + if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { + restoreState(panel_config.readAll(), 0); + panel_config.close(); + } } layout()->update(); @@ -662,6 +657,7 @@ void MainWindow::windowMenu_About_To_Be_Shown() { ui->actionTimeline->setChecked(panel_timeline->isVisible()); ui->actionViewer->setChecked(panel_sequence_viewer->isVisible()); ui->actionFootage_Viewer->setChecked(panel_footage_viewer->isVisible()); + ui->actionGraph_Editor->setChecked(panel_graph_editor->isVisible()); } void MainWindow::viewMenu_About_To_Be_Shown() { @@ -1064,3 +1060,7 @@ void MainWindow::on_actionHand_Tool_triggered() { || panel_sequence_viewer->is_focused()) panel_timeline->ui->toolHandButton->click(); } + +void MainWindow::on_actionGraph_Editor_triggered() { + panel_graph_editor->setVisible(!panel_graph_editor->isVisible()); +} diff --git a/mainwindow.h b/mainwindow.h index a1b380542..76d70263a 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -215,6 +215,8 @@ private slots: void on_actionHand_Tool_triggered(); + void on_actionGraph_Editor_triggered(); + private: Ui::MainWindow *ui; void setup_layout(bool reset); diff --git a/mainwindow.ui b/mainwindow.ui index fe24d5e23..1daae80cf 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -111,6 +111,7 @@ + @@ -957,6 +958,14 @@ H + + + true + + + Graph Editor + + diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index ef2f1fdd2..4128b2474 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -5,9 +5,11 @@ #include #include "ui/graphview.h" +#include "ui/keyframenavigator.h" GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent) { setWindowTitle("Graph Editor"); + resize(720, 480); QWidget* central_widget = new QWidget(); setWidget(central_widget); @@ -15,17 +17,32 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); central_widget->setLayout(layout); + QWidget* tool_widget = new QWidget(); + tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); QHBoxLayout* tools = new QHBoxLayout(); + tool_widget->setLayout(tools); + + KeyframeNavigator* keyframe_nav = new KeyframeNavigator(); + tools->addWidget(keyframe_nav); + tools->addStretch(); + tools->addWidget(new QPushButton("HECK!")); - layout->addLayout(tools); + tools->addStretch(); + + tools->addWidget(new QPushButton("Hand")); + + layout->addWidget(tool_widget); view = new GraphView(); layout->addWidget(view); + QWidget* value_widget = new QWidget(); + value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); QHBoxLayout* values = new QHBoxLayout(); + value_widget->setLayout(values); values->addStretch(); values->addWidget(new QLabel("1920.0")); values->addWidget(new QLabel("1080.0")); values->addStretch(); - layout->addLayout(values); + layout->addWidget(value_widget); } diff --git a/panels/panels.cpp b/panels/panels.cpp index fcc9e0008..be40e402b 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -143,11 +143,17 @@ QDockWidget *get_focused_panel() { void alloc_panels(QWidget* parent) { // TODO maybe replace these with non-pointers later on? panel_sequence_viewer = new Viewer(parent); + panel_sequence_viewer->setObjectName("seq_viewer"); panel_footage_viewer = new Viewer(parent); + panel_footage_viewer->setObjectName("footage_viewer"); panel_project = new Project(parent); + panel_project->setObjectName("proj_root"); panel_effect_controls = new EffectControls(parent); + panel_effect_controls->setObjectName("fx_controls"); panel_timeline = new Timeline(parent); + panel_timeline->setObjectName("timeline"); panel_graph_editor = new GraphEditor(parent); + panel_graph_editor->setObjectName("graph_editor"); } void free_panels() { diff --git a/playback/audio.cpp b/playback/audio.cpp index 8d04cedce..9f80c7c6c 100644 --- a/playback/audio.cpp +++ b/playback/audio.cpp @@ -99,8 +99,10 @@ void stop_audio() { } void clear_audio_ibuffer() { + if (audio_thread != NULL) audio_thread->lock.lock(); memset(audio_ibuffer, 0, audio_ibuffer_size); audio_ibuffer_read = 0; + if (audio_thread != NULL) audio_thread->lock.unlock(); } int get_buffer_offset_from_frame(double framerate, long frame) { @@ -130,7 +132,7 @@ void AudioSenderThread::run() { // start data loop send_audio_to_output(0, audio_ibuffer_size); - lock.lock(); + lock.lock(); while (true) { cond.wait(&lock); if (close) { @@ -149,7 +151,7 @@ void AudioSenderThread::run() { audio_scrub = false; } - } + } lock.unlock(); } diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 405663889..7b1bad15f 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -9,5 +9,8 @@ void GraphView::paintEvent(QPaintEvent *event) { p.setPen(Qt::white); - p.drawRect(rect()); + QRect border = rect(); + border.setWidth(border.width()-1); + border.setHeight(border.height()-1); + p.drawRect(border); } From e101739214607fbb5e3ffcd5f751f34c4b792e9c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 28 Dec 2018 22:06:38 +1100 Subject: [PATCH 04/65] big progress on graph editor --- olive.pro | 10 +- panels/effectcontrols.cpp | 4 + panels/grapheditor.cpp | 190 ++++++++++++++++++++++++++++++---- panels/grapheditor.h | 20 +++- panels/panels.cpp | 1 + project/effect.cpp | 2 + project/effectfield.cpp | 1 + project/effectfield.h | 1 + project/effectrow.cpp | 24 ++++- project/effectrow.h | 14 ++- project/keyframe.cpp | 2 + project/keyframe.h | 17 ++++ ui/clickablelabel.cpp | 13 +++ ui/clickablelabel.h | 16 +++ ui/graphview.cpp | 207 ++++++++++++++++++++++++++++++++++++-- ui/graphview.h | 30 ++++++ ui/keyframedrawing.cpp | 26 +++++ ui/keyframedrawing.h | 10 ++ ui/keyframenavigator.cpp | 27 +++-- ui/keyframenavigator.h | 4 +- ui/keyframeview.cpp | 53 ++++------ ui/keyframeview.h | 5 +- ui/labelslider.cpp | 10 +- ui/labelslider.h | 2 + ui/timelineheader.h | 2 +- 25 files changed, 607 insertions(+), 84 deletions(-) create mode 100644 project/keyframe.cpp create mode 100644 project/keyframe.h create mode 100644 ui/clickablelabel.cpp create mode 100644 ui/clickablelabel.h create mode 100644 ui/keyframedrawing.cpp create mode 100644 ui/keyframedrawing.h diff --git a/olive.pro b/olive.pro index eab6b3b19..3cfcd8ff8 100644 --- a/olive.pro +++ b/olive.pro @@ -107,7 +107,10 @@ SOURCES += \ project/sourcescommon.cpp \ ui/keyframenavigator.cpp \ panels/grapheditor.cpp \ - ui/graphview.cpp + ui/graphview.cpp \ + ui/keyframedrawing.cpp \ + ui/clickablelabel.cpp \ + project/keyframe.cpp HEADERS += \ mainwindow.h \ @@ -189,7 +192,10 @@ HEADERS += \ project/sourcescommon.h \ ui/keyframenavigator.h \ panels/grapheditor.h \ - ui/graphview.h + ui/graphview.h \ + ui/keyframedrawing.h \ + ui/clickablelabel.h \ + project/keyframe.h FORMS += \ mainwindow.ui \ diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 4809aa482..aa576ec64 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -16,6 +16,7 @@ #include "panels/project.h" #include "panels/timeline.h" #include "panels/viewer.h" +#include "panels/grapheditor.h" #include "ui/viewerwidget.h" #include "io/clipboard.h" #include "debug.h" @@ -204,6 +205,9 @@ void EffectControls::clear_effects(bool clear_cache) { // clear existing clips deselect_all_effects(NULL); + // clear graph editor + if (panel_graph_editor != NULL) panel_graph_editor->set_row(NULL); + QVBoxLayout* video_layout = static_cast(ui->video_effect_area->layout()); QVBoxLayout* audio_layout = static_cast(ui->audio_effect_area->layout()); QLayoutItem* item; diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 4128b2474..71dd5142f 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -3,46 +3,200 @@ #include #include #include +#include -#include "ui/graphview.h" #include "ui/keyframenavigator.h" +#include "ui/timelineheader.h" +#include "ui/timelinetools.h" +#include "ui/labelslider.h" +#include "ui/graphview.h" +#include "project/effect.h" +#include "project/effectfield.h" +#include "project/effectrow.h" +#include "project/clip.h" +#include "panels.h" +#include "debug.h" -GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent) { +GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { setWindowTitle("Graph Editor"); resize(720, 480); - QWidget* central_widget = new QWidget(); - setWidget(central_widget); - + QWidget* main_widget = new QWidget(); + setWidget(main_widget); QVBoxLayout* layout = new QVBoxLayout(); - central_widget->setLayout(layout); + main_widget->setLayout(layout); QWidget* tool_widget = new QWidget(); tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); QHBoxLayout* tools = new QHBoxLayout(); tool_widget->setLayout(tools); - KeyframeNavigator* keyframe_nav = new KeyframeNavigator(); - tools->addWidget(keyframe_nav); - tools->addStretch(); + QWidget* left_tool_widget = new QWidget(); + QHBoxLayout* left_tool_layout = new QHBoxLayout(); + left_tool_layout->setSpacing(0); + left_tool_layout->setMargin(0); + left_tool_widget->setLayout(left_tool_layout); + tools->addWidget(left_tool_widget); + QWidget* center_tool_widget = new QWidget(); + QHBoxLayout* center_tool_layout = new QHBoxLayout(); + center_tool_layout->setSpacing(0); + center_tool_layout->setMargin(0); + center_tool_widget->setLayout(center_tool_layout); + tools->addWidget(center_tool_widget); + QWidget* right_tool_widget = new QWidget(); + QHBoxLayout* right_tool_layout = new QHBoxLayout(); + right_tool_layout->setSpacing(0); + right_tool_layout->setMargin(0); + right_tool_widget->setLayout(right_tool_layout); + tools->addWidget(right_tool_widget); - tools->addWidget(new QPushButton("HECK!")); - tools->addStretch(); + keyframe_nav = new KeyframeNavigator(); + keyframe_nav->enable_keyframes(true); + keyframe_nav->enable_keyframe_toggle(false); + left_tool_layout->addWidget(keyframe_nav); + left_tool_layout->addStretch(); - tools->addWidget(new QPushButton("Hand")); + QPushButton* linear_button = new QPushButton("Linear"); + linear_button->setCheckable(true); + QPushButton* bezier_button = new QPushButton("Bezier"); + bezier_button->setCheckable(true); + QPushButton* hold_button = new QPushButton("Hold"); + hold_button->setCheckable(true); + + center_tool_layout->addStretch(); + center_tool_layout->addWidget(linear_button); + center_tool_layout->addWidget(bezier_button); + center_tool_layout->addWidget(hold_button); + + /*QPushButton* tool_arrow_button = new QPushButton(); + tool_arrow_button->setCheckable(true); + tool_arrow_button->setIcon(QIcon(":/icons/arrow.png")); + tool_arrow_button->setProperty("tool", TIMELINE_TOOL_POINTER); + tool_buttons.append(tool_arrow_button); + QPushButton* tool_hand_button = new QPushButton(); + tool_hand_button->setCheckable(true); + tool_hand_button->setIcon(QIcon(":/icons/hand.png")); + tool_hand_button->setProperty("tool", TIMELINE_TOOL_HAND); + tool_buttons.append(tool_hand_button); + QPushButton* tool_zoom_button = new QPushButton(); + tool_zoom_button->setCheckable(true); + tool_zoom_button->setIcon(QIcon(":/icons/zoomin.png")); + tool_zoom_button->setProperty("tool", TIMELINE_TOOL_ZOOM); + tool_buttons.append(tool_zoom_button); + + right_tool_layout->addStretch(); + for (int i=0;iaddWidget(tool_buttons.at(i)); + }*/ layout->addWidget(tool_widget); - view = new GraphView(); - layout->addWidget(view); + QWidget* central_widget = new QWidget(); + QVBoxLayout* central_layout = new QVBoxLayout(); + central_widget->setLayout(central_layout); + central_layout->setSpacing(0); + central_layout->setMargin(0); + header = new TimelineHeader(); + header->viewer = panel_sequence_viewer; + central_layout->addWidget(header); + view = new GraphView(); + central_layout->addWidget(view); + + layout->addWidget(central_widget); QWidget* value_widget = new QWidget(); value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - QHBoxLayout* values = new QHBoxLayout(); + QHBoxLayout* values = new QHBoxLayout(); value_widget->setLayout(values); values->addStretch(); - values->addWidget(new QLabel("1920.0")); - values->addWidget(new QLabel("1080.0")); + + current_row_desc = new QLabel(); + values->addWidget(current_row_desc); + + QWidget* central_value_widget = new QWidget(); + value_layout = new QHBoxLayout(); + value_layout->setMargin(0); + central_value_widget->setLayout(value_layout); + values->addWidget(central_value_widget); + values->addStretch(); - layout->addWidget(value_widget); + layout->addWidget(value_widget); + + connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double))); + connect(view, SIGNAL(x_scroll_changed(int)), header, SLOT(set_scroll(int))); +} + +void GraphEditor::update_panel() { + if (isVisible()) { + if (row != NULL) { + int slider_index = 0; + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + slider_proxies.at(slider_index)->set_value(row->field(i)->get_current_data().toDouble(), false); + slider_index++; + } + } + } + + header->update(); + view->update(); + } +} + +void GraphEditor::set_row(EffectRow *r) { + for (int i=0;iisKeyframing()) { + for (int i=0;ifieldCount();i++) { + EffectField* field = r->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + LabelSlider* slider = new LabelSlider(); + slider->set_color(get_curve_color(i, r->fieldCount()).name()); + connect(slider, SIGNAL(valueChanged()), this, SLOT(passthrough_slider_value())); + + slider_proxies.append(slider); + value_layout->addWidget(slider); + + slider_proxy_sources.append(static_cast(field->ui_element)); + + found_vals = true; + } + } + } + + if (found_vals) { + row = r; + current_row_desc->setText(row->parent_effect->parent_clip->name + " :: " + row->parent_effect->meta->name + " :: " + row->get_name()); + + connect(keyframe_nav, SIGNAL(goto_previous_key()), row, SLOT(goto_previous_key())); + connect(keyframe_nav, SIGNAL(toggle_key()), row, SLOT(toggle_key())); + connect(keyframe_nav, SIGNAL(goto_next_key()), row, SLOT(goto_next_key())); + } else { + row = NULL; + current_row_desc->setText(0); + } + view->set_row(row); + update_panel(); +} + +void GraphEditor::passthrough_slider_value() { + for (int i=0;iset_value(slider_proxies.at(i)->value(), true); + } + } } diff --git a/panels/grapheditor.h b/panels/grapheditor.h index 5e4a919cf..87f8e0d14 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -4,13 +4,31 @@ #include class GraphView; +class TimelineHeader; +class QPushButton; +class EffectRow; +class QHBoxLayout; +class LabelSlider; +class QLabel; +class KeyframeNavigator; class GraphEditor : public QDockWidget { Q_OBJECT public: GraphEditor(QWidget* parent = 0); + void update_panel(); + void set_row(EffectRow* r); private: - GraphView* view; + GraphView* view; + TimelineHeader* header; + QHBoxLayout* value_layout; + QVector slider_proxies; + QVector slider_proxy_sources; + QLabel* current_row_desc; + EffectRow* row; + KeyframeNavigator* keyframe_nav; +private slots: + void passthrough_slider_value(); }; #endif // GRAPHEDITOR_H diff --git a/panels/panels.cpp b/panels/panels.cpp index be40e402b..35bed658a 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -107,6 +107,7 @@ void update_ui(bool modified) { panel_effect_controls->update_keyframes(); panel_timeline->repaint_timeline(); panel_sequence_viewer->update_viewer(); + panel_graph_editor->update_panel(); } QDockWidget *get_focused_panel() { diff --git a/project/effect.cpp b/project/effect.cpp index 153987319..8c049801e 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -10,6 +10,7 @@ #include "project/clip.h" #include "panels/timeline.h" #include "panels/effectcontrols.h" +#include "panels/grapheditor.h" #include "ui/checkboxex.h" #include "debug.h" #include "io/path.h" @@ -489,6 +490,7 @@ void Effect::refresh() {} void Effect::field_changed() { panel_sequence_viewer->viewer_widget->update(); + panel_graph_editor->update_panel(); } void Effect::show_context_menu(const QPoint& pos) { diff --git a/project/effectfield.cpp b/project/effectfield.cpp index 2caaf2991..f465e5e27 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -28,6 +28,7 @@ EffectField::EffectField(EffectRow *parent, int t, const QString &i) : LabelSlider* ls = new LabelSlider(); ui_element = ls; connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change())); + connect(ls, SIGNAL(clicked()), this, SIGNAL(clicked())); } break; case EFFECT_FIELD_COLOR: diff --git a/project/effectfield.h b/project/effectfield.h index f2b99002e..485b43287 100644 --- a/project/effectfield.h +++ b/project/effectfield.h @@ -69,6 +69,7 @@ private slots: signals: void changed(); void toggled(bool); + void clicked(); }; #endif // EFFECTFIELD_H diff --git a/project/effectrow.cpp b/project/effectrow.cpp index 1cfd81cb9..28d4cfaeb 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -1,6 +1,5 @@ #include "effectrow.h" -#include #include #include #include @@ -11,9 +10,11 @@ #include "panels/panels.h" #include "panels/effectcontrols.h" #include "panels/viewer.h" +#include "panels/grapheditor.h" #include "effect.h" #include "ui/viewerwidget.h" #include "ui/keyframenavigator.h" +#include "ui/clickablelabel.h" EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QString &n, int row) : parent_effect(parent), @@ -24,15 +25,19 @@ EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QSt ui_row(row), just_made_unsafe_keyframe(false) { - label = new QLabel(name); + label = new ClickableLabel(name); + ui->addWidget(label, row, 0); if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { + connect(label, SIGNAL(clicked()), this, SLOT(focus_row())); + keyframe_nav = new KeyframeNavigator(); connect(keyframe_nav, SIGNAL(goto_previous_key()), this, SLOT(goto_previous_key())); connect(keyframe_nav, SIGNAL(toggle_key()), this, SLOT(toggle_key())); connect(keyframe_nav, SIGNAL(goto_next_key()), this, SLOT(goto_next_key())); - connect(keyframe_nav, SIGNAL(set_keyframe_enabled(bool)), this, SLOT(set_keyframe_enabled(bool))); + connect(keyframe_nav, SIGNAL(keyframe_enabled_changed(bool)), this, SLOT(set_keyframe_enabled(bool))); + connect(keyframe_nav, SIGNAL(clicked()), this, SLOT(focus_row())); ui->addWidget(keyframe_nav, row, 6); } } @@ -114,11 +119,16 @@ void EffectRow::goto_next_key() { key = qMin(comp, key); } } - if (key != LONG_MAX) panel_sequence_viewer->seek(key); + if (key != LONG_MAX) panel_sequence_viewer->seek(key); +} + +void EffectRow::focus_row() { + panel_graph_editor->set_row(this); } EffectField* EffectRow::add_field(int type, const QString& id, int colspan) { EffectField* field = new EffectField(this, type, id); + if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) connect(field, SIGNAL(clicked()), this, SLOT(focus_row())); fields.append(field); QWidget* element = field->get_ui_element(); ui->addWidget(element, ui_row, fields.size(), 1, colspan); @@ -162,7 +172,11 @@ void EffectRow::delete_keyframe_at_time(KeyframeDelete* kd, long time) { delete_keyframe(kd, i); break; } - } + } +} + +const QString &EffectRow::get_name() { + return name; } void EffectRow::delete_keyframe(KeyframeDelete* kd, int index) { diff --git a/project/effectrow.h b/project/effectrow.h index 943ba709f..a18cd56af 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -13,6 +13,7 @@ class QPushButton; class ComboAction; class QHBoxLayout; class KeyframeNavigator; +class ClickableLabel; class EffectRow : public QObject { Q_OBJECT @@ -25,20 +26,23 @@ public: void set_keyframe_now(ComboAction *ca); void delete_keyframe(KeyframeDelete *kd, int index); void delete_keyframe_at_time(KeyframeDelete* kd, long time); - QLabel* label; + ClickableLabel* label; Effect* parent_effect; bool savable; + const QString& get_name(); bool isKeyframing(); void setKeyframing(bool); QVector keyframe_times; QVector keyframe_types; +public slots: + void goto_previous_key(); + void toggle_key(); + void goto_next_key(); + void focus_row(); private slots: - void set_keyframe_enabled(bool); - void goto_previous_key(); - void toggle_key(); - void goto_next_key(); + void set_keyframe_enabled(bool); private: bool keyframing; QGridLayout* ui; diff --git a/project/keyframe.cpp b/project/keyframe.cpp new file mode 100644 index 000000000..8068d5129 --- /dev/null +++ b/project/keyframe.cpp @@ -0,0 +1,2 @@ +#include "keyframe.h" + diff --git a/project/keyframe.h b/project/keyframe.h new file mode 100644 index 000000000..7cad20bfc --- /dev/null +++ b/project/keyframe.h @@ -0,0 +1,17 @@ +#ifndef KEYFRAME_H +#define KEYFRAME_H + + + +#include +#include + +class KeyframeData +{ +public: + QVariant data; + QPoint handle_pre; + QPoint handle_post; +}; + +#endif // KEYFRAME_H diff --git a/ui/clickablelabel.cpp b/ui/clickablelabel.cpp new file mode 100644 index 000000000..b5afb2327 --- /dev/null +++ b/ui/clickablelabel.cpp @@ -0,0 +1,13 @@ +#include "clickablelabel.h" + +ClickableLabel::ClickableLabel(QWidget *parent, Qt::WindowFlags f) : + QLabel(parent, f) +{} + +ClickableLabel::ClickableLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) : + QLabel(text, parent, f) +{} + +void ClickableLabel::mousePressEvent(QMouseEvent *) { + emit clicked(); +} diff --git a/ui/clickablelabel.h b/ui/clickablelabel.h new file mode 100644 index 000000000..785c4c7c6 --- /dev/null +++ b/ui/clickablelabel.h @@ -0,0 +1,16 @@ +#ifndef CLICKABLELABEL_H +#define CLICKABLELABEL_H + +#include + +class ClickableLabel : public QLabel { + Q_OBJECT +public: + ClickableLabel(QWidget * parent = 0, Qt::WindowFlags f = 0); + ClickableLabel(const QString & text, QWidget * parent = 0, Qt::WindowFlags f = 0); + void mousePressEvent(QMouseEvent *ev); +signals: + void clicked(); +}; + +#endif // CLICKABLELABEL_H diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 7b1bad15f..194139c7d 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -1,16 +1,209 @@ #include "graphview.h" #include +#include -GraphView::GraphView(QWidget* parent) {} +#include "panels/panels.h" +#include "panels/timeline.h" +#include "panels/viewer.h" +#include "project/sequence.h" +#include "project/effectrow.h" +#include "project/effectfield.h" +#include "ui/keyframedrawing.h" + +#include "debug.h" + +#define GRAPH_ZOOM_SPEED 0.05 +#define GRAPH_SIZE 100 + +QColor get_curve_color(int index, int length) { + QColor c; + int hue = (double(index)/double(length))*255; + c.setHsv(hue, 255, 255); + return c; +} + +GraphView::GraphView(QWidget* parent) : + QWidget(parent), + x_scroll(0), + y_scroll(0), + mousedown(false), + zoom(1.0), + row(NULL) +{ + setMouseTracking(true); +} void GraphView::paintEvent(QPaintEvent *event) { - QPainter p(this); + QPainter p(this); - p.setPen(Qt::white); + if (panel_sequence_viewer->seq != NULL) { + // draw lines + //int graph_size = GRAPH_SIZE*zoom; + bool draw_text = true;//(fontMetrics().height() < graph_size && fontMetrics().width("0000") < graph_size); - QRect border = rect(); - border.setWidth(border.width()-1); - border.setHeight(border.height()-1); - p.drawRect(border); + p.setPen(Qt::gray); + int i = 0; + while (true) { + int line_x = (i*GRAPH_SIZE*zoom) - x_scroll; + if (line_x >= width()) { + break; + } + if (line_x >= 0) { + if (line_x > 0) p.drawLine(line_x, 0, line_x, height()); + if (draw_text) p.drawText(QRect(line_x, height()-50, 50, 50), Qt::AlignBottom | Qt::AlignLeft, QString::number(i*GRAPH_SIZE)); + } + i++; + } + i = 0; + while (true) { + int line_y = height() - (i*GRAPH_SIZE*zoom) + y_scroll; + if (line_y <= 0) { + break; + } + if (line_y <= height()) { + if (line_y < height()) p.drawLine(0, line_y, width(), line_y); + if (draw_text) p.drawText(QRect(0, line_y-50, 50, 50), Qt::AlignBottom | Qt::AlignLeft, QString::number(i*GRAPH_SIZE)); + } + i++; + } + + // draw keyframes + if (row != NULL) { + QPen line_pen; + line_pen.setWidth(2); + + // sort keyframes by time + QVector sorted_keys; + for (int i=0;ikeyframe_times.size();i++) { + bool inserted = false; + for (int j=0;jkeyframe_times.at(sorted_keys.at(j)) > row->keyframe_times.at(i)) { + sorted_keys.insert(j, i); + inserted = true; + break; + } + } + if (!inserted) { + sorted_keys.append(i); + } + } + + int last_key_x, last_key_y; + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + for (int j=0;jkeyframe_times.at(key_index)); + int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); + + line_pen.setColor(get_curve_color(i, row->fieldCount())); + p.setPen(line_pen); + if (key_index == 0) { + p.drawLine(0, key_y, key_x, key_y); + } else { + p.drawLine(last_key_x, last_key_y, key_x, key_y); + } + last_key_x = key_x; + last_key_y = key_y; + } + for (int j=0;jkeyframe_times.at(key_index)); + int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); + + draw_keyframe(p, row->keyframe_types.at(key_index), key_x, key_y, false); + } + p.setBrush(Qt::NoBrush); + } + } + } + + // draw playhead + p.setPen(Qt::red); + int playhead_x = get_screen_x(panel_sequence_viewer->seq->playhead); + p.drawLine(playhead_x, 0, playhead_x, height()); + } + + p.setPen(Qt::white); + + QRect border = rect(); + border.setWidth(border.width()-1); + border.setHeight(border.height()-1); + p.drawRect(border); +} + +void GraphView::mousePressEvent(QMouseEvent *event) { + mousedown = true; + start_x = event->pos().x(); + start_y = event->pos().y(); +} + +void GraphView::mouseMoveEvent(QMouseEvent *event) { + if (mousedown) { + if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { + set_scroll_x(x_scroll + start_x - event->pos().x()); + set_scroll_y(y_scroll + event->pos().y() - start_y); + start_x = event->pos().x(); + start_y = event->pos().y(); + update(); + } + } +} + +void GraphView::mouseReleaseEvent(QMouseEvent *event) { + mousedown = false; +} + +void GraphView::wheelEvent(QWheelEvent *event) { + bool redraw = false; + bool shift = (event->modifiers() & Qt::ShiftModifier); // scroll instead of zoom + bool alt = (event->modifiers() & Qt::AltModifier); // horiz scroll instead of vert scroll + + if (shift) { + // scroll + set_scroll_x(x_scroll + event->angleDelta().x()/10); + set_scroll_y(y_scroll + event->angleDelta().y()/10); + redraw = true; + } else { + // set zoom + if (event->angleDelta().y() != 0) { + double zoom_diff = (GRAPH_ZOOM_SPEED*zoom); + if (event->angleDelta().y() < 0) zoom_diff = -zoom_diff; + zoom += zoom_diff; + emit zoom_changed(zoom); + redraw = true; + } + } + + if (redraw) { + update(); + } +} + +void GraphView::set_row(EffectRow *r) { + row = r; + update(); +} + +void GraphView::set_scroll_x(int s) { + x_scroll = s;//qMax(0, s); + dout << x_scroll; + emit x_scroll_changed(x_scroll); +} + +void GraphView::set_scroll_y(int s) { + y_scroll = s;//qMax(0, s); + emit y_scroll_changed(y_scroll); +} + +int GraphView::get_screen_x(double d) { + return (d*zoom) - x_scroll; +} + +int GraphView::get_screen_y(double d) { + return height() + y_scroll - d*zoom; } diff --git a/ui/graphview.h b/ui/graphview.h index b28d80bf9..ca1404c94 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -3,11 +3,41 @@ #include +class EffectRow; + +QColor get_curve_color(int index, int length); + class GraphView : public QWidget { + Q_OBJECT public: GraphView(QWidget* parent = 0); void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void wheelEvent(QWheelEvent *event); + + void set_row(EffectRow* r); +signals: + void x_scroll_changed(int); + void y_scroll_changed(int); + void zoom_changed(double); +private: + int x_scroll; + int y_scroll; + bool mousedown; + int start_x; + int start_y; + double zoom; + + void set_scroll_x(int s); + void set_scroll_y(int s); + + int get_screen_x(double); + int get_screen_y(double); + + EffectRow* row; }; #endif // GRAPHVIEW_H diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp new file mode 100644 index 000000000..693a4e299 --- /dev/null +++ b/ui/keyframedrawing.cpp @@ -0,0 +1,26 @@ +#include "keyframedrawing.h" + +#include "project/effect.h" + +#define KEYFRAME_POINT_COUNT 4 + +void draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { + int color = (darker) ? 100 : 160; + p.setPen(QColor(0, 0, 0)); + p.setBrush(QColor(color, color, color)); + + switch (type) { + case KEYFRAME_TYPE_LINEAR: + { + QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)}; + p.drawPolygon(points, KEYFRAME_POINT_COUNT); + } + break; + case KEYFRAME_TYPE_BEZIER: + p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE); + break; + case KEYFRAME_TYPE_HOLD: + p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2)); + break; + } +} diff --git a/ui/keyframedrawing.h b/ui/keyframedrawing.h new file mode 100644 index 000000000..c09ae2626 --- /dev/null +++ b/ui/keyframedrawing.h @@ -0,0 +1,10 @@ +#ifndef KEYFRAMEDRAWING_H +#define KEYFRAMEDRAWING_H + +#include + +#define KEYFRAME_SIZE 6 + +void draw_keyframe(QPainter &p, int type, int x, int y, bool darker); + +#endif // KEYFRAMEDRAWING_H diff --git a/ui/keyframenavigator.cpp b/ui/keyframenavigator.cpp index a7022fe45..f5495a135 100644 --- a/ui/keyframenavigator.cpp +++ b/ui/keyframenavigator.cpp @@ -2,15 +2,22 @@ #include #include +#include +#include KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) { - QSize icon_size(12, 12); - QSize button_size(20, 20); + int icon_size_val = 8*QApplication::desktop()->devicePixelRatio(); + int clock_size_val = 12*QApplication::desktop()->devicePixelRatio(); + int button_size_val = 20*QApplication::desktop()->devicePixelRatio(); + + QSize icon_size(icon_size_val, icon_size_val); + QSize clock_size(clock_size_val, clock_size_val); + QSize button_size(button_size_val, button_size_val); key_controls = new QHBoxLayout(); key_controls->setSpacing(0); key_controls->setMargin(0); - key_controls->addStretch(); + //key_controls->addStretch(); setLayout(key_controls); @@ -21,6 +28,7 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) { left_key_nav->setVisible(false); key_controls->addWidget(left_key_nav); connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_previous_key())); + connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked())); key_addremove = new QPushButton(); key_addremove->setIcon(QIcon(":/icons/diamond.png")); @@ -29,6 +37,7 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) { key_addremove->setVisible(false); key_controls->addWidget(key_addremove); connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(toggle_key())); + connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(clicked())); right_key_nav = new QPushButton(); right_key_nav->setIcon(QIcon(":/icons/tri-right.png")); @@ -37,20 +46,26 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) { right_key_nav->setVisible(false); key_controls->addWidget(right_key_nav); connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_next_key())); + connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked())); keyframe_enable = new QPushButton(QIcon(":/icons/clock.png"), ""); keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); keyframe_enable->setMaximumSize(button_size); - keyframe_enable->setIconSize(icon_size); + keyframe_enable->setIconSize(clock_size); keyframe_enable->setCheckable(true); keyframe_enable->setToolTip("Enable Keyframes"); - connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(set_keyframe_enabled(bool))); + connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(keyframe_enabled_changed(bool))); connect(keyframe_enable, SIGNAL(toggled(bool)), this, SLOT(keyframe_ui_enabled(bool))); + connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(clicked())); key_controls->addWidget(keyframe_enable); } void KeyframeNavigator::enable_keyframes(bool b) { - keyframe_enable->setChecked(b); + keyframe_enable->setChecked(b); +} + +void KeyframeNavigator::enable_keyframe_toggle(bool b) { + keyframe_enable->setVisible(b); } void KeyframeNavigator::keyframe_ui_enabled(bool enabled) { diff --git a/ui/keyframenavigator.h b/ui/keyframenavigator.h index d41a7ac0d..6b6eaee85 100644 --- a/ui/keyframenavigator.h +++ b/ui/keyframenavigator.h @@ -12,11 +12,13 @@ class KeyframeNavigator : public QWidget public: KeyframeNavigator(QWidget* parent = 0); void enable_keyframes(bool); + void enable_keyframe_toggle(bool); signals: void goto_previous_key(); void toggle_key(); void goto_next_key(); - void set_keyframe_enabled(bool); + void keyframe_enabled_changed(bool); + void clicked(); private slots: void keyframe_ui_enabled(bool); private: diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 431d9ab03..0da66dada 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -12,16 +12,14 @@ #include "ui/viewerwidget.h" #include "project/sequence.h" #include "panels/grapheditor.h" +#include "ui/keyframedrawing.h" #include "ui_effectcontrols.h" +#include "ui/clickablelabel.h" -#include #include #include #include -#define KEYFRAME_SIZE 6 -#define KEYFRAME_POINT_COUNT 4 - long KeyframeView::adjust_row_keyframe(EffectRow* row, long time) { return time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); } @@ -36,7 +34,7 @@ KeyframeView::KeyframeView(QWidget *parent) : select_rect(false), x_scroll(0), y_scroll(0), - scroll_drag(false) + scroll_drag(false) { setFocusPolicy(Qt::ClickFocus); setMouseTracking(true); @@ -73,7 +71,7 @@ void KeyframeView::menu_set_key_type(QAction* a) { ca->append(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt())); } undo_stack.push(ca); - update(); + update_keys(); } } @@ -101,7 +99,7 @@ void KeyframeView::paintEvent(QPaintEvent*) { for (int j=0;jrow_count();j++) { EffectRow* row = e->row(j); - QLabel* label = row->label; + ClickableLabel* label = row->label; QWidget* contents = e->container->contents; int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height()/* - y_scroll*/; @@ -153,6 +151,11 @@ bool KeyframeView::keyframeIsSelected(EffectRow *row, int keyframe) { return false; } +void KeyframeView::update_keys() { +// panel_graph_editor->update_panel(); + update(); +} + void KeyframeView::delete_selected_keyframes() { KeyframeDelete* kd = new KeyframeDelete(); bool del = false; @@ -165,7 +168,7 @@ void KeyframeView::delete_selected_keyframes() { selected_keyframes.clear(); selected_rows.clear(); - update(); + update_keys(); panel_sequence_viewer->viewer_widget->update(); } else { delete kd; @@ -174,33 +177,12 @@ void KeyframeView::delete_selected_keyframes() { void KeyframeView::set_x_scroll(int s) { x_scroll = s; - update(); + update_keys(); } void KeyframeView::set_y_scroll(int s) { y_scroll = s; - update(); -} - -void KeyframeView::draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { - int color = (darker) ? 100 : 160; - p.setPen(QColor(0, 0, 0)); - p.setBrush(QColor(color, color, color)); - - switch (type) { - case KEYFRAME_TYPE_LINEAR: - { - QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)}; - p.drawPolygon(points, KEYFRAME_POINT_COUNT); - } - break; - case KEYFRAME_TYPE_BEZIER: - p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE); - break; - case KEYFRAME_TYPE_HOLD: - p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2)); - break; - } + update_keys(); } void KeyframeView::resize_move(double d) { @@ -227,6 +209,9 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { for (int i=0;i rowY.at(i)-KEYFRAME_SIZE-KEYFRAME_SIZE && mouse_y < rowY.at(i)+KEYFRAME_SIZE+KEYFRAME_SIZE) { EffectRow* row = rows.at(i); + + row->focus_row(); + for (int j=0;jkeyframe_times.size();j++) { long eval_keyframe_time = row->keyframe_times.at(j)-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { @@ -259,7 +244,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { keys_selected = true; } - update(); + update_keys(); if (event->button() == Qt::LeftButton) { mousedown = true; @@ -345,7 +330,7 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { select_rect = true; } - update(); + update_keys(); } } @@ -363,5 +348,5 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent*) { mousedown = false; scroll_drag = false; panel_timeline->snapped = false; - update(); + update_ui(false); } diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 6c3a72f31..c81186683 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -23,7 +23,7 @@ public: public slots: void set_x_scroll(int); void set_y_scroll(int); - void resize_move(double d); + void resize_move(double d); private: long adjust_row_keyframe(EffectRow* row, long time); QVector selected_rows; @@ -35,7 +35,6 @@ private: void mouseMoveEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent *event); void paintEvent(QPaintEvent *event); - void draw_keyframe(QPainter& p, int type, int x, int y, bool darker); bool mousedown; bool dragging; bool keys_selected; @@ -53,6 +52,8 @@ private: int x_scroll; int y_scroll; + + void update_keys(); private slots: void show_context_menu(const QPoint& pos); void menu_set_key_type(QAction*); diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index 357a891db..7395ad752 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -16,7 +16,7 @@ LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) { drag_proc = false; min_enabled = false; max_enabled = false; - setStyleSheet("QLabel{color:#ffc000;text-decoration:underline;}QLabel:disabled{color:#808080;}"); + set_color(); setCursor(Qt::SizeHorCursor); internal_value = -1; set = false; @@ -75,7 +75,12 @@ double LabelSlider::getPreviousValue() { } void LabelSlider::set_previous_value() { - previous_value = internal_value; + previous_value = internal_value; +} + +void LabelSlider::set_color(QString c) { + if (c.isEmpty()) c = "#ffc000"; + setStyleSheet("QLabel{color:" + c + ";text-decoration:underline;}QLabel:disabled{color:#808080;}"); } double LabelSlider::value() { @@ -115,6 +120,7 @@ void LabelSlider::mousePressEvent(QMouseEvent *ev) { drag_start_x = cursor().pos().x(); drag_start_y = cursor().pos().y(); } + emit clicked(); } void LabelSlider::mouseMoveEvent(QMouseEvent* event) { diff --git a/ui/labelslider.h b/ui/labelslider.h index 3476128ff..f671d18df 100644 --- a/ui/labelslider.h +++ b/ui/labelslider.h @@ -25,6 +25,7 @@ public: QString valueToString(double v); double getPreviousValue(); void set_previous_value(); + void set_color(QString c = 0); int decimal_places; protected: void mousePressEvent(QMouseEvent *ev); @@ -53,6 +54,7 @@ private: double frame_rate; signals: void valueChanged(); + void clicked(); }; #endif // LABELSLIDER_H diff --git a/ui/timelineheader.h b/ui/timelineheader.h index 884a44adf..2950cb0fc 100644 --- a/ui/timelineheader.h +++ b/ui/timelineheader.h @@ -21,12 +21,12 @@ public: bool snapping; void show_text(bool enable); - void update_zoom(double z); double get_zoom(); void delete_markers(); void set_scrollbar_max(QScrollBar* bar, long sequence_end_frame, int offset); public slots: + void update_zoom(double z); void set_scroll(int); void set_visible_in(long i); void show_context_menu(const QPoint &pos); From e46ba45bd65eb878188b271e2961246a066624b6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 28 Dec 2018 22:45:53 +1100 Subject: [PATCH 05/65] fixed crash when deleting media of a selected clip --- panels/project.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/panels/project.cpp b/panels/project.cpp index ce3264a6c..2c1a50dba 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -476,7 +476,7 @@ void Project::delete_selected_media() { remove = false; } } - if (confirm_delete) { + if (confirm_delete) { ca->append(new DeleteClipAction(s, k)); } } @@ -491,6 +491,9 @@ void Project::delete_selected_media() { // remove if (remove) { + panel_effect_controls->clear_effects(true); + sequence->selections.clear(); + // remove media and parents for (int m=0;m Date: Sat, 29 Dec 2018 19:56:20 +1100 Subject: [PATCH 06/65] rounded hue value --- ui/graphview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 194139c7d..4a62ed8fd 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -18,7 +18,7 @@ QColor get_curve_color(int index, int length) { QColor c; - int hue = (double(index)/double(length))*255; + int hue = qRound((double(index)/double(length))*255); c.setHsv(hue, 255, 255); return c; } From 419632b598b89dc62407ef0e0fb0bddd3864a4ae Mon Sep 17 00:00:00 2001 From: Peter Eszlari Date: Sun, 30 Dec 2018 23:49:54 +0100 Subject: [PATCH 07/65] move olive.icns to packaging/macos --- olive.pro | 2 +- {icons => packaging/macos}/olive.icns | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename {icons => packaging/macos}/olive.icns (100%) diff --git a/olive.pro b/olive.pro index 1ab9f79e8..33a4e5123 100644 --- a/olive.pro +++ b/olive.pro @@ -196,7 +196,7 @@ win32 { mac { LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample - ICON = icons/olive.icns + ICON = packaging/macos/olive.icns INCLUDEPATH = /usr/local/include } diff --git a/icons/olive.icns b/packaging/macos/olive.icns similarity index 100% rename from icons/olive.icns rename to packaging/macos/olive.icns From 8a6af759fd73f3f3667b21e7413a1538eacff272 Mon Sep 17 00:00:00 2001 From: Peter Eszlari Date: Sun, 30 Dec 2018 23:53:53 +0100 Subject: [PATCH 08/65] olive.ico, resources.rc -> packaging/windows --- olive.pro | 2 +- {icons => packaging/windows}/olive.ico | Bin {icons => packaging/windows}/resources.rc | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename {icons => packaging/windows}/olive.ico (100%) rename {icons => packaging/windows}/resources.rc (100%) diff --git a/olive.pro b/olive.pro index 33a4e5123..c4883bf5f 100644 --- a/olive.pro +++ b/olive.pro @@ -190,7 +190,7 @@ HEADERS += \ FORMS += win32 { - RC_FILE = icons/resources.rc + RC_FILE = packaging/windows/resources.rc LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lopengl32 } diff --git a/icons/olive.ico b/packaging/windows/olive.ico similarity index 100% rename from icons/olive.ico rename to packaging/windows/olive.ico diff --git a/icons/resources.rc b/packaging/windows/resources.rc similarity index 100% rename from icons/resources.rc rename to packaging/windows/resources.rc From 3d8c5f2fed4570d55538c971235fafe7604006e3 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 31 Dec 2018 10:47:07 +1100 Subject: [PATCH 09/65] removed redundant link --- olive-editor.pro | 1 - 1 file changed, 1 deletion(-) delete mode 120000 olive-editor.pro diff --git a/olive-editor.pro b/olive-editor.pro deleted file mode 120000 index eb1b3bf56..000000000 --- a/olive-editor.pro +++ /dev/null @@ -1 +0,0 @@ -olive.pro \ No newline at end of file From 7a4e655d2c1901f07e141a66b80128004681c9a3 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 31 Dec 2018 22:18:42 +1100 Subject: [PATCH 10/65] keyframes are movable in graph view --- project/clip.cpp | 19 ++++++++++----- project/undo.cpp | 12 ++++++--- project/undo.h | 9 ++++--- ui/graphview.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++-- ui/graphview.h | 11 +++++++-- ui/keyframeview.cpp | 40 +++++++++++++++++++++--------- ui/keyframeview.h | 14 +++++------ 7 files changed, 128 insertions(+), 36 deletions(-) diff --git a/project/clip.cpp b/project/clip.cpp index 213a53858..88258441f 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -309,18 +309,25 @@ void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_t track); } + QVector key_rows; + QVector key_indices; + QVector key_old; + QVector key_new; + for (int i=0;irow_count();j++) { EffectRow* r = e->row(j); for (int k=0;kkeyframe_times.size();k++) { - long new_pos = r->keyframe_times.at(k) * multiplier; - KeyframeMove* km = new KeyframeMove(); - km->movement = new_pos - r->keyframe_times.at(k); - km->rows.append(r); - km->keyframes.append(k); - ca->append(km); + key_rows.append(r); + key_indices.at(k); + key_old.append(r->keyframe_times.at(k)); + key_new.append(r->keyframe_times.at(k) * multiplier); } } } + + if (key_rows.size() > 0) { + ca->append(new KeyframeMove(key_rows, key_indices, key_old, key_new)); + } } diff --git a/project/undo.cpp b/project/undo.cpp index e67c38941..fc9f77d92 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -737,18 +737,24 @@ void MediaRename::redo() { mainWindow->setWindowModified(true); } -KeyframeMove::KeyframeMove() : old_project_changed(mainWindow->isWindowModified()) {} +KeyframeMove::KeyframeMove(const QVector& irows, const QVector& ikeyframes, const QVector& iold_values, const QVector& inew_values) : + rows(irows), + keyframes(ikeyframes), + old_values(iold_values), + new_values(inew_values), + old_project_changed(mainWindow->isWindowModified()) +{} void KeyframeMove::undo() { for (int i=0;ikeyframe_times[keyframes.at(i)] -= movement; + rows.at(i)->keyframe_times[keyframes.at(i)] = old_values.at(i); } mainWindow->setWindowModified(old_project_changed); } void KeyframeMove::redo() { for (int i=0;ikeyframe_times[keyframes.at(i)] += movement; + rows.at(i)->keyframe_times[keyframes.at(i)] = new_values.at(i); } mainWindow->setWindowModified(true); } diff --git a/project/undo.h b/project/undo.h index a1f79212f..f7aae7626 100644 --- a/project/undo.h +++ b/project/undo.h @@ -330,13 +330,14 @@ private: class KeyframeMove : public QUndoCommand { public: - KeyframeMove(); - QVector rows; - QVector keyframes; - long movement; + KeyframeMove(const QVector& rows, const QVector& keyframes, const QVector& old_values, const QVector& new_values); void undo(); void redo(); private: + QVector rows; + QVector keyframes; + QVector old_values; + QVector new_values; bool old_project_changed; }; diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 4a62ed8fd..4c26a081e 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -10,6 +10,7 @@ #include "project/effectrow.h" #include "project/effectfield.h" #include "ui/keyframedrawing.h" +#include "project/undo.h" #include "debug.h" @@ -29,7 +30,8 @@ GraphView::GraphView(QWidget* parent) : y_scroll(0), mousedown(false), zoom(1.0), - row(NULL) + row(NULL), + moved_keys(false) { setMouseTracking(true); } @@ -115,7 +117,7 @@ void GraphView::paintEvent(QPaintEvent *event) { int key_x = get_screen_x(row->keyframe_times.at(key_index)); int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); - draw_keyframe(p, row->keyframe_types.at(key_index), key_x, key_y, false); + draw_keyframe(p, row->keyframe_types.at(key_index), key_x, key_y, (selected_keys.contains(key_index) && selected_keys_fields.contains(i))); } p.setBrush(Qt::NoBrush); } @@ -140,6 +142,42 @@ void GraphView::mousePressEvent(QMouseEvent *event) { mousedown = true; start_x = event->pos().x(); start_y = event->pos().y(); + + // selecting + int sel_key = -1; + int sel_key_field = -1; + if (!(event->modifiers() & Qt::ShiftModifier)) { + selected_keys.clear(); + selected_keys_fields.clear(); + } + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + for (int j=0;jkeyframe_times.size();j++) { + int key_x = get_screen_x(row->keyframe_times.at(j)); + int key_y = get_screen_y(field->keyframe_data.at(j).toDouble()); + if (event->pos().x() > key_x-KEYFRAME_SIZE + && event->pos().x() < key_x+KEYFRAME_SIZE + && event->pos().y() > key_y-KEYFRAME_SIZE + && event->pos().y() < key_y+KEYFRAME_SIZE) { + sel_key = j; + sel_key_field = i; + break; + } + } + } + } + if (sel_key > -1) { + selected_keys.append(sel_key); + selected_keys_fields.append(sel_key_field); + } + + selected_keys_old_vals.clear(); + for (int i=0;ikeyframe_times.at(selected_keys.at(i))); + } + + update(); } void GraphView::mouseMoveEvent(QMouseEvent *event) { @@ -150,11 +188,28 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { start_x = event->pos().x(); start_y = event->pos().y(); update(); + } else { + for (int i=0;ikeyframe_times[selected_keys.at(i)] = selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom); + } + moved_keys = true; + update_ui(false); } } } void GraphView::mouseReleaseEvent(QMouseEvent *event) { + if (moved_keys) { + QVector rows; + QVector new_vals; + for (int i=0;ikeyframe_times.at(selected_keys.at(i))); + } + + undo_stack.push(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); + } + moved_keys = false; mousedown = false; } diff --git a/ui/graphview.h b/ui/graphview.h index ca1404c94..5706b48a6 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -2,6 +2,7 @@ #define GRAPHVIEW_H #include +#include class EffectRow; @@ -10,9 +11,9 @@ QColor get_curve_color(int index, int length); class GraphView : public QWidget { Q_OBJECT public: - GraphView(QWidget* parent = 0); + GraphView(QWidget* parent = 0); - void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); @@ -37,6 +38,12 @@ private: int get_screen_x(double); int get_screen_y(double); + QVector selected_keys; + QVector selected_keys_fields; + QVector selected_keys_old_vals; + + bool moved_keys; + EffectRow* row; }; diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index e12feb4f7..b37f71b17 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -106,7 +106,6 @@ void KeyframeView::paintEvent(QPaintEvent*) { for (int k=0;kkeyframe_times.size();k++) { bool keyframe_selected = keyframeIsSelected(row, k); long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(k)); - if (dragging && keyframe_selected) keyframe_frame += frame_diff; draw_keyframe(p, row->keyframe_types.at(k), getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); } @@ -198,6 +197,8 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { return; } + old_key_vals.clear(); + int mouse_x = event->x() + x_scroll; int mouse_y = event->y(); int row_index = -1; @@ -241,6 +242,10 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { } if (selected_rows.size() > 0) { + for (int i=0;ikeyframe_times.at(selected_keyframes.at(i))); + } + keys_selected = true; } @@ -266,14 +271,15 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { int mouse_x = event->x() + x_scroll; if (keys_selected) { // move keyframes - frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start; + long frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start; // snapping to playhead + panel_timeline->snapped = false; if (panel_timeline->snapping) { for (int i=0;iparent_effect->parent_clip; - long key_time = row->keyframe_times.at(selected_keyframes.at(i)) + frame_diff - c->clip_in + c->timeline_in; + long key_time = old_key_vals.at(i) + frame_diff - c->clip_in + c->timeline_in; long key_eval = key_time; if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) { frame_diff += (key_eval - key_time); @@ -283,9 +289,9 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } // validate frame_diff (make sure no keyframes overlap each other) - for (int i=0;ikeyframe_times.at(selected_keyframes.at(i)); + long eval_key = old_key_vals.at(i); for (int j=0;jkeyframe_times.size();j++) { while (!keyframeIsSelected(row, j) && row->keyframe_times.at(j) == eval_key + frame_diff) { if (last_frame_diff > frame_diff) { @@ -299,9 +305,17 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } } + // apply frame_diffs + for (int i=0;ikeyframe_times[selected_keyframes.at(i)] = old_key_vals.at(i) + frame_diff; + } + last_frame_diff = frame_diff; dragging = true; + + update_ui(false); } else { // do a rect select rect_select_w = event->x() - rect_select_x; @@ -329,18 +343,20 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } select_rect = true; + + update_keys(); } - update_keys(); } } void KeyframeView::mouseReleaseEvent(QMouseEvent*) { - if (dragging && frame_diff != 0) { - KeyframeMove* ka = new KeyframeMove(); - ka->movement = frame_diff; - ka->keyframes = selected_keyframes; - ka->rows = selected_rows; - undo_stack.push(ka); + if (dragging) { + QVector new_key_vals; + for (int i=0;ikeyframe_times.at(selected_keyframes.at(i))); + } + + undo_stack.push(new KeyframeMove(selected_rows, selected_keyframes, old_key_vals, new_key_vals)); } select_rect = false; diff --git a/ui/keyframeview.h b/ui/keyframeview.h index c81186683..990bdef9e 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -27,19 +27,19 @@ public slots: private: long adjust_row_keyframe(EffectRow* row, long time); QVector selected_rows; - QVector selected_keyframes; + QVector selected_keyframes; QVector rowY; - long frame_diff; QVector rows; + QVector old_key_vals; void mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent *event); void paintEvent(QPaintEvent *event); - bool mousedown; + bool mousedown; bool dragging; bool keys_selected; - bool select_rect; - bool scroll_drag; + bool select_rect; + bool scroll_drag; bool keyframeIsSelected(EffectRow* row, int keyframe); @@ -55,8 +55,8 @@ private: void update_keys(); private slots: - void show_context_menu(const QPoint& pos); - void menu_set_key_type(QAction*); + void show_context_menu(const QPoint& pos); + void menu_set_key_type(QAction*); }; #endif // KEYFRAMEVIEW_H From 1e1ed96bff9752d273d2f8352423316e081b487c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 31 Dec 2018 22:44:34 +1100 Subject: [PATCH 11/65] keyframe values can now be set in graph editor --- project/undo.cpp | 31 +++++++++++++++++++++++++++++++ project/undo.h | 23 +++++++++++++++++++++++ ui/graphview.cpp | 23 +++++++++++++++++++++-- ui/graphview.h | 1 + 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/project/undo.cpp b/project/undo.cpp index fc9f77d92..963b02c32 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -1301,3 +1301,34 @@ void RippleAction::redo() { } ca->redo(); } + +SetDouble::SetDouble(double* pointer, double new_value) : + p(pointer), + oldval(*pointer), + newval(new_value), + old_project_changed(mainWindow->isWindowModified()) +{} + +void SetDouble::undo() { + *p = oldval; + mainWindow->setWindowModified(old_project_changed); +} + +void SetDouble::redo() { + *p = newval; + mainWindow->setWindowModified(true); +} + +SetQVariant::SetQVariant(QVariant *itarget, const QVariant &iold, const QVariant &inew) : + target(itarget), + old_val(iold), + new_val(inew) +{} + +void SetQVariant::undo() { + *target = old_val; +} + +void SetQVariant::redo() { + *target = new_val; +} diff --git a/project/undo.h b/project/undo.h index f7aae7626..e74d09a25 100644 --- a/project/undo.h +++ b/project/undo.h @@ -524,6 +524,18 @@ private: bool old_project_changed; }; +class SetDouble : public QUndoCommand { +public: + SetDouble(double* pointer, double new_value); + void undo(); + void redo(); +private: + double* p; + double oldval; + double newval; + bool old_project_changed; +}; + class SetString : public QUndoCommand { public: SetString(QString* pointer, QString new_value); @@ -606,4 +618,15 @@ public: void redo(); }; +class SetQVariant : public QUndoCommand { +public: + SetQVariant(QVariant* itarget, const QVariant& iold, const QVariant& inew); + void undo(); + void redo(); +private: + QVariant* target; + QVariant old_val; + QVariant new_val; +}; + #endif // UNDO_H diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 4c26a081e..f76168677 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -173,8 +173,13 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } selected_keys_old_vals.clear(); + selected_keys_old_doubles.clear(); for (int i=0;ikeyframe_times.at(selected_keys.at(i))); + + for (int j=0;jfield(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)).toDouble()); + } } update(); @@ -191,6 +196,10 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } else { for (int i=0;ikeyframe_times[selected_keys.at(i)] = selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom); + + for (int j=0;jfield(selected_keys_fields.at(j))->keyframe_data[selected_keys.at(i)] = selected_keys_old_doubles.at((i*selected_keys_fields.size())+j) + (double(start_y - event->pos().y())/zoom); + } } moved_keys = true; update_ui(false); @@ -199,15 +208,25 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } void GraphView::mouseReleaseEvent(QMouseEvent *event) { - if (moved_keys) { + if (moved_keys && selected_keys.size() > 0) { + ComboAction* ca = new ComboAction(); QVector rows; QVector new_vals; + for (int i=0;ikeyframe_times.at(selected_keys.at(i))); + + for (int j=0;jappend(new SetQVariant(&row->field(selected_keys_fields.at(j))->keyframe_data[selected_keys.at(i)], + selected_keys_old_doubles.at((i*selected_keys_fields.size())+j), + row->field(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)))); + } + //ca->append(new KeyframeSet(row, selected_keys.at(i), 0, false)); } - undo_stack.push(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); + ca->append(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); + undo_stack.push(ca); } moved_keys = false; mousedown = false; diff --git a/ui/graphview.h b/ui/graphview.h index 5706b48a6..363303632 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -41,6 +41,7 @@ private: QVector selected_keys; QVector selected_keys_fields; QVector selected_keys_old_vals; + QVector selected_keys_old_doubles; bool moved_keys; From ef3e0754be392a70ac373fbe4505cbf3c39598a1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 08:36:47 +1100 Subject: [PATCH 12/65] fixed graph view bug --- ui/graphview.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index f76168677..b87302824 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -150,19 +150,21 @@ void GraphView::mousePressEvent(QMouseEvent *event) { selected_keys.clear(); selected_keys_fields.clear(); } - for (int i=0;ifieldCount();i++) { - EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE) { - for (int j=0;jkeyframe_times.size();j++) { - int key_x = get_screen_x(row->keyframe_times.at(j)); - int key_y = get_screen_y(field->keyframe_data.at(j).toDouble()); - if (event->pos().x() > key_x-KEYFRAME_SIZE - && event->pos().x() < key_x+KEYFRAME_SIZE - && event->pos().y() > key_y-KEYFRAME_SIZE - && event->pos().y() < key_y+KEYFRAME_SIZE) { - sel_key = j; - sel_key_field = i; - break; + if (row != NULL) { + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + for (int j=0;jkeyframe_times.size();j++) { + int key_x = get_screen_x(row->keyframe_times.at(j)); + int key_y = get_screen_y(field->keyframe_data.at(j).toDouble()); + if (event->pos().x() > key_x-KEYFRAME_SIZE + && event->pos().x() < key_x+KEYFRAME_SIZE + && event->pos().y() > key_y-KEYFRAME_SIZE + && event->pos().y() < key_y+KEYFRAME_SIZE) { + sel_key = j; + sel_key_field = i; + break; + } } } } From 8d36921b3351a949ff2ad236324b2acd77d856df Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 09:04:33 +1100 Subject: [PATCH 13/65] panel tweaks --- mainwindow.cpp | 57 +++++++++++++++++++-------------------- panels/effectcontrols.cpp | 4 +-- panels/grapheditor.cpp | 28 ++++++++++--------- panels/project.cpp | 2 +- panels/timeline.cpp | 2 ++ panels/viewer.cpp | 4 +-- 6 files changed, 50 insertions(+), 47 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index b81ab17c4..1f4094bce 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -219,36 +219,7 @@ MainWindow::MainWindow(QWidget *parent) : } MainWindow::~MainWindow() { - panel_effect_controls->clear_effects(true); - panel_sequence_viewer->viewer_widget->delete_function(); - panel_footage_viewer->viewer_widget->delete_function(); - - set_sequence(NULL); - - QString data_dir = get_data_path(); - if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) { - if (QFile::exists(autorecovery_filename)) { - QFile::rename(autorecovery_filename, autorecovery_filename + "." + QDateTime::currentDateTimeUtc().toString("yyyyMMddHHmmss")); - } - } - if (!config_dir.isEmpty()) { - // save settings - config.save(config_dir); - - // save panel layout - QFile panel_config(data_dir + "/layout"); - if (panel_config.open(QFile::WriteOnly)) { - panel_config.write(saveState(0)); - panel_config.close(); - } else { - dout << "[ERROR] Failed to save layout"; - } - } - - stop_audio(); - free_panels(); - close_debug(); } @@ -790,6 +761,34 @@ void MainWindow::updateTitle(const QString& url) { void MainWindow::closeEvent(QCloseEvent *e) { if (can_close_project()) { + panel_effect_controls->clear_effects(true); + panel_sequence_viewer->viewer_widget->delete_function(); + panel_footage_viewer->viewer_widget->delete_function(); + + set_sequence(NULL); + + QString data_dir = get_data_path(); + if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) { + if (QFile::exists(autorecovery_filename)) { + QFile::rename(autorecovery_filename, autorecovery_filename + "." + QDateTime::currentDateTimeUtc().toString("yyyyMMddHHmmss")); + } + } + if (!config_dir.isEmpty()) { + // save settings + config.save(config_dir); + + // save panel layout + QFile panel_config(data_dir + "/layout"); + if (panel_config.open(QFile::WriteOnly)) { + panel_config.write(saveState(0)); + panel_config.close(); + } else { + dout << "[ERROR] Failed to save layout"; + } + } + + stop_audio(); + e->accept(); } else { e->ignore(); diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index a163ed549..de1726900 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -37,6 +37,8 @@ EffectControls::EffectControls(QWidget *parent) : panel_name("Effects: "), mode(TA_NO_TRANSITION) { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setup_ui(); init_effects(); @@ -253,8 +255,6 @@ void EffectControls::open_effect(QVBoxLayout* layout, Effect* e) { } void EffectControls::setup_ui() { - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - QWidget* contents = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(contents); diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 71dd5142f..bc8bc48dc 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -18,18 +18,20 @@ #include "debug.h" GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { - setWindowTitle("Graph Editor"); - resize(720, 480); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + setWindowTitle("Graph Editor"); + resize(720, 480); QWidget* main_widget = new QWidget(); setWidget(main_widget); - QVBoxLayout* layout = new QVBoxLayout(); + QVBoxLayout* layout = new QVBoxLayout(); main_widget->setLayout(layout); - QWidget* tool_widget = new QWidget(); - tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - QHBoxLayout* tools = new QHBoxLayout(); - tool_widget->setLayout(tools); + QWidget* tool_widget = new QWidget(); + tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + QHBoxLayout* tools = new QHBoxLayout(); + tool_widget->setLayout(tools); QWidget* left_tool_widget = new QWidget(); QHBoxLayout* left_tool_layout = new QHBoxLayout(); @@ -89,7 +91,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { right_tool_layout->addWidget(tool_buttons.at(i)); }*/ - layout->addWidget(tool_widget); + layout->addWidget(tool_widget); QWidget* central_widget = new QWidget(); QVBoxLayout* central_layout = new QVBoxLayout(); @@ -104,11 +106,11 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { layout->addWidget(central_widget); - QWidget* value_widget = new QWidget(); - value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + QWidget* value_widget = new QWidget(); + value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); QHBoxLayout* values = new QHBoxLayout(); - value_widget->setLayout(values); - values->addStretch(); + value_widget->setLayout(values); + values->addStretch(); current_row_desc = new QLabel(); values->addWidget(current_row_desc); @@ -119,7 +121,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { central_value_widget->setLayout(value_layout); values->addWidget(central_value_widget); - values->addStretch(); + values->addStretch(); layout->addWidget(value_widget); connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double))); diff --git a/panels/project.cpp b/panels/project.cpp index 31669a555..563f38c9b 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -60,7 +60,7 @@ QString recent_proj_file; Project::Project(QWidget *parent) : QDockWidget(parent) { - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget* dockWidgetContents = new QWidget(); QVBoxLayout* verticalLayout = new QVBoxLayout(dockWidgetContents); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 432fb6996..0e97fb22a 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -80,6 +80,8 @@ Timeline::Timeline(QWidget *parent) : last_frame(0), scroll(0) { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setup_ui(); default_track_height = (QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96) * TRACK_DEFAULT_HEIGHT; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 01e488079..d09f53ae7 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -46,6 +46,8 @@ Viewer::Viewer(QWidget *parent) : panel_name("Viewer: "), minimum_zoom(1.0) { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setup_ui(); headers->viewer = this; @@ -449,8 +451,6 @@ void Viewer::set_sb_max() { } void Viewer::setup_ui() { - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - QWidget* contents = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(contents); From 88bf0ff2be937423e27316a6a029dd76e549c58e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 09:49:11 +1100 Subject: [PATCH 14/65] buttons enabled by selection --- panels/grapheditor.cpp | 36 +++++++++++------------------------- panels/grapheditor.h | 8 ++++++-- ui/graphview.cpp | 7 +++++++ ui/graphview.h | 1 + 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index bc8bc48dc..259ade390 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -58,11 +58,11 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { left_tool_layout->addWidget(keyframe_nav); left_tool_layout->addStretch(); - QPushButton* linear_button = new QPushButton("Linear"); + linear_button = new QPushButton("Linear"); linear_button->setCheckable(true); - QPushButton* bezier_button = new QPushButton("Bezier"); + bezier_button = new QPushButton("Bezier"); bezier_button->setCheckable(true); - QPushButton* hold_button = new QPushButton("Hold"); + hold_button = new QPushButton("Hold"); hold_button->setCheckable(true); center_tool_layout->addStretch(); @@ -70,27 +70,6 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { center_tool_layout->addWidget(bezier_button); center_tool_layout->addWidget(hold_button); - /*QPushButton* tool_arrow_button = new QPushButton(); - tool_arrow_button->setCheckable(true); - tool_arrow_button->setIcon(QIcon(":/icons/arrow.png")); - tool_arrow_button->setProperty("tool", TIMELINE_TOOL_POINTER); - tool_buttons.append(tool_arrow_button); - QPushButton* tool_hand_button = new QPushButton(); - tool_hand_button->setCheckable(true); - tool_hand_button->setIcon(QIcon(":/icons/hand.png")); - tool_hand_button->setProperty("tool", TIMELINE_TOOL_HAND); - tool_buttons.append(tool_hand_button); - QPushButton* tool_zoom_button = new QPushButton(); - tool_zoom_button->setCheckable(true); - tool_zoom_button->setIcon(QIcon(":/icons/zoomin.png")); - tool_zoom_button->setProperty("tool", TIMELINE_TOOL_ZOOM); - tool_buttons.append(tool_zoom_button); - - right_tool_layout->addStretch(); - for (int i=0;iaddWidget(tool_buttons.at(i)); - }*/ - layout->addWidget(tool_widget); QWidget* central_widget = new QWidget(); @@ -114,7 +93,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { current_row_desc = new QLabel(); values->addWidget(current_row_desc); - +1 QWidget* central_value_widget = new QWidget(); value_layout = new QHBoxLayout(); value_layout->setMargin(0); @@ -126,6 +105,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double))); connect(view, SIGNAL(x_scroll_changed(int)), header, SLOT(set_scroll(int))); + connect(view, SIGNAL(selection_changed(bool)), this, SLOT(set_key_button_enabled(bool))); } void GraphEditor::update_panel() { @@ -195,6 +175,12 @@ void GraphEditor::set_row(EffectRow *r) { update_panel(); } +void GraphEditor::set_key_button_enabled(bool e) { + linear_button->setEnabled(e); + bezier_button->setEnabled(e); + hold_button->setEnabled(e); +} + void GraphEditor::passthrough_slider_value() { for (int i=0;i 0); } void GraphView::mouseMoveEvent(QMouseEvent *event) { @@ -261,6 +263,11 @@ void GraphView::wheelEvent(QWheelEvent *event) { } void GraphView::set_row(EffectRow *r) { + selected_keys.clear(); + selected_keys_fields.clear(); + selected_keys_old_vals.clear(); + selected_keys_old_doubles.clear(); + emit selection_changed(false); row = r; update(); } diff --git a/ui/graphview.h b/ui/graphview.h index 363303632..97b6e1e2c 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -24,6 +24,7 @@ signals: void x_scroll_changed(int); void y_scroll_changed(int); void zoom_changed(double); + void selection_changed(bool); private: int x_scroll; int y_scroll; From 9934ec577717ba855de0100a11e4c83dbceaa625 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 11:29:37 +1100 Subject: [PATCH 15/65] changed effect field struct --- panels/grapheditor.cpp | 2 +- project/effectfield.h | 93 +++++++++++++++++++++++------------------- project/effectrow.h | 43 +++++++++---------- 3 files changed, 73 insertions(+), 65 deletions(-) diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 259ade390..9b84dd8d0 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -93,7 +93,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { current_row_desc = new QLabel(); values->addWidget(current_row_desc); -1 + QWidget* central_value_widget = new QWidget(); value_layout = new QHBoxLayout(); value_layout->setMargin(0); diff --git a/project/effectfield.h b/project/effectfield.h index 485b43287..f95c896cd 100644 --- a/project/effectfield.h +++ b/project/effectfield.h @@ -15,60 +15,71 @@ class EffectRow; class ComboAction; -class EffectField : public QObject { - Q_OBJECT +class EffectKeyframe { public: - EffectField(EffectRow* parent, int t, const QString& i); - EffectRow* parent_row; - int type; - QString id; + long time; + int type; + QVariant data; - QVariant get_previous_data(); - QVariant get_current_data(); - double frameToTimecode(long frame); - long timecodeToFrame(double timecode); - void set_current_data(const QVariant&); - void get_keyframe_data(double timecode, int& before, int& after, double& d); - QVariant validate_keyframe_data(double timecode, bool async = false); + // only for bezier type + QPointF pre_handle; + QPointF post_handle; +}; - double get_double_value(double timecode, bool async = false); - void set_double_value(double v); - void set_double_default_value(double v); - void set_double_minimum_value(double v); - void set_double_maximum_value(double v); +class EffectField : public QObject { + Q_OBJECT +public: + EffectField(EffectRow* parent, int t, const QString& i); + EffectRow* parent_row; + int type; + QString id; - const QString get_string_value(double timecode, bool async = false); - void set_string_value(const QString &s); + QVariant get_previous_data(); + QVariant get_current_data(); + double frameToTimecode(long frame); + long timecodeToFrame(double timecode); + void set_current_data(const QVariant&); + void get_keyframe_data(double timecode, int& before, int& after, double& d); + QVariant validate_keyframe_data(double timecode, bool async = false); - void add_combo_item(const QString& name, const QVariant &data); - int get_combo_index(double timecode, bool async = false); - const QVariant get_combo_data(double timecode); - const QString get_combo_string(double timecode); - void set_combo_index(int index); - void set_combo_string(const QString& s); + double get_double_value(double timecode, bool async = false); + void set_double_value(double v); + void set_double_default_value(double v); + void set_double_minimum_value(double v); + void set_double_maximum_value(double v); - bool get_bool_value(double timecode, bool async = false); - void set_bool_value(bool b); + const QString get_string_value(double timecode, bool async = false); + void set_string_value(const QString &s); - const QString get_font_name(double timecode, bool async = false); - void set_font_name(const QString& s); + void add_combo_item(const QString& name, const QVariant &data); + int get_combo_index(double timecode, bool async = false); + const QVariant get_combo_data(double timecode); + const QString get_combo_string(double timecode); + void set_combo_index(int index); + void set_combo_string(const QString& s); - QColor get_color_value(double timecode, bool async = false); - void set_color_value(QColor color); + bool get_bool_value(double timecode, bool async = false); + void set_bool_value(bool b); - QWidget* get_ui_element(); - void set_enabled(bool e); - QVector keyframe_data; - QWidget* ui_element; + const QString get_font_name(double timecode, bool async = false); + void set_font_name(const QString& s); - void make_key_from_change(ComboAction* ca); + QColor get_color_value(double timecode, bool async = false); + void set_color_value(QColor color); + + QWidget* get_ui_element(); + void set_enabled(bool e); + QVector keyframes; + QWidget* ui_element; + + void make_key_from_change(ComboAction* ca); private: - bool hasKeyframes(); + bool hasKeyframes(); private slots: - void ui_element_change(); + void ui_element_change(); signals: - void changed(); - void toggled(bool); + void changed(); + void toggled(bool); void clicked(); }; diff --git a/project/effectrow.h b/project/effectrow.h index a18cd56af..672c00b16 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -16,26 +16,23 @@ class KeyframeNavigator; class ClickableLabel; class EffectRow : public QObject { - Q_OBJECT + Q_OBJECT public: - EffectRow(Effect* parent, bool save, QGridLayout* uilayout, const QString& n, int row); - ~EffectRow(); - EffectField* add_field(int type, const QString &id, int colspan = 1); - EffectField* field(int i); - int fieldCount(); - void set_keyframe_now(ComboAction *ca); - void delete_keyframe(KeyframeDelete *kd, int index); - void delete_keyframe_at_time(KeyframeDelete* kd, long time); + EffectRow(Effect* parent, bool save, QGridLayout* uilayout, const QString& n, int row); + ~EffectRow(); + EffectField* add_field(int type, const QString &id, int colspan = 1); + EffectField* field(int i); + int fieldCount(); + void set_keyframe_now(ComboAction *ca); + void delete_keyframe(KeyframeDelete *kd, int index); + void delete_keyframe_at_time(KeyframeDelete* kd, long time); ClickableLabel* label; - Effect* parent_effect; - bool savable; + Effect* parent_effect; + bool savable; const QString& get_name(); - bool isKeyframing(); - void setKeyframing(bool); - - QVector keyframe_times; - QVector keyframe_types; + bool isKeyframing(); + void setKeyframing(bool); public slots: void goto_previous_key(); void toggle_key(); @@ -44,15 +41,15 @@ public slots: private slots: void set_keyframe_enabled(bool); private: - bool keyframing; - QGridLayout* ui; - QString name; - int ui_row; - QVector fields; + bool keyframing; + QGridLayout* ui; + QString name; + int ui_row; + QVector fields; - KeyframeNavigator* keyframe_nav; + KeyframeNavigator* keyframe_nav; - bool just_made_unsafe_keyframe; + bool just_made_unsafe_keyframe; }; #endif // EFFECTROW_H From 19765088d9b537ae6f78eea1554c495e4297396b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 12:07:15 +1100 Subject: [PATCH 16/65] corrected sample rate issue when exporting --- playback/audio.cpp | 7 +- playback/audio.h | 2 + playback/cacher.cpp | 1024 +++++++++++++++++++++---------------------- 3 files changed, 520 insertions(+), 513 deletions(-) diff --git a/playback/audio.cpp b/playback/audio.cpp index e5e90601e..aa0c38b2b 100644 --- a/playback/audio.cpp +++ b/playback/audio.cpp @@ -8,6 +8,7 @@ #include "panels/timeline.h" #include "panels/viewer.h" #include "ui/audiomonitor.h" +#include "playback/playback.h" #include "debug.h" #include @@ -104,9 +105,13 @@ void clear_audio_ibuffer() { audio_ibuffer_read = 0; } +int current_audio_freq() { + return rendering ? sequence->audio_frequency : audio_output->format().sampleRate(); +} + int get_buffer_offset_from_frame(double framerate, long frame) { if (frame >= audio_ibuffer_frame) { - return qFloor(((double) (frame - audio_ibuffer_frame)/framerate)*audio_output->format().sampleRate())*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); + return qFloor(((double) (frame - audio_ibuffer_frame)/framerate)*current_audio_freq())*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); } else { dout << "[WARNING] Invalid values passed to get_buffer_offset_from_frame"; return 0; diff --git a/playback/audio.h b/playback/audio.h index 795c6ba01..081a92e78 100644 --- a/playback/audio.h +++ b/playback/audio.h @@ -43,6 +43,8 @@ extern double audio_ibuffer_timecode; extern bool audio_scrub; void clear_audio_ibuffer(); +int current_audio_freq(); + bool is_audio_device_set(); void init_audio(); diff --git a/playback/cacher.cpp b/playback/cacher.cpp index ae3ecc683..d9a7056ce 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -42,36 +42,36 @@ double bytes_to_seconds(int nb_bytes, int nb_channels, int sample_rate) { } void apply_audio_effects(Clip* c, double timecode_start, AVFrame* frame, int nb_bytes, QVector nests) { - // perform all audio effects + // perform all audio effects double timecode_end; timecode_end = timecode_start + bytes_to_seconds(nb_bytes, frame->channels, frame->sample_rate); - for (int j=0;jeffects.size();j++) { + for (int j=0;jeffects.size();j++) { Effect* e = c->effects.at(j); if (e->is_enabled()) e->process_audio(timecode_start, timecode_end, frame->data[0], nb_bytes, 2); - } - if (c->get_opening_transition() != NULL) { - if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) { - double transition_start = (c->get_clip_in_with_transition() / c->sequence->frame_rate); - double transition_end = (c->get_clip_in_with_transition() + c->get_opening_transition()->get_length()) / c->sequence->frame_rate; + } + if (c->get_opening_transition() != NULL) { + if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) { + double transition_start = (c->get_clip_in_with_transition() / c->sequence->frame_rate); + double transition_end = (c->get_clip_in_with_transition() + c->get_opening_transition()->get_length()) / c->sequence->frame_rate; if (timecode_end < transition_end) { double adjustment = transition_end - transition_start; double adjusted_range_start = (timecode_start - transition_start) / adjustment; double adjusted_range_end = (timecode_end - transition_start) / adjustment; - c->get_opening_transition()->process_audio(adjusted_range_start, adjusted_range_end, frame->data[0], nb_bytes, TA_OPENING_TRANSITION); + c->get_opening_transition()->process_audio(adjusted_range_start, adjusted_range_end, frame->data[0], nb_bytes, TA_OPENING_TRANSITION); } } } - if (c->get_closing_transition() != NULL) { - if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) { - long length_with_transitions = c->get_timeline_out_with_transition() - c->get_timeline_in_with_transition(); - double transition_start = (c->get_clip_in_with_transition() + length_with_transitions - c->get_closing_transition()->get_length()) / c->sequence->frame_rate; - double transition_end = (c->get_clip_in_with_transition() + length_with_transitions) / c->sequence->frame_rate; - if (timecode_start > transition_start) { + if (c->get_closing_transition() != NULL) { + if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) { + long length_with_transitions = c->get_timeline_out_with_transition() - c->get_timeline_in_with_transition(); + double transition_start = (c->get_clip_in_with_transition() + length_with_transitions - c->get_closing_transition()->get_length()) / c->sequence->frame_rate; + double transition_end = (c->get_clip_in_with_transition() + length_with_transitions) / c->sequence->frame_rate; + if (timecode_start > transition_start) { double adjustment = transition_end - transition_start; double adjusted_range_start = (timecode_start - transition_start) / adjustment; double adjusted_range_end = (timecode_end - transition_start) / adjustment; - c->get_closing_transition()->process_audio(adjusted_range_start, adjusted_range_end, frame->data[0], nb_bytes, TA_CLOSING_TRANSITION); + c->get_closing_transition()->process_audio(adjusted_range_start, adjusted_range_end, frame->data[0], nb_bytes, TA_CLOSING_TRANSITION); } } } @@ -79,290 +79,290 @@ void apply_audio_effects(Clip* c, double timecode_start, AVFrame* frame, int nb_ if (!nests.isEmpty()) { Clip* next_nest = nests.last(); nests.removeLast(); - apply_audio_effects(next_nest, timecode_start + (((double)c->get_timeline_in_with_transition()-c->get_clip_in_with_transition())/c->sequence->frame_rate), frame, nb_bytes, nests); + apply_audio_effects(next_nest, timecode_start + (((double)c->get_timeline_in_with_transition()-c->get_clip_in_with_transition())/c->sequence->frame_rate), frame, nb_bytes, nests); } } #define AUDIO_BUFFER_PADDING 2048 void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { - long timeline_in = c->get_timeline_in_with_transition(); - long timeline_out = c->get_timeline_out_with_transition(); + long timeline_in = c->get_timeline_in_with_transition(); + long timeline_out = c->get_timeline_out_with_transition(); long target_frame = c->audio_target_frame; long frame_skip = 0; - double last_fr = c->sequence->frame_rate; + double last_fr = c->sequence->frame_rate; if (!nests.isEmpty()) { for (int i=nests.size()-1;i>=0;i--) { - timeline_in = refactor_frame_number(timeline_in, last_fr, nests.at(i)->sequence->frame_rate) + nests.at(i)->get_timeline_in_with_transition() - nests.at(i)->get_clip_in_with_transition(); - timeline_out = refactor_frame_number(timeline_out, last_fr, nests.at(i)->sequence->frame_rate) + nests.at(i)->get_timeline_in_with_transition() - nests.at(i)->get_clip_in_with_transition(); - target_frame = refactor_frame_number(target_frame, last_fr, nests.at(i)->sequence->frame_rate) + nests.at(i)->get_timeline_in_with_transition() - nests.at(i)->get_clip_in_with_transition(); + timeline_in = refactor_frame_number(timeline_in, last_fr, nests.at(i)->sequence->frame_rate) + nests.at(i)->get_timeline_in_with_transition() - nests.at(i)->get_clip_in_with_transition(); + timeline_out = refactor_frame_number(timeline_out, last_fr, nests.at(i)->sequence->frame_rate) + nests.at(i)->get_timeline_in_with_transition() - nests.at(i)->get_clip_in_with_transition(); + target_frame = refactor_frame_number(target_frame, last_fr, nests.at(i)->sequence->frame_rate) + nests.at(i)->get_timeline_in_with_transition() - nests.at(i)->get_clip_in_with_transition(); - timeline_out = qMin(timeline_out, nests.at(i)->get_timeline_out_with_transition()); + timeline_out = qMin(timeline_out, nests.at(i)->get_timeline_out_with_transition()); frame_skip = refactor_frame_number(frame_skip, last_fr, nests.at(i)->sequence->frame_rate); - long validator = nests.at(i)->get_timeline_in_with_transition() - timeline_in; + long validator = nests.at(i)->get_timeline_in_with_transition() - timeline_in; if (validator > 0) { frame_skip += validator; - //timeline_in = nests.at(i)->get_timeline_in_with_transition(); + //timeline_in = nests.at(i)->get_timeline_in_with_transition(); } last_fr = nests.at(i)->sequence->frame_rate; } } - while (true) { + while (true) { AVFrame* frame; int nb_bytes = INT_MAX; - if (c->media == NULL) { - frame = c->frame; - nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; - while ((c->frame_sample_index == -1 || c->frame_sample_index >= nb_bytes) && nb_bytes > 0) { - // create "new frame" - memset(c->frame->data[0], 0, nb_bytes); - apply_audio_effects(c, bytes_to_seconds(frame->pts, frame->channels, frame->sample_rate), frame, nb_bytes, nests); - c->frame->pts += nb_bytes; - c->frame_sample_index = 0; - if (c->audio_buffer_write == 0) { - c->audio_buffer_write = get_buffer_offset_from_frame(last_fr, qMax(timeline_in, target_frame)); - } - int offset = audio_ibuffer_read - c->audio_buffer_write; - if (offset > 0) { - c->audio_buffer_write += offset; - c->frame_sample_index += offset; - } - } - } else if (c->media->get_type() == MEDIA_TYPE_FOOTAGE) { - double timebase = av_q2d(c->stream->time_base); + if (c->media == NULL) { + frame = c->frame; + nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; + while ((c->frame_sample_index == -1 || c->frame_sample_index >= nb_bytes) && nb_bytes > 0) { + // create "new frame" + memset(c->frame->data[0], 0, nb_bytes); + apply_audio_effects(c, bytes_to_seconds(frame->pts, frame->channels, frame->sample_rate), frame, nb_bytes, nests); + c->frame->pts += nb_bytes; + c->frame_sample_index = 0; + if (c->audio_buffer_write == 0) { + c->audio_buffer_write = get_buffer_offset_from_frame(last_fr, qMax(timeline_in, target_frame)); + } + int offset = audio_ibuffer_read - c->audio_buffer_write; + if (offset > 0) { + c->audio_buffer_write += offset; + c->frame_sample_index += offset; + } + } + } else if (c->media->get_type() == MEDIA_TYPE_FOOTAGE) { + double timebase = av_q2d(c->stream->time_base); - frame = c->queue.at(0); + frame = c->queue.at(0); - // retrieve frame - bool new_frame = false; - while ((c->frame_sample_index == -1 || c->frame_sample_index >= nb_bytes) && nb_bytes > 0) { - // no more audio left in frame, get a new one - if (!c->reached_end) { - int loop = 0; + // retrieve frame + bool new_frame = false; + while ((c->frame_sample_index == -1 || c->frame_sample_index >= nb_bytes) && nb_bytes > 0) { + // no more audio left in frame, get a new one + if (!c->reached_end) { + int loop = 0; - if (c->reverse && !c->audio_just_reset) { - avcodec_flush_buffers(c->codecCtx); - c->reached_end = false; - int64_t backtrack_seek = qMax(c->reverse_target - static_cast(av_q2d(av_inv_q(c->stream->time_base))), static_cast(0)); - av_seek_frame(c->formatCtx, c->stream->index, backtrack_seek, AVSEEK_FLAG_BACKWARD); + if (c->reverse && !c->audio_just_reset) { + avcodec_flush_buffers(c->codecCtx); + c->reached_end = false; + int64_t backtrack_seek = qMax(c->reverse_target - static_cast(av_q2d(av_inv_q(c->stream->time_base))), static_cast(0)); + av_seek_frame(c->formatCtx, c->stream->index, backtrack_seek, AVSEEK_FLAG_BACKWARD); #ifdef AUDIOWARNINGS - if (backtrack_seek == 0) { - dout << "backtracked to 0"; - } + if (backtrack_seek == 0) { + dout << "backtracked to 0"; + } #endif - } + } - do { - av_frame_unref(frame); + do { + av_frame_unref(frame); - int ret; + int ret; - while ((ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) { - ret = retrieve_next_frame(c, c->frame); - if (ret >= 0) { - if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) { - dout << "[ERROR] Could not feed filtergraph -" << ret; - break; - } - } else { - if (ret == AVERROR_EOF) { + while ((ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) { + ret = retrieve_next_frame(c, c->frame); + if (ret >= 0) { + if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) { + dout << "[ERROR] Could not feed filtergraph -" << ret; + break; + } + } else { + if (ret == AVERROR_EOF) { #ifdef AUDIOWARNINGS - dout << "reached EOF while reading"; + dout << "reached EOF while reading"; #endif - // TODO revise usage of reached_end in audio - if (!c->reverse) { - c->reached_end = true; - } else { - } - } else { - dout << "[WARNING] Raw audio frame data could not be retrieved." << ret; - c->reached_end = true; - } - break; - } - } + // TODO revise usage of reached_end in audio + if (!c->reverse) { + c->reached_end = true; + } else { + } + } else { + dout << "[WARNING] Raw audio frame data could not be retrieved." << ret; + c->reached_end = true; + } + break; + } + } - if (ret < 0) { - if (ret != AVERROR_EOF) { - dout << "[ERROR] Could not pull from filtergraph"; - c->reached_end = true; - break; - } else { + if (ret < 0) { + if (ret != AVERROR_EOF) { + dout << "[ERROR] Could not pull from filtergraph"; + c->reached_end = true; + break; + } else { #ifdef AUDIOWARNINGS - dout << "reached EOF while pulling from filtergraph"; + dout << "reached EOF while pulling from filtergraph"; #endif - if (!c->reverse) break; - } - } + if (!c->reverse) break; + } + } - if (c->reverse) { - if (loop > 1) { - AVFrame* rev_frame = c->queue.at(1); - if (ret != AVERROR_EOF) { - if (loop == 2) { + if (c->reverse) { + if (loop > 1) { + AVFrame* rev_frame = c->queue.at(1); + if (ret != AVERROR_EOF) { + if (loop == 2) { #ifdef AUDIOWARNINGS - dout << "starting rev_frame"; + dout << "starting rev_frame"; #endif - rev_frame->nb_samples = 0; - rev_frame->pts = c->frame->pkt_pts; - } - int offset = rev_frame->nb_samples * av_get_bytes_per_sample(static_cast(rev_frame->format)) * rev_frame->channels; + rev_frame->nb_samples = 0; + rev_frame->pts = c->frame->pkt_pts; + } + int offset = rev_frame->nb_samples * av_get_bytes_per_sample(static_cast(rev_frame->format)) * rev_frame->channels; #ifdef AUDIOWARNINGS - dout << "offset 1:" << offset; - dout << "retrieved samples:" << frame->nb_samples << "size:" << (frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels); + dout << "offset 1:" << offset; + dout << "retrieved samples:" << frame->nb_samples << "size:" << (frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels); #endif - memcpy( - rev_frame->data[0]+offset, - frame->data[0], - (frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels) - ); + memcpy( + rev_frame->data[0]+offset, + frame->data[0], + (frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels) + ); #ifdef AUDIOWARNINGS - dout << "pts:" << c->frame->pts << "dur:" << c->frame->pkt_duration << "rev_target:" << c->reverse_target << "offset:" << offset << "limit:" << rev_frame->linesize[0]; + dout << "pts:" << c->frame->pts << "dur:" << c->frame->pkt_duration << "rev_target:" << c->reverse_target << "offset:" << offset << "limit:" << rev_frame->linesize[0]; #endif - } + } - rev_frame->nb_samples += frame->nb_samples; + rev_frame->nb_samples += frame->nb_samples; - if ((c->frame->pts >= c->reverse_target) || (ret == AVERROR_EOF)) { + if ((c->frame->pts >= c->reverse_target) || (ret == AVERROR_EOF)) { /* #ifdef AUDIOWARNINGS - dout << "time for the end of rev cache" << rev_frame->nb_samples << c->rev_target << c->frame->pts << c->frame->pkt_duration << c->frame->nb_samples; - dout << "diff:" << (c->frame->pkt_pts + c->frame->pkt_duration) - c->rev_target; + dout << "time for the end of rev cache" << rev_frame->nb_samples << c->rev_target << c->frame->pts << c->frame->pkt_duration << c->frame->nb_samples; + dout << "diff:" << (c->frame->pkt_pts + c->frame->pkt_duration) - c->rev_target; #endif - int cutoff = qRound64((((c->frame->pkt_pts + c->frame->pkt_duration) - c->reverse_target) * timebase) * audio_output->format().sampleRate()); - if (cutoff > 0) { + int cutoff = qRound64((((c->frame->pkt_pts + c->frame->pkt_duration) - c->reverse_target) * timebase) * audio_output->format().sampleRate()); + if (cutoff > 0) { #ifdef AUDIOWARNINGS - dout << "cut off" << cutoff << "samples (rate:" << audio_output->format().sampleRate() << ")"; + dout << "cut off" << cutoff << "samples (rate:" << audio_output->format().sampleRate() << ")"; #endif - rev_frame->nb_samples -= cutoff; - } + rev_frame->nb_samples -= cutoff; + } */ #ifdef AUDIOWARNINGS - dout << "pre cutoff deets::: rev_frame.pts:" << rev_frame->pts << "rev_frame.nb_samples" << rev_frame->nb_samples << "rev_target:" << c->reverse_target; + dout << "pre cutoff deets::: rev_frame.pts:" << rev_frame->pts << "rev_frame.nb_samples" << rev_frame->nb_samples << "rev_target:" << c->reverse_target; #endif - rev_frame->nb_samples = qRound64(static_cast(c->reverse_target - rev_frame->pts) / c->stream->codecpar->sample_rate * (audio_output->format().sampleRate() / c->speed)); + rev_frame->nb_samples = qRound64(static_cast(c->reverse_target - rev_frame->pts) / c->stream->codecpar->sample_rate * (current_audio_freq() / c->speed)); #ifdef AUDIOWARNINGS - dout << "post cutoff deets::" << rev_frame->nb_samples; + dout << "post cutoff deets::" << rev_frame->nb_samples; #endif - int frame_size = rev_frame->nb_samples * rev_frame->channels * av_get_bytes_per_sample(static_cast(rev_frame->format)); - int half_frame_size = frame_size >> 1; + int frame_size = rev_frame->nb_samples * rev_frame->channels * av_get_bytes_per_sample(static_cast(rev_frame->format)); + int half_frame_size = frame_size >> 1; - int sample_size = rev_frame->channels*av_get_bytes_per_sample(static_cast(rev_frame->format)); - char* temp_chars = new char[sample_size]; - for (int i=0;idata[0][i+j]; - } - for (int j=0;jdata[0][i+j] = rev_frame->data[0][frame_size-i-sample_size+j]; - } - for (int j=0;jdata[0][frame_size-i-sample_size+j] = temp_chars[j]; - } - } - delete [] temp_chars; + int sample_size = rev_frame->channels*av_get_bytes_per_sample(static_cast(rev_frame->format)); + char* temp_chars = new char[sample_size]; + for (int i=0;idata[0][i+j]; + } + for (int j=0;jdata[0][i+j] = rev_frame->data[0][frame_size-i-sample_size+j]; + } + for (int j=0;jdata[0][frame_size-i-sample_size+j] = temp_chars[j]; + } + } + delete [] temp_chars; - c->reverse_target = rev_frame->pts; - frame = rev_frame; - break; - } - } + c->reverse_target = rev_frame->pts; + frame = rev_frame; + break; + } + } - loop++; + loop++; #ifdef AUDIOWARNINGS - dout << "loop" << loop; + dout << "loop" << loop; #endif - } else { - frame->pts = c->frame->pts; - break; - } - } while (true); - } else { - // if there is no more data in the file, we flush the remainder out of swresample - break; - } + } else { + frame->pts = c->frame->pts; + break; + } + } while (true); + } else { + // if there is no more data in the file, we flush the remainder out of swresample + break; + } - new_frame = true; + new_frame = true; - if (c->frame_sample_index < 0) { - c->frame_sample_index = 0; - } else { - c->frame_sample_index -= nb_bytes; - } + if (c->frame_sample_index < 0) { + c->frame_sample_index = 0; + } else { + c->frame_sample_index -= nb_bytes; + } - nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; + nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; - if (c->audio_just_reset) { - // get precise sample offset for the elected clip_in from this audio frame - double target_sts = playhead_to_clip_seconds(c, c->audio_target_frame); - double frame_sts = ((frame->pts - c->stream->start_time) * timebase); - int nb_samples = qRound64((target_sts - frame_sts)*audio_output->format().sampleRate()); - c->frame_sample_index = nb_samples * 4; + if (c->audio_just_reset) { + // get precise sample offset for the elected clip_in from this audio frame + double target_sts = playhead_to_clip_seconds(c, c->audio_target_frame); + double frame_sts = ((frame->pts - c->stream->start_time) * timebase); + int nb_samples = qRound64((target_sts - frame_sts)*current_audio_freq()); + c->frame_sample_index = nb_samples * 4; #ifdef AUDIOWARNINGS - dout << "fsts:" << frame_sts << "tsts:" << target_sts << "nbs:" << nb_samples << "nbb:" << nb_bytes << "rev_targetToSec:" << (c->reverse_target * timebase); - dout << "fsi-calc:" << c->frame_sample_index; + dout << "fsts:" << frame_sts << "tsts:" << target_sts << "nbs:" << nb_samples << "nbb:" << nb_bytes << "rev_targetToSec:" << (c->reverse_target * timebase); + dout << "fsi-calc:" << c->frame_sample_index; #endif - if (c->reverse) c->frame_sample_index = nb_bytes - c->frame_sample_index; - c->audio_just_reset = false; - } + if (c->reverse) c->frame_sample_index = nb_bytes - c->frame_sample_index; + c->audio_just_reset = false; + } #ifdef AUDIOWARNINGS - dout << "fsi-post-post:" << c->frame_sample_index; + dout << "fsi-post-post:" << c->frame_sample_index; #endif - if (c->audio_buffer_write == 0) { - c->audio_buffer_write = get_buffer_offset_from_frame(last_fr, qMax(timeline_in, target_frame)); + if (c->audio_buffer_write == 0) { + c->audio_buffer_write = get_buffer_offset_from_frame(last_fr, qMax(timeline_in, target_frame)); - if (frame_skip > 0) { - int target = get_buffer_offset_from_frame(last_fr, qMax(timeline_in + frame_skip, target_frame)); - c->frame_sample_index += (target - c->audio_buffer_write); - c->audio_buffer_write = target; - } - } + if (frame_skip > 0) { + int target = get_buffer_offset_from_frame(last_fr, qMax(timeline_in + frame_skip, target_frame)); + c->frame_sample_index += (target - c->audio_buffer_write); + c->audio_buffer_write = target; + } + } - int offset = audio_ibuffer_read - c->audio_buffer_write; - if (offset > 0) { - c->audio_buffer_write += offset; - c->frame_sample_index += offset; - } + int offset = audio_ibuffer_read - c->audio_buffer_write; + if (offset > 0) { + c->audio_buffer_write += offset; + c->frame_sample_index += offset; + } - // try to correct negative fsi - if (c->frame_sample_index < 0) { - c->audio_buffer_write -= c->frame_sample_index; - c->frame_sample_index = 0; - } - } + // try to correct negative fsi + if (c->frame_sample_index < 0) { + c->audio_buffer_write -= c->frame_sample_index; + c->frame_sample_index = 0; + } + } - if (c->reverse) frame = c->queue.at(1); + if (c->reverse) frame = c->queue.at(1); #ifdef AUDIOWARNINGS - dout << "j" << c->frame_sample_index << nb_bytes; + dout << "j" << c->frame_sample_index << nb_bytes; #endif - // apply any audio effects to the data - if (nb_bytes == INT_MAX) nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; - if (new_frame) { - apply_audio_effects(c, bytes_to_seconds(c->audio_buffer_write, 2, audio_output->format().sampleRate()) + audio_ibuffer_timecode + ((double)c->get_clip_in_with_transition()/c->sequence->frame_rate) - ((double)timeline_in/last_fr), frame, nb_bytes, nests); - } - } else { - // shouldn't ever get here - dout << "[ERROR] Tried to cache a non-footage/tone clip"; - return; - } + // apply any audio effects to the data + if (nb_bytes == INT_MAX) nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; + if (new_frame) { + apply_audio_effects(c, bytes_to_seconds(c->audio_buffer_write, 2, current_audio_freq()) + audio_ibuffer_timecode + ((double)c->get_clip_in_with_transition()/c->sequence->frame_rate) - ((double)timeline_in/last_fr), frame, nb_bytes, nests); + } + } else { + // shouldn't ever get here + dout << "[ERROR] Tried to cache a non-footage/tone clip"; + return; + } // mix audio into internal buffer if (frame->nb_samples == 0) { break; } else { - long buffer_timeline_out = get_buffer_offset_from_frame(c->sequence->frame_rate, timeline_out); + long buffer_timeline_out = get_buffer_offset_from_frame(c->sequence->frame_rate, timeline_out); audio_write_lock.lock(); while (c->frame_sample_index < nb_bytes @@ -387,9 +387,9 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { audio_write_lock.unlock(); - if (scrubbing) { - if (audio_thread != NULL) audio_thread->notifyReceiver(); - } + if (scrubbing) { + if (audio_thread != NULL) audio_thread->notifyReceiver(); + } if (c->frame_sample_index == nb_bytes) { c->frame_sample_index = -1; @@ -403,9 +403,9 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { if (c->reached_end) { frame->nb_samples = 0; } - if (scrubbing) { - break; - } + if (scrubbing) { + break; + } } QMetaObject::invokeMethod(panel_footage_viewer, "play_wake", Qt::QueuedConnection); @@ -425,7 +425,7 @@ void cache_video_worker(Clip* c, long playhead) { limit *= 2; } - if (c->queue.size() < limit) { + if (c->queue.size() < limit) { bool reverse = (c->reverse && !c->ignore_reverse); c->ignore_reverse = false; @@ -443,40 +443,40 @@ void cache_video_worker(Clip* c, long playhead) { smallest_pts = target_pts; } - if (c->multithreaded && c->cacher->interrupt) { // ignore interrupts for now - c->cacher->interrupt = false; - } + if (c->multithreaded && c->cacher->interrupt) { // ignore interrupts for now + c->cacher->interrupt = false; + } while (true) { AVFrame* frame = av_frame_alloc(); - Footage* media = c->media->to_footage(); + Footage* media = c->media->to_footage(); FootageStream* ms = media->get_stream_from_file_index(true, c->media_stream); while ((retr_ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) { - if (c->multithreaded && c->cacher->interrupt) return; // abort + if (c->multithreaded && c->cacher->interrupt) return; // abort AVFrame* send_frame = c->frame; read_ret = (c->use_existing_frame) ? 0 : retrieve_next_frame(c, send_frame); c->use_existing_frame = false; if (read_ret >= 0) { - bool send_it = true; + bool send_it = true; - /*if (reverse) { + /*if (reverse) { send_it = true; } else if (send_frame->pts > target_pts - eighth_second) { send_it = true; } else if (media->get_stream_from_file_index(true, c->media_stream)->infinite_length) { send_it = true; - } else { + } else { dout << "skipped adding a frame to the queue - fpts:" << send_frame->pts << "target:" << target_pts; - }*/ + }*/ - if (send_it) { + if (send_it) { if ((send_ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, send_frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) { dout << "[ERROR] Failed to add frame to buffer source." << send_ret; break; - } + } } av_frame_unref(c->frame); @@ -510,7 +510,7 @@ void cache_video_worker(Clip* c, long playhead) { if (!ms->infinite_length && !reverse && c->queue.size() == limit) { // see if we got the frame we needed (used for speed ups primarily) bool found = false; - for (int i=0;iqueue.size();i++) { + for (int i=0;iqueue.size();i++) { if (c->queue.at(i)->pts >= target_pts) { found = true; break; @@ -528,92 +528,92 @@ void cache_video_worker(Clip* c, long playhead) { } } - if (c->multithreaded && c->cacher->interrupt) { // abort - return; - } + if (c->multithreaded && c->cacher->interrupt) { // abort + return; + } } } } void reset_cache(Clip* c, long target_frame) { - // if we seek to a whole other place in the timeline, we'll need to reset the cache with new values - if (c->media == NULL) { - if (c->track >= 0) { - // tone clip - c->reached_end = false; - c->audio_target_frame = target_frame; - c->frame_sample_index = -1; - c->frame->pts = 0; - } - } else { - FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); - if (ms->infinite_length) { - /*avcodec_flush_buffers(c->codecCtx); - av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD);*/ - c->use_existing_frame = false; - } else { - if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - // clear current queue - c->queue_clear(); + // if we seek to a whole other place in the timeline, we'll need to reset the cache with new values + if (c->media == NULL) { + if (c->track >= 0) { + // tone clip + c->reached_end = false; + c->audio_target_frame = target_frame; + c->frame_sample_index = -1; + c->frame->pts = 0; + } + } else { + FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + if (ms->infinite_length) { + /*avcodec_flush_buffers(c->codecCtx); + av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD);*/ + c->use_existing_frame = false; + } else { + if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + // clear current queue + c->queue_clear(); - // seeks to nearest keyframe (target_frame represents internal clip frame) - int64_t target_ts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame)); - int64_t seek_ts = target_ts; - int64_t timebase_half_second = qRound64(av_q2d(av_inv_q(c->stream->time_base))); - if (c->reverse) seek_ts -= timebase_half_second; + // seeks to nearest keyframe (target_frame represents internal clip frame) + int64_t target_ts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame)); + int64_t seek_ts = target_ts; + int64_t timebase_half_second = qRound64(av_q2d(av_inv_q(c->stream->time_base))); + if (c->reverse) seek_ts -= timebase_half_second; - while (true) { - // flush ffmpeg codecs - avcodec_flush_buffers(c->codecCtx); - c->reached_end = false; + while (true) { + // flush ffmpeg codecs + avcodec_flush_buffers(c->codecCtx); + c->reached_end = false; - if (seek_ts > 0) { - av_seek_frame(c->formatCtx, ms->file_index, seek_ts, AVSEEK_FLAG_BACKWARD); + if (seek_ts > 0) { + av_seek_frame(c->formatCtx, ms->file_index, seek_ts, AVSEEK_FLAG_BACKWARD); - av_frame_unref(c->frame); - int ret = retrieve_next_frame(c, c->frame); - if (ret < 0) { - dout << "[WARNING] Seeking terminated prematurely"; - break; - } - if (c->frame->pts <= target_ts) { - c->use_existing_frame = true; - break; - } else { - seek_ts -= timebase_half_second; - } - } else { - av_frame_unref(c->frame); - av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD); - c->use_existing_frame = false; - break; - } - } - } else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - // flush ffmpeg codecs - avcodec_flush_buffers(c->codecCtx); - c->reached_end = false; + av_frame_unref(c->frame); + int ret = retrieve_next_frame(c, c->frame); + if (ret < 0) { + dout << "[WARNING] Seeking terminated prematurely"; + break; + } + if (c->frame->pts <= target_ts) { + c->use_existing_frame = true; + break; + } else { + seek_ts -= timebase_half_second; + } + } else { + av_frame_unref(c->frame); + av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD); + c->use_existing_frame = false; + break; + } + } + } else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + // flush ffmpeg codecs + avcodec_flush_buffers(c->codecCtx); + c->reached_end = false; - // seek (target_frame represents timeline timecode in frames, not clip timecode) + // seek (target_frame represents timeline timecode in frames, not clip timecode) - int64_t timestamp = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame)); + int64_t timestamp = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame)); - if (c->reverse) { - c->reverse_target = timestamp; - timestamp -= av_q2d(av_inv_q(c->stream->time_base)); + if (c->reverse) { + c->reverse_target = timestamp; + timestamp -= av_q2d(av_inv_q(c->stream->time_base)); #ifdef AUDIOWARNINGS - dout << "seeking to" << timestamp << "(originally" << c->reverse_target << ")"; - } else { - dout << "reset called; seeking to" << timestamp; + dout << "seeking to" << timestamp << "(originally" << c->reverse_target << ")"; + } else { + dout << "reset called; seeking to" << timestamp; #endif - } - av_seek_frame(c->formatCtx, ms->file_index, timestamp, AVSEEK_FLAG_BACKWARD); - c->audio_target_frame = target_frame; - c->frame_sample_index = -1; - c->audio_just_reset = true; - } - } - } + } + av_seek_frame(c->formatCtx, ms->file_index, timestamp, AVSEEK_FLAG_BACKWARD); + c->audio_target_frame = target_frame; + c->frame_sample_index = -1; + c->audio_just_reset = true; + } + } + } } Cacher::Cacher(Clip* c) : clip(c) {} @@ -621,122 +621,122 @@ Cacher::Cacher(Clip* c) : clip(c) {} AVSampleFormat sample_format = AV_SAMPLE_FMT_S16; void open_clip_worker(Clip* clip) { - if (clip->media == NULL) { - if (clip->track >= 0) { - clip->frame = av_frame_alloc(); - clip->frame->format = sample_format; - clip->frame->channel_layout = clip->sequence->audio_layout; - clip->frame->channels = av_get_channel_layout_nb_channels(clip->frame->channel_layout); - clip->frame->sample_rate = audio_output->format().sampleRate(); - clip->frame->nb_samples = 2048; - av_frame_make_writable(clip->frame); - if (av_frame_get_buffer(clip->frame, 0)) { - dout << "[ERROR] Could not allocate buffer for tone clip"; - } - clip->audio_reset = true; - } - } else if (clip->media->get_type() == MEDIA_TYPE_FOOTAGE) { - // opens file resource for FFmpeg and prepares Clip struct for playback - Footage* m = clip->media->to_footage(); - QByteArray ba = m->url.toUtf8(); - const char* filename = ba.constData(); - FootageStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream); + if (clip->media == NULL) { + if (clip->track >= 0) { + clip->frame = av_frame_alloc(); + clip->frame->format = sample_format; + clip->frame->channel_layout = clip->sequence->audio_layout; + clip->frame->channels = av_get_channel_layout_nb_channels(clip->frame->channel_layout); + clip->frame->sample_rate = current_audio_freq(); + clip->frame->nb_samples = 2048; + av_frame_make_writable(clip->frame); + if (av_frame_get_buffer(clip->frame, 0)) { + dout << "[ERROR] Could not allocate buffer for tone clip"; + } + clip->audio_reset = true; + } + } else if (clip->media->get_type() == MEDIA_TYPE_FOOTAGE) { + // opens file resource for FFmpeg and prepares Clip struct for playback + Footage* m = clip->media->to_footage(); + QByteArray ba = m->url.toUtf8(); + const char* filename = ba.constData(); + FootageStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream); - int errCode = avformat_open_input( - &clip->formatCtx, - filename, - NULL, - NULL - ); - if (errCode != 0) { - char err[1024]; - av_strerror(errCode, err, 1024); - dout << "[ERROR] Could not open" << filename << "-" << err; - return; - } + int errCode = avformat_open_input( + &clip->formatCtx, + filename, + NULL, + NULL + ); + if (errCode != 0) { + char err[1024]; + av_strerror(errCode, err, 1024); + dout << "[ERROR] Could not open" << filename << "-" << err; + return; + } - errCode = avformat_find_stream_info(clip->formatCtx, NULL); - if (errCode < 0) { - char err[1024]; - av_strerror(errCode, err, 1024); - dout << "[ERROR] Could not open" << filename << "-" << err; - return; - } + errCode = avformat_find_stream_info(clip->formatCtx, NULL); + if (errCode < 0) { + char err[1024]; + av_strerror(errCode, err, 1024); + dout << "[ERROR] Could not open" << filename << "-" << err; + return; + } - av_dump_format(clip->formatCtx, 0, filename, 0); + av_dump_format(clip->formatCtx, 0, filename, 0); - clip->stream = clip->formatCtx->streams[ms->file_index]; - clip->codec = avcodec_find_decoder(clip->stream->codecpar->codec_id); - clip->codecCtx = avcodec_alloc_context3(clip->codec); - avcodec_parameters_to_context(clip->codecCtx, clip->stream->codecpar); + clip->stream = clip->formatCtx->streams[ms->file_index]; + clip->codec = avcodec_find_decoder(clip->stream->codecpar->codec_id); + clip->codecCtx = avcodec_alloc_context3(clip->codec); + avcodec_parameters_to_context(clip->codecCtx, clip->stream->codecpar); - clip->max_queue_size = (ms->infinite_length) ? 1 : qCeil(ms->video_frame_rate*0.5); - if (ms->video_interlacing != VIDEO_PROGRESSIVE) clip->max_queue_size *= 2; + clip->max_queue_size = (ms->infinite_length) ? 1 : qCeil(ms->video_frame_rate*0.5); + if (ms->video_interlacing != VIDEO_PROGRESSIVE) clip->max_queue_size *= 2; - clip->opts = NULL; + clip->opts = NULL; - // optimized decoding settings - if (clip->stream->codecpar->codec_id != AV_CODEC_ID_PNG && - clip->stream->codecpar->codec_id != AV_CODEC_ID_APNG && - clip->stream->codecpar->codec_id != AV_CODEC_ID_TIFF && - clip->stream->codecpar->codec_id != AV_CODEC_ID_PSD) { - av_dict_set(&clip->opts, "threads", "auto", 0); - } - if (clip->stream->codecpar->codec_id == AV_CODEC_ID_H264) { - av_dict_set(&clip->opts, "tune", "fastdecode", 0); - av_dict_set(&clip->opts, "tune", "zerolatency", 0); - } + // optimized decoding settings + if (clip->stream->codecpar->codec_id != AV_CODEC_ID_PNG && + clip->stream->codecpar->codec_id != AV_CODEC_ID_APNG && + clip->stream->codecpar->codec_id != AV_CODEC_ID_TIFF && + clip->stream->codecpar->codec_id != AV_CODEC_ID_PSD) { + av_dict_set(&clip->opts, "threads", "auto", 0); + } + if (clip->stream->codecpar->codec_id == AV_CODEC_ID_H264) { + av_dict_set(&clip->opts, "tune", "fastdecode", 0); + av_dict_set(&clip->opts, "tune", "zerolatency", 0); + } - // Open codec - if (avcodec_open2(clip->codecCtx, clip->codec, &clip->opts) < 0) { - dout << "[ERROR] Could not open codec"; - } + // Open codec + if (avcodec_open2(clip->codecCtx, clip->codec, &clip->opts) < 0) { + dout << "[ERROR] Could not open codec"; + } - // allocate filtergraph - clip->filter_graph = avfilter_graph_alloc(); - if (clip->filter_graph == NULL) { - dout << "[ERROR] Could not create filtergraph"; - } - char filter_args[512]; + // allocate filtergraph + clip->filter_graph = avfilter_graph_alloc(); + if (clip->filter_graph == NULL) { + dout << "[ERROR] Could not create filtergraph"; + } + char filter_args[512]; - if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - snprintf(filter_args, sizeof(filter_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", - clip->stream->codecpar->width, - clip->stream->codecpar->height, - clip->stream->codecpar->format, - clip->stream->time_base.num, - clip->stream->time_base.den, - clip->stream->codecpar->sample_aspect_ratio.num, - clip->stream->codecpar->sample_aspect_ratio.den - ); + if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + snprintf(filter_args, sizeof(filter_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", + clip->stream->codecpar->width, + clip->stream->codecpar->height, + clip->stream->codecpar->format, + clip->stream->time_base.num, + clip->stream->time_base.den, + clip->stream->codecpar->sample_aspect_ratio.num, + clip->stream->codecpar->sample_aspect_ratio.den + ); - avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("buffer"), "in", filter_args, NULL, clip->filter_graph); - avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("buffersink"), "out", NULL, NULL, clip->filter_graph); + avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("buffer"), "in", filter_args, NULL, clip->filter_graph); + avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("buffersink"), "out", NULL, NULL, clip->filter_graph); - AVFilterContext* last_filter = clip->buffersrc_ctx; + AVFilterContext* last_filter = clip->buffersrc_ctx; - if (ms->video_interlacing != VIDEO_PROGRESSIVE) { - AVFilterContext* yadif_filter; - char yadif_args[100]; - snprintf(yadif_args, sizeof(yadif_args), "mode=3:parity=%d", ((ms->video_interlacing == VIDEO_TOP_FIELD_FIRST) ? 0 : 1)); // there's a CUDA version if we start using nvdec/nvenc - avfilter_graph_create_filter(&yadif_filter, avfilter_get_by_name("yadif"), "yadif", yadif_args, NULL, clip->filter_graph); + if (ms->video_interlacing != VIDEO_PROGRESSIVE) { + AVFilterContext* yadif_filter; + char yadif_args[100]; + snprintf(yadif_args, sizeof(yadif_args), "mode=3:parity=%d", ((ms->video_interlacing == VIDEO_TOP_FIELD_FIRST) ? 0 : 1)); // there's a CUDA version if we start using nvdec/nvenc + avfilter_graph_create_filter(&yadif_filter, avfilter_get_by_name("yadif"), "yadif", yadif_args, NULL, clip->filter_graph); - avfilter_link(last_filter, 0, yadif_filter, 0); - last_filter = yadif_filter; - } + avfilter_link(last_filter, 0, yadif_filter, 0); + last_filter = yadif_filter; + } /* stabilization code */ - bool stabilize = false; - if (stabilize) { - AVFilterContext* stab_filter; - int stab_ret = avfilter_graph_create_filter(&stab_filter, avfilter_get_by_name("vidstabtransform"), "vidstab", "input=/media/matt/Home/samples/transforms.trf", NULL, clip->filter_graph); - if (stab_ret < 0) { - char err[100]; - av_strerror(stab_ret, err, sizeof(err)); - } else { - avfilter_link(last_filter, 0, stab_filter, 0); - last_filter = stab_filter; - } + bool stabilize = false; + if (stabilize) { + AVFilterContext* stab_filter; + int stab_ret = avfilter_graph_create_filter(&stab_filter, avfilter_get_by_name("vidstabtransform"), "vidstab", "input=/media/matt/Home/samples/transforms.trf", NULL, clip->filter_graph); + if (stab_ret < 0) { + char err[100]; + av_strerror(stab_ret, err, sizeof(err)); + } else { + avfilter_link(last_filter, 0, stab_filter, 0); + last_filter = stab_filter; + } } enum AVPixelFormat valid_pix_fmts[] = { @@ -750,102 +750,102 @@ void open_clip_worker(Clip* clip) { char format_args[100]; snprintf(format_args, sizeof(format_args), "pix_fmts=%s", chosen_format); - AVFilterContext* format_conv; + AVFilterContext* format_conv; avfilter_graph_create_filter(&format_conv, avfilter_get_by_name("format"), "fmt", format_args, NULL, clip->filter_graph); - avfilter_link(last_filter, 0, format_conv, 0); + avfilter_link(last_filter, 0, format_conv, 0); - avfilter_link(format_conv, 0, clip->buffersink_ctx, 0); + avfilter_link(format_conv, 0, clip->buffersink_ctx, 0); - avfilter_graph_config(clip->filter_graph, NULL); - } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - if (clip->codecCtx->channel_layout == 0) clip->codecCtx->channel_layout = av_get_default_channel_layout(clip->stream->codecpar->channels); + avfilter_graph_config(clip->filter_graph, NULL); + } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (clip->codecCtx->channel_layout == 0) clip->codecCtx->channel_layout = av_get_default_channel_layout(clip->stream->codecpar->channels); - // set up cache - clip->queue.append(av_frame_alloc()); - if (clip->reverse) { - AVFrame* reverse_frame = av_frame_alloc(); + // set up cache + clip->queue.append(av_frame_alloc()); + if (clip->reverse) { + AVFrame* reverse_frame = av_frame_alloc(); - reverse_frame->format = sample_format; - reverse_frame->nb_samples = audio_output->format().sampleRate()*2; - reverse_frame->channel_layout = clip->sequence->audio_layout; - reverse_frame->channels = av_get_channel_layout_nb_channels(clip->sequence->audio_layout); - av_frame_get_buffer(reverse_frame, 0); + reverse_frame->format = sample_format; + reverse_frame->nb_samples = current_audio_freq()*2; + reverse_frame->channel_layout = clip->sequence->audio_layout; + reverse_frame->channels = av_get_channel_layout_nb_channels(clip->sequence->audio_layout); + av_frame_get_buffer(reverse_frame, 0); - clip->queue.append(reverse_frame); - } + clip->queue.append(reverse_frame); + } - snprintf(filter_args, sizeof(filter_args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64, - clip->stream->time_base.num, - clip->stream->time_base.den, - clip->stream->codecpar->sample_rate, - av_get_sample_fmt_name(clip->codecCtx->sample_fmt), - clip->codecCtx->channel_layout - ); + snprintf(filter_args, sizeof(filter_args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64, + clip->stream->time_base.num, + clip->stream->time_base.den, + clip->stream->codecpar->sample_rate, + av_get_sample_fmt_name(clip->codecCtx->sample_fmt), + clip->codecCtx->channel_layout + ); - avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("abuffer"), "in", filter_args, NULL, clip->filter_graph); - avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("abuffersink"), "out", NULL, NULL, clip->filter_graph); + avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("abuffer"), "in", filter_args, NULL, clip->filter_graph); + avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("abuffersink"), "out", NULL, NULL, clip->filter_graph); - enum AVSampleFormat sample_fmts[] = { sample_format, static_cast(-1) }; - if (av_opt_set_int_list(clip->buffersink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN) < 0) { - dout << "[ERROR] Could not set output sample format"; - } + enum AVSampleFormat sample_fmts[] = { sample_format, static_cast(-1) }; + if (av_opt_set_int_list(clip->buffersink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN) < 0) { + dout << "[ERROR] Could not set output sample format"; + } - int64_t channel_layouts[] = { AV_CH_LAYOUT_STEREO, static_cast(-1) }; - if (av_opt_set_int_list(clip->buffersink_ctx, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN) < 0) { - dout << "[ERROR] Could not set output sample format"; - } + int64_t channel_layouts[] = { AV_CH_LAYOUT_STEREO, static_cast(-1) }; + if (av_opt_set_int_list(clip->buffersink_ctx, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN) < 0) { + dout << "[ERROR] Could not set output sample format"; + } - int target_sample_rate = audio_output->format().sampleRate(); + int target_sample_rate = current_audio_freq(); - if (qFuzzyCompare(clip->speed, 1.0)) { - avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0); - } else if (clip->maintain_audio_pitch) { - AVFilterContext* previous_filter = clip->buffersrc_ctx; - AVFilterContext* last_filter = clip->buffersrc_ctx; + if (qFuzzyCompare(clip->speed, 1.0)) { + avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0); + } else if (clip->maintain_audio_pitch) { + AVFilterContext* previous_filter = clip->buffersrc_ctx; + AVFilterContext* last_filter = clip->buffersrc_ctx; - char speed_param[10]; + char speed_param[10]; - if (clip->speed != 1.0) { - double base = (clip->speed > 1.0) ? 2.0 : 0.5; + if (clip->speed != 1.0) { + double base = (clip->speed > 1.0) ? 2.0 : 0.5; - double speedlog = log(clip->speed) / log(base); - int whole2 = qFloor(speedlog); - speedlog -= whole2; + double speedlog = log(clip->speed) / log(base); + int whole2 = qFloor(speedlog); + speedlog -= whole2; - if (whole2 > 0) { - snprintf(speed_param, sizeof(speed_param), "%f", base); - for (int i=0;ifilter_graph); - avfilter_link(previous_filter, 0, tempo_filter, 0); - previous_filter = tempo_filter; - } - } + if (whole2 > 0) { + snprintf(speed_param, sizeof(speed_param), "%f", base); + for (int i=0;ifilter_graph); + avfilter_link(previous_filter, 0, tempo_filter, 0); + previous_filter = tempo_filter; + } + } - snprintf(speed_param, sizeof(speed_param), "%f", qPow(base, speedlog)); - last_filter = NULL; - avfilter_graph_create_filter(&last_filter, avfilter_get_by_name("atempo"), "atempo", speed_param, NULL, clip->filter_graph); - avfilter_link(previous_filter, 0, last_filter, 0); - } + snprintf(speed_param, sizeof(speed_param), "%f", qPow(base, speedlog)); + last_filter = NULL; + avfilter_graph_create_filter(&last_filter, avfilter_get_by_name("atempo"), "atempo", speed_param, NULL, clip->filter_graph); + avfilter_link(previous_filter, 0, last_filter, 0); + } - avfilter_link(last_filter, 0, clip->buffersink_ctx, 0); - } else { - target_sample_rate = qRound64(target_sample_rate / clip->speed); - avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0); - } + avfilter_link(last_filter, 0, clip->buffersink_ctx, 0); + } else { + target_sample_rate = qRound64(target_sample_rate / clip->speed); + avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0); + } - int sample_rates[] = { target_sample_rate, 0 }; - if (av_opt_set_int_list(clip->buffersink_ctx, "sample_rates", sample_rates, 0, AV_OPT_SEARCH_CHILDREN) < 0) { - dout << "[ERROR] Could not set output sample rates"; - } + int sample_rates[] = { target_sample_rate, 0 }; + if (av_opt_set_int_list(clip->buffersink_ctx, "sample_rates", sample_rates, 0, AV_OPT_SEARCH_CHILDREN) < 0) { + dout << "[ERROR] Could not set output sample rates"; + } - avfilter_graph_config(clip->filter_graph, NULL); + avfilter_graph_config(clip->filter_graph, NULL); - clip->audio_reset = true; - } + clip->audio_reset = true; + } - clip->frame = av_frame_alloc(); - } + clip->frame = av_frame_alloc(); + } for (int i=0;ieffects.size();i++) { clip->effects.at(i)->open(); @@ -856,30 +856,30 @@ void open_clip_worker(Clip* clip) { dout << "[INFO] Clip opened on track" << clip->track; } -void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector nests) { - if (reset) { - // note: for video, playhead is in "internal clip" frames - for audio, it's the timeline playhead - reset_cache(clip, playhead); - clip->audio_reset = false; - } +void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector nests) { + if (reset) { + // note: for video, playhead is in "internal clip" frames - for audio, it's the timeline playhead + reset_cache(clip, playhead); + clip->audio_reset = false; + } - if (clip->media == NULL) { - if (clip->track >= 0) { - cache_audio_worker(clip, scrubbing, nests); - } - } else if (clip->media->get_type() == MEDIA_TYPE_FOOTAGE) { - if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - cache_video_worker(clip, playhead); - } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - cache_audio_worker(clip, scrubbing, nests); - } - } + if (clip->media == NULL) { + if (clip->track >= 0) { + cache_audio_worker(clip, scrubbing, nests); + } + } else if (clip->media->get_type() == MEDIA_TYPE_FOOTAGE) { + if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + cache_video_worker(clip, playhead); + } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + cache_audio_worker(clip, scrubbing, nests); + } + } } void close_clip_worker(Clip* clip) { clip->finished_opening = false; - if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE) { + if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE) { clip->queue_clear(); avfilter_graph_free(&clip->filter_graph); @@ -887,46 +887,46 @@ void close_clip_worker(Clip* clip) { avcodec_close(clip->codecCtx); avcodec_free_context(&clip->codecCtx); - av_dict_free(&clip->opts); + av_dict_free(&clip->opts); avformat_close_input(&clip->formatCtx); } av_frame_free(&clip->frame); - clip->reset(); + clip->reset(); dout << "[INFO] Clip closed on track" << clip->track; } void Cacher::run() { // open_lock is used to prevent the clip from being destroyed before the cacher has closed it properly - clip->lock.lock(); - clip->finished_opening = false; - clip->open = true; - caching = true; - interrupt = false; + clip->lock.lock(); + clip->finished_opening = false; + clip->open = true; + caching = true; + interrupt = false; open_clip_worker(clip); while (caching) { clip->can_cache.wait(&clip->lock); - if (!caching) { + if (!caching) { break; } else { - while (true) { - cache_clip_worker(clip, playhead, reset, scrubbing, nests); - if (clip->multithreaded && clip->cacher->interrupt && clip->track < 0) { - clip->cacher->interrupt = false; - } else { - break; - } - } + while (true) { + cache_clip_worker(clip, playhead, reset, scrubbing, nests); + if (clip->multithreaded && clip->cacher->interrupt && clip->track < 0) { + clip->cacher->interrupt = false; + } else { + break; + } + } } } - close_clip_worker(clip); + close_clip_worker(clip); - clip->lock.unlock(); + clip->lock.unlock(); clip->open_lock.unlock(); } From 05524153ab8fef7550c86a994a3b4ad636eb47ad Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 16:12:10 +1100 Subject: [PATCH 17/65] began refactoring keyframe times --- project/clip.cpp | 20 ++++++-------------- project/undo.cpp | 39 +++++++++++++++++---------------------- project/undo.h | 30 ++++++++++++++---------------- 3 files changed, 37 insertions(+), 52 deletions(-) diff --git a/project/clip.cpp b/project/clip.cpp index 88258441f..883705b2f 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -309,25 +309,17 @@ void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_t track); } - QVector key_rows; - QVector key_indices; - QVector key_old; - QVector key_new; - + // move keyframes for (int i=0;irow_count();j++) { EffectRow* r = e->row(j); - for (int k=0;kkeyframe_times.size();k++) { - key_rows.append(r); - key_indices.at(k); - key_old.append(r->keyframe_times.at(k)); - key_new.append(r->keyframe_times.at(k) * multiplier); + for (int l=0;lfieldCount();l++) { + EffectField* f = r->field(l); + for (int k=0;kkeyframes.size();k++) { + ca->append(new SetLong(&f->keyframes[k].time, f->keyframes[k].time, f->keyframes[k].time * multiplier)); + } } } } - - if (key_rows.size() > 0) { - ca->append(new KeyframeMove(key_rows, key_indices, key_old, key_new)); - } } diff --git a/project/undo.cpp b/project/undo.cpp index 963b02c32..b1a7caf04 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -737,28 +737,6 @@ void MediaRename::redo() { mainWindow->setWindowModified(true); } -KeyframeMove::KeyframeMove(const QVector& irows, const QVector& ikeyframes, const QVector& iold_values, const QVector& inew_values) : - rows(irows), - keyframes(ikeyframes), - old_values(iold_values), - new_values(inew_values), - old_project_changed(mainWindow->isWindowModified()) -{} - -void KeyframeMove::undo() { - for (int i=0;ikeyframe_times[keyframes.at(i)] = old_values.at(i); - } - mainWindow->setWindowModified(old_project_changed); -} - -void KeyframeMove::redo() { - for (int i=0;ikeyframe_times[keyframes.at(i)] = new_values.at(i); - } - mainWindow->setWindowModified(true); -} - KeyframeDelete::KeyframeDelete() : disable_keyframes_on_row(NULL), old_project_changed(mainWindow->isWindowModified()), @@ -1332,3 +1310,20 @@ void SetQVariant::undo() { void SetQVariant::redo() { *target = new_val; } + +SetLong::SetLong(long *pointer, long old_value, long new_value) : + p(pointer), + oldval(old_value), + newval(new_value), + old_project_changed(mainWindow->isWindowModified()) +{} + +void SetLong::undo() { + *p = oldval; + mainWindow->setWindowModified(old_project_changed); +} + +void SetLong::redo() { + *p = newval; + mainWindow->setWindowModified(true); +} diff --git a/project/undo.h b/project/undo.h index e74d09a25..1e9fde486 100644 --- a/project/undo.h +++ b/project/undo.h @@ -17,6 +17,7 @@ struct EffectMeta; #include "project/marker.h" #include "project/selection.h" +#include "project/effectfield.h" #include #include @@ -328,19 +329,6 @@ private: QString to; }; -class KeyframeMove : public QUndoCommand { -public: - KeyframeMove(const QVector& rows, const QVector& keyframes, const QVector& old_values, const QVector& new_values); - void undo(); - void redo(); -private: - QVector rows; - QVector keyframes; - QVector old_values; - QVector new_values; - bool old_project_changed; -}; - class KeyframeDelete : public QUndoCommand { public: KeyframeDelete(); @@ -351,9 +339,7 @@ public: void redo(); private: bool old_project_changed; - QVector deleted_keyframe_times; - QVector deleted_keyframe_types; - QVector deleted_keyframe_data; + QVector deleted_keyframe_times; bool sorted; }; @@ -524,6 +510,18 @@ private: bool old_project_changed; }; +class SetLong : public QUndoCommand { +public: + SetLong(long* pointer, long old_value, long new_value); + void undo(); + void redo(); +private: + long* p; + long oldval; + long newval; + bool old_project_changed; +}; + class SetDouble : public QUndoCommand { public: SetDouble(double* pointer, double new_value); From 37f21d2ff732359a823ffc39886249741b41c767 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 22:30:32 +1100 Subject: [PATCH 18/65] refactored keyframes to fields rather than rows --- project/effect.cpp | 38 ++++++------ project/effectfield.cpp | 22 +++---- project/effectrow.cpp | 101 ++++++++++++++++++-------------- project/effectrow.h | 5 +- project/undo.cpp | 89 ++++++---------------------- project/undo.h | 15 +++-- ui/graphview.cpp | 78 +++++++++++++------------ ui/keyframedrawing.cpp | 2 + ui/keyframeview.cpp | 126 +++++++++++++++++++++++----------------- ui/keyframeview.h | 5 +- 10 files changed, 236 insertions(+), 245 deletions(-) diff --git a/project/effect.cpp b/project/effect.cpp index 0f75405b2..1be29a308 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -456,13 +456,11 @@ void Effect::copy_field_keyframes(Effect* e) { for (int i=0;irows.at(i); - copy_row->setKeyframing(row->isKeyframing()); - copy_row->keyframe_times = row->keyframe_times; - copy_row->keyframe_types = row->keyframe_types; + copy_row->setKeyframing(row->isKeyframing()); for (int j=0;jfieldCount();j++) { - EffectField* field = row->field(j); - copy_row->field(j)->set_current_data(field->get_current_data()); - copy_row->field(j)->keyframe_data = field->keyframe_data; + EffectField* field = row->field(j); + EffectField* copy_field = copy_row->field(j); + copy_field->keyframes = field->keyframes; } } } @@ -633,8 +631,13 @@ void Effect::load(QXmlStreamReader& stream) { keyframe_type = attr.value().toInt(); } } - row->keyframe_times.append(keyframe_frame); - row->keyframe_types.append(keyframe_type); + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + EffectKeyframe key; + key.time = keyframe_frame; + key.type = keyframe_type; + field->keyframes.append(key); + } } stream.readNext(); } @@ -677,13 +680,15 @@ void Effect::load(QXmlStreamReader& stream) { } } + int field_index = 0; while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) { stream.readNext(); // read all keyframes if (stream.name() == "key" && stream.isStartElement()) { stream.readNext(); - field->keyframe_data.append(load_data_from_string(field->type, stream.text().toString())); + field->keyframes[field_index].data = load_data_from_string(field->type, stream.text().toString()); + field_index++; } } } else { @@ -711,20 +716,19 @@ void Effect::save(QXmlStreamWriter& stream) { stream.writeStartElement("row"); // row stream.writeStartElement("keyframes"); // keyframes stream.writeAttribute("enabled", QString::number(row->isKeyframing())); - for (int j=0;jkeyframe_times.size();j++) { - stream.writeStartElement("key"); // key - stream.writeAttribute("frame", QString::number(row->keyframe_times.at(j))); - stream.writeAttribute("type", QString::number(row->keyframe_types.at(j))); - stream.writeEndElement(); // key - } stream.writeEndElement(); // keyframes for (int j=0;jfieldCount();j++) { EffectField* field = row->field(j); stream.writeStartElement("field"); // field stream.writeAttribute("id", field->id); stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data())); - for (int k=0;kkeyframe_data.size();k++) { - stream.writeTextElement("key", save_data_to_string(field->type, field->keyframe_data.at(k))); + for (int k=0;kkeyframes.size();k++) { + const EffectKeyframe& key = field->keyframes.at(k); + stream.writeStartElement("key"); + stream.writeAttribute("value", save_data_to_string(field->type, key.data)); + stream.writeAttribute("frame", QString::number(key.time)); + stream.writeAttribute("type", QString::number(key.type)); + stream.writeEndElement(); // key } stream.writeEndElement(); // field } diff --git a/project/effectfield.cpp b/project/effectfield.cpp index f465e5e27..00435b91d 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -121,8 +121,8 @@ void EffectField::get_keyframe_data(double timecode, int &before, int &after, do long after_keyframe_time = LONG_MAX; long frame = timecodeToFrame(timecode); - for (int i=0;ikeyframe_times.size();i++) { - long eval_keyframe_time = parent_row->keyframe_times.at(i); + for (int i=0;ikeyframe_types.at(before) == KEYFRAME_TYPE_HOLD) { + if (keyframes.at(before).type == KEYFRAME_TYPE_HOLD) { progress = 0; } else { // TODO replace with bezier function @@ -157,7 +157,7 @@ void EffectField::get_keyframe_data(double timecode, int &before, int &after, do } bool EffectField::hasKeyframes() { - return (parent_row->isKeyframing() && keyframe_data.size() > 0); + return (parent_row->isKeyframing() && keyframes.size() > 0); } QVariant EffectField::validate_keyframe_data(double timecode, bool async) { @@ -175,16 +175,16 @@ QVariant EffectField::validate_keyframe_data(double timecode, bool async) { progress -= 0.01865; }*/ - const QVariant& before_data = keyframe_data.at(before_keyframe); + const QVariant& before_data = keyframes.at(before_keyframe).data; switch (type) { case EFFECT_FIELD_DOUBLE: { double value; if (before_keyframe == after_keyframe) { - value = keyframe_data.at(before_keyframe).toDouble(); + value = keyframes.at(before_keyframe).data.toDouble(); } else { - double before_dbl = keyframe_data.at(before_keyframe).toDouble(); - double after_dbl = keyframe_data.at(after_keyframe).toDouble(); + double before_dbl = keyframes.at(before_keyframe).data.toDouble(); + double after_dbl = keyframes.at(after_keyframe).data.toDouble(); value = double_lerp(before_dbl, after_dbl, progress); } if (async) { @@ -197,10 +197,10 @@ QVariant EffectField::validate_keyframe_data(double timecode, bool async) { { QColor value; if (before_keyframe == after_keyframe) { - value = keyframe_data.at(before_keyframe).value(); + value = keyframes.at(before_keyframe).data.value(); } else { - QColor before_data = keyframe_data.at(before_keyframe).value(); - QColor after_data = keyframe_data.at(after_keyframe).value(); + QColor before_data = keyframes.at(before_keyframe).data.value(); + QColor after_data = keyframes.at(after_keyframe).data.value(); value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress)); } if (async) { diff --git a/project/effectrow.cpp b/project/effectrow.cpp index 28d4cfaeb..db86e75f3 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -61,12 +61,14 @@ void EffectRow::set_keyframe_enabled(bool enabled) { } else { if (QMessageBox::question(panel_effect_controls, "Disable Keyframes", "Disabling keyframes will delete all current keyframes. Are you sure you want to do this?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { // clear - KeyframeDelete* kd = new KeyframeDelete(); - for (int i=keyframe_times.size()-1;i>=0;i--) { - delete_keyframe(kd, i); + ComboAction* ca = new ComboAction(); + for (int i=0;ikeyframes.size();j++) { + ca->append(new KeyframeDelete(f, 0)); + } } - kd->disable_keyframes_on_row = this; - undo_stack.push(kd); + undo_stack.push(ca); panel_effect_controls->update_keyframes(); } else { setKeyframing(true); @@ -77,49 +79,61 @@ void EffectRow::set_keyframe_enabled(bool enabled) { void EffectRow::goto_previous_key() { long key = LONG_MIN; Clip* c = parent_effect->parent_clip; - for (int i=0;iclip_in + c->timeline_in; - if (comp < sequence->playhead) { - key = qMax(comp, key); + for (int i=0;ikeyframes.size();j++) { + long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; + if (comp < sequence->playhead) { + key = qMax(comp, key); + } } } if (key != LONG_MIN) panel_sequence_viewer->seek(key); } void EffectRow::toggle_key() { - int index = -1; + QVector key_fields; + QVector key_field_index; Clip* c = parent_effect->parent_clip; - for (int i=0;itimeline_in - c->clip_in + keyframe_times.at(i); - if (comp == sequence->playhead) { - index = i; - break; + for (int j=0;jkeyframes.size();i++) { + long comp = c->timeline_in - c->clip_in + f->keyframes.at(i).time; + if (comp == sequence->playhead) { + key_fields.append(f); + key_field_index.append(i); + } } } - if (index < 0) { + + ComboAction* ca = new ComboAction(); + if (key_fields.size() == 0) { // keyframe doesn't exist, set one - ComboAction* ca = new ComboAction(); set_keyframe_now(ca); - undo_stack.push(ca); } else { - KeyframeDelete* kd = new KeyframeDelete(); - delete_keyframe(kd, index); - undo_stack.push(kd); - panel_effect_controls->update_keyframes(); - panel_sequence_viewer->viewer_widget->update(); + for (int i=0;iappend(new KeyframeDelete(key_fields.at(i), key_field_index.at(i))); + } } + undo_stack.push(ca); + panel_effect_controls->update_keyframes(); + panel_sequence_viewer->viewer_widget->update(); } void EffectRow::goto_next_key() { long key = LONG_MAX; Clip* c = parent_effect->parent_clip; - for (int i=0;itimeline_in - c->clip_in + keyframe_times.at(i); - if (comp > sequence->playhead) { - key = qMin(comp, key); + for (int i=0;ikeyframes.size();j++) { + long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; + if (comp > sequence->playhead) { + key = qMax(comp, key); + } } } - if (key != LONG_MAX) panel_sequence_viewer->seek(key); + if (key != LONG_MAX) panel_sequence_viewer->seek(key); } void EffectRow::focus_row() { @@ -145,10 +159,13 @@ EffectRow::~EffectRow() { void EffectRow::set_keyframe_now(ComboAction* ca) { int index = -1; long time = sequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; - for (int i=0;ikeyframes.size();i++) { + if (f->keyframes.at(i).time == time) { + index = i; + break; + } } } @@ -166,24 +183,22 @@ void EffectRow::set_keyframe_now(ComboAction* ca) { panel_effect_controls->update_keyframes(); } -void EffectRow::delete_keyframe_at_time(KeyframeDelete* kd, long time) { - for (int i=0;ikeyframes.size();i++) { + if (f->keyframes.at(i).time == time) { + ca->append(new KeyframeDelete(f, i)); + break; + } } - } + } } const QString &EffectRow::get_name() { return name; } -void EffectRow::delete_keyframe(KeyframeDelete* kd, int index) { - kd->rows.append(this); - kd->keyframes.append(index); -} - EffectField* EffectRow::field(int i) { return fields.at(i); } diff --git a/project/effectrow.h b/project/effectrow.h index 672c00b16..a91171545 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -23,9 +23,8 @@ public: EffectField* add_field(int type, const QString &id, int colspan = 1); EffectField* field(int i); int fieldCount(); - void set_keyframe_now(ComboAction *ca); - void delete_keyframe(KeyframeDelete *kd, int index); - void delete_keyframe_at_time(KeyframeDelete* kd, long time); + void set_keyframe_now(ComboAction *ca); + void delete_keyframe_at_time(ComboAction *ca, long time); ClickableLabel* label; Effect* parent_effect; bool savable; diff --git a/project/undo.cpp b/project/undo.cpp index b1a7caf04..5187d2a6e 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -737,69 +737,21 @@ void MediaRename::redo() { mainWindow->setWindowModified(true); } -KeyframeDelete::KeyframeDelete() : - disable_keyframes_on_row(NULL), - old_project_changed(mainWindow->isWindowModified()), - sorted(false) +KeyframeDelete::KeyframeDelete(EffectField *ifield, int iindex) : + field(ifield), + index(iindex), + old_project_changed(mainWindow->isWindowModified()) {} void KeyframeDelete::undo() { - if (disable_keyframes_on_row != NULL) disable_keyframes_on_row->setKeyframing(true); - - int data_index = deleted_keyframe_data.size()-1; - for (int i=rows.size()-1;i>=0;i--) { - EffectRow* row = rows.at(i); - int keyframe_index = keyframes.at(i); - - row->keyframe_times.insert(keyframe_index, deleted_keyframe_times.at(i)); - row->keyframe_types.insert(keyframe_index, deleted_keyframe_types.at(i)); - - for (int j=row->fieldCount()-1;j>=0;j--) { - row->field(j)->keyframe_data.insert(keyframe_index, deleted_keyframe_data.at(data_index)); - data_index--; - } - } - + field->keyframes.insert(index, deleted_key); mainWindow->setWindowModified(old_project_changed); } void KeyframeDelete::redo() { - if (!sorted) { - deleted_keyframe_times.resize(rows.size()); - deleted_keyframe_types.resize(rows.size()); - deleted_keyframe_data.clear(); - } - - for (int i=0;ikeyframe_times.at(keyframe_index)); - deleted_keyframe_types[i] = (row->keyframe_types.at(keyframe_index)); - } - - row->keyframe_times.removeAt(keyframe_index); - row->keyframe_types.removeAt(keyframe_index); - - for (int j=0;jfieldCount();j++) { - if (!sorted) deleted_keyframe_data.append(row->field(j)->keyframe_data.at(keyframe_index)); - row->field(j)->keyframe_data.removeAt(keyframe_index); - } - - // correct following indices - if (!sorted) { - for (int j=i+1;j keyframe_index) { - keyframes[j]--; - } - } - } - } - - if (disable_keyframes_on_row != NULL) disable_keyframes_on_row->setKeyframing(false); - mainWindow->setWindowModified(true); - sorted = true; + deleted_key = field->keyframes.at(index); + field->keyframes.removeAt(index); + mainWindow->setWindowModified(true); } KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : @@ -819,7 +771,7 @@ KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : if (field->type == EFFECT_FIELD_DOUBLE) { old_values[i] = static_cast(field->ui_element)->getPreviousValue(); } else { - old_values[i] = field->keyframe_data.at(index); + old_values[i] = field->keyframes.at(index).data; } } new_values[i] = field->get_current_data(); @@ -829,16 +781,12 @@ KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : void KeyframeSet::undo() { if (enable_keyframes) row->setKeyframing(false); - bool append = (index == -1 || just_made_keyframe); - if (append) { - row->keyframe_times.removeLast(); - row->keyframe_types.removeLast(); - } + bool append = (index == -1 || just_made_keyframe); for (int i=0;ifieldCount();i++) { if (append) { - row->field(i)->keyframe_data.removeLast(); + row->field(i)->keyframes.removeLast(); } else { - row->field(i)->keyframe_data[index] = old_values.at(i); + row->field(i)->keyframes[index].data = old_values.at(i); } } @@ -848,15 +796,16 @@ void KeyframeSet::undo() { void KeyframeSet::redo() { bool append = (index == -1 || (just_made_keyframe && !done)); - if (append) { - row->keyframe_times.append(time); - row->keyframe_types.append((row->keyframe_types.size() > 0) ? row->keyframe_types.last() : EFFECT_KEYFRAME_LINEAR); - } for (int i=0;ifieldCount();i++) { + EffectField* f = row->field(i); if (append) { - row->field(i)->keyframe_data.append(new_values.at(i)); + EffectKeyframe k; + k.data = new_values.at(i); + k.time = time; + k.type = (f->keyframes.size() > 0) ? f->keyframes.last().type : EFFECT_KEYFRAME_LINEAR; + f->keyframes.append(k); } else { - row->field(i)->keyframe_data[index] = new_values.at(i); + f->keyframes[index].data = new_values.at(i); } } row->setKeyframing(true); diff --git a/project/undo.h b/project/undo.h index 1e9fde486..9ef3a1e3b 100644 --- a/project/undo.h +++ b/project/undo.h @@ -331,22 +331,21 @@ private: class KeyframeDelete : public QUndoCommand { public: - KeyframeDelete(); - QVector rows; - EffectRow* disable_keyframes_on_row; - QVector keyframes; + KeyframeDelete(EffectField* ifield, int iindex); void undo(); void redo(); private: - bool old_project_changed; - QVector deleted_keyframe_times; - bool sorted; + EffectField* field; + int index; + bool done; + EffectKeyframe deleted_key; + bool old_project_changed; }; class KeyframeSet : public QUndoCommand { public: - KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); + KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); void undo(); void redo(); QVector old_values; diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 4fc859645..5accaacad 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -75,31 +75,34 @@ void GraphView::paintEvent(QPaintEvent *event) { QPen line_pen; line_pen.setWidth(2); - // sort keyframes by time - QVector sorted_keys; - for (int i=0;ikeyframe_times.size();i++) { - bool inserted = false; - for (int j=0;jkeyframe_times.at(sorted_keys.at(j)) > row->keyframe_times.at(i)) { - sorted_keys.insert(j, i); - inserted = true; - break; - } - } - if (!inserted) { - sorted_keys.append(i); - } - } + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + + if (field->type == EFFECT_FIELD_DOUBLE) { + // sort keyframes by time + QVector sorted_keys; + for (int k=0;kkeyframes.size();k++) { + bool inserted = false; + for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { + sorted_keys.insert(j, k); + inserted = true; + break; + } + } + if (!inserted) { + sorted_keys.append(i); + } + } + + int last_key_x, last_key_y; - int last_key_x, last_key_y; - for (int i=0;ifieldCount();i++) { - EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE) { for (int j=0;jkeyframe_times.at(key_index)); - int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); + int key_x = get_screen_x(field->keyframes.at(key_index).time); + int key_y = get_screen_y(field->keyframes.at(key_index).data.toDouble()); line_pen.setColor(get_curve_color(i, row->fieldCount())); p.setPen(line_pen); @@ -112,14 +115,14 @@ void GraphView::paintEvent(QPaintEvent *event) { last_key_y = key_y; } for (int j=0;jkeyframe_times.at(key_index)); - int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); + int key_x = get_screen_x(field->keyframes.at(key_index).time); + int key_y = get_screen_y(field->keyframes.at(key_index).data.toDouble()); - draw_keyframe(p, row->keyframe_types.at(key_index), key_x, key_y, (selected_keys.contains(key_index) && selected_keys_fields.contains(i))); + draw_keyframe(p, field->keyframes.at(key_index).type, key_x, key_y, (selected_keys.contains(key_index) && selected_keys_fields.contains(i))); } - p.setBrush(Qt::NoBrush); } } } @@ -154,9 +157,9 @@ void GraphView::mousePressEvent(QMouseEvent *event) { for (int i=0;ifieldCount();i++) { EffectField* field = row->field(i); if (field->type == EFFECT_FIELD_DOUBLE) { - for (int j=0;jkeyframe_times.size();j++) { - int key_x = get_screen_x(row->keyframe_times.at(j)); - int key_y = get_screen_y(field->keyframe_data.at(j).toDouble()); + for (int j=0;jkeyframes.size();j++) { + int key_x = get_screen_x(field->keyframes.at(j).time); + int key_y = get_screen_y(field->keyframes.at(j).data.toDouble()); if (event->pos().x() > key_x-KEYFRAME_SIZE && event->pos().x() < key_x+KEYFRAME_SIZE && event->pos().y() > key_y-KEYFRAME_SIZE @@ -177,10 +180,9 @@ void GraphView::mousePressEvent(QMouseEvent *event) { selected_keys_old_vals.clear(); selected_keys_old_doubles.clear(); for (int i=0;ikeyframe_times.at(selected_keys.at(i))); - for (int j=0;jfield(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)).toDouble()); + selected_keys_old_vals.append(row->field(selected_keys_fields.at(j))->keyframes.at(selected_keys.at(i)).time); + selected_keys_old_doubles.append(row->field(selected_keys_fields.at(j))->keyframes.at(selected_keys.at(i)).data.toDouble()); } } @@ -199,10 +201,10 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { update(); } else { for (int i=0;ikeyframe_times[selected_keys.at(i)] = selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom); - for (int j=0;jfield(selected_keys_fields.at(j))->keyframe_data[selected_keys.at(i)] = selected_keys_old_doubles.at((i*selected_keys_fields.size())+j) + (double(start_y - event->pos().y())/zoom); + int index = (i*selected_keys_fields.size())+j; + row->field(selected_keys_fields.at(j))->keyframes[selected_keys.at(i)].time = selected_keys_old_vals.at(index) + (double(event->pos().x() - start_x)/zoom); + row->field(selected_keys_fields.at(j))->keyframes[selected_keys.at(i)].data = selected_keys_old_doubles.at(index) + (double(start_y - event->pos().y())/zoom); } } moved_keys = true; @@ -213,11 +215,11 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { void GraphView::mouseReleaseEvent(QMouseEvent *event) { if (moved_keys && selected_keys.size() > 0) { - ComboAction* ca = new ComboAction(); + /*ComboAction* ca = new ComboAction(); QVector rows; QVector new_vals; - for (int i=0;ikeyframe_times.at(selected_keys.at(i))); @@ -227,10 +229,10 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { row->field(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)))); } //ca->append(new KeyframeSet(row, selected_keys.at(i), 0, false)); - } + } ca->append(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); - undo_stack.push(ca); + undo_stack.push(ca);*/ } moved_keys = false; mousedown = false; diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp index 693a4e299..07d515957 100644 --- a/ui/keyframedrawing.cpp +++ b/ui/keyframedrawing.cpp @@ -23,4 +23,6 @@ void draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2)); break; } + + p.setBrush(Qt::NoBrush); } diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index b37f71b17..7518c4575 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -44,7 +44,7 @@ KeyframeView::KeyframeView(QWidget *parent) : } void KeyframeView::show_context_menu(const QPoint& pos) { - if (selected_rows.size() > 0) { + if (selected_fields.size() > 0) { QMenu menu(this); QAction* linear = menu.addAction("Linear"); @@ -67,8 +67,9 @@ void KeyframeView::menu_set_key_type(QAction* a) { panel_graph_editor->show(); } else { ComboAction* ca = new ComboAction(); - for (int i=0;iappend(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt())); + for (int i=0;iappend(new SetInt(&f->keyframes[selected_keyframes.at(i)].type, a->data().toInt())); } undo_stack.push(ca); update_keys(); @@ -102,12 +103,19 @@ void KeyframeView::paintEvent(QPaintEvent*) { ClickableLabel* label = row->label; QWidget* contents = e->container->contents; + QVector key_times; int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height()/* - y_scroll*/; - for (int k=0;kkeyframe_times.size();k++) { - bool keyframe_selected = keyframeIsSelected(row, k); - long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(k)); - draw_keyframe(p, row->keyframe_types.at(k), getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); - } + for (int l=0;lfieldCount();l++) { + EffectField* f = row->field(l); + for (int k=0;kkeyframes.size();k++) { + if (!key_times.contains(f->keyframes.at(k).time)) { + bool keyframe_selected = keyframeIsSelected(f, k); + long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time); + draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); + key_times.append(f->keyframes.at(k).time); + } + } + } rows.append(row); rowY.append(keyframe_y); @@ -141,9 +149,9 @@ void KeyframeView::paintEvent(QPaintEvent*) { }*/ } -bool KeyframeView::keyframeIsSelected(EffectRow *row, int keyframe) { - for (int i=0;idelete_keyframe(kd, selected_keyframes.at(i)); + // TODO these need to be sorted + ca->append(new KeyframeDelete(selected_fields.at(i), selected_keyframes.at(i))); del = true; } if (del) { - undo_stack.push(kd); + undo_stack.push(ca); selected_keyframes.clear(); - selected_rows.clear(); + selected_fields.clear(); update_keys(); panel_sequence_viewer->viewer_widget->update(); } else { - delete kd; + delete ca; } } @@ -202,6 +211,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { int mouse_x = event->x() + x_scroll; int mouse_y = event->y(); int row_index = -1; + int field_index = -1; int keyframe_index = -1; long frame_diff = 0; long frame_min = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x-KEYFRAME_SIZE); @@ -213,37 +223,41 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { row->focus_row(); - for (int j=0;jkeyframe_times.size();j++) { - long eval_keyframe_time = row->keyframe_times.at(j)-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); - if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { - long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start); - if (keyframe_index == -1 || eval_frame_diff < frame_diff) { - row_index = i; - keyframe_index = j; - frame_diff = eval_frame_diff; - } - } - } + for (int k=0;kfieldCount();k++) { + EffectField* f = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long eval_keyframe_time = f->keyframes.at(j).time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); + if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { + long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start); + if (keyframe_index == -1 || eval_frame_diff < frame_diff) { + row_index = i; + field_index = k; + keyframe_index = j; + frame_diff = eval_frame_diff; + } + } + } + } break; } } bool already_selected = false; keys_selected = false; - if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index), keyframe_index); + if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); if (!already_selected) { if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_rows.clear(); + selected_fields.clear(); selected_keyframes.clear(); } if (keyframe_index > -1) { - selected_rows.append(rows.at(row_index)); + selected_fields.append(rows.at(row_index)->field(field_index)); selected_keyframes.append(keyframe_index); } } - if (selected_rows.size() > 0) { - for (int i=0;ikeyframe_times.at(selected_keyframes.at(i))); + if (selected_fields.size() > 0) { + for (int i=0;ikeyframes.at(selected_keyframes.at(i)).time); } keys_selected = true; @@ -277,8 +291,8 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { panel_timeline->snapped = false; if (panel_timeline->snapping) { for (int i=0;iparent_effect->parent_clip; + EffectField* field = selected_fields.at(i); + Clip* c = field->parent_row->parent_effect->parent_clip; long key_time = old_key_vals.at(i) + frame_diff - c->clip_in + c->timeline_in; long key_eval = key_time; if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) { @@ -289,11 +303,11 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } // validate frame_diff (make sure no keyframes overlap each other) - for (int i=0;ikeyframe_times.size();j++) { - while (!keyframeIsSelected(row, j) && row->keyframe_times.at(j) == eval_key + frame_diff) { + for (int j=0;jkeyframes.size();j++) { + while (!keyframeIsSelected(field, j) && field->keyframes.at(j).time == eval_key + frame_diff) { if (last_frame_diff > frame_diff) { frame_diff++; panel_timeline->snapped = false; @@ -307,8 +321,8 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { // apply frame_diffs for (int i=0;ikeyframe_times[selected_keyframes.at(i)] = old_key_vals.at(i) + frame_diff; + EffectField* field = selected_fields.at(i); + field->keyframes[selected_keyframes.at(i)].time = old_key_vals.at(i) + frame_diff; } last_frame_diff = frame_diff; @@ -332,13 +346,16 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { for (int i=0;i= min_row && rowY.at(i) <= max_row) { EffectRow* row = rows.at(i); - for (int j=0;jkeyframe_times.size();j++) { - long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(j)); - if (!keyframeIsSelected(row, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { - selected_rows.append(rows.at(i)); - selected_keyframes.append(j); - } - } + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); + if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { + selected_fields.append(field); + selected_keyframes.append(j); + } + } + } } } @@ -351,12 +368,15 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { void KeyframeView::mouseReleaseEvent(QMouseEvent*) { if (dragging) { - QVector new_key_vals; - for (int i=0;ikeyframe_times.at(selected_keyframes.at(i))); + ComboAction* ca = new ComboAction(); + for (int i=0;iappend(new SetLong( + &selected_fields.at(i)->keyframes[selected_keyframes.at(i)].time, + old_key_vals.at(i), + selected_fields.at(i)->keyframes.at(selected_keyframes.at(i)).time + )); } - - undo_stack.push(new KeyframeMove(selected_rows, selected_keyframes, old_key_vals, new_key_vals)); + undo_stack.push(ca); } select_rect = false; diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 990bdef9e..59a328ed9 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -7,6 +7,7 @@ struct Clip; class Effect; class EffectRow; +class EffectField; class TimelineHeader; class KeyframeView : public QWidget { @@ -26,7 +27,7 @@ public slots: void resize_move(double d); private: long adjust_row_keyframe(EffectRow* row, long time); - QVector selected_rows; + QVector selected_fields; QVector selected_keyframes; QVector rowY; QVector rows; @@ -41,7 +42,7 @@ private: bool select_rect; bool scroll_drag; - bool keyframeIsSelected(EffectRow* row, int keyframe); + bool keyframeIsSelected(EffectField *field, int keyframe); long drag_frame_start; long last_frame_diff; From 215c784f8ed280c968bdd90b86e6067ad949c81e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 22:43:35 +1100 Subject: [PATCH 19/65] fixed keyframe save/load and goto next key --- mainwindow.cpp | 4 ++-- project/effect.cpp | 27 +++++++++++++++++---------- project/effectrow.cpp | 2 +- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 1f4094bce..e1cb1764e 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -80,13 +80,13 @@ void MainWindow::setup_layout(bool reset) { #endif // load panels from file - if (!reset) { + if (!reset) { QFile panel_config(get_data_path() + "/layout"); if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { restoreState(panel_config.readAll(), 0); panel_config.close(); } - } + } layout()->update(); } diff --git a/project/effect.cpp b/project/effect.cpp index 1be29a308..bf20bd12a 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -610,7 +610,7 @@ void Effect::load(QXmlStreamReader& stream) { stream.readNext(); // read keyframes - if (stream.name() == "keyframes" && stream.isStartElement()) { + /*if (stream.name() == "keyframes" && stream.isStartElement()) { for (int k=0;kkeyframes[field_index].data = load_data_from_string(field->type, stream.text().toString()); - field_index++; + row->setKeyframing(true); + + EffectKeyframe key; + for (int k=0;ktype, attr.value().toString()); + } else if (attr.name() == "frame") { + key.time = attr.value().toLong(); + } else if (attr.name() == "type") { + key.type = attr.value().toInt(); + } + } + field->keyframes.append(key); } } } else { @@ -714,9 +724,6 @@ void Effect::save(QXmlStreamWriter& stream) { EffectRow* row = rows.at(i); if (row->savable) { stream.writeStartElement("row"); // row - stream.writeStartElement("keyframes"); // keyframes - stream.writeAttribute("enabled", QString::number(row->isKeyframing())); - stream.writeEndElement(); // keyframes for (int j=0;jfieldCount();j++) { EffectField* field = row->field(j); stream.writeStartElement("field"); // field diff --git a/project/effectrow.cpp b/project/effectrow.cpp index db86e75f3..c980c37f3 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -129,7 +129,7 @@ void EffectRow::goto_next_key() { for (int j=0;jkeyframes.size();j++) { long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; if (comp > sequence->playhead) { - key = qMax(comp, key); + key = qMin(comp, key); } } } From f4715fdf99971747ba303281b64f5c84d16cb68c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 23:09:00 +1100 Subject: [PATCH 20/65] corrected tab order for export and new sequence dialog --- dialogs/exportdialog.cpp | 115 +++++++++++++----------------- dialogs/newsequencedialog.cpp | 128 ++++++++++++++++------------------ dialogs/newsequencedialog.h | 2 +- 3 files changed, 107 insertions(+), 138 deletions(-) diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index 44835dacd..39e788e12 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -57,6 +57,7 @@ enum ExportFormats { ExportDialog::ExportDialog(QWidget *parent) : QDialog(parent) { + setWindowTitle("Export \"" + sequence->name + "\""); setup_ui(); rangeCombobox->setCurrentIndex(0); @@ -601,54 +602,41 @@ void ExportDialog::setup_ui() { videoGroupbox->setTitle("Video"); videoGroupbox->setFlat(false); videoGroupbox->setCheckable(true); - QGridLayout* gridLayout = new QGridLayout(videoGroupbox); - gridLayout->addWidget(new QLabel("Compression Type:"), 4, 0, 1, 1); + QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox); + videoGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); + vcodecCombobox = new QComboBox(videoGroupbox); + videoGridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1); + + videoGridLayout->addWidget(new QLabel("Width:"), 1, 0, 1, 1); + widthSpinbox = new QSpinBox(videoGroupbox); + widthSpinbox->setMaximum(16777216); + videoGridLayout->addWidget(widthSpinbox, 1, 1, 1, 1); + + videoGridLayout->addWidget(new QLabel("Height:"), 2, 0, 1, 1); + heightSpinbox = new QSpinBox(videoGroupbox); + heightSpinbox->setMaximum(16777216); + videoGridLayout->addWidget(heightSpinbox, 2, 1, 1, 1); + + videoGridLayout->addWidget(new QLabel("Frame Rate:"), 3, 0, 1, 1); + framerateSpinbox = new QDoubleSpinBox(videoGroupbox); + framerateSpinbox->setMaximum(60); + framerateSpinbox->setValue(0); + videoGridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1); + + videoGridLayout->addWidget(new QLabel("Compression Type:"), 4, 0, 1, 1); compressionTypeCombobox = new QComboBox(videoGroupbox); compressionTypeCombobox->addItem("Quality-based (Constant Rate Factor)"); compressionTypeCombobox->addItem("File size-based (Two-Pass)"); + videoGridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1); - gridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1); - - gridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); - - vcodecCombobox = new QComboBox(videoGroupbox); - - gridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1); - - gridLayout->addWidget(new QLabel("Height:"), 2, 0, 1, 1); - - gridLayout->addWidget(new QLabel("Width:"), 1, 0, 1, 1); - - heightSpinbox = new QSpinBox(videoGroupbox); - heightSpinbox->setMaximum(16777216); - - gridLayout->addWidget(heightSpinbox, 2, 1, 1, 1); - - widthSpinbox = new QSpinBox(videoGroupbox); - widthSpinbox->setMaximum(16777216); - - gridLayout->addWidget(widthSpinbox, 1, 1, 1, 1); - - videobitrateSpinbox = new QDoubleSpinBox(videoGroupbox); - videobitrateSpinbox->setMaximum(100); - videobitrateSpinbox->setValue(2); - - gridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1); - - videoBitrateLabel = new QLabel(videoGroupbox); - - gridLayout->addWidget(videoBitrateLabel, 5, 0, 1, 1); - - gridLayout->addWidget(new QLabel("Frame Rate:"), 3, 0, 1, 1); - - framerateSpinbox = new QDoubleSpinBox(videoGroupbox); - framerateSpinbox->setMaximum(60); - framerateSpinbox->setValue(0); - - gridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1); - + videoBitrateLabel = new QLabel(videoGroupbox); + videoGridLayout->addWidget(videoBitrateLabel, 5, 0, 1, 1); + videobitrateSpinbox = new QDoubleSpinBox(videoGroupbox); + videobitrateSpinbox->setMaximum(100); + videobitrateSpinbox->setValue(2); + videoGridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1); verticalLayout->addWidget(videoGroupbox); @@ -656,69 +644,60 @@ void ExportDialog::setup_ui() { audioGroupbox->setTitle("Audio"); audioGroupbox->setCheckable(true); - QGridLayout* gridLayout_2 = new QGridLayout(audioGroupbox); - gridLayout_2->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); + QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox); + audioGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); acodecCombobox = new QComboBox(audioGroupbox); + audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1); - gridLayout_2->addWidget(acodecCombobox, 0, 1, 1, 1); - + audioGridLayout->addWidget(new QLabel("Sampling Rate:"), 1, 0, 1, 1); samplingRateSpinbox = new QSpinBox(audioGroupbox); samplingRateSpinbox->setMaximum(96000); samplingRateSpinbox->setValue(0); + audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1); - gridLayout_2->addWidget(samplingRateSpinbox, 1, 1, 1, 1); - - gridLayout_2->addWidget(new QLabel("Sampling Rate:"), 1, 0, 1, 1); - - gridLayout_2->addWidget(new QLabel("Bitrate (Kbps/CBR):"), 3, 0, 1, 1); - + audioGridLayout->addWidget(new QLabel("Bitrate (Kbps/CBR):"), 3, 0, 1, 1); audiobitrateSpinbox = new QSpinBox(audioGroupbox); audiobitrateSpinbox->setMaximum(320); audiobitrateSpinbox->setValue(256); - - gridLayout_2->addWidget(audiobitrateSpinbox, 3, 1, 1, 1); - + audioGridLayout->addWidget(audiobitrateSpinbox, 3, 1, 1, 1); verticalLayout->addWidget(audioGroupbox); - QHBoxLayout* horizontalLayout_3 = new QHBoxLayout(); + QHBoxLayout* progressLayout = new QHBoxLayout(); progressBar = new QProgressBar(this); progressBar->setFormat("%p% (ETA: 0:00:00)"); progressBar->setEnabled(false); progressBar->setValue(0); - - horizontalLayout_3->addWidget(progressBar); + progressLayout->addWidget(progressBar); renderCancel = new QPushButton(this); renderCancel->setText("x"); renderCancel->setEnabled(false); renderCancel->setMaximumSize(QSize(20, 16777215)); connect(renderCancel, SIGNAL(clicked(bool)), this, SLOT(cancel_render())); + progressLayout->addWidget(renderCancel); - horizontalLayout_3->addWidget(renderCancel); + verticalLayout->addLayout(progressLayout); - - verticalLayout->addLayout(horizontalLayout_3); - - QHBoxLayout* horizontalLayout_2 = new QHBoxLayout(); - horizontalLayout_2->addStretch(); + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->addStretch(); export_button = new QPushButton(this); export_button->setText("Export"); connect(export_button, SIGNAL(clicked(bool)), this, SLOT(export_action())); - horizontalLayout_2->addWidget(export_button); + buttonLayout->addWidget(export_button); cancel_button = new QPushButton(this); cancel_button->setText("Cancel"); connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject())); - horizontalLayout_2->addWidget(cancel_button); + buttonLayout->addWidget(cancel_button); - horizontalLayout_2->addStretch(); + buttonLayout->addStretch(); - verticalLayout->addLayout(horizontalLayout_2); + verticalLayout->addLayout(buttonLayout); connect(formatCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(format_changed(int))); connect(compressionTypeCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(comp_type_changed(int))); diff --git a/dialogs/newsequencedialog.cpp b/dialogs/newsequencedialog.cpp index cfcd0678e..0451aa886 100644 --- a/dialogs/newsequencedialog.cpp +++ b/dialogs/newsequencedialog.cpp @@ -42,7 +42,7 @@ NewSequenceDialog::NewSequenceDialog(QWidget *parent, Media *existing) : break; } } - lineEdit->setText(existing_sequence->name); + sequence_name_edit->setText(existing_sequence->name); for (int i=0;icount();i++) { if (audio_frequency_combobox->itemData(i) == existing_sequence->audio_frequency) { audio_frequency_combobox->setCurrentIndex(i); @@ -59,14 +59,14 @@ NewSequenceDialog::~NewSequenceDialog() {} void NewSequenceDialog::set_sequence_name(const QString& s) { - lineEdit->setText(s); + sequence_name_edit->setText(s); } void NewSequenceDialog::create() { if (existing_sequence == NULL) { Sequence* s = new Sequence(); - s->name = lineEdit->text(); + s->name = sequence_name_edit->text(); s->width = width_numeric->value(); s->height = height_numeric->value(); s->frame_rate = frame_rate_combobox->currentData().toDouble(); @@ -82,7 +82,7 @@ void NewSequenceDialog::create() { double multiplier = frame_rate_combobox->currentData().toDouble() / existing_sequence->frame_rate; EditSequenceCommand* esc = new EditSequenceCommand(existing_item, existing_sequence); - esc->name = lineEdit->text(); + esc->name = sequence_name_edit->text(); esc->width = width_numeric->value(); esc->height = height_numeric->value(); esc->frame_rate = frame_rate_combobox->currentData().toDouble(); @@ -154,10 +154,10 @@ void NewSequenceDialog::setup_ui() { QWidget* widget = new QWidget(this); - QHBoxLayout* horizontalLayout_2 = new QHBoxLayout(widget); - horizontalLayout_2->setContentsMargins(0, 0, 0, 0); + QHBoxLayout* preset_layout = new QHBoxLayout(widget); + preset_layout->setContentsMargins(0, 0, 0, 0); - horizontalLayout_2->addWidget(new QLabel("Preset:")); + preset_layout->addWidget(new QLabel("Preset:")); preset_combobox = new QComboBox(widget); @@ -174,75 +174,65 @@ void NewSequenceDialog::setup_ui() { preset_combobox->addItem("Custom"); preset_combobox->setCurrentIndex(2); - horizontalLayout_2->addWidget(preset_combobox); + preset_layout->addWidget(preset_combobox); verticalLayout->addWidget(widget); - QGroupBox* groupBox = new QGroupBox(this); - groupBox->setTitle("Video"); + QGroupBox* videoGroupBox = new QGroupBox(this); + videoGroupBox->setTitle("Video"); - QGridLayout* gridLayout = new QGridLayout(groupBox); + QGridLayout* videoLayout = new QGridLayout(videoGroupBox); - height_numeric = new QSpinBox(groupBox); + videoLayout->addWidget(new QLabel("Width:"), 0, 0, 1, 1); + width_numeric = new QSpinBox(videoGroupBox); + width_numeric->setMaximum(9999); + width_numeric->setValue(1920); + videoLayout->addWidget(width_numeric, 0, 2, 1, 2); + + videoLayout->addWidget(new QLabel("Height:"), 1, 0, 1, 2); + height_numeric = new QSpinBox(videoGroupBox); height_numeric->setMaximum(9999); height_numeric->setValue(1080); + videoLayout->addWidget(height_numeric, 1, 2, 1, 2); - gridLayout->addWidget(height_numeric, 1, 2, 1, 2); + videoLayout->addWidget(new QLabel("Frame Rate:"), 2, 0, 1, 1); + frame_rate_combobox = new QComboBox(videoGroupBox); + frame_rate_combobox->addItem("10 FPS", 10.0); + frame_rate_combobox->addItem("12.5 FPS", 12.5); + frame_rate_combobox->addItem("15 FPS", 15.0); + frame_rate_combobox->addItem("23.976 FPS", 23.976); + frame_rate_combobox->addItem("24 FPS", 24.0); + frame_rate_combobox->addItem("25 FPS", 25.0); + frame_rate_combobox->addItem("29.97 FPS", 29.97); + frame_rate_combobox->addItem("30 FPS", 30.0); + frame_rate_combobox->addItem("50 FPS", 50.0); + frame_rate_combobox->addItem("59.94 FPS", 59.94); + frame_rate_combobox->addItem("60 FPS", 60.0); + frame_rate_combobox->setCurrentIndex(6); + videoLayout->addWidget(frame_rate_combobox, 2, 2, 1, 2); - width_numeric = new QSpinBox(groupBox); - width_numeric->setMaximum(9999); - width_numeric->setValue(1920); + videoLayout->addWidget(new QLabel("Pixel Aspect Ratio:"), 4, 0, 1, 1); + par_combobox = new QComboBox(videoGroupBox); + par_combobox->addItem("Square Pixels (1.0)"); + videoLayout->addWidget(par_combobox, 4, 2, 1, 2); - gridLayout->addWidget(width_numeric, 0, 2, 1, 2); - - gridLayout->addWidget(new QLabel("Width:"), 0, 0, 1, 1); - - gridLayout->addWidget(new QLabel("Pixel Aspect Ratio:"), 4, 0, 1, 1); - - gridLayout->addWidget(new QLabel("Interlacing:"), 6, 0, 1, 1); - - gridLayout->addWidget(new QLabel("Height:"), 1, 0, 1, 2); - - par_combobox = new QComboBox(groupBox); - par_combobox->addItem("Square Pixels (1.0)"); - - gridLayout->addWidget(par_combobox, 4, 2, 1, 2); - - interlacing_combobox = new QComboBox(groupBox); + videoLayout->addWidget(new QLabel("Interlacing:"), 6, 0, 1, 1); + interlacing_combobox = new QComboBox(videoGroupBox); interlacing_combobox->addItem("None (Progressive)"); - interlacing_combobox->addItem("Upper Field First"); - interlacing_combobox->addItem("Lower Field First"); +// interlacing_combobox->addItem("Upper Field First"); +// interlacing_combobox->addItem("Lower Field First"); + videoLayout->addWidget(interlacing_combobox, 6, 2, 1, 2); - gridLayout->addWidget(interlacing_combobox, 6, 2, 1, 2); + verticalLayout->addWidget(videoGroupBox); - frame_rate_combobox = new QComboBox(groupBox); - frame_rate_combobox->addItem("10 FPS", 10.0); - frame_rate_combobox->addItem("12.5 FPS", 12.5); - frame_rate_combobox->addItem("15 FPS", 15.0); - frame_rate_combobox->addItem("23.976 FPS", 23.976); - frame_rate_combobox->addItem("24 FPS", 24.0); - frame_rate_combobox->addItem("25 FPS", 25.0); - frame_rate_combobox->addItem("29.97 FPS", 29.97); - frame_rate_combobox->addItem("30 FPS", 30.0); - frame_rate_combobox->addItem("50 FPS", 50.0); - frame_rate_combobox->addItem("59.94 FPS", 59.94); - frame_rate_combobox->addItem("60 FPS", 60.0); - frame_rate_combobox->setCurrentIndex(6); + QGroupBox* audioGroupBox = new QGroupBox(this); + audioGroupBox->setTitle("Audio"); - gridLayout->addWidget(frame_rate_combobox, 2, 2, 1, 2); + QGridLayout* audioLayout = new QGridLayout(audioGroupBox); - gridLayout->addWidget(new QLabel("Frame Rate:"), 2, 0, 1, 1); + audioLayout->addWidget(new QLabel("Sample Rate: "), 0, 0, 1, 1); - verticalLayout->addWidget(groupBox); - - QGroupBox* groupBox_2 = new QGroupBox(this); - groupBox_2->setTitle("Audio"); - - QGridLayout* gridLayout_2 = new QGridLayout(groupBox_2); - - gridLayout_2->addWidget(new QLabel("Sample Rate: "), 0, 0, 1, 1); - - audio_frequency_combobox = new QComboBox(groupBox_2); + audio_frequency_combobox = new QComboBox(audioGroupBox); audio_frequency_combobox->addItem("22050 Hz", 22050); audio_frequency_combobox->addItem("24000 Hz", 24000); audio_frequency_combobox->addItem("32000 Hz", 32000); @@ -252,21 +242,21 @@ void NewSequenceDialog::setup_ui() { audio_frequency_combobox->addItem("96000 Hz", 96000); audio_frequency_combobox->setCurrentIndex(4); - gridLayout_2->addWidget(audio_frequency_combobox, 0, 1, 1, 1); + audioLayout->addWidget(audio_frequency_combobox, 0, 1, 1, 1); - verticalLayout->addWidget(groupBox_2); + verticalLayout->addWidget(audioGroupBox); - QWidget* widget_2 = new QWidget(this); - QHBoxLayout* horizontalLayout = new QHBoxLayout(widget_2); - horizontalLayout->setContentsMargins(0, 0, 0, 0); + QWidget* nameWidget = new QWidget(this); + QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget); + nameLayout->setContentsMargins(0, 0, 0, 0); - horizontalLayout->addWidget(new QLabel("Name:")); + nameLayout->addWidget(new QLabel("Name:")); - lineEdit = new QLineEdit(widget_2); + sequence_name_edit = new QLineEdit(nameWidget); - horizontalLayout->addWidget(lineEdit); + nameLayout->addWidget(sequence_name_edit); - verticalLayout->addWidget(widget_2); + verticalLayout->addWidget(nameWidget); QDialogButtonBox* buttonBox = new QDialogButtonBox(this); buttonBox->setOrientation(Qt::Horizontal); diff --git a/dialogs/newsequencedialog.h b/dialogs/newsequencedialog.h index 1a5cf2042..56502d817 100644 --- a/dialogs/newsequencedialog.h +++ b/dialogs/newsequencedialog.h @@ -37,7 +37,7 @@ private: QComboBox* interlacing_combobox; QComboBox* frame_rate_combobox; QComboBox* audio_frequency_combobox; - QLineEdit* lineEdit; + QLineEdit* sequence_name_edit; }; #endif // NEWSEQUENCEDIALOG_H From 2b7103e286561ea8ae44336d15139d249316a3d1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 3 Jan 2019 00:09:39 +1100 Subject: [PATCH 21/65] added #225 --- dialogs/mediapropertiesdialog.cpp | 90 ++++++++++++++++++++----- dialogs/mediapropertiesdialog.h | 2 + io/previewgenerator.cpp | 105 ++++++++++++++++-------------- io/previewgenerator.h | 4 +- panels/project.cpp | 38 +++++------ panels/timeline.cpp | 24 ++++--- panels/viewer.cpp | 18 ++--- playback/cacher.cpp | 6 +- playback/playback.cpp | 6 +- project/clip.cpp | 12 ++-- project/footage.cpp | 14 ++-- project/footage.h | 7 +- project/media.cpp | 33 +++++----- ui/timelinewidget.cpp | 6 +- ui/timelinewidget.h | 2 +- ui/viewerwidget.cpp | 2 +- ui/viewerwidget.h | 2 +- 17 files changed, 221 insertions(+), 150 deletions(-) diff --git a/dialogs/mediapropertiesdialog.cpp b/dialogs/mediapropertiesdialog.cpp index 6f4fae51e..a94215f0f 100644 --- a/dialogs/mediapropertiesdialog.cpp +++ b/dialogs/mediapropertiesdialog.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "project/footage.h" #include "project/media.h" @@ -16,49 +18,107 @@ MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) : QDialog(parent), item(i) { + setWindowTitle("\"" + i->data(0, Qt::DisplayRole).toString() + "\" Properties"); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QGridLayout* grid = new QGridLayout(); setLayout(grid); + int row = 0; + Footage* f = item->to_footage(); + + grid->addWidget(new QLabel("Tracks:"), row, 0, 1, 2); + row++; + + track_list = new QListWidget(); + for (int i=0;ivideo_tracks.size();i++) { + const FootageStream& fs = f->video_tracks.at(i); + QListWidgetItem* item = new QListWidgetItem("Video " + QString::number(fs.file_index) + ": " + QString::number(fs.video_width) + "x" + QString::number(fs.video_height) + " " + QString::number(fs.video_frame_rate) + "FPS"); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked); + item->setData(Qt::UserRole+1, fs.file_index); + track_list->addItem(item); + } + for (int i=0;iaudio_tracks.size();i++) { + const FootageStream& fs = f->audio_tracks.at(i); + QListWidgetItem* item = new QListWidgetItem("Audio " + QString::number(fs.file_index) + ": " + QString::number(fs.audio_frequency) + "Hz " + QString::number(fs.audio_channels) + " channels"); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked); + item->setData(Qt::UserRole+1, fs.file_index); + track_list->addItem(item); + } + grid->addWidget(track_list, row, 0, 1, 2); + row++; + if (f->video_tracks.size() > 0) { interlacing_box = new QComboBox(); - interlacing_box->addItem("Auto (" + get_interlacing_name(f->video_tracks.at(0)->video_auto_interlacing) + ")"); + interlacing_box->addItem("Auto (" + get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing) + ")"); interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE)); interlacing_box->addItem(get_interlacing_name(VIDEO_TOP_FIELD_FIRST)); interlacing_box->addItem(get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST)); - interlacing_box->setCurrentIndex((f->video_tracks.at(0)->video_auto_interlacing == f->video_tracks.at(0)->video_interlacing) ? 0 : f->video_tracks.at(0)->video_interlacing + 1); + interlacing_box->setCurrentIndex((f->video_tracks.at(0).video_auto_interlacing == f->video_tracks.at(0).video_interlacing) ? 0 : f->video_tracks.at(0).video_interlacing + 1); - grid->addWidget(new QLabel("Interlacing:"), 0, 0); - grid->addWidget(interlacing_box, 0, 1); + grid->addWidget(new QLabel("Interlacing:"), row, 0); + grid->addWidget(interlacing_box, row, 1); + + row++; } name_box = new QLineEdit(item->get_name()); - grid->addWidget(new QLabel("Name:"), 1, 0); - grid->addWidget(name_box, 1, 1); + grid->addWidget(new QLabel("Name:"), row, 0); + grid->addWidget(name_box, row, 1); + row++; QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttons->setCenterButtons(true); - grid->addWidget(buttons, 2, 0, 1, 2); + grid->addWidget(buttons, row, 0, 1, 2); connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); } void MediaPropertiesDialog::accept() { + Footage* f = item->to_footage(); + ComboAction* ca = new ComboAction(); - //set interlacing - Footage* f = item->to_footage(); - if (interlacing_box->currentIndex() > 0) { - ca->append(new SetInt(&f->video_tracks.at(0)->video_interlacing, interlacing_box->currentIndex() - 1)); - } else { - ca->append(new SetInt(&f->video_tracks.at(0)->video_interlacing, f->video_tracks.at(0)->video_auto_interlacing)); - } + // set track enable + for (int i=0;icount();i++) { + QListWidgetItem* item = track_list->item(i); + const QVariant& data = item->data(Qt::UserRole+1); + if (!data.isNull()) { + int index = data.toInt(); + bool found = false; + for (int j=0;jvideo_tracks.size();j++) { + if (f->video_tracks.at(j).file_index == index) { + f->video_tracks[j].enabled = (item->checkState() == Qt::Checked); + found = true; + break; + } + } + if (!found) { + for (int j=0;jaudio_tracks.size();j++) { + if (f->audio_tracks.at(j).file_index == index) { + f->audio_tracks[j].enabled = (item->checkState() == Qt::Checked); + break; + } + } + } + } + } - //set name + // set interlacing + if (f->video_tracks.size() > 0) { + if (interlacing_box->currentIndex() > 0) { + ca->append(new SetInt(&f->video_tracks[0].video_interlacing, interlacing_box->currentIndex() - 1)); + } else { + ca->append(new SetInt(&f->video_tracks[0].video_interlacing, f->video_tracks.at(0).video_auto_interlacing)); + } + } + + // set name MediaRename* mr = new MediaRename(item, name_box->text()); ca->append(mr); diff --git a/dialogs/mediapropertiesdialog.h b/dialogs/mediapropertiesdialog.h index 13f96c3d1..db173408d 100644 --- a/dialogs/mediapropertiesdialog.h +++ b/dialogs/mediapropertiesdialog.h @@ -7,6 +7,7 @@ struct Footage; class QComboBox; class QLineEdit; class Media; +class QListWidget; class MediaPropertiesDialog : public QDialog { Q_OBJECT @@ -16,6 +17,7 @@ private: QComboBox* interlacing_box; QLineEdit* name_box; Media* item; + QListWidget* track_list; private slots: void accept(); }; diff --git a/io/previewgenerator.cpp b/io/previewgenerator.cpp index bb24f3cbd..afb753ef4 100644 --- a/io/previewgenerator.cpp +++ b/io/previewgenerator.cpp @@ -55,14 +55,13 @@ void PreviewGenerator::parse_media() { if (avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id) == NULL) { dout << "[ERROR] Unsupported codec in stream" << i << "of file" << footage->name; } else { - FootageStream* ms = footage->get_stream_from_file_index(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, i); + FootageStream ms; + ms.preview_done = false; + ms.file_index = i; + ms.enabled = true; + bool append = false; - if (ms == NULL) { - ms = new FootageStream(); - ms->preview_done = false; - ms->file_index = i; - append = true; - } + if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && fmt_ctx->streams[i]->codecpar->width > 0 && fmt_ctx->streams[i]->codecpar->height > 0) { @@ -76,33 +75,43 @@ void PreviewGenerator::parse_media() { // heuristic to determine if video is a still image if (fmt_ctx->streams[i]->avg_frame_rate.den == 0 && fmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_DNXHD) { // silly hack but this is the only scenario i've seen this - ms->infinite_length = true; + ms.infinite_length = true; contains_still_image = true; - ms->video_frame_rate = 0; + ms.video_frame_rate = 0; } else { - ms->infinite_length = false; + ms.infinite_length = false; if (fmt_ctx->streams[i]->r_frame_rate.den == 0) { - ms->video_frame_rate = av_q2d(fmt_ctx->streams[i]->avg_frame_rate); + ms.video_frame_rate = av_q2d(fmt_ctx->streams[i]->avg_frame_rate); } else { - ms->video_frame_rate = av_q2d(fmt_ctx->streams[i]->r_frame_rate); + ms.video_frame_rate = av_q2d(fmt_ctx->streams[i]->r_frame_rate); } } - ms->video_width = fmt_ctx->streams[i]->codecpar->width; - ms->video_height = fmt_ctx->streams[i]->codecpar->height; + ms.video_width = fmt_ctx->streams[i]->codecpar->width; + ms.video_height = fmt_ctx->streams[i]->codecpar->height; // default value, we get the true value later in generate_waveform() - ms->video_auto_interlacing = VIDEO_PROGRESSIVE; - ms->video_interlacing = VIDEO_PROGRESSIVE; + ms.video_auto_interlacing = VIDEO_PROGRESSIVE; + ms.video_interlacing = VIDEO_PROGRESSIVE; - if (append) footage->video_tracks.append(ms); + append = true; } else if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - ms->audio_channels = fmt_ctx->streams[i]->codecpar->channels; - ms->audio_layout = fmt_ctx->streams[i]->codecpar->channel_layout; - ms->audio_frequency = fmt_ctx->streams[i]->codecpar->sample_rate; - if (append) footage->audio_tracks.append(ms); - } else if (append) { - delete ms; - } + ms.audio_channels = fmt_ctx->streams[i]->codecpar->channels; + ms.audio_layout = fmt_ctx->streams[i]->codecpar->channel_layout; + ms.audio_frequency = fmt_ctx->streams[i]->codecpar->sample_rate; + + append = true; + } + + if (append) { + QVector& stream_list = (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ? footage->audio_tracks : footage->video_tracks; + for (int j=0;jlength = fmt_ctx->duration; @@ -123,32 +132,32 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) { bool found = true; for (int i=0;ivideo_tracks.size();i++) { - FootageStream* ms = footage->video_tracks.at(i); + FootageStream& ms = footage->video_tracks[i]; QString thumb_path = get_thumbnail_path(hash, ms); QFile f(thumb_path); - if (f.exists() && ms->video_preview.load(thumb_path)) { + if (f.exists() && ms.video_preview.load(thumb_path)) { //dout << "loaded thumb" << ms->file_index << "from" << thumb_path; - ms->make_square_thumb(); - ms->preview_done = true; + ms.make_square_thumb(); + ms.preview_done = true; } else { found = false; break; } } for (int i=0;iaudio_tracks.size();i++) { - FootageStream* ms = footage->audio_tracks.at(i); + FootageStream& ms = footage->audio_tracks[i]; QString waveform_path = get_waveform_path(hash, ms); QFile f(waveform_path); if (f.exists()) { //dout << "loaded wave" << ms->file_index << "from" << waveform_path; f.open(QFile::ReadOnly); QByteArray data = f.readAll(); - ms->audio_preview.resize(data.size()); + ms.audio_preview.resize(data.size()); for (int j=0;jaudio_preview[j] = data.at(j); + ms.audio_preview[j] = data.at(j); } - ms->preview_done = true; + ms.preview_done = true; f.close(); } else { found = false; @@ -157,13 +166,13 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) { } if (!found) { for (int i=0;ivideo_tracks.size();i++) { - FootageStream* ms = footage->video_tracks.at(i); - ms->preview_done = false; + FootageStream& ms = footage->video_tracks[i]; + ms.preview_done = false; } for (int i=0;iaudio_tracks.size();i++) { - FootageStream* ms = footage->audio_tracks.at(i); - ms->audio_preview.clear(); - ms->preview_done = false; + FootageStream& ms = footage->audio_tracks[i]; + ms.audio_preview.clear(); + ms.preview_done = false; } } return !found; @@ -336,7 +345,7 @@ void PreviewGenerator::generate_waveform() { break; } } - s->audio_preview.append(min >> 8); + s->audio_preview.append(min >> 8); s->audio_preview.append(max >> 8); if (cancelled) break; } @@ -359,7 +368,7 @@ void PreviewGenerator::generate_waveform() { } else if (footage->audio_tracks.size() == 0) { done = true; for (int i=0;ivideo_tracks.size();i++) { - if (!footage->video_tracks.at(i)->preview_done) { + if (!footage->video_tracks.at(i).preview_done) { done = false; break; } @@ -373,7 +382,7 @@ void PreviewGenerator::generate_waveform() { } } for (int i=0;iaudio_tracks.size();i++) { - footage->audio_tracks.at(i)->preview_done = true; + footage->audio_tracks[i].preview_done = true; } av_frame_free(&temp_frame); av_packet_free(&packet); @@ -397,12 +406,12 @@ void PreviewGenerator::generate_waveform() { delete [] codec_ctx; } -QString PreviewGenerator::get_thumbnail_path(const QString& hash, FootageStream* ms) { - return data_path + "/" + hash + "t" + QString::number(ms->file_index); +QString PreviewGenerator::get_thumbnail_path(const QString& hash, const FootageStream& ms) { + return data_path + "/" + hash + "t" + QString::number(ms.file_index); } -QString PreviewGenerator::get_waveform_path(const QString& hash, FootageStream* ms) { - return data_path + "/" + hash + "w" + QString::number(ms->file_index); +QString PreviewGenerator::get_waveform_path(const QString& hash, const FootageStream& ms) { + return data_path + "/" + hash + "w" + QString::number(ms.file_index); } void PreviewGenerator::run() { @@ -445,15 +454,15 @@ void PreviewGenerator::run() { // save preview to file for (int i=0;ivideo_tracks.size();i++) { - FootageStream* ms = footage->video_tracks.at(i); - ms->video_preview.save(get_thumbnail_path(hash, ms), "PNG"); + FootageStream& ms = footage->video_tracks[i]; + ms.video_preview.save(get_thumbnail_path(hash, ms), "PNG"); //dout << "saved" << ms->file_index << "thumbnail to" << get_thumbnail_path(hash, ms); } for (int i=0;iaudio_tracks.size();i++) { - FootageStream* ms = footage->audio_tracks.at(i); + FootageStream& ms = footage->audio_tracks[i]; QFile f(get_waveform_path(hash, ms)); f.open(QFile::WriteOnly); - f.write(ms->audio_preview.constData(), ms->audio_preview.size()); + f.write(ms.audio_preview.constData(), ms.audio_preview.size()); f.close(); //dout << "saved" << ms->file_index << "waveform to" << get_waveform_path(hash, ms); } diff --git a/io/previewgenerator.h b/io/previewgenerator.h index 2f1f86c7d..3065bb072 100644 --- a/io/previewgenerator.h +++ b/io/previewgenerator.h @@ -36,8 +36,8 @@ private: bool replace; bool cancelled; QString data_path; - QString get_thumbnail_path(const QString &hash, FootageStream* ms); - QString get_waveform_path(const QString& hash, FootageStream* ms); + QString get_thumbnail_path(const QString &hash, const FootageStream &ms); + QString get_waveform_path(const QString& hash, const FootageStream &ms); }; #endif // PREVIEWGENERATOR_H diff --git a/panels/project.cpp b/panels/project.cpp index 96640180b..4b31f4676 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -181,13 +181,13 @@ Sequence* create_sequence_from_media(QVector& media_list) { if (m->ready) { if (!got_video_values) { for (int j=0;jvideo_tracks.size();j++) { - FootageStream* ms = m->video_tracks.at(j); - s->width = ms->video_width; - s->height = ms->video_height; - if (ms->video_frame_rate != 0) { - s->frame_rate = ms->video_frame_rate; + const FootageStream& ms = m->video_tracks.at(j); + s->width = ms.video_width; + s->height = ms.video_height; + if (ms.video_frame_rate != 0) { + s->frame_rate = ms.video_frame_rate; - if (ms->video_interlacing != VIDEO_PROGRESSIVE) s->frame_rate *= 2; + if (ms.video_interlacing != VIDEO_PROGRESSIVE) s->frame_rate *= 2; // only break with a decent frame rate, otherwise there may be a better candidate got_video_values = true; @@ -197,8 +197,8 @@ Sequence* create_sequence_from_media(QVector& media_list) { } if (!got_audio_values) { for (int j=0;jaudio_tracks.size();j++) { - FootageStream* ms = m->audio_tracks.at(j); - s->audio_frequency = ms->audio_frequency; + const FootageStream& ms = m->audio_tracks.at(j); + s->audio_frequency = ms.audio_frequency; got_audio_values = true; break; } @@ -851,22 +851,22 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, stream.writeAttribute("in", QString::number(f->in)); stream.writeAttribute("out", QString::number(f->out)); for (int j=0;jvideo_tracks.size();j++) { - FootageStream* ms = f->video_tracks.at(j); + const FootageStream& ms = f->video_tracks.at(j); stream.writeStartElement("video"); - stream.writeAttribute("id", QString::number(ms->file_index)); - stream.writeAttribute("width", QString::number(ms->video_width)); - stream.writeAttribute("height", QString::number(ms->video_height)); - stream.writeAttribute("framerate", QString::number(ms->video_frame_rate, 'f', 10)); - stream.writeAttribute("infinite", QString::number(ms->infinite_length)); + stream.writeAttribute("id", QString::number(ms.file_index)); + stream.writeAttribute("width", QString::number(ms.video_width)); + stream.writeAttribute("height", QString::number(ms.video_height)); + stream.writeAttribute("framerate", QString::number(ms.video_frame_rate, 'f', 10)); + stream.writeAttribute("infinite", QString::number(ms.infinite_length)); stream.writeEndElement(); } for (int j=0;jaudio_tracks.size();j++) { - FootageStream* ms = f->audio_tracks.at(j); + const FootageStream& ms = f->audio_tracks.at(j); stream.writeStartElement("audio"); - stream.writeAttribute("id", QString::number(ms->file_index)); - stream.writeAttribute("channels", QString::number(ms->audio_channels)); - stream.writeAttribute("layout", QString::number(ms->audio_layout)); - stream.writeAttribute("frequency", QString::number(ms->audio_frequency)); + stream.writeAttribute("id", QString::number(ms.file_index)); + stream.writeAttribute("channels", QString::number(ms.audio_channels)); + stream.writeAttribute("layout", QString::number(ms.audio_layout)); + stream.writeAttribute("frequency", QString::number(ms.audio_frequency)); stream.writeEndElement(); } stream.writeEndElement(); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 432fb6996..2f450ae56 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -182,7 +182,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector can_import = m->ready; if (m->using_inout) { double source_fr = 30; - if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0)->video_frame_rate)) source_fr = m->video_tracks.at(0)->video_frame_rate; + if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate; default_clip_in = refactor_frame_number(m->in, source_fr, seq->frame_rate); default_clip_out = refactor_frame_number(m->out, source_fr, seq->frame_rate); } @@ -214,7 +214,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector switch (medium->get_type()) { case MEDIA_TYPE_FOOTAGE: // is video source a still image? - if (m->video_tracks.size() > 0 && m->video_tracks[0]->infinite_length && m->audio_tracks.size() == 0) { + if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) { g.out = g.in + 100; } else { long length = m->get_length_in_frames(seq->frame_rate); @@ -225,16 +225,20 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector } for (int j=0;jaudio_tracks.size();j++) { - g.track = j; - g.media_stream = m->audio_tracks.at(j)->file_index; - ghosts.append(g); - audio_ghosts = true; + if (m->audio_tracks.at(j).enabled) { + g.track = j; + g.media_stream = m->audio_tracks.at(j).file_index; + ghosts.append(g); + audio_ghosts = true; + } } for (int j=0;jvideo_tracks.size();j++) { - g.track = -1-j; - g.media_stream = m->video_tracks.at(j)->file_index; - ghosts.append(g); - video_ghosts = true; + if (m->video_tracks.at(j).enabled) { + g.track = -1-j; + g.media_stream = m->video_tracks.at(j).file_index; + ghosts.append(g); + video_ghosts = true; + } } break; case MEDIA_TYPE_SEQUENCE: diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 01e488079..5563a5943 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -573,14 +573,14 @@ void Viewer::set_media(Media* m) { seq->frame_rate = 30; if (footage->video_tracks.size() > 0) { - FootageStream* video_stream = footage->video_tracks.at(0); - seq->width = video_stream->video_width; - seq->height = video_stream->video_height; - if (video_stream->video_frame_rate > 0 && !video_stream->infinite_length) seq->frame_rate = video_stream->video_frame_rate; + const FootageStream& video_stream = footage->video_tracks.at(0); + seq->width = video_stream.video_width; + seq->height = video_stream.video_height; + if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate; Clip* c = new Clip(seq); c->media = media; - c->media_stream = video_stream->file_index; + c->media_stream = video_stream.file_index; c->timeline_in = 0; c->timeline_out = footage->get_length_in_frames(seq->frame_rate); if (c->timeline_out <= 0) c->timeline_out = 150; @@ -594,12 +594,12 @@ void Viewer::set_media(Media* m) { } if (footage->audio_tracks.size() > 0) { - FootageStream* audio_stream = footage->audio_tracks.at(0); - seq->audio_frequency = audio_stream->audio_frequency; + const FootageStream& audio_stream = footage->audio_tracks.at(0); + seq->audio_frequency = audio_stream.audio_frequency; Clip* c = new Clip(seq); c->media = media; - c->media_stream = audio_stream->file_index; + c->media_stream = audio_stream.file_index; c->timeline_in = 0; c->timeline_out = footage->get_length_in_frames(seq->frame_rate); c->track = 0; @@ -610,7 +610,7 @@ void Viewer::set_media(Media* m) { if (footage->video_tracks.size() == 0) { viewer_widget->waveform = true; viewer_widget->waveform_clip = c; - viewer_widget->waveform_ms = audio_stream; + viewer_widget->waveform_ms = &audio_stream; viewer_widget->update(); } } else { diff --git a/playback/cacher.cpp b/playback/cacher.cpp index d9a7056ce..ef6687c9e 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -451,7 +451,7 @@ void cache_video_worker(Clip* c, long playhead) { AVFrame* frame = av_frame_alloc(); Footage* media = c->media->to_footage(); - FootageStream* ms = media->get_stream_from_file_index(true, c->media_stream); + const FootageStream* ms = media->get_stream_from_file_index(true, c->media_stream); while ((retr_ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) { if (c->multithreaded && c->cacher->interrupt) return; // abort @@ -546,7 +546,7 @@ void reset_cache(Clip* c, long target_frame) { c->frame->pts = 0; } } else { - FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); if (ms->infinite_length) { /*avcodec_flush_buffers(c->codecCtx); av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD);*/ @@ -640,7 +640,7 @@ void open_clip_worker(Clip* clip) { Footage* m = clip->media->to_footage(); QByteArray ba = m->url.toUtf8(); const char* filename = ba.constData(); - FootageStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream); + const FootageStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream); int errCode = avformat_open_input( &clip->formatCtx, diff --git a/playback/playback.cpp b/playback/playback.cpp index 1448e312a..9dd4a399d 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -116,11 +116,11 @@ double get_timecode(Clip* c, long playhead) { void get_clip_frame(Clip* c, long playhead) { if (c->finished_opening) { - FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); int64_t target_pts = qMax(static_cast(0), playhead_to_timestamp(c, playhead)); int64_t second_pts = qRound64(av_q2d(av_inv_q(c->stream->time_base))); - if (ms->video_interlacing != VIDEO_PROGRESSIVE) { + if (ms->video_interlacing != VIDEO_PROGRESSIVE) { target_pts *= 2; second_pts *= 2; } @@ -132,7 +132,7 @@ void get_clip_frame(Clip* c, long playhead) { c->queue_lock.lock(); if (c->queue.size() > 0) { - if (ms->infinite_length) { + if (ms->infinite_length) { target_frame = c->queue.at(0); #ifdef GCF_DEBUG dout << "GCF ==> USE PRECISE (INFINITE)"; diff --git a/project/clip.cpp b/project/clip.cpp index 213a53858..a2f4eafb6 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -113,9 +113,9 @@ void Clip::refresh() { Footage* m = media->to_footage(); if (track < 0 && m->video_tracks.size() > 0) { - media_stream = m->video_tracks.at(0)->file_index; + media_stream = m->video_tracks.at(0).file_index; } else if (track >= 0 && m->audio_tracks.size() > 0) { - media_stream = m->audio_tracks.at(0)->file_index; + media_stream = m->audio_tracks.at(0).file_index; } } replaced = false; @@ -241,8 +241,8 @@ void Clip::recalculateMaxLength() { case MEDIA_TYPE_FOOTAGE: { Footage* m = media->to_footage(); - FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream); - if (ms != NULL && ms->infinite_length) { + const FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream); + if (ms != NULL && ms->infinite_length) { calculated_length = LONG_MAX; } else { calculated_length = m->get_length_in_frames(fr); @@ -269,7 +269,7 @@ int Clip::getWidth() { switch (media->get_type()) { case MEDIA_TYPE_FOOTAGE: { - FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); + const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); if (ms != NULL) return ms->video_width; if (sequence != NULL) return sequence->width; } @@ -287,7 +287,7 @@ int Clip::getHeight() { switch (media->get_type()) { case MEDIA_TYPE_FOOTAGE: { - FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); + const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); if (ms != NULL) return ms->video_height; if (sequence != NULL) return sequence->height; } diff --git a/project/footage.cpp b/project/footage.cpp index b54f5a4a3..ec3a8baa1 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -24,12 +24,6 @@ void Footage::reset() { preview_gen->cancel(); preview_gen->wait(); } - for (int i=0;ifile_index == index) { - return video_tracks.at(i); + if (video_tracks.at(i).file_index == index) { + return &video_tracks[i]; } } } else { for (int i=0;ifile_index == index) { - return audio_tracks.at(i); + if (audio_tracks.at(i).file_index == index) { + return &audio_tracks[i]; } } } diff --git a/project/footage.h b/project/footage.h index 015253173..effbced64 100644 --- a/project/footage.h +++ b/project/footage.h @@ -29,6 +29,7 @@ struct FootageStream { int audio_channels; int audio_layout; int audio_frequency; + bool enabled; // preview thumbnail/waveform bool preview_done; @@ -45,8 +46,8 @@ struct Footage { QString url; QString name; int64_t length; - QVector video_tracks; - QVector audio_tracks; + QVector video_tracks; + QVector audio_tracks; int save_id; bool ready; bool invalid; @@ -59,7 +60,7 @@ struct Footage { long out; long get_length_in_frames(double frame_rate); - FootageStream* get_stream_from_file_index(bool video, int index); + FootageStream *get_stream_from_file_index(bool video, int index); void reset(); }; diff --git a/project/media.cpp b/project/media.cpp index cec9c7f8f..bf72646cb 100644 --- a/project/media.cpp +++ b/project/media.cpp @@ -104,21 +104,21 @@ void Media::update_tooltip(const QString& error) { if (i > 0) { tooltip += ", "; } - tooltip += QString::number(f->video_tracks.at(i)->video_width) + "x" + QString::number(f->video_tracks.at(i)->video_height); + tooltip += QString::number(f->video_tracks.at(i).video_width) + "x" + QString::number(f->video_tracks.at(i).video_height); } tooltip += "\n"; - if (!f->video_tracks.at(0)->infinite_length) { + if (!f->video_tracks.at(0).infinite_length) { tooltip += "Frame Rate: "; for (int i=0;ivideo_tracks.size();i++) { if (i > 0) { tooltip += ", "; } - if (f->video_tracks.at(i)->video_interlacing == VIDEO_PROGRESSIVE) { - tooltip += QString::number(f->video_tracks.at(i)->video_frame_rate); + if (f->video_tracks.at(i).video_interlacing == VIDEO_PROGRESSIVE) { + tooltip += QString::number(f->video_tracks.at(i).video_frame_rate); } else { - tooltip += QString::number(f->video_tracks.at(i)->video_frame_rate * 2); - tooltip += " fields (" + QString::number(f->video_tracks.at(i)->video_frame_rate) + " frames)"; + tooltip += QString::number(f->video_tracks.at(i).video_frame_rate * 2); + tooltip += " fields (" + QString::number(f->video_tracks.at(i).video_frame_rate) + " frames)"; } } tooltip += "\n"; @@ -129,7 +129,7 @@ void Media::update_tooltip(const QString& error) { if (i > 0) { tooltip += ", "; } - tooltip += get_interlacing_name(f->video_tracks.at(i)->video_interlacing); + tooltip += get_interlacing_name(f->video_tracks.at(i).video_interlacing); } } @@ -141,7 +141,7 @@ void Media::update_tooltip(const QString& error) { if (i > 0) { tooltip += ", "; } - tooltip += QString::number(f->audio_tracks.at(i)->audio_frequency); + tooltip += QString::number(f->audio_tracks.at(i).audio_frequency); } tooltip += "\n"; @@ -150,7 +150,7 @@ void Media::update_tooltip(const QString& error) { if (i > 0) { tooltip += ", "; } - tooltip += get_channel_layout_name(f->audio_tracks.at(i)->audio_channels, f->audio_tracks.at(i)->audio_layout); + tooltip += get_channel_layout_name(f->audio_tracks.at(i).audio_channels, f->audio_tracks.at(i).audio_layout); } // tooltip += "\n"; } @@ -202,8 +202,8 @@ double Media::get_frame_rate(int stream) { case MEDIA_TYPE_FOOTAGE: { Footage* f = to_footage(); - if (stream < 0) return f->video_tracks.at(0)->video_frame_rate; - return f->get_stream_from_file_index(true, stream)->video_frame_rate; + if (stream < 0) return f->video_tracks.at(0).video_frame_rate; + return f->get_stream_from_file_index(true, stream)->video_frame_rate; } case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate; } @@ -215,8 +215,8 @@ int Media::get_sampling_rate(int stream) { case MEDIA_TYPE_FOOTAGE: { Footage* f = to_footage(); - if (stream < 0) return f->audio_tracks.at(0)->audio_frequency; - return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency; + if (stream < 0) return f->audio_tracks.at(0).audio_frequency; + return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency; } case MEDIA_TYPE_SEQUENCE: return to_sequence()->audio_frequency; } @@ -258,8 +258,8 @@ QVariant Media::data(int column, int role) { if (get_type() == MEDIA_TYPE_FOOTAGE) { Footage* f = to_footage(); if (f->video_tracks.size() > 0 - && f->video_tracks.at(0)->preview_done) { - return f->video_tracks.at(0)->video_preview_square; + && f->video_tracks.at(0).preview_done) { + return f->video_tracks.at(0).video_preview_square; } } @@ -279,7 +279,8 @@ QVariant Media::data(int column, int role) { Footage* f = to_footage(); double r = 30; - if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0)->video_frame_rate)) r = f->video_tracks.at(0)->video_frame_rate; + if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate)) + r = f->video_tracks.at(0).video_frame_rate; long len = f->get_length_in_frames(r); if (len > 0) return frame_to_timecode(len, config.timecode_view, r); diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index da2d3b738..2e11b423e 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -1272,9 +1272,9 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { Clip* c = NULL; if (g.clip != -1) c = sequence->clips.at(g.clip); - FootageStream* ms = NULL; + const FootageStream* ms = NULL; if (g.clip != -1 && c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) { - ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); } // validate ghosts for trimming @@ -2145,7 +2145,7 @@ int color_brightness(int r, int g, int b) { return (0.2126*r + 0.7152*g + 0.0722*b); } -void draw_waveform(Clip* clip, FootageStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) { +void draw_waveform(Clip* clip, const FootageStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) { int divider = ms->audio_channels*2; int channel_height = clip_rect.height()/ms->audio_channels; diff --git a/ui/timelinewidget.h b/ui/timelinewidget.h index bf03b1595..b8339a1b6 100644 --- a/ui/timelinewidget.h +++ b/ui/timelinewidget.h @@ -22,7 +22,7 @@ class QPainter; class Media; bool same_sign(int a, int b); -void draw_waveform(Clip* clip, FootageStream* ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom); +void draw_waveform(Clip* clip, const FootageStream *ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom); class TimelineWidget : public QWidget { Q_OBJECT diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index a501fbfec..9a1681dc7 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -485,7 +485,7 @@ GLuint ViewerWidget::compose_sequence(QVector& nests, bool render_audio) Footage* m = c->media->to_footage(); if (!m->invalid && !(c->track >= 0 && !is_audio_device_set())) { if (m->ready) { - FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream); + const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream); if (ms != NULL && is_clip_active(c, playhead)) { // if thread is already working, we don't want to touch this, // but we also don't want to hang the UI thread diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index fa51aa383..0450efd52 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -34,7 +34,7 @@ public: bool waveform; Clip* waveform_clip; - FootageStream* waveform_ms; + const FootageStream* waveform_ms; double waveform_zoom; int waveform_scroll; From d4c115ef54bc6baf999fea4fde5df6d243fc5d3b Mon Sep 17 00:00:00 2001 From: eszlari Date: Wed, 2 Jan 2019 22:46:49 +0100 Subject: [PATCH 22/65] Linux mime: add icon --- packaging/linux/org.olivevideoeditor.Olive.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/linux/org.olivevideoeditor.Olive.xml b/packaging/linux/org.olivevideoeditor.Olive.xml index df8e8e517..418f4a795 100644 --- a/packaging/linux/org.olivevideoeditor.Olive.xml +++ b/packaging/linux/org.olivevideoeditor.Olive.xml @@ -3,5 +3,6 @@ Olive project + From 820be3b8b518835a134fac6276e8876a568cf758 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 3 Jan 2019 10:12:55 +1100 Subject: [PATCH 23/65] fixed crash caused by editing a sequence after deleting a clip --- project/undo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/undo.cpp b/project/undo.cpp index e67c38941..2fa50573a 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -1127,7 +1127,7 @@ void EditSequenceCommand::update() { // See if one exists or if you have to make one, make it // re-usable - seq->clips.at(i)->refresh(); + if (seq->clips.at(i) != NULL) seq->clips.at(i)->refresh(); } if (sequence == seq) { From 0f4664093190010ce860f73fbeaa47d7e3fab228 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 3 Jan 2019 11:25:47 +1100 Subject: [PATCH 24/65] fixed replace clip media regression --- dialogs/mediapropertiesdialog.cpp | 160 ++++++++++++++--------------- dialogs/replaceclipmediadialog.cpp | 6 +- project/undo.cpp | 5 - 3 files changed, 83 insertions(+), 88 deletions(-) diff --git a/dialogs/mediapropertiesdialog.cpp b/dialogs/mediapropertiesdialog.cpp index a94215f0f..ada35aa76 100644 --- a/dialogs/mediapropertiesdialog.cpp +++ b/dialogs/mediapropertiesdialog.cpp @@ -15,117 +15,117 @@ #include "project/undo.h" MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) : - QDialog(parent), - item(i) + QDialog(parent), + item(i) { - setWindowTitle("\"" + i->data(0, Qt::DisplayRole).toString() + "\" Properties"); + setWindowTitle("\"" + i->get_name() + "\" Properties"); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QGridLayout* grid = new QGridLayout(); setLayout(grid); - int row = 0; + int row = 0; - Footage* f = item->to_footage(); + Footage* f = item->to_footage(); - grid->addWidget(new QLabel("Tracks:"), row, 0, 1, 2); - row++; + grid->addWidget(new QLabel("Tracks:"), row, 0, 1, 2); + row++; - track_list = new QListWidget(); - for (int i=0;ivideo_tracks.size();i++) { - const FootageStream& fs = f->video_tracks.at(i); - QListWidgetItem* item = new QListWidgetItem("Video " + QString::number(fs.file_index) + ": " + QString::number(fs.video_width) + "x" + QString::number(fs.video_height) + " " + QString::number(fs.video_frame_rate) + "FPS"); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked); - item->setData(Qt::UserRole+1, fs.file_index); - track_list->addItem(item); - } - for (int i=0;iaudio_tracks.size();i++) { - const FootageStream& fs = f->audio_tracks.at(i); - QListWidgetItem* item = new QListWidgetItem("Audio " + QString::number(fs.file_index) + ": " + QString::number(fs.audio_frequency) + "Hz " + QString::number(fs.audio_channels) + " channels"); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked); - item->setData(Qt::UserRole+1, fs.file_index); - track_list->addItem(item); - } - grid->addWidget(track_list, row, 0, 1, 2); - row++; + track_list = new QListWidget(); + for (int i=0;ivideo_tracks.size();i++) { + const FootageStream& fs = f->video_tracks.at(i); + QListWidgetItem* item = new QListWidgetItem("Video " + QString::number(fs.file_index) + ": " + QString::number(fs.video_width) + "x" + QString::number(fs.video_height) + " " + QString::number(fs.video_frame_rate) + "FPS"); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked); + item->setData(Qt::UserRole+1, fs.file_index); + track_list->addItem(item); + } + for (int i=0;iaudio_tracks.size();i++) { + const FootageStream& fs = f->audio_tracks.at(i); + QListWidgetItem* item = new QListWidgetItem("Audio " + QString::number(fs.file_index) + ": " + QString::number(fs.audio_frequency) + "Hz " + QString::number(fs.audio_channels) + " channels"); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked); + item->setData(Qt::UserRole+1, fs.file_index); + track_list->addItem(item); + } + grid->addWidget(track_list, row, 0, 1, 2); + row++; - if (f->video_tracks.size() > 0) { - interlacing_box = new QComboBox(); - interlacing_box->addItem("Auto (" + get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing) + ")"); - interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE)); - interlacing_box->addItem(get_interlacing_name(VIDEO_TOP_FIELD_FIRST)); - interlacing_box->addItem(get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST)); + if (f->video_tracks.size() > 0) { + interlacing_box = new QComboBox(); + interlacing_box->addItem("Auto (" + get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing) + ")"); + interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE)); + interlacing_box->addItem(get_interlacing_name(VIDEO_TOP_FIELD_FIRST)); + interlacing_box->addItem(get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST)); - interlacing_box->setCurrentIndex((f->video_tracks.at(0).video_auto_interlacing == f->video_tracks.at(0).video_interlacing) ? 0 : f->video_tracks.at(0).video_interlacing + 1); + interlacing_box->setCurrentIndex((f->video_tracks.at(0).video_auto_interlacing == f->video_tracks.at(0).video_interlacing) ? 0 : f->video_tracks.at(0).video_interlacing + 1); - grid->addWidget(new QLabel("Interlacing:"), row, 0); - grid->addWidget(interlacing_box, row, 1); + grid->addWidget(new QLabel("Interlacing:"), row, 0); + grid->addWidget(interlacing_box, row, 1); - row++; - } + row++; + } - name_box = new QLineEdit(item->get_name()); - grid->addWidget(new QLabel("Name:"), row, 0); - grid->addWidget(name_box, row, 1); - row++; + name_box = new QLineEdit(item->get_name()); + grid->addWidget(new QLabel("Name:"), row, 0); + grid->addWidget(name_box, row, 1); + row++; QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttons->setCenterButtons(true); - grid->addWidget(buttons, row, 0, 1, 2); + grid->addWidget(buttons, row, 0, 1, 2); connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); - connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); + connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); } void MediaPropertiesDialog::accept() { - Footage* f = item->to_footage(); + Footage* f = item->to_footage(); ComboAction* ca = new ComboAction(); - // set track enable - for (int i=0;icount();i++) { - QListWidgetItem* item = track_list->item(i); - const QVariant& data = item->data(Qt::UserRole+1); - if (!data.isNull()) { - int index = data.toInt(); - bool found = false; - for (int j=0;jvideo_tracks.size();j++) { - if (f->video_tracks.at(j).file_index == index) { - f->video_tracks[j].enabled = (item->checkState() == Qt::Checked); - found = true; - break; - } - } - if (!found) { - for (int j=0;jaudio_tracks.size();j++) { - if (f->audio_tracks.at(j).file_index == index) { - f->audio_tracks[j].enabled = (item->checkState() == Qt::Checked); - break; - } - } - } - } - } + // set track enable + for (int i=0;icount();i++) { + QListWidgetItem* item = track_list->item(i); + const QVariant& data = item->data(Qt::UserRole+1); + if (!data.isNull()) { + int index = data.toInt(); + bool found = false; + for (int j=0;jvideo_tracks.size();j++) { + if (f->video_tracks.at(j).file_index == index) { + f->video_tracks[j].enabled = (item->checkState() == Qt::Checked); + found = true; + break; + } + } + if (!found) { + for (int j=0;jaudio_tracks.size();j++) { + if (f->audio_tracks.at(j).file_index == index) { + f->audio_tracks[j].enabled = (item->checkState() == Qt::Checked); + break; + } + } + } + } + } - // set interlacing - if (f->video_tracks.size() > 0) { - if (interlacing_box->currentIndex() > 0) { - ca->append(new SetInt(&f->video_tracks[0].video_interlacing, interlacing_box->currentIndex() - 1)); - } else { - ca->append(new SetInt(&f->video_tracks[0].video_interlacing, f->video_tracks.at(0).video_auto_interlacing)); - } - } + // set interlacing + if (f->video_tracks.size() > 0) { + if (interlacing_box->currentIndex() > 0) { + ca->append(new SetInt(&f->video_tracks[0].video_interlacing, interlacing_box->currentIndex() - 1)); + } else { + ca->append(new SetInt(&f->video_tracks[0].video_interlacing, f->video_tracks.at(0).video_auto_interlacing)); + } + } - // set name - MediaRename* mr = new MediaRename(item, name_box->text()); + // set name + MediaRename* mr = new MediaRename(item, name_box->text()); ca->append(mr); ca->appendPost(new CloseAllClipsCommand()); - ca->appendPost(new UpdateFootageTooltip(item)); + ca->appendPost(new UpdateFootageTooltip(item)); undo_stack.push(ca); - QDialog::accept(); + QDialog::accept(); } diff --git a/dialogs/replaceclipmediadialog.cpp b/dialogs/replaceclipmediadialog.cpp index c1f042f22..5243cc71c 100644 --- a/dialogs/replaceclipmediadialog.cpp +++ b/dialogs/replaceclipmediadialog.cpp @@ -23,6 +23,8 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media *old_media QDialog(parent), media(old_media) { + setWindowTitle("Replace clips using \"" + old_media->get_name() + "\""); + resize(300, 400); QVBoxLayout* layout = new QVBoxLayout(); @@ -56,12 +58,10 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media *old_media setLayout(layout); tree->setModel(&project_model); - - //copy_tree(NULL, NULL); } void ReplaceClipMediaDialog::replace() { - QModelIndexList selected_items = panel_project->get_current_selected(); + QModelIndexList selected_items = tree->selectionModel()->selectedRows(); if (selected_items.size() != 1) { QMessageBox::critical(this, "No media selected", "Please select a media to replace with or click 'Cancel'.", QMessageBox::Ok); } else { diff --git a/project/undo.cpp b/project/undo.cpp index 2fa50573a..c77a30e67 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -1122,11 +1122,6 @@ void EditSequenceCommand::update() { item->set_sequence(seq); for (int i=0;iclips.size();i++) { - // TODO shift in/out/clipin points to match new frame rate - // BUT ALSO copy/paste must need a similar routine, no? - // See if one exists or if you have to make one, make it - // re-usable - if (seq->clips.at(i) != NULL) seq->clips.at(i)->refresh(); } From a98fb0bbc000c889cc7a000b6aa229f29b0decee Mon Sep 17 00:00:00 2001 From: eszlari Date: Thu, 3 Jan 2019 01:35:35 +0100 Subject: [PATCH 25/65] appdata: screenshot, license, bugtracker --- packaging/linux/org.olivevideoeditor.Olive.appdata.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/linux/org.olivevideoeditor.Olive.appdata.xml b/packaging/linux/org.olivevideoeditor.Olive.appdata.xml index fb3a2c6f5..703ce1de1 100644 --- a/packaging/linux/org.olivevideoeditor.Olive.appdata.xml +++ b/packaging/linux/org.olivevideoeditor.Olive.appdata.xml @@ -3,7 +3,7 @@ org.olivevideoeditor.Olive Olive CC0-1.0 - GPLv3 + GPL-3.0 Olive Team Non-linear video editor Editor de vídeo não-linear @@ -15,9 +15,9 @@

Olive — свободный нелинейный видеоредактор, задуманный как полноценная замена закрытым коммерческим продуктам.

https://www.olivevideoeditor.org https://www.patreon.com/olivevideoeditor - https://github.com/olive-editor/olive/issues + https://github.com/olive-editor/olive/issues - http://images.libregraphicsworld.org/video/2018/12/introducing-olive/olive-alpha-main-window.jpg + https://olivevideoeditor.org/img/screenshot.jpg From 30933cbfcd6eeae5c1853cd9bcf1469dc775b05c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 3 Jan 2019 21:52:01 +1100 Subject: [PATCH 26/65] implemented full bezier interpolation for keyframes --- effects/internal/audionoiseeffect.cpp | 8 +- effects/internal/cornerpineffect.cpp | 78 ++-- effects/internal/paneffect.cpp | 8 +- effects/internal/shakeeffect.cpp | 14 +- effects/internal/solideffect.cpp | 90 ++--- effects/internal/texteffect.cpp | 50 +-- effects/internal/timecodeeffect.cpp | 102 +++--- effects/internal/toneeffect.cpp | 10 +- effects/internal/transformeffect.cpp | 230 ++++++------ effects/internal/volumeeffect.cpp | 6 +- io/math.cpp | 45 ++- io/math.h | 5 + panels/grapheditor.cpp | 47 ++- panels/grapheditor.h | 5 +- project/effect.cpp | 386 +++++++++---------- project/effectfield.cpp | 509 +++++++++++++------------- project/effectfield.h | 13 +- project/effectrow.cpp | 264 ++++++------- project/keyframe.cpp | 7 + project/keyframe.h | 18 +- project/undo.cpp | 40 +- project/undo.h | 16 +- ui/graphview.cpp | 269 ++++++++++---- ui/graphview.h | 12 +- ui/keyframeview.cpp | 150 ++++---- 25 files changed, 1311 insertions(+), 1071 deletions(-) diff --git a/effects/internal/audionoiseeffect.cpp b/effects/internal/audionoiseeffect.cpp index bd0283a5b..effd65f6e 100644 --- a/effects/internal/audionoiseeffect.cpp +++ b/effects/internal/audionoiseeffect.cpp @@ -4,15 +4,15 @@ #include AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE, "amount"); + amount_val = add_row("Amount")->add_field(EFFECT_FIELD_DOUBLE, "amount"); amount_val->set_double_minimum_value(0); amount_val->set_double_maximum_value(100); - amount_val->set_double_default_value(20); + amount_val->set_double_default_value(20); - mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL, "mix"); + mix_val = add_row("Mix")->add_field(EFFECT_FIELD_BOOL, "mix"); mix_val->set_bool_value(true); - srand(QDateTime::currentMSecsSinceEpoch()); + srand(QDateTime::currentMSecsSinceEpoch()); } void AudioNoiseEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) { diff --git a/effects/internal/cornerpineffect.cpp b/effects/internal/cornerpineffect.cpp index 96069b0bf..5be93b671 100644 --- a/effects/internal/cornerpineffect.cpp +++ b/effects/internal/cornerpineffect.cpp @@ -5,59 +5,59 @@ #include "debug.h" CornerPinEffect::CornerPinEffect(Clip *c, const EffectMeta *em) : Effect(c, em) { - enable_coords = true; + enable_coords = true; enable_shader = true; - EffectRow* top_left = add_row("Top Left:"); - top_left_x = top_left->add_field(EFFECT_FIELD_DOUBLE, "topleftx"); - top_left_y = top_left->add_field(EFFECT_FIELD_DOUBLE, "toplefty"); + EffectRow* top_left = add_row("Top Left"); + top_left_x = top_left->add_field(EFFECT_FIELD_DOUBLE, "topleftx"); + top_left_y = top_left->add_field(EFFECT_FIELD_DOUBLE, "toplefty"); - EffectRow* top_right = add_row("Top Right:"); - top_right_x = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprightx"); - top_right_y = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprighty"); + EffectRow* top_right = add_row("Top Right"); + top_right_x = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprightx"); + top_right_y = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprighty"); - EffectRow* bottom_left = add_row("Bottom Left:"); - bottom_left_x = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomleftx"); - bottom_left_y = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomlefty"); + EffectRow* bottom_left = add_row("Bottom Left"); + bottom_left_x = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomleftx"); + bottom_left_y = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomlefty"); - EffectRow* bottom_right = add_row("Bottom Right:"); - bottom_right_x = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrightx"); - bottom_right_y = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrighty"); + EffectRow* bottom_right = add_row("Bottom Right"); + bottom_right_x = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrightx"); + bottom_right_y = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrighty"); - perspective = add_row("Perspective:")->add_field(EFFECT_FIELD_BOOL, "perspective"); - perspective->set_bool_value(true); + perspective = add_row("Perspective")->add_field(EFFECT_FIELD_BOOL, "perspective"); + perspective->set_bool_value(true); - top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_left_gizmo->x_field1 = top_left_x; - top_left_gizmo->y_field1 = top_left_y; + top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_left_gizmo->x_field1 = top_left_x; + top_left_gizmo->y_field1 = top_left_y; - top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_right_gizmo->x_field1 = top_right_x; - top_right_gizmo->y_field1 = top_right_y; + top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_right_gizmo->x_field1 = top_right_x; + top_right_gizmo->y_field1 = top_right_y; - bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_left_gizmo->x_field1 = bottom_left_x; - bottom_left_gizmo->y_field1 = bottom_left_y; + bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_left_gizmo->x_field1 = bottom_left_x; + bottom_left_gizmo->y_field1 = bottom_left_y; - bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_right_gizmo->x_field1 = bottom_right_x; - bottom_right_gizmo->y_field1 = bottom_right_y; + bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_right_gizmo->x_field1 = bottom_right_x; + bottom_right_gizmo->y_field1 = bottom_right_y; vertPath = "cornerpin.vert"; fragPath = "cornerpin.frag"; } void CornerPinEffect::process_coords(double timecode, GLTextureCoords &coords, int data) { - coords.vertexTopLeftX += top_left_x->get_double_value(timecode); - coords.vertexTopLeftY += top_left_y->get_double_value(timecode); + coords.vertexTopLeftX += top_left_x->get_double_value(timecode); + coords.vertexTopLeftY += top_left_y->get_double_value(timecode); - coords.vertexTopRightX += top_right_x->get_double_value(timecode); - coords.vertexTopRightY += top_right_y->get_double_value(timecode); + coords.vertexTopRightX += top_right_x->get_double_value(timecode); + coords.vertexTopRightY += top_right_y->get_double_value(timecode); - coords.vertexBottomLeftX += bottom_left_x->get_double_value(timecode); - coords.vertexBottomLeftY += bottom_left_y->get_double_value(timecode); + coords.vertexBottomLeftX += bottom_left_x->get_double_value(timecode); + coords.vertexBottomLeftY += bottom_left_y->get_double_value(timecode); - coords.vertexBottomRightX += bottom_right_x->get_double_value(timecode); + coords.vertexBottomRightX += bottom_right_x->get_double_value(timecode); coords.vertexBottomRightY += bottom_right_y->get_double_value(timecode); } @@ -66,12 +66,12 @@ void CornerPinEffect::process_shader(double timecode, GLTextureCoords &coords) { glslProgram->setUniformValue("p1", (GLfloat) coords.vertexBottomRightX, (GLfloat) coords.vertexBottomRightY); glslProgram->setUniformValue("p2", (GLfloat) coords.vertexTopLeftX, (GLfloat) coords.vertexTopLeftY); glslProgram->setUniformValue("p3", (GLfloat) coords.vertexTopRightX, (GLfloat) coords.vertexTopRightY); - glslProgram->setUniformValue("perspective", perspective->get_bool_value(timecode)); + glslProgram->setUniformValue("perspective", perspective->get_bool_value(timecode)); } void CornerPinEffect::gizmo_draw(double timecode, GLTextureCoords &coords) { - top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); + top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); + bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); + bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); } diff --git a/effects/internal/paneffect.cpp b/effects/internal/paneffect.cpp index 0a7b25a58..fcb862192 100644 --- a/effects/internal/paneffect.cpp +++ b/effects/internal/paneffect.cpp @@ -9,13 +9,13 @@ #include "ui/collapsiblewidget.h" PanEffect::PanEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - EffectRow* pan_row = add_row("Pan:"); - pan_val = pan_row->add_field(EFFECT_FIELD_DOUBLE, "pan"); + EffectRow* pan_row = add_row("Pan"); + pan_val = pan_row->add_field(EFFECT_FIELD_DOUBLE, "pan"); pan_val->set_double_minimum_value(-100); - pan_val->set_double_maximum_value(100); + pan_val->set_double_maximum_value(100); // set defaults - pan_val->set_double_default_value(0); + pan_val->set_double_default_value(0); } void PanEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) { diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index 0687a71ad..92975fe8a 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -16,19 +16,19 @@ ShakeEffect::ShakeEffect(Clip *c, const EffectMeta *em) : Effect(c, em) { enable_coords = true; - EffectRow* intensity_row = add_row("Intensity:"); - intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE, "intensity"); + EffectRow* intensity_row = add_row("Intensity"); + intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE, "intensity"); intensity_val->set_double_minimum_value(0); - EffectRow* rotation_row = add_row("Rotation:"); - rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); + EffectRow* rotation_row = add_row("Rotation"); + rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); rotation_val->set_double_minimum_value(0); - EffectRow* frequency_row = add_row("Frequency:"); - frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE, "frequency"); + EffectRow* frequency_row = add_row("Frequency"); + frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE, "frequency"); frequency_val->set_double_minimum_value(0); - // set defaults + // set defaults intensity_val->set_double_default_value(25); rotation_val->set_double_default_value(10); frequency_val->set_double_default_value(5); diff --git a/effects/internal/solideffect.cpp b/effects/internal/solideffect.cpp index 0f888da61..512dccda6 100644 --- a/effects/internal/solideffect.cpp +++ b/effects/internal/solideffect.cpp @@ -21,36 +21,36 @@ SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { enable_superimpose = true; - solid_type = add_row("Type:")->add_field(EFFECT_FIELD_COMBO, "type"); + solid_type = add_row("Type")->add_field(EFFECT_FIELD_COMBO, "type"); solid_type->add_combo_item("Solid Color", SOLID_TYPE_COLOR); solid_type->add_combo_item("SMPTE Bars", SOLID_TYPE_BARS); - solid_type->add_combo_item("Checkerboard", SOLID_TYPE_CHECKERBOARD); + solid_type->add_combo_item("Checkerboard", SOLID_TYPE_CHECKERBOARD); - opacity_field = add_row("Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "opacity"); - opacity_field->set_double_minimum_value(0); - opacity_field->set_double_maximum_value(100); - opacity_field->set_double_default_value(100); + opacity_field = add_row("Opacity")->add_field(EFFECT_FIELD_DOUBLE, "opacity"); + opacity_field->set_double_minimum_value(0); + opacity_field->set_double_maximum_value(100); + opacity_field->set_double_default_value(100); - solid_color_field = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color"); - solid_color_field->set_color_value(Qt::red); + solid_color_field = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color"); + solid_color_field->set_color_value(Qt::red); - checkerboard_size_field = add_row("Checkerboard Size:")->add_field(EFFECT_FIELD_DOUBLE, "checker_size"); - checkerboard_size_field->set_double_minimum_value(1); - checkerboard_size_field->set_double_default_value(10); + checkerboard_size_field = add_row("Checkerboard Size")->add_field(EFFECT_FIELD_DOUBLE, "checker_size"); + checkerboard_size_field->set_double_minimum_value(1); + checkerboard_size_field->set_double_default_value(10); - // hacky but eh - QComboBox* solid_type_combo = static_cast(solid_type->get_ui_element()); - connect(solid_type_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(ui_update(int))); - ui_update(solid_type_combo->currentIndex()); + // hacky but eh + QComboBox* solid_type_combo = static_cast(solid_type->get_ui_element()); + connect(solid_type_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(ui_update(int))); + ui_update(solid_type_combo->currentIndex()); - /*vertPath = ":/shaders/common.vert"; - fragPath = ":/shaders/solideffect.frag";*/ + /*vertPath = ":/shaders/common.vert"; + fragPath = ":/shaders/solideffect.frag";*/ } void SolidEffect::redraw(double timecode) { int w = img.width(); int h = img.height(); - int alpha = qRound(opacity_field->get_double_value(timecode)*2.55); + int alpha = qRound(opacity_field->get_double_value(timecode)*2.55); switch (solid_type->get_combo_data(timecode).toInt()) { case SOLID_TYPE_COLOR: { @@ -141,37 +141,37 @@ void SolidEffect::redraw(double timecode) { third_color.setAlpha(alpha); p.fillRect(QRect(bar_x, third_bar_y, third_bar_width, third_bar_height), third_color); } - } - break; - case SOLID_TYPE_CHECKERBOARD: - { - // draw checkboard - QPainter p(&img); - img.fill(Qt::transparent); + } + break; + case SOLID_TYPE_CHECKERBOARD: + { + // draw checkboard + QPainter p(&img); + img.fill(Qt::transparent); - int checker_width = qCeil(checkerboard_size_field->get_double_value(timecode)); - int checker_x, checker_y; - int checkerboard_size_w = qCeil(double(w)/checker_width); - int checkerboard_size_h = qCeil(double(h)/checker_width); + int checker_width = qCeil(checkerboard_size_field->get_double_value(timecode)); + int checker_x, checker_y; + int checkerboard_size_w = qCeil(double(w)/checker_width); + int checkerboard_size_h = qCeil(double(h)/checker_width); - QColor checker_odd(QColor(0, 0, 0, alpha)); - QColor checker_even(solid_color_field->get_color_value(timecode)); - checker_even.setAlpha(alpha); - QVector checker_color{checker_odd, checker_even}; + QColor checker_odd(QColor(0, 0, 0, alpha)); + QColor checker_even(solid_color_field->get_color_value(timecode)); + checker_even.setAlpha(alpha); + QVector checker_color{checker_odd, checker_even}; - for(int i = 0; i < checkerboard_size_w; i++){ - checker_x = checker_width*i; - for(int j = 0; j < checkerboard_size_h; j++){ - checker_y = checker_width*j; - p.fillRect(QRect(checker_x, checker_y, checker_width, checker_width), checker_color[(i + j)%2]); - } - } - } - break; - } + for(int i = 0; i < checkerboard_size_w; i++){ + checker_x = checker_width*i; + for(int j = 0; j < checkerboard_size_h; j++){ + checker_y = checker_width*j; + p.fillRect(QRect(checker_x, checker_y, checker_width, checker_width), checker_color[(i + j)%2]); + } + } + } + break; + } } void SolidEffect::ui_update(int i) { - solid_color_field->set_enabled(i == SOLID_TYPE_COLOR || i == SOLID_TYPE_CHECKERBOARD); - checkerboard_size_field->set_enabled(i == SOLID_TYPE_CHECKERBOARD); + solid_color_field->set_enabled(i == SOLID_TYPE_COLOR || i == SOLID_TYPE_CHECKERBOARD); + checkerboard_size_field->set_enabled(i == SOLID_TYPE_CHECKERBOARD); } diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index 66a6a6108..cb3ea3155 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -24,43 +24,43 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) : Effect(c, em) { enable_superimpose = true; - enable_shader = true; + enable_shader = true; - text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, "text", 2); + text_val = add_row("Text")->add_field(EFFECT_FIELD_STRING, "text", 2); - set_font_combobox = add_row("Font:")->add_field(EFFECT_FIELD_FONT, "font", 2); + set_font_combobox = add_row("Font")->add_field(EFFECT_FIELD_FONT, "font", 2); - size_val = add_row("Size:")->add_field(EFFECT_FIELD_DOUBLE, "size", 2); - size_val->set_double_minimum_value(0); + size_val = add_row("Size")->add_field(EFFECT_FIELD_DOUBLE, "size", 2); + size_val->set_double_minimum_value(0); - set_color_button = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color", 2); + set_color_button = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color", 2); - EffectRow* alignment_row = add_row("Alignment:"); - halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign"); + EffectRow* alignment_row = add_row("Alignment"); + halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign"); halign_field->add_combo_item("Left", Qt::AlignLeft); halign_field->add_combo_item("Center", Qt::AlignHCenter); halign_field->add_combo_item("Right", Qt::AlignRight); - halign_field->add_combo_item("Justify", Qt::AlignJustify); + halign_field->add_combo_item("Justify", Qt::AlignJustify); - valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign"); + valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign"); valign_field->add_combo_item("Top", Qt::AlignTop); valign_field->add_combo_item("Center", Qt::AlignVCenter); - valign_field->add_combo_item("Bottom", Qt::AlignBottom); + valign_field->add_combo_item("Bottom", Qt::AlignBottom); - word_wrap_field = add_row("Word Wrap:")->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2); + word_wrap_field = add_row("Word Wrap")->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2); - outline_bool = add_row("Outline:")->add_field(EFFECT_FIELD_BOOL, "outline", 2); - outline_color = add_row("Outline Color:")->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2); - outline_width = add_row("Outline Width:")->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2); + outline_bool = add_row("Outline")->add_field(EFFECT_FIELD_BOOL, "outline", 2); + outline_color = add_row("Outline Color")->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2); + outline_width = add_row("Outline Width")->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2); outline_width->set_double_minimum_value(0); - shadow_bool = add_row("Shadow:")->add_field(EFFECT_FIELD_BOOL, "shadow", 2); - shadow_color = add_row("Shadow Color:")->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2); - shadow_distance = add_row("Shadow Distance:")->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2); + shadow_bool = add_row("Shadow")->add_field(EFFECT_FIELD_BOOL, "shadow", 2); + shadow_color = add_row("Shadow Color")->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2); + shadow_distance = add_row("Shadow Distance")->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2); shadow_distance->set_double_minimum_value(0); - shadow_softness = add_row("Shadow Softness:")->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2); + shadow_softness = add_row("Shadow Softness")->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2); shadow_softness->set_double_minimum_value(0); - shadow_opacity = add_row("Shadow Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2); + shadow_opacity = add_row("Shadow Opacity")->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2); shadow_opacity->set_double_minimum_value(0); shadow_opacity->set_double_maximum_value(100); @@ -78,13 +78,13 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) : outline_width->set_double_default_value(20); outline_enable(false); - shadow_enable(false); + shadow_enable(false); connect(shadow_bool, SIGNAL(toggled(bool)), this, SLOT(shadow_enable(bool))); connect(outline_bool, SIGNAL(toggled(bool)), this, SLOT(outline_enable(bool))); - vertPath = "common.vert"; - fragPath = "dropshadow.frag"; + vertPath = "common.vert"; + fragPath = "dropshadow.frag"; } void TextEffect::redraw(double timecode) { @@ -170,7 +170,7 @@ void TextEffect::redraw(double timecode) { } path.addText(text_x, text_y, font, lines.at(i)); - } + } // draw outline int outline_width_val = outline_width->get_double_value(timecode); @@ -185,7 +185,7 @@ void TextEffect::redraw(double timecode) { // draw "master" text p.setPen(Qt::NoPen); p.setBrush(set_color_button->get_color_value(timecode)); - p.drawPath(path); + p.drawPath(path); } void TextEffect::shadow_enable(bool e) { diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index f09b7fecc..baba8c50d 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -24,82 +24,82 @@ #include "playback/playback.h" TimecodeEffect::TimecodeEffect(Clip *c, const EffectMeta* em) : - Effect(c, em) + Effect(c, em) { - enable_always_update = true; + enable_always_update = true; enable_superimpose = true; - EffectRow* tc_row = add_row("Timecode:"); - tc_select = tc_row->add_field(EFFECT_FIELD_COMBO, "tc_selector"); - tc_select->add_combo_item("Sequence", true); - tc_select->add_combo_item("Media", false); - tc_select->set_combo_index(0); + EffectRow* tc_row = add_row("Timecode"); + tc_select = tc_row->add_field(EFFECT_FIELD_COMBO, "tc_selector"); + tc_select->add_combo_item("Sequence", true); + tc_select->add_combo_item("Media", false); + tc_select->set_combo_index(0); - scale_val = add_row("Scale:")->add_field(EFFECT_FIELD_DOUBLE, "scale", 2); - scale_val->set_double_minimum_value(1); - scale_val->set_double_default_value(100); - scale_val->set_double_maximum_value(1000); + scale_val = add_row("Scale")->add_field(EFFECT_FIELD_DOUBLE, "scale", 2); + scale_val->set_double_minimum_value(1); + scale_val->set_double_default_value(100); + scale_val->set_double_maximum_value(1000); - color_val = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color", 2); - color_val->set_color_value(Qt::white); + color_val = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color", 2); + color_val->set_color_value(Qt::white); - color_bg_val = add_row("Background Color:")->add_field(EFFECT_FIELD_COLOR, "bgcolor", 2); - color_bg_val->set_color_value(Qt::black); + color_bg_val = add_row("Background Color")->add_field(EFFECT_FIELD_COLOR, "bgcolor", 2); + color_bg_val->set_color_value(Qt::black); - bg_alpha = add_row("Background Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "bgalpha", 2); - bg_alpha->set_double_minimum_value(0); - bg_alpha->set_double_maximum_value(100); - bg_alpha->set_double_default_value(50); + bg_alpha = add_row("Background Opacity")->add_field(EFFECT_FIELD_DOUBLE, "bgalpha", 2); + bg_alpha->set_double_minimum_value(0); + bg_alpha->set_double_maximum_value(100); + bg_alpha->set_double_default_value(50); - EffectRow* offset = add_row("Offset:"); - offset_x_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsetx"); - offset_y_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsety"); + EffectRow* offset = add_row("Offset"); + offset_x_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsetx"); + offset_y_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsety"); - prepend_text = add_row("Prepend:")->add_field(EFFECT_FIELD_STRING, "prepend", 2); + prepend_text = add_row("Prepend")->add_field(EFFECT_FIELD_STRING, "prepend", 2); } void TimecodeEffect::redraw(double timecode) { - if (tc_select->get_combo_data(timecode).toBool()){ - display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(sequence->playhead, config.timecode_view, sequence->frame_rate);} - else { - double media_rate = parent_clip->getMediaFrameRate(); - display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(timecode * media_rate, config.timecode_view, media_rate);} - img.fill(Qt::transparent); + if (tc_select->get_combo_data(timecode).toBool()){ + display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(sequence->playhead, config.timecode_view, sequence->frame_rate);} + else { + double media_rate = parent_clip->getMediaFrameRate(); + display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(timecode * media_rate, config.timecode_view, media_rate);} + img.fill(Qt::transparent); QPainter p(&img); - p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::Antialiasing); int width = img.width(); int height = img.height(); // set font font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias); - font.setFamily("Helvetica"); - font.setPixelSize(qCeil(scale_val->get_double_value(timecode)*.01*(height/10))); - p.setFont(font); + font.setFamily("Helvetica"); + font.setPixelSize(qCeil(scale_val->get_double_value(timecode)*.01*(height/10))); + p.setFont(font); QFontMetrics fm(font); - QPainterPath path; + QPainterPath path; - int text_x, text_y, rect_y, offset_x, offset_y; - int text_height = fm.height(); - int text_width = fm.width(display_timecode); - QColor background_color = color_bg_val->get_color_value(timecode); - int alpha_val = bg_alpha->get_double_value(timecode)*2.55; - background_color.setAlpha(alpha_val); + int text_x, text_y, rect_y, offset_x, offset_y; + int text_height = fm.height(); + int text_width = fm.width(display_timecode); + QColor background_color = color_bg_val->get_color_value(timecode); + int alpha_val = bg_alpha->get_double_value(timecode)*2.55; + background_color.setAlpha(alpha_val); - offset_x = int(offset_x_val->get_double_value(timecode)); - offset_y = int(offset_y_val->get_double_value(timecode)); + offset_x = int(offset_x_val->get_double_value(timecode)); + offset_y = int(offset_y_val->get_double_value(timecode)); - text_x = offset_x + (width/2) - (text_width/2); - text_y = offset_y + height - height/10; - rect_y = text_y + fm.descent()/2 - text_height; + text_x = offset_x + (width/2) - (text_width/2); + text_y = offset_y + height - height/10; + rect_y = text_y + fm.descent()/2 - text_height; - path.addText(text_x, text_y, font, display_timecode); + path.addText(text_x, text_y, font, display_timecode); - p.setPen(Qt::NoPen); - p.setBrush(background_color); - p.drawRect(QRect(text_x-fm.descent()/2, rect_y, text_width+fm.descent(), text_height)); - p.setBrush(color_val->get_color_value(timecode)); - p.drawPath(path); + p.setPen(Qt::NoPen); + p.setBrush(background_color); + p.drawRect(QRect(text_x-fm.descent()/2, rect_y, text_width+fm.descent(), text_height)); + p.setBrush(color_val->get_color_value(timecode)); + p.drawPath(path); } diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index bb0fb7ac8..b17478e40 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -9,21 +9,21 @@ #include "debug.h" ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_MIN) { - type_val = add_row("Type:")->add_field(EFFECT_FIELD_COMBO, "type"); + type_val = add_row("Type")->add_field(EFFECT_FIELD_COMBO, "type"); type_val->add_combo_item("Sine", TONE_TYPE_SINE); - freq_val = add_row("Frequency:")->add_field(EFFECT_FIELD_DOUBLE, "frequency"); + freq_val = add_row("Frequency")->add_field(EFFECT_FIELD_DOUBLE, "frequency"); freq_val->set_double_minimum_value(20); freq_val->set_double_maximum_value(20000); freq_val->set_double_default_value(1000); - amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE, "amount"); + amount_val = add_row("Amount")->add_field(EFFECT_FIELD_DOUBLE, "amount"); amount_val->set_double_minimum_value(0); amount_val->set_double_maximum_value(100); amount_val->set_double_default_value(25); - mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL, "mix"); - mix_val->set_bool_value(true); + mix_val = add_row("Mix")->add_field(EFFECT_FIELD_BOOL, "mix"); + mix_val->set_bool_value(true); } void ToneEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) { diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 42b54497c..0b7466dd5 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -28,104 +28,104 @@ #define BLEND_MODE_MULTIPLY 2 #define BLEND_MODE_OVERLAY 3 -TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { - enable_coords = true; +TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { + enable_coords = true; - EffectRow* position_row = add_row("Position:"); - position_x = position_row->add_field(EFFECT_FIELD_DOUBLE, "posx"); // position X - position_y = position_row->add_field(EFFECT_FIELD_DOUBLE, "posy"); // position Y + EffectRow* position_row = add_row("Position"); + position_x = position_row->add_field(EFFECT_FIELD_DOUBLE, "posx"); // position X + position_y = position_row->add_field(EFFECT_FIELD_DOUBLE, "posy"); // position Y - EffectRow* scale_row = add_row("Scale:"); - scale_x = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scalex"); // scale X (and Y is uniform scale is selected) + EffectRow* scale_row = add_row("Scale"); + scale_x = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scalex"); // scale X (and Y is uniform scale is selected) scale_x->set_double_minimum_value(0); scale_x->set_double_maximum_value(3000); - scale_y = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scaley"); // scale Y (disabled if uniform scale is selected) + scale_y = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scaley"); // scale Y (disabled if uniform scale is selected) scale_y->set_double_minimum_value(0); scale_y->set_double_maximum_value(3000); - EffectRow* uniform_scale_row = add_row("Uniform Scale:"); - uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL, "uniformscale"); // uniform scale option + EffectRow* uniform_scale_row = add_row("Uniform Scale"); + uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL, "uniformscale"); // uniform scale option - EffectRow* rotation_row = add_row("Rotation:"); - rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); + EffectRow* rotation_row = add_row("Rotation"); + rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); - EffectRow* anchor_point_row = add_row("Anchor Point:"); - anchor_x_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchorx"); // anchor point X - anchor_y_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchory"); // anchor point Y + EffectRow* anchor_point_row = add_row("Anchor Point"); + anchor_x_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchorx"); // anchor point X + anchor_y_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchory"); // anchor point Y - EffectRow* opacity_row = add_row("Opacity:"); - opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE, "opacity"); // opacity + EffectRow* opacity_row = add_row("Opacity"); + opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE, "opacity"); // opacity opacity->set_double_minimum_value(0); opacity->set_double_maximum_value(100); - EffectRow* blend_mode_row = add_row("Blend Mode:"); - blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO, "blendmode"); // blend mode + EffectRow* blend_mode_row = add_row("Blend Mode"); + blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO, "blendmode"); // blend mode blend_mode_box->add_combo_item("Normal", BLEND_MODE_NORMAL); blend_mode_box->add_combo_item("Overlay", BLEND_MODE_OVERLAY); blend_mode_box->add_combo_item("Screen", BLEND_MODE_SCREEN); blend_mode_box->add_combo_item("Multiply", BLEND_MODE_MULTIPLY); - // set up gizmos - top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_left_gizmo->set_cursor(Qt::SizeFDiagCursor); - top_left_gizmo->x_field1 = scale_x; + // set up gizmos + top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_left_gizmo->set_cursor(Qt::SizeFDiagCursor); + top_left_gizmo->x_field1 = scale_x; - top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_center_gizmo->set_cursor(Qt::SizeVerCursor); - top_center_gizmo->y_field1 = scale_x; + top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_center_gizmo->set_cursor(Qt::SizeVerCursor); + top_center_gizmo->y_field1 = scale_x; - top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_right_gizmo->set_cursor(Qt::SizeBDiagCursor); - top_right_gizmo->x_field1 = scale_x; + top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_right_gizmo->set_cursor(Qt::SizeBDiagCursor); + top_right_gizmo->x_field1 = scale_x; - bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor); - bottom_left_gizmo->x_field1 = scale_x; + bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor); + bottom_left_gizmo->x_field1 = scale_x; - bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_center_gizmo->set_cursor(Qt::SizeVerCursor); - bottom_center_gizmo->y_field1 = scale_x; + bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_center_gizmo->set_cursor(Qt::SizeVerCursor); + bottom_center_gizmo->y_field1 = scale_x; - bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor); - bottom_right_gizmo->x_field1 = scale_x; + bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor); + bottom_right_gizmo->x_field1 = scale_x; - left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - left_center_gizmo->set_cursor(Qt::SizeHorCursor); - left_center_gizmo->x_field1 = scale_x; + left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + left_center_gizmo->set_cursor(Qt::SizeHorCursor); + left_center_gizmo->x_field1 = scale_x; - right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - right_center_gizmo->set_cursor(Qt::SizeHorCursor); - right_center_gizmo->x_field1 = scale_x; + right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + right_center_gizmo->set_cursor(Qt::SizeHorCursor); + right_center_gizmo->x_field1 = scale_x; - anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET); - anchor_gizmo->set_cursor(Qt::SizeAllCursor); - anchor_gizmo->x_field1 = anchor_x_box; - anchor_gizmo->y_field1 = anchor_y_box; - anchor_gizmo->x_field2 = position_x; - anchor_gizmo->y_field2 = position_y; + anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET); + anchor_gizmo->set_cursor(Qt::SizeAllCursor); + anchor_gizmo->x_field1 = anchor_x_box; + anchor_gizmo->y_field1 = anchor_y_box; + anchor_gizmo->x_field2 = position_x; + anchor_gizmo->y_field2 = position_y; - rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT); - rotate_gizmo->color = Qt::green; - rotate_gizmo->set_cursor(Qt::SizeAllCursor); - rotate_gizmo->x_field1 = rotation; + rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT); + rotate_gizmo->color = Qt::green; + rotate_gizmo->set_cursor(Qt::SizeAllCursor); + rotate_gizmo->x_field1 = rotation; - rect_gizmo = add_gizmo(GIZMO_TYPE_POLY); - rect_gizmo->x_field1 = position_x; - rect_gizmo->y_field1 = position_y; + rect_gizmo = add_gizmo(GIZMO_TYPE_POLY); + rect_gizmo->x_field1 = position_x; + rect_gizmo->y_field1 = position_y; - connect(uniform_scale_field, SIGNAL(toggled(bool)), this, SLOT(toggle_uniform_scale(bool))); + connect(uniform_scale_field, SIGNAL(toggled(bool)), this, SLOT(toggle_uniform_scale(bool))); - // set defaults + // set defaults uniform_scale_field->set_bool_value(true); blend_mode_box->set_combo_index(0); refresh(); } void TransformEffect::refresh() { - if (parent_clip != NULL && parent_clip->sequence != NULL) { - double default_pos_x = parent_clip->sequence->width/2; - double default_pos_y = parent_clip->sequence->height/2; + if (parent_clip != NULL && parent_clip->sequence != NULL) { + double default_pos_x = parent_clip->sequence->width/2; + double default_pos_y = parent_clip->sequence->height/2; position_x->set_double_default_value(default_pos_x); position_y->set_double_default_value(default_pos_y); @@ -140,35 +140,35 @@ void TransformEffect::refresh() { anchor_x_box->set_double_default_value(default_anchor_x); anchor_y_box->set_double_default_value(default_anchor_y); - opacity->set_double_default_value(100); + opacity->set_double_default_value(100); - double x_percent_multipler = 200.0 / parent_clip->sequence->width; - double y_percent_multipler = 200.0 / parent_clip->sequence->height; - top_left_gizmo->x_field_multi1 = -x_percent_multipler; - top_left_gizmo->y_field_multi1 = -y_percent_multipler; - top_center_gizmo->y_field_multi1 = -y_percent_multipler; - top_right_gizmo->x_field_multi1 = x_percent_multipler; - top_right_gizmo->y_field_multi1 = -y_percent_multipler; - bottom_left_gizmo->x_field_multi1 = -x_percent_multipler; - bottom_left_gizmo->y_field_multi1 = y_percent_multipler; - bottom_center_gizmo->y_field_multi1 = y_percent_multipler; - bottom_right_gizmo->x_field_multi1 = x_percent_multipler; - bottom_right_gizmo->y_field_multi1 = y_percent_multipler; - left_center_gizmo->x_field_multi1 = -x_percent_multipler; - right_center_gizmo->x_field_multi1 = x_percent_multipler; - rotate_gizmo->x_field_multi1 = x_percent_multipler; + double x_percent_multipler = 200.0 / parent_clip->sequence->width; + double y_percent_multipler = 200.0 / parent_clip->sequence->height; + top_left_gizmo->x_field_multi1 = -x_percent_multipler; + top_left_gizmo->y_field_multi1 = -y_percent_multipler; + top_center_gizmo->y_field_multi1 = -y_percent_multipler; + top_right_gizmo->x_field_multi1 = x_percent_multipler; + top_right_gizmo->y_field_multi1 = -y_percent_multipler; + bottom_left_gizmo->x_field_multi1 = -x_percent_multipler; + bottom_left_gizmo->y_field_multi1 = y_percent_multipler; + bottom_center_gizmo->y_field_multi1 = y_percent_multipler; + bottom_right_gizmo->x_field_multi1 = x_percent_multipler; + bottom_right_gizmo->y_field_multi1 = y_percent_multipler; + left_center_gizmo->x_field_multi1 = -x_percent_multipler; + right_center_gizmo->x_field_multi1 = x_percent_multipler; + rotate_gizmo->x_field_multi1 = x_percent_multipler; } } void TransformEffect::toggle_uniform_scale(bool enabled) { scale_y->set_enabled(!enabled); - top_center_gizmo->y_field1 = enabled ? scale_x : scale_y; - bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y; - top_left_gizmo->y_field1 = enabled ? NULL : scale_y; - top_right_gizmo->y_field1 = enabled ? NULL : scale_y; - bottom_left_gizmo->y_field1 = enabled ? NULL : scale_y; - bottom_right_gizmo->y_field1 = enabled ? NULL : scale_y; + top_center_gizmo->y_field1 = enabled ? scale_x : scale_y; + bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y; + top_left_gizmo->y_field1 = enabled ? NULL : scale_y; + top_right_gizmo->y_field1 = enabled ? NULL : scale_y; + bottom_left_gizmo->y_field1 = enabled ? NULL : scale_y; + bottom_right_gizmo->y_field1 = enabled ? NULL : scale_y; } void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int data) { @@ -188,51 +188,51 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i coords.vertexBottomRightY -= anchor_y_offset; // rotation - glRotatef(rotation->get_double_value(timecode), 0, 0, 1); + glRotatef(rotation->get_double_value(timecode), 0, 0, 1); // scale float sx = scale_x->get_double_value(timecode)*0.01; float sy = (uniform_scale_field->get_bool_value(timecode)) ? sx : scale_y->get_double_value(timecode)*0.01; - glScalef(sx, sy, 1); + glScalef(sx, sy, 1); - // blend mode + // blend mode switch (blend_mode_box->get_combo_data(timecode).toInt()) { - case BLEND_MODE_NORMAL: - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - case BLEND_MODE_OVERLAY: + case BLEND_MODE_NORMAL: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case BLEND_MODE_OVERLAY: glBlendFunc(GL_SRC_ALPHA, GL_ONE); - break; - case BLEND_MODE_SCREEN: - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); - break; - case BLEND_MODE_MULTIPLY: + break; + case BLEND_MODE_SCREEN: + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); + break; + case BLEND_MODE_MULTIPLY: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); - break; - default: + break; + default: dout << "[ERROR] Invalid blend mode. This is a bug - please contact developers"; - } + } // opacity - float color[4]; - glGetFloatv(GL_CURRENT_COLOR, color); - glColor4f(1.0, 1.0, 1.0, color[3]*(opacity->get_double_value(timecode)*0.01)); + float color[4]; + glGetFloatv(GL_CURRENT_COLOR, color); + glColor4f(1.0, 1.0, 1.0, color[3]*(opacity->get_double_value(timecode)*0.01)); } void TransformEffect::gizmo_draw(double timecode, GLTextureCoords& coords) { - top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5)); - top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5)); - bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5)); - bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); - left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5)); + top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); + top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5)); + top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); + right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5)); + bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); + bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5)); + bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5)); - rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1)); + rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1)); - rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); + rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); + rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); + rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); } diff --git a/effects/internal/volumeeffect.cpp b/effects/internal/volumeeffect.cpp index 6a71d06a7..b7b18017b 100644 --- a/effects/internal/volumeeffect.cpp +++ b/effects/internal/volumeeffect.cpp @@ -9,12 +9,12 @@ #include "ui/collapsiblewidget.h" VolumeEffect::VolumeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - EffectRow* volume_row = add_row("Volume:"); - volume_val = volume_row->add_field(EFFECT_FIELD_DOUBLE, "volume"); + EffectRow* volume_row = add_row("Volume"); + volume_val = volume_row->add_field(EFFECT_FIELD_DOUBLE, "volume"); volume_val->set_double_minimum_value(0); // set defaults - volume_val->set_double_default_value(100); + volume_val->set_double_default_value(100); } void VolumeEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) { diff --git a/io/math.cpp b/io/math.cpp index c26bb5cad..4d71229a4 100644 --- a/io/math.cpp +++ b/io/math.cpp @@ -1,15 +1,54 @@ #include "math.h" #include +#include + +#include "debug.h" int lerp(int a, int b, double t) { - return qRound(((1.0 - t) * a) + (t * b)); + return qRound(((1.0 - t) * a) + (t * b)); } float float_lerp(float a, float b, float t) { - return ((1.0F - t) * a) + (t * b); + return ((1.0F - t) * a) + (t * b); } double double_lerp(double a, double b, double t) { - return ((1.0 - t) * a) + (t * b); + return ((1.0 - t) * a) + (t * b); +} + +double quad_from_t(double a, double b, double c, double t) { + return qPow(1.0 - t, 2)*a + 2*(1.0 - t)*t*b + qPow(t, 2)*c; +} + +double quad_t_from_x(double x, double a, double b, double c) { + return (a - b + qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c); + // alt: return (a - b - qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c); +} + +double cubic_from_t(double a, double b, double c, double d, double t) { + return qPow(1.0 - t, 3)*a + 3*qPow(1.0 - t, 2)*t*b + 3*(1.0 - t)*qPow(t, 2)*c + qPow(t, 3)*d; +} + +double cubic_t_from_x(double x_target, double a, double b, double c, double d) { + double tolerance = 0.0001; + + double lower = 0.0; + double upper = 1.0; + + double percent = 0.5; + double x = cubic_from_t(a, b, c, d, percent); + + while (qAbs(x_target - x) > tolerance) { + if (x_target > x) { + lower = percent; + } else { + upper = percent; + } + + percent = (upper + lower) / 2.0; + x = cubic_from_t(a, b, c, d, percent); + } + + return percent; } diff --git a/io/math.h b/io/math.h index 927e988ac..ba1e7ed9e 100644 --- a/io/math.h +++ b/io/math.h @@ -4,5 +4,10 @@ int lerp(int a, int b, double t); float float_lerp(float a, float b, float t); double double_lerp(double a, double b, double t); +double quad_from_t(double a, double b, double c, double t); +double quad_t_from_x(double x, double a, double b, double c); +double cubic_from_t(double a, double b, double c, double d, double t); +double cubic_t_from_x(double x_target, double a, double b, double c, double d); +double solveCubicBezier(double p0, double p1, double p2, double p3, double x); #endif // MATH_H diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 9b84dd8d0..7dc981fb4 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -59,10 +59,13 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { left_tool_layout->addStretch(); linear_button = new QPushButton("Linear"); + linear_button->setProperty("type", KEYFRAME_TYPE_LINEAR); linear_button->setCheckable(true); bezier_button = new QPushButton("Bezier"); + bezier_button->setProperty("type", KEYFRAME_TYPE_BEZIER); bezier_button->setCheckable(true); hold_button = new QPushButton("Hold"); + hold_button->setProperty("type", KEYFRAME_TYPE_HOLD); hold_button->setCheckable(true); center_tool_layout->addStretch(); @@ -91,9 +94,6 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { value_widget->setLayout(values); values->addStretch(); - current_row_desc = new QLabel(); - values->addWidget(current_row_desc); - QWidget* central_value_widget = new QWidget(); value_layout = new QHBoxLayout(); value_layout->setMargin(0); @@ -103,9 +103,18 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { values->addStretch(); layout->addWidget(value_widget); + current_row_desc = new QLabel(); + current_row_desc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + current_row_desc->setAlignment(Qt::AlignCenter); + layout->addWidget(current_row_desc); + connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double))); connect(view, SIGNAL(x_scroll_changed(int)), header, SLOT(set_scroll(int))); - connect(view, SIGNAL(selection_changed(bool)), this, SLOT(set_key_button_enabled(bool))); + connect(view, SIGNAL(selection_changed(bool, int)), this, SLOT(set_key_button_enabled(bool, int))); + + connect(linear_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type())); + connect(bezier_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type())); + connect(hold_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type())); } void GraphEditor::update_panel() { @@ -129,8 +138,10 @@ void GraphEditor::update_panel() { void GraphEditor::set_row(EffectRow *r) { for (int i=0;ifieldCount();i++) { EffectField* field = r->field(i); if (field->type == EFFECT_FIELD_DOUBLE) { + QPushButton* slider_button = new QPushButton(); + slider_button->setCheckable(true); + slider_button->setChecked(true); + slider_button->setIcon(QIcon(":/icons/record.png")); + slider_button->setProperty("field", i); + slider_button->setIconSize(QSize(8, 8)); + slider_button->setMaximumSize(QSize(12, 12)); + connect(slider_button, SIGNAL(toggled(bool)), this, SLOT(set_field_visibility(bool))); + slider_proxy_buttons.append(slider_button); + value_layout->addWidget(slider_button); + LabelSlider* slider = new LabelSlider(); slider->set_color(get_curve_color(i, r->fieldCount()).name()); connect(slider, SIGNAL(valueChanged()), this, SLOT(passthrough_slider_value())); - slider_proxies.append(slider); value_layout->addWidget(slider); @@ -175,10 +196,13 @@ void GraphEditor::set_row(EffectRow *r) { update_panel(); } -void GraphEditor::set_key_button_enabled(bool e) { +void GraphEditor::set_key_button_enabled(bool e, int type) { linear_button->setEnabled(e); + linear_button->setChecked(type == KEYFRAME_TYPE_LINEAR); bezier_button->setEnabled(e); + bezier_button->setChecked(type == KEYFRAME_TYPE_BEZIER); hold_button->setEnabled(e); + hold_button->setChecked(type == KEYFRAME_TYPE_HOLD); } void GraphEditor::passthrough_slider_value() { @@ -188,3 +212,14 @@ void GraphEditor::passthrough_slider_value() { } } } + +void GraphEditor::set_keyframe_type() { + linear_button->setChecked(linear_button == sender()); + bezier_button->setChecked(bezier_button == sender()); + hold_button->setChecked(hold_button == sender()); + view->set_selected_keyframe_type(sender()->property("type").toInt()); +} + +void GraphEditor::set_field_visibility(bool b) { + view->set_field_visibility(sender()->property("field").toInt(), b); +} diff --git a/panels/grapheditor.h b/panels/grapheditor.h index 79329342b..467de9ec3 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -23,6 +23,7 @@ private: TimelineHeader* header; QHBoxLayout* value_layout; QVector slider_proxies; + QVector slider_proxy_buttons; QVector slider_proxy_sources; QLabel* current_row_desc; EffectRow* row; @@ -31,8 +32,10 @@ private: QPushButton* bezier_button; QPushButton* hold_button; private slots: - void set_key_button_enabled(bool e); + void set_key_button_enabled(bool e, int type); void passthrough_slider_value(); + void set_keyframe_type(); + void set_field_visibility(bool b); }; #endif // GRAPHEDITOR_H diff --git a/project/effect.cpp b/project/effect.cpp index bf20bd12a..40fc2b6b0 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -52,14 +52,14 @@ Effect* create_effect(Clip* c, const EffectMeta* em) { switch (em->internal) { case EFFECT_INTERNAL_TRANSFORM: return new TransformEffect(c, em); case EFFECT_INTERNAL_TEXT: return new TextEffect(c, em); - case EFFECT_INTERNAL_TIMECODE: return new TimecodeEffect(c, em); + case EFFECT_INTERNAL_TIMECODE: return new TimecodeEffect(c, em); case EFFECT_INTERNAL_SOLID: return new SolidEffect(c, em); case EFFECT_INTERNAL_NOISE: return new AudioNoiseEffect(c, em); case EFFECT_INTERNAL_VOLUME: return new VolumeEffect(c, em); case EFFECT_INTERNAL_PAN: return new PanEffect(c, em); case EFFECT_INTERNAL_TONE: return new ToneEffect(c, em); - case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em); - case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em); + case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em); + case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em); } } else { dout << "[ERROR] Invalid effect data"; @@ -69,12 +69,12 @@ Effect* create_effect(Clip* c, const EffectMeta* em) { } const EffectMeta* get_internal_meta(int internal_id, int type) { - for (int i=0;ienabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed())); - ui = new QWidget(); - ui_layout = new QGridLayout(); + // set up base UI + container = new CollapsibleWidget(); + connect(container->enabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed())); + ui = new QWidget(); + ui_layout = new QGridLayout(); ui_layout->setSpacing(4); - ui->setLayout(ui_layout); + ui->setLayout(ui_layout); container->setContents(ui); connect(container->title_bar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); - // set up UI from effect file + // set up UI from effect file container->setText(em->name); if (!em->filename.isEmpty()) { @@ -277,7 +277,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : } } if (!row_name.isEmpty()) { - EffectRow* row = add_row(row_name + ":"); + EffectRow* row = add_row(row_name); while (!reader.atEnd() && !(reader.name() == "row" && reader.isEndElement())) { reader.readNext(); if (reader.name() == "field" && reader.isStartElement()) { @@ -311,7 +311,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : if (id.isEmpty()) { dout << "[ERROR] Couldn't load field from" << em->filename << "- ID cannot be empty."; } else if (type > -1) { - EffectField* field = row->add_field(type, id); + EffectField* field = row->add_field(type, id); connect(field, SIGNAL(changed()), this, SLOT(field_changed())); switch (type) { case EFFECT_FIELD_DOUBLE: @@ -447,27 +447,27 @@ Effect::~Effect() { for (int i=0;irows.at(i); - copy_row->setKeyframing(row->isKeyframing()); + copy_row->setKeyframing(row->isKeyframing()); for (int j=0;jfieldCount();j++) { - EffectField* field = row->field(j); - EffectField* copy_field = copy_row->field(j); - copy_field->keyframes = field->keyframes; + EffectField* field = row->field(j); + EffectField* copy_field = copy_row->field(j); + copy_field->keyframes = field->keyframes; } } } EffectRow* Effect::add_row(const QString& name, bool savable) { - EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size()); - rows.append(row); + EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size()); + rows.append(row); return row; } @@ -476,53 +476,53 @@ EffectRow* Effect::row(int i) { } int Effect::row_count() { - return rows.size(); + return rows.size(); } EffectGizmo *Effect::add_gizmo(int type) { - EffectGizmo* gizmo = new EffectGizmo(type); - gizmos.append(gizmo); - return gizmo; + EffectGizmo* gizmo = new EffectGizmo(type); + gizmos.append(gizmo); + return gizmo; } EffectGizmo *Effect::gizmo(int i) { - return gizmos.at(i); + return gizmos.at(i); } int Effect::gizmo_count(){ - return gizmos.size(); + return gizmos.size(); } void Effect::refresh() {} void Effect::field_changed() { - panel_sequence_viewer->viewer_widget->update(); + panel_sequence_viewer->viewer_widget->update(); panel_graph_editor->update_panel(); } void Effect::show_context_menu(const QPoint& pos) { - if (meta->type == EFFECT_TYPE_EFFECT) { - QMenu menu(mainWindow); + if (meta->type == EFFECT_TYPE_EFFECT) { + QMenu menu(mainWindow); - int index = get_index_in_clip(); + int index = get_index_in_clip(); - if (index > 0) { - QAction* move_up = menu.addAction("Move &Up"); - connect(move_up, SIGNAL(triggered(bool)), this, SLOT(move_up())); - } + if (index > 0) { + QAction* move_up = menu.addAction("Move &Up"); + connect(move_up, SIGNAL(triggered(bool)), this, SLOT(move_up())); + } - if (index < parent_clip->effects.size() - 1) { - QAction* move_down = menu.addAction("Move &Down"); - connect(move_down, SIGNAL(triggered(bool)), this, SLOT(move_down())); - } + if (index < parent_clip->effects.size() - 1) { + QAction* move_down = menu.addAction("Move &Down"); + connect(move_down, SIGNAL(triggered(bool)), this, SLOT(move_down())); + } - menu.addSeparator(); + menu.addSeparator(); - QAction* del_action = menu.addAction("D&elete"); - connect(del_action, SIGNAL(triggered(bool)), this, SLOT(delete_self())); + QAction* del_action = menu.addAction("D&elete"); + connect(del_action, SIGNAL(triggered(bool)), this, SLOT(delete_self())); - menu.exec(container->title_bar->mapToGlobal(pos)); - } + menu.exec(container->title_bar->mapToGlobal(pos)); + } } void Effect::delete_self() { @@ -572,24 +572,24 @@ void Effect::set_enabled(bool b) { QVariant load_data_from_string(int type, const QString& string) { switch (type) { - case EFFECT_FIELD_DOUBLE: return string.toDouble(); - case EFFECT_FIELD_COLOR: return QColor(string); - case EFFECT_FIELD_STRING: return string; - case EFFECT_FIELD_BOOL: return (string == "1"); - case EFFECT_FIELD_COMBO: return string.toInt(); - case EFFECT_FIELD_FONT: return string; + case EFFECT_FIELD_DOUBLE: return string.toDouble(); + case EFFECT_FIELD_COLOR: return QColor(string); + case EFFECT_FIELD_STRING: return string; + case EFFECT_FIELD_BOOL: return (string == "1"); + case EFFECT_FIELD_COMBO: return string.toInt(); + case EFFECT_FIELD_FONT: return string; } return QVariant(); } QString save_data_to_string(int type, const QVariant& data) { switch (type) { - case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); - case EFFECT_FIELD_COLOR: return data.value().name(); - case EFFECT_FIELD_STRING: return data.toString(); - case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); - case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); - case EFFECT_FIELD_FONT: return data.toString(); + case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); + case EFFECT_FIELD_COLOR: return data.value().name(); + case EFFECT_FIELD_STRING: return data.toString(); + case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); + case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); + case EFFECT_FIELD_FONT: return data.toString(); } return QString(); } @@ -610,7 +610,7 @@ void Effect::load(QXmlStreamReader& stream) { stream.readNext(); // read keyframes - /*if (stream.name() == "keyframes" && stream.isStartElement()) { + /*if (stream.name() == "keyframes" && stream.isStartElement()) { for (int k=0;kfieldCount();k++) { - EffectField* field = row->field(k); - EffectKeyframe key; - key.time = keyframe_frame; - key.type = keyframe_type; - field->keyframes.append(key); - } + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + EffectKeyframe key; + key.time = keyframe_frame; + key.type = keyframe_type; + field->keyframes.append(key); + } } stream.readNext(); } } stream.readNext(); - }*/ + }*/ // read field if (stream.name() == "field" && stream.isStartElement()) { @@ -683,22 +683,22 @@ void Effect::load(QXmlStreamReader& stream) { while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) { stream.readNext(); - // read keyframes + // read keyframes if (stream.name() == "key" && stream.isStartElement()) { - row->setKeyframing(true); + row->setKeyframing(true); - EffectKeyframe key; - for (int k=0;ktype, attr.value().toString()); - } else if (attr.name() == "frame") { - key.time = attr.value().toLong(); - } else if (attr.name() == "type") { - key.type = attr.value().toInt(); - } - } - field->keyframes.append(key); + EffectKeyframe key; + for (int k=0;ktype, attr.value().toString()); + } else if (attr.name() == "frame") { + key.time = attr.value().toLong(); + } else if (attr.name() == "type") { + key.type = attr.value().toInt(); + } + } + field->keyframes.append(key); } } } else { @@ -718,29 +718,29 @@ void Effect::load(QXmlStreamReader& stream) { void Effect::save(QXmlStreamWriter& stream) { stream.writeAttribute("name", meta->name); - stream.writeAttribute("enabled", QString::number(is_enabled())); + stream.writeAttribute("enabled", QString::number(is_enabled())); for (int i=0;isavable) { - stream.writeStartElement("row"); // row - for (int j=0;jfieldCount();j++) { - EffectField* field = row->field(j); - stream.writeStartElement("field"); // field - stream.writeAttribute("id", field->id); - stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data())); - for (int k=0;kkeyframes.size();k++) { - const EffectKeyframe& key = field->keyframes.at(k); - stream.writeStartElement("key"); - stream.writeAttribute("value", save_data_to_string(field->type, key.data)); - stream.writeAttribute("frame", QString::number(key.time)); - stream.writeAttribute("type", QString::number(key.type)); - stream.writeEndElement(); // key - } - stream.writeEndElement(); // field - } - stream.writeEndElement(); // row - } + if (row->savable) { + stream.writeStartElement("row"); // row + for (int j=0;jfieldCount();j++) { + EffectField* field = row->field(j); + stream.writeStartElement("field"); // field + stream.writeAttribute("id", field->id); + stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data())); + for (int k=0;kkeyframes.size();k++) { + const EffectKeyframe& key = field->keyframes.at(k); + stream.writeStartElement("key"); + stream.writeAttribute("value", save_data_to_string(field->type, key.data)); + stream.writeAttribute("frame", QString::number(key.time)); + stream.writeAttribute("type", QString::number(key.type)); + stream.writeEndElement(); // key + } + stream.writeEndElement(); // field + } + stream.writeEndElement(); // row + } } } @@ -790,16 +790,16 @@ void Effect::startEffect() { void Effect::endEffect() { if (bound) glslProgram->release(); - bound = false; + bound = false; } void Effect::process_image(double, uint8_t *, int) {} Effect* Effect::copy(Clip* c) { Effect* copy = create_effect(c, meta); - copy->set_enabled(is_enabled()); - copy_field_keyframes(copy); - return copy; + copy->set_enabled(is_enabled()); + copy_field_keyframes(copy); + return copy; } void Effect::process_shader(double timecode, GLTextureCoords&) { @@ -844,7 +844,7 @@ GLuint Effect::process_superimpose(double timecode) { recreate_texture = true; } - if (valueHasChanged(timecode) || recreate_texture || enable_always_update) { + if (valueHasChanged(timecode) || recreate_texture || enable_always_update) { redraw(timecode); } @@ -865,78 +865,78 @@ GLuint Effect::process_superimpose(double timecode) { void Effect::process_audio(double, double, quint8*, int, int) { // only volume/pan, hand off to AU and VST for all other cases - /*double interval = (timecode_end-timecode_start)/nb_bytes; + /*double interval = (timecode_end-timecode_start)/nb_bytes; for (int i=0;ifield(0)->get_double_value(timecode_start+(interval*i), true)); QJSValue result = eval.call(); - samp = result.toInt(); - QJSValueList args; - args << samples << nb_bytes; + samp = result.toInt(); + QJSValueList args; + args << samples << nb_bytes; samples[i+1] = (quint8) (samp >> 8); samples[i] = (quint8) samp; - }*/ + }*/ } void Effect::gizmo_draw(double, GLTextureCoords &) {} void Effect::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, double timecode, bool done) { - for (int i=0;ix_field1 != NULL) { - gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1); - gizmo->x_field1->make_key_from_change(ca); - } - if (gizmo->y_field1 != NULL) { - gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1); - gizmo->y_field1->make_key_from_change(ca); - } - if (gizmo->x_field2 != NULL) { - gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2); - gizmo->x_field2->make_key_from_change(ca); - } - if (gizmo->y_field2 != NULL) { - gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2); - gizmo->y_field2->make_key_from_change(ca); - } - if (done) undo_stack.push(ca); - break; - } - } + for (int i=0;ix_field1 != NULL) { + gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1); + gizmo->x_field1->make_key_from_change(ca); + } + if (gizmo->y_field1 != NULL) { + gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1); + gizmo->y_field1->make_key_from_change(ca); + } + if (gizmo->x_field2 != NULL) { + gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2); + gizmo->x_field2->make_key_from_change(ca); + } + if (gizmo->y_field2 != NULL) { + gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2); + gizmo->y_field2->make_key_from_change(ca); + } + if (done) undo_stack.push(ca); + break; + } + } } void Effect::gizmo_world_to_screen() { - GLfloat view_val[16]; - GLfloat projection_val[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, view_val); - glGetFloatv(GL_PROJECTION_MATRIX, projection_val); + GLfloat view_val[16]; + GLfloat projection_val[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, view_val); + glGetFloatv(GL_PROJECTION_MATRIX, projection_val); - QMatrix4x4 view_matrix(view_val); - QMatrix4x4 projection_matrix(projection_val); + QMatrix4x4 view_matrix(view_val); + QMatrix4x4 projection_matrix(projection_val); - for (int i=0;iget_point_count();j++) { - QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix); + for (int j=0;jget_point_count();j++) { + QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix); - int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width); - int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height); + int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width); + int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height); - g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1); - } - } + g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1); + } + } } bool Effect::are_gizmos_enabled() { - return (gizmos.size() > 0); + return (gizmos.size() > 0); } void Effect::redraw(double) { diff --git a/project/effectfield.cpp b/project/effectfield.cpp index 00435b91d..6eda75505 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -15,353 +15,366 @@ #include "project/sequence.h" #include "io/math.h" -#include +#include + +#include "debug.h" EffectField::EffectField(EffectRow *parent, int t, const QString &i) : - parent_row(parent), - type(t), - id(i) + parent_row(parent), + type(t), + id(i) { - switch (t) { - case EFFECT_FIELD_DOUBLE: - { - LabelSlider* ls = new LabelSlider(); - ui_element = ls; - connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change())); + switch (t) { + case EFFECT_FIELD_DOUBLE: + { + LabelSlider* ls = new LabelSlider(); + ui_element = ls; + connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change())); connect(ls, SIGNAL(clicked()), this, SIGNAL(clicked())); - } - break; - case EFFECT_FIELD_COLOR: - { - ColorButton* cb = new ColorButton(); - ui_element = cb; - connect(cb, SIGNAL(color_changed()), this, SLOT(ui_element_change())); - } - break; - case EFFECT_FIELD_STRING: - { - TextEditEx* edit = new TextEditEx(); - edit->setUndoRedoEnabled(true); - ui_element = edit; - connect(edit, SIGNAL(textChanged()), this, SLOT(ui_element_change())); - } - break; - case EFFECT_FIELD_BOOL: - { - CheckboxEx* cb = new CheckboxEx(); - ui_element = cb; - connect(cb, SIGNAL(clicked(bool)), this, SLOT(ui_element_change())); - connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); - } - break; - case EFFECT_FIELD_COMBO: - { - ComboBoxEx* cb = new ComboBoxEx(); - ui_element = cb; - connect(cb, SIGNAL(activated(int)), this, SLOT(ui_element_change())); - } - break; - case EFFECT_FIELD_FONT: - { - FontCombobox* fcb = new FontCombobox(); - ui_element = fcb; - connect(fcb, SIGNAL(activated(int)), this, SLOT(ui_element_change())); - } - break; - } + } + break; + case EFFECT_FIELD_COLOR: + { + ColorButton* cb = new ColorButton(); + ui_element = cb; + connect(cb, SIGNAL(color_changed()), this, SLOT(ui_element_change())); + } + break; + case EFFECT_FIELD_STRING: + { + TextEditEx* edit = new TextEditEx(); + edit->setUndoRedoEnabled(true); + ui_element = edit; + connect(edit, SIGNAL(textChanged()), this, SLOT(ui_element_change())); + } + break; + case EFFECT_FIELD_BOOL: + { + CheckboxEx* cb = new CheckboxEx(); + ui_element = cb; + connect(cb, SIGNAL(clicked(bool)), this, SLOT(ui_element_change())); + connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); + } + break; + case EFFECT_FIELD_COMBO: + { + ComboBoxEx* cb = new ComboBoxEx(); + ui_element = cb; + connect(cb, SIGNAL(activated(int)), this, SLOT(ui_element_change())); + } + break; + case EFFECT_FIELD_FONT: + { + FontCombobox* fcb = new FontCombobox(); + ui_element = fcb; + connect(fcb, SIGNAL(activated(int)), this, SLOT(ui_element_change())); + } + break; + } } QVariant EffectField::get_previous_data() { - switch (type) { - case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->getPreviousValue(); - case EFFECT_FIELD_COLOR: return static_cast(ui_element)->getPreviousValue(); - case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPreviousValue(); - case EFFECT_FIELD_BOOL: return !static_cast(ui_element)->isChecked(); - case EFFECT_FIELD_COMBO: return static_cast(ui_element)->getPreviousIndex(); - case EFFECT_FIELD_FONT: return static_cast(ui_element)->getPreviousValue(); - } - return QVariant(); + switch (type) { + case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_COLOR: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_BOOL: return !static_cast(ui_element)->isChecked(); + case EFFECT_FIELD_COMBO: return static_cast(ui_element)->getPreviousIndex(); + case EFFECT_FIELD_FONT: return static_cast(ui_element)->getPreviousValue(); + } + return QVariant(); } QVariant EffectField::get_current_data() { - switch (type) { - case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->value(); - case EFFECT_FIELD_COLOR: return static_cast(ui_element)->get_color(); - case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPlainTextEx(); - case EFFECT_FIELD_BOOL: return static_cast(ui_element)->isChecked(); - case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); - case EFFECT_FIELD_FONT: return static_cast(ui_element)->currentText(); - } - return QVariant(); + switch (type) { + case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->value(); + case EFFECT_FIELD_COLOR: return static_cast(ui_element)->get_color(); + case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPlainTextEx(); + case EFFECT_FIELD_BOOL: return static_cast(ui_element)->isChecked(); + case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); + case EFFECT_FIELD_FONT: return static_cast(ui_element)->currentText(); + } + return QVariant(); } double EffectField::frameToTimecode(long frame) { - return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate); + return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate); } long EffectField::timecodeToFrame(double timecode) { - return qRound(timecode * parent_row->parent_effect->parent_clip->sequence->frame_rate); + return qRound(timecode * parent_row->parent_effect->parent_clip->sequence->frame_rate); } void EffectField::set_current_data(const QVariant& data) { - switch (type) { - case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->set_value(data.toDouble(), false); - case EFFECT_FIELD_COLOR: return static_cast(ui_element)->set_color(data.value()); - case EFFECT_FIELD_STRING: return static_cast(ui_element)->setPlainTextEx(data.toString()); - case EFFECT_FIELD_BOOL: return static_cast(ui_element)->setChecked(data.toBool()); - case EFFECT_FIELD_COMBO: return static_cast(ui_element)->setCurrentIndexEx(data.toInt()); - case EFFECT_FIELD_FONT: return static_cast(ui_element)->setCurrentTextEx(data.toString()); - } + switch (type) { + case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->set_value(data.toDouble(), false); + case EFFECT_FIELD_COLOR: return static_cast(ui_element)->set_color(data.value()); + case EFFECT_FIELD_STRING: return static_cast(ui_element)->setPlainTextEx(data.toString()); + case EFFECT_FIELD_BOOL: return static_cast(ui_element)->setChecked(data.toBool()); + case EFFECT_FIELD_COMBO: return static_cast(ui_element)->setCurrentIndexEx(data.toInt()); + case EFFECT_FIELD_FONT: return static_cast(ui_element)->setCurrentTextEx(data.toString()); + } } void EffectField::get_keyframe_data(double timecode, int &before, int &after, double &progress) { - int before_keyframe_index = -1; - int after_keyframe_index = -1; - long before_keyframe_time = LONG_MIN; - long after_keyframe_time = LONG_MAX; - long frame = timecodeToFrame(timecode); + int before_keyframe_index = -1; + int after_keyframe_index = -1; + long before_keyframe_time = LONG_MIN; + long after_keyframe_time = LONG_MAX; + long frame = timecodeToFrame(timecode); - for (int i=0;i before_keyframe_time) { - before_keyframe_index = i; - before_keyframe_time = eval_keyframe_time; - } else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) { - after_keyframe_index = i; - after_keyframe_time = eval_keyframe_time; - } - } + for (int i=0;i before_keyframe_time) { + before_keyframe_index = i; + before_keyframe_time = eval_keyframe_time; + } else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) { + after_keyframe_index = i; + after_keyframe_time = eval_keyframe_time; + } + } - if ((type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) && (before_keyframe_index > -1 && after_keyframe_index > -1)) { - // interpolate - before = before_keyframe_index; - after = after_keyframe_index; - - if (keyframes.at(before).type == KEYFRAME_TYPE_HOLD) { - progress = 0; - } else { - // TODO replace with bezier function - progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time)); - } - } else if (before_keyframe_index > -1) { - before = before_keyframe_index; - after = before_keyframe_index; - } else { - before = after_keyframe_index; - after = after_keyframe_index; - } + if ((type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) && (before_keyframe_index > -1 && after_keyframe_index > -1)) { + // interpolate + before = before_keyframe_index; + after = after_keyframe_index; + progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time)); + } else if (before_keyframe_index > -1) { + before = before_keyframe_index; + after = before_keyframe_index; + } else { + before = after_keyframe_index; + after = after_keyframe_index; + } } bool EffectField::hasKeyframes() { - return (parent_row->isKeyframing() && keyframes.size() > 0); + return (parent_row->isKeyframing() && keyframes.size() > 0); } QVariant EffectField::validate_keyframe_data(double timecode, bool async) { - if (hasKeyframes()) { - int before_keyframe; - int after_keyframe; - double progress; - get_keyframe_data(timecode, before_keyframe, after_keyframe, progress); + if (hasKeyframes()) { + int before_keyframe; + int after_keyframe; + double progress; + get_keyframe_data(timecode, before_keyframe, after_keyframe, progress); - /*int kf_type = (progress < 0.5) ? parent_row->keyframe_types.at(before_keyframe) : parent_row->keyframe_types.at(after_keyframe); - if (kf_type == KEYFRAME_TYPE_BEZIER) { - double x = (8.0 * progress) - 4.0; - progress = 1.0 / (1.0 + qPow(M_E, -x)); - progress *= 1.0373; - progress -= 0.01865; - }*/ + const QVariant& before_data = keyframes.at(before_keyframe).data; + switch (type) { + case EFFECT_FIELD_DOUBLE: + { + double value; + if (before_keyframe == after_keyframe) { + value = keyframes.at(before_keyframe).data.toDouble(); + } else { + const EffectKeyframe& before_key = keyframes.at(before_keyframe); + const EffectKeyframe& after_key = keyframes.at(after_keyframe); - const QVariant& before_data = keyframes.at(before_keyframe).data; - switch (type) { - case EFFECT_FIELD_DOUBLE: - { - double value; - if (before_keyframe == after_keyframe) { - value = keyframes.at(before_keyframe).data.toDouble(); - } else { - double before_dbl = keyframes.at(before_keyframe).data.toDouble(); - double after_dbl = keyframes.at(after_keyframe).data.toDouble(); - value = double_lerp(before_dbl, after_dbl, progress); - } - if (async) { - return value; - } - static_cast(ui_element)->set_value(value, false); - } - break; - case EFFECT_FIELD_COLOR: - { - QColor value; - if (before_keyframe == after_keyframe) { - value = keyframes.at(before_keyframe).data.value(); - } else { - QColor before_data = keyframes.at(before_keyframe).data.value(); - QColor after_data = keyframes.at(after_keyframe).data.value(); - value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress)); - } - if (async) { - return value; - } - static_cast(ui_element)->set_color(value); - } - break; - case EFFECT_FIELD_STRING: - if (async) { - return before_data; - } - static_cast(ui_element)->setPlainTextEx(before_data.toString()); - break; - case EFFECT_FIELD_BOOL: - if (async) { - return before_data; - } - static_cast(ui_element)->setChecked(before_data.toBool()); - break; - case EFFECT_FIELD_COMBO: - if (async) { - return before_data; - } - static_cast(ui_element)->setCurrentIndexEx(before_data.toInt()); - break; - case EFFECT_FIELD_FONT: - if (async) { - return before_data; - } - static_cast(ui_element)->setCurrentTextEx(before_data.toString()); - break; - } - } - return QVariant(); + double before_dbl = before_key.data.toDouble(); + double after_dbl = after_key.data.toDouble(); + + if (before_key.type == KEYFRAME_TYPE_HOLD) { + // hold + value = before_dbl; + } else if (before_key.type == KEYFRAME_TYPE_BEZIER || after_key.type == KEYFRAME_TYPE_BEZIER) { + // bezier interpolation + if (before_key.type == KEYFRAME_TYPE_BEZIER && after_key.type == KEYFRAME_TYPE_BEZIER) { + // cubic bezier + double t = cubic_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+before_key.post_handle_x, after_key.time+after_key.pre_handle_x, after_key.time); + value = cubic_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl+after_key.pre_handle_y, after_dbl, t); + } else if (after_key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier + // last keyframe is the bezier one + double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+before_key.post_handle_x, after_key.time); + value = quad_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl, t); + } else { + // this keyframe is the bezier one + double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, after_key.time+after_key.pre_handle_x, after_key.time); + value = quad_from_t(before_dbl, after_dbl+after_key.pre_handle_y, after_dbl, t); + } + } else { + // linear + value = double_lerp(before_dbl, after_dbl, progress); + } + } + if (async) { + return value; + } + static_cast(ui_element)->set_value(value, false); + } + break; + case EFFECT_FIELD_COLOR: + { + QColor value; + if (before_keyframe == after_keyframe) { + value = keyframes.at(before_keyframe).data.value(); + } else { + QColor before_data = keyframes.at(before_keyframe).data.value(); + QColor after_data = keyframes.at(after_keyframe).data.value(); + value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress)); + } + if (async) { + return value; + } + static_cast(ui_element)->set_color(value); + } + break; + case EFFECT_FIELD_STRING: + if (async) { + return before_data; + } + static_cast(ui_element)->setPlainTextEx(before_data.toString()); + break; + case EFFECT_FIELD_BOOL: + if (async) { + return before_data; + } + static_cast(ui_element)->setChecked(before_data.toBool()); + break; + case EFFECT_FIELD_COMBO: + if (async) { + return before_data; + } + static_cast(ui_element)->setCurrentIndexEx(before_data.toInt()); + break; + case EFFECT_FIELD_FONT: + if (async) { + return before_data; + } + static_cast(ui_element)->setCurrentTextEx(before_data.toString()); + break; + } + } + return QVariant(); } void EffectField::ui_element_change() { - bool dragging_double = (type == EFFECT_FIELD_DOUBLE && static_cast(ui_element)->is_dragging()); - ComboAction* ca = NULL; - if (!dragging_double) ca = new ComboAction(); - make_key_from_change(ca); - if (!dragging_double) undo_stack.push(ca); - emit changed(); + bool dragging_double = (type == EFFECT_FIELD_DOUBLE && static_cast(ui_element)->is_dragging()); + ComboAction* ca = NULL; + if (!dragging_double) ca = new ComboAction(); + make_key_from_change(ca); + if (!dragging_double) undo_stack.push(ca); + emit changed(); } void EffectField::make_key_from_change(ComboAction* ca) { - if (parent_row->isKeyframing()) { - parent_row->set_keyframe_now(ca); - } else if (ca != NULL) { - // set undo - ca->append(new EffectFieldUndo(this)); - } + if (parent_row->isKeyframing()) { + parent_row->set_keyframe_now(ca); + } else if (ca != NULL) { + // set undo + ca->append(new EffectFieldUndo(this)); + } } QWidget* EffectField::get_ui_element() { - return ui_element; + return ui_element; } void EffectField::set_enabled(bool e) { - ui_element->setEnabled(e); + ui_element->setEnabled(e); } double EffectField::get_double_value(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toDouble(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->value(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toDouble(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->value(); } void EffectField::set_double_value(double v) { - static_cast(ui_element)->set_value(v, false); + static_cast(ui_element)->set_value(v, false); } void EffectField::set_double_default_value(double v) { - static_cast(ui_element)->set_default_value(v); + static_cast(ui_element)->set_default_value(v); } void EffectField::set_double_minimum_value(double v) { - static_cast(ui_element)->set_minimum_value(v); + static_cast(ui_element)->set_minimum_value(v); } void EffectField::set_double_maximum_value(double v) { - static_cast(ui_element)->set_maximum_value(v); + static_cast(ui_element)->set_maximum_value(v); } void EffectField::add_combo_item(const QString& name, const QVariant& data) { - static_cast(ui_element)->addItem(name, data); + static_cast(ui_element)->addItem(name, data); } int EffectField::get_combo_index(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toInt(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentIndex(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toInt(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentIndex(); } const QVariant EffectField::get_combo_data(double timecode) { - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentData(); + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentData(); } const QString EffectField::get_combo_string(double timecode) { - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentText(); + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentText(); } void EffectField::set_combo_index(int index) { - static_cast(ui_element)->setCurrentIndexEx(index); + static_cast(ui_element)->setCurrentIndexEx(index); } void EffectField::set_combo_string(const QString& s) { - static_cast(ui_element)->setCurrentTextEx(s); + static_cast(ui_element)->setCurrentTextEx(s); } bool EffectField::get_bool_value(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toBool(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->isChecked(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toBool(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->isChecked(); } void EffectField::set_bool_value(bool b) { - return static_cast(ui_element)->setChecked(b); + return static_cast(ui_element)->setChecked(b); } const QString EffectField::get_string_value(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toString(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->getPlainTextEx(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toString(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->getPlainTextEx(); } void EffectField::set_string_value(const QString& s) { - static_cast(ui_element)->setPlainTextEx(s); + static_cast(ui_element)->setPlainTextEx(s); } const QString EffectField::get_font_name(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toString(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentText(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toString(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentText(); } void EffectField::set_font_name(const QString& s) { - static_cast(ui_element)->setCurrentText(s); + static_cast(ui_element)->setCurrentText(s); } QColor EffectField::get_color_value(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).value(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->get_color(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).value(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->get_color(); } void EffectField::set_color_value(QColor color) { - static_cast(ui_element)->set_color(color); + static_cast(ui_element)->set_color(color); } diff --git a/project/effectfield.h b/project/effectfield.h index f95c896cd..c946c475b 100644 --- a/project/effectfield.h +++ b/project/effectfield.h @@ -12,20 +12,11 @@ #include #include +#include "keyframe.h" + class EffectRow; class ComboAction; -class EffectKeyframe { -public: - long time; - int type; - QVariant data; - - // only for bezier type - QPointF pre_handle; - QPointF post_handle; -}; - class EffectField : public QObject { Q_OBJECT public: diff --git a/project/effectrow.cpp b/project/effectrow.cpp index c980c37f3..a9b7fd0f1 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -17,123 +17,123 @@ #include "ui/clickablelabel.h" EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QString &n, int row) : - parent_effect(parent), - savable(save), - keyframing(false), - ui(uilayout), - name(n), - ui_row(row), - just_made_unsafe_keyframe(false) + parent_effect(parent), + savable(save), + keyframing(false), + ui(uilayout), + name(n), + ui_row(row), + just_made_unsafe_keyframe(false) { - label = new ClickableLabel(name); + label = new ClickableLabel(name + ":"); - ui->addWidget(label, row, 0); + ui->addWidget(label, row, 0); - if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { + if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { connect(label, SIGNAL(clicked()), this, SLOT(focus_row())); - keyframe_nav = new KeyframeNavigator(); - connect(keyframe_nav, SIGNAL(goto_previous_key()), this, SLOT(goto_previous_key())); - connect(keyframe_nav, SIGNAL(toggle_key()), this, SLOT(toggle_key())); - connect(keyframe_nav, SIGNAL(goto_next_key()), this, SLOT(goto_next_key())); + keyframe_nav = new KeyframeNavigator(); + connect(keyframe_nav, SIGNAL(goto_previous_key()), this, SLOT(goto_previous_key())); + connect(keyframe_nav, SIGNAL(toggle_key()), this, SLOT(toggle_key())); + connect(keyframe_nav, SIGNAL(goto_next_key()), this, SLOT(goto_next_key())); connect(keyframe_nav, SIGNAL(keyframe_enabled_changed(bool)), this, SLOT(set_keyframe_enabled(bool))); connect(keyframe_nav, SIGNAL(clicked()), this, SLOT(focus_row())); - ui->addWidget(keyframe_nav, row, 6); - } + ui->addWidget(keyframe_nav, row, 6); + } } bool EffectRow::isKeyframing() { - return keyframing; + return keyframing; } void EffectRow::setKeyframing(bool b) { - if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { - keyframing = b; - keyframe_nav->enable_keyframes(b); - } + if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { + keyframing = b; + keyframe_nav->enable_keyframes(b); + } } void EffectRow::set_keyframe_enabled(bool enabled) { - if (enabled) { - ComboAction* ca = new ComboAction(); - set_keyframe_now(ca); - undo_stack.push(ca); - } else { - if (QMessageBox::question(panel_effect_controls, "Disable Keyframes", "Disabling keyframes will delete all current keyframes. Are you sure you want to do this?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - // clear - ComboAction* ca = new ComboAction(); - for (int i=0;ikeyframes.size();j++) { - ca->append(new KeyframeDelete(f, 0)); - } - } - undo_stack.push(ca); - panel_effect_controls->update_keyframes(); - } else { - setKeyframing(true); - } - } + if (enabled) { + ComboAction* ca = new ComboAction(); + set_keyframe_now(ca); + undo_stack.push(ca); + } else { + if (QMessageBox::question(panel_effect_controls, "Disable Keyframes", "Disabling keyframes will delete all current keyframes. Are you sure you want to do this?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + // clear + ComboAction* ca = new ComboAction(); + for (int i=0;ikeyframes.size();j++) { + ca->append(new KeyframeDelete(f, 0)); + } + } + undo_stack.push(ca); + panel_effect_controls->update_keyframes(); + } else { + setKeyframing(true); + } + } } void EffectRow::goto_previous_key() { - long key = LONG_MIN; - Clip* c = parent_effect->parent_clip; - for (int i=0;ikeyframes.size();j++) { - long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; - if (comp < sequence->playhead) { - key = qMax(comp, key); - } - } - } - if (key != LONG_MIN) panel_sequence_viewer->seek(key); + long key = LONG_MIN; + Clip* c = parent_effect->parent_clip; + for (int i=0;ikeyframes.size();j++) { + long comp = f->keyframes.at(j).time - c->clip_in + c->timeline_in; + if (comp < sequence->playhead) { + key = qMax(comp, key); + } + } + } + if (key != LONG_MIN) panel_sequence_viewer->seek(key); } void EffectRow::toggle_key() { - QVector key_fields; - QVector key_field_index; - Clip* c = parent_effect->parent_clip; - for (int j=0;jkeyframes.size();i++) { - long comp = c->timeline_in - c->clip_in + f->keyframes.at(i).time; - if (comp == sequence->playhead) { - key_fields.append(f); - key_field_index.append(i); - } - } - } + QVector key_fields; + QVector key_field_index; + Clip* c = parent_effect->parent_clip; + for (int j=0;jkeyframes.size();i++) { + long comp = c->timeline_in - c->clip_in + f->keyframes.at(i).time; + if (comp == sequence->playhead) { + key_fields.append(f); + key_field_index.append(i); + } + } + } - ComboAction* ca = new ComboAction(); - if (key_fields.size() == 0) { - // keyframe doesn't exist, set one - set_keyframe_now(ca); - } else { - for (int i=0;iappend(new KeyframeDelete(key_fields.at(i), key_field_index.at(i))); - } - } - undo_stack.push(ca); - panel_effect_controls->update_keyframes(); - panel_sequence_viewer->viewer_widget->update(); + ComboAction* ca = new ComboAction(); + if (key_fields.size() == 0) { + // keyframe doesn't exist, set one + set_keyframe_now(ca); + } else { + for (int i=0;iappend(new KeyframeDelete(key_fields.at(i), key_field_index.at(i))); + } + } + undo_stack.push(ca); + panel_effect_controls->update_keyframes(); + panel_sequence_viewer->viewer_widget->update(); } void EffectRow::goto_next_key() { - long key = LONG_MAX; - Clip* c = parent_effect->parent_clip; - for (int i=0;ikeyframes.size();j++) { - long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; - if (comp > sequence->playhead) { - key = qMin(comp, key); - } - } - } - if (key != LONG_MAX) panel_sequence_viewer->seek(key); + long key = LONG_MAX; + Clip* c = parent_effect->parent_clip; + for (int i=0;ikeyframes.size();j++) { + long comp = f->keyframes.at(j).time - c->clip_in + c->timeline_in; + if (comp > sequence->playhead) { + key = qMin(comp, key); + } + } + } + if (key != LONG_MAX) panel_sequence_viewer->seek(key); } void EffectRow::focus_row() { @@ -141,58 +141,58 @@ void EffectRow::focus_row() { } EffectField* EffectRow::add_field(int type, const QString& id, int colspan) { - EffectField* field = new EffectField(this, type, id); + EffectField* field = new EffectField(this, type, id); if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) connect(field, SIGNAL(clicked()), this, SLOT(focus_row())); - fields.append(field); - QWidget* element = field->get_ui_element(); - ui->addWidget(element, ui_row, fields.size(), 1, colspan); - connect(field, SIGNAL(changed()), parent_effect, SLOT(field_changed())); - return field; + fields.append(field); + QWidget* element = field->get_ui_element(); + ui->addWidget(element, ui_row, fields.size(), 1, colspan); + connect(field, SIGNAL(changed()), parent_effect, SLOT(field_changed())); + return field; } EffectRow::~EffectRow() { - for (int i=0;iplayhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; - for (int j=0;jkeyframes.size();i++) { - if (f->keyframes.at(i).time == time) { - index = i; - break; - } - } - } + int index = -1; + long time = sequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; + for (int j=0;jkeyframes.size();i++) { + if (f->keyframes.at(i).time == time) { + index = i; + break; + } + } + } - KeyframeSet* ks = new KeyframeSet(this, index, time, just_made_unsafe_keyframe); + KeyframeSet* ks = new KeyframeSet(this, index, time, just_made_unsafe_keyframe); - if (ca != NULL) { - just_made_unsafe_keyframe = false; - ca->append(ks); - } else { - if (index == -1) just_made_unsafe_keyframe = true; - ks->redo(); - delete ks; - } + if (ca != NULL) { + just_made_unsafe_keyframe = false; + ca->append(ks); + } else { + if (index == -1) just_made_unsafe_keyframe = true; + ks->redo(); + delete ks; + } - panel_effect_controls->update_keyframes(); + panel_effect_controls->update_keyframes(); } void EffectRow::delete_keyframe_at_time(ComboAction* ca, long time) { - for (int j=0;jkeyframes.size();i++) { - if (f->keyframes.at(i).time == time) { - ca->append(new KeyframeDelete(f, i)); - break; - } - } - } + for (int j=0;jkeyframes.size();i++) { + if (f->keyframes.at(i).time == time) { + ca->append(new KeyframeDelete(f, i)); + break; + } + } + } } const QString &EffectRow::get_name() { @@ -200,9 +200,9 @@ const QString &EffectRow::get_name() { } EffectField* EffectRow::field(int i) { - return fields.at(i); + return fields.at(i); } int EffectRow::fieldCount() { - return fields.size(); + return fields.size(); } diff --git a/project/keyframe.cpp b/project/keyframe.cpp index 8068d5129..3d12220df 100644 --- a/project/keyframe.cpp +++ b/project/keyframe.cpp @@ -1,2 +1,9 @@ #include "keyframe.h" + +EffectKeyframe::EffectKeyframe() { + pre_handle_x = -40; + pre_handle_y = 0; + post_handle_x = 40; + post_handle_y = 0; +} diff --git a/project/keyframe.h b/project/keyframe.h index 7cad20bfc..7974a8a1b 100644 --- a/project/keyframe.h +++ b/project/keyframe.h @@ -1,17 +1,21 @@ #ifndef KEYFRAME_H #define KEYFRAME_H - - #include -#include -class KeyframeData -{ +class EffectKeyframe { public: + EffectKeyframe(); + + long time; + int type; QVariant data; - QPoint handle_pre; - QPoint handle_post; + + // only for bezier type + double pre_handle_x; + double pre_handle_y; + double post_handle_x; + double post_handle_y; }; #endif // KEYFRAME_H diff --git a/project/undo.cpp b/project/undo.cpp index 5187d2a6e..3e2b445d0 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -738,20 +738,20 @@ void MediaRename::redo() { } KeyframeDelete::KeyframeDelete(EffectField *ifield, int iindex) : - field(ifield), - index(iindex), - old_project_changed(mainWindow->isWindowModified()) + field(ifield), + index(iindex), + old_project_changed(mainWindow->isWindowModified()) {} void KeyframeDelete::undo() { - field->keyframes.insert(index, deleted_key); + field->keyframes.insert(index, deleted_key); mainWindow->setWindowModified(old_project_changed); } void KeyframeDelete::redo() { - deleted_key = field->keyframes.at(index); - field->keyframes.removeAt(index); - mainWindow->setWindowModified(true); + deleted_key = field->keyframes.at(index); + field->keyframes.removeAt(index); + mainWindow->setWindowModified(true); } KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : @@ -771,7 +771,7 @@ KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : if (field->type == EFFECT_FIELD_DOUBLE) { old_values[i] = static_cast(field->ui_element)->getPreviousValue(); } else { - old_values[i] = field->keyframes.at(index).data; + old_values[i] = field->keyframes.at(index).data; } } new_values[i] = field->get_current_data(); @@ -781,12 +781,12 @@ KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : void KeyframeSet::undo() { if (enable_keyframes) row->setKeyframing(false); - bool append = (index == -1 || just_made_keyframe); + bool append = (index == -1 || just_made_keyframe); for (int i=0;ifieldCount();i++) { if (append) { - row->field(i)->keyframes.removeLast(); + row->field(i)->keyframes.removeLast(); } else { - row->field(i)->keyframes[index].data = old_values.at(i); + row->field(i)->keyframes[index].data = old_values.at(i); } } @@ -797,15 +797,15 @@ void KeyframeSet::undo() { void KeyframeSet::redo() { bool append = (index == -1 || (just_made_keyframe && !done)); for (int i=0;ifieldCount();i++) { - EffectField* f = row->field(i); + EffectField* f = row->field(i); if (append) { - EffectKeyframe k; - k.data = new_values.at(i); - k.time = time; - k.type = (f->keyframes.size() > 0) ? f->keyframes.last().type : EFFECT_KEYFRAME_LINEAR; - f->keyframes.append(k); + EffectKeyframe k; + k.data = new_values.at(i); + k.time = time; + k.type = (f->keyframes.size() > 0) ? f->keyframes.last().type : EFFECT_KEYFRAME_LINEAR; + f->keyframes.append(k); } else { - f->keyframes[index].data = new_values.at(i); + f->keyframes[index].data = new_values.at(i); } } row->setKeyframing(true); @@ -1229,9 +1229,9 @@ void RippleAction::redo() { ca->redo(); } -SetDouble::SetDouble(double* pointer, double new_value) : +SetDouble::SetDouble(double* pointer, double old_value, double new_value) : p(pointer), - oldval(*pointer), + oldval(old_value), newval(new_value), old_project_changed(mainWindow->isWindowModified()) {} diff --git a/project/undo.h b/project/undo.h index 9ef3a1e3b..fcb44f500 100644 --- a/project/undo.h +++ b/project/undo.h @@ -331,21 +331,21 @@ private: class KeyframeDelete : public QUndoCommand { public: - KeyframeDelete(EffectField* ifield, int iindex); + KeyframeDelete(EffectField* ifield, int iindex); void undo(); void redo(); private: - EffectField* field; - int index; - bool done; - EffectKeyframe deleted_key; - bool old_project_changed; + EffectField* field; + int index; + bool done; + EffectKeyframe deleted_key; + bool old_project_changed; }; class KeyframeSet : public QUndoCommand { public: - KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); + KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); void undo(); void redo(); QVector old_values; @@ -523,7 +523,7 @@ private: class SetDouble : public QUndoCommand { public: - SetDouble(double* pointer, double new_value); + SetDouble(double* pointer, double old_value, double new_value); void undo(); void redo(); private: diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 5accaacad..84a87cebe 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -11,11 +11,17 @@ #include "project/effectfield.h" #include "ui/keyframedrawing.h" #include "project/undo.h" +#include "project/effect.h" #include "debug.h" #define GRAPH_ZOOM_SPEED 0.05 #define GRAPH_SIZE 100 +#define BEZIER_HANDLE_SIZE 3 + +#define BEZIER_HANDLE_NONE 1 +#define BEZIER_HANDLE_PRE 2 +#define BEZIER_HANDLE_POST 3 QColor get_curve_color(int index, int length) { QColor c; @@ -31,7 +37,8 @@ GraphView::GraphView(QWidget* parent) : mousedown(false), zoom(1.0), row(NULL), - moved_keys(false) + moved_keys(false), + current_handle(BEZIER_HANDLE_NONE) { setMouseTracking(true); } @@ -40,7 +47,8 @@ void GraphView::paintEvent(QPaintEvent *event) { QPainter p(this); if (panel_sequence_viewer->seq != NULL) { - // draw lines + // draw grid lines + //int graph_size = GRAPH_SIZE*zoom; bool draw_text = true;//(fontMetrics().height() < graph_size && fontMetrics().width("0000") < graph_size); @@ -75,53 +83,100 @@ void GraphView::paintEvent(QPaintEvent *event) { QPen line_pen; line_pen.setWidth(2); - for (int i=0;ifieldCount();i++) { - EffectField* field = row->field(i); + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE) { - // sort keyframes by time - QVector sorted_keys; - for (int k=0;kkeyframes.size();k++) { - bool inserted = false; - for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { - sorted_keys.insert(j, k); - inserted = true; - break; - } - } - if (!inserted) { - sorted_keys.append(i); - } - } + if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + // sort keyframes by time + QVector sorted_keys; + for (int k=0;kkeyframes.size();k++) { + bool inserted = false; + for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { + sorted_keys.insert(j, k); + inserted = true; + break; + } + } + if (!inserted) { + sorted_keys.append(k); + } + } - int last_key_x, last_key_y; + int last_key_x, last_key_y; + // draw lines for (int j=0;jkeyframes.at(sorted_keys.at(j)); - int key_x = get_screen_x(field->keyframes.at(key_index).time); - int key_y = get_screen_y(field->keyframes.at(key_index).data.toDouble()); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); line_pen.setColor(get_curve_color(i, row->fieldCount())); p.setPen(line_pen); - if (key_index == 0) { + if (j == 0) { p.drawLine(0, key_y, key_x, key_y); } else { - p.drawLine(last_key_x, last_key_y, key_x, key_y); + const EffectKeyframe& last_key = field->keyframes.at(sorted_keys.at(j-1)); + if (last_key.type == KEYFRAME_TYPE_HOLD) { + // hold + p.drawLine(last_key_x, last_key_y, key_x, last_key_y); + p.drawLine(key_x, last_key_y, key_x, key_y); + } else if (last_key.type == KEYFRAME_TYPE_BEZIER || key.type == KEYFRAME_TYPE_BEZIER) { + QPainterPath bezier_path; + bezier_path.moveTo(last_key_x, last_key_y); + if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { + // cubic bezier + bezier_path.cubicTo( + QPointF(last_key_x+last_key.post_handle_x, last_key_y+last_key.post_handle_y), + QPointF(key_x+key.pre_handle_x, key_y+key.pre_handle_y), + QPointF(key_x, key_y) + ); + } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier + // last keyframe is the bezier one + bezier_path.quadTo( + QPointF(last_key_x+last_key.post_handle_x, last_key_y+last_key.post_handle_y), + QPointF(key_x, key_y) + ); + } else { + // this keyframe is the bezier one + bezier_path.quadTo( + QPointF(key_x+key.pre_handle_x, key_y+key.pre_handle_y), + QPointF(key_x, key_y) + ); + } + p.drawPath(bezier_path); + } else { + // linear + p.drawLine(last_key_x, last_key_y, key_x, key_y); + } } last_key_x = key_x; last_key_y = key_y; } + + // draw keys for (int j=0;jkeyframes.at(sorted_keys.at(j)); - int key_x = get_screen_x(field->keyframes.at(key_index).time); - int key_y = get_screen_y(field->keyframes.at(key_index).data.toDouble()); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); - draw_keyframe(p, field->keyframes.at(key_index).type, key_x, key_y, (selected_keys.contains(key_index) && selected_keys_fields.contains(i))); + if (key.type == KEYFRAME_TYPE_BEZIER) { + p.setPen(Qt::gray); + + // pre handle line + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y + key.pre_handle_y*zoom); + p.drawLine(pre_point, QPointF(key_x, key_y)); + p.drawEllipse(pre_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); + + // post handle line + QPointF post_point(key_x + key.post_handle_x*zoom, key_y + key.post_handle_y*zoom); + p.drawLine(post_point, QPointF(key_x, key_y)); + p.drawEllipse(post_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); + } + + draw_keyframe(p, key.type, key_x, key_y, (selected_keys.contains(sorted_keys.at(j)) && selected_keys_fields.contains(i))); } } } @@ -153,13 +208,15 @@ void GraphView::mousePressEvent(QMouseEvent *event) { selected_keys.clear(); selected_keys_fields.clear(); } + current_handle = BEZIER_HANDLE_NONE; if (row != NULL) { for (int i=0;ifieldCount();i++) { EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE) { - for (int j=0;jkeyframes.size();j++) { - int key_x = get_screen_x(field->keyframes.at(j).time); - int key_y = get_screen_y(field->keyframes.at(j).data.toDouble()); + if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + for (int j=0;jkeyframes.size();j++) { + const EffectKeyframe& key = field->keyframes.at(j); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); if (event->pos().x() > key_x-KEYFRAME_SIZE && event->pos().x() < key_x+KEYFRAME_SIZE && event->pos().y() > key_y-KEYFRAME_SIZE @@ -167,6 +224,33 @@ void GraphView::mousePressEvent(QMouseEvent *event) { sel_key = j; sel_key_field = i; break; + } else { + // selecting a handle + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y + key.pre_handle_y*zoom); + if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < pre_point.y()+BEZIER_HANDLE_SIZE) { + sel_key = j; + sel_key_field = i; + old_handle_x = key.pre_handle_x; + old_handle_y = key.pre_handle_y; + current_handle = BEZIER_HANDLE_PRE; + break; + } else { + QPointF post_point(key_x + key.post_handle_x*zoom, key_y + key.post_handle_y*zoom); + if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { + sel_key = j; + sel_key_field = i; + old_handle_x = key.post_handle_x; + old_handle_y = key.post_handle_y; + current_handle = BEZIER_HANDLE_POST; + break; + } + } } } } @@ -179,16 +263,24 @@ void GraphView::mousePressEvent(QMouseEvent *event) { selected_keys_old_vals.clear(); selected_keys_old_doubles.clear(); + + int selected_key_type = -1; + for (int i=0;ifield(selected_keys_fields.at(j))->keyframes.at(selected_keys.at(i)).time); - selected_keys_old_doubles.append(row->field(selected_keys_fields.at(j))->keyframes.at(selected_keys.at(i)).data.toDouble()); + const EffectKeyframe& key = row->field(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i)); + selected_keys_old_vals.append(key.time); + selected_keys_old_doubles.append(key.data.toDouble()); + + if (selected_key_type == -1) { + selected_key_type = key.type; + } else if (selected_key_type != key.type) { + selected_key_type = -2; } } update(); - emit selection_changed(selected_keys.size() > 0); + emit selection_changed(selected_keys.size() > 0, selected_key_type); } void GraphView::mouseMoveEvent(QMouseEvent *event) { @@ -200,39 +292,60 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { start_y = event->pos().y(); update(); } else { - for (int i=0;ifield(selected_keys_fields.at(j))->keyframes[selected_keys.at(i)].time = selected_keys_old_vals.at(index) + (double(event->pos().x() - start_x)/zoom); - row->field(selected_keys_fields.at(j))->keyframes[selected_keys.at(i)].data = selected_keys_old_doubles.at(index) + (double(start_y - event->pos().y())/zoom); + switch (current_handle) { + case BEZIER_HANDLE_NONE: + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].time = qRound(selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom)); + row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = qRound(selected_keys_old_doubles.at(i) + (double(start_y - event->pos().y())/zoom)); } + moved_keys = true; + update_ui(false); + break; + case BEZIER_HANDLE_PRE: + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(event->pos().y() - start_y)/zoom; + moved_keys = true; + update_ui(false); + break; + case BEZIER_HANDLE_POST: + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(event->pos().y() - start_y)/zoom; + moved_keys = true; + update_ui(false); + break; } - moved_keys = true; - update_ui(false); + } } } void GraphView::mouseReleaseEvent(QMouseEvent *event) { if (moved_keys && selected_keys.size() > 0) { - /*ComboAction* ca = new ComboAction(); - QVector rows; - QVector new_vals; - - for (int i=0;ikeyframe_times.at(selected_keys.at(i))); - - for (int j=0;jappend(new SetQVariant(&row->field(selected_keys_fields.at(j))->keyframe_data[selected_keys.at(i)], - selected_keys_old_doubles.at((i*selected_keys_fields.size())+j), - row->field(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)))); + ComboAction* ca = new ComboAction(); + switch (current_handle) { + case BEZIER_HANDLE_NONE: + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)]; + ca->append(new SetLong(&key.time, selected_keys_old_vals.at(i), key.time)); + ca->append(new SetQVariant(&key.data, selected_keys_old_doubles.at(i), key.data)); } - //ca->append(new KeyframeSet(row, selected_keys.at(i), 0, false)); - } - - ca->append(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); - undo_stack.push(ca);*/ + break; + case BEZIER_HANDLE_PRE: + { + EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + ca->append(new SetDouble(&key.pre_handle_x, old_handle_x, key.pre_handle_x)); + ca->append(new SetDouble(&key.pre_handle_y, old_handle_y, key.pre_handle_y)); + } + break; + case BEZIER_HANDLE_POST: + { + EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + ca->append(new SetDouble(&key.post_handle_x, old_handle_x, key.post_handle_x)); + ca->append(new SetDouble(&key.post_handle_y, old_handle_y, key.post_handle_y)); + } + break; + } + undo_stack.push(ca); } moved_keys = false; mousedown = false; @@ -269,19 +382,39 @@ void GraphView::set_row(EffectRow *r) { selected_keys_fields.clear(); selected_keys_old_vals.clear(); selected_keys_old_doubles.clear(); - emit selection_changed(false); + emit selection_changed(false, -1); row = r; + if (row != NULL) { + field_visibility.resize(row->fieldCount()); + field_visibility.fill(true); + } + update(); +} + +void GraphView::set_selected_keyframe_type(int type) { + if (selected_keys.size() > 0) { + ComboAction* ca = new ComboAction(); + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)]; + ca->append(new SetInt(&key.type, type)); + } + undo_stack.push(ca); + update_ui(false); + } +} + +void GraphView::set_field_visibility(int field, bool b) { + field_visibility[field] = b; update(); } void GraphView::set_scroll_x(int s) { - x_scroll = s;//qMax(0, s); - dout << x_scroll; + x_scroll = s; emit x_scroll_changed(x_scroll); } void GraphView::set_scroll_y(int s) { - y_scroll = s;//qMax(0, s); + y_scroll = s; emit y_scroll_changed(y_scroll); } diff --git a/ui/graphview.h b/ui/graphview.h index 97b6e1e2c..d7511184b 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -20,11 +20,14 @@ public: void wheelEvent(QWheelEvent *event); void set_row(EffectRow* r); + + void set_selected_keyframe_type(int type); + void set_field_visibility(int field, bool b); signals: void x_scroll_changed(int); void y_scroll_changed(int); void zoom_changed(double); - void selection_changed(bool); + void selection_changed(bool, int); private: int x_scroll; int y_scroll; @@ -39,13 +42,20 @@ private: int get_screen_x(double); int get_screen_y(double); + QVector field_visibility; + QVector selected_keys; QVector selected_keys_fields; QVector selected_keys_old_vals; QVector selected_keys_old_doubles; + double old_handle_x; + double old_handle_y; + bool moved_keys; + int current_handle; + EffectRow* row; }; diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 7518c4575..4137f68e2 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -44,13 +44,13 @@ KeyframeView::KeyframeView(QWidget *parent) : } void KeyframeView::show_context_menu(const QPoint& pos) { - if (selected_fields.size() > 0) { + if (selected_fields.size() > 0) { QMenu menu(this); QAction* linear = menu.addAction("Linear"); linear->setData(KEYFRAME_TYPE_LINEAR); - QAction* smooth = menu.addAction("Smooth"); - smooth->setData(KEYFRAME_TYPE_BEZIER); + QAction* bezier = menu.addAction("Bezier"); + bezier->setData(KEYFRAME_TYPE_BEZIER); QAction* hold = menu.addAction("Hold"); hold->setData(KEYFRAME_TYPE_HOLD); menu.addSeparator(); @@ -67,12 +67,12 @@ void KeyframeView::menu_set_key_type(QAction* a) { panel_graph_editor->show(); } else { ComboAction* ca = new ComboAction(); - for (int i=0;iappend(new SetInt(&f->keyframes[selected_keyframes.at(i)].type, a->data().toInt())); + for (int i=0;iappend(new SetInt(&f->keyframes[selected_keyframes.at(i)].type, a->data().toInt())); } undo_stack.push(ca); - update_keys(); + update_ui(false); } } @@ -103,19 +103,19 @@ void KeyframeView::paintEvent(QPaintEvent*) { ClickableLabel* label = row->label; QWidget* contents = e->container->contents; - QVector key_times; + QVector key_times; int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height()/* - y_scroll*/; - for (int l=0;lfieldCount();l++) { - EffectField* f = row->field(l); - for (int k=0;kkeyframes.size();k++) { - if (!key_times.contains(f->keyframes.at(k).time)) { - bool keyframe_selected = keyframeIsSelected(f, k); - long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time); - draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); - key_times.append(f->keyframes.at(k).time); - } - } - } + for (int l=0;lfieldCount();l++) { + EffectField* f = row->field(l); + for (int k=0;kkeyframes.size();k++) { + if (!key_times.contains(f->keyframes.at(k).time)) { + bool keyframe_selected = keyframeIsSelected(f, k); + long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time); + draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); + key_times.append(f->keyframes.at(k).time); + } + } + } rows.append(row); rowY.append(keyframe_y); @@ -150,8 +150,8 @@ void KeyframeView::paintEvent(QPaintEvent*) { } bool KeyframeView::keyframeIsSelected(EffectField *field, int keyframe) { - for (int i=0;iappend(new KeyframeDelete(selected_fields.at(i), selected_keyframes.at(i))); + // TODO these need to be sorted + ca->append(new KeyframeDelete(selected_fields.at(i), selected_keyframes.at(i))); del = true; } if (del) { - undo_stack.push(ca); + undo_stack.push(ca); selected_keyframes.clear(); - selected_fields.clear(); + selected_fields.clear(); update_keys(); panel_sequence_viewer->viewer_widget->update(); } else { - delete ca; + delete ca; } } @@ -211,7 +211,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { int mouse_x = event->x() + x_scroll; int mouse_y = event->y(); int row_index = -1; - int field_index = -1; + int field_index = -1; int keyframe_index = -1; long frame_diff = 0; long frame_min = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x-KEYFRAME_SIZE); @@ -223,41 +223,41 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { row->focus_row(); - for (int k=0;kfieldCount();k++) { - EffectField* f = row->field(k); - for (int j=0;jkeyframes.size();j++) { - long eval_keyframe_time = f->keyframes.at(j).time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); - if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { - long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start); - if (keyframe_index == -1 || eval_frame_diff < frame_diff) { - row_index = i; - field_index = k; - keyframe_index = j; - frame_diff = eval_frame_diff; - } - } - } - } + for (int k=0;kfieldCount();k++) { + EffectField* f = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long eval_keyframe_time = f->keyframes.at(j).time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); + if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { + long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start); + if (keyframe_index == -1 || eval_frame_diff < frame_diff) { + row_index = i; + field_index = k; + keyframe_index = j; + frame_diff = eval_frame_diff; + } + } + } + } break; } } bool already_selected = false; keys_selected = false; - if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); + if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); if (!already_selected) { if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_fields.clear(); + selected_fields.clear(); selected_keyframes.clear(); } if (keyframe_index > -1) { - selected_fields.append(rows.at(row_index)->field(field_index)); + selected_fields.append(rows.at(row_index)->field(field_index)); selected_keyframes.append(keyframe_index); } } - if (selected_fields.size() > 0) { - for (int i=0;ikeyframes.at(selected_keyframes.at(i)).time); + if (selected_fields.size() > 0) { + for (int i=0;ikeyframes.at(selected_keyframes.at(i)).time); } keys_selected = true; @@ -291,8 +291,8 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { panel_timeline->snapped = false; if (panel_timeline->snapping) { for (int i=0;iparent_row->parent_effect->parent_clip; + EffectField* field = selected_fields.at(i); + Clip* c = field->parent_row->parent_effect->parent_clip; long key_time = old_key_vals.at(i) + frame_diff - c->clip_in + c->timeline_in; long key_eval = key_time; if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) { @@ -303,11 +303,11 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } // validate frame_diff (make sure no keyframes overlap each other) - for (int i=0;ikeyframes.size();j++) { - while (!keyframeIsSelected(field, j) && field->keyframes.at(j).time == eval_key + frame_diff) { + for (int j=0;jkeyframes.size();j++) { + while (!keyframeIsSelected(field, j) && field->keyframes.at(j).time == eval_key + frame_diff) { if (last_frame_diff > frame_diff) { frame_diff++; panel_timeline->snapped = false; @@ -321,8 +321,8 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { // apply frame_diffs for (int i=0;ikeyframes[selected_keyframes.at(i)].time = old_key_vals.at(i) + frame_diff; + EffectField* field = selected_fields.at(i); + field->keyframes[selected_keyframes.at(i)].time = old_key_vals.at(i) + frame_diff; } last_frame_diff = frame_diff; @@ -346,16 +346,16 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { for (int i=0;i= min_row && rowY.at(i) <= max_row) { EffectRow* row = rows.at(i); - for (int k=0;kfieldCount();k++) { - EffectField* field = row->field(k); - for (int j=0;jkeyframes.size();j++) { - long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); - if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { - selected_fields.append(field); - selected_keyframes.append(j); - } - } - } + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); + if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { + selected_fields.append(field); + selected_keyframes.append(j); + } + } + } } } @@ -368,15 +368,15 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { void KeyframeView::mouseReleaseEvent(QMouseEvent*) { if (dragging) { - ComboAction* ca = new ComboAction(); - for (int i=0;iappend(new SetLong( - &selected_fields.at(i)->keyframes[selected_keyframes.at(i)].time, - old_key_vals.at(i), - selected_fields.at(i)->keyframes.at(selected_keyframes.at(i)).time - )); + ComboAction* ca = new ComboAction(); + for (int i=0;iappend(new SetLong( + &selected_fields.at(i)->keyframes[selected_keyframes.at(i)].time, + old_key_vals.at(i), + selected_fields.at(i)->keyframes.at(selected_keyframes.at(i)).time + )); } - undo_stack.push(ca); + undo_stack.push(ca); } select_rect = false; From e8b2f3129cf0d6fafdc8fcf684187221a2554ca0 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 3 Jan 2019 23:20:41 +1100 Subject: [PATCH 27/65] anchor points now recenter when the media is replaced (#226) --- effects/internal/transformeffect.cpp | 42 ++++++++++++++++++++++++---- effects/internal/transformeffect.h | 3 ++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 0b7466dd5..352e0a6e6 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -119,24 +119,52 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) // set defaults uniform_scale_field->set_bool_value(true); blend_mode_box->set_combo_index(0); + set = false; refresh(); } +void adjust_field(EffectField* field, double old_offset, double new_offset) { + if (field->keyframes.size() > 0) { + for (int i=0;ikeyframes.size();i++) { + field->keyframes[i].data = field->keyframes.at(i).data.toDouble() - old_offset + new_offset; + } + } else { + field->set_current_data(field->get_current_data().toDouble() - old_offset + new_offset); + } +} + void TransformEffect::refresh() { if (parent_clip != NULL && parent_clip->sequence != NULL) { - double default_pos_x = parent_clip->sequence->width/2; - double default_pos_y = parent_clip->sequence->height/2; + double new_default_pos_x = parent_clip->sequence->width/2; + double new_default_pos_y = parent_clip->sequence->height/2; + + /*if (set) { + adjust_field(position_x, default_pos_x, new_default_pos_x); + adjust_field(position_y, default_pos_y, new_default_pos_y); + }*/ + + default_pos_x = new_default_pos_x; + default_pos_y = new_default_pos_y; position_x->set_double_default_value(default_pos_x); position_y->set_double_default_value(default_pos_y); scale_x->set_double_default_value(100); scale_y->set_double_default_value(100); - default_anchor_x = parent_clip->getWidth()/2; - default_anchor_y = parent_clip->getHeight()/2; + int new_default_anchor_x = parent_clip->getWidth()/2; + int new_default_anchor_y = parent_clip->getHeight()/2; - if (default_anchor_x == 0) default_anchor_x = default_pos_x; - if (default_anchor_y == 0) default_anchor_y = default_pos_y; + if (new_default_anchor_x == 0) new_default_anchor_x = default_pos_x; + if (new_default_anchor_y == 0) new_default_anchor_y = default_pos_y; + + // adjust anchors for new size + if (set) { + adjust_field(anchor_x_box, default_anchor_x, new_default_anchor_x); + adjust_field(anchor_y_box, default_anchor_y, new_default_anchor_y); + } + + default_anchor_x = new_default_anchor_x; + default_anchor_y = new_default_anchor_y; anchor_x_box->set_double_default_value(default_anchor_x); anchor_y_box->set_double_default_value(default_anchor_y); @@ -157,6 +185,8 @@ void TransformEffect::refresh() { left_center_gizmo->x_field_multi1 = -x_percent_multipler; right_center_gizmo->x_field_multi1 = x_percent_multipler; rotate_gizmo->x_field_multi1 = x_percent_multipler; + + set = true; } } diff --git a/effects/internal/transformeffect.h b/effects/internal/transformeffect.h index c198ffb0a..c20ff6a6d 100644 --- a/effects/internal/transformeffect.h +++ b/effects/internal/transformeffect.h @@ -39,6 +39,9 @@ private: int default_anchor_x; int default_anchor_y; + double default_pos_x; + double default_pos_y; + bool set; }; #endif // TRANSFORMEFFECT_H From 9e912ef10356b1897b12b575a1750355d28143eb Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 3 Jan 2019 23:26:27 +1100 Subject: [PATCH 28/65] added playback control shortcuts to graph editor --- mainwindow.cpp | 27 +++++++++++++++++++++------ panels/grapheditor.cpp | 6 +++++- panels/grapheditor.h | 1 + ui/graphview.cpp | 1 + 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index e1cb1764e..42afa55d2 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -727,7 +727,7 @@ void MainWindow::setup_menus() { tools_menu->addAction("Preferences", this, SLOT(preferences()), QKeySequence("Ctrl+.")); #ifdef QT_DEBUG - tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack()), QKeySequence("Ctrl+.")); + tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack())); #endif // INITIALIZE HELP MENU @@ -824,7 +824,10 @@ void MainWindow::reset_layout() { } void MainWindow::go_to_start() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->go_to_start(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->go_to_start(); @@ -832,7 +835,10 @@ void MainWindow::go_to_start() { } void MainWindow::prev_frame() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->previous_frame(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->previous_frame(); @@ -840,7 +846,10 @@ void MainWindow::prev_frame() { } void MainWindow::next_frame() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->next_frame(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->next_frame(); @@ -848,7 +857,10 @@ void MainWindow::next_frame() { } void MainWindow::go_to_end() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->go_to_end(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->go_to_end(); @@ -856,7 +868,10 @@ void MainWindow::go_to_end() { } void MainWindow::playpause() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->toggle_play(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->toggle_play(); diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 7dc981fb4..961acac5e 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -193,7 +193,11 @@ void GraphEditor::set_row(EffectRow *r) { current_row_desc->setText(0); } view->set_row(row); - update_panel(); + update_panel(); +} + +bool GraphEditor::view_is_focused() { + return view->hasFocus() || header->hasFocus(); } void GraphEditor::set_key_button_enabled(bool e, int type) { diff --git a/panels/grapheditor.h b/panels/grapheditor.h index 467de9ec3..16c71d23a 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -18,6 +18,7 @@ public: GraphEditor(QWidget* parent = 0); void update_panel(); void set_row(EffectRow* r); + bool view_is_focused(); private: GraphView* view; TimelineHeader* header; diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 84a87cebe..1c7b2e92c 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -41,6 +41,7 @@ GraphView::GraphView(QWidget* parent) : current_handle(BEZIER_HANDLE_NONE) { setMouseTracking(true); + setFocusPolicy(Qt::ClickFocus); } void GraphView::paintEvent(QPaintEvent *event) { From 4ec5ca887066d2b58a5ab707343178ff7827944e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 09:25:53 +1100 Subject: [PATCH 29/65] fixed crash when dragging the left side of a solid #238 --- ui/timelinewidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 2e11b423e..f7ae50e5b 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -1272,9 +1272,9 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { Clip* c = NULL; if (g.clip != -1) c = sequence->clips.at(g.clip); - const FootageStream* ms = NULL; + const FootageStream* ms = NULL; if (g.clip != -1 && c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) { - ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); } // validate ghosts for trimming @@ -1304,7 +1304,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } // prevent clip_in from going below 0 - if (c->media->get_type() == MEDIA_TYPE_SEQUENCE + if ((c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) || (ms != NULL && !ms->infinite_length)) { validator = g.old_clip_in + frame_diff; if (validator < 0) frame_diff -= validator; From b82817132c4767c3e7e643c0a8f3d0c71e8f7d71 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 09:32:30 +1100 Subject: [PATCH 30/65] fixed #239 --- ui/timelinewidget.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index f7ae50e5b..c51530b22 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -1234,15 +1234,19 @@ void validate_transitions(Clip* c, int transition_type, long& frame_diff) { } void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { + int effective_tool = panel_timeline->tool; + if (panel_timeline->importing || panel_timeline->creating) effective_tool = TIMELINE_TOOL_POINTER; + int mouse_track = getTrackFromScreenPoint(mouse_pos.y()); long frame_diff = (lock_frame) ? 0 : panel_timeline->getTimelineFrameFromScreenPoint(mouse_pos.x()) - panel_timeline->drag_frame_start; - int track_diff = ((panel_timeline->tool == TIMELINE_TOOL_SLIDE || panel_timeline->transition_select != TA_NO_TRANSITION) && !panel_timeline->importing) ? 0 : mouse_track - panel_timeline->drag_track_start; + int track_diff = ((effective_tool == TIMELINE_TOOL_SLIDE || panel_timeline->transition_select != TA_NO_TRANSITION) && !panel_timeline->importing) ? 0 : mouse_track - panel_timeline->drag_track_start; long validator; long earliest_in_point = LONG_MAX; // first try to snap long fm; - if (panel_timeline->tool != TIMELINE_TOOL_SLIP) { + + if (effective_tool != TIMELINE_TOOL_SLIP) { // slipping doesn't move the clips so we don't bother snapping for it for (int i=0;ighosts.size();i++) { const Ghost& g = panel_timeline->ghosts.at(i); @@ -1263,7 +1267,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } } - bool clips_are_movable = (panel_timeline->tool == TIMELINE_TOOL_POINTER || panel_timeline->tool == TIMELINE_TOOL_SLIDE || panel_timeline->importing); + bool clips_are_movable = (effective_tool == TIMELINE_TOOL_POINTER || effective_tool == TIMELINE_TOOL_SLIDE); // validate ghosts long temp_frame_diff = frame_diff; // cache to see if we change it (thus cancelling any snap) @@ -1280,8 +1284,8 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { // validate ghosts for trimming if (panel_timeline->creating) { // i feel like we might need something here but we haven't so far? - } else if (panel_timeline->tool == TIMELINE_TOOL_SLIP) { - if (c->media->get_type() == MEDIA_TYPE_SEQUENCE + } else if (effective_tool == TIMELINE_TOOL_SLIP) { + if ((c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) || (ms != NULL && !ms->infinite_length)) { // prevent slip moving a clip below 0 clip_in validator = g.old_clip_in - frame_diff; @@ -1298,7 +1302,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { if (validator < 1) frame_diff -= (1 - validator); // prevent timeline in from going below 0 - if (panel_timeline->tool != TIMELINE_TOOL_RIPPLE) { + if (effective_tool != TIMELINE_TOOL_RIPPLE) { validator = g.old_in + frame_diff; if (validator < 0) frame_diff -= validator; } @@ -1349,7 +1353,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } // ripple ops - if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) { + if (effective_tool == TIMELINE_TOOL_RIPPLE) { for (int j=0;jtool == TIMELINE_TOOL_TRANSITION) { + } else if (effective_tool == TIMELINE_TOOL_TRANSITION) { if (panel_timeline->transition_tool_post_clip == -1) { validate_transitions(c, panel_timeline->transition_tool_type, frame_diff); } else { @@ -1453,7 +1457,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { for (int i=0;ighosts.size();i++) { Ghost& g = panel_timeline->ghosts[i]; - if (panel_timeline->tool == TIMELINE_TOOL_SLIP) { + if (effective_tool == TIMELINE_TOOL_SLIP) { g.clip_in = g.old_clip_in - frame_diff; } else if (g.trimming) { long ghost_diff = frame_diff; @@ -1506,7 +1510,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } else if (same_sign(g.old_track, panel_timeline->drag_track_start)) { g.track += track_diff; } - } else if (panel_timeline->tool == TIMELINE_TOOL_TRANSITION) { + } else if (effective_tool == TIMELINE_TOOL_TRANSITION) { if (panel_timeline->transition_tool_post_clip > -1) { g.in = g.old_in - frame_diff; g.out = g.old_out + frame_diff; @@ -1521,7 +1525,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } // apply changes to selections - if (panel_timeline->tool != TIMELINE_TOOL_SLIP && !panel_timeline->importing && !panel_timeline->creating) { + if (effective_tool != TIMELINE_TOOL_SLIP && !panel_timeline->importing && !panel_timeline->creating) { for (int i=0;iselections.size();i++) { Selection& s = sequence->selections[i]; if (panel_timeline->trim_target > -1) { From dabed8ceec43c874092cab2a8cdad4da2071a394 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 10:40:02 +1100 Subject: [PATCH 31/65] fixed regression with internal shaders and multiple effects paths --- project/effect.cpp | 25 +++++++++++++- project/effect.h | 83 +++++++++++++++++++++++----------------------- 2 files changed, 66 insertions(+), 42 deletions(-) diff --git a/project/effect.cpp b/project/effect.cpp index 40fc2b6b0..fa6c4b878 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -155,12 +155,17 @@ void load_internal_effects() { effects.append(em); } -void load_shader_effects() { +QList get_effects_paths() { QList effects_paths; effects_paths.append(get_app_dir() + "/effects"); effects_paths.append(get_app_dir() + "/../share/olive-editor/effects"); QString env_path(qgetenv("OLIVE_EFFECTS_PATH")); if (!env_path.isEmpty()) effects_paths.append(env_path); + return effects_paths; +} + +void load_shader_effects() { + QList effects_paths = get_effects_paths(); for (int h=0;hpath.isEmpty() || (vertPath.isEmpty() && fragPath.isEmpty())) return; + QList effects_paths = get_effects_paths(); + const QString& test_fn = vertPath.isEmpty() ? fragPath : vertPath; + for (int i=0;iaddShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath); if (!fragPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath); glslProgram->link(); diff --git a/project/effect.h b/project/effect.h index 6f8b35fc1..208ee18a0 100644 --- a/project/effect.h +++ b/project/effect.h @@ -26,13 +26,13 @@ class CheckboxEx; class KeyframeDelete; struct EffectMeta { - QString name; - QString category; - QString filename; + QString name; + QString category; + QString filename; QString path; int internal; int type; - int subtype; + int subtype; }; extern QVector effects; @@ -74,33 +74,33 @@ extern QMutex effects_loaded; #define KEYFRAME_TYPE_HOLD 2 struct GLTextureCoords { - int grid_size; + int grid_size; int vertexTopLeftX; int vertexTopLeftY; - int vertexTopLeftZ; + int vertexTopLeftZ; int vertexTopRightX; int vertexTopRightY; - int vertexTopRightZ; + int vertexTopRightZ; int vertexBottomLeftX; int vertexBottomLeftY; - int vertexBottomLeftZ; + int vertexBottomLeftZ; int vertexBottomRightX; int vertexBottomRightY; - int vertexBottomRightZ; + int vertexBottomRightZ; - float textureTopLeftX; - float textureTopLeftY; - float textureTopLeftQ; - float textureTopRightX; - float textureTopRightY; - float textureTopRightQ; - float textureBottomRightX; - float textureBottomRightY; - float textureBottomRightQ; - float textureBottomLeftX; - float textureBottomLeftY; - float textureBottomLeftQ; + float textureTopLeftX; + float textureTopLeftY; + float textureTopLeftQ; + float textureTopRightX; + float textureTopRightY; + float textureTopRightQ; + float textureBottomRightX; + float textureBottomRightY; + float textureBottomRightQ; + float textureBottomLeftX; + float textureBottomLeftY; + float textureBottomLeftQ; }; qint16 mix_audio_sample(qint16 a, qint16 b); @@ -114,21 +114,21 @@ class Effect : public QObject { public: Effect(Clip* c, const EffectMeta* em); ~Effect(); - Clip* parent_clip; + Clip* parent_clip; const EffectMeta* meta; - int id; + int id; QString name; CollapsibleWidget* container; - EffectRow* add_row(const QString &name, bool savable = true); + EffectRow* add_row(const QString &name, bool savable = true); EffectRow* row(int i); int row_count(); - EffectGizmo* add_gizmo(int type); - EffectGizmo* gizmo(int i); - int gizmo_count(); + EffectGizmo* add_gizmo(int type); + EffectGizmo* gizmo(int i); + int gizmo_count(); - bool is_enabled(); + bool is_enabled(); void set_enabled(bool b); virtual void refresh(); @@ -137,7 +137,7 @@ public: void copy_field_keyframes(Effect *e); void load(QXmlStreamReader& stream); - void save(QXmlStreamWriter& stream); + void save(QXmlStreamWriter& stream); // glsl handling void open(); @@ -147,24 +147,24 @@ public: bool enable_shader; bool enable_coords; - bool enable_superimpose; - bool enable_image; + bool enable_superimpose; + bool enable_image; int getIterations(); void setIterations(int i); const char* ffmpeg_filter; - virtual void process_image(double timecode, uint8_t* data, int size); + virtual void process_image(double timecode, uint8_t* data, int size); virtual void process_shader(double timecode, GLTextureCoords&); - virtual void process_coords(double timecode, GLTextureCoords& coords, int data); + virtual void process_coords(double timecode, GLTextureCoords& coords, int data); virtual GLuint process_superimpose(double timecode); virtual void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); - virtual void gizmo_draw(double timecode, GLTextureCoords& coords); - void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done); - void gizmo_world_to_screen(); - bool are_gizmos_enabled(); + virtual void gizmo_draw(double timecode, GLTextureCoords& coords); + void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done); + void gizmo_world_to_screen(); + bool are_gizmos_enabled(); public slots: void field_changed(); private slots: @@ -182,17 +182,17 @@ protected: QImage img; QOpenGLTexture* texture; - // enable effect to update constantly - bool enable_always_update; + // enable effect to update constantly + bool enable_always_update; private: // superimpose effect QString script; bool isOpen; QVector rows; - QVector gizmos; + QVector gizmos; QGridLayout* ui_layout; - QWidget* ui; + QWidget* ui; bool bound; // superimpose functions @@ -201,6 +201,7 @@ private: QVector cachedValues; void delete_texture(); int get_index_in_clip(); + void validate_meta_path(); }; class EffectInit : public QThread { From 4157e4d432600b0bf7e3de9e381751154303e512 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 11:54:18 +1100 Subject: [PATCH 32/65] potential fix for #240 --- dialogs/exportdialog.cpp | 88 ++++++++++---------- dialogs/speeddialog.cpp | 2 +- mainwindow.cpp | 50 ++++++------ panels/viewer.cpp | 22 ++--- playback/playback.cpp | 171 +++++++++++++++++++-------------------- playback/playback.h | 4 +- project/clip.cpp | 20 ++--- project/effect.cpp | 4 + project/effect.h | 1 + project/undo.cpp | 12 ++- ui/viewerwidget.cpp | 8 +- 11 files changed, 192 insertions(+), 190 deletions(-) diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index 39e788e12..7f4c433e0 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -57,7 +57,7 @@ enum ExportFormats { ExportDialog::ExportDialog(QWidget *parent) : QDialog(parent) { - setWindowTitle("Export \"" + sequence->name + "\""); + setWindowTitle("Export \"" + sequence->name + "\""); setup_ui(); rangeCombobox->setCurrentIndex(0); @@ -479,7 +479,7 @@ void ExportDialog::export_action() { connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished())); connect(et, SIGNAL(progress_changed(int, qint64)), this, SLOT(update_progress_bar(int, qint64))); - closeActiveClips(sequence, true); + closeActiveClips(sequence); mainWindow->autorecover_interval(); @@ -603,40 +603,40 @@ void ExportDialog::setup_ui() { videoGroupbox->setFlat(false); videoGroupbox->setCheckable(true); - QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox); + QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox); - videoGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); - vcodecCombobox = new QComboBox(videoGroupbox); - videoGridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1); + videoGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); + vcodecCombobox = new QComboBox(videoGroupbox); + videoGridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1); - videoGridLayout->addWidget(new QLabel("Width:"), 1, 0, 1, 1); - widthSpinbox = new QSpinBox(videoGroupbox); - widthSpinbox->setMaximum(16777216); - videoGridLayout->addWidget(widthSpinbox, 1, 1, 1, 1); + videoGridLayout->addWidget(new QLabel("Width:"), 1, 0, 1, 1); + widthSpinbox = new QSpinBox(videoGroupbox); + widthSpinbox->setMaximum(16777216); + videoGridLayout->addWidget(widthSpinbox, 1, 1, 1, 1); - videoGridLayout->addWidget(new QLabel("Height:"), 2, 0, 1, 1); - heightSpinbox = new QSpinBox(videoGroupbox); - heightSpinbox->setMaximum(16777216); - videoGridLayout->addWidget(heightSpinbox, 2, 1, 1, 1); + videoGridLayout->addWidget(new QLabel("Height:"), 2, 0, 1, 1); + heightSpinbox = new QSpinBox(videoGroupbox); + heightSpinbox->setMaximum(16777216); + videoGridLayout->addWidget(heightSpinbox, 2, 1, 1, 1); - videoGridLayout->addWidget(new QLabel("Frame Rate:"), 3, 0, 1, 1); - framerateSpinbox = new QDoubleSpinBox(videoGroupbox); - framerateSpinbox->setMaximum(60); - framerateSpinbox->setValue(0); - videoGridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1); + videoGridLayout->addWidget(new QLabel("Frame Rate:"), 3, 0, 1, 1); + framerateSpinbox = new QDoubleSpinBox(videoGroupbox); + framerateSpinbox->setMaximum(60); + framerateSpinbox->setValue(0); + videoGridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1); - videoGridLayout->addWidget(new QLabel("Compression Type:"), 4, 0, 1, 1); + videoGridLayout->addWidget(new QLabel("Compression Type:"), 4, 0, 1, 1); compressionTypeCombobox = new QComboBox(videoGroupbox); compressionTypeCombobox->addItem("Quality-based (Constant Rate Factor)"); compressionTypeCombobox->addItem("File size-based (Two-Pass)"); - videoGridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1); + videoGridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1); - videoBitrateLabel = new QLabel(videoGroupbox); - videoGridLayout->addWidget(videoBitrateLabel, 5, 0, 1, 1); - videobitrateSpinbox = new QDoubleSpinBox(videoGroupbox); - videobitrateSpinbox->setMaximum(100); - videobitrateSpinbox->setValue(2); - videoGridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1); + videoBitrateLabel = new QLabel(videoGroupbox); + videoGridLayout->addWidget(videoBitrateLabel, 5, 0, 1, 1); + videobitrateSpinbox = new QDoubleSpinBox(videoGroupbox); + videobitrateSpinbox->setMaximum(100); + videobitrateSpinbox->setValue(2); + videoGridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1); verticalLayout->addWidget(videoGroupbox); @@ -644,60 +644,60 @@ void ExportDialog::setup_ui() { audioGroupbox->setTitle("Audio"); audioGroupbox->setCheckable(true); - QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox); + QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox); - audioGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); + audioGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); acodecCombobox = new QComboBox(audioGroupbox); - audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1); + audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1); - audioGridLayout->addWidget(new QLabel("Sampling Rate:"), 1, 0, 1, 1); + audioGridLayout->addWidget(new QLabel("Sampling Rate:"), 1, 0, 1, 1); samplingRateSpinbox = new QSpinBox(audioGroupbox); samplingRateSpinbox->setMaximum(96000); samplingRateSpinbox->setValue(0); - audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1); + audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1); - audioGridLayout->addWidget(new QLabel("Bitrate (Kbps/CBR):"), 3, 0, 1, 1); + audioGridLayout->addWidget(new QLabel("Bitrate (Kbps/CBR):"), 3, 0, 1, 1); audiobitrateSpinbox = new QSpinBox(audioGroupbox); audiobitrateSpinbox->setMaximum(320); audiobitrateSpinbox->setValue(256); - audioGridLayout->addWidget(audiobitrateSpinbox, 3, 1, 1, 1); + audioGridLayout->addWidget(audiobitrateSpinbox, 3, 1, 1, 1); verticalLayout->addWidget(audioGroupbox); - QHBoxLayout* progressLayout = new QHBoxLayout(); + QHBoxLayout* progressLayout = new QHBoxLayout(); progressBar = new QProgressBar(this); progressBar->setFormat("%p% (ETA: 0:00:00)"); progressBar->setEnabled(false); progressBar->setValue(0); - progressLayout->addWidget(progressBar); + progressLayout->addWidget(progressBar); renderCancel = new QPushButton(this); renderCancel->setText("x"); renderCancel->setEnabled(false); renderCancel->setMaximumSize(QSize(20, 16777215)); connect(renderCancel, SIGNAL(clicked(bool)), this, SLOT(cancel_render())); - progressLayout->addWidget(renderCancel); + progressLayout->addWidget(renderCancel); - verticalLayout->addLayout(progressLayout); + verticalLayout->addLayout(progressLayout); - QHBoxLayout* buttonLayout = new QHBoxLayout(); - buttonLayout->addStretch(); + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->addStretch(); export_button = new QPushButton(this); export_button->setText("Export"); connect(export_button, SIGNAL(clicked(bool)), this, SLOT(export_action())); - buttonLayout->addWidget(export_button); + buttonLayout->addWidget(export_button); cancel_button = new QPushButton(this); cancel_button->setText("Cancel"); connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject())); - buttonLayout->addWidget(cancel_button); + buttonLayout->addWidget(cancel_button); - buttonLayout->addStretch(); + buttonLayout->addStretch(); - verticalLayout->addLayout(buttonLayout); + verticalLayout->addLayout(buttonLayout); connect(formatCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(format_changed(int))); connect(compressionTypeCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(comp_type_changed(int))); diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index c48baacd3..ff4c52a14 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -335,7 +335,7 @@ void SpeedDialog::accept() { for (int i=0;iopen) close_clip(c); + if (c->open) close_clip(c, true); if (c->track >= 0 && maintain_pitch->checkState() != Qt::PartiallyChecked diff --git a/mainwindow.cpp b/mainwindow.cpp index 42afa55d2..bffdf2169 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -80,13 +80,13 @@ void MainWindow::setup_layout(bool reset) { #endif // load panels from file - if (!reset) { + if (!reset) { QFile panel_config(get_data_path() + "/layout"); if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { restoreState(panel_config.readAll(), 0); panel_config.close(); } - } + } layout()->update(); } @@ -727,7 +727,7 @@ void MainWindow::setup_menus() { tools_menu->addAction("Preferences", this, SLOT(preferences()), QKeySequence("Ctrl+.")); #ifdef QT_DEBUG - tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack())); + tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack())); #endif // INITIALIZE HELP MENU @@ -762,11 +762,11 @@ void MainWindow::updateTitle(const QString& url) { void MainWindow::closeEvent(QCloseEvent *e) { if (can_close_project()) { panel_effect_controls->clear_effects(true); - panel_sequence_viewer->viewer_widget->delete_function(); - panel_footage_viewer->viewer_widget->delete_function(); set_sequence(NULL); + panel_footage_viewer->set_main_sequence(); + QString data_dir = get_data_path(); if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) { if (QFile::exists(autorecovery_filename)) { @@ -824,10 +824,10 @@ void MainWindow::reset_layout() { } void MainWindow::go_to_start() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->go_to_start(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->go_to_start(); @@ -835,10 +835,10 @@ void MainWindow::go_to_start() { } void MainWindow::prev_frame() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->previous_frame(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->previous_frame(); @@ -846,10 +846,10 @@ void MainWindow::prev_frame() { } void MainWindow::next_frame() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->next_frame(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->next_frame(); @@ -857,10 +857,10 @@ void MainWindow::next_frame() { } void MainWindow::go_to_end() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->go_to_end(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->go_to_end(); @@ -868,10 +868,10 @@ void MainWindow::go_to_end() { } void MainWindow::playpause() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->toggle_play(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->toggle_play(); diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 866c940dc..4d5a98d32 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -573,14 +573,14 @@ void Viewer::set_media(Media* m) { seq->frame_rate = 30; if (footage->video_tracks.size() > 0) { - const FootageStream& video_stream = footage->video_tracks.at(0); - seq->width = video_stream.video_width; - seq->height = video_stream.video_height; - if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate; + const FootageStream& video_stream = footage->video_tracks.at(0); + seq->width = video_stream.video_width; + seq->height = video_stream.video_height; + if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate; Clip* c = new Clip(seq); c->media = media; - c->media_stream = video_stream.file_index; + c->media_stream = video_stream.file_index; c->timeline_in = 0; c->timeline_out = footage->get_length_in_frames(seq->frame_rate); if (c->timeline_out <= 0) c->timeline_out = 150; @@ -594,12 +594,12 @@ void Viewer::set_media(Media* m) { } if (footage->audio_tracks.size() > 0) { - const FootageStream& audio_stream = footage->audio_tracks.at(0); - seq->audio_frequency = audio_stream.audio_frequency; + const FootageStream& audio_stream = footage->audio_tracks.at(0); + seq->audio_frequency = audio_stream.audio_frequency; Clip* c = new Clip(seq); c->media = media; - c->media_stream = audio_stream.file_index; + c->media_stream = audio_stream.file_index; c->timeline_in = 0; c->timeline_out = footage->get_length_in_frames(seq->frame_rate); c->track = 0; @@ -610,7 +610,7 @@ void Viewer::set_media(Media* m) { if (footage->video_tracks.size() == 0) { viewer_widget->waveform = true; viewer_widget->waveform_clip = c; - viewer_widget->waveform_ms = &audio_stream; + viewer_widget->waveform_ms = &audio_stream; viewer_widget->update(); } } else { @@ -698,6 +698,10 @@ void Viewer::clean_created_seq() { void Viewer::set_sequence(bool main, Sequence *s) { reset_all_audio(); + if (seq != NULL) { + closeActiveClips(seq); + } + main_sequence = main; seq = (main) ? sequence : s; diff --git a/playback/playback.cpp b/playback/playback.cpp index 9dd4a399d..dfb67461a 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -37,39 +37,39 @@ bool texture_failed = false; bool rendering = false; bool clip_uses_cacher(Clip* clip) { - return (clip->media == NULL && clip->track >= 0) || (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE); + return (clip->media == NULL && clip->track >= 0) || (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE); } void open_clip(Clip* clip, bool multithreaded) { - if (clip_uses_cacher(clip)) { - clip->multithreaded = multithreaded; - if (multithreaded) { - if (clip->open_lock.tryLock()) { - // maybe keep cacher instance in memory while clip exists for performance? - clip->cacher = new Cacher(clip); - QObject::connect(clip->cacher, SIGNAL(finished()), clip->cacher, SLOT(deleteLater())); - clip->cacher->start((clip->track < 0) ? QThread::NormalPriority : QThread::TimeCriticalPriority); - } - } else { - clip->finished_opening = false; - clip->open = true; + if (clip_uses_cacher(clip)) { + clip->multithreaded = multithreaded; + if (multithreaded) { + if (clip->open_lock.tryLock()) { + // maybe keep cacher instance in memory while clip exists for performance? + clip->cacher = new Cacher(clip); + QObject::connect(clip->cacher, SIGNAL(finished()), clip->cacher, SLOT(deleteLater())); + clip->cacher->start((clip->track < 0) ? QThread::NormalPriority : QThread::TimeCriticalPriority); + } + } else { + clip->finished_opening = false; + clip->open = true; - open_clip_worker(clip); - } - } else { - clip->open = true; - } + open_clip_worker(clip); + } + } else { + clip->open = true; + } } -void close_clip(Clip* clip) { +void close_clip(Clip* clip, bool wait) { // destroy opengl texture in main thread - if (clip->texture != NULL) { + if (clip->texture != NULL) { delete clip->texture; clip->texture = NULL; } for (int i=0;ieffects.size();i++) { - clip->effects.at(i)->close(); + if (clip->effects.at(i)->is_open()) clip->effects.at(i)->close(); } if (clip->fbo != NULL) { @@ -79,29 +79,30 @@ void close_clip(Clip* clip) { clip->fbo = NULL; } - if (clip_uses_cacher(clip)) { - if (clip->multithreaded) { - clip->cacher->caching = false; - clip->can_cache.wakeAll(); - } else { - close_clip_worker(clip); - } - } else { - if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_SEQUENCE) - closeActiveClips(clip->media->to_sequence(), false); + if (clip_uses_cacher(clip)) { + if (clip->multithreaded) { + clip->cacher->caching = false; + clip->can_cache.wakeAll(); + if (wait) clip->cacher->wait(); + } else { + close_clip_worker(clip); + } + } else { + if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_SEQUENCE) + closeActiveClips(clip->media->to_sequence()); clip->open = false; - } + } } void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector& nests) { - if (clip_uses_cacher(clip)) { + if (clip_uses_cacher(clip)) { if (clip->multithreaded) { clip->cacher->playhead = playhead; clip->cacher->reset = reset; clip->cacher->nests = nests; - clip->cacher->scrubbing = scrubbing; - if (reset && clip->queue.size() > 0) clip->cacher->interrupt = true; + clip->cacher->scrubbing = scrubbing; + if (reset && clip->queue.size() > 0) clip->cacher->interrupt = true; clip->can_cache.wakeAll(); } else { @@ -111,16 +112,16 @@ void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVectorget_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate); + return ((double)(playhead-c->get_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate); } void get_clip_frame(Clip* c, long playhead) { if (c->finished_opening) { - const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); - int64_t target_pts = qMax(static_cast(0), playhead_to_timestamp(c, playhead)); - int64_t second_pts = qRound64(av_q2d(av_inv_q(c->stream->time_base))); - if (ms->video_interlacing != VIDEO_PROGRESSIVE) { + int64_t target_pts = qMax(static_cast(0), playhead_to_timestamp(c, playhead)); + int64_t second_pts = qRound64(av_q2d(av_inv_q(c->stream->time_base))); + if (ms->video_interlacing != VIDEO_PROGRESSIVE) { target_pts *= 2; second_pts *= 2; } @@ -132,7 +133,7 @@ void get_clip_frame(Clip* c, long playhead) { c->queue_lock.lock(); if (c->queue.size() > 0) { - if (ms->infinite_length) { + if (ms->infinite_length) { target_frame = c->queue.at(0); #ifdef GCF_DEBUG dout << "GCF ==> USE PRECISE (INFINITE)"; @@ -192,14 +193,14 @@ void get_clip_frame(Clip* c, long playhead) { #endif c->reached_end = false; cache = false; - } else if (target_pts != c->last_invalid_ts && (target_pts < target_frame->pts || pts_diff > second_pts)) { + } else if (target_pts != c->last_invalid_ts && (target_pts < target_frame->pts || pts_diff > second_pts)) { #ifdef GCF_DEBUG dout << "GCF ==> RESET" << target_pts << "(" << target_frame->pts << "-" << target_frame->pts+target_frame->pkt_duration << ")"; #endif - if (!config.fast_seeking) target_frame = NULL; + if (!config.fast_seeking) target_frame = NULL; reset = true; - c->last_invalid_ts = target_pts; + c->last_invalid_ts = target_pts; } else { #ifdef GCF_DEBUG dout << "GCF ==> WAIT - target pts:" << target_pts << "closest frame:" << target_frame->pts; @@ -225,26 +226,26 @@ void get_clip_frame(Clip* c, long playhead) { int nb_components = av_pix_fmt_desc_get(static_cast(c->pix_fmt))->nb_components; glPixelStorei(GL_UNPACK_ROW_LENGTH, target_frame->linesize[0]/nb_components); - bool copied = false; - uint8_t* data = target_frame->data[0]; - int frame_size; + bool copied = false; + uint8_t* data = target_frame->data[0]; + int frame_size; - for (int i=0;ieffects.size();i++) { - Effect* e = c->effects.at(i); - if (e->enable_image) { - if (!copied) { - frame_size = target_frame->linesize[0]*target_frame->height; - data = new uint8_t[frame_size]; - memcpy(data, target_frame->data[0], frame_size); - copied = true; - } - e->process_image(get_timecode(c, playhead), data, frame_size); - } - } + for (int i=0;ieffects.size();i++) { + Effect* e = c->effects.at(i); + if (e->enable_image) { + if (!copied) { + frame_size = target_frame->linesize[0]*target_frame->height; + data = new uint8_t[frame_size]; + memcpy(data, target_frame->data[0], frame_size); + copied = true; + } + e->process_image(get_timecode(c, playhead), data, frame_size); + } + } c->texture->setData(0, get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8, data); - if (copied) delete [] data; + if (copied) delete [] data; glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } @@ -252,13 +253,13 @@ void get_clip_frame(Clip* c, long playhead) { c->queue_lock.unlock(); // get more frames - QVector empty; - if (cache) cache_clip(c, playhead, reset, false, empty); + QVector empty; + if (cache) cache_clip(c, playhead, reset, false, empty); } } long playhead_to_clip_frame(Clip* c, long playhead) { - return (qMax(0L, playhead - c->get_timeline_in_with_transition()) + c->get_clip_in_with_transition()); + return (qMax(0L, playhead - c->get_timeline_in_with_transition()) + c->get_clip_in_with_transition()); } double playhead_to_clip_seconds(Clip* c, long playhead) { @@ -278,11 +279,11 @@ int64_t playhead_to_timestamp(Clip* c, long playhead) { int retrieve_next_frame(Clip* c, AVFrame* f) { int result = 0; - int receive_ret; + int receive_ret; // do we need to retrieve a new packet for a new frame? av_frame_unref(f); - while ((receive_ret = avcodec_receive_frame(c->codecCtx, f)) == AVERROR(EAGAIN)) { + while ((receive_ret = avcodec_receive_frame(c->codecCtx, f)) == AVERROR(EAGAIN)) { int read_ret = 0; do { if (c->pkt_written) { @@ -293,15 +294,15 @@ int retrieve_next_frame(Clip* c, AVFrame* f) { if (read_ret >= 0) { c->pkt_written = true; } - } while (read_ret >= 0 && c->pkt->stream_index != c->media_stream); + } while (read_ret >= 0 && c->pkt->stream_index != c->media_stream); if (read_ret >= 0) { int send_ret = avcodec_send_packet(c->codecCtx, c->pkt); if (send_ret < 0) { dout << "[ERROR] Failed to send packet to decoder." << send_ret; - return send_ret; + return send_ret; } - } else { + } else { if (read_ret == AVERROR_EOF) { int send_ret = avcodec_send_packet(c->codecCtx, NULL); if (send_ret < 0) { @@ -323,32 +324,30 @@ int retrieve_next_frame(Clip* c, AVFrame* f) { } bool is_clip_active(Clip* c, long playhead) { - return c->enabled - && c->get_timeline_in_with_transition() < playhead + ceil(c->sequence->frame_rate*2) - && c->get_timeline_out_with_transition() > playhead - && playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition() < c->getMaximumLength(); + return c->enabled + && c->get_timeline_in_with_transition() < playhead + ceil(c->sequence->frame_rate*2) + && c->get_timeline_out_with_transition() > playhead + && playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition() < c->getMaximumLength(); } void set_sequence(Sequence* s) { - closeActiveClips(sequence, true); panel_effect_controls->clear_effects(true); - sequence = s; + sequence = s; panel_sequence_viewer->set_main_sequence(); - panel_timeline->update_sequence(); - panel_timeline->setFocus(); + panel_timeline->update_sequence(); + panel_timeline->setFocus(); } -void closeActiveClips(Sequence *s, bool wait) { +void closeActiveClips(Sequence *s) { if (s != NULL) { - for (int i=0;iclips.size();i++) { - Clip* c = s->clips.at(i); - if (c != NULL) { - if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) { - closeActiveClips(c->media->to_sequence(), wait); - if (c->open) close_clip(c); - } else if (clip_uses_cacher(c) && c->open) { - close_clip(c); - if (c->multithreaded && wait) c->cacher->wait(); + for (int i=0;iclips.size();i++) { + Clip* c = s->clips.at(i); + if (c != NULL) { + if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) { + closeActiveClips(c->media->to_sequence()); + if (c->open) close_clip(c, true); + } else if (c->open) { + close_clip(c, true); } } } diff --git a/playback/playback.h b/playback/playback.h index 67a18c6d4..db4a80600 100644 --- a/playback/playback.h +++ b/playback/playback.h @@ -15,7 +15,7 @@ extern bool rendering; bool clip_uses_cacher(Clip* clip); void open_clip(Clip* clip, bool multithreaded); void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector &nests); -void close_clip(Clip* clip); +void close_clip(Clip* clip, bool wait); void cache_audio_worker(Clip* c, bool write_A); void cache_video_worker(Clip* c, long playhead); void handle_media(Sequence* sequence, long playhead, bool multithreaded); @@ -32,6 +32,6 @@ int retrieve_next_frame(Clip* c, AVFrame* f); bool is_clip_active(Clip* c, long playhead); void get_next_audio(Clip* c, bool mix); void set_sequence(Sequence* s); -void closeActiveClips(Sequence* s, bool wait); +void closeActiveClips(Sequence* s); #endif // PLAYBACK_H diff --git a/project/clip.cpp b/project/clip.cpp index 2253b1ba4..05758928c 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -12,6 +12,7 @@ #include "project/media.h" #include "io/clipboard.h" #include "undo.h" +#include "debug.h" extern "C" { #include @@ -113,9 +114,9 @@ void Clip::refresh() { Footage* m = media->to_footage(); if (track < 0 && m->video_tracks.size() > 0) { - media_stream = m->video_tracks.at(0).file_index; + media_stream = m->video_tracks.at(0).file_index; } else if (track >= 0 && m->audio_tracks.size() > 0) { - media_stream = m->audio_tracks.at(0).file_index; + media_stream = m->audio_tracks.at(0).file_index; } } replaced = false; @@ -171,12 +172,7 @@ Transition* Clip::get_closing_transition() { Clip::~Clip() { if (open) { - close_clip(this); - - // make sure clip has closed before clip is destroyed - if (multithreaded && media != NULL && media->get_type() == MEDIA_TYPE_FOOTAGE) { - cacher->wait(); - } + close_clip(this, true); } if (opening_transition != -1) this->sequence->hard_delete_transition(this, TA_OPENING_TRANSITION); @@ -241,8 +237,8 @@ void Clip::recalculateMaxLength() { case MEDIA_TYPE_FOOTAGE: { Footage* m = media->to_footage(); - const FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream); - if (ms != NULL && ms->infinite_length) { + const FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream); + if (ms != NULL && ms->infinite_length) { calculated_length = LONG_MAX; } else { calculated_length = m->get_length_in_frames(fr); @@ -269,7 +265,7 @@ int Clip::getWidth() { switch (media->get_type()) { case MEDIA_TYPE_FOOTAGE: { - const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); + const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); if (ms != NULL) return ms->video_width; if (sequence != NULL) return sequence->width; } @@ -287,7 +283,7 @@ int Clip::getHeight() { switch (media->get_type()) { case MEDIA_TYPE_FOOTAGE: { - const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); + const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); if (ms != NULL) return ms->video_height; if (sequence != NULL) return sequence->height; } diff --git a/project/effect.cpp b/project/effect.cpp index fa6c4b878..d91019d79 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -749,6 +749,10 @@ void Effect::save(QXmlStreamWriter& stream) { } } +bool Effect::is_open() { + return isOpen; +} + void Effect::validate_meta_path() { if (!meta->path.isEmpty() || (vertPath.isEmpty() && fragPath.isEmpty())) return; QList effects_paths = get_effects_paths(); diff --git a/project/effect.h b/project/effect.h index 208ee18a0..a7210a3de 100644 --- a/project/effect.h +++ b/project/effect.h @@ -140,6 +140,7 @@ public: void save(QXmlStreamWriter& stream); // glsl handling + bool is_open(); void open(); void close(); virtual void startEffect(); diff --git a/project/undo.cpp b/project/undo.cpp index 010561b53..fae0bfc10 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -150,7 +150,7 @@ void DeleteClipAction::redo() { // remove ref to clip ref = seq->clips.at(index); if (ref->open) { - close_clip(ref); + close_clip(ref, true); } seq->clips[index] = NULL; @@ -493,7 +493,7 @@ void AddClipCommand::undo() { Clip* c = seq->clips.last(); panel_timeline->deselect_area(c->timeline_in, c->timeline_out, c->track); undone_clips.prepend(c); - if (c->open) close_clip(c); + if (c->open) close_clip(c, true); seq->clips.removeLast(); } mainWindow->setWindowModified(old_project_changed); @@ -587,8 +587,7 @@ void ReplaceMediaCommand::replace(QString& filename) { for (int j=0;jclips.size();j++) { Clip* c = s->clips.at(j); if (c != NULL && c->media == item && c->open) { - close_clip(c); - if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) c->cacher->wait(); + close_clip(c, true); c->replaced = true; } } @@ -628,8 +627,7 @@ void ReplaceClipMediaCommand::replace(bool undo) { for (int i=0;iopen) { - close_clip(c); - if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) c->cacher->wait(); + close_clip(c, true); } if (undo) { @@ -1102,7 +1100,7 @@ void CloseAllClipsCommand::undo() { } void CloseAllClipsCommand::redo() { - closeActiveClips(sequence, true); + closeActiveClips(sequence); } UpdateFootageTooltip::UpdateFootageTooltip(Media *i) : diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 9a1681dc7..8481f90fa 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -72,7 +72,7 @@ void ViewerWidget::delete_function() { // destroy all textures as well if (viewer->seq != NULL) { makeCurrent(); - closeActiveClips(viewer->seq, true); + closeActiveClips(viewer->seq); doneCurrent(); } } @@ -485,7 +485,7 @@ GLuint ViewerWidget::compose_sequence(QVector& nests, bool render_audio) Footage* m = c->media->to_footage(); if (!m->invalid && !(c->track >= 0 && !is_audio_device_set())) { if (m->ready) { - const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream); + const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream); if (ms != NULL && is_clip_active(c, playhead)) { // if thread is already working, we don't want to touch this, // but we also don't want to hang the UI thread @@ -495,7 +495,7 @@ GLuint ViewerWidget::compose_sequence(QVector& nests, bool render_audio) clip_is_active = true; if (c->track >= 0) audio_track_count++; } else if (c->open) { - close_clip(c); + close_clip(c, false); } } else { //dout << "[WARNING] Media '" + m->name + "' was not ready, retrying..."; @@ -507,7 +507,7 @@ GLuint ViewerWidget::compose_sequence(QVector& nests, bool render_audio) if (!c->open) open_clip(c, !rendering); clip_is_active = true; } else if (c->open) { - close_clip(c); + close_clip(c, false); } } if (clip_is_active) { From 50a64c3bf506f08dfd112f3033e4e6b85d049530 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 12:31:14 +1100 Subject: [PATCH 33/65] corrected bezier handles inverting Y values --- ui/graphview.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 1c7b2e92c..7bbd9b85e 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -41,7 +41,7 @@ GraphView::GraphView(QWidget* parent) : current_handle(BEZIER_HANDLE_NONE) { setMouseTracking(true); - setFocusPolicy(Qt::ClickFocus); + setFocusPolicy(Qt::ClickFocus); } void GraphView::paintEvent(QPaintEvent *event) { @@ -129,20 +129,20 @@ void GraphView::paintEvent(QPaintEvent *event) { if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { // cubic bezier bezier_path.cubicTo( - QPointF(last_key_x+last_key.post_handle_x, last_key_y+last_key.post_handle_y), - QPointF(key_x+key.pre_handle_x, key_y+key.pre_handle_y), + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y+last_key.post_handle_y*zoom), + QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), QPointF(key_x, key_y) ); } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier // last keyframe is the bezier one bezier_path.quadTo( - QPointF(last_key_x+last_key.post_handle_x, last_key_y+last_key.post_handle_y), + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y+last_key.post_handle_y*zoom), QPointF(key_x, key_y) ); } else { // this keyframe is the bezier one bezier_path.quadTo( - QPointF(key_x+key.pre_handle_x, key_y+key.pre_handle_y), + QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), QPointF(key_x, key_y) ); } @@ -167,12 +167,12 @@ void GraphView::paintEvent(QPaintEvent *event) { p.setPen(Qt::gray); // pre handle line - QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y + key.pre_handle_y*zoom); + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); p.drawLine(pre_point, QPointF(key_x, key_y)); p.drawEllipse(pre_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); // post handle line - QPointF post_point(key_x + key.post_handle_x*zoom, key_y + key.post_handle_y*zoom); + QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); p.drawLine(post_point, QPointF(key_x, key_y)); p.drawEllipse(post_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); } @@ -304,13 +304,13 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { break; case BEZIER_HANDLE_PRE: row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(event->pos().y() - start_y)/zoom; + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; moved_keys = true; update_ui(false); break; case BEZIER_HANDLE_POST: row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(event->pos().y() - start_y)/zoom; + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; moved_keys = true; update_ui(false); break; From 6c8ec756d89631236aa6f78dffc680ba14a03c21 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 15:26:31 +1100 Subject: [PATCH 34/65] various graph editor improvements --- panels/grapheditor.cpp | 5 +- project/effectrow.cpp | 4 +- ui/graphview.cpp | 180 ++++++++++++++++++++++++++++------------- ui/graphview.h | 7 ++ ui/keyframeview.cpp | 14 ++++ 5 files changed, 150 insertions(+), 60 deletions(-) diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 961acac5e..b73bf2438 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -97,6 +97,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { QWidget* central_value_widget = new QWidget(); value_layout = new QHBoxLayout(); value_layout->setMargin(0); + value_layout->addWidget(new QLabel("")); // a spacer so the layout doesn't jump central_value_widget->setLayout(value_layout); values->addWidget(central_value_widget); @@ -193,11 +194,11 @@ void GraphEditor::set_row(EffectRow *r) { current_row_desc->setText(0); } view->set_row(row); - update_panel(); + update_panel(); } bool GraphEditor::view_is_focused() { - return view->hasFocus() || header->hasFocus(); + return view->hasFocus() || header->hasFocus(); } void GraphEditor::set_key_button_enabled(bool e, int type) { diff --git a/project/effectrow.cpp b/project/effectrow.cpp index a9b7fd0f1..da203fd74 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -112,13 +112,11 @@ void EffectRow::toggle_key() { set_keyframe_now(ca); } else { for (int i=0;iappend(new KeyframeDelete(key_fields.at(i), key_field_index.at(i))); } } undo_stack.push(ca); - panel_effect_controls->update_keyframes(); - panel_sequence_viewer->viewer_widget->update(); + update_ui(false); } void EffectRow::goto_next_key() { diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 7bbd9b85e..0118a95fd 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include "panels/panels.h" #include "panels/timeline.h" @@ -42,6 +44,60 @@ GraphView::GraphView(QWidget* parent) : { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); +} + +void GraphView::show_context_menu(const QPoint& pos) { + QMenu menu(this); + + QAction* reset_action = menu.addAction("Reset View"); + connect(reset_action, SIGNAL(triggered(bool)), this, SLOT(reset_view())); + + menu.exec(mapToGlobal(pos)); +} + +void GraphView::reset_view() { + zoom = 1.0; + set_scroll_x(0); + set_scroll_y(0); + emit zoom_changed(zoom); + update(); +} + +void GraphView::draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos) { + // draws last line's text + QString str = QString::number(line_no*GRAPH_SIZE); + int text_sz = vert ? fontMetrics().height() : fontMetrics().width(str); + if (text_sz < (next_line_pos - line_pos)) { + QRect text_rect = vert ? QRect(0, line_pos-50, 50, 50) : QRect(line_pos, height()-50, 50, 50); + p.drawText(text_rect, Qt::AlignBottom | Qt::AlignLeft, str); + } +} + +void GraphView::draw_lines(QPainter& p, bool vert) { + int last_line = INT_MIN; + int last_line_x = INT_MIN; + int lim = vert ? height() : width(); + int scroll = vert ? y_scroll : x_scroll; + for (int i=0;iseq != NULL) { // draw grid lines - //int graph_size = GRAPH_SIZE*zoom; - bool draw_text = true;//(fontMetrics().height() < graph_size && fontMetrics().width("0000") < graph_size); - p.setPen(Qt::gray); - int i = 0; - while (true) { - int line_x = (i*GRAPH_SIZE*zoom) - x_scroll; - if (line_x >= width()) { - break; - } - if (line_x >= 0) { - if (line_x > 0) p.drawLine(line_x, 0, line_x, height()); - if (draw_text) p.drawText(QRect(line_x, height()-50, 50, 50), Qt::AlignBottom | Qt::AlignLeft, QString::number(i*GRAPH_SIZE)); - } - i++; - } - i = 0; - while (true) { - int line_y = height() - (i*GRAPH_SIZE*zoom) + y_scroll; - if (line_y <= 0) { - break; - } - if (line_y <= height()) { - if (line_y < height()) p.drawLine(0, line_y, width(), line_y); - if (draw_text) p.drawText(QRect(0, line_y-50, 50, 50), Qt::AlignBottom | Qt::AlignLeft, QString::number(i*GRAPH_SIZE)); - } - i++; - } + + draw_lines(p, true); + draw_lines(p, false); // draw keyframes if (row != NULL) { QPen line_pen; line_pen.setWidth(2); - for (int i=0;ifieldCount();i++) { + for (int i=row->fieldCount()-1;i>=0;i--) { EffectField* field = row->field(i); if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { @@ -129,14 +161,14 @@ void GraphView::paintEvent(QPaintEvent *event) { if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { // cubic bezier bezier_path.cubicTo( - QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y+last_key.post_handle_y*zoom), + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), QPointF(key_x, key_y) ); } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier // last keyframe is the bezier one bezier_path.quadTo( - QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y+last_key.post_handle_y*zoom), + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), QPointF(key_x, key_y) ); } else { @@ -205,10 +237,6 @@ void GraphView::mousePressEvent(QMouseEvent *event) { // selecting int sel_key = -1; int sel_key_field = -1; - if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_keys.clear(); - selected_keys_fields.clear(); - } current_handle = BEZIER_HANDLE_NONE; if (row != NULL) { for (int i=0;ifieldCount();i++) { @@ -227,7 +255,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { break; } else { // selecting a handle - QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y + key.pre_handle_y*zoom); + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE @@ -239,7 +267,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { current_handle = BEZIER_HANDLE_PRE; break; } else { - QPointF post_point(key_x + key.post_handle_x*zoom, key_y + key.post_handle_y*zoom); + QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE @@ -255,11 +283,29 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } } } + if (sel_key > -1) break; } } - if (sel_key > -1) { - selected_keys.append(sel_key); - selected_keys_fields.append(sel_key_field); + bool already_selected = false; + for (int i=0;imodifiers() & Qt::ShiftModifier)) { + selected_keys.removeAt(i); + selected_keys_fields.removeAt(i); + } + already_selected = true; + break; + } + } + if (!already_selected) { + if (!(event->modifiers() & Qt::ShiftModifier)) { + selected_keys.clear(); + selected_keys_fields.clear(); + } + if (sel_key > -1) { + selected_keys.append(sel_key); + selected_keys_fields.append(sel_key_field); + } } selected_keys_old_vals.clear(); @@ -293,24 +339,37 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { start_y = event->pos().y(); update(); } else { + bool shift = (event->modifiers() & Qt::ShiftModifier); switch (current_handle) { case BEZIER_HANDLE_NONE: for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].time = qRound(selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom)); - row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = qRound(selected_keys_old_doubles.at(i) + (double(start_y - event->pos().y())/zoom)); + if (shift) { + row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = selected_keys_old_doubles.at(i); + } else { + row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = qRound(selected_keys_old_doubles.at(i) + (double(start_y - event->pos().y())/zoom)); + } } moved_keys = true; update_ui(false); break; case BEZIER_HANDLE_PRE: row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + if (shift) { + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y; + } else { + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + } moved_keys = true; update_ui(false); break; case BEZIER_HANDLE_POST: row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + if (shift) { + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y; + } else { + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + } moved_keys = true; update_ui(false); break; @@ -366,9 +425,13 @@ void GraphView::wheelEvent(QWheelEvent *event) { // set zoom if (event->angleDelta().y() != 0) { double zoom_diff = (GRAPH_ZOOM_SPEED*zoom); - if (event->angleDelta().y() < 0) zoom_diff = -zoom_diff; - zoom += zoom_diff; - emit zoom_changed(zoom); + double new_zoom = (event->angleDelta().y() < 0) ? zoom - zoom_diff : zoom + zoom_diff; + + // center zoom on screen + set_scroll_x(qRound(x_scroll + double(event->pos().x())*new_zoom - double(event->pos().x())*zoom)); + set_scroll_y(qRound(y_scroll + double(height()-event->pos().y())*new_zoom - double(height()-event->pos().y())*zoom)); + + set_zoom(new_zoom); redraw = true; } } @@ -379,17 +442,19 @@ void GraphView::wheelEvent(QWheelEvent *event) { } void GraphView::set_row(EffectRow *r) { - selected_keys.clear(); - selected_keys_fields.clear(); - selected_keys_old_vals.clear(); - selected_keys_old_doubles.clear(); - emit selection_changed(false, -1); - row = r; - if (row != NULL) { - field_visibility.resize(row->fieldCount()); - field_visibility.fill(true); + if (row != r) { + selected_keys.clear(); + selected_keys_fields.clear(); + selected_keys_old_vals.clear(); + selected_keys_old_doubles.clear(); + emit selection_changed(false, -1); + row = r; + if (row != NULL) { + field_visibility.resize(row->fieldCount()); + field_visibility.fill(true); + } + update(); } - update(); } void GraphView::set_selected_keyframe_type(int type) { @@ -419,6 +484,11 @@ void GraphView::set_scroll_y(int s) { emit y_scroll_changed(y_scroll); } +void GraphView::set_zoom(double z) { + zoom = z; + emit zoom_changed(zoom); +} + int GraphView::get_screen_x(double d) { return (d*zoom) - x_scroll; } diff --git a/ui/graphview.h b/ui/graphview.h index d7511184b..474ffee91 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -38,6 +38,7 @@ private: void set_scroll_x(int s); void set_scroll_y(int s); + void set_zoom(double z); int get_screen_x(double); int get_screen_y(double); @@ -56,7 +57,13 @@ private: int current_handle; + void draw_lines(QPainter &p, bool vert); + void draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos); + EffectRow* row; +private slots: + void show_context_menu(const QPoint& pos); + void reset_view(); }; #endif // GRAPHVIEW_H diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 4137f68e2..6db4164cf 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -252,6 +252,20 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { if (keyframe_index > -1) { selected_fields.append(rows.at(row_index)->field(field_index)); selected_keyframes.append(keyframe_index); + + // find other field with keyframes at the same time + long comp_time = rows.at(row_index)->field(field_index)->keyframes.at(keyframe_index).time; + for (int i=0;ifieldCount();i++) { + if (i != field_index) { + EffectField* f = rows.at(row_index)->field(i); + for (int j=0;jkeyframes.size();j++) { + if (f->keyframes.at(j).time == comp_time) { + selected_fields.append(f); + selected_keyframes.append(j); + } + } + } + } } } From 28fbab2d78782dc2cd02472bb3423d83f19a3018 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 16:33:07 +1100 Subject: [PATCH 35/65] rect select for graph editor --- olive.pro | 6 +- panels/timeline.cpp | 34 ++++----- panels/timeline.h | 1 - ui/graphview.cpp | 165 ++++++++++++++++++++++++++++------------- ui/graphview.h | 9 +++ ui/keyframeview.cpp | 1 + ui/rectangleselect.cpp | 7 ++ ui/rectangleselect.h | 8 ++ ui/timelinewidget.cpp | 5 +- 9 files changed, 161 insertions(+), 75 deletions(-) create mode 100644 ui/rectangleselect.cpp create mode 100644 ui/rectangleselect.h diff --git a/olive.pro b/olive.pro index 66dc5ed66..234d5c5f1 100644 --- a/olive.pro +++ b/olive.pro @@ -110,7 +110,8 @@ SOURCES += \ ui/graphview.cpp \ ui/keyframedrawing.cpp \ ui/clickablelabel.cpp \ - project/keyframe.cpp + project/keyframe.cpp \ + ui/rectangleselect.cpp HEADERS += \ mainwindow.h \ @@ -195,7 +196,8 @@ HEADERS += \ ui/graphview.h \ ui/keyframedrawing.h \ ui/clickablelabel.h \ - project/keyframe.h + project/keyframe.h \ + ui/rectangleselect.h FORMS += diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 37b9dc98e..5482df7d8 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -43,12 +43,6 @@ long refactor_frame_number(long framenumber, double source_frame_rate, double ta return qRound(((double)framenumber/source_frame_rate)*target_frame_rate); } -void draw_selection_rectangle(QPainter& painter, const QRect& rect) { - painter.setPen(QColor(204, 204, 204)); - painter.setBrush(QColor(0, 0, 0, 32)); - painter.drawRect(rect); -} - Timeline::Timeline(QWidget *parent) : QDockWidget(parent), cursor_frame(0), @@ -184,7 +178,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector can_import = m->ready; if (m->using_inout) { double source_fr = 30; - if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate; + if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate; default_clip_in = refactor_frame_number(m->in, source_fr, seq->frame_rate); default_clip_out = refactor_frame_number(m->out, source_fr, seq->frame_rate); } @@ -216,7 +210,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector switch (medium->get_type()) { case MEDIA_TYPE_FOOTAGE: // is video source a still image? - if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) { + if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) { g.out = g.in + 100; } else { long length = m->get_length_in_frames(seq->frame_rate); @@ -227,20 +221,20 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector } for (int j=0;jaudio_tracks.size();j++) { - if (m->audio_tracks.at(j).enabled) { - g.track = j; - g.media_stream = m->audio_tracks.at(j).file_index; - ghosts.append(g); - audio_ghosts = true; - } + if (m->audio_tracks.at(j).enabled) { + g.track = j; + g.media_stream = m->audio_tracks.at(j).file_index; + ghosts.append(g); + audio_ghosts = true; + } } for (int j=0;jvideo_tracks.size();j++) { - if (m->video_tracks.at(j).enabled) { - g.track = -1-j; - g.media_stream = m->video_tracks.at(j).file_index; - ghosts.append(g); - video_ghosts = true; - } + if (m->video_tracks.at(j).enabled) { + g.track = -1-j; + g.media_stream = m->video_tracks.at(j).file_index; + ghosts.append(g); + video_ghosts = true; + } } break; case MEDIA_TYPE_SEQUENCE: diff --git a/panels/timeline.h b/panels/timeline.h index a76adc3a9..278aacbb3 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -38,7 +38,6 @@ struct FootageStream; long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate); int getScreenPointFromFrame(double zoom, long frame); long getFrameFromScreenPoint(double zoom, int x); -void draw_selection_rectangle(QPainter& painter, const QRect& rect); bool selection_contains_transition(const Selection& s, Clip* c, int type); void move_clip(ComboAction *ca, Clip *c, long iin, long iout, long iclip_in, int itrack, bool verify_transitions = true, bool relative = false); void ripple_clips(ComboAction *ca, Sequence* s, long point, long length, const QVector& ignore = QVector()); diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 0118a95fd..14ef7ddae 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -14,12 +14,14 @@ #include "ui/keyframedrawing.h" #include "project/undo.h" #include "project/effect.h" +#include "ui/rectangleselect.h" #include "debug.h" #define GRAPH_ZOOM_SPEED 0.05 #define GRAPH_SIZE 100 #define BEZIER_HANDLE_SIZE 3 +#define BEZIER_LINE_SIZE 2 #define BEZIER_HANDLE_NONE 1 #define BEZIER_HANDLE_PRE 2 @@ -40,7 +42,8 @@ GraphView::GraphView(QWidget* parent) : zoom(1.0), row(NULL), moved_keys(false), - current_handle(BEZIER_HANDLE_NONE) + current_handle(BEZIER_HANDLE_NONE), + rect_select(false) { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); @@ -114,7 +117,7 @@ void GraphView::paintEvent(QPaintEvent *event) { // draw keyframes if (row != NULL) { QPen line_pen; - line_pen.setWidth(2); + line_pen.setWidth(BEZIER_LINE_SIZE); for (int i=row->fieldCount()-1;i>=0;i--) { EffectField* field = row->field(i); @@ -209,7 +212,15 @@ void GraphView::paintEvent(QPaintEvent *event) { p.drawEllipse(post_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); } - draw_keyframe(p, key.type, key_x, key_y, (selected_keys.contains(sorted_keys.at(j)) && selected_keys_fields.contains(i))); + bool selected = false; + for (int k=0;kseq->playhead); p.drawLine(playhead_x, 0, playhead_x, height()); + + if (rect_select) { + draw_selection_rectangle(p, QRect(rect_select_x, rect_select_y, rect_select_w, rect_select_h)); + p.setBrush(Qt::NoBrush); + } } p.setPen(Qt::white); @@ -230,15 +246,16 @@ void GraphView::paintEvent(QPaintEvent *event) { } void GraphView::mousePressEvent(QMouseEvent *event) { - mousedown = true; - start_x = event->pos().x(); - start_y = event->pos().y(); - - // selecting - int sel_key = -1; - int sel_key_field = -1; - current_handle = BEZIER_HANDLE_NONE; if (row != NULL) { + mousedown = true; + start_x = event->pos().x(); + start_y = event->pos().y(); + + // selecting + int sel_key = -1; + int sel_key_field = -1; + current_handle = BEZIER_HANDLE_NONE; + for (int i=0;ifieldCount();i++) { EffectField* field = row->field(i); if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { @@ -285,49 +302,40 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } if (sel_key > -1) break; } - } - bool already_selected = false; - for (int i=0;imodifiers() & Qt::ShiftModifier)) { - selected_keys.removeAt(i); - selected_keys_fields.removeAt(i); - } - already_selected = true; - break; - } - } - if (!already_selected) { - if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_keys.clear(); - selected_keys_fields.clear(); - } + + bool already_selected = false; if (sel_key > -1) { - selected_keys.append(sel_key); - selected_keys_fields.append(sel_key_field); + for (int i=0;imodifiers() & Qt::ShiftModifier)) { + selected_keys.removeAt(i); + selected_keys_fields.removeAt(i); + } + already_selected = true; + break; + } + } } - } - - selected_keys_old_vals.clear(); - selected_keys_old_doubles.clear(); - - int selected_key_type = -1; - - for (int i=0;ifield(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i)); - selected_keys_old_vals.append(key.time); - selected_keys_old_doubles.append(key.data.toDouble()); - - if (selected_key_type == -1) { - selected_key_type = key.type; - } else if (selected_key_type != key.type) { - selected_key_type = -2; + if (!already_selected) { + if (!(event->modifiers() & Qt::ShiftModifier)) { + selected_keys.clear(); + selected_keys_fields.clear(); + } + if (sel_key > -1) { + selected_keys.append(sel_key); + selected_keys_fields.append(sel_key_field); + } else { + rect_select = true; + rect_select_x = event->pos().x(); + rect_select_y = event->pos().y(); + rect_select_w = 0; + rect_select_h = 0; + rect_select_offset = selected_keys.size(); + } } + + selection_update(); } - - update(); - - emit selection_changed(selected_keys.size() > 0, selected_key_type); } void GraphView::mouseMoveEvent(QMouseEvent *event) { @@ -338,6 +346,35 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { start_x = event->pos().x(); start_y = event->pos().y(); update(); + } else if (rect_select) { + rect_select_w = event->pos().x() - rect_select_x; + rect_select_h = event->pos().y() - rect_select_y; + + selected_keys.resize(rect_select_offset); + selected_keys_fields.resize(rect_select_offset); + + for (int i=0;ifieldCount();i++) { + EffectField* f = row->field(i); + for (int j=0;jkeyframes.size();j++) { + bool already_selected = false; + for (int k=0;kkeyframes.at(j).time), get_screen_y(f->keyframes.at(j).data.toDouble())); + QRect select_rect(rect_select_x, rect_select_y, rect_select_w, rect_select_h); + if (select_rect.contains(key_screen_point)) { + selected_keys.append(j); + selected_keys_fields.append(i); + } + } + } + } + update(); } else { bool shift = (event->modifiers() & Qt::ShiftModifier); switch (current_handle) { @@ -409,6 +446,11 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { } moved_keys = false; mousedown = false; + if (rect_select) { + rect_select = false; + selection_update(); + update(); + } } void GraphView::wheelEvent(QWheelEvent *event) { @@ -496,3 +538,26 @@ int GraphView::get_screen_x(double d) { int GraphView::get_screen_y(double d) { return height() + y_scroll - d*zoom; } + +void GraphView::selection_update() { + selected_keys_old_vals.clear(); + selected_keys_old_doubles.clear(); + + int selected_key_type = -1; + + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i)); + selected_keys_old_vals.append(key.time); + selected_keys_old_doubles.append(key.data.toDouble()); + + if (selected_key_type == -1) { + selected_key_type = key.type; + } else if (selected_key_type != key.type) { + selected_key_type = -2; + } + } + + update(); + + emit selection_changed(selected_keys.size() > 0, selected_key_type); +} diff --git a/ui/graphview.h b/ui/graphview.h index 474ffee91..6a694a999 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -43,6 +43,8 @@ private: int get_screen_x(double); int get_screen_y(double); + void selection_update(); + QVector field_visibility; QVector selected_keys; @@ -61,6 +63,13 @@ private: void draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos); EffectRow* row; + + bool rect_select; + int rect_select_x; + int rect_select_y; + int rect_select_w; + int rect_select_h; + int rect_select_offset; private slots: void show_context_menu(const QPoint& pos); void reset_view(); diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 6db4164cf..bbe8d7dac 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -15,6 +15,7 @@ #include "ui/keyframedrawing.h" #include "ui/clickablelabel.h" #include "ui/resizablescrollbar.h" +#include "ui/rectangleselect.h" #include #include diff --git a/ui/rectangleselect.cpp b/ui/rectangleselect.cpp new file mode 100644 index 000000000..b4a9348ce --- /dev/null +++ b/ui/rectangleselect.cpp @@ -0,0 +1,7 @@ +#include "rectangleselect.h" + +void draw_selection_rectangle(QPainter& painter, const QRect& rect) { + painter.setPen(QColor(204, 204, 204)); + painter.setBrush(QColor(0, 0, 0, 32)); + painter.drawRect(rect); +} diff --git a/ui/rectangleselect.h b/ui/rectangleselect.h new file mode 100644 index 000000000..40eee2aeb --- /dev/null +++ b/ui/rectangleselect.h @@ -0,0 +1,8 @@ +#ifndef RECTANGLESELECT_H +#define RECTANGLESELECT_H + +#include + +void draw_selection_rectangle(QPainter& painter, const QRect& rect); + +#endif // RECTANGLESELECT_H diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 2e11b423e..71cdca314 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -21,6 +21,7 @@ #include "ui/resizablescrollbar.h" #include "dialogs/newsequencedialog.h" #include "mainwindow.h" +#include "ui/rectangleselect.h" #include "debug.h" #include "project/effect.h" @@ -1272,9 +1273,9 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { Clip* c = NULL; if (g.clip != -1) c = sequence->clips.at(g.clip); - const FootageStream* ms = NULL; + const FootageStream* ms = NULL; if (g.clip != -1 && c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) { - ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); } // validate ghosts for trimming From f444cdb8bbe21876f1dcde87aa2626f99db0ff31 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 18:36:47 +1100 Subject: [PATCH 36/65] correct frame offsets in graph view --- panels/grapheditor.cpp | 1 + ui/graphview.cpp | 8 ++++++-- ui/graphview.h | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index b73bf2438..c6e8500f6 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -185,6 +185,7 @@ void GraphEditor::set_row(EffectRow *r) { if (found_vals) { row = r; current_row_desc->setText(row->parent_effect->parent_clip->name + " :: " + row->parent_effect->meta->name + " :: " + row->get_name()); + header->set_visible_in(r->parent_effect->parent_clip->timeline_in); connect(keyframe_nav, SIGNAL(goto_previous_key()), row, SLOT(goto_previous_key())); connect(keyframe_nav, SIGNAL(toggle_key()), row, SLOT(toggle_key())); diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 14ef7ddae..ec535fc10 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -14,6 +14,7 @@ #include "ui/keyframedrawing.h" #include "project/undo.h" #include "project/effect.h" +#include "project/clip.h" #include "ui/rectangleselect.h" #include "debug.h" @@ -43,7 +44,8 @@ GraphView::GraphView(QWidget* parent) : row(NULL), moved_keys(false), current_handle(BEZIER_HANDLE_NONE), - rect_select(false) + rect_select(false), + visible_in(0) { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); @@ -228,7 +230,7 @@ void GraphView::paintEvent(QPaintEvent *event) { // draw playhead p.setPen(Qt::red); - int playhead_x = get_screen_x(panel_sequence_viewer->seq->playhead); + int playhead_x = get_screen_x(panel_sequence_viewer->seq->playhead - visible_in); p.drawLine(playhead_x, 0, playhead_x, height()); if (rect_select) { @@ -339,6 +341,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } void GraphView::mouseMoveEvent(QMouseEvent *event) { + unsetCursor(); if (mousedown) { if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { set_scroll_x(x_scroll + start_x - event->pos().x()); @@ -494,6 +497,7 @@ void GraphView::set_row(EffectRow *r) { if (row != NULL) { field_visibility.resize(row->fieldCount()); field_visibility.fill(true); + visible_in = row->parent_effect->parent_clip->timeline_in; } update(); } diff --git a/ui/graphview.h b/ui/graphview.h index 6a694a999..5d1246269 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -70,6 +70,8 @@ private: int rect_select_w; int rect_select_h; int rect_select_offset; + + long visible_in; private slots: void show_context_menu(const QPoint& pos); void reset_view(); From fda1ba64daa6aadcbc9e176c3a9221abbf9a7318 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 18:55:14 +1100 Subject: [PATCH 37/65] fix #242 --- main.cpp | 5 +++-- mainwindow.cpp | 31 ++++++++++++++++++++++++------- mainwindow.h | 8 ++++++++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/main.cpp b/main.cpp index 4286a3bd0..8dd018067 100644 --- a/main.cpp +++ b/main.cpp @@ -9,10 +9,11 @@ extern "C" { int main(int argc, char *argv[]) { // init ffmpeg subsystem av_register_all(); - avfilter_register_all(); + avfilter_register_all(); - QApplication a(argc, argv); + QApplication a(argc, argv); MainWindow w; + if (argc > 1) w.launch_with_project(argv[1]); w.showMaximized(); return a.exec(); diff --git a/mainwindow.cpp b/mainwindow.cpp index f84212d13..2d1f6b7b1 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -96,6 +96,8 @@ void MainWindow::setup_layout(bool reset) { MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { + enable_launch_with_project = false; + setup_debug(); mainWindow = this; @@ -211,8 +213,7 @@ MainWindow::MainWindow(QWidget *parent) : autorecovery_filename = data_dir + "/autorecovery.ove"; if (QFile::exists(autorecovery_filename)) { if (QMessageBox::question(NULL, "Auto-recovery", "Olive didn't close properly and an autorecovery file was detected. Would you like to open it?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - updateTitle(autorecovery_filename); - panel_project->load_project(true); + open_project_worker(autorecovery_filename, true); } } autorecovery_timer.setInterval(60000); @@ -268,6 +269,11 @@ MainWindow::~MainWindow() { close_debug(); } +void MainWindow::launch_with_project(const char* s) { + project_url = s; + enable_launch_with_project = true; +} + void MainWindow::make_new_menu(QMenu *parent) { parent->addAction("&Project", this, SLOT(new_project()), QKeySequence("Ctrl+N")); parent->addSeparator(); @@ -810,6 +816,10 @@ void MainWindow::closeEvent(QCloseEvent *e) { void MainWindow::paintEvent(QPaintEvent *event) { QMainWindow::paintEvent(event); + if (enable_launch_with_project) { + QTimer::singleShot(10, this, SLOT(load_with_launch())); + enable_launch_with_project = false; + } #ifndef QT_DEBUG if (!demoNoticeShown) { DemoNotice* d = new DemoNotice(this); @@ -826,12 +836,20 @@ void MainWindow::clear_undo_stack() { void MainWindow::open_project() { QString fn = QFileDialog::getOpenFileName(this, "Open Project...", "", OLIVE_FILE_FILTER); if (!fn.isEmpty() && can_close_project()) { - updateTitle(fn); - panel_project->load_project(false); - undo_stack.clear(); + open_project_worker(fn, false); } } +void MainWindow::open_project_worker(const QString& fn, bool autorecovery) { + updateTitle(fn); + panel_project->load_project(autorecovery); + undo_stack.clear(); +} + +void MainWindow::load_with_launch() { + open_project_worker(project_url, false); +} + void MainWindow::reset_layout() { setup_layout(true); } @@ -1012,8 +1030,7 @@ void MainWindow::load_recent_project() { panel_project->save_recent_projects(); } } else if (can_close_project()) { - updateTitle(recent_url); - panel_project->load_project(false); + open_project_worker(recent_url, false); } } diff --git a/mainwindow.h b/mainwindow.h index 043b083b3..fc9f32e88 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -15,6 +15,8 @@ public: void updateTitle(const QString &url); ~MainWindow(); + void launch_with_project(const char *s); + void make_new_menu(QMenu* parent); void make_inout_menu(QMenu* parent); @@ -111,6 +113,10 @@ private slots: void toggle_panel_visibility(); void set_timecode_view(); + void open_project_worker(const QString &fn, bool autorecovery); + + void load_with_launch(); + private: void setup_layout(bool reset); bool can_close_project(); @@ -169,6 +175,8 @@ private: void set_bool_action_checked(QAction* a); void set_int_action_checked(QAction* a, const int& i); void set_button_action_checked(QAction* a); + + bool enable_launch_with_project; }; extern MainWindow* mainWindow; From cdd01db9abd651bb09ec89e1bc8f6258d8321762 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 19:01:49 +1100 Subject: [PATCH 38/65] added #244 --- io/config.cpp | 247 ++++++++++++++++++++++---------------------- io/config.h | 47 ++++----- mainwindow.cpp | 5 + mainwindow.h | 1 + panels/timeline.cpp | 49 +++++---- 5 files changed, 185 insertions(+), 164 deletions(-) diff --git a/io/config.cpp b/io/config.cpp index 2ecb744ba..0e1c14028 100644 --- a/io/config.cpp +++ b/io/config.cpp @@ -9,88 +9,89 @@ Config config; Config::Config() - : saved_layout(false), + : saved_layout(false), show_track_lines(true), - scroll_zooms(false), - edit_tool_selects_links(false), - edit_tool_also_seeks(false), - select_also_seeks(false), + scroll_zooms(false), + edit_tool_selects_links(false), + edit_tool_also_seeks(false), + select_also_seeks(false), paste_seeks(true), - img_seq_formats("jpg|jpeg|bmp|tiff|tif|psd|png|tga|jp2|gif"), + img_seq_formats("jpg|jpeg|bmp|tiff|tif|psd|png|tga|jp2|gif"), rectified_waveforms(false), - default_transition_length(30), - timecode_view(TIMECODE_DROP), - show_title_safe_area(false), - use_custom_title_safe_ratio(false), + default_transition_length(30), + timecode_view(TIMECODE_DROP), + show_title_safe_area(false), + use_custom_title_safe_ratio(false), custom_title_safe_ratio(1), enable_drag_files_to_timeline(true), autoscale_by_default(false), recording_mode(2), - enable_seek_to_import(false), - enable_audio_scrubbing(true), - drop_on_media_to_replace(true), - autoscroll(AUTOSCROLL_PAGE_SCROLL), - audio_rate(48000), - fast_seeking(false), - hover_focus(false), - project_view_type(PROJECT_VIEW_TREE) + enable_seek_to_import(false), + enable_audio_scrubbing(true), + drop_on_media_to_replace(true), + autoscroll(AUTOSCROLL_PAGE_SCROLL), + audio_rate(48000), + fast_seeking(false), + hover_focus(false), + project_view_type(PROJECT_VIEW_TREE), + set_name_with_marker(true) {} void Config::load(QString path) { - QFile f(path); - if (f.exists() && f.open(QIODevice::ReadOnly)) { - QXmlStreamReader stream(&f); + QFile f(path); + if (f.exists() && f.open(QIODevice::ReadOnly)) { + QXmlStreamReader stream(&f); - while (!stream.atEnd()) { - stream.readNext(); - if (stream.isStartElement()) { - if (stream.name() == "SavedLayout") { - stream.readNext(); - saved_layout = (stream.text() == "1"); - } else if (stream.name() == "ShowTrackLines") { - stream.readNext(); - show_track_lines = (stream.text() == "1"); - } else if (stream.name() == "ScrollZooms") { - stream.readNext(); - scroll_zooms = (stream.text() == "1"); - } else if (stream.name() == "EditToolSelectsLinks") { - stream.readNext(); - edit_tool_selects_links = (stream.text() == "1"); - } else if (stream.name() == "EditToolAlsoSeeks") { - stream.readNext(); - edit_tool_also_seeks = (stream.text() == "1"); - } else if (stream.name() == "SelectAlsoSeeks") { - stream.readNext(); - select_also_seeks = (stream.text() == "1"); - } else if (stream.name() == "PasteSeeks") { - stream.readNext(); - paste_seeks = (stream.text() == "1"); - } else if (stream.name() == "ImageSequenceFormats") { - stream.readNext(); - img_seq_formats = stream.text().toString(); + while (!stream.atEnd()) { + stream.readNext(); + if (stream.isStartElement()) { + if (stream.name() == "SavedLayout") { + stream.readNext(); + saved_layout = (stream.text() == "1"); + } else if (stream.name() == "ShowTrackLines") { + stream.readNext(); + show_track_lines = (stream.text() == "1"); + } else if (stream.name() == "ScrollZooms") { + stream.readNext(); + scroll_zooms = (stream.text() == "1"); + } else if (stream.name() == "EditToolSelectsLinks") { + stream.readNext(); + edit_tool_selects_links = (stream.text() == "1"); + } else if (stream.name() == "EditToolAlsoSeeks") { + stream.readNext(); + edit_tool_also_seeks = (stream.text() == "1"); + } else if (stream.name() == "SelectAlsoSeeks") { + stream.readNext(); + select_also_seeks = (stream.text() == "1"); + } else if (stream.name() == "PasteSeeks") { + stream.readNext(); + paste_seeks = (stream.text() == "1"); + } else if (stream.name() == "ImageSequenceFormats") { + stream.readNext(); + img_seq_formats = stream.text().toString(); } else if (stream.name() == "RectifiedWaveforms") { - stream.readNext(); + stream.readNext(); rectified_waveforms = (stream.text() == "1"); } else if (stream.name() == "DefaultTransitionLength") { - stream.readNext(); - default_transition_length = stream.text().toInt(); - } else if (stream.name() == "TimecodeView") { - stream.readNext(); - timecode_view = stream.text().toInt(); - }else if (stream.name() == "ShowTitleSafeArea") { - stream.readNext(); - show_title_safe_area = (stream.text() == "1"); - } else if (stream.name() == "UseCustomTitleSafeRatio") { - stream.readNext(); - use_custom_title_safe_ratio = (stream.text() == "1"); - } else if (stream.name() == "CustomTitleSafeRatio") { - stream.readNext(); - custom_title_safe_ratio = stream.text().toDouble(); + stream.readNext(); + default_transition_length = stream.text().toInt(); + } else if (stream.name() == "TimecodeView") { + stream.readNext(); + timecode_view = stream.text().toInt(); + } else if (stream.name() == "ShowTitleSafeArea") { + stream.readNext(); + show_title_safe_area = (stream.text() == "1"); + } else if (stream.name() == "UseCustomTitleSafeRatio") { + stream.readNext(); + use_custom_title_safe_ratio = (stream.text() == "1"); + } else if (stream.name() == "CustomTitleSafeRatio") { + stream.readNext(); + custom_title_safe_ratio = stream.text().toDouble(); } else if (stream.name() == "EnableDragFilesToTimeline") { stream.readNext(); enable_drag_files_to_timeline = (stream.text() == "1");; - } else if (stream.name() == "AutoscaleByDefault") { - stream.readNext(); + } else if (stream.name() == "AutoscaleByDefault") { + stream.readNext(); autoscale_by_default = (stream.text() == "1"); } else if (stream.name() == "RecordingMode") { stream.readNext(); @@ -98,78 +99,82 @@ void Config::load(QString path) { } else if (stream.name() == "EnableSeekToImport") { stream.readNext(); enable_seek_to_import = (stream.text() == "1"); - } else if (stream.name() == "AudioScrubbing") { - stream.readNext(); - enable_audio_scrubbing = (stream.text() == "1"); - } else if (stream.name() == "DropFileOnMediaToReplace") { - stream.readNext(); - drop_on_media_to_replace = (stream.text() == "1"); - } else if (stream.name() == "Autoscroll") { - stream.readNext(); - autoscroll = stream.text().toInt(); - } else if (stream.name() == "AudioRate") { - stream.readNext(); - audio_rate = stream.text().toInt(); - } else if (stream.name() == "FastSeeking") { - stream.readNext(); - fast_seeking = (stream.text() == "1"); - } else if (stream.name() == "HoverFocus") { - stream.readNext(); - hover_focus = (stream.text() == "1"); - } else if (stream.name() == "ProjectViewType") { - stream.readNext(); - project_view_type = stream.text().toInt(); - } - } - } - if (stream.hasError()) { + } else if (stream.name() == "AudioScrubbing") { + stream.readNext(); + enable_audio_scrubbing = (stream.text() == "1"); + } else if (stream.name() == "DropFileOnMediaToReplace") { + stream.readNext(); + drop_on_media_to_replace = (stream.text() == "1"); + } else if (stream.name() == "Autoscroll") { + stream.readNext(); + autoscroll = stream.text().toInt(); + } else if (stream.name() == "AudioRate") { + stream.readNext(); + audio_rate = stream.text().toInt(); + } else if (stream.name() == "FastSeeking") { + stream.readNext(); + fast_seeking = (stream.text() == "1"); + } else if (stream.name() == "HoverFocus") { + stream.readNext(); + hover_focus = (stream.text() == "1"); + } else if (stream.name() == "ProjectViewType") { + stream.readNext(); + project_view_type = stream.text().toInt(); + } else if (stream.name() == "SetNameWithMarker") { + stream.readNext(); + set_name_with_marker = (stream.text() == "1"); + } + } + } + if (stream.hasError()) { dout << "[ERROR] Error parsing config XML." << stream.errorString(); - } + } - f.close(); + f.close(); } } void Config::save(QString path) { - QFile f(path); - if (!f.open(QIODevice::WriteOnly)) { + QFile f(path); + if (!f.open(QIODevice::WriteOnly)) { dout << "[ERROR] Could not save configuration"; - return; - } + return; + } - QXmlStreamWriter stream(&f); - stream.setAutoFormatting(true); - stream.writeStartDocument(); // doc - stream.writeStartElement("Configuration"); // configuration + QXmlStreamWriter stream(&f); + stream.setAutoFormatting(true); + stream.writeStartDocument(); // doc + stream.writeStartElement("Configuration"); // configuration stream.writeTextElement("Version", QString::number(SAVE_VERSION)); - stream.writeTextElement("SavedLayout", QString::number(saved_layout)); - stream.writeTextElement("ShowTrackLines", QString::number(show_track_lines)); - stream.writeTextElement("ScrollZooms", QString::number(scroll_zooms)); - stream.writeTextElement("EditToolSelectsLinks", QString::number(edit_tool_selects_links)); - stream.writeTextElement("EditToolAlsoSeeks", QString::number(edit_tool_also_seeks)); - stream.writeTextElement("SelectAlsoSeeks", QString::number(select_also_seeks)); - stream.writeTextElement("PasteSeeks", QString::number(paste_seeks)); - stream.writeTextElement("ImageSequenceFormats", img_seq_formats); + stream.writeTextElement("SavedLayout", QString::number(saved_layout)); + stream.writeTextElement("ShowTrackLines", QString::number(show_track_lines)); + stream.writeTextElement("ScrollZooms", QString::number(scroll_zooms)); + stream.writeTextElement("EditToolSelectsLinks", QString::number(edit_tool_selects_links)); + stream.writeTextElement("EditToolAlsoSeeks", QString::number(edit_tool_also_seeks)); + stream.writeTextElement("SelectAlsoSeeks", QString::number(select_also_seeks)); + stream.writeTextElement("PasteSeeks", QString::number(paste_seeks)); + stream.writeTextElement("ImageSequenceFormats", img_seq_formats); stream.writeTextElement("RectifiedWaveforms", QString::number(rectified_waveforms)); stream.writeTextElement("DefaultTransitionLength", QString::number(default_transition_length)); - stream.writeTextElement("TimecodeView", QString::number(timecode_view)); - stream.writeTextElement("ShowTitleSafeArea", QString::number(show_title_safe_area)); - stream.writeTextElement("UseCustomTitleSafeRatio", QString::number(use_custom_title_safe_ratio)); - stream.writeTextElement("CustomTitleSafeRatio", QString::number(custom_title_safe_ratio)); + stream.writeTextElement("TimecodeView", QString::number(timecode_view)); + stream.writeTextElement("ShowTitleSafeArea", QString::number(show_title_safe_area)); + stream.writeTextElement("UseCustomTitleSafeRatio", QString::number(use_custom_title_safe_ratio)); + stream.writeTextElement("CustomTitleSafeRatio", QString::number(custom_title_safe_ratio)); stream.writeTextElement("EnableDragFilesToTimeline", QString::number(enable_drag_files_to_timeline)); - stream.writeTextElement("AutoscaleByDefault", QString::number(autoscale_by_default)); + stream.writeTextElement("AutoscaleByDefault", QString::number(autoscale_by_default)); stream.writeTextElement("RecordingMode", QString::number(recording_mode)); stream.writeTextElement("EnableSeekToImport", QString::number(enable_seek_to_import)); - stream.writeTextElement("AudioScrubbing", QString::number(enable_audio_scrubbing)); - stream.writeTextElement("DropFileOnMediaToReplace", QString::number(drop_on_media_to_replace)); - stream.writeTextElement("Autoscroll", QString::number(autoscroll)); - stream.writeTextElement("AudioRate", QString::number(audio_rate)); - stream.writeTextElement("FastSeeking", QString::number(fast_seeking)); - stream.writeTextElement("HoverFocus", QString::number(hover_focus)); - stream.writeTextElement("ProjectViewType", QString::number(project_view_type)); + stream.writeTextElement("AudioScrubbing", QString::number(enable_audio_scrubbing)); + stream.writeTextElement("DropFileOnMediaToReplace", QString::number(drop_on_media_to_replace)); + stream.writeTextElement("Autoscroll", QString::number(autoscroll)); + stream.writeTextElement("AudioRate", QString::number(audio_rate)); + stream.writeTextElement("FastSeeking", QString::number(fast_seeking)); + stream.writeTextElement("HoverFocus", QString::number(hover_focus)); + stream.writeTextElement("ProjectViewType", QString::number(project_view_type)); + stream.writeTextElement("SetNameWithMarker", QString::number(set_name_with_marker)); stream.writeEndElement(); // configuration - stream.writeEndDocument(); // doc + stream.writeEndDocument(); // doc f.close(); } diff --git a/io/config.h b/io/config.h index 4972c9dd3..ea91f7f99 100644 --- a/io/config.h +++ b/io/config.h @@ -22,35 +22,36 @@ #define PROJECT_VIEW_ICON 1 struct Config { - Config(); - bool saved_layout; - bool show_track_lines; - bool scroll_zooms; - bool edit_tool_selects_links; - bool edit_tool_also_seeks; - bool select_also_seeks; - bool paste_seeks; - QString img_seq_formats; + Config(); + bool saved_layout; + bool show_track_lines; + bool scroll_zooms; + bool edit_tool_selects_links; + bool edit_tool_also_seeks; + bool select_also_seeks; + bool paste_seeks; + QString img_seq_formats; bool rectified_waveforms; int default_transition_length; - int timecode_view; - bool show_title_safe_area; - bool use_custom_title_safe_ratio; - double custom_title_safe_ratio; + int timecode_view; + bool show_title_safe_area; + bool use_custom_title_safe_ratio; + double custom_title_safe_ratio; bool enable_drag_files_to_timeline; - bool autoscale_by_default; + bool autoscale_by_default; int recording_mode; bool enable_seek_to_import; - bool enable_audio_scrubbing; - bool drop_on_media_to_replace; - int autoscroll; - int audio_rate; - bool fast_seeking; - bool hover_focus; - int project_view_type; + bool enable_audio_scrubbing; + bool drop_on_media_to_replace; + int autoscroll; + int audio_rate; + bool fast_seeking; + bool hover_focus; + int project_view_type; + bool set_name_with_marker; - void load(QString path); - void save(QString path); + void load(QString path); + void save(QString path); }; extern Config config; diff --git a/mainwindow.cpp b/mainwindow.cpp index 2d1f6b7b1..5ea809050 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -755,6 +755,10 @@ void MainWindow::setup_menus() { enable_hover_focus->setCheckable(true); enable_hover_focus->setData(reinterpret_cast(&config.hover_focus)); + set_name_and_marker = tools_menu->addAction("Ask For Name When Setting Marker", this, SLOT(toggle_bool_action())); + set_name_and_marker->setCheckable(true); + set_name_and_marker->setData(reinterpret_cast(&config.set_name_with_marker)); + tools_menu->addSeparator(); no_autoscroll = tools_menu->addAction("No Auto-Scroll", this, SLOT(set_autoscroll())); @@ -973,6 +977,7 @@ void MainWindow::toolMenu_About_To_Be_Shown() { set_bool_action_checked(enable_audio_scrubbing); set_bool_action_checked(enable_drop_on_media_to_replace); set_bool_action_checked(enable_hover_focus); + set_bool_action_checked(set_name_and_marker); set_int_action_checked(no_autoscroll, config.autoscroll); set_int_action_checked(page_autoscroll, config.autoscroll); diff --git a/mainwindow.h b/mainwindow.h index fc9f32e88..ed04b9e0c 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -167,6 +167,7 @@ private: QAction* enable_audio_scrubbing; QAction* enable_drop_on_media_to_replace; QAction* enable_hover_focus; + QAction* set_name_and_marker; // edit menu actions QAction* undo_action; diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 2f450ae56..82ec7ac02 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -182,7 +182,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector can_import = m->ready; if (m->using_inout) { double source_fr = 30; - if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate; + if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate; default_clip_in = refactor_frame_number(m->in, source_fr, seq->frame_rate); default_clip_out = refactor_frame_number(m->out, source_fr, seq->frame_rate); } @@ -214,7 +214,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector switch (medium->get_type()) { case MEDIA_TYPE_FOOTAGE: // is video source a still image? - if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) { + if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) { g.out = g.in + 100; } else { long length = m->get_length_in_frames(seq->frame_rate); @@ -225,20 +225,20 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector } for (int j=0;jaudio_tracks.size();j++) { - if (m->audio_tracks.at(j).enabled) { - g.track = j; - g.media_stream = m->audio_tracks.at(j).file_index; - ghosts.append(g); - audio_ghosts = true; - } + if (m->audio_tracks.at(j).enabled) { + g.track = j; + g.media_stream = m->audio_tracks.at(j).file_index; + ghosts.append(g); + audio_ghosts = true; + } } for (int j=0;jvideo_tracks.size();j++) { - if (m->video_tracks.at(j).enabled) { - g.track = -1-j; - g.media_stream = m->video_tracks.at(j).file_index; - ghosts.append(g); - video_ghosts = true; - } + if (m->video_tracks.at(j).enabled) { + g.track = -1-j; + g.media_stream = m->video_tracks.at(j).file_index; + ghosts.append(g); + video_ghosts = true; + } } break; case MEDIA_TYPE_SEQUENCE: @@ -1359,12 +1359,21 @@ bool Timeline::snap_to_timeline(long* l, bool use_playhead, bool use_markers, bo } void Timeline::set_marker() { - QInputDialog d(this); - d.setWindowTitle("Set Marker"); - d.setLabelText("Set marker name:"); - d.setInputMode(QInputDialog::TextInput); - if (d.exec() == QDialog::Accepted) { - undo_stack.push(new AddMarkerAction(sequence, sequence->playhead, d.textValue())); + bool add_marker = !config.set_name_with_marker; + QString marker_name; + + if (!add_marker) { + QInputDialog d(this); + d.setWindowTitle("Set Marker"); + d.setLabelText("Set marker name:"); + d.setInputMode(QInputDialog::TextInput); + add_marker = (d.exec() == QDialog::Accepted); + marker_name = d.textValue(); + } + + + if (add_marker) { + undo_stack.push(new AddMarkerAction(sequence, sequence->playhead, marker_name)); } } From a2eef2e8292fba4bf6c326389444ca294d55bec7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 21:01:36 +1100 Subject: [PATCH 39/65] numerous keyframe improvements --- mainwindow.cpp | 5 +++ panels/grapheditor.cpp | 8 ++++ panels/grapheditor.h | 4 +- project/effect.h | 1 - project/effectrow.h | 5 +-- project/keyframe.cpp | 37 ++++++++++++++++ project/keyframe.h | 4 ++ ui/graphview.cpp | 94 ++++++++++++++++++++++++++++++++++++++++ ui/graphview.h | 6 +++ ui/keyframedrawing.cpp | 12 ++++-- ui/keyframedrawing.h | 3 +- ui/keyframeview.cpp | 97 ++++++++++++++++++++---------------------- ui/keyframeview.h | 5 ++- 13 files changed, 217 insertions(+), 64 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index bcca7a9fa..6bdb414ed 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -206,6 +206,7 @@ MainWindow::MainWindow(QWidget *parent) : autorecovery_filename = data_dir + "/autorecovery.ove"; if (QFile::exists(autorecovery_filename)) { if (QMessageBox::question(NULL, "Auto-recovery", "Olive didn't close properly and an autorecovery file was detected. Would you like to open it?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + enable_launch_with_project = false; open_project_worker(autorecovery_filename, true); } } @@ -258,12 +259,16 @@ void MainWindow::delete_slot() { panel_project->delete_selected_media(); } else if (panel_effect_controls->keyframe_focus()) { panel_effect_controls->delete_selected_keyframes(); + } else if (panel_graph_editor->view_is_focused()) { + panel_graph_editor->delete_selected_keys(); } } void MainWindow::select_all() { if (panel_timeline->focused()) { panel_timeline->select_all(); + } else if (panel_graph_editor->view_is_focused()) { + panel_graph_editor->select_all(); } } diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index c6e8500f6..2cb44f673 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -202,6 +202,14 @@ bool GraphEditor::view_is_focused() { return view->hasFocus() || header->hasFocus(); } +void GraphEditor::delete_selected_keys() { + view->delete_selected_keys(); +} + +void GraphEditor::select_all() { + view->select_all(); +} + void GraphEditor::set_key_button_enabled(bool e, int type) { linear_button->setEnabled(e); linear_button->setChecked(type == KEYFRAME_TYPE_LINEAR); diff --git a/panels/grapheditor.h b/panels/grapheditor.h index 16c71d23a..1a5277b58 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -18,7 +18,9 @@ public: GraphEditor(QWidget* parent = 0); void update_panel(); void set_row(EffectRow* r); - bool view_is_focused(); + bool view_is_focused(); + void delete_selected_keys(); + void select_all(); private: GraphView* view; TimelineHeader* header; diff --git a/project/effect.h b/project/effect.h index a7210a3de..290fa6cc7 100644 --- a/project/effect.h +++ b/project/effect.h @@ -23,7 +23,6 @@ class QXmlStreamWriter; class Effect; class EffectRow; class CheckboxEx; -class KeyframeDelete; struct EffectMeta { QString name; diff --git a/project/effectrow.h b/project/effectrow.h index a91171545..8326cb1f2 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -8,7 +8,6 @@ class Effect; class QGridLayout; class EffectField; class QLabel; -class KeyframeDelete; class QPushButton; class ComboAction; class QHBoxLayout; @@ -23,8 +22,8 @@ public: EffectField* add_field(int type, const QString &id, int colspan = 1); EffectField* field(int i); int fieldCount(); - void set_keyframe_now(ComboAction *ca); - void delete_keyframe_at_time(ComboAction *ca, long time); + void set_keyframe_now(ComboAction *ca); + void delete_keyframe_at_time(ComboAction *ca, long time); ClickableLabel* label; Effect* parent_effect; bool savable; diff --git a/project/keyframe.cpp b/project/keyframe.cpp index 3d12220df..09b73be75 100644 --- a/project/keyframe.cpp +++ b/project/keyframe.cpp @@ -1,5 +1,10 @@ #include "keyframe.h" +#include + +#include "effectfield.h" +#include "undo.h" +#include "panels/panels.h" EffectKeyframe::EffectKeyframe() { pre_handle_x = -40; @@ -7,3 +12,35 @@ EffectKeyframe::EffectKeyframe() { post_handle_x = 40; post_handle_y = 0; } + +void delete_keyframes(QVector& selected_key_fields, QVector &selected_keys) { + QVector fields; + QVector key_indices; + + for (int i=0;i 0) { + ComboAction* ca = new ComboAction(); + for (int i=0;iappend(new KeyframeDelete(fields.at(i), key_indices.at(i))); + } + undo_stack.push(ca); + selected_keys.clear(); + selected_key_fields.clear(); + update_ui(false); + } +} diff --git a/project/keyframe.h b/project/keyframe.h index 7974a8a1b..fd7e4ac0f 100644 --- a/project/keyframe.h +++ b/project/keyframe.h @@ -3,6 +3,8 @@ #include +class EffectField; + class EffectKeyframe { public: EffectKeyframe(); @@ -18,4 +20,6 @@ public: double post_handle_y; }; +void delete_keyframes(QVector &selected_key_fields, QVector &selected_keys); + #endif // KEYFRAME_H diff --git a/ui/graphview.cpp b/ui/graphview.cpp index ec535fc10..3f226ad8c 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -56,6 +56,18 @@ GraphView::GraphView(QWidget* parent) : void GraphView::show_context_menu(const QPoint& pos) { QMenu menu(this); + QAction* zoom_to_selection = menu.addAction("Zoom to Selection"); + if (selected_keys.size() == 0) { + zoom_to_selection->setEnabled(false); + } else { + connect(zoom_to_selection, SIGNAL(triggered(bool)), this, SLOT(set_view_to_selection())); + } + + QAction* zoom_to_all = menu.addAction("Zoom to Show All"); + connect(zoom_to_all, SIGNAL(triggered(bool)), this, SLOT(set_view_to_all())); + + menu.addSeparator(); + QAction* reset_action = menu.addAction("Reset View"); connect(reset_action, SIGNAL(triggered(bool)), this, SLOT(reset_view())); @@ -70,6 +82,61 @@ void GraphView::reset_view() { update(); } +void GraphView::set_view_to_selection() { + if (selected_keys.size() > 0) { + long min_time = LONG_MAX; + long max_time = LONG_MIN; + double min_dbl = DBL_MAX; + double max_dbl = DBL_MIN; + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i)); + min_time = qMin(key.time, min_time); + max_time = qMax(key.time, max_time); + min_dbl = qMin(key.data.toDouble(), min_dbl); + max_dbl = qMax(key.data.toDouble(), max_dbl); + } + set_view_to_rect(min_time, min_dbl, max_time, max_dbl); + } +} + +void GraphView::set_view_to_all() { + bool can_set = false; + + long min_time = LONG_MAX; + long max_time = LONG_MIN; + double min_dbl = DBL_MAX; + double max_dbl = DBL_MIN; + for (int i=0;ifieldCount();i++) { + for (int j=0;jfield(i)->keyframes.size();j++) { + const EffectKeyframe& key = row->field(i)->keyframes.at(j); + min_time = qMin(key.time, min_time); + max_time = qMax(key.time, max_time); + min_dbl = qMin(key.data.toDouble(), min_dbl); + max_dbl = qMax(key.data.toDouble(), max_dbl); + can_set = true; + } + } + if (can_set) { + set_view_to_rect(min_time, min_dbl, max_time, max_dbl); + } +} + +void GraphView::set_view_to_rect(int x1, double y1, int x2, double y2) { + double padding = 1.5; + double inverse_padding = 1.0/padding; + double x_diff = double(x2 - x1); + double y_diff = (y2 - y1); + double x_diff_padded = x_diff*padding; + double y_diff_padded = y_diff*padding; + set_zoom(qMin(double(width()) / x_diff_padded, double(height()) / y_diff_padded)); + + set_scroll_x(qRound((double(x1) - ((x_diff_padded-x_diff)/2))*zoom)); + set_scroll_y(qRound((double(y1) - ((y_diff_padded-y_diff)/2))*zoom)); + + //set_scroll_y(height() - y1); + +} + void GraphView::draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos) { // draws last line's text QString str = QString::number(line_no*GRAPH_SIZE); @@ -520,12 +587,39 @@ void GraphView::set_field_visibility(int field, bool b) { update(); } +void GraphView::delete_selected_keys() { + if (row != NULL) { + QVector fields; + for (int i=0;ifield(selected_keys_fields.at(i))); + } + delete_keyframes(fields, selected_keys); + } +} + +void GraphView::select_all() { + if (row != NULL) { + selected_keys.clear(); + selected_keys_fields.clear(); + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + for (int j=0;jkeyframes.size();j++) { + selected_keys.append(j); + selected_keys_fields.append(i); + } + } + selection_update(); + } +} + void GraphView::set_scroll_x(int s) { x_scroll = s; emit x_scroll_changed(x_scroll); } void GraphView::set_scroll_y(int s) { + dout << s << height() << zoom; + y_scroll = s; emit y_scroll_changed(y_scroll); } diff --git a/ui/graphview.h b/ui/graphview.h index 5d1246269..e4646cb66 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -23,6 +23,9 @@ public: void set_selected_keyframe_type(int type); void set_field_visibility(int field, bool b); + + void delete_selected_keys(); + void select_all(); signals: void x_scroll_changed(int); void y_scroll_changed(int); @@ -75,6 +78,9 @@ private: private slots: void show_context_menu(const QPoint& pos); void reset_view(); + void set_view_to_selection(); + void set_view_to_all(); + void set_view_to_rect(int x1, double y1, int x2, double y2); }; #endif // GRAPHVIEW_H diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp index 07d515957..53229e46c 100644 --- a/ui/keyframedrawing.cpp +++ b/ui/keyframedrawing.cpp @@ -4,10 +4,14 @@ #define KEYFRAME_POINT_COUNT 4 -void draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { - int color = (darker) ? 100 : 160; +void draw_keyframe(QPainter &p, int type, int x, int y, bool darker, int r, int g, int b) { + if (darker) { + r *= 0.625; + g *= 0.625; + b *= 0.625; + } p.setPen(QColor(0, 0, 0)); - p.setBrush(QColor(color, color, color)); + p.setBrush(QColor(r, g, b)); switch (type) { case KEYFRAME_TYPE_LINEAR: @@ -24,5 +28,5 @@ void draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { break; } - p.setBrush(Qt::NoBrush); + p.setBrush(Qt::NoBrush); } diff --git a/ui/keyframedrawing.h b/ui/keyframedrawing.h index c09ae2626..c8c60e9e3 100644 --- a/ui/keyframedrawing.h +++ b/ui/keyframedrawing.h @@ -4,7 +4,8 @@ #include #define KEYFRAME_SIZE 6 +#define KEYFRAME_COLOR 160 -void draw_keyframe(QPainter &p, int type, int x, int y, bool darker); +void draw_keyframe(QPainter &p, int type, int x, int y, bool darker, int r = KEYFRAME_COLOR, int g = KEYFRAME_COLOR, int b = KEYFRAME_COLOR); #endif // KEYFRAMEDRAWING_H diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index bbe8d7dac..ddebcf1c4 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -16,6 +16,7 @@ #include "ui/clickablelabel.h" #include "ui/resizablescrollbar.h" #include "ui/rectangleselect.h" +#include "project/keyframe.h" #include #include @@ -165,23 +166,7 @@ void KeyframeView::update_keys() { } void KeyframeView::delete_selected_keyframes() { - ComboAction* ca = new ComboAction(); - bool del = false; - for (int i=0;iappend(new KeyframeDelete(selected_fields.at(i), selected_keyframes.at(i))); - del = true; - } - if (del) { - undo_stack.push(ca); - - selected_keyframes.clear(); - selected_fields.clear(); - update_keys(); - panel_sequence_viewer->viewer_widget->update(); - } else { - delete ca; - } + delete_keyframes(selected_fields, selected_keyframes); } void KeyframeView::set_x_scroll(int s) { @@ -201,6 +186,8 @@ void KeyframeView::resize_move(double d) { void KeyframeView::mousePressEvent(QMouseEvent *event) { rect_select_x = event->x(); rect_select_y = event->y(); + rect_select_w = 0; + rect_select_h = 0; if (panel_timeline->tool == TIMELINE_TOOL_HAND || event->buttons() & Qt::MiddleButton) { scroll_drag = true; @@ -244,7 +231,11 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { } bool already_selected = false; keys_selected = false; - if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); + if (keyframe_index > -1) { + already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); + } else { + select_rect = true; + } if (!already_selected) { if (!(event->modifiers() & Qt::ShiftModifier)) { selected_fields.clear(); @@ -274,10 +265,11 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { for (int i=0;ikeyframes.at(selected_keyframes.at(i)).time); } - keys_selected = true; } + rect_select_offset = selected_fields.size(); + update_keys(); if (event->button() == Qt::LeftButton) { @@ -298,7 +290,40 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { rect_select_y = event->pos().y(); } else if (mousedown) { int mouse_x = event->x() + x_scroll; - if (keys_selected) { + if (select_rect) { + // do a rect select + selected_fields.resize(rect_select_offset); + selected_keyframes.resize(rect_select_offset); + + rect_select_w = event->x() - rect_select_x; + rect_select_h = event->y() - rect_select_y; + + int min_row = qMin(rect_select_y, event->y())-KEYFRAME_SIZE; + int max_row = qMax(rect_select_y, event->y())+KEYFRAME_SIZE; + + long frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, rect_select_x+x_scroll); + long frame_end = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x); + long min_frame = qMin(frame_start, frame_end)-KEYFRAME_SIZE; + long max_frame = qMax(frame_start, frame_end)+KEYFRAME_SIZE; + + for (int i=0;i= min_row && rowY.at(i) <= max_row) { + EffectRow* row = rows.at(i); + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); + if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { + selected_fields.append(field); + selected_keyframes.append(j); + } + } + } + } + } + + update_keys(); + } else if (keys_selected) { // move keyframes long frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start; @@ -345,38 +370,6 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { dragging = true; update_ui(false); - } else { - // do a rect select - rect_select_w = event->x() - rect_select_x; - rect_select_h = event->y() - rect_select_y; - - int min_row = qMin(rect_select_y, event->y())-KEYFRAME_SIZE; - int max_row = qMax(rect_select_y, event->y())+KEYFRAME_SIZE; - - long frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, rect_select_x+x_scroll); - long frame_end = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x); - long min_frame = qMin(frame_start, frame_end)-KEYFRAME_SIZE; - long max_frame = qMax(frame_start, frame_end)+KEYFRAME_SIZE; - - for (int i=0;i= min_row && rowY.at(i) <= max_row) { - EffectRow* row = rows.at(i); - for (int k=0;kfieldCount();k++) { - EffectField* field = row->field(k); - for (int j=0;jkeyframes.size();j++) { - long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); - if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { - selected_fields.append(field); - selected_keyframes.append(j); - } - } - } - } - } - - select_rect = true; - - update_keys(); } } } diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 59a328ed9..51bbd361d 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -27,7 +27,7 @@ public slots: void resize_move(double d); private: long adjust_row_keyframe(EffectRow* row, long time); - QVector selected_fields; + QVector selected_fields; QVector selected_keyframes; QVector rowY; QVector rows; @@ -42,7 +42,7 @@ private: bool select_rect; bool scroll_drag; - bool keyframeIsSelected(EffectField *field, int keyframe); + bool keyframeIsSelected(EffectField *field, int keyframe); long drag_frame_start; long last_frame_diff; @@ -50,6 +50,7 @@ private: int rect_select_y; int rect_select_w; int rect_select_h; + int rect_select_offset; int x_scroll; int y_scroll; From 3f641ff815eeb85f51d66f133b768d8777a68fe2 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 21:26:34 +1100 Subject: [PATCH 40/65] more graph tweaks --- ui/graphview.cpp | 96 ++++++++++++++++++++++++--------------------- ui/graphview.h | 6 ++- ui/keyframeview.cpp | 23 ++++++++++- 3 files changed, 77 insertions(+), 48 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 3f226ad8c..f1e7cab72 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -123,11 +123,10 @@ void GraphView::set_view_to_all() { void GraphView::set_view_to_rect(int x1, double y1, int x2, double y2) { double padding = 1.5; - double inverse_padding = 1.0/padding; double x_diff = double(x2 - x1); double y_diff = (y2 - y1); - double x_diff_padded = x_diff*padding; - double y_diff_padded = y_diff*padding; + double x_diff_padded = (x_diff+10)*padding; + double y_diff_padded = (y_diff+10)*padding; set_zoom(qMin(double(width()) / x_diff_padded, double(height()) / y_diff_padded)); set_scroll_x(qRound((double(x1) - ((x_diff_padded-x_diff)/2))*zoom)); @@ -342,29 +341,27 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } else { // selecting a handle QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); + QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE && event->pos().y() < pre_point.y()+BEZIER_HANDLE_SIZE) { + current_handle = BEZIER_HANDLE_PRE; + } else if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { + current_handle = BEZIER_HANDLE_POST; + } + + if (current_handle != BEZIER_HANDLE_NONE) { sel_key = j; sel_key_field = i; - old_handle_x = key.pre_handle_x; - old_handle_y = key.pre_handle_y; - current_handle = BEZIER_HANDLE_PRE; + old_pre_handle_x = key.pre_handle_x; + old_pre_handle_y = key.pre_handle_y; + old_post_handle_x = key.post_handle_x; + old_post_handle_y = key.post_handle_y; break; - } else { - QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); - if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE - && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE - && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE - && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { - sel_key = j; - sel_key_field = i; - old_handle_x = key.post_handle_x; - old_handle_y = key.post_handle_y; - current_handle = BEZIER_HANDLE_POST; - break; - } } } } @@ -446,12 +443,11 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } update(); } else { - bool shift = (event->modifiers() & Qt::ShiftModifier); switch (current_handle) { case BEZIER_HANDLE_NONE: for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].time = qRound(selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom)); - if (shift) { + if (event->modifiers() & Qt::ShiftModifier) { row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = selected_keys_old_doubles.at(i); } else { row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = qRound(selected_keys_old_doubles.at(i) + (double(start_y - event->pos().y())/zoom)); @@ -461,27 +457,43 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { update_ui(false); break; case BEZIER_HANDLE_PRE: - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - if (shift) { - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y; - } else { - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; - } - moved_keys = true; - update_ui(false); - break; case BEZIER_HANDLE_POST: - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - if (shift) { - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y; + { + double new_pre_handle_x = old_pre_handle_x; + double new_pre_handle_y = old_pre_handle_y; + double new_post_handle_x = old_post_handle_x; + double new_post_handle_y = old_post_handle_y; + + double x_diff = double(event->pos().x() - start_x)/zoom; + double y_diff = double(start_y - event->pos().y())/zoom; + + if (current_handle == BEZIER_HANDLE_PRE) { + new_pre_handle_x += x_diff; + if (!(event->modifiers() & Qt::ShiftModifier)) new_pre_handle_y += y_diff; + if (!(event->modifiers() & Qt::ControlModifier)) { + new_post_handle_x = -new_pre_handle_x; + new_post_handle_y = -new_pre_handle_y; + } } else { - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + new_post_handle_x += x_diff; + if (!(event->modifiers() & Qt::ShiftModifier)) new_post_handle_y += y_diff; + if (!(event->modifiers() & Qt::ControlModifier)) { + new_pre_handle_x = -new_post_handle_x; + new_pre_handle_y = -new_post_handle_y; + } } + + EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + key.pre_handle_x = new_pre_handle_x; + key.pre_handle_y = new_pre_handle_y; + key.post_handle_x = new_post_handle_x; + key.post_handle_y = new_post_handle_y; + moved_keys = true; update_ui(false); + } break; } - } } } @@ -498,17 +510,13 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { } break; case BEZIER_HANDLE_PRE: - { - EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; - ca->append(new SetDouble(&key.pre_handle_x, old_handle_x, key.pre_handle_x)); - ca->append(new SetDouble(&key.pre_handle_y, old_handle_y, key.pre_handle_y)); - } - break; case BEZIER_HANDLE_POST: { EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; - ca->append(new SetDouble(&key.post_handle_x, old_handle_x, key.post_handle_x)); - ca->append(new SetDouble(&key.post_handle_y, old_handle_y, key.post_handle_y)); + ca->append(new SetDouble(&key.pre_handle_x, old_pre_handle_x, key.pre_handle_x)); + ca->append(new SetDouble(&key.pre_handle_y, old_pre_handle_y, key.pre_handle_y)); + ca->append(new SetDouble(&key.post_handle_x, old_post_handle_x, key.post_handle_x)); + ca->append(new SetDouble(&key.post_handle_y, old_post_handle_y, key.post_handle_y)); } break; } @@ -618,8 +626,6 @@ void GraphView::set_scroll_x(int s) { } void GraphView::set_scroll_y(int s) { - dout << s << height() << zoom; - y_scroll = s; emit y_scroll_changed(y_scroll); } diff --git a/ui/graphview.h b/ui/graphview.h index e4646cb66..2070b5420 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -55,8 +55,10 @@ private: QVector selected_keys_old_vals; QVector selected_keys_old_doubles; - double old_handle_x; - double old_handle_y; + double old_pre_handle_x; + double old_pre_handle_y; + double old_post_handle_x; + double old_post_handle_y; bool moved_keys; diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index ddebcf1c4..ba8557694 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -17,6 +17,7 @@ #include "ui/resizablescrollbar.h" #include "ui/rectangleselect.h" #include "project/keyframe.h" +#include "ui/graphview.h" #include #include @@ -113,7 +114,27 @@ void KeyframeView::paintEvent(QPaintEvent*) { if (!key_times.contains(f->keyframes.at(k).time)) { bool keyframe_selected = keyframeIsSelected(f, k); long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time); - draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); + + // see if any other keyframes have this time + bool solo = true; + for (int m=0;mfieldCount();m++) { + EffectField* compf = row->field(m); + for (int n=0;nkeyframes.size();n++) { + if (f->keyframes.at(k).time == compf->keyframes.at(n).time + && !(m == l && k == n)) { + solo = false; + break; + } + } + } + + if (solo) { + QColor cc = get_curve_color(l, row->fieldCount()); + draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected, cc.red(), cc.green(), cc.blue()); + } else { + draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); + } + key_times.append(f->keyframes.at(k).time); } } From fc7c17dee90d6191ad8830b74561b17147e6a107 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:35:26 +1100 Subject: [PATCH 41/65] implemented adding keyframes by click to graph editor --- project/undo.cpp | 22 ++++ project/undo.h | 15 +++ ui/graphview.cpp | 327 +++++++++++++++++++++++++++++++++++------------ ui/graphview.h | 8 ++ 4 files changed, 292 insertions(+), 80 deletions(-) diff --git a/project/undo.cpp b/project/undo.cpp index fae0bfc10..a8f7eacda 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -1269,3 +1269,25 @@ void SetLong::redo() { *p = newval; mainWindow->setWindowModified(true); } + +KeyframeFieldSet::KeyframeFieldSet(EffectField *ifield, int ii) : + field(ifield), + index(ii), + key(ifield->keyframes.at(ii)), + done(true), + old_project_changed(mainWindow->isWindowModified()) +{} + +void KeyframeFieldSet::undo() { + field->keyframes.removeAt(index); + mainWindow->setWindowModified(old_project_changed); + done = false; +} + +void KeyframeFieldSet::redo() { + if (!done) { + field->keyframes.insert(index, key); + mainWindow->setWindowModified(true); + } + done = true; +} diff --git a/project/undo.h b/project/undo.h index fcb44f500..c2e9f62a9 100644 --- a/project/undo.h +++ b/project/undo.h @@ -360,6 +360,21 @@ private: bool done; }; +// a more modern version of the above, could probably replace it +// assumes the keyframe already exists +class KeyframeFieldSet : public QUndoCommand { +public: + KeyframeFieldSet(EffectField* ifield, int ii); + void undo(); + void redo(); +private: + EffectField* field; + int index; + EffectKeyframe key; + bool done; + bool old_project_changed; +}; + class EffectFieldUndo : public QUndoCommand { public: EffectFieldUndo(EffectField* field); diff --git a/ui/graphview.cpp b/ui/graphview.cpp index f1e7cab72..fa15c25ce 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -16,6 +16,7 @@ #include "project/effect.h" #include "project/clip.h" #include "ui/rectangleselect.h" +#include "Ui/labelslider.h" #include "debug.h" @@ -171,6 +172,24 @@ void GraphView::draw_lines(QPainter& p, bool vert) { draw_line_text(p, vert, last_line, last_line_x, width()); } +QVector sort_keys_from_field(EffectField* field) { + QVector sorted_keys; + for (int k=0;kkeyframes.size();k++) { + bool inserted = false; + for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { + sorted_keys.insert(j, k); + inserted = true; + break; + } + } + if (!inserted) { + sorted_keys.append(k); + } + } + return sorted_keys; +} + void GraphView::paintEvent(QPaintEvent *event) { QPainter p(this); @@ -192,20 +211,7 @@ void GraphView::paintEvent(QPaintEvent *event) { if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { // sort keyframes by time - QVector sorted_keys; - for (int k=0;kkeyframes.size();k++) { - bool inserted = false; - for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { - sorted_keys.insert(j, k); - inserted = true; - break; - } - } - if (!inserted) { - sorted_keys.append(k); - } - } + QVector sorted_keys = sort_keys_from_field(field); int last_key_x, last_key_y; @@ -258,6 +264,7 @@ void GraphView::paintEvent(QPaintEvent *event) { last_key_x = key_x; last_key_y = key_y; } + if (last_key_x < width()) p.drawLine(last_key_x, last_key_y, width(), last_key_y); // draw keys for (int j=0;jfieldCount();i++) { - EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { - for (int j=0;jkeyframes.size();j++) { - const EffectKeyframe& key = field->keyframes.at(j); - int key_x = get_screen_x(key.time); - int key_y = get_screen_y(key.data.toDouble()); - if (event->pos().x() > key_x-KEYFRAME_SIZE - && event->pos().x() < key_x+KEYFRAME_SIZE - && event->pos().y() > key_y-KEYFRAME_SIZE - && event->pos().y() < key_y+KEYFRAME_SIZE) { - sel_key = j; - sel_key_field = i; - break; - } else { - // selecting a handle - QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); - QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); - if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE - && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE - && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE - && event->pos().y() < pre_point.y()+BEZIER_HANDLE_SIZE) { - current_handle = BEZIER_HANDLE_PRE; - } else if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE - && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE - && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE - && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { - current_handle = BEZIER_HANDLE_POST; - } + if (click_add) { + selected_keys.clear(); + selected_keys_fields.clear(); - if (current_handle != BEZIER_HANDLE_NONE) { + EffectKeyframe key; + key.time = get_value_x(event->pos().x()); + key.data = get_value_y(event->pos().y()); + key.type = click_add_type; + click_add_key = click_add_field->keyframes.size(); + click_add_field->keyframes.append(key); + update_ui(false); + } else { + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + for (int j=0;jkeyframes.size();j++) { + const EffectKeyframe& key = field->keyframes.at(j); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); + if (event->pos().x() > key_x-KEYFRAME_SIZE + && event->pos().x() < key_x+KEYFRAME_SIZE + && event->pos().y() > key_y-KEYFRAME_SIZE + && event->pos().y() < key_y+KEYFRAME_SIZE) { sel_key = j; sel_key_field = i; - old_pre_handle_x = key.pre_handle_x; - old_pre_handle_y = key.pre_handle_y; - old_post_handle_x = key.post_handle_x; - old_post_handle_y = key.post_handle_y; break; + } else { + // selecting a handle + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); + QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); + if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < pre_point.y()+BEZIER_HANDLE_SIZE) { + current_handle = BEZIER_HANDLE_PRE; + } else if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { + current_handle = BEZIER_HANDLE_POST; + } + + if (current_handle != BEZIER_HANDLE_NONE) { + sel_key = j; + sel_key_field = i; + old_pre_handle_x = key.pre_handle_x; + old_pre_handle_y = key.pre_handle_y; + old_post_handle_x = key.post_handle_x; + old_post_handle_y = key.post_handle_y; + break; + } } } } + if (sel_key > -1) break; } - if (sel_key > -1) break; - } - bool already_selected = false; - if (sel_key > -1) { - for (int i=0;imodifiers() & Qt::ShiftModifier)) { - selected_keys.removeAt(i); - selected_keys_fields.removeAt(i); + bool already_selected = false; + if (sel_key > -1) { + for (int i=0;imodifiers() & Qt::ShiftModifier)) { + selected_keys.removeAt(i); + selected_keys_fields.removeAt(i); + } + already_selected = true; + break; } - already_selected = true; - break; } } - } - if (!already_selected) { - if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_keys.clear(); - selected_keys_fields.clear(); + if (!already_selected) { + if (!(event->modifiers() & Qt::ShiftModifier)) { + selected_keys.clear(); + selected_keys_fields.clear(); + } + if (sel_key > -1) { + selected_keys.append(sel_key); + selected_keys_fields.append(sel_key_field); + } else { + rect_select = true; + rect_select_x = event->pos().x(); + rect_select_y = event->pos().y(); + rect_select_w = 0; + rect_select_h = 0; + rect_select_offset = selected_keys.size(); + } } - if (sel_key > -1) { - selected_keys.append(sel_key); - selected_keys_fields.append(sel_key_field); - } else { - rect_select = true; - rect_select_x = event->pos().x(); - rect_select_y = event->pos().y(); - rect_select_w = 0; - rect_select_h = 0; - rect_select_offset = selected_keys.size(); - } - } - selection_update(); + selection_update(); + } } } void GraphView::mouseMoveEvent(QMouseEvent *event) { - unsetCursor(); + if (!mousedown || !click_add) unsetCursor(); if (mousedown) { - if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { + if (click_add) { + click_add_field->keyframes[click_add_key].time = get_value_x(event->pos().x()); + click_add_field->keyframes[click_add_key].data = get_value_y(event->pos().y()); + update_ui(false); + } else if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { set_scroll_x(x_scroll + start_x - event->pos().x()); set_scroll_y(y_scroll + event->pos().y() - start_y); start_x = event->pos().x(); @@ -495,11 +519,145 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { break; } } + } else if (row != NULL) { + // clicking on the curve + click_add = false; + + bool hovering_key = false; + + for (int i=0;ifieldCount();i++) { + for (int j=0;jfield(i)->keyframes.size();j++) { + const EffectKeyframe& key = row->field(i)->keyframes.at(j); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); + QRect test_rect( + key_x - KEYFRAME_SIZE, + key_y - KEYFRAME_SIZE, + KEYFRAME_SIZE+KEYFRAME_SIZE, + KEYFRAME_SIZE+KEYFRAME_SIZE + ); + QRect pre_rect( + key_x + key.pre_handle_x*zoom - BEZIER_HANDLE_SIZE, + key_y + key.pre_handle_y*zoom - BEZIER_HANDLE_SIZE, + BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE, + BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE + ); + QRect post_rect( + key_x + key.post_handle_x*zoom - BEZIER_HANDLE_SIZE, + key_y + key.post_handle_y*zoom - BEZIER_HANDLE_SIZE, + BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE, + BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE + ); + + if (test_rect.contains(event->pos()) + || pre_rect.contains(event->pos()) + || post_rect.contains(event->pos())) { + hovering_key = true; + break; + } + } + } + + if (!hovering_key) { + for (int i=0;ifieldCount();i++) { + EffectField* f = row->field(i); + if (field_visibility.at(i)) { + QVector sorted_keys = sort_keys_from_field(f); + + if (event->pos().x() <= get_screen_x(f->keyframes.at(sorted_keys.first()).time)) { + int y_comp = get_screen_y(f->keyframes.at(sorted_keys.first()).data.toDouble()); + if (event->pos().y() >= y_comp-BEZIER_LINE_SIZE + && event->pos().y() <= y_comp+BEZIER_LINE_SIZE) { + // dout << "make an EARLY key on field" << i; + click_add = true; + click_add_type = f->keyframes.at(sorted_keys.first()).type; + } + } else if (event->pos().x() >= get_screen_x(f->keyframes.at(sorted_keys.last()).time)) { + int y_comp = get_screen_y(f->keyframes.at(sorted_keys.last()).data.toDouble()); + if (event->pos().y() >= y_comp-BEZIER_LINE_SIZE + && event->pos().y() <= y_comp+BEZIER_LINE_SIZE) { + // dout << "make an LATE key on field" << i; + click_add = true; + click_add_type = f->keyframes.at(sorted_keys.last()).type; + } + } else { + for (int j=1;jkeyframes.at(sorted_keys.at(j-1)); + const EffectKeyframe& key = f->keyframes.at(sorted_keys.at(j)); + + int last_key_x = get_screen_x(last_key.time); + int key_x = get_screen_x(key.time); + int last_key_y = get_screen_y(last_key.data.toDouble()); + int key_y = get_screen_y(key.data.toDouble()); + + click_add_type = last_key.type; + + if (event->pos().x() >= last_key_x + && event->pos().x() <= key_x) { + QRect mouse_rect(event->pos().x()-BEZIER_LINE_SIZE, event->pos().y()-BEZIER_LINE_SIZE, BEZIER_LINE_SIZE+BEZIER_LINE_SIZE, BEZIER_LINE_SIZE+BEZIER_LINE_SIZE); + // NOTE: FILTHY copy/paste from paintEvent + if (last_key.type == KEYFRAME_TYPE_HOLD) { + // hold + if (event->pos().y() >= last_key_y-BEZIER_LINE_SIZE + && event->pos().y() <= last_key_y+BEZIER_LINE_SIZE) { + // dout << "make an HOLD key on field" << i << "after key" << j; + click_add = true; + } + } else if (last_key.type == KEYFRAME_TYPE_BEZIER || key.type == KEYFRAME_TYPE_BEZIER) { + QPainterPath bezier_path; + bezier_path.moveTo(last_key_x, last_key_y); + if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { + // cubic bezier + bezier_path.cubicTo( + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), + QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), + QPointF(key_x, key_y) + ); + } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier + // last keyframe is the bezier one + bezier_path.quadTo( + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), + QPointF(key_x, key_y) + ); + } else { + // this keyframe is the bezier one + bezier_path.quadTo( + QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), + QPointF(key_x, key_y) + ); + } + if (bezier_path.intersects(mouse_rect)) { + // dout << "make an BEZIER key on field" << i << "after key" << j; + click_add = true; + } + } else { + // linear + QPainterPath linear_path; + linear_path.moveTo(last_key_x, last_key_y); + linear_path.lineTo(key_x, key_y); + if (linear_path.intersects(mouse_rect)) { + // dout << "make an LINEAR key on field" << i << "after key" << j; + click_add = true; + } + } + } + } + } + } + if (click_add) { + click_add_field = f; + setCursor(Qt::CrossCursor); + break; + } + } + } } } void GraphView::mouseReleaseEvent(QMouseEvent *event) { - if (moved_keys && selected_keys.size() > 0) { + if (click_add) { + undo_stack.push(new KeyframeFieldSet(click_add_field, click_add_key)); + } else if (moved_keys && selected_keys.size() > 0) { ComboAction* ca = new ComboAction(); switch (current_handle) { case BEZIER_HANDLE_NONE: @@ -524,6 +682,7 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { } moved_keys = false; mousedown = false; + click_add = false; if (rect_select) { rect_select = false; selection_update(); @@ -643,6 +802,14 @@ int GraphView::get_screen_y(double d) { return height() + y_scroll - d*zoom; } +long GraphView::get_value_x(int i) { + return qRound((i + x_scroll)/zoom); +} + +double GraphView::get_value_y(int i) { + return double(height() + y_scroll - i)/zoom; +} + void GraphView::selection_update() { selected_keys_old_vals.clear(); selected_keys_old_doubles.clear(); diff --git a/ui/graphview.h b/ui/graphview.h index 2070b5420..f7aa805bc 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -5,6 +5,7 @@ #include class EffectRow; +class EffectField; QColor get_curve_color(int index, int length); @@ -45,6 +46,8 @@ private: int get_screen_x(double); int get_screen_y(double); + long get_value_x(int); + double get_value_y(int); void selection_update(); @@ -77,6 +80,11 @@ private: int rect_select_offset; long visible_in; + + bool click_add; + EffectField* click_add_field; + int click_add_key; + int click_add_type; private slots: void show_context_menu(const QPoint& pos); void reset_view(); From 4aa44f8fa605ff1e76b0c9e6eb42d26e94ef240f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:45:08 +1100 Subject: [PATCH 42/65] implemented saving bezier handles and fixed selection crash --- project/effect.cpp | 12 ++++++++++++ ui/graphview.cpp | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/project/effect.cpp b/project/effect.cpp index d91019d79..0315e1c4f 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -701,6 +701,14 @@ void Effect::load(QXmlStreamReader& stream) { key.time = attr.value().toLong(); } else if (attr.name() == "type") { key.type = attr.value().toInt(); + } else if (attr.name() == "prehx") { + key.pre_handle_x = attr.value().toDouble(); + } else if (attr.name() == "prehy") { + key.pre_handle_y = attr.value().toDouble(); + } else if (attr.name() == "posthx") { + key.post_handle_x = attr.value().toDouble(); + } else if (attr.name() == "posthy") { + key.post_handle_y = attr.value().toDouble(); } } field->keyframes.append(key); @@ -740,6 +748,10 @@ void Effect::save(QXmlStreamWriter& stream) { stream.writeAttribute("value", save_data_to_string(field->type, key.data)); stream.writeAttribute("frame", QString::number(key.time)); stream.writeAttribute("type", QString::number(key.type)); + stream.writeAttribute("prehx", QString::number(key.pre_handle_x)); + stream.writeAttribute("prehy", QString::number(key.pre_handle_y)); + stream.writeAttribute("posthx", QString::number(key.post_handle_x)); + stream.writeAttribute("posthy", QString::number(key.post_handle_y)); stream.writeEndElement(); // key } stream.writeEndElement(); // field diff --git a/ui/graphview.cpp b/ui/graphview.cpp index fa15c25ce..a939b155d 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -392,7 +392,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { if (sel_key > -1) { for (int i=0;imodifiers() & Qt::ShiftModifier)) { + if ((event->modifiers() & Qt::ShiftModifier) && current_handle == BEZIER_HANDLE_NONE) { selected_keys.removeAt(i); selected_keys_fields.removeAt(i); } From d9c5dfa489c6ba4cd80a3cc1545da55133fe2023 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:49:44 +1100 Subject: [PATCH 43/65] tiny code cleanup --- ui/graphview.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index a939b155d..d97bf58c8 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -16,7 +16,6 @@ #include "project/effect.h" #include "project/clip.h" #include "ui/rectangleselect.h" -#include "Ui/labelslider.h" #include "debug.h" @@ -190,7 +189,7 @@ QVector sort_keys_from_field(EffectField* field) { return sorted_keys; } -void GraphView::paintEvent(QPaintEvent *event) { +void GraphView::paintEvent(QPaintEvent *) { QPainter p(this); if (panel_sequence_viewer->seq != NULL) { @@ -537,14 +536,14 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { KEYFRAME_SIZE+KEYFRAME_SIZE ); QRect pre_rect( - key_x + key.pre_handle_x*zoom - BEZIER_HANDLE_SIZE, - key_y + key.pre_handle_y*zoom - BEZIER_HANDLE_SIZE, + qRound(key_x + key.pre_handle_x*zoom - BEZIER_HANDLE_SIZE), + qRound(key_y + key.pre_handle_y*zoom - BEZIER_HANDLE_SIZE), BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE ); QRect post_rect( - key_x + key.post_handle_x*zoom - BEZIER_HANDLE_SIZE, - key_y + key.post_handle_y*zoom - BEZIER_HANDLE_SIZE, + qRound(key_x + key.post_handle_x*zoom - BEZIER_HANDLE_SIZE), + qRound(key_y + key.post_handle_y*zoom - BEZIER_HANDLE_SIZE), BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE ); @@ -654,7 +653,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } } -void GraphView::mouseReleaseEvent(QMouseEvent *event) { +void GraphView::mouseReleaseEvent(QMouseEvent *) { if (click_add) { undo_stack.push(new KeyframeFieldSet(click_add_field, click_add_key)); } else if (moved_keys && selected_keys.size() > 0) { @@ -693,7 +692,6 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { void GraphView::wheelEvent(QWheelEvent *event) { bool redraw = false; bool shift = (event->modifiers() & Qt::ShiftModifier); // scroll instead of zoom - bool alt = (event->modifiers() & Qt::AltModifier); // horiz scroll instead of vert scroll if (shift) { // scroll @@ -795,11 +793,11 @@ void GraphView::set_zoom(double z) { } int GraphView::get_screen_x(double d) { - return (d*zoom) - x_scroll; + return qRound((d*zoom) - x_scroll); } int GraphView::get_screen_y(double d) { - return height() + y_scroll - d*zoom; + return qRound(height() + y_scroll - d*zoom); } long GraphView::get_value_x(int i) { From 8619aceb91aa0b77cc8b1ad030f093778c4bdb0c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:55:30 +1100 Subject: [PATCH 44/65] fixed graph editor using wrong key for handles --- ui/graphview.cpp | 6 ++++-- ui/graphview.h | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index d97bf58c8..f71fd98ee 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -375,6 +375,8 @@ void GraphView::mousePressEvent(QMouseEvent *event) { if (current_handle != BEZIER_HANDLE_NONE) { sel_key = j; sel_key_field = i; + handle_index = j; + handle_field = i; old_pre_handle_x = key.pre_handle_x; old_pre_handle_y = key.pre_handle_y; old_post_handle_x = key.post_handle_x; @@ -506,7 +508,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } } - EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + EffectKeyframe& key = row->field(handle_field)->keyframes[handle_index]; key.pre_handle_x = new_pre_handle_x; key.pre_handle_y = new_pre_handle_y; key.post_handle_x = new_post_handle_x; @@ -669,7 +671,7 @@ void GraphView::mouseReleaseEvent(QMouseEvent *) { case BEZIER_HANDLE_PRE: case BEZIER_HANDLE_POST: { - EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + EffectKeyframe& key = row->field(handle_field)->keyframes[handle_index]; ca->append(new SetDouble(&key.pre_handle_x, old_pre_handle_x, key.pre_handle_x)); ca->append(new SetDouble(&key.pre_handle_y, old_pre_handle_y, key.pre_handle_y)); ca->append(new SetDouble(&key.post_handle_x, old_post_handle_x, key.post_handle_x)); diff --git a/ui/graphview.h b/ui/graphview.h index f7aa805bc..1a678083a 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -63,6 +63,9 @@ private: double old_post_handle_x; double old_post_handle_y; + int handle_field; + int handle_index; + bool moved_keys; int current_handle; From 212f7b84ca0d3cd4f12ad80b5865d2d7a0adec5c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:59:42 +1100 Subject: [PATCH 45/65] added include for some compilers --- ui/graphview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index f71fd98ee..39c05da7c 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "panels/panels.h" #include "panels/timeline.h" From 9b5e5a9ed679e2ba8bd14cf866d92537f5d1f8ca Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 23:11:17 +1100 Subject: [PATCH 46/65] changed project version for keyframe overhaul --- io/config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io/config.h b/io/config.h index ea91f7f99..394710270 100644 --- a/io/config.h +++ b/io/config.h @@ -3,8 +3,8 @@ #include -#define SAVE_VERSION 181124 // YYMMDD -#define MIN_SAVE_VERSION 181114 // lowest compatible project version +#define SAVE_VERSION 190104 // YYMMDD +#define MIN_SAVE_VERSION 190104 // lowest compatible project version #define TIMECODE_DROP 0 #define TIMECODE_NONDROP 1 From 5c9ded9cf16a34c339babc3f41f391967f49e7b9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 23:12:56 +1100 Subject: [PATCH 47/65] updated version --- mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 6bdb414ed..ca2822b17 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -55,7 +55,7 @@ MainWindow* mainWindow; QTimer autorecovery_timer; QString config_dir; -QString appName = "Olive (December 2018 | Alpha)"; +QString appName = "Olive (January 2019 | Alpha)"; bool demoNoticeShown = false; void MainWindow::setup_layout(bool reset) { From 864d171c4f01d3eef2fc811d1aa98970345cabbf Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Jan 2019 10:16:45 +1100 Subject: [PATCH 48/65] revised q and w functions --- panels/timeline.cpp | 70 ++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 5a78fb147..3326ac371 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -1043,15 +1043,17 @@ void Timeline::paste(bool insert) { void Timeline::ripple_to_in_point(bool in, bool ripple) { if (sequence != NULL) { if (sequence->clips.size() > 0) { - if (!in && sequence->playhead == 0) return; - // get track count int track_min = INT_MAX; int track_max = INT_MIN; long sequence_end = 0; + bool playhead_falls_on_in = false; + bool playhead_falls_on_out = false; + long next_cut = LONG_MAX; + long prev_cut = 0; + // find closest in point to playhead - long in_point = in ? LONG_MIN : LONG_MAX; for (int i=0;iclips.size();i++) { Clip* c = sequence->clips.at(i); if (c != NULL) { @@ -1060,32 +1062,36 @@ void Timeline::ripple_to_in_point(bool in, bool ripple) { sequence_end = qMax(c->timeline_out, sequence_end); - if (sequence->playhead != in_point) { - if ((in && c->timeline_in > in_point && c->timeline_in <= sequence->playhead) - || (!in && c->timeline_in < in_point && c->timeline_in >= sequence->playhead)) { - in_point = c->timeline_in; - } - if ((in && c->timeline_out > in_point && c->timeline_out <= sequence->playhead) - || (!in && c->timeline_out < in_point && c->timeline_out >= sequence->playhead)) { - in_point = c->timeline_out; - } - } + if (c->timeline_in == sequence->playhead) + playhead_falls_on_in = true; + if (c->timeline_out == sequence->playhead) + playhead_falls_on_out = true; + if (c->timeline_in > sequence->playhead) + next_cut = qMin(c->timeline_in, next_cut); + if (c->timeline_out > sequence->playhead) + next_cut = qMin(c->timeline_out, next_cut); + if (c->timeline_in < sequence->playhead) + prev_cut = qMax(c->timeline_in, prev_cut); + if (c->timeline_out < sequence->playhead) + prev_cut = qMax(c->timeline_out, prev_cut); } } - if (in && sequence->playhead == sequence_end) return; + next_cut = qMin(sequence_end, next_cut); QVector areas; ComboAction* ca = new ComboAction(); bool push_undo = true; + long seek = sequence->playhead; - if (sequence->playhead == in_point) { // one frame mode + if ((in && (playhead_falls_on_out || (playhead_falls_on_in && sequence->playhead == 0))) + || (!in && (playhead_falls_on_in || (playhead_falls_on_out && sequence->playhead == sequence_end)))) { // one frame mode if (ripple) { // set up deletion areas based on track count - if (in) { - in_point = sequence->playhead; - } else { - in_point = sequence->playhead - 1; + long in_point = sequence->playhead; + if (!in) { + in_point--; + seek--; } if (in_point >= 0) { @@ -1110,16 +1116,22 @@ void Timeline::ripple_to_in_point(bool in, bool ripple) { } else { // set up deletion areas based on track count Selection s; - s.in = qMin(in_point, sequence->playhead); - s.out = qMax(in_point, sequence->playhead); - for (int i=track_min;i<=track_max;i++) { - s.track = i; - areas.append(s); - } + if (in) seek = prev_cut; + s.in = in ? prev_cut : sequence->playhead; + s.out = in ? sequence->playhead : next_cut; - // trim and move clips around the in point - delete_areas_and_relink(ca, areas); - if (ripple) ripple_clips(ca, sequence, in_point, (in) ? (in_point - sequence->playhead) : (sequence->playhead - in_point)); + if (s.in == s.out) { + push_undo = false; + } else { + for (int i=track_min;i<=track_max;i++) { + s.track = i; + areas.append(s); + } + + // trim and move clips around the in point + delete_areas_and_relink(ca, areas); + if (ripple) ripple_clips(ca, sequence, s.in, s.in - s.out); + } } if (push_undo) { @@ -1127,7 +1139,7 @@ void Timeline::ripple_to_in_point(bool in, bool ripple) { update_ui(true); - if (in_point < sequence->playhead && ripple) panel_sequence_viewer->seek(in_point); + if (seek != sequence->playhead && ripple) panel_sequence_viewer->seek(seek); } else { delete ca; } From 1077d7f6aa2a3f27d0e10b1fa3a4f080c4abf2fb Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Jan 2019 13:29:50 +1100 Subject: [PATCH 49/65] more anchor point shenanigans --- effects/internal/transformeffect.cpp | 57 ++++++++++------------------ effects/internal/transformeffect.h | 52 ++++++++++++------------- 2 files changed, 45 insertions(+), 64 deletions(-) diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 352e0a6e6..ca65a4b9a 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -119,55 +119,40 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) // set defaults uniform_scale_field->set_bool_value(true); blend_mode_box->set_combo_index(0); - set = false; + set = false; refresh(); } void adjust_field(EffectField* field, double old_offset, double new_offset) { - if (field->keyframes.size() > 0) { - for (int i=0;ikeyframes.size();i++) { - field->keyframes[i].data = field->keyframes.at(i).data.toDouble() - old_offset + new_offset; - } - } else { - field->set_current_data(field->get_current_data().toDouble() - old_offset + new_offset); - } + if (field->keyframes.size() > 0) { + for (int i=0;ikeyframes.size();i++) { + field->keyframes[i].data = field->keyframes.at(i).data.toDouble() - old_offset + new_offset; + } + } else { + field->set_current_data(field->get_current_data().toDouble() - old_offset + new_offset); + } } void TransformEffect::refresh() { if (parent_clip != NULL && parent_clip->sequence != NULL) { - double new_default_pos_x = parent_clip->sequence->width/2; - double new_default_pos_y = parent_clip->sequence->height/2; + double new_default_pos_x = parent_clip->sequence->width/2; + double new_default_pos_y = parent_clip->sequence->height/2; - /*if (set) { - adjust_field(position_x, default_pos_x, new_default_pos_x); - adjust_field(position_y, default_pos_y, new_default_pos_y); - }*/ + /*if (set) { + adjust_field(position_x, default_pos_x, new_default_pos_x); + adjust_field(position_y, default_pos_y, new_default_pos_y); + }*/ - default_pos_x = new_default_pos_x; - default_pos_y = new_default_pos_y; + double default_pos_x = new_default_pos_x; + double default_pos_y = new_default_pos_y; position_x->set_double_default_value(default_pos_x); position_y->set_double_default_value(default_pos_y); scale_x->set_double_default_value(100); scale_y->set_double_default_value(100); - int new_default_anchor_x = parent_clip->getWidth()/2; - int new_default_anchor_y = parent_clip->getHeight()/2; - - if (new_default_anchor_x == 0) new_default_anchor_x = default_pos_x; - if (new_default_anchor_y == 0) new_default_anchor_y = default_pos_y; - - // adjust anchors for new size - if (set) { - adjust_field(anchor_x_box, default_anchor_x, new_default_anchor_x); - adjust_field(anchor_y_box, default_anchor_y, new_default_anchor_y); - } - - default_anchor_x = new_default_anchor_x; - default_anchor_y = new_default_anchor_y; - - anchor_x_box->set_double_default_value(default_anchor_x); - anchor_y_box->set_double_default_value(default_anchor_y); + anchor_x_box->set_double_default_value(0); + anchor_y_box->set_double_default_value(0); opacity->set_double_default_value(100); double x_percent_multipler = 200.0 / parent_clip->sequence->width; @@ -186,7 +171,7 @@ void TransformEffect::refresh() { right_center_gizmo->x_field_multi1 = x_percent_multipler; rotate_gizmo->x_field_multi1 = x_percent_multipler; - set = true; + set = true; } } @@ -206,8 +191,8 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i glTranslatef(position_x->get_double_value(timecode)-(parent_clip->sequence->width/2), position_y->get_double_value(timecode)-(parent_clip->sequence->height/2), 0); // anchor point - int anchor_x_offset = (anchor_x_box->get_double_value(timecode)-default_anchor_x); - int anchor_y_offset = (anchor_y_box->get_double_value(timecode)-default_anchor_y); + int anchor_x_offset = (anchor_x_box->get_double_value(timecode)); + int anchor_y_offset = (anchor_y_box->get_double_value(timecode)); coords.vertexTopLeftX -= anchor_x_offset; coords.vertexTopRightX -= anchor_x_offset; coords.vertexBottomLeftX -= anchor_x_offset; diff --git a/effects/internal/transformeffect.h b/effects/internal/transformeffect.h index c20ff6a6d..feb875ac6 100644 --- a/effects/internal/transformeffect.h +++ b/effects/internal/transformeffect.h @@ -8,40 +8,36 @@ class TransformEffect : public Effect { public: TransformEffect(Clip* c, const EffectMeta* em); void refresh(); - void process_coords(double timecode, GLTextureCoords& coords, int data); + void process_coords(double timecode, GLTextureCoords& coords, int data); - void gizmo_draw(double timecode, GLTextureCoords& coords); + void gizmo_draw(double timecode, GLTextureCoords& coords); public slots: void toggle_uniform_scale(bool enabled); private: - EffectField* position_x; - EffectField* position_y; - EffectField* scale_x; - EffectField* scale_y; - EffectField* uniform_scale_field; - EffectField* rotation; - EffectField* anchor_x_box; - EffectField* anchor_y_box; - EffectField* opacity; - EffectField* blend_mode_box; + EffectField* position_x; + EffectField* position_y; + EffectField* scale_x; + EffectField* scale_y; + EffectField* uniform_scale_field; + EffectField* rotation; + EffectField* anchor_x_box; + EffectField* anchor_y_box; + EffectField* opacity; + EffectField* blend_mode_box; - EffectGizmo* top_left_gizmo; - EffectGizmo* top_center_gizmo; - EffectGizmo* top_right_gizmo; - EffectGizmo* bottom_left_gizmo; - EffectGizmo* bottom_center_gizmo; - EffectGizmo* bottom_right_gizmo; - EffectGizmo* left_center_gizmo; - EffectGizmo* right_center_gizmo; - EffectGizmo* anchor_gizmo; - EffectGizmo* rotate_gizmo; - EffectGizmo* rect_gizmo; + EffectGizmo* top_left_gizmo; + EffectGizmo* top_center_gizmo; + EffectGizmo* top_right_gizmo; + EffectGizmo* bottom_left_gizmo; + EffectGizmo* bottom_center_gizmo; + EffectGizmo* bottom_right_gizmo; + EffectGizmo* left_center_gizmo; + EffectGizmo* right_center_gizmo; + EffectGizmo* anchor_gizmo; + EffectGizmo* rotate_gizmo; + EffectGizmo* rect_gizmo; - int default_anchor_x; - int default_anchor_y; - double default_pos_x; - double default_pos_y; - bool set; + bool set; }; #endif // TRANSFORMEFFECT_H From edf8492c7f4cfadf90eb1eb06bf9d25513488a9b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Jan 2019 13:33:30 +1100 Subject: [PATCH 50/65] auto view all if graph editor row is switched --- ui/graphview.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 39c05da7c..83bb15a19 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -733,8 +733,10 @@ void GraphView::set_row(EffectRow *r) { field_visibility.resize(row->fieldCount()); field_visibility.fill(true); visible_in = row->parent_effect->parent_clip->timeline_in; + set_view_to_all(); + } else { + update(); } - update(); } } From 4cc97d607f89fe4055d60fb0353e7ef8df64d86f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Jan 2019 14:08:01 +1100 Subject: [PATCH 51/65] improved graph zooming --- ui/graphview.cpp | 32 +++++++++++++++++++++----------- ui/graphview.h | 1 + 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 83bb15a19..67f271910 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -46,7 +46,8 @@ GraphView::GraphView(QWidget* parent) : moved_keys(false), current_handle(BEZIER_HANDLE_NONE), rect_select(false), - visible_in(0) + visible_in(0), + click_add_proc(false) { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); @@ -331,7 +332,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { int sel_key_field = -1; current_handle = BEZIER_HANDLE_NONE; - if (click_add) { + if (click_add && (event->buttons() & Qt::LeftButton)) { selected_keys.clear(); selected_keys_fields.clear(); @@ -342,6 +343,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { click_add_key = click_add_field->keyframes.size(); click_add_field->keyframes.append(key); update_ui(false); + click_add_proc = true; } else { for (int i=0;ifieldCount();i++) { EffectField* field = row->field(i); @@ -429,16 +431,16 @@ void GraphView::mousePressEvent(QMouseEvent *event) { void GraphView::mouseMoveEvent(QMouseEvent *event) { if (!mousedown || !click_add) unsetCursor(); if (mousedown) { - if (click_add) { - click_add_field->keyframes[click_add_key].time = get_value_x(event->pos().x()); - click_add_field->keyframes[click_add_key].data = get_value_y(event->pos().y()); - update_ui(false); - } else if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { + if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { set_scroll_x(x_scroll + start_x - event->pos().x()); set_scroll_y(y_scroll + event->pos().y() - start_y); start_x = event->pos().x(); start_y = event->pos().y(); update(); + } else if (click_add_proc) { + click_add_field->keyframes[click_add_key].time = get_value_x(event->pos().x()); + click_add_field->keyframes[click_add_key].data = get_value_y(event->pos().y()); + update_ui(false); } else if (rect_select) { rect_select_w = event->pos().x() - rect_select_x; rect_select_h = event->pos().y() - rect_select_y; @@ -656,8 +658,8 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } } -void GraphView::mouseReleaseEvent(QMouseEvent *) { - if (click_add) { +void GraphView::mouseReleaseEvent(QMouseEvent *e) { + if (click_add_proc) { undo_stack.push(new KeyframeFieldSet(click_add_field, click_add_key)); } else if (moved_keys && selected_keys.size() > 0) { ComboAction* ca = new ComboAction(); @@ -685,6 +687,7 @@ void GraphView::mouseReleaseEvent(QMouseEvent *) { moved_keys = false; mousedown = false; click_add = false; + click_add_proc = false; if (rect_select) { rect_select = false; selection_update(); @@ -693,6 +696,8 @@ void GraphView::mouseReleaseEvent(QMouseEvent *) { } void GraphView::wheelEvent(QWheelEvent *event) { + dout << x_scroll << y_scroll; + bool redraw = false; bool shift = (event->modifiers() & Qt::ShiftModifier); // scroll instead of zoom @@ -708,10 +713,15 @@ void GraphView::wheelEvent(QWheelEvent *event) { double new_zoom = (event->angleDelta().y() < 0) ? zoom - zoom_diff : zoom + zoom_diff; // center zoom on screen - set_scroll_x(qRound(x_scroll + double(event->pos().x())*new_zoom - double(event->pos().x())*zoom)); - set_scroll_y(qRound(y_scroll + double(height()-event->pos().y())*new_zoom - double(height()-event->pos().y())*zoom)); + /*set_scroll_x(qRound(x_scroll + double(event->pos().x())*new_zoom - double(event->pos().x())*zoom)); + set_scroll_y(qRound(y_scroll + double(height()-event->pos().y())*new_zoom - double(height()-event->pos().y())*zoom));*/ + /*set_scroll_x(qRound((double(x_scroll)/zoom*new_zoom) + (double(event->pos().x())/zoom*new_zoom))); + set_scroll_y(qRound((double(y_scroll)/zoom*new_zoom)));*/ + set_scroll_x(qRound((double(x_scroll)/zoom*new_zoom) + double(event->pos().x())*new_zoom - double(event->pos().x())*zoom)); + set_scroll_y(qRound((double(y_scroll)/zoom*new_zoom) + double(height()-event->pos().y())*new_zoom - double(height()-event->pos().y())*zoom)); set_zoom(new_zoom); + redraw = true; } } diff --git a/ui/graphview.h b/ui/graphview.h index 1a678083a..29d4f6f91 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -85,6 +85,7 @@ private: long visible_in; bool click_add; + bool click_add_proc; EffectField* click_add_field; int click_add_key; int click_add_type; From 90181ea9c50d238ee20bc77f8e969571ab88ca45 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Jan 2019 14:29:21 +1100 Subject: [PATCH 52/65] fixed disabling keyframe bug --- project/effectrow.cpp | 1 + project/undo.cpp | 13 +++++++++++++ project/undo.h | 10 ++++++++++ ui/graphview.cpp | 4 ---- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/project/effectrow.cpp b/project/effectrow.cpp index da203fd74..2dc6dae98 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -68,6 +68,7 @@ void EffectRow::set_keyframe_enabled(bool enabled) { ca->append(new KeyframeDelete(f, 0)); } } + ca->append(new SetKeyframing(this, false)); undo_stack.push(ca); panel_effect_controls->update_keyframes(); } else { diff --git a/project/undo.cpp b/project/undo.cpp index a8f7eacda..9b3aafe23 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -1291,3 +1291,16 @@ void KeyframeFieldSet::redo() { } done = true; } + +SetKeyframing::SetKeyframing(EffectRow *irow, bool ib) : + row(irow), + b(ib) +{} + +void SetKeyframing::undo() { + row->setKeyframing(!b); +} + +void SetKeyframing::redo() { + row->setKeyframing(b); +} diff --git a/project/undo.h b/project/undo.h index c2e9f62a9..ab8fdba95 100644 --- a/project/undo.h +++ b/project/undo.h @@ -641,4 +641,14 @@ private: QVariant new_val; }; +class SetKeyframing : public QUndoCommand { +public: + SetKeyframing(EffectRow* irow, bool ib); + void undo(); + void redo(); +private: + EffectRow* row; + bool b; +}; + #endif // UNDO_H diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 67f271910..ea8d4ecac 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -713,10 +713,6 @@ void GraphView::wheelEvent(QWheelEvent *event) { double new_zoom = (event->angleDelta().y() < 0) ? zoom - zoom_diff : zoom + zoom_diff; // center zoom on screen - /*set_scroll_x(qRound(x_scroll + double(event->pos().x())*new_zoom - double(event->pos().x())*zoom)); - set_scroll_y(qRound(y_scroll + double(height()-event->pos().y())*new_zoom - double(height()-event->pos().y())*zoom));*/ - /*set_scroll_x(qRound((double(x_scroll)/zoom*new_zoom) + (double(event->pos().x())/zoom*new_zoom))); - set_scroll_y(qRound((double(y_scroll)/zoom*new_zoom)));*/ set_scroll_x(qRound((double(x_scroll)/zoom*new_zoom) + double(event->pos().x())*new_zoom - double(event->pos().x())*zoom)); set_scroll_y(qRound((double(y_scroll)/zoom*new_zoom) + double(height()-event->pos().y())*new_zoom - double(height()-event->pos().y())*zoom)); From 5123ec06694d09dacd7da40c49565c3c18a8f7a0 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Jan 2019 16:30:51 +1100 Subject: [PATCH 53/65] revised keyframe system for graph editor --- panels/viewer.cpp | 52 ++++++++++--------------------- panels/viewer.h | 10 ++---- project/effectrow.cpp | 72 +++++++++++++++++++++++++++++++++++++++++-- project/effectrow.h | 4 +++ project/undo.cpp | 60 ------------------------------------ project/undo.h | 18 ----------- ui/graphview.cpp | 3 -- ui/keyframeview.cpp | 10 +++--- 8 files changed, 96 insertions(+), 133 deletions(-) diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 4d5a98d32..abfd3c461 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -53,8 +53,8 @@ Viewer::Viewer(QWidget *parent) : headers->viewer = this; headers->snapping = false; headers->show_text(false); - glViewerPane->viewer = this; - viewer_widget = glViewerPane->child; + viewer_container->viewer = this; + viewer_widget = viewer_container->child; viewer_widget->viewer = this; set_media(NULL); @@ -69,9 +69,9 @@ Viewer::Viewer(QWidget *parent) : connect(&playback_updater, SIGNAL(timeout()), this, SLOT(timer_update())); connect(&recording_flasher, SIGNAL(timeout()), this, SLOT(recording_flasher_update())); - connect(horizontalScrollBar, SIGNAL(valueChanged(int)), headers, SLOT(set_scroll(int))); - connect(horizontalScrollBar, SIGNAL(valueChanged(int)), viewer_widget, SLOT(set_waveform_scroll(int))); - connect(horizontalScrollBar, SIGNAL(resize_move(double)), this, SLOT(resize_move(double))); + connect(horizontal_bar, SIGNAL(valueChanged(int)), headers, SLOT(set_scroll(int))); + connect(horizontal_bar, SIGNAL(valueChanged(int)), viewer_widget, SLOT(set_waveform_scroll(int))); + connect(horizontal_bar, SIGNAL(resize_move(double)), this, SLOT(resize_move(double))); update_playhead_timecode(0); update_end_timecode(); @@ -441,13 +441,13 @@ void Viewer::set_zoom_value(double d) { } if (seq != NULL) { set_sb_max(); - if (!horizontalScrollBar->is_resizing()) - center_scroll_to_playhead(horizontalScrollBar, headers->get_zoom(), seq->playhead); + if (!horizontal_bar->is_resizing()) + center_scroll_to_playhead(horizontal_bar, headers->get_zoom(), seq->playhead); } } void Viewer::set_sb_max() { - headers->set_scrollbar_max(horizontalScrollBar, seq->getEndFrame(), headers->width()); + headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width()); } void Viewer::setup_ui() { @@ -457,17 +457,17 @@ void Viewer::setup_ui() { layout->setSpacing(0); layout->setMargin(0); - glViewerPane = new ViewerContainer(contents); - glViewerPane->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - layout->addWidget(glViewerPane); + viewer_container = new ViewerContainer(contents); + viewer_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(viewer_container); headers = new TimelineHeader(contents); layout->addWidget(headers); - horizontalScrollBar = new ResizableScrollBar(contents); - horizontalScrollBar->setSingleStep(20); - horizontalScrollBar->setOrientation(Qt::Horizontal); - layout->addWidget(horizontalScrollBar); + horizontal_bar = new ResizableScrollBar(contents); + horizontal_bar->setSingleStep(20); + horizontal_bar->setOrientation(Qt::Horizontal); + layout->addWidget(horizontal_bar); QWidget* lower_controls = new QWidget(contents); @@ -628,26 +628,6 @@ void Viewer::set_media(Media* m) { set_sequence(false, seq); } -void Viewer::on_btnSkipToStart_clicked() { - go_to_start(); -} - -void Viewer::on_btnSkipToEnd_clicked() { - go_to_end(); -} - -void Viewer::on_btnRewind_clicked() { - previous_frame(); -} - -void Viewer::on_btnFastForward_clicked() { - next_frame(); -} - -void Viewer::on_btnPlay_clicked() { - toggle_play(); -} - void Viewer::update_playhead() { seek(currentTimecode->value()); } @@ -725,7 +705,7 @@ void Viewer::set_sequence(bool main, Sequence *s) { update_playhead_timecode(seq->playhead); update_end_timecode(); - glViewerPane->adjust(); + viewer_container->adjust(); setWindowTitle(panel_name + seq->name); } else { diff --git a/panels/viewer.h b/panels/viewer.h index d46ba94c4..9cce59418 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -77,12 +77,6 @@ public slots: void go_to_end(); private slots: - void on_btnSkipToStart_clicked(); - void on_btnRewind_clicked(); - void on_btnPlay_clicked(); - void on_btnFastForward_clicked(); - void on_btnSkipToEnd_clicked(); - void update_playhead(); void timer_update(); void recording_flasher_update(); @@ -102,8 +96,8 @@ private: void setup_ui(); TimelineHeader* headers; - ResizableScrollBar* horizontalScrollBar; - ViewerContainer* glViewerPane; + ResizableScrollBar* horizontal_bar; + ViewerContainer* viewer_container; LabelSlider* currentTimecode; QLabel* endTimecode; diff --git a/project/effectrow.cpp b/project/effectrow.cpp index 2dc6dae98..08a5608f5 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -56,6 +56,7 @@ void EffectRow::setKeyframing(bool b) { void EffectRow::set_keyframe_enabled(bool enabled) { if (enabled) { ComboAction* ca = new ComboAction(); + ca->append(new SetKeyframing(this, true)); set_keyframe_now(ca); undo_stack.push(ca); } else { @@ -156,7 +157,74 @@ EffectRow::~EffectRow() { } void EffectRow::set_keyframe_now(ComboAction* ca) { - int index = -1; + long time = sequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; + + if (!just_made_unsafe_keyframe) { + EffectKeyframe key; + key.time = time; + + unsafe_keys.resize(fieldCount()); + unsafe_old_data.resize(fieldCount()); + key_is_new.resize(fieldCount()); + + for (int i=0;ikeyframes.size();j++) { + if (f->keyframes.at(j).time == time) { + exist_key = j; + } else if (f->keyframes.at(j).time < time + && f->keyframes.at(closest_key).time < f->keyframes.at(j).time) { + closest_key = j; + } + } + if (exist_key == -1) { + key.type = (f->keyframes.size() == 0) ? KEYFRAME_TYPE_LINEAR : f->keyframes.at(closest_key).type; + key.data = f->get_current_data();//f->keyframes.at(closest_key).data; + unsafe_keys[i] = f->keyframes.size(); + f->keyframes.append(key); + key_is_new[i] = true; + } else { + unsafe_keys[i] = exist_key; + key_is_new[i] = false; + } + unsafe_old_data[i] = f->get_current_data(); + } + just_made_unsafe_keyframe = true; + } + + for (int i=0;ikeyframes[unsafe_keys.at(i)].data = field(i)->get_current_data(); + } + + if (ca != NULL) { + for (int i=0;iappend(new KeyframeFieldSet(field(i), unsafe_keys.at(i))); + ca->append(new SetQVariant(&field(i)->keyframes[unsafe_keys.at(i)].data, unsafe_old_data.at(i), field(i)->get_current_data())); + } + unsafe_keys.clear(); + unsafe_old_data.clear(); + just_made_unsafe_keyframe = false; + } + + panel_effect_controls->update_keyframes(); + + + + + + /*if (ca != NULL) { + just_made_unsafe_keyframe = false; + } else { + if (!just_made_unsafe_keyframe) { + just_made_unsafe_keyframe = true; + } + }*/ + + + /*int index = -1; long time = sequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; for (int j=0;jupdate_keyframes(); + panel_effect_controls->update_keyframes();*/ } void EffectRow::delete_keyframe_at_time(ComboAction* ca, long time) { diff --git a/project/effectrow.h b/project/effectrow.h index 8326cb1f2..ad91a5ab9 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -48,6 +48,10 @@ private: KeyframeNavigator* keyframe_nav; bool just_made_unsafe_keyframe; + QVector unsafe_keys; + QVector unsafe_old_data; + QVector key_is_new; + }; #endif // EFFECTROW_H diff --git a/project/undo.cpp b/project/undo.cpp index 9b3aafe23..81881de44 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -752,66 +752,6 @@ void KeyframeDelete::redo() { mainWindow->setWindowModified(true); } -KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : - old_project_changed(mainWindow->isWindowModified()), - row(r), - index(i), - time(t), - just_made_keyframe(justMadeKeyframe), - done(true) -{ - enable_keyframes = !row->isKeyframing(); - if (index != -1) old_values.resize(row->fieldCount()); - new_values.resize(row->fieldCount()); - for (int i=0;ifieldCount();i++) { - EffectField* field = row->field(i); - if (index != -1) { - if (field->type == EFFECT_FIELD_DOUBLE) { - old_values[i] = static_cast(field->ui_element)->getPreviousValue(); - } else { - old_values[i] = field->keyframes.at(index).data; - } - } - new_values[i] = field->get_current_data(); - } -} - -void KeyframeSet::undo() { - if (enable_keyframes) row->setKeyframing(false); - - bool append = (index == -1 || just_made_keyframe); - for (int i=0;ifieldCount();i++) { - if (append) { - row->field(i)->keyframes.removeLast(); - } else { - row->field(i)->keyframes[index].data = old_values.at(i); - } - } - - mainWindow->setWindowModified(old_project_changed); - done = false; -} - -void KeyframeSet::redo() { - bool append = (index == -1 || (just_made_keyframe && !done)); - for (int i=0;ifieldCount();i++) { - EffectField* f = row->field(i); - if (append) { - EffectKeyframe k; - k.data = new_values.at(i); - k.time = time; - k.type = (f->keyframes.size() > 0) ? f->keyframes.last().type : EFFECT_KEYFRAME_LINEAR; - f->keyframes.append(k); - } else { - f->keyframes[index].data = new_values.at(i); - } - } - row->setKeyframing(true); - - mainWindow->setWindowModified(true); - done = true; -} - EffectFieldUndo::EffectFieldUndo(EffectField* f) : field(f), done(true), diff --git a/project/undo.h b/project/undo.h index ab8fdba95..4b555746c 100644 --- a/project/undo.h +++ b/project/undo.h @@ -342,24 +342,6 @@ private: bool old_project_changed; }; - -class KeyframeSet : public QUndoCommand { -public: - KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); - void undo(); - void redo(); - QVector old_values; - QVector new_values; -private: - bool old_project_changed; - EffectRow* row; - int index; - long time; - bool enable_keyframes; - bool just_made_keyframe; - bool done; -}; - // a more modern version of the above, could probably replace it // assumes the keyframe already exists class KeyframeFieldSet : public QUndoCommand { diff --git a/ui/graphview.cpp b/ui/graphview.cpp index ea8d4ecac..4be23a422 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -133,9 +133,6 @@ void GraphView::set_view_to_rect(int x1, double y1, int x2, double y2) { set_scroll_x(qRound((double(x1) - ((x_diff_padded-x_diff)/2))*zoom)); set_scroll_y(qRound((double(y1) - ((y_diff_padded-y_diff)/2))*zoom)); - - //set_scroll_y(height() - y1); - } void GraphView::draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos) { diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index ba8557694..4a53bd118 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -116,19 +116,17 @@ void KeyframeView::paintEvent(QPaintEvent*) { long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time); // see if any other keyframes have this time - bool solo = true; + int appearances = 0; for (int m=0;mfieldCount();m++) { EffectField* compf = row->field(m); for (int n=0;nkeyframes.size();n++) { - if (f->keyframes.at(k).time == compf->keyframes.at(n).time - && !(m == l && k == n)) { - solo = false; - break; + if (f->keyframes.at(k).time == compf->keyframes.at(n).time) { + appearances++; } } } - if (solo) { + if (appearances != row->fieldCount()) { QColor cc = get_curve_color(l, row->fieldCount()); draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected, cc.red(), cc.green(), cc.blue()); } else { From fcaed728657c75e38cef98df723346059b698c89 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Jan 2019 22:31:23 +1100 Subject: [PATCH 54/65] addressed #229 --- io/exportthread.cpp | 91 ++++++++++++++++++--------------------------- io/exportthread.h | 36 +++++++++++++++--- panels/viewer.cpp | 2 + 3 files changed, 70 insertions(+), 59 deletions(-) diff --git a/io/exportthread.cpp b/io/exportthread.cpp index 5715ce9f0..b857271e3 100644 --- a/io/exportthread.cpp +++ b/io/exportthread.cpp @@ -12,9 +12,8 @@ #include "debug.h" extern "C" { - #include - #include - #include + #include + #include #include #include } @@ -25,30 +24,11 @@ extern "C" { #include #include -AVFormatContext* fmt_ctx = NULL; -AVStream* video_stream; -AVCodec* vcodec; -AVCodecContext* vcodec_ctx; -AVFrame* video_frame; -AVFrame* sws_frame; -SwsContext* sws_ctx = NULL; -AVStream* audio_stream; -AVCodec* acodec; -AVFrame* audio_frame; -AVFrame* swr_frame; -AVCodecContext* acodec_ctx; -AVPacket video_pkt; -AVPacket audio_pkt; -SwrContext* swr_ctx = NULL; -int aframe_bytes; -int ret; -char* c_filename; - ExportThread::ExportThread() : continueEncode(true) { surface.create(); } -bool ExportThread::encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream) { +bool ExportThread::encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale) { ret = avcodec_send_frame(codec_ctx, frame); if (ret < 0) { dout << "[ERROR] Failed to send frame to encoder." << ret; @@ -69,6 +49,7 @@ bool ExportThread::encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, } packet->stream_index = stream->index; + if (rescale) av_packet_rescale_ts(packet, codec_ctx->time_base, stream->time_base); av_interleaved_write_frame(ofmt_ctx, packet); av_packet_unref(packet); } @@ -98,7 +79,7 @@ bool ExportThread::setupVideo() { // allocate context // vcodec_ctx = video_stream->codec; - vcodec_ctx = avcodec_alloc_context3(vcodec); + vcodec_ctx = avcodec_alloc_context3(vcodec); if (!vcodec_ctx) { dout << "[ERROR] Could not allocate video encoding context"; ed->export_error = "could not allocate video encoding context"; @@ -107,6 +88,7 @@ bool ExportThread::setupVideo() { // setup context vcodec_ctx->codec_id = static_cast(video_codec); + vcodec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; vcodec_ctx->width = video_width; vcodec_ctx->height = video_height; vcodec_ctx->sample_aspect_ratio = {1, 1}; @@ -122,10 +104,10 @@ bool ExportThread::setupVideo() { if (vcodec_ctx->codec_id == AV_CODEC_ID_H264) { /*char buffer[50]; - itoa(vcodec_ctx, buffer, 10);*/ + itoa(vcodec_ctx, buffer, 10);*/ - //av_opt_set(vcodec_ctx->priv_data, "preset", "fast", AV_OPT_SEARCH_CHILDREN); - //av_opt_set(vcodec_ctx->priv_data, "x264opts", "opencl", AV_OPT_SEARCH_CHILDREN); + //av_opt_set(vcodec_ctx->priv_data, "preset", "fast", AV_OPT_SEARCH_CHILDREN); + //av_opt_set(vcodec_ctx->priv_data, "x264opts", "opencl", AV_OPT_SEARCH_CHILDREN); switch (video_compression_type) { case COMPRESSION_TYPE_CFR: @@ -204,7 +186,7 @@ bool ExportThread::setupAudio() { // allocate context // acodec_ctx = audio_stream->codec; - acodec_ctx = avcodec_alloc_context3(acodec); + acodec_ctx = avcodec_alloc_context3(acodec); if (!acodec_ctx) { dout << "[ERROR] Could not find allocate audio encoding context"; ed->export_error = "could not allocate audio encoding context"; @@ -213,6 +195,7 @@ bool ExportThread::setupAudio() { // setup context acodec_ctx->codec_id = static_cast(audio_codec); + acodec_ctx->codec_type = AVMEDIA_TYPE_AUDIO; acodec_ctx->sample_rate = audio_sampling_rate; acodec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // change this to support surround/mono sound in the future (this is what the user sets the output audio to) acodec_ctx->channels = av_get_channel_layout_nb_channels(acodec_ctx->channel_layout); @@ -261,7 +244,7 @@ bool ExportThread::setupAudio() { audio_frame = av_frame_alloc(); audio_frame->sample_rate = sequence->audio_frequency; audio_frame->nb_samples = acodec_ctx->frame_size; - if (audio_frame->nb_samples == 0) audio_frame->nb_samples = 2048; // should possibly be smaller? + if (audio_frame->nb_samples == 0) audio_frame->nb_samples = 256; // should possibly be smaller? audio_frame->format = AV_SAMPLE_FMT_S16; audio_frame->channel_layout = AV_CH_LAYOUT_STEREO; // change this to support surround/mono sound in the future (this is whatever format they're held in the internal buffer) audio_frame->channels = av_get_channel_layout_nb_channels(audio_frame->channel_layout); @@ -295,7 +278,7 @@ bool ExportThread::setupContainer() { return false; } -// av_dump_format(fmt_ctx, 0, c_filename, 1); + //av_dump_format(fmt_ctx, 0, c_filename, 1); ret = avio_open(&fmt_ctx->pb, c_filename, AVIO_FLAG_WRITE); if (ret < 0) { @@ -323,9 +306,9 @@ void ExportThread::run() { continueEncode = setupContainer(); - if (video_enabled && continueEncode) continueEncode = setupVideo(); + if (video_enabled && continueEncode) continueEncode = setupVideo(); - if (audio_enabled && continueEncode) continueEncode = setupAudio(); + if (audio_enabled && continueEncode) continueEncode = setupAudio(); if (continueEncode) { ret = avformat_write_header(fmt_ctx, NULL); @@ -345,11 +328,11 @@ void ExportThread::run() { panel_sequence_viewer->viewer_widget->default_fbo = &fbo; long file_audio_samples = 0; - qint64 start_time, frame_time, avg_time, eta, total_time = 0; - long remaining_frames, frame_count = 1; + qint64 start_time, frame_time, avg_time, eta, total_time = 0; + long remaining_frames, frame_count = 1; while (sequence->playhead < end_frame && continueEncode) { - start_time = QDateTime::currentMSecsSinceEpoch(); + start_time = QDateTime::currentMSecsSinceEpoch(); panel_sequence_viewer->viewer_widget->paintGL(); @@ -360,10 +343,10 @@ void ExportThread::run() { // change pixel format sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize); - sws_frame->pts = round(timecode_secs/av_q2d(video_stream->time_base)); + sws_frame->pts = qRound(timecode_secs/av_q2d(video_stream->time_base)); - // send to encoder - if (!encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream)) continueEncode = false; + // send to encoder + if (!encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream, false)) continueEncode = false; } if (audio_enabled) { // do we need to encode more audio samples? @@ -386,25 +369,25 @@ void ExportThread::run() { swr_convert_frame(swr_ctx, swr_frame, audio_frame); swr_frame->pts = file_audio_samples; - // send to encoder - if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream)) continueEncode = false; + // send to encoder + if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream, true)) continueEncode = false; file_audio_samples += swr_frame->nb_samples; } } - // encoding stats - frame_time = (QDateTime::currentMSecsSinceEpoch()-start_time); - total_time += frame_time; - remaining_frames = (end_frame-sequence->playhead); - avg_time = (total_time/frame_count); - eta = (remaining_frames*avg_time); + // encoding stats + frame_time = (QDateTime::currentMSecsSinceEpoch()-start_time); + total_time += frame_time; + remaining_frames = (end_frame-sequence->playhead); + avg_time = (total_time/frame_count); + eta = (remaining_frames*avg_time); // dout << "[INFO] Encoded frame" << sequence->playhead << "- took" << frame_time << "ms (avg:" << avg_time << "ms, remaining:" << remaining_frames << ", ETA:" << eta << ")"; - emit progress_changed(qRound(((double) (sequence->playhead-start_frame) / (double) (end_frame-start_frame)) * 100), eta); + emit progress_changed(qRound(((double) (sequence->playhead-start_frame) / (double) (end_frame-start_frame)) * 100), eta); sequence->playhead++; - frame_count++; + frame_count++; } panel_sequence_viewer->viewer_widget->default_fbo = NULL; @@ -418,7 +401,7 @@ void ExportThread::run() { swr_convert_frame(swr_ctx, swr_frame, NULL); if (swr_frame->nb_samples == 0) break; swr_frame->pts = file_audio_samples; - if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream)) continueEncode = false; + if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream, true)) continueEncode = false; file_audio_samples += swr_frame->nb_samples; } while (swr_frame->nb_samples > 0); } @@ -428,8 +411,8 @@ void ExportThread::run() { if (continueEncode) { // flush remaining packets while (continueVideo && continueAudio) { - if (continueVideo && video_enabled) continueVideo = encode(fmt_ctx, vcodec_ctx, NULL, &video_pkt, video_stream); - if (continueAudio && audio_enabled) continueAudio = encode(fmt_ctx, acodec_ctx, NULL, &audio_pkt, audio_stream); + if (continueVideo && video_enabled) continueVideo = encode(fmt_ctx, vcodec_ctx, NULL, &video_pkt, video_stream, false); + if (continueAudio && audio_enabled) continueAudio = encode(fmt_ctx, acodec_ctx, NULL, &audio_pkt, audio_stream, true); } ret = av_write_trailer(fmt_ctx); @@ -439,7 +422,7 @@ void ExportThread::run() { continueEncode = false; } - emit progress_changed(100, 0); + emit progress_changed(100, 0); } avio_closep(&fmt_ctx->pb); @@ -448,14 +431,14 @@ void ExportThread::run() { avcodec_close(vcodec_ctx); av_packet_unref(&video_pkt); av_frame_free(&video_frame); - avcodec_free_context(&vcodec_ctx); + avcodec_free_context(&vcodec_ctx); } if (audio_enabled) { avcodec_close(acodec_ctx); av_packet_unref(&audio_pkt); av_frame_free(&audio_frame); - avcodec_free_context(&acodec_ctx); + avcodec_free_context(&acodec_ctx); } avformat_free_context(fmt_ctx); diff --git a/io/exportthread.h b/io/exportthread.h index dfc48c442..0eb74b952 100644 --- a/io/exportthread.h +++ b/io/exportthread.h @@ -10,6 +10,13 @@ struct AVCodecContext; struct AVFrame; struct AVPacket; struct AVStream; +struct AVCodec; +struct SwsContext; +struct SwrContext; + +extern "C" { + #include +} #define COMPRESSION_TYPE_CBR 0 #define COMPRESSION_TYPE_CFR 1 @@ -20,7 +27,7 @@ class ExportThread : public QThread { Q_OBJECT public: ExportThread(); - void run(); + void run(); // export parameters QString filename; @@ -35,8 +42,8 @@ public: int audio_codec; int audio_sampling_rate; int audio_bitrate; - long start_frame; - long end_frame; + long start_frame; + long end_frame; QOffscreenSurface surface; @@ -44,12 +51,31 @@ public: bool continueEncode; signals: - void progress_changed(int value, qint64 remaining_ms); + void progress_changed(int value, qint64 remaining_ms); private: - bool encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream); + bool encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale); bool setupVideo(); bool setupAudio(); bool setupContainer(); + + AVFormatContext* fmt_ctx = NULL; + AVStream* video_stream; + AVCodec* vcodec; + AVCodecContext* vcodec_ctx; + AVFrame* video_frame; + AVFrame* sws_frame; + SwsContext* sws_ctx = NULL; + AVStream* audio_stream; + AVCodec* acodec; + AVFrame* audio_frame; + AVFrame* swr_frame; + AVCodecContext* acodec_ctx; + AVPacket video_pkt; + AVPacket audio_pkt; + SwrContext* swr_ctx = NULL; + int aframe_bytes; + int ret; + char* c_filename; }; #endif // EXPORTTHREAD_H diff --git a/panels/viewer.cpp b/panels/viewer.cpp index abfd3c461..37520ba62 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -676,6 +676,8 @@ void Viewer::clean_created_seq() { } void Viewer::set_sequence(bool main, Sequence *s) { + pause(); + reset_all_audio(); if (seq != NULL) { From 64c0f1e90d2f4002698542af2f4b3d5f9309e550 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Jan 2019 23:43:38 +1100 Subject: [PATCH 55/65] updated paths --- debug.cpp | 2 +- io/path.cpp | 29 +++++++++++++++++++---------- io/path.h | 1 + mainwindow.cpp | 12 +++++++++--- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/debug.cpp b/debug.cpp index 0c63079ef..1e64959d8 100644 --- a/debug.cpp +++ b/debug.cpp @@ -11,7 +11,7 @@ QDebug debug_out(&debug_file); void setup_debug() { #ifndef QT_DEBUG - debug_file.setFileName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/debug_log"); + debug_file.setFileName(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/debug_log"); if (debug_file.open(QFile::WriteOnly)) { QString debug_intro = "Olive Session " + QString::number(QDateTime::currentMSecsSinceEpoch()); debug_file.write(debug_intro.toUtf8()); diff --git a/io/path.cpp b/io/path.cpp index b90e13345..2d24812f0 100644 --- a/io/path.cpp +++ b/io/path.cpp @@ -9,17 +9,26 @@ QString real_app_dir; QString get_app_dir() { if (real_app_dir.isEmpty()) { - QString app_path = QCoreApplication::applicationFilePath(); - real_app_dir = app_path.left(app_path.lastIndexOf('/')); - } - return real_app_dir; + QString app_path = QCoreApplication::applicationFilePath(); + real_app_dir = app_path.left(app_path.lastIndexOf('/')); + } + return real_app_dir; } QString get_data_path() { - QString app_dir = get_app_dir(); - if (QFileInfo::exists(app_dir + "/portable")) { - return app_dir; - } else { - return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - } + QString app_dir = get_app_dir(); + if (QFileInfo::exists(app_dir + "/portable")) { + return app_dir; + } else { + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + } +} + +QString get_config_path() { + QString app_dir = get_app_dir(); + if (QFileInfo::exists(app_dir + "/portable")) { + return app_dir; + } else { + return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + } } diff --git a/io/path.h b/io/path.h index 31ff1e651..9eff9033f 100644 --- a/io/path.h +++ b/io/path.h @@ -5,5 +5,6 @@ QString get_app_dir(); QString get_data_path(); +QString get_config_path(); #endif // PATH_H diff --git a/mainwindow.cpp b/mainwindow.cpp index ca2822b17..6b535e050 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -187,9 +187,15 @@ MainWindow::MainWindow(QWidget *parent) : } f.close(); } - - config_dir = data_dir + "/config.xml"; - config.load(config_dir); + } + } + QString config_path = get_config_path(); + if (!config_path.isEmpty()) { + QDir config_dir(config_path); + config_dir.mkpath("."); + QString config_fn = config_path + "/config.xml"; + if (QFileInfo::exists(config_fn)) { + config.load(config_fn); } } From 771bcadd8ec4972c824b24ecb6581078e847051c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Jan 2019 23:43:47 +1100 Subject: [PATCH 56/65] updated paths more --- mainwindow.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 6b535e050..2739d0976 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -54,7 +54,7 @@ MainWindow* mainWindow; #define OLIVE_FILE_FILTER "Olive Project (*.ove)" QTimer autorecovery_timer; -QString config_dir; +QString config_fn; QString appName = "Olive (January 2019 | Alpha)"; bool demoNoticeShown = false; @@ -81,7 +81,7 @@ void MainWindow::setup_layout(bool reset) { // load panels from file if (!reset) { - QFile panel_config(get_data_path() + "/layout"); + QFile panel_config(get_config_path() + "/layout"); if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { restoreState(panel_config.readAll(), 0); panel_config.close(); @@ -193,7 +193,7 @@ MainWindow::MainWindow(QWidget *parent) : if (!config_path.isEmpty()) { QDir config_dir(config_path); config_dir.mkpath("."); - QString config_fn = config_path + "/config.xml"; + config_fn = config_path + "/config.xml"; if (QFileInfo::exists(config_fn)) { config.load(config_fn); } @@ -789,17 +789,18 @@ void MainWindow::closeEvent(QCloseEvent *e) { panel_footage_viewer->set_main_sequence(); QString data_dir = get_data_path(); + QString config_dir = get_config_path(); if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) { if (QFile::exists(autorecovery_filename)) { QFile::rename(autorecovery_filename, autorecovery_filename + "." + QDateTime::currentDateTimeUtc().toString("yyyyMMddHHmmss")); } } - if (!config_dir.isEmpty()) { + if (!config_dir.isEmpty() && !config_fn.isEmpty()) { // save settings - config.save(config_dir); + config.save(config_fn); // save panel layout - QFile panel_config(data_dir + "/layout"); + QFile panel_config(config_dir + "/layout"); if (panel_config.open(QFile::WriteOnly)) { panel_config.write(saveState(0)); panel_config.close(); From 33868a7481daef47668e234910ba295bd53671af Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 6 Jan 2019 11:33:25 +1100 Subject: [PATCH 57/65] backend for frame rate conform done --- panels/project.cpp | 38 ++-- panels/timeline.cpp | 2 +- panels/viewer.cpp | 2 +- playback/cacher.cpp | 25 +-- playback/playback.cpp | 4 +- project/footage.cpp | 66 +++---- project/footage.h | 57 +++--- project/media.cpp | 400 +++++++++++++++++++++--------------------- 8 files changed, 300 insertions(+), 294 deletions(-) diff --git a/panels/project.cpp b/panels/project.cpp index 9edfb2bc4..3c924b933 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -181,13 +181,13 @@ Sequence* create_sequence_from_media(QVector& media_list) { if (m->ready) { if (!got_video_values) { for (int j=0;jvideo_tracks.size();j++) { - const FootageStream& ms = m->video_tracks.at(j); - s->width = ms.video_width; - s->height = ms.video_height; - if (ms.video_frame_rate != 0) { - s->frame_rate = ms.video_frame_rate; + const FootageStream& ms = m->video_tracks.at(j); + s->width = ms.video_width; + s->height = ms.video_height; + if (ms.video_frame_rate != 0) { + s->frame_rate = ms.video_frame_rate * m->speed; - if (ms.video_interlacing != VIDEO_PROGRESSIVE) s->frame_rate *= 2; + if (ms.video_interlacing != VIDEO_PROGRESSIVE) s->frame_rate *= 2; // only break with a decent frame rate, otherwise there may be a better candidate got_video_values = true; @@ -197,8 +197,8 @@ Sequence* create_sequence_from_media(QVector& media_list) { } if (!got_audio_values) { for (int j=0;jaudio_tracks.size();j++) { - const FootageStream& ms = m->audio_tracks.at(j); - s->audio_frequency = ms.audio_frequency; + const FootageStream& ms = m->audio_tracks.at(j); + s->audio_frequency = ms.audio_frequency; got_audio_values = true; break; } @@ -853,22 +853,22 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, stream.writeAttribute("in", QString::number(f->in)); stream.writeAttribute("out", QString::number(f->out)); for (int j=0;jvideo_tracks.size();j++) { - const FootageStream& ms = f->video_tracks.at(j); + const FootageStream& ms = f->video_tracks.at(j); stream.writeStartElement("video"); - stream.writeAttribute("id", QString::number(ms.file_index)); - stream.writeAttribute("width", QString::number(ms.video_width)); - stream.writeAttribute("height", QString::number(ms.video_height)); - stream.writeAttribute("framerate", QString::number(ms.video_frame_rate, 'f', 10)); - stream.writeAttribute("infinite", QString::number(ms.infinite_length)); + stream.writeAttribute("id", QString::number(ms.file_index)); + stream.writeAttribute("width", QString::number(ms.video_width)); + stream.writeAttribute("height", QString::number(ms.video_height)); + stream.writeAttribute("framerate", QString::number(ms.video_frame_rate, 'f', 10)); + stream.writeAttribute("infinite", QString::number(ms.infinite_length)); stream.writeEndElement(); } for (int j=0;jaudio_tracks.size();j++) { - const FootageStream& ms = f->audio_tracks.at(j); + const FootageStream& ms = f->audio_tracks.at(j); stream.writeStartElement("audio"); - stream.writeAttribute("id", QString::number(ms.file_index)); - stream.writeAttribute("channels", QString::number(ms.audio_channels)); - stream.writeAttribute("layout", QString::number(ms.audio_layout)); - stream.writeAttribute("frequency", QString::number(ms.audio_frequency)); + stream.writeAttribute("id", QString::number(ms.file_index)); + stream.writeAttribute("channels", QString::number(ms.audio_channels)); + stream.writeAttribute("layout", QString::number(ms.audio_layout)); + stream.writeAttribute("frequency", QString::number(ms.audio_frequency)); stream.writeEndElement(); } stream.writeEndElement(); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 3326ac371..3ba55d0ef 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -178,7 +178,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector can_import = m->ready; if (m->using_inout) { double source_fr = 30; - if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate; + if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate * m->speed; default_clip_in = refactor_frame_number(m->in, source_fr, seq->frame_rate); default_clip_out = refactor_frame_number(m->out, source_fr, seq->frame_rate); } diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 37520ba62..65ff704c3 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -576,7 +576,7 @@ void Viewer::set_media(Media* m) { const FootageStream& video_stream = footage->video_tracks.at(0); seq->width = video_stream.video_width; seq->height = video_stream.video_height; - if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate; + if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate * footage->speed; Clip* c = new Clip(seq); c->media = media; diff --git a/playback/cacher.cpp b/playback/cacher.cpp index ef6687c9e..7df2ad948 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -247,7 +247,8 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) { #ifdef AUDIOWARNINGS dout << "pre cutoff deets::: rev_frame.pts:" << rev_frame->pts << "rev_frame.nb_samples" << rev_frame->nb_samples << "rev_target:" << c->reverse_target; #endif - rev_frame->nb_samples = qRound64(static_cast(c->reverse_target - rev_frame->pts) / c->stream->codecpar->sample_rate * (current_audio_freq() / c->speed)); + double playback_speed = c->speed * c->media->to_footage()->speed; + rev_frame->nb_samples = qRound64(static_cast(c->reverse_target - rev_frame->pts) / c->stream->codecpar->sample_rate * (current_audio_freq() / playback_speed)); #ifdef AUDIOWARNINGS dout << "post cutoff deets::" << rev_frame->nb_samples; #endif @@ -451,7 +452,7 @@ void cache_video_worker(Clip* c, long playhead) { AVFrame* frame = av_frame_alloc(); Footage* media = c->media->to_footage(); - const FootageStream* ms = media->get_stream_from_file_index(true, c->media_stream); + const FootageStream* ms = media->get_stream_from_file_index(true, c->media_stream); while ((retr_ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) { if (c->multithreaded && c->cacher->interrupt) return; // abort @@ -546,7 +547,7 @@ void reset_cache(Clip* c, long target_frame) { c->frame->pts = 0; } } else { - const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); if (ms->infinite_length) { /*avcodec_flush_buffers(c->codecCtx); av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD);*/ @@ -640,7 +641,7 @@ void open_clip_worker(Clip* clip) { Footage* m = clip->media->to_footage(); QByteArray ba = m->url.toUtf8(); const char* filename = ba.constData(); - const FootageStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream); + const FootageStream* ms = m->get_stream_from_file_index(clip->track < 0, clip->media_stream); int errCode = avformat_open_input( &clip->formatCtx, @@ -670,7 +671,7 @@ void open_clip_worker(Clip* clip) { clip->codecCtx = avcodec_alloc_context3(clip->codec); avcodec_parameters_to_context(clip->codecCtx, clip->stream->codecpar); - clip->max_queue_size = (ms->infinite_length) ? 1 : qCeil(ms->video_frame_rate*0.5); + clip->max_queue_size = (ms->infinite_length) ? 1 : qCeil(ms->video_frame_rate * m->speed * 0.5); if (ms->video_interlacing != VIDEO_PROGRESSIVE) clip->max_queue_size *= 2; clip->opts = NULL; @@ -797,7 +798,9 @@ void open_clip_worker(Clip* clip) { int target_sample_rate = current_audio_freq(); - if (qFuzzyCompare(clip->speed, 1.0)) { + double playback_speed = clip->speed * m->speed; + + if (qFuzzyCompare(playback_speed, 1.0)) { avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0); } else if (clip->maintain_audio_pitch) { AVFilterContext* previous_filter = clip->buffersrc_ctx; @@ -805,10 +808,10 @@ void open_clip_worker(Clip* clip) { char speed_param[10]; - if (clip->speed != 1.0) { - double base = (clip->speed > 1.0) ? 2.0 : 0.5; +// if (playback_speed != 1.0) { + double base = (playback_speed > 1.0) ? 2.0 : 0.5; - double speedlog = log(clip->speed) / log(base); + double speedlog = log(playback_speed) / log(base); int whole2 = qFloor(speedlog); speedlog -= whole2; @@ -826,11 +829,11 @@ void open_clip_worker(Clip* clip) { last_filter = NULL; avfilter_graph_create_filter(&last_filter, avfilter_get_by_name("atempo"), "atempo", speed_param, NULL, clip->filter_graph); avfilter_link(previous_filter, 0, last_filter, 0); - } +// } avfilter_link(last_filter, 0, clip->buffersink_ctx, 0); } else { - target_sample_rate = qRound64(target_sample_rate / clip->speed); + target_sample_rate = qRound64(target_sample_rate / playback_speed); avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0); } diff --git a/playback/playback.cpp b/playback/playback.cpp index dfb67461a..cd6c906d7 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -266,7 +266,9 @@ double playhead_to_clip_seconds(Clip* c, long playhead) { // returns time in seconds long clip_frame = playhead_to_clip_frame(c, playhead); if (c->reverse) clip_frame = c->getMaximumLength() - clip_frame - 1; - return ((double) clip_frame/c->sequence->frame_rate)*c->speed; + double secs = ((double) clip_frame/c->sequence->frame_rate)*c->speed; + if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) secs *= c->media->to_footage()->speed; + return secs; } int64_t seconds_to_timestamp(Clip* c, double seconds) { diff --git a/project/footage.cpp b/project/footage.cpp index ec3a8baa1..e63d343f2 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -11,55 +11,55 @@ extern "C" { #include "project/clip.h" -Footage::Footage() : ready(false), preview_gen(NULL), invalid(false), in(0), out(0) { - ready_lock.lock(); +Footage::Footage() : ready(false), preview_gen(NULL), invalid(false), in(0), out(0), speed(1.0) { + ready_lock.lock(); } Footage::~Footage() { - reset(); + reset(); } void Footage::reset() { if (preview_gen != NULL) { preview_gen->cancel(); preview_gen->wait(); - } - video_tracks.clear(); - audio_tracks.clear(); - ready = false; + } + video_tracks.clear(); + audio_tracks.clear(); + ready = false; } long Footage::get_length_in_frames(double frame_rate) { - if (length >= 0) return qFloor(((double) length / (double) AV_TIME_BASE) * frame_rate); - return 0; + if (length >= 0) return qFloor(((double) length / (double) AV_TIME_BASE) * frame_rate / speed); + return 0; } FootageStream* Footage::get_stream_from_file_index(bool video, int index) { - if (video) { - for (int i=0;i>1; - int sqx = (diff < 0) ? -diff : 0; - int sqy = (diff > 0) ? diff : 0; - p.drawImage(sqx, sqy, video_preview); - video_preview_square = QIcon(pixmap); + // generate square version for QListView? + int square_size = qMax(video_preview.width(), video_preview.height()); + QPixmap pixmap(square_size, square_size); + pixmap.fill(Qt::transparent); + QPainter p(&pixmap); + int diff = (video_preview.width() - video_preview.height())>>1; + int sqx = (diff < 0) ? -diff : 0; + int sqy = (diff > 0) ? diff : 0; + p.drawImage(sqx, sqy, video_preview); + video_preview_square = QIcon(pixmap); } diff --git a/project/footage.h b/project/footage.h index effbced64..d6408857b 100644 --- a/project/footage.h +++ b/project/footage.h @@ -23,45 +23,46 @@ struct FootageStream { int video_width; int video_height; bool infinite_length; - double video_frame_rate; - int video_interlacing; + double video_frame_rate; + int video_interlacing; int video_auto_interlacing; - int audio_channels; - int audio_layout; - int audio_frequency; - bool enabled; + int audio_channels; + int audio_layout; + int audio_frequency; + bool enabled; - // preview thumbnail/waveform - bool preview_done; - QImage video_preview; - QIcon video_preview_square; + // preview thumbnail/waveform + bool preview_done; + QImage video_preview; + QIcon video_preview_square; QVector audio_preview; - void make_square_thumb(); + void make_square_thumb(); }; struct Footage { - Footage(); - ~Footage(); + Footage(); + ~Footage(); - QString url; - QString name; + QString url; + QString name; int64_t length; - QVector video_tracks; - QVector audio_tracks; - int save_id; - bool ready; - bool invalid; + QVector video_tracks; + QVector audio_tracks; + int save_id; + bool ready; + bool invalid; + double speed; - PreviewGenerator* preview_gen; - QMutex ready_lock; + PreviewGenerator* preview_gen; + QMutex ready_lock; - bool using_inout; - long in; - long out; + bool using_inout; + long in; + long out; - long get_length_in_frames(double frame_rate); - FootageStream *get_stream_from_file_index(bool video, int index); - void reset(); + long get_length_in_frames(double frame_rate); + FootageStream *get_stream_from_file_index(bool video, int index); + void reset(); }; #endif // FOOTAGE_H diff --git a/project/media.cpp b/project/media.cpp index bf72646cb..1ecb0cc8b 100644 --- a/project/media.cpp +++ b/project/media.cpp @@ -13,311 +13,311 @@ #include "debug.h" extern "C" { - #include - #include + #include + #include } QString get_interlacing_name(int interlacing) { - switch (interlacing) { - case VIDEO_PROGRESSIVE: return "None (Progressive)"; - case VIDEO_TOP_FIELD_FIRST: return "Top Field First"; - case VIDEO_BOTTOM_FIELD_FIRST: return "Bottom Field First"; - default: return "Invalid"; - } + switch (interlacing) { + case VIDEO_PROGRESSIVE: return "None (Progressive)"; + case VIDEO_TOP_FIELD_FIRST: return "Top Field First"; + case VIDEO_BOTTOM_FIELD_FIRST: return "Bottom Field First"; + default: return "Invalid"; + } } QString get_channel_layout_name(int channels, uint64_t layout) { - switch (channels) { - case 0: return "Invalid"; break; - case 1: return "Mono"; break; - case 2: return "Stereo"; break; - default: { - char buf[50]; - av_get_channel_layout_string(buf, sizeof(buf), channels, layout); - return QString(buf); - } - } + switch (channels) { + case 0: return "Invalid"; break; + case 1: return "Mono"; break; + case 2: return "Stereo"; break; + default: { + char buf[50]; + av_get_channel_layout_string(buf, sizeof(buf), channels, layout); + return QString(buf); + } + } } Media::Media(Media* iparent) : - parent(iparent), - throbber(NULL), - root(false), - type(-1) + parent(iparent), + throbber(NULL), + root(false), + type(-1) {} Media::~Media() { - switch (get_type()) { - case MEDIA_TYPE_FOOTAGE: delete to_footage(); break; - case MEDIA_TYPE_SEQUENCE: if (object != NULL) delete to_sequence(); break; - } - if (throbber != NULL) delete throbber; - qDeleteAll(children); + switch (get_type()) { + case MEDIA_TYPE_FOOTAGE: delete to_footage(); break; + case MEDIA_TYPE_SEQUENCE: if (object != NULL) delete to_sequence(); break; + } + if (throbber != NULL) delete throbber; + qDeleteAll(children); } Footage *Media::to_footage() { - return static_cast(object); + return static_cast(object); } Sequence *Media::to_sequence() { - return static_cast(object); + return static_cast(object); } void Media::set_footage(Footage *f) { - type = MEDIA_TYPE_FOOTAGE; - object = f; + type = MEDIA_TYPE_FOOTAGE; + object = f; } void Media::set_sequence(Sequence *s) { - set_icon(QIcon(":/icons/sequence.png")); - type = MEDIA_TYPE_SEQUENCE; - object = s; - if (s != NULL) update_tooltip(); + set_icon(QIcon(":/icons/sequence.png")); + type = MEDIA_TYPE_SEQUENCE; + object = s; + if (s != NULL) update_tooltip(); } void Media::set_folder() { - if (folder_name.isEmpty()) folder_name = "New Folder"; - set_icon(QIcon(":/icons/folder.png")); - type = MEDIA_TYPE_FOLDER; - object = NULL; + if (folder_name.isEmpty()) folder_name = "New Folder"; + set_icon(QIcon(":/icons/folder.png")); + type = MEDIA_TYPE_FOLDER; + object = NULL; } void Media::set_icon(const QIcon &ico) { - icon = ico; + icon = ico; } void Media::set_parent(Media *p) { - parent = p; + parent = p; } void Media::update_tooltip(const QString& error) { - switch (type) { - case MEDIA_TYPE_FOOTAGE: - { - Footage* f = to_footage(); - tooltip = "Name: " + f->name + "\nFilename: " + f->url + "\n"; + switch (type) { + case MEDIA_TYPE_FOOTAGE: + { + Footage* f = to_footage(); + tooltip = "Name: " + f->name + "\nFilename: " + f->url + "\n"; - if (error.isEmpty()) { - if (f->video_tracks.size() > 0) { - tooltip += "Video Dimensions: "; - for (int i=0;ivideo_tracks.size();i++) { - if (i > 0) { - tooltip += ", "; - } - tooltip += QString::number(f->video_tracks.at(i).video_width) + "x" + QString::number(f->video_tracks.at(i).video_height); - } - tooltip += "\n"; + if (error.isEmpty()) { + if (f->video_tracks.size() > 0) { + tooltip += "Video Dimensions: "; + for (int i=0;ivideo_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + tooltip += QString::number(f->video_tracks.at(i).video_width) + "x" + QString::number(f->video_tracks.at(i).video_height); + } + tooltip += "\n"; - if (!f->video_tracks.at(0).infinite_length) { - tooltip += "Frame Rate: "; - for (int i=0;ivideo_tracks.size();i++) { - if (i > 0) { - tooltip += ", "; - } - if (f->video_tracks.at(i).video_interlacing == VIDEO_PROGRESSIVE) { - tooltip += QString::number(f->video_tracks.at(i).video_frame_rate); - } else { - tooltip += QString::number(f->video_tracks.at(i).video_frame_rate * 2); - tooltip += " fields (" + QString::number(f->video_tracks.at(i).video_frame_rate) + " frames)"; - } - } - tooltip += "\n"; - } + if (!f->video_tracks.at(0).infinite_length) { + tooltip += "Frame Rate: "; + for (int i=0;ivideo_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + if (f->video_tracks.at(i).video_interlacing == VIDEO_PROGRESSIVE) { + tooltip += QString::number(f->video_tracks.at(i).video_frame_rate * f->speed); + } else { + tooltip += QString::number(f->video_tracks.at(i).video_frame_rate * f->speed * 2); + tooltip += " fields (" + QString::number(f->video_tracks.at(i).video_frame_rate * f->speed) + " frames)"; + } + } + tooltip += "\n"; + } - tooltip += "Interlacing: "; - for (int i=0;ivideo_tracks.size();i++) { - if (i > 0) { - tooltip += ", "; - } - tooltip += get_interlacing_name(f->video_tracks.at(i).video_interlacing); - } - } + tooltip += "Interlacing: "; + for (int i=0;ivideo_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + tooltip += get_interlacing_name(f->video_tracks.at(i).video_interlacing); + } + } - if (f->audio_tracks.size() > 0) { - tooltip += "\n"; + if (f->audio_tracks.size() > 0) { + tooltip += "\n"; - tooltip += "Audio Frequency: "; - for (int i=0;iaudio_tracks.size();i++) { - if (i > 0) { - tooltip += ", "; - } - tooltip += QString::number(f->audio_tracks.at(i).audio_frequency); - } - tooltip += "\n"; + tooltip += "Audio Frequency: "; + for (int i=0;iaudio_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + tooltip += QString::number(f->audio_tracks.at(i).audio_frequency * f->speed); + } + tooltip += "\n"; - tooltip += "Audio Channels: "; - for (int i=0;iaudio_tracks.size();i++) { - if (i > 0) { - tooltip += ", "; - } - tooltip += get_channel_layout_name(f->audio_tracks.at(i).audio_channels, f->audio_tracks.at(i).audio_layout); - } - // tooltip += "\n"; - } - } else { - tooltip += error; - } - } - break; - case MEDIA_TYPE_SEQUENCE: - { - Sequence* s = to_sequence(); - tooltip = "Name: " + s->name - + "\nVideo Dimensions: " + QString::number(s->width) + "x" + QString::number(s->height) - + "\nFrame Rate: " + QString::number(s->frame_rate) - + "\nAudio Frequency: " + QString::number(s->audio_frequency) - + "\nAudio Layout: " + get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout); - } - break; - } + tooltip += "Audio Channels: "; + for (int i=0;iaudio_tracks.size();i++) { + if (i > 0) { + tooltip += ", "; + } + tooltip += get_channel_layout_name(f->audio_tracks.at(i).audio_channels, f->audio_tracks.at(i).audio_layout); + } + // tooltip += "\n"; + } + } else { + tooltip += error; + } + } + break; + case MEDIA_TYPE_SEQUENCE: + { + Sequence* s = to_sequence(); + tooltip = "Name: " + s->name + + "\nVideo Dimensions: " + QString::number(s->width) + "x" + QString::number(s->height) + + "\nFrame Rate: " + QString::number(s->frame_rate) + + "\nAudio Frequency: " + QString::number(s->audio_frequency) + + "\nAudio Layout: " + get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout); + } + break; + } } void *Media::to_object() { - return object; + return object; } int Media::get_type() { - return type; + return type; } const QString &Media::get_name() { - switch (type) { - case MEDIA_TYPE_FOOTAGE: return to_footage()->name; - case MEDIA_TYPE_SEQUENCE: return to_sequence()->name; + switch (type) { + case MEDIA_TYPE_FOOTAGE: return to_footage()->name; + case MEDIA_TYPE_SEQUENCE: return to_sequence()->name; default: return folder_name; } } void Media::set_name(const QString &n) { - switch (type) { - case MEDIA_TYPE_FOOTAGE: to_footage()->name = n; break; - case MEDIA_TYPE_SEQUENCE: to_sequence()->name = n; break; - case MEDIA_TYPE_FOLDER: folder_name = n; break; - } + switch (type) { + case MEDIA_TYPE_FOOTAGE: to_footage()->name = n; break; + case MEDIA_TYPE_SEQUENCE: to_sequence()->name = n; break; + case MEDIA_TYPE_FOLDER: folder_name = n; break; + } } double Media::get_frame_rate(int stream) { - switch (get_type()) { + switch (get_type()) { case MEDIA_TYPE_FOOTAGE: { Footage* f = to_footage(); - if (stream < 0) return f->video_tracks.at(0).video_frame_rate; - return f->get_stream_from_file_index(true, stream)->video_frame_rate; + if (stream < 0) return f->video_tracks.at(0).video_frame_rate * f->speed; + return f->get_stream_from_file_index(true, stream)->video_frame_rate * f->speed; + } + case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate; } - case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate; - } return NULL; } int Media::get_sampling_rate(int stream) { - switch (get_type()) { + switch (get_type()) { case MEDIA_TYPE_FOOTAGE: { Footage* f = to_footage(); - if (stream < 0) return f->audio_tracks.at(0).audio_frequency; - return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency; + if (stream < 0) return f->audio_tracks.at(0).audio_frequency * f->speed; + return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency * f->speed; } - case MEDIA_TYPE_SEQUENCE: return to_sequence()->audio_frequency; - } - return 0; + case MEDIA_TYPE_SEQUENCE: return to_sequence()->audio_frequency; + } + return 0; } void Media::appendChild(Media *child) { - child->set_parent(this); - children.append(child); + child->set_parent(this); + children.append(child); } bool Media::setData(int col, const QVariant &value) { - if (col == 0) { - QString n = value.toString(); - if (!n.isEmpty() && get_name() != n) { - undo_stack.push(new MediaRename(this, value.toString())); - return true; - } - } - return false; + if (col == 0) { + QString n = value.toString(); + if (!n.isEmpty() && get_name() != n) { + undo_stack.push(new MediaRename(this, value.toString())); + return true; + } + } + return false; } Media *Media::child(int row) { - return children.value(row); + return children.value(row); } int Media::childCount() const { - return children.count(); + return children.count(); } int Media::columnCount() const { - return 3; + return 3; } QVariant Media::data(int column, int role) { - switch (role) { - case Qt::DecorationRole: - if (column == 0) { - if (get_type() == MEDIA_TYPE_FOOTAGE) { - Footage* f = to_footage(); - if (f->video_tracks.size() > 0 - && f->video_tracks.at(0).preview_done) { - return f->video_tracks.at(0).video_preview_square; - } - } + switch (role) { + case Qt::DecorationRole: + if (column == 0) { + if (get_type() == MEDIA_TYPE_FOOTAGE) { + Footage* f = to_footage(); + if (f->video_tracks.size() > 0 + && f->video_tracks.at(0).preview_done) { + return f->video_tracks.at(0).video_preview_square; + } + } - return icon; - } - break; - case Qt::DisplayRole: - switch (column) { - case 0: return (root) ? "Name" : get_name(); - case 1: - if (root) return "Duration"; - if (get_type() == MEDIA_TYPE_SEQUENCE) { - Sequence* s = to_sequence(); - return frame_to_timecode(s->getEndFrame(), config.timecode_view, s->frame_rate); - } - if (get_type() == MEDIA_TYPE_FOOTAGE) { - Footage* f = to_footage(); - double r = 30; + return icon; + } + break; + case Qt::DisplayRole: + switch (column) { + case 0: return (root) ? "Name" : get_name(); + case 1: + if (root) return "Duration"; + if (get_type() == MEDIA_TYPE_SEQUENCE) { + Sequence* s = to_sequence(); + return frame_to_timecode(s->getEndFrame(), config.timecode_view, s->frame_rate); + } + if (get_type() == MEDIA_TYPE_FOOTAGE) { + Footage* f = to_footage(); + double r = 30; - if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate)) - r = f->video_tracks.at(0).video_frame_rate; + if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate)) + r = f->video_tracks.at(0).video_frame_rate * f->speed; long len = f->get_length_in_frames(r); if (len > 0) return frame_to_timecode(len, config.timecode_view, r); - } - break; - case 2: - if (root) return "Rate"; - if (get_type() == MEDIA_TYPE_SEQUENCE) return QString::number(get_frame_rate()) + " FPS"; - if (get_type() == MEDIA_TYPE_FOOTAGE) { - Footage* f = to_footage(); + } + break; + case 2: + if (root) return "Rate"; + if (get_type() == MEDIA_TYPE_SEQUENCE) return QString::number(get_frame_rate()) + " FPS"; + if (get_type() == MEDIA_TYPE_FOOTAGE) { + Footage* f = to_footage(); double r; if (f->video_tracks.size() > 0 && !qIsNull(r = get_frame_rate())) { return QString::number(get_frame_rate()) + " FPS"; - } else if (f->audio_tracks.size() > 0) { - return QString::number(get_sampling_rate()) + " Hz"; - } - } - break; - } - break; - case Qt::ToolTipRole: - return tooltip; - } - return QVariant(); + } else if (f->audio_tracks.size() > 0) { + return QString::number(get_sampling_rate()) + " Hz"; + } + } + break; + } + break; + case Qt::ToolTipRole: + return tooltip; + } + return QVariant(); } int Media::row() const { - if (parent) { - return parent->children.indexOf(const_cast(this)); - } - return 0; + if (parent) { + return parent->children.indexOf(const_cast(this)); + } + return 0; } Media *Media::parentItem() { - return parent; + return parent; } void Media::removeChild(int i) { - children.removeAt(i); + children.removeAt(i); } From 6afce14c97f634b7ad835d3e87a8820ad719806f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 6 Jan 2019 16:58:35 +1100 Subject: [PATCH 58/65] added saving keyboard shortcuts --- dialogs/preferencesdialog.cpp | 39 ++++++++++++++++++--- dialogs/preferencesdialog.h | 3 ++ mainwindow.cpp | 66 +++++++++++++++++++++++++++++++++++ panels/viewer.cpp | 7 ++-- panels/viewer.h | 3 ++ 5 files changed, 109 insertions(+), 9 deletions(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index ec0eb76f5..0eec771e9 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -14,6 +14,11 @@ #include #include #include +#include +#include +#include + +#include "debug.h" KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a) : QKeySequenceEdit(parent), action(a) { @@ -53,6 +58,7 @@ void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* if (a->menu() != NULL) { setup_kbd_shortcut_worker(a->menu(), item); } else { + item->setData(0, Qt::UserRole + 1, reinterpret_cast(a)); key_shortcut_items.append(item); key_shortcut_actions.append(a); } @@ -94,6 +100,19 @@ void PreferencesDialog::save() { accept(); } +void PreferencesDialog::reset_default_shortcut() { + QList items = keyboard_tree->selectedItems(); + for (int i=0;iselectedItems().at(i); + const QVariant& data = item->data(0, Qt::UserRole + 1); + if (!data.isNull()) { + QAction* a = reinterpret_cast(data.value()); + QKeySequence ks(a->property("default").toString()); + static_cast(keyboard_tree->itemWidget(item, 1))->setKeySequence(ks); + } + } +} + void PreferencesDialog::setup_ui() { QVBoxLayout* verticalLayout = new QVBoxLayout(this); QTabWidget* tabWidget = new QTabWidget(this); @@ -136,17 +155,27 @@ void PreferencesDialog::setup_ui() { verticalLayout_2->addWidget(groupBox); tabWidget->addTab(tab_4, "Playback"); - QWidget* tab_3 = new QWidget(); - QHBoxLayout* horizontalLayout = new QHBoxLayout(tab_3); - horizontalLayout->setContentsMargins(0, 0, 0, 0); + + QWidget* shortcut_tab = new QWidget(); + + QVBoxLayout* shortcut_layout = new QVBoxLayout(shortcut_tab); keyboard_tree = new QTreeWidget(); QTreeWidgetItem* tree_header = keyboard_tree->headerItem(); tree_header->setText(0, "Action"); tree_header->setText(1, "Shortcut"); - horizontalLayout->addWidget(keyboard_tree); + shortcut_layout->addWidget(keyboard_tree); - tabWidget->addTab(tab_3, "Keyboard"); + QHBoxLayout* reset_shortcut_layout = new QHBoxLayout(); + reset_shortcut_layout->addStretch(); + + reset_shortcut_button = new QPushButton("Reset to Default"); + reset_shortcut_layout->addWidget(reset_shortcut_button); + connect(reset_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(reset_default_shortcut())); + + shortcut_layout->addLayout(reset_shortcut_layout); + + tabWidget->addTab(shortcut_tab, "Keyboard"); verticalLayout->addWidget(tabWidget); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index b0560a247..510b5dc5a 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -32,6 +32,7 @@ public: private slots: void save(); + void reset_default_shortcut(); private: void setup_ui(); @@ -46,6 +47,8 @@ private: QVector key_shortcut_actions; QVector key_shortcut_items; QVector key_shortcut_fields; + + QPushButton* reset_shortcut_button; }; #endif // PREFERENCESDIALOG_H diff --git a/mainwindow.cpp b/mainwindow.cpp index 2739d0976..53b4bfec4 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -443,6 +443,47 @@ bool MainWindow::can_close_project() { return true; } +void kbd_shortcut_processor(QByteArray& file, QMenu* menu, bool save) { + QList actions = menu->actions(); + for (int i=0;imenu() != NULL) { + kbd_shortcut_processor(file, a->menu(), save); + } else if (!a->isSeparator()) { + if (save) { + // saving custom shortcuts + if (!a->property("default").isNull()) { + QKeySequence defks(a->property("default").toString()); + if (a->shortcut() != defks) { + // custom shortcut + if (!file.isEmpty()) file.append('\n'); + file.append(a->text().replace("&", "")); + file.append('\t'); + file.append(a->shortcut().toString()); + } + } + } else { + // loading custom shortcuts + a->setProperty("default", a->shortcut().toString()); + QString comp_str = a->text().replace("&", ""); + int shortcut_index = file.indexOf(comp_str); + if (shortcut_index == 0 || (shortcut_index > 0 && file.at(shortcut_index-1) == '\n')) { + shortcut_index += comp_str.size() + 1; + QString shortcut; + while (shortcut_index < file.size() && file.at(shortcut_index) != '\n') { + shortcut.append(file.at(shortcut_index)); + shortcut_index++; + } + QKeySequence ks(shortcut); + if (!ks.isEmpty()) { + a->setShortcut(ks); + } + } + } + } + } +} + void MainWindow::setup_menus() { QMenuBar* menuBar = new QMenuBar(this); setMenuBar(menuBar); @@ -756,6 +797,16 @@ void MainWindow::setup_menus() { QMenu* help_menu = menuBar->addMenu("&Help"); help_menu->addAction("&About...", this, SLOT(show_about())); + + QFile shortcut_path(get_config_path() + "/shortcuts"); + if (shortcut_path.exists() && shortcut_path.open(QFile::ReadOnly)) { + QList menus = menuBar->actions(); + QByteArray shortcut_bytes = shortcut_path.readAll(); + for (int i=0;imenu(); + kbd_shortcut_processor(shortcut_bytes, menu, false); + } + } } void MainWindow::set_bool_action_checked(QAction *a) { @@ -807,6 +858,21 @@ void MainWindow::closeEvent(QCloseEvent *e) { } else { dout << "[ERROR] Failed to save layout"; } + + // save main menu actions + QList menus = menuBar()->actions(); + QByteArray shortcut_file; + for (int i=0;imenu(); + kbd_shortcut_processor(shortcut_file, menu, true); + } + QFile shortcut_file_io(config_dir + "/shortcuts"); + if (shortcut_file_io.open(QFile::WriteOnly)) { + shortcut_file_io.write(shortcut_file); + shortcut_file_io.close(); + } else { + dout << "[ERROR] Failed to save shortcut file"; + } } stop_audio(); diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 37520ba62..74b5fdb2d 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -506,9 +506,8 @@ void Viewer::setup_ui() { playback_control_layout->addWidget(btnRewind); btnPlay = new QPushButton(playback_controls); - QIcon playIcon; - playIcon.addFile(QStringLiteral(":/icons/play.png"), QSize(), QIcon::Normal, QIcon::Off); - playIcon.addFile(QStringLiteral(":/icons/play-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off); + playIcon.addFile(QStringLiteral(":/icons/play.png"), QSize(), QIcon::Normal, QIcon::On); + playIcon.addFile(QStringLiteral(":/icons/play-disabled.png"), QSize(), QIcon::Disabled, QIcon::On); btnPlay->setIcon(playIcon); connect(btnPlay, SIGNAL(clicked(bool)), this, SLOT(toggle_play())); playback_control_layout->addWidget(btnPlay); @@ -725,5 +724,5 @@ void Viewer::set_sequence(bool main, Sequence *s) { } void Viewer::set_playpause_icon(bool play) { - btnPlay->setIcon(QIcon((play) ? ":/icons/play.png" : ":/icons/pause.png")); + btnPlay->setIcon(play ? playIcon : QIcon(":/icons/pause.png")); } diff --git a/panels/viewer.h b/panels/viewer.h index 9cce59418..624d8df8e 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -3,6 +3,7 @@ #include #include +#include class Timeline; class ViewerWidget; @@ -93,6 +94,8 @@ private: void set_zoom_value(double d); void set_sb_max(); + QIcon playIcon; + void setup_ui(); TimelineHeader* headers; From a70feb5b0b3ff4ae4b984a78c2747a8b54cc7306 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 6 Jan 2019 20:26:10 +1100 Subject: [PATCH 59/65] finalized media frame rate change --- dialogs/mediapropertiesdialog.cpp | 20 ++++++++++++++++++++ dialogs/mediapropertiesdialog.h | 12 +++++++----- io/exportthread.cpp | 2 +- project/undo.cpp | 23 +++++++++++++++++++++++ project/undo.h | 9 +++++++++ ui/timelinewidget.cpp | 4 ++++ 6 files changed, 64 insertions(+), 6 deletions(-) diff --git a/dialogs/mediapropertiesdialog.cpp b/dialogs/mediapropertiesdialog.cpp index ada35aa76..c37db60c2 100644 --- a/dialogs/mediapropertiesdialog.cpp +++ b/dialogs/mediapropertiesdialog.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "project/footage.h" #include "project/media.h" @@ -52,6 +53,16 @@ MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) : row++; if (f->video_tracks.size() > 0) { + // frame conforming + grid->addWidget(new QLabel("Conform to Frame Rate:"), row, 0); + conform_fr = new QDoubleSpinBox(); + conform_fr->setMinimum(0.01); + conform_fr->setValue(f->video_tracks.at(0).video_frame_rate * f->speed); + grid->addWidget(conform_fr, row, 1); + + row++; + + // deinterlacing mode interlacing_box = new QComboBox(); interlacing_box->addItem("Auto (" + get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing) + ")"); interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE)); @@ -109,6 +120,8 @@ void MediaPropertiesDialog::accept() { } } + bool refresh_clips = false; + // set interlacing if (f->video_tracks.size() > 0) { if (interlacing_box->currentIndex() > 0) { @@ -116,6 +129,12 @@ void MediaPropertiesDialog::accept() { } else { ca->append(new SetInt(&f->video_tracks[0].video_interlacing, f->video_tracks.at(0).video_auto_interlacing)); } + + // set frame rate conform + if (!qFuzzyCompare(conform_fr->value(), f->video_tracks.at(0).video_frame_rate)) { + ca->append(new SetDouble(&f->speed, f->speed, conform_fr->value()/f->video_tracks.at(0).video_frame_rate)); + refresh_clips = true; + } } // set name @@ -124,6 +143,7 @@ void MediaPropertiesDialog::accept() { ca->append(mr); ca->appendPost(new CloseAllClipsCommand()); ca->appendPost(new UpdateFootageTooltip(item)); + if (refresh_clips) ca->appendPost(new RefreshClips(item)); undo_stack.push(ca); diff --git a/dialogs/mediapropertiesdialog.h b/dialogs/mediapropertiesdialog.h index db173408d..b5699dff7 100644 --- a/dialogs/mediapropertiesdialog.h +++ b/dialogs/mediapropertiesdialog.h @@ -8,18 +8,20 @@ class QComboBox; class QLineEdit; class Media; class QListWidget; +class QDoubleSpinBox; class MediaPropertiesDialog : public QDialog { Q_OBJECT public: - MediaPropertiesDialog(QWidget *parent, Media* i); + MediaPropertiesDialog(QWidget *parent, Media* i); private: QComboBox* interlacing_box; - QLineEdit* name_box; - Media* item; - QListWidget* track_list; + QLineEdit* name_box; + Media* item; + QListWidget* track_list; + QDoubleSpinBox* conform_fr; private slots: - void accept(); + void accept(); }; #endif // MEDIAPROPERTIESDIALOG_H diff --git a/io/exportthread.cpp b/io/exportthread.cpp index b857271e3..a2eeb5962 100644 --- a/io/exportthread.cpp +++ b/io/exportthread.cpp @@ -331,7 +331,7 @@ void ExportThread::run() { qint64 start_time, frame_time, avg_time, eta, total_time = 0; long remaining_frames, frame_count = 1; - while (sequence->playhead < end_frame && continueEncode) { + while (sequence->playhead <= end_frame && continueEncode) { start_time = QDateTime::currentMSecsSinceEpoch(); panel_sequence_viewer->viewer_widget->paintGL(); diff --git a/project/undo.cpp b/project/undo.cpp index 81881de44..a169d9d7f 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -1244,3 +1244,26 @@ void SetKeyframing::undo() { void SetKeyframing::redo() { row->setKeyframing(b); } + +RefreshClips::RefreshClips(Media *m) : + media(m) +{} + +void RefreshClips::undo() { + redo(); +} + +void RefreshClips::redo() { + // close any clips currently using this media + QVector all_sequences = panel_project->list_all_project_sequences(); + for (int i=0;ito_sequence(); + for (int j=0;jclips.size();j++) { + Clip* c = s->clips.at(j); + if (c != NULL && c->media == media) { + c->replaced = true; + c->refresh(); + } + } + } +} diff --git a/project/undo.h b/project/undo.h index 4b555746c..ebc7348de 100644 --- a/project/undo.h +++ b/project/undo.h @@ -633,4 +633,13 @@ private: bool b; }; +class RefreshClips : public QUndoCommand { +public: + RefreshClips(Media* m); + void undo(); + void redo(); +private: + Media* media; +}; + #endif // UNDO_H diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 3dd273c77..d56a36765 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -2347,6 +2347,10 @@ void TimelineWidget::paintEvent(QPaintEvent*) { p.drawImage(QRect(thumb_x, clip_rect.y()+thumb_y, thumb_clip_width, thumb_height), ms->video_preview, QRect(0, 0, thumb_clip_width*((double)ms->video_preview.width()/(double)thumb_width), ms->video_preview.height())); } } + if (clip->timeline_out - clip->timeline_in + clip->clip_in > clip->getMaximumLength()) { + draw_checkerboard = true; + checkerboard_rect.setLeft(panel_timeline->getTimelineScreenPointFromFrame(clip->getMaximumLength() + clip->timeline_in - clip->clip_in)); + } } else if (clip_rect.height() > TRACK_MIN_HEIGHT) { // draw waveform p.setPen(QColor(80, 80, 80)); From 90ee9ecb4ec5b2d1fc91bcfa9a807e8ef61f1a01 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 6 Jan 2019 20:31:08 +1100 Subject: [PATCH 60/65] made rate conforms savable --- io/loadthread.cpp | 1136 ++++++++++++++++++++++---------------------- panels/project.cpp | 1 + 2 files changed, 570 insertions(+), 567 deletions(-) diff --git a/io/loadthread.cpp b/io/loadthread.cpp index 65bdc50f4..e343369d8 100644 --- a/io/loadthread.cpp +++ b/io/loadthread.cpp @@ -20,621 +20,623 @@ #include struct TransitionData { - int id; - QString name; - long length; - Clip* otc; - Clip* ctc; + int id; + QString name; + long length; + Clip* otc; + Clip* ctc; }; LoadThread::LoadThread(LoadDialog* l, bool a) : ld(l), autorecovery(a), cancelled(false) { - connect(this, SIGNAL(finished()), this, SLOT(deleteLater())); - connect(this, SIGNAL(success()), this, SLOT(success_func())); + connect(this, SIGNAL(finished()), this, SLOT(deleteLater())); + connect(this, SIGNAL(success()), this, SLOT(success_func())); connect(this, SIGNAL(error()), this, SLOT(error_func())); connect(this, SIGNAL(start_create_dual_transition(const TransitionData*,Clip*,Clip*,const EffectMeta*)), this, SLOT(create_dual_transition(const TransitionData*,Clip*,Clip*,const EffectMeta*))); - connect(this, SIGNAL(start_create_effect_ui(QXmlStreamReader*, Clip*, int, const EffectMeta*, long, bool)), this, SLOT(create_effect_ui(QXmlStreamReader*, Clip*, int, const EffectMeta*, long, bool))); + connect(this, SIGNAL(start_create_effect_ui(QXmlStreamReader*, Clip*, int, const EffectMeta*, long, bool)), this, SLOT(create_effect_ui(QXmlStreamReader*, Clip*, int, const EffectMeta*, long, bool))); } const EffectMeta* get_meta_from_name(const QString& name) { - for (int j=0;jtrack < 0) ? "Transform" : "Volume"; break; - case 1: effect_name = (c->track < 0) ? "Shake" : "Pan"; break; - case 2: effect_name = (c->track < 0) ? "Text" : "Noise"; break; - case 3: effect_name = (c->track < 0) ? "Solid" : "Tone"; break; - case 4: effect_name = "Invert"; break; - case 5: effect_name = "Chroma Key"; break; - case 6: effect_name = "Gaussian Blur"; break; - case 7: effect_name = "Crop"; break; - case 8: effect_name = "Flip"; break; - case 9: effect_name = "Box Blur"; break; - case 10: effect_name = "Wave"; break; - case 11: effect_name = "Temperature"; break; - } - } + // backwards compatibility with 180820 + if (stream.name() == "effect" && effect_id != -1) { + switch (effect_id) { + case 0: effect_name = (c->track < 0) ? "Transform" : "Volume"; break; + case 1: effect_name = (c->track < 0) ? "Shake" : "Pan"; break; + case 2: effect_name = (c->track < 0) ? "Text" : "Noise"; break; + case 3: effect_name = (c->track < 0) ? "Solid" : "Tone"; break; + case 4: effect_name = "Invert"; break; + case 5: effect_name = "Chroma Key"; break; + case 6: effect_name = "Gaussian Blur"; break; + case 7: effect_name = "Crop"; break; + case 8: effect_name = "Flip"; break; + case 9: effect_name = "Box Blur"; break; + case 10: effect_name = "Wave"; break; + case 11: effect_name = "Temperature"; break; + } + } - // wait for effects to be loaded - effects_loaded.lock(); + // wait for effects to be loaded + effects_loaded.lock(); - const EffectMeta* meta = NULL; + const EffectMeta* meta = NULL; - // find effect with this name - if (!effect_name.isEmpty()) { - meta = get_meta_from_name(effect_name); - } + // find effect with this name + if (!effect_name.isEmpty()) { + meta = get_meta_from_name(effect_name); + } - effects_loaded.unlock(); + effects_loaded.unlock(); - if (meta == NULL) { - dout << "[WARNING] An effect used by this project is missing. It was not loaded."; - } else { - QString tag = stream.name().toString(); + if (meta == NULL) { + dout << "[WARNING] An effect used by this project is missing. It was not loaded."; + } else { + QString tag = stream.name().toString(); - int type; - if (tag == "opening") { - type = TA_OPENING_TRANSITION; - } else if (tag == "closing") { - type = TA_CLOSING_TRANSITION; - } else { - type = TA_NO_TRANSITION; - } + int type; + if (tag == "opening") { + type = TA_OPENING_TRANSITION; + } else if (tag == "closing") { + type = TA_CLOSING_TRANSITION; + } else { + type = TA_NO_TRANSITION; + } - emit start_create_effect_ui(&stream, c, type, meta, effect_length, effect_enabled); + emit start_create_effect_ui(&stream, c, type, meta, effect_length, effect_enabled); - waitCond.wait(&mutex); - } + waitCond.wait(&mutex); + } } void LoadThread::read_next(QXmlStreamReader &stream) { - stream.readNext(); - update_current_element_count(stream); + stream.readNext(); + update_current_element_count(stream); } void LoadThread::read_next_start_element(QXmlStreamReader &stream) { - stream.readNextStartElement(); - update_current_element_count(stream); + stream.readNextStartElement(); + update_current_element_count(stream); } void LoadThread::update_current_element_count(QXmlStreamReader &stream) { - if (is_element(stream)) { - current_element_count++; - report_progress((current_element_count * 100) / total_element_count); - } + if (is_element(stream)) { + current_element_count++; + report_progress((current_element_count * 100) / total_element_count); + } } bool LoadThread::is_element(QXmlStreamReader &stream) { - return stream.isStartElement() - && (stream.name() == "folder" - || stream.name() == "footage" - || stream.name() == "sequence" - || stream.name() == "clip" - || stream.name() == "effect"); + return stream.isStartElement() + && (stream.name() == "folder" + || stream.name() == "footage" + || stream.name() == "sequence" + || stream.name() == "clip" + || stream.name() == "effect"); } bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { - f.seek(0); - stream.setDevice(stream.device()); + f.seek(0); + stream.setDevice(stream.device()); - QString root_search; - QString child_search; + QString root_search; + QString child_search; - switch (type) { - case LOAD_TYPE_VERSION: - root_search = "version"; - break; - case LOAD_TYPE_URL: - root_search = "url"; - break; - case MEDIA_TYPE_FOLDER: - root_search = "folders"; - child_search = "folder"; - break; - case MEDIA_TYPE_FOOTAGE: - root_search = "media"; - child_search = "footage"; - break; - case MEDIA_TYPE_SEQUENCE: - root_search = "sequences"; - child_search = "sequence"; - break; - } + switch (type) { + case LOAD_TYPE_VERSION: + root_search = "version"; + break; + case LOAD_TYPE_URL: + root_search = "url"; + break; + case MEDIA_TYPE_FOLDER: + root_search = "folders"; + child_search = "folder"; + break; + case MEDIA_TYPE_FOOTAGE: + root_search = "media"; + child_search = "footage"; + break; + case MEDIA_TYPE_SEQUENCE: + root_search = "sequences"; + child_search = "sequence"; + break; + } - show_err = true; + show_err = true; - while (!stream.atEnd() && !cancelled) { - read_next_start_element(stream); - if (stream.name() == root_search) { - if (type == LOAD_TYPE_VERSION) { - int proj_version = stream.readElementText().toInt(); - if (proj_version < MIN_SAVE_VERSION && proj_version > SAVE_VERSION) { - if (QMessageBox::warning(mainWindow, "Version Mismatch", "This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) { - show_err = false; - return false; - } - } - } else if (type == LOAD_TYPE_URL) { - internal_proj_url = stream.readElementText(); - internal_proj_dir = QFileInfo(internal_proj_url).absoluteDir(); - } else { - while (!cancelled && !(stream.name() == root_search && stream.isEndElement())) { - read_next(stream); - if (stream.name() == child_search && stream.isStartElement()) { - switch (type) { - case MEDIA_TYPE_FOLDER: - { - Media* folder = panel_project->new_folder(0); - folder->temp_id2 = 0; - for (int j=0;jtemp_id = attr.value().toInt(); - } else if (attr.name() == "name") { - folder->set_name(attr.value().toString()); - } else if (attr.name() == "parent") { - folder->temp_id2 = attr.value().toInt(); - } - } - loaded_folders.append(folder); - } - break; - case MEDIA_TYPE_FOOTAGE: - { - int folder = 0; + while (!stream.atEnd() && !cancelled) { + read_next_start_element(stream); + if (stream.name() == root_search) { + if (type == LOAD_TYPE_VERSION) { + int proj_version = stream.readElementText().toInt(); + if (proj_version < MIN_SAVE_VERSION && proj_version > SAVE_VERSION) { + if (QMessageBox::warning(mainWindow, "Version Mismatch", "This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) { + show_err = false; + return false; + } + } + } else if (type == LOAD_TYPE_URL) { + internal_proj_url = stream.readElementText(); + internal_proj_dir = QFileInfo(internal_proj_url).absoluteDir(); + } else { + while (!cancelled && !(stream.name() == root_search && stream.isEndElement())) { + read_next(stream); + if (stream.name() == child_search && stream.isStartElement()) { + switch (type) { + case MEDIA_TYPE_FOLDER: + { + Media* folder = panel_project->new_folder(0); + folder->temp_id2 = 0; + for (int j=0;jtemp_id = attr.value().toInt(); + } else if (attr.name() == "name") { + folder->set_name(attr.value().toString()); + } else if (attr.name() == "parent") { + folder->temp_id2 = attr.value().toInt(); + } + } + loaded_folders.append(folder); + } + break; + case MEDIA_TYPE_FOOTAGE: + { + int folder = 0; - Media* item = new Media(0); - Footage* m = new Footage(); + Media* item = new Media(0); + Footage* m = new Footage(); - m->using_inout = false; + m->using_inout = false; - for (int j=0;jsave_id = attr.value().toInt(); - } else if (attr.name() == "folder") { - folder = attr.value().toInt(); - } else if (attr.name() == "name") { - m->name = attr.value().toString(); - } else if (attr.name() == "url") { - m->url = attr.value().toString(); + for (int j=0;jsave_id = attr.value().toInt(); + } else if (attr.name() == "folder") { + folder = attr.value().toInt(); + } else if (attr.name() == "name") { + m->name = attr.value().toString(); + } else if (attr.name() == "url") { + m->url = attr.value().toString(); - if (!QFileInfo::exists(m->url)) { // if path is not absolute - QString proj_dir_test = proj_dir.absoluteFilePath(m->url); - QString internal_proj_dir_test = internal_proj_dir.absoluteFilePath(m->url); + if (!QFileInfo::exists(m->url)) { // if path is not absolute + QString proj_dir_test = proj_dir.absoluteFilePath(m->url); + QString internal_proj_dir_test = internal_proj_dir.absoluteFilePath(m->url); - if (QFileInfo::exists(proj_dir_test)) { // if path is relative to the project's current dir - m->url = proj_dir_test; - dout << "[INFO] Matched" << attr.value().toString() << "relative to project's current directory"; - } else if (QFileInfo::exists(internal_proj_dir_test)) { // if path is relative to the last directory the project was saved in - m->url = internal_proj_dir_test; - dout << "[INFO] Matched" << attr.value().toString() << "relative to project's internal directory"; - } else if (m->url.contains('%')) { - // hack for image sequences (qt won't be able to find the URL with %, but ffmpeg may) - m->url = internal_proj_dir_test; - dout << "[INFO] Guess image sequence" << attr.value().toString() << "path to project's internal directory"; - } else { - dout << "[INFO] Failed to match" << attr.value().toString() << "to file"; - } - } else { - dout << "[INFO] Matched" << attr.value().toString() << "with absolute path"; - } - } else if (attr.name() == "duration") { - m->length = attr.value().toLongLong(); - } else if (attr.name() == "using_inout") { - m->using_inout = (attr.value() == "1"); - } else if (attr.name() == "in") { - m->in = attr.value().toLong(); - } else if (attr.name() == "out") { - m->out = attr.value().toLong(); - } - } + if (QFileInfo::exists(proj_dir_test)) { // if path is relative to the project's current dir + m->url = proj_dir_test; + dout << "[INFO] Matched" << attr.value().toString() << "relative to project's current directory"; + } else if (QFileInfo::exists(internal_proj_dir_test)) { // if path is relative to the last directory the project was saved in + m->url = internal_proj_dir_test; + dout << "[INFO] Matched" << attr.value().toString() << "relative to project's internal directory"; + } else if (m->url.contains('%')) { + // hack for image sequences (qt won't be able to find the URL with %, but ffmpeg may) + m->url = internal_proj_dir_test; + dout << "[INFO] Guess image sequence" << attr.value().toString() << "path to project's internal directory"; + } else { + dout << "[INFO] Failed to match" << attr.value().toString() << "to file"; + } + } else { + dout << "[INFO] Matched" << attr.value().toString() << "with absolute path"; + } + } else if (attr.name() == "duration") { + m->length = attr.value().toLongLong(); + } else if (attr.name() == "using_inout") { + m->using_inout = (attr.value() == "1"); + } else if (attr.name() == "in") { + m->in = attr.value().toLong(); + } else if (attr.name() == "out") { + m->out = attr.value().toLong(); + } else if (attr.name() == "speed") { + m->speed = attr.value().toDouble(); + } + } - item->set_footage(m); + item->set_footage(m); - project_model.appendChild(find_loaded_folder_by_id(folder), item); + project_model.appendChild(find_loaded_folder_by_id(folder), item); - // analyze media to see if it's the same - loaded_media_items.append(item); - } - break; - case MEDIA_TYPE_SEQUENCE: - { - Media* parent = NULL; - Sequence* s = new Sequence(); + // analyze media to see if it's the same + loaded_media_items.append(item); + } + break; + case MEDIA_TYPE_SEQUENCE: + { + Media* parent = NULL; + Sequence* s = new Sequence(); - // load attributes about sequence - for (int j=0;jname = attr.value().toString(); - } else if (attr.name() == "folder") { - int folder = attr.value().toInt(); - if (folder > 0) parent = find_loaded_folder_by_id(folder); - } else if (attr.name() == "id") { - s->save_id = attr.value().toInt(); - } else if (attr.name() == "width") { - s->width = attr.value().toInt(); - } else if (attr.name() == "height") { - s->height = attr.value().toInt(); - } else if (attr.name() == "framerate") { - s->frame_rate = attr.value().toDouble(); - } else if (attr.name() == "afreq") { - s->audio_frequency = attr.value().toInt(); - } else if (attr.name() == "alayout") { - s->audio_layout = attr.value().toInt(); - } else if (attr.name() == "open") { - open_seq = s; - } else if (attr.name() == "workarea") { - s->using_workarea = (attr.value() == "1"); - } else if (attr.name() == "workareaIn") { - s->workarea_in = attr.value().toLong(); - } else if (attr.name() == "workareaOut") { - s->workarea_out = attr.value().toLong(); - } - } + // load attributes about sequence + for (int j=0;jname = attr.value().toString(); + } else if (attr.name() == "folder") { + int folder = attr.value().toInt(); + if (folder > 0) parent = find_loaded_folder_by_id(folder); + } else if (attr.name() == "id") { + s->save_id = attr.value().toInt(); + } else if (attr.name() == "width") { + s->width = attr.value().toInt(); + } else if (attr.name() == "height") { + s->height = attr.value().toInt(); + } else if (attr.name() == "framerate") { + s->frame_rate = attr.value().toDouble(); + } else if (attr.name() == "afreq") { + s->audio_frequency = attr.value().toInt(); + } else if (attr.name() == "alayout") { + s->audio_layout = attr.value().toInt(); + } else if (attr.name() == "open") { + open_seq = s; + } else if (attr.name() == "workarea") { + s->using_workarea = (attr.value() == "1"); + } else if (attr.name() == "workareaIn") { + s->workarea_in = attr.value().toLong(); + } else if (attr.name() == "workareaOut") { + s->workarea_out = attr.value().toLong(); + } + } - QVector transition_data; + QVector transition_data; - // load all clips and clip information - while (!cancelled && !(stream.name() == child_search && stream.isEndElement()) && !stream.atEnd()) { - read_next_start_element(stream); - if (stream.name() == "marker" && stream.isStartElement()) { - Marker m; - for (int j=0;jmarkers.append(m); - } else if (stream.name() == "transition" && stream.isStartElement()) { - TransitionData td; - td.otc = NULL; - td.ctc = NULL; - for (int j=0;jmarkers.append(m); + } else if (stream.name() == "transition" && stream.isStartElement()) { + TransitionData td; + td.otc = NULL; + td.ctc = NULL; + for (int j=0;jautoscale = false; + // backwards compatibility code + c->autoscale = false; - c->media = NULL; + c->media = NULL; - for (int j=0;jname = attr.value().toString(); - } else if (attr.name() == "enabled") { - c->enabled = (attr.value() == "1"); - } else if (attr.name() == "id") { - c->load_id = attr.value().toInt(); - } else if (attr.name() == "clipin") { - c->clip_in = attr.value().toLong(); - } else if (attr.name() == "in") { - c->timeline_in = attr.value().toLong(); - } else if (attr.name() == "out") { - c->timeline_out = attr.value().toLong(); - } else if (attr.name() == "track") { - c->track = attr.value().toInt(); - } else if (attr.name() == "r") { - c->color_r = attr.value().toInt(); - } else if (attr.name() == "g") { - c->color_g = attr.value().toInt(); - } else if (attr.name() == "b") { - c->color_b = attr.value().toInt(); - } else if (attr.name() == "autoscale") { - c->autoscale = (attr.value() == "1"); - } else if (attr.name() == "media") { - media_type = MEDIA_TYPE_FOOTAGE; - media_id = attr.value().toInt(); - } else if (attr.name() == "stream") { - stream_id = attr.value().toInt(); - } else if (attr.name() == "speed") { - c->speed = attr.value().toDouble(); - } else if (attr.name() == "maintainpitch") { - c->maintain_audio_pitch = (attr.value() == "1"); - } else if (attr.name() == "reverse") { - c->reverse = (attr.value() == "1"); - } else if (attr.name() == "opening") { - c->opening_transition = attr.value().toInt(); - } else if (attr.name() == "closing") { - c->closing_transition = attr.value().toInt(); - } else if (attr.name() == "sequence") { - media_type = MEDIA_TYPE_SEQUENCE; + for (int j=0;jname = attr.value().toString(); + } else if (attr.name() == "enabled") { + c->enabled = (attr.value() == "1"); + } else if (attr.name() == "id") { + c->load_id = attr.value().toInt(); + } else if (attr.name() == "clipin") { + c->clip_in = attr.value().toLong(); + } else if (attr.name() == "in") { + c->timeline_in = attr.value().toLong(); + } else if (attr.name() == "out") { + c->timeline_out = attr.value().toLong(); + } else if (attr.name() == "track") { + c->track = attr.value().toInt(); + } else if (attr.name() == "r") { + c->color_r = attr.value().toInt(); + } else if (attr.name() == "g") { + c->color_g = attr.value().toInt(); + } else if (attr.name() == "b") { + c->color_b = attr.value().toInt(); + } else if (attr.name() == "autoscale") { + c->autoscale = (attr.value() == "1"); + } else if (attr.name() == "media") { + media_type = MEDIA_TYPE_FOOTAGE; + media_id = attr.value().toInt(); + } else if (attr.name() == "stream") { + stream_id = attr.value().toInt(); + } else if (attr.name() == "speed") { + c->speed = attr.value().toDouble(); + } else if (attr.name() == "maintainpitch") { + c->maintain_audio_pitch = (attr.value() == "1"); + } else if (attr.name() == "reverse") { + c->reverse = (attr.value() == "1"); + } else if (attr.name() == "opening") { + c->opening_transition = attr.value().toInt(); + } else if (attr.name() == "closing") { + c->closing_transition = attr.value().toInt(); + } else if (attr.name() == "sequence") { + media_type = MEDIA_TYPE_SEQUENCE; - // since we haven't finished loading sequences, we defer linking this until later - c->media = NULL; - c->media_stream = attr.value().toInt(); - loaded_clips.append(c); - } - } + // since we haven't finished loading sequences, we defer linking this until later + c->media = NULL; + c->media_stream = attr.value().toInt(); + loaded_clips.append(c); + } + } - // set media and media stream - switch (media_type) { - case MEDIA_TYPE_FOOTAGE: - if (media_id >= 0) { - for (int j=0;jto_footage(); - if (m->save_id == media_id) { - c->media = loaded_media_items.at(j); - c->media_stream = stream_id; - break; - } - } - } - break; - } + // set media and media stream + switch (media_type) { + case MEDIA_TYPE_FOOTAGE: + if (media_id >= 0) { + for (int j=0;jto_footage(); + if (m->save_id == media_id) { + c->media = loaded_media_items.at(j); + c->media_stream = stream_id; + break; + } + } + } + break; + } - // load links and effects - while (!cancelled && !(stream.name() == "clip" && stream.isEndElement()) && !stream.atEnd()) { - read_next(stream); - if (stream.isStartElement()) { - if (stream.name() == "linked") { - while (!cancelled && !(stream.name() == "linked" && stream.isEndElement()) && !stream.atEnd()) { - read_next(stream); - if (stream.name() == "link" && stream.isStartElement()) { - for (int k=0;klinked.append(link_attr.value().toInt()); - break; - } - } - } - } - if (cancelled) return false; - } else if (stream.isStartElement() && (stream.name() == "effect" || stream.name() == "opening" || stream.name() == "closing")) { - // "opening" and "closing" are backwards compatibility code - load_effect(stream, c); - } - } - } - if (cancelled) return false; + // load links and effects + while (!cancelled && !(stream.name() == "clip" && stream.isEndElement()) && !stream.atEnd()) { + read_next(stream); + if (stream.isStartElement()) { + if (stream.name() == "linked") { + while (!cancelled && !(stream.name() == "linked" && stream.isEndElement()) && !stream.atEnd()) { + read_next(stream); + if (stream.name() == "link" && stream.isStartElement()) { + for (int k=0;klinked.append(link_attr.value().toInt()); + break; + } + } + } + } + if (cancelled) return false; + } else if (stream.isStartElement() && (stream.name() == "effect" || stream.name() == "opening" || stream.name() == "closing")) { + // "opening" and "closing" are backwards compatibility code + load_effect(stream, c); + } + } + } + if (cancelled) return false; - s->clips.append(c); - } - } - if (cancelled) return false; + s->clips.append(c); + } + } + if (cancelled) return false; - // correct links, clip IDs, transitions - for (int i=0;iclips.size();i++) { - // correct links - Clip* correct_clip = s->clips.at(i); - for (int j=0;jlinked.size();j++) { - bool found = false; - for (int k=0;kclips.size();k++) { - if (s->clips.at(k)->load_id == correct_clip->linked.at(j)) { - correct_clip->linked[j] = k; - found = true; - break; - } - } - if (!found) { - correct_clip->linked.removeAt(j); - j--; - if (QMessageBox::warning(mainWindow, "Invalid Clip Link", "This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) { - delete s; - return false; - } - } - } + // correct links, clip IDs, transitions + for (int i=0;iclips.size();i++) { + // correct links + Clip* correct_clip = s->clips.at(i); + for (int j=0;jlinked.size();j++) { + bool found = false; + for (int k=0;kclips.size();k++) { + if (s->clips.at(k)->load_id == correct_clip->linked.at(j)) { + correct_clip->linked[j] = k; + found = true; + break; + } + } + if (!found) { + correct_clip->linked.removeAt(j); + j--; + if (QMessageBox::warning(mainWindow, "Invalid Clip Link", "This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) { + delete s; + return false; + } + } + } - // re-link clips to transitions - if (correct_clip->opening_transition > -1) { - for (int j=0;jopening_transition) { - transition_data[j].otc = correct_clip; - } - } - } - if (correct_clip->closing_transition > -1) { - for (int j=0;jclosing_transition) { - transition_data[j].ctc = correct_clip; - } - } - } - } + // re-link clips to transitions + if (correct_clip->opening_transition > -1) { + for (int j=0;jopening_transition) { + transition_data[j].otc = correct_clip; + } + } + } + if (correct_clip->closing_transition > -1) { + for (int j=0;jclosing_transition) { + transition_data[j].ctc = correct_clip; + } + } + } + } - // create transitions - for (int i=0;iopening_transition = -1; - if (td.ctc != NULL) td.ctc->closing_transition = -1; + // create transitions + for (int i=0;iopening_transition = -1; + if (td.ctc != NULL) td.ctc->closing_transition = -1; } else { emit start_create_dual_transition(&td, primary, secondary, meta); waitCond.wait(&mutex); - } - } - } + } + } + } - Media* m = panel_project->new_sequence(NULL, s, false, parent); + Media* m = panel_project->new_sequence(NULL, s, false, parent); - loaded_sequences.append(m); - } - break; - } - } - } - if (cancelled) return false; - } - break; - } - } - return !cancelled; + loaded_sequences.append(m); + } + break; + } + } + } + if (cancelled) return false; + } + break; + } + } + return !cancelled; } Media* LoadThread::find_loaded_folder_by_id(int id) { - if (id == 0) return NULL; - for (int j=0;jtemp_id == id) { - return parent_item; - } - } - return NULL; + if (id == 0) return NULL; + for (int j=0;jtemp_id == id) { + return parent_item; + } + } + return NULL; } void LoadThread::run() { - mutex.lock(); + mutex.lock(); - QFile file(project_url); - if (!file.open(QIODevice::ReadOnly)) { - dout << "[ERROR] Could not open file"; - return; - } + QFile file(project_url); + if (!file.open(QIODevice::ReadOnly)) { + dout << "[ERROR] Could not open file"; + return; + } - /* set up directories to search for media - * most of the time, these will be the same but in - * case the project file has moved without the footage, - * we check both - */ - proj_dir = QFileInfo(project_url).absoluteDir(); - internal_proj_dir = QFileInfo(project_url).absoluteDir(); - internal_proj_url = project_url; + /* set up directories to search for media + * most of the time, these will be the same but in + * case the project file has moved without the footage, + * we check both + */ + proj_dir = QFileInfo(project_url).absoluteDir(); + internal_proj_dir = QFileInfo(project_url).absoluteDir(); + internal_proj_url = project_url; - QXmlStreamReader stream(&file); + QXmlStreamReader stream(&file); - bool cont = false; - error_str.clear(); - show_err = true; + bool cont = false; + error_str.clear(); + show_err = true; - // temp variables for loading (unnecessary?) - open_seq = NULL; - loaded_folders.clear(); - loaded_media_items.clear(); - loaded_clips.clear(); - loaded_sequences.clear(); + // temp variables for loading (unnecessary?) + open_seq = NULL; + loaded_folders.clear(); + loaded_media_items.clear(); + loaded_clips.clear(); + loaded_sequences.clear(); - // get "element" count - current_element_count = 0; - total_element_count = 0; - while (!cancelled && !stream.atEnd()) { - stream.readNextStartElement(); - if (is_element(stream)) { - total_element_count++; - } - } - cont = !cancelled; + // get "element" count + current_element_count = 0; + total_element_count = 0; + while (!cancelled && !stream.atEnd()) { + stream.readNextStartElement(); + if (is_element(stream)) { + total_element_count++; + } + } + cont = !cancelled; - // find project file version - cont = load_worker(file, stream, LOAD_TYPE_VERSION); + // find project file version + cont = load_worker(file, stream, LOAD_TYPE_VERSION); - // find project's internal URL + // find project's internal URL cont = load_worker(file, stream, LOAD_TYPE_URL); - // load folders first - if (cont) { - cont = load_worker(file, stream, MEDIA_TYPE_FOLDER); - } + // load folders first + if (cont) { + cont = load_worker(file, stream, MEDIA_TYPE_FOLDER); + } - // load media - if (cont) { - // since folders loaded correctly, organize them appropriately - for (int i=0;itemp_id2; - project_model.appendChild(find_loaded_folder_by_id(parent), folder); - } + // load media + if (cont) { + // since folders loaded correctly, organize them appropriately + for (int i=0;itemp_id2; + project_model.appendChild(find_loaded_folder_by_id(parent), folder); + } - cont = load_worker(file, stream, MEDIA_TYPE_FOOTAGE); - } + cont = load_worker(file, stream, MEDIA_TYPE_FOOTAGE); + } - // load sequences - if (cont) { - cont = load_worker(file, stream, MEDIA_TYPE_SEQUENCE); - } + // load sequences + if (cont) { + cont = load_worker(file, stream, MEDIA_TYPE_SEQUENCE); + } - if (!cancelled) { - if (!cont) { + if (!cancelled) { + if (!cont) { xml_error = false; if (show_err) emit error(); - } else if (stream.hasError()) { + } else if (stream.hasError()) { error_str = stream.errorString(); xml_error = true; emit error(); - cont = false; + cont = false; - } else { - // attach nested sequence clips to their sequences - for (int i=0;imedia == NULL && loaded_clips.at(i)->media_stream == loaded_sequences.at(j)->to_sequence()->save_id) { - loaded_clips.at(i)->media = loaded_sequences.at(j); - loaded_clips.at(i)->refresh(); - break; - } - } - } - } - } - - if (cont) { - emit success(); // run in main thread - - for (int i=0;istart_preview_generator(loaded_media_items.at(i), true); - } + } else { + // attach nested sequence clips to their sequences + for (int i=0;imedia == NULL && loaded_clips.at(i)->media_stream == loaded_sequences.at(j)->to_sequence()->save_id) { + loaded_clips.at(i)->media = loaded_sequences.at(j); + loaded_clips.at(i)->refresh(); + break; + } + } + } + } } - file.close(); + if (cont) { + emit success(); // run in main thread - mutex.unlock(); + for (int i=0;istart_preview_generator(loaded_media_items.at(i), true); + } + } + + file.close(); + + mutex.unlock(); } void LoadThread::cancel() { - waitCond.wakeAll(); + waitCond.wakeAll(); cancelled = true; } @@ -663,64 +665,64 @@ void LoadThread::success_func() { counter++; } mainWindow->updateTitle(orig_filename); - } else { - panel_project->add_recent_project(project_url); - } + } else { + panel_project->add_recent_project(project_url); + } - mainWindow->setWindowModified(autorecovery); - if (open_seq != NULL) set_sequence(open_seq); - update_ui(false); + mainWindow->setWindowModified(autorecovery); + if (open_seq != NULL) set_sequence(open_seq); + update_ui(false); } void LoadThread::create_effect_ui( - QXmlStreamReader* stream, - Clip* c, - int type, - const EffectMeta* meta, - long effect_length, - bool effect_enabled) + QXmlStreamReader* stream, + Clip* c, + int type, + const EffectMeta* meta, + long effect_length, + bool effect_enabled) { - /* This is extremely hacky - prepare yourself. - * - * When moving project loading to a separate thread, it was soon discovered - * that effects wouldn't load correctly anymore. They were actually still - * "functional", but there were no controls appearing in EffectControls. - * - * Turns out since Effect creates its UI in its constructor, the UI was - * created in this thread rather than the main GUI thread, which is a big - * no-no. Unfortunately the design of Effect does not separate UI and data, - * so having the UI set up was integral to creating annd loading the effect. - * - * Therefore, rather than rewrite the class (I just rewrote QTreeWidget to - * QTreeView with a custom model/item so I'm exhausted), for - * quick-n-dirty-ness, I made LoadThread offload the effect creation to the - * main thread (and since the effect loads data from the same XML stream, - * the LoadThread has to wait for the effect to finish before it can - * continue. - * - * Sorry. I'll fix it one day. - */ + /* This is extremely hacky - prepare yourself. + * + * When moving project loading to a separate thread, it was soon discovered + * that effects wouldn't load correctly anymore. They were actually still + * "functional", but there were no controls appearing in EffectControls. + * + * Turns out since Effect creates its UI in its constructor, the UI was + * created in this thread rather than the main GUI thread, which is a big + * no-no. Unfortunately the design of Effect does not separate UI and data, + * so having the UI set up was integral to creating annd loading the effect. + * + * Therefore, rather than rewrite the class (I just rewrote QTreeWidget to + * QTreeView with a custom model/item so I'm exhausted), for + * quick-n-dirty-ness, I made LoadThread offload the effect creation to the + * main thread (and since the effect loads data from the same XML stream, + * the LoadThread has to wait for the effect to finish before it can + * continue. + * + * Sorry. I'll fix it one day. + */ - if (cancelled) return; - if (type == TA_NO_TRANSITION) { - Effect* e = create_effect(c, meta); - e->set_enabled(effect_enabled); - e->load(*stream); + if (cancelled) return; + if (type == TA_NO_TRANSITION) { + Effect* e = create_effect(c, meta); + e->set_enabled(effect_enabled); + e->load(*stream); - c->effects.append(e); - } else { - int transition_index = create_transition(c, NULL, meta); - Transition* t = c->sequence->transitions.at(transition_index); - if (effect_length > -1) t->set_length(effect_length); - t->set_enabled(effect_enabled); - t->load(*stream); + c->effects.append(e); + } else { + int transition_index = create_transition(c, NULL, meta); + Transition* t = c->sequence->transitions.at(transition_index); + if (effect_length > -1) t->set_length(effect_length); + t->set_enabled(effect_enabled); + t->load(*stream); - if (type == TA_OPENING_TRANSITION) { - c->opening_transition = transition_index; - } else { - c->closing_transition = transition_index; - } - } + if (type == TA_OPENING_TRANSITION) { + c->opening_transition = transition_index; + } else { + c->closing_transition = transition_index; + } + } waitCond.wakeAll(); } diff --git a/panels/project.cpp b/panels/project.cpp index 3c924b933..b2ee221b2 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -852,6 +852,7 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, stream.writeAttribute("using_inout", QString::number(f->using_inout)); stream.writeAttribute("in", QString::number(f->in)); stream.writeAttribute("out", QString::number(f->out)); + stream.writeAttribute("speed", QString::number(f->speed)); for (int j=0;jvideo_tracks.size();j++) { const FootageStream& ms = f->video_tracks.at(j); stream.writeStartElement("video"); From 1398a54cf4131f8cb05df3b87807e88ea8c8111d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 6 Jan 2019 21:06:31 +1100 Subject: [PATCH 61/65] added optional toolbar --- io/config.cpp | 10 ++++++- io/config.h | 1 + panels/project.cpp | 58 +++++++++++++++++++++++++++++++++++++++ panels/project.h | 3 ++ project/sourcescommon.cpp | 5 ++++ 5 files changed, 76 insertions(+), 1 deletion(-) diff --git a/io/config.cpp b/io/config.cpp index 0e1c14028..e36e7b763 100644 --- a/io/config.cpp +++ b/io/config.cpp @@ -4,6 +4,9 @@ #include #include +#include "panels/project.h" +#include "panels/panels.h" + #include "debug.h" Config config; @@ -34,7 +37,8 @@ Config::Config() fast_seeking(false), hover_focus(false), project_view_type(PROJECT_VIEW_TREE), - set_name_with_marker(true) + set_name_with_marker(true), + show_project_toolbar(false) {} void Config::load(QString path) { @@ -123,6 +127,9 @@ void Config::load(QString path) { } else if (stream.name() == "SetNameWithMarker") { stream.readNext(); set_name_with_marker = (stream.text() == "1"); + } else if (stream.name() == "ShowProjectToolbar") { + stream.readNext(); + show_project_toolbar = (stream.text() == "1"); } } } @@ -173,6 +180,7 @@ void Config::save(QString path) { stream.writeTextElement("HoverFocus", QString::number(hover_focus)); stream.writeTextElement("ProjectViewType", QString::number(project_view_type)); stream.writeTextElement("SetNameWithMarker", QString::number(set_name_with_marker)); + stream.writeTextElement("ShowProjectToolbar", QString::number(panel_project->toolbar_widget->isVisible())); stream.writeEndElement(); // configuration stream.writeEndDocument(); // doc diff --git a/io/config.h b/io/config.h index 394710270..312ba5c84 100644 --- a/io/config.h +++ b/io/config.h @@ -49,6 +49,7 @@ struct Config { bool hover_focus; int project_view_type; bool set_name_with_marker; + bool show_project_toolbar; void load(QString path); void save(QString path); diff --git a/panels/project.cpp b/panels/project.cpp index b2ee221b2..0c245f316 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -42,6 +42,7 @@ #include #include #include +#include extern "C" { #include @@ -74,11 +75,62 @@ Project::Project(QWidget *parent) : sorter = new QSortFilterProxyModel(this); sorter->setSourceModel(&project_model); + // optional toolbar + toolbar_widget = new QWidget(); + toolbar_widget->setVisible(config.show_project_toolbar); + QHBoxLayout* toolbar = new QHBoxLayout(); + toolbar->setMargin(0); + toolbar->setSpacing(0); + toolbar_widget->setLayout(toolbar); + + QPushButton* toolbar_new = new QPushButton("New"); + toolbar_new->setIcon(QIcon(":/icons/tri-down.png")); + toolbar_new->setIconSize(QSize(8, 8)); + toolbar_new->setToolTip("New"); + connect(toolbar_new, SIGNAL(clicked(bool)), this, SLOT(make_new_menu())); + toolbar->addWidget(toolbar_new); + + QPushButton* toolbar_open = new QPushButton("Open"); + toolbar_open->setToolTip("Open Project"); + connect(toolbar_open, SIGNAL(clicked(bool)), mainWindow, SLOT(open_project())); + toolbar->addWidget(toolbar_open); + + QPushButton* toolbar_save = new QPushButton("Save"); + toolbar_save->setToolTip("Save Project"); + connect(toolbar_save, SIGNAL(clicked(bool)), mainWindow, SLOT(save_project())); + toolbar->addWidget(toolbar_save); + + QPushButton* toolbar_undo = new QPushButton("Undo"); + toolbar_undo->setToolTip("Undo"); + connect(toolbar_undo, SIGNAL(clicked(bool)), mainWindow, SLOT(undo())); + toolbar->addWidget(toolbar_undo); + + QPushButton* toolbar_redo = new QPushButton("Redo"); + toolbar_redo->setToolTip("Redo"); + connect(toolbar_redo, SIGNAL(clicked(bool)), mainWindow, SLOT(redo())); + toolbar->addWidget(toolbar_redo); + + toolbar->addStretch(); + + QPushButton* toolbar_tree_view = new QPushButton("Tree View"); + toolbar_tree_view->setToolTip("Tree View"); + connect(toolbar_tree_view, SIGNAL(clicked(bool)), this, SLOT(set_tree_view())); + toolbar->addWidget(toolbar_tree_view); + + QPushButton* toolbar_icon_view = new QPushButton("Icon View"); + toolbar_icon_view->setToolTip("Icon View"); + connect(toolbar_icon_view, SIGNAL(clicked(bool)), this, SLOT(set_icon_view())); + toolbar->addWidget(toolbar_icon_view); + + verticalLayout->addWidget(toolbar_widget); + + // tree view tree_view = new SourceTable(dockWidgetContents); tree_view->project_parent = this; tree_view->setModel(sorter); verticalLayout->addWidget(tree_view); + // icon view icon_view_container = new QWidget(); QVBoxLayout* icon_view_container_layout = new QVBoxLayout(); @@ -1087,6 +1139,12 @@ void Project::go_up_dir() { set_up_dir_enabled(); } +void Project::make_new_menu() { + QMenu new_menu(this); + mainWindow->make_new_menu(&new_menu); + new_menu.exec(QCursor::pos()); +} + void Project::add_recent_project(QString url) { bool found = false; for (int i=0;i &items, QList &list, int type = -1); + + QWidget* toolbar_widget; public slots: void import_dialog(); void delete_selected_media(); @@ -103,6 +105,7 @@ private slots: void set_icon_view_size(int); void set_up_dir_enabled(); void go_up_dir(); + void make_new_menu(); }; class MediaThrobber : public QObject { diff --git a/project/sourcescommon.cpp b/project/sourcescommon.cpp index b5272c8cd..0dd870944 100644 --- a/project/sourcescommon.cpp +++ b/project/sourcescommon.cpp @@ -127,6 +127,11 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it QAction* icon_view_action = menu.addAction("Icon View"); connect(icon_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_icon_view())); + QAction* toolbar_action = menu.addAction("Show Toolbar"); + toolbar_action->setCheckable(true); + toolbar_action->setChecked(project_parent->toolbar_widget->isVisible()); + connect(toolbar_action, SIGNAL(triggered(bool)), project_parent->toolbar_widget, SLOT(setVisible(bool))); + menu.exec(QCursor::pos()); } From 25c621a09c59a712e4845716434fd2f399030d71 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 6 Jan 2019 21:16:22 +1100 Subject: [PATCH 62/65] moved missing file --- {icons => packaging/windows}/version.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {icons => packaging/windows}/version.h (100%) diff --git a/icons/version.h b/packaging/windows/version.h similarity index 100% rename from icons/version.h rename to packaging/windows/version.h From d46252b823fb40e317e0857c9d88b9bf63df2c54 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 7 Jan 2019 09:14:48 +1100 Subject: [PATCH 63/65] added search to keyboard shortcuts --- dialogs/preferencesdialog.cpp | 46 +++++++++++++++++++++++++++++++++++ dialogs/preferencesdialog.h | 1 + 2 files changed, 47 insertions(+) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 0eec771e9..d558f1a12 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -113,6 +113,46 @@ void PreferencesDialog::reset_default_shortcut() { } } +bool PreferencesDialog::refine_shortcut_list(const QString &s, QTreeWidgetItem* parent) { + if (parent == NULL) { + for (int i=0;itopLevelItemCount();i++) { + refine_shortcut_list(s, keyboard_tree->topLevelItem(i)); + } + } else { + parent->setExpanded(!s.isEmpty()); + + bool all_children_are_hidden = !s.isEmpty(); + + for (int i=0;ichildCount();i++) { + QTreeWidgetItem* item = parent->child(i); + if (item->childCount() > 0) { + all_children_are_hidden = refine_shortcut_list(s, item); + } else { + item->setHidden(false); + if (s.isEmpty()) { + all_children_are_hidden = false; + } else { + QString shortcut; + if (keyboard_tree->itemWidget(item, 1) != NULL) { + shortcut = static_cast(keyboard_tree->itemWidget(item, 1))->keySequence().toString(); + } + if (item->text(0).contains(s, Qt::CaseInsensitive) || shortcut.contains(s, Qt::CaseInsensitive)) { + all_children_are_hidden = false; + } else { + item->setHidden(true); + } + } + } + } + + if (parent->text(0).contains(s, Qt::CaseInsensitive)) all_children_are_hidden = false; + + parent->setHidden(all_children_are_hidden); + + return all_children_are_hidden; + } +} + void PreferencesDialog::setup_ui() { QVBoxLayout* verticalLayout = new QVBoxLayout(this); QTabWidget* tabWidget = new QTabWidget(this); @@ -160,6 +200,12 @@ void PreferencesDialog::setup_ui() { QVBoxLayout* shortcut_layout = new QVBoxLayout(shortcut_tab); + QLineEdit* key_search_line = new QLineEdit(); + key_search_line->setPlaceholderText("Search for action or shortcut"); + connect(key_search_line, SIGNAL(textChanged(const QString &)), this, SLOT(refine_shortcut_list(const QString &))); + + shortcut_layout->addWidget(key_search_line); + keyboard_tree = new QTreeWidget(); QTreeWidgetItem* tree_header = keyboard_tree->headerItem(); tree_header->setText(0, "Action"); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 510b5dc5a..9e3031bc5 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -33,6 +33,7 @@ public: private slots: void save(); void reset_default_shortcut(); + bool refine_shortcut_list(const QString &, QTreeWidgetItem* parent = NULL); private: void setup_ui(); From c4a590a704b183833fc150eb6684c08e5cac3f00 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 7 Jan 2019 09:16:20 +1100 Subject: [PATCH 64/65] made vector initialize more compliant --- mainwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 53b4bfec4..09cc827c8 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1298,7 +1298,8 @@ void MainWindow::nest() { Media* m = panel_project->new_sequence(ca, s, false, NULL); // add nested sequence to active sequence - QVector media_list = {m}; + QVector media_list; + media_list.append(m); panel_timeline->create_ghosts_from_media(sequence, earliest_point, media_list); panel_timeline->add_clips_from_ghosts(ca, sequence); From 42ebd48a93151ef406fbfefe7116e66326dbe1d9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 7 Jan 2019 09:21:18 +1100 Subject: [PATCH 65/65] disabled nonfunctional mask effect --- project/effect.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/effect.cpp b/project/effect.cpp index 0315e1c4f..3b5baef6a 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -111,9 +111,9 @@ void load_internal_effects() { em.internal = EFFECT_INTERNAL_CORNERPIN; effects.append(em); - em.name = "Mask"; + /*em.name = "Mask"; em.internal = EFFECT_INTERNAL_MASK; - effects.append(em); + effects.append(em);*/ em.name = "Shake"; em.internal = EFFECT_INTERNAL_SHAKE;