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";
}
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 738e7c9b6..f8be11aff 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,31 +58,28 @@ 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;
-
- /*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);
+ 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();
@@ -194,12 +192,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 +252,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();
}
@@ -673,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() {
@@ -1075,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/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..4128b2474
--- /dev/null
+++ b/panels/grapheditor.cpp
@@ -0,0 +1,48 @@
+#include "grapheditor.h"
+
+#include
+#include
+#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);
+
+ 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!"));
+ 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->addWidget(value_widget);
+}
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..be40e402b 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,32 @@ 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_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() {
+ 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/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/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..7b1bad15f
--- /dev/null
+++ b/ui/graphview.cpp
@@ -0,0 +1,16 @@
+#include "graphview.h"
+
+#include
+
+GraphView::GraphView(QWidget* parent) {}
+
+void GraphView::paintEvent(QPaintEvent *event) {
+ QPainter p(this);
+
+ p.setPen(Qt::white);
+
+ QRect border = rect();
+ border.setWidth(border.width()-1);
+ border.setHeight(border.height()-1);
+ p.drawRect(border);
+}
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;
}
}