From b7f1fae0289a10b5d21bd0696106ce643e655396 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 9 Jan 2019 20:40:45 +1100 Subject: [PATCH 01/14] wrote handler for #281 --- effects/internal/voideffect.cpp | 56 +++++ effects/internal/voideffect.h | 21 ++ io/loadthread.cpp | 72 +++--- io/loadthread.h | 4 +- olive.pro | 6 +- panels/effectcontrols.cpp | 1 - panels/effectcontrols.h | 3 + panels/panels.cpp | 1 + project/effect.cpp | 373 ++++++++++++++++---------------- project/effect.h | 4 +- project/effectrow.cpp | 4 +- 11 files changed, 308 insertions(+), 237 deletions(-) create mode 100644 effects/internal/voideffect.cpp create mode 100644 effects/internal/voideffect.h diff --git a/effects/internal/voideffect.cpp b/effects/internal/voideffect.cpp new file mode 100644 index 000000000..eee3b4e87 --- /dev/null +++ b/effects/internal/voideffect.cpp @@ -0,0 +1,56 @@ +#include "voideffect.h" + +#include +#include + +#include "ui/collapsiblewidget.h" +#include "debug.h" + +VoidEffect::VoidEffect(Clip *c, const QString& n) : Effect(c, NULL) { + name = n; + QString display_name; + if (n.isEmpty()) { + display_name = "(unknown)"; + } else { + display_name = n; + } + EffectRow* row = add_row("Missing Effect", false, false); + row->add_widget(new QLabel(display_name)); + container->setText(display_name); +} + +void VoidEffect::load(QXmlStreamReader &stream) { + QString tag = stream.name().toString(); + qint64 start_index = stream.characterOffset(); + qint64 end_index = start_index; + while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) { + end_index = stream.characterOffset(); + stream.readNext(); + } + qint64 passage_length = end_index - start_index; + if (passage_length > 0) { + // store xml data verbatim + QIODevice* device = stream.device(); + device->seek(start_index); + bytes = device->read(passage_length); + int passage_end = bytes.lastIndexOf('>')+1; + bytes.remove(passage_end, bytes.size()-passage_end); + } +} + +void VoidEffect::save(QXmlStreamWriter &stream) { + if (!name.isEmpty()) { + stream.writeAttribute("name", name); + stream.writeAttribute("enabled", QString::number(is_enabled())); + + // force xml writer to expand tag, ignored when loading + stream.writeStartElement("void"); + stream.writeEndElement(); + + if (!bytes.isEmpty()) { + // write stored data + QIODevice* device = stream.device(); + device->write(bytes); + } + } +} diff --git a/effects/internal/voideffect.h b/effects/internal/voideffect.h new file mode 100644 index 000000000..340030998 --- /dev/null +++ b/effects/internal/voideffect.h @@ -0,0 +1,21 @@ +#ifndef VOIDEFFECT_H +#define VOIDEFFECT_H + +/* VoidEffect is a placeholder used when Olive is unable to find an effect + * requested by a loaded project. It displays a missing effect so the user knows + * an effect is missing, and stores the XML project data verbatim so that it + * isn't lost if the user saves over the project. + */ + +#include "project/effect.h" + +class VoidEffect : public Effect { +public: + VoidEffect(Clip* c, const QString& n); + void load(QXmlStreamReader &stream) override; + void save(QXmlStreamWriter &stream) override; +private: + QByteArray bytes; +}; + +#endif // VOIDEFFECT_H diff --git a/io/loadthread.cpp b/io/loadthread.cpp index e343369d8..71fa7035a 100644 --- a/io/loadthread.cpp +++ b/io/loadthread.cpp @@ -2,6 +2,7 @@ #include "mainwindow.h" #include "panels/panels.h" +#include "panels/effectcontrols.h" #include "panels/project.h" #include "project/footage.h" #include "io/config.h" @@ -13,6 +14,7 @@ #include "io/previewgenerator.h" #include "dialogs/loaddialog.h" #include "project/media.h" +#include "effects/internal/voideffect.h" #include "debug.h" #include @@ -32,7 +34,7 @@ LoadThread::LoadThread(LoadDialog* l, bool a) : ld(l), autorecovery(a), cancelle 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 QString*, const EffectMeta*, long, bool)), this, SLOT(create_effect_ui(QXmlStreamReader*, Clip*, int, const QString*, const EffectMeta*, long, bool))); } const EffectMeta* get_meta_from_name(const QString& name) { @@ -60,28 +62,10 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { } else if (attr.name() == "length") { effect_length = attr.value().toLong(); } - } - - // 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(); + panel_effect_controls->effects_loaded.lock(); const EffectMeta* meta = NULL; @@ -90,26 +74,21 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { meta = get_meta_from_name(effect_name); } - effects_loaded.unlock(); + panel_effect_controls->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(); + 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); - - waitCond.wait(&mutex); - } + emit start_create_effect_ui(&stream, c, type, &effect_name, meta, effect_length, effect_enabled); + waitCond.wait(&mutex); } void LoadThread::read_next(QXmlStreamReader &stream) { @@ -678,6 +657,7 @@ void LoadThread::create_effect_ui( QXmlStreamReader* stream, Clip* c, int type, + const QString* effect_name, const EffectMeta* meta, long effect_length, bool effect_enabled) @@ -705,11 +685,19 @@ void LoadThread::create_effect_ui( if (cancelled) return; if (type == TA_NO_TRANSITION) { - Effect* e = create_effect(c, meta); - e->set_enabled(effect_enabled); - e->load(*stream); + if (meta == NULL) { + // create void effect + VoidEffect* ve = new VoidEffect(c, *effect_name); + ve->set_enabled(effect_enabled); + ve->load(*stream); + c->effects.append(ve); + } else { + Effect* e = create_effect(c, meta); + e->set_enabled(effect_enabled); + e->load(*stream); - c->effects.append(e); + c->effects.append(e); + } } else { int transition_index = create_transition(c, NULL, meta); Transition* t = c->sequence->transitions.at(transition_index); diff --git a/io/loadthread.h b/io/loadthread.h index 572e7b8d4..e3ae55fe3 100644 --- a/io/loadthread.h +++ b/io/loadthread.h @@ -25,13 +25,13 @@ public: signals: void success(); void error(); - void start_create_effect_ui(QXmlStreamReader* stream, Clip* c, int type, const EffectMeta* meta, long effect_length, bool effect_enabled); + void start_create_effect_ui(QXmlStreamReader* stream, Clip* c, int type, const QString *effect_name, const EffectMeta* meta, long effect_length, bool effect_enabled); void start_create_dual_transition(const TransitionData* td, Clip* primary, Clip* secondary, const EffectMeta* meta); void report_progress(int p); private slots: void error_func(); void success_func(); - void create_effect_ui(QXmlStreamReader* stream, Clip* c, int type, const EffectMeta* meta, long effect_length, bool effect_enabled); + void create_effect_ui(QXmlStreamReader* stream, Clip* c, int type, const QString *effect_name, const EffectMeta* meta, long effect_length, bool effect_enabled); void create_dual_transition(const TransitionData* td, Clip* primary, Clip* secondary, const EffectMeta* meta); private: LoadDialog* ld; diff --git a/olive.pro b/olive.pro index b514c823f..1f8bc78d0 100644 --- a/olive.pro +++ b/olive.pro @@ -119,7 +119,8 @@ SOURCES += \ ui/rectangleselect.cpp \ dialogs/actionsearch.cpp \ ui/embeddedfilechooser.cpp \ - effects/internal/fillleftrighteffect.cpp + effects/internal/fillleftrighteffect.cpp \ + effects/internal/voideffect.cpp HEADERS += \ mainwindow.h \ @@ -208,7 +209,8 @@ HEADERS += \ ui/rectangleselect.h \ dialogs/actionsearch.h \ ui/embeddedfilechooser.h \ - effects/internal/fillleftrighteffect.h + effects/internal/fillleftrighteffect.h \ + effects/internal/voideffect.h FORMS += diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index fc64d0083..5e58f3cdf 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -41,7 +41,6 @@ EffectControls::EffectControls(QWidget *parent) : setup_ui(); - init_effects(); clear_effects(false); headers->viewer = panel_sequence_viewer; headers->snapping = false; diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index afb9a7e26..1201e8a13 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -3,6 +3,7 @@ #include #include +#include struct Clip; class QMenu; @@ -50,6 +51,8 @@ public: ResizableScrollBar* horizontalScrollBar; QScrollBar* verticalScrollBar; + + QMutex effects_loaded; public slots: void update_keyframes(); private slots: diff --git a/panels/panels.cpp b/panels/panels.cpp index e1f57e94d..c5653a171 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -152,6 +152,7 @@ void alloc_panels(QWidget* parent) { panel_project = new Project(parent); panel_project->setObjectName("proj_root"); panel_effect_controls = new EffectControls(parent); + init_effects(); panel_effect_controls->setObjectName("fx_controls"); panel_timeline = new Timeline(parent); panel_timeline->setObjectName("timeline"); diff --git a/project/effect.cpp b/project/effect.cpp index 9a872d57f..05078c77a 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -45,7 +45,6 @@ #include QVector effects; -QMutex effects_loaded; Effect* create_effect(Clip* c, const EffectMeta* em) { if (!em->filename.isEmpty()) { @@ -94,7 +93,7 @@ void load_internal_effects() { em.name = "Volume"; em.internal = EFFECT_INTERNAL_VOLUME; - effects.append(em); + effects.append(em); em.name = "Pan"; em.internal = EFFECT_INTERNAL_PAN; @@ -245,7 +244,7 @@ void init_effects() { } EffectInit::EffectInit() { - effects_loaded.lock(); + panel_effect_controls->effects_loaded.lock(); } void EffectInit::run() { @@ -253,7 +252,7 @@ void EffectInit::run() { load_internal_effects(); load_shader_effects(); load_vst_effects(); - effects_loaded.unlock(); + panel_effect_controls->effects_loaded.unlock(); dout << "[INFO] Finished initializing effects"; } @@ -281,193 +280,195 @@ Effect::Effect(Clip* c, const EffectMeta *em) : connect(container->title_bar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); - // set up UI from effect file - container->setText(em->name); + if (em != NULL) { + // set up UI from effect file + container->setText(em->name); - if (!em->filename.isEmpty()) { - QFile effect_file(em->filename); - if (effect_file.open(QFile::ReadOnly)) { - QXmlStreamReader reader(&effect_file); + if (!em->filename.isEmpty()) { + QFile effect_file(em->filename); + if (effect_file.open(QFile::ReadOnly)) { + QXmlStreamReader reader(&effect_file); - while (!reader.atEnd()) { - if (reader.name() == "row" && reader.isStartElement()) { - QString row_name; - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename << "- ID cannot be empty."; - } else if (type > -1) { - EffectField* field = row->add_field(type, id); - connect(field, SIGNAL(changed()), this, SLOT(field_changed())); - switch (type) { - case EFFECT_FIELD_DOUBLE: - for (int i=0;iset_double_default_value(attr.value().toDouble()); - } else if (attr.name() == "min") { - field->set_double_minimum_value(attr.value().toDouble()); - } else if (attr.name() == "max") { - field->set_double_maximum_value(attr.value().toDouble()); - } - } - break; - case EFFECT_FIELD_COLOR: - { - QColor color; - for (int i=0;iset_color_value(color); - } - break; - case EFFECT_FIELD_STRING: - for (int i=0;iset_string_value(attr.value().toString()); - } - } - break; - case EFFECT_FIELD_BOOL: - for (int i=0;iset_bool_value(attr.value() == "1"); - } - } - break; - case EFFECT_FIELD_COMBO: - { - int combo_index = 0; - for (int i=0;iadd_combo_item(reader.text().toString(), 0); - } - } - field->set_combo_index(combo_index); - } - break; - case EFFECT_FIELD_FONT: - for (int i=0;iset_font_name(attr.value().toString()); - } - } - break; - case EFFECT_FIELD_FILE: - for (int i=0;iset_filename(attr.value().toString()); - } - } - break; - } - } - } - } - } - } else if (reader.name() == "shader" && reader.isStartElement()) { - enable_shader = true; - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename; - enable_superimpose = false; - } - break; - } - } - }*/ - reader.readNext(); - } + 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); + connect(field, SIGNAL(changed()), this, SLOT(field_changed())); + switch (type) { + case EFFECT_FIELD_DOUBLE: + for (int i=0;iset_double_default_value(attr.value().toDouble()); + } else if (attr.name() == "min") { + field->set_double_minimum_value(attr.value().toDouble()); + } else if (attr.name() == "max") { + field->set_double_maximum_value(attr.value().toDouble()); + } + } + break; + case EFFECT_FIELD_COLOR: + { + QColor color; + for (int i=0;iset_color_value(color); + } + break; + case EFFECT_FIELD_STRING: + for (int i=0;iset_string_value(attr.value().toString()); + } + } + break; + case EFFECT_FIELD_BOOL: + for (int i=0;iset_bool_value(attr.value() == "1"); + } + } + break; + case EFFECT_FIELD_COMBO: + { + int combo_index = 0; + for (int i=0;iadd_combo_item(reader.text().toString(), 0); + } + } + field->set_combo_index(combo_index); + } + break; + case EFFECT_FIELD_FONT: + for (int i=0;iset_font_name(attr.value().toString()); + } + } + break; + case EFFECT_FIELD_FILE: + for (int i=0;iset_filename(attr.value().toString()); + } + } + break; + } + } + } + } + } + } else if (reader.name() == "shader" && reader.isStartElement()) { + enable_shader = true; + const QXmlStreamAttributes& attributes = reader.attributes(); + for (int i=0;ifilename; + enable_superimpose = false; + } + break; + } + } + }*/ + reader.readNext(); + } - effect_file.close(); - } else { - dout << "[ERROR] Failed to open effect file" << em->filename; - } - } + effect_file.close(); + } else { + dout << "[ERROR] Failed to open effect file" << em->filename; + } + } + } } Effect::~Effect() { diff --git a/project/effect.h b/project/effect.h index f3f39e7d9..21e0f381b 100644 --- a/project/effect.h +++ b/project/effect.h @@ -41,8 +41,6 @@ void init_effects(); Effect* create_effect(Clip* c, const EffectMeta *em); const EffectMeta* get_internal_meta(int internal_id, int type); -extern QMutex effects_loaded; - #define EFFECT_TYPE_INVALID 0 #define EFFECT_TYPE_VIDEO 1 #define EFFECT_TYPE_AUDIO 2 @@ -135,7 +133,7 @@ public: Effect* copy(Clip* c); void copy_field_keyframes(Effect *e); - void load(QXmlStreamReader& stream); + virtual void load(QXmlStreamReader& stream); virtual void custom_load(QXmlStreamReader& stream); virtual void save(QXmlStreamWriter& stream); diff --git a/project/effectrow.cpp b/project/effectrow.cpp index 7f574d379..4735e8def 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -31,7 +31,9 @@ EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QSt column_count = 1; - if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION && keyframable) { + if (parent_effect->meta != NULL + && parent_effect->meta->type != EFFECT_TYPE_TRANSITION + && keyframable) { connect(label, SIGNAL(clicked()), this, SLOT(focus_row())); keyframe_nav = new KeyframeNavigator(); From 0318ad4965a35ad317609dc9d3a648cbbd5216b4 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 09:26:25 +1100 Subject: [PATCH 02/14] added loop options and middle click drag in timeline --- io/config.cpp | 14 ++++++++++++-- io/config.h | 2 ++ mainwindow.cpp | 10 ++++++++++ mainwindow.h | 2 ++ panels/viewer.cpp | 40 ++++++++++++++++++++++++++++++++-------- panels/viewer.h | 5 +++++ 6 files changed, 63 insertions(+), 10 deletions(-) diff --git a/io/config.cpp b/io/config.cpp index 1b243a0ca..fe5d30a8a 100644 --- a/io/config.cpp +++ b/io/config.cpp @@ -43,7 +43,9 @@ Config::Config() previous_queue_size(3), previous_queue_type(FRAME_QUEUE_TYPE_FRAMES), upcoming_queue_size(0.5), - upcoming_queue_type(FRAME_QUEUE_TYPE_SECONDS) + upcoming_queue_type(FRAME_QUEUE_TYPE_SECONDS), + loop(true), + pause_at_out_point(true) {} void Config::load(QString path) { @@ -150,7 +152,13 @@ void Config::load(QString path) { } else if (stream.name() == "UpcomingFrameQueueType") { stream.readNext(); upcoming_queue_type = stream.text().toInt(); - } + } else if (stream.name() == "Loop") { + stream.readNext(); + loop = (stream.text() == "1"); + } else if (stream.name() == "PauseAtOutPoint") { + stream.readNext(); + pause_at_out_point = (stream.text() == "1"); + } } } if (stream.hasError()) { @@ -206,6 +214,8 @@ void Config::save(QString path) { stream.writeTextElement("PreviousFrameQueueType", QString::number(previous_queue_type)); stream.writeTextElement("UpcomingFrameQueueSize", QString::number(upcoming_queue_size)); stream.writeTextElement("UpcomingFrameQueueType", QString::number(upcoming_queue_type)); + stream.writeTextElement("Loop", QString::number(loop)); + stream.writeTextElement("PauseAtOutPoint", QString::number(pause_at_out_point)); stream.writeEndElement(); // configuration stream.writeEndDocument(); // doc diff --git a/io/config.h b/io/config.h index f1a6e1d48..ce5a323dc 100644 --- a/io/config.h +++ b/io/config.h @@ -58,6 +58,8 @@ struct Config { int previous_queue_type; double upcoming_queue_size; int upcoming_queue_type; + bool loop; + bool pause_at_out_point; void load(QString path); void save(QString path); diff --git a/mainwindow.cpp b/mainwindow.cpp index 5b6a695fd..17e06f0c4 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -769,6 +769,14 @@ void MainWindow::setup_menus() { set_name_and_marker->setCheckable(true); set_name_and_marker->setData(reinterpret_cast(&config.set_name_with_marker)); + loop_action = tools_menu->addAction("Loop", this, SLOT(toggle_bool_action())); + loop_action->setCheckable(true); + loop_action->setData(reinterpret_cast(&config.loop)); + + pause_at_out_point_action = tools_menu->addAction("Pause At Out Point", this, SLOT(toggle_bool_action())); + pause_at_out_point_action->setCheckable(true); + pause_at_out_point_action->setData(reinterpret_cast(&config.pause_at_out_point)); + tools_menu->addSeparator(); no_autoscroll = tools_menu->addAction("No Auto-Scroll", this, SLOT(set_autoscroll())); @@ -1066,6 +1074,8 @@ void MainWindow::toolMenu_About_To_Be_Shown() { 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_bool_action_checked(loop_action); + set_bool_action_checked(pause_at_out_point_action); 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 557fc23f0..da96851fe 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -172,6 +172,8 @@ private: QAction* enable_drop_on_media_to_replace; QAction* enable_hover_focus; QAction* set_name_and_marker; + QAction* loop_action; + QAction* pause_at_out_point_action; // edit menu actions QAction* undo_action; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index af1522dc7..5cf5d6d41 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -307,6 +307,12 @@ void Viewer::play() { if (panel_footage_viewer->playing) panel_footage_viewer->pause(); if (seq != NULL) { + if (!is_recording_cued() + && seq->playhead >= get_seq_out() + && (config.loop || !main_sequence)) { + seek(get_seq_in()); + } + reset_all_audio(); if (is_recording_cued() && !start_recording()) { dout << "[ERROR] Failed to record audio"; @@ -452,7 +458,19 @@ void Viewer::set_zoom_value(double d) { } void Viewer::set_sb_max() { - headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width()); + headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width()); +} + +long Viewer::get_seq_in() { + return (seq->using_workarea) + ? seq->workarea_in + : 0; +} + +long Viewer::get_seq_out() { + return (seq->using_workarea && previous_playhead < seq->workarea_out) + ? seq->workarea_out + : seq->getEndFrame(); } void Viewer::setup_ui() { @@ -637,19 +655,25 @@ void Viewer::update_playhead() { } void Viewer::timer_update() { - long previous_playhead = seq->playhead; + previous_playhead = seq->playhead; seq->playhead = qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate)); update_parents(); - long end_frame = (seq->using_workarea && previous_playhead < seq->workarea_out) ? seq->workarea_out : seq->getEndFrame(); - if ((!recording + long end_frame = get_seq_out(); + if (!recording && playing && seq->playhead >= end_frame - && previous_playhead < end_frame) - || (recording && recording_start != recording_end && seq->playhead >= recording_end)) { - pause(); - } + && previous_playhead < end_frame) { + if (!config.pause_at_out_point && config.loop) { + seek(get_seq_in()); + play(); + } else if (config.pause_at_out_point || !main_sequence) { + pause(); + } + } else if (recording && recording_start != recording_end && seq->playhead >= recording_end) { + pause(); + } } void Viewer::recording_flasher_update() { diff --git a/panels/viewer.h b/panels/viewer.h index 624d8df8e..8cca7b7c0 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -94,6 +94,9 @@ private: void set_zoom_value(double d); void set_sb_max(); + long get_seq_in(); + long get_seq_out(); + QIcon playIcon; void setup_ui(); @@ -112,6 +115,8 @@ private: bool cue_recording_internal; QTimer recording_flasher; + + long previous_playhead; }; #endif // VIEWER_H From 342e4c765a71daad6e12741eb73a003fabe6f20e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 09:31:42 +1100 Subject: [PATCH 03/14] timeline middle click moving and more glsl debugging info --- project/effect.cpp | 26 +++++++++++++++++++------- ui/timelinewidget.cpp | 7 +++++-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/project/effect.cpp b/project/effect.cpp index 9a872d57f..fefb2e627 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -793,16 +793,28 @@ void Effect::open() { validate_meta_path(); bool glsl_compiled = true; if (!vertPath.isEmpty()) { - if (!glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath)) { - glsl_compiled = false; - } + if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath)) { + dout << "[INFO] Vertex shader added successfully"; + } else { + glsl_compiled = false; + dout << "[WARNING] Vertex shader could not be added"; + } } if (!fragPath.isEmpty()) { - if (!glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath)) { - glsl_compiled = false; - } + if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath)) { + dout << "[INFO] Fragment shader added successfully"; + } else { + glsl_compiled = false; + dout << "[WARNING] Fragment shader could not be added"; + } } - if (glsl_compiled) glslProgram->link(); + if (glsl_compiled) { + if (glslProgram->link()) { + dout << "[INFO] Shader program linked successfully"; + } else { + dout << "[WARNING] Shader program failed to link"; + } + } isOpen = true; } } else { diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index d56a36765..34896859a 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -554,7 +554,10 @@ bool isLiveEditing() { void TimelineWidget::mousePressEvent(QMouseEvent *event) { if (sequence != NULL) { int tool = panel_timeline->tool; - if (event->button() == Qt::RightButton) { + if (event->button() == Qt::MiddleButton) { + tool = TIMELINE_TOOL_HAND; + panel_timeline->creating = false; + } else if (event->button() == Qt::RightButton) { tool = TIMELINE_TOOL_MENU; panel_timeline->creating = false; } @@ -1155,12 +1158,12 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { panel_timeline->rect_select_proc = false; panel_timeline->transition_tool_init = false; panel_timeline->transition_tool_proc = false; - panel_timeline->hand_moving = false; pre_clips.clear(); post_clips.clear(); update_ui(true); } + panel_timeline->hand_moving = false; } } From 5ee3df312f7e167c817d1d36502b67045d94593f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 10:26:55 +1100 Subject: [PATCH 04/14] import/export keyboard shortcuts --- dialogs/preferencesdialog.cpp | 95 +++++++++++++++++++--- dialogs/preferencesdialog.h | 5 ++ mainwindow.cpp | 146 +++++++++++++++++++--------------- mainwindow.h | 5 +- 4 files changed, 174 insertions(+), 77 deletions(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 044ffd895..c85724086 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "debug.h" @@ -29,7 +31,23 @@ KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a) } void KeySequenceEditor::set_action_shortcut() { - action->setShortcut(keySequence()); + action->setShortcut(keySequence()); +} + +void KeySequenceEditor::reset_to_default() { + setKeySequence(action->property("default").toString()); +} + +QString KeySequenceEditor::action_name() { + return action->text().replace("&", ""); +} + +QString KeySequenceEditor::export_shortcut() { + QString ks = keySequence().toString(); + if (ks != action->property("default")) { + return action->text().replace("&", "") + "\t" + keySequence().toString(); + } + return 0; } PreferencesDialog::PreferencesDialog(QWidget *parent) : @@ -60,8 +78,7 @@ void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* if (a->menu() != NULL) { item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); setup_kbd_shortcut_worker(a->menu(), item); - } else { - item->setData(0, Qt::UserRole + 1, reinterpret_cast(a)); + } else { key_shortcut_items.append(item); key_shortcut_actions.append(a); } @@ -112,12 +129,7 @@ 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); - } + static_cast(keyboard_tree->itemWidget(item, 1))->reset_to_default(); } } @@ -158,7 +170,59 @@ bool PreferencesDialog::refine_shortcut_list(const QString &s, QTreeWidgetItem* parent->setHidden(all_children_are_hidden); return all_children_are_hidden; - } + } + return true; +} + +void PreferencesDialog::load_shortcut_file() { + QString fn = QFileDialog::getOpenFileName(this, "Import Keyboard Shortcuts"); + if (!fn.isEmpty()) { + QFile f(fn); + if (f.exists() && f.open(QFile::ReadOnly)) { + QByteArray ba = f.readAll(); + f.close(); + for (int i=0;iaction_name()); + if (index == 0 || (index > 0 && ba.at(index-1) == '\n')) { + while (index < ba.size() && ba.at(index) != '\t') index++; + QString ks; + index++; + while (index < ba.size() && ba.at(index) != '\n') { + ks.append(ba.at(index)); + index++; + } + dout << "set" << key_shortcut_fields.at(i)->action_name() << "to" << ks; + key_shortcut_fields.at(i)->setKeySequence(ks); + } else { + key_shortcut_fields.at(i)->reset_to_default(); + } + } + } else { + QMessageBox::critical(this, "Error saving shortcuts", "Failed to open file for reading"); + } + } +} + +void PreferencesDialog::save_shortcut_file() { + QString fn = QFileDialog::getSaveFileName(this, "Export Keyboard Shortcuts"); + if (!fn.isEmpty()) { + QFile f(fn); + if (f.open(QFile::WriteOnly)) { + bool start = true; + for (int i=0;iexport_shortcut(); + if (!s.isEmpty()) { + if (!start) f.write("\n"); + f.write(s.toUtf8()); + start = false; + } + } + QMessageBox::information(this, "Export Shortcuts", "Shortcuts exported successfully"); + f.close(); + } else { + QMessageBox::critical(this, "Error saving shortcuts", "Failed to open file for writing"); + } + } } void PreferencesDialog::setup_ui() { @@ -249,7 +313,16 @@ void PreferencesDialog::setup_ui() { tree_header->setText(1, "Shortcut"); shortcut_layout->addWidget(keyboard_tree); - QHBoxLayout* reset_shortcut_layout = new QHBoxLayout(); + QHBoxLayout* reset_shortcut_layout = new QHBoxLayout(); + + QPushButton* import_shortcut_button = new QPushButton("Import"); + reset_shortcut_layout->addWidget(import_shortcut_button); + connect(import_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(load_shortcut_file())); + + QPushButton* export_shortcut_button = new QPushButton("Export"); + reset_shortcut_layout->addWidget(export_shortcut_button); + connect(export_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(save_shortcut_file())); + reset_shortcut_layout->addStretch(); reset_shortcut_button = new QPushButton("Reset to Default"); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 0e6d575d4..1bdb37b12 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -18,6 +18,9 @@ class KeySequenceEditor : public QKeySequenceEdit { public: KeySequenceEditor(QWidget *parent, QAction* a); void set_action_shortcut(); + void reset_to_default(); + QString action_name(); + QString export_shortcut(); private: QAction* action; }; @@ -36,6 +39,8 @@ private slots: void save(); void reset_default_shortcut(); bool refine_shortcut_list(const QString &, QTreeWidgetItem* parent = NULL); + void load_shortcut_file(); + void save_shortcut_file(); private: void setup_ui(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 17e06f0c4..590a652f3 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -245,7 +245,85 @@ void MainWindow::make_new_menu(QMenu *parent) { void MainWindow::make_inout_menu(QMenu *parent) { parent->addAction("Set In Point", this, SLOT(set_in_point()), QKeySequence("I")); parent->addAction("Set Out Point", this, SLOT(set_out_point()), QKeySequence("O")); - parent->addAction("Clear In/Out Point", this, SLOT(clear_inout()), QKeySequence("G")); + parent->addAction("Clear In/Out Point", this, SLOT(clear_inout()), QKeySequence("G")); +} + +void kbd_shortcut_processor(QByteArray& file, QMenu* menu, bool save, bool first) { + QList actions = menu->actions(); + for (int i=0;imenu() != NULL) { + kbd_shortcut_processor(file, a->menu(), save, first); + } 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 + if (first) { + // store default shortcut + a->setProperty("default", a->shortcut().toString()); + } else { + // restore default shortcut + a->setShortcut(a->property("default").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::load_shortcuts(const QString& fn, bool first) { + QByteArray shortcut_bytes; + QFile shortcut_path(fn); + if (shortcut_path.exists() && shortcut_path.open(QFile::ReadOnly)) { + shortcut_bytes = shortcut_path.readAll(); + shortcut_path.close(); + } + QList menus = menuBar()->actions(); + for (int i=0;imenu(); + kbd_shortcut_processor(shortcut_bytes, menu, false, first); + } +} + +void MainWindow::save_shortcuts(const QString& fn) { + // save main menu actions + QList menus = menuBar()->actions(); + QByteArray shortcut_file; + for (int i=0;imenu(); + kbd_shortcut_processor(shortcut_file, menu, true, false); + } + QFile shortcut_file_io(fn); + 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"; + } } void MainWindow::show_about() { @@ -442,47 +520,6 @@ 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); @@ -809,15 +846,7 @@ void MainWindow::setup_menus() { 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); - } - } + load_shortcuts(get_config_path() + "/shortcuts", true); } void MainWindow::set_bool_action_checked(QAction *a) { @@ -870,20 +899,7 @@ void MainWindow::closeEvent(QCloseEvent *e) { 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"; - } + save_shortcuts(config_dir + "/shortcuts"); } stop_audio(); diff --git a/mainwindow.h b/mainwindow.h index da96851fe..b95f9f165 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -18,7 +18,10 @@ public: void launch_with_project(const QString& s); void make_new_menu(QMenu* parent); - void make_inout_menu(QMenu* parent); + void make_inout_menu(QMenu* parent); + + void load_shortcuts(const QString &fn, bool first = false); + void save_shortcuts(const QString &fn); QString appName; From e0f628060719deb3c5dd2aa7dd92bdbd3174eab4 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 10:57:00 +1100 Subject: [PATCH 05/14] added go to in/out --- mainwindow.cpp | 41 +++++++++++++++++++++------ mainwindow.h | 6 ++-- panels/viewer.cpp | 72 ++++++++++++++++++++++++++--------------------- panels/viewer.h | 8 ++++-- 4 files changed, 82 insertions(+), 45 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 17e06f0c4..77d83fb42 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -642,6 +642,9 @@ void MainWindow::setup_menus() { playback_menu->addSeparator(); playback_menu->addAction("Go to Previous Cut", this, SLOT(prev_cut()), QKeySequence("Up")); playback_menu->addAction("Go to Next Cut", this, SLOT(next_cut()), QKeySequence("Down")); + playback_menu->addSeparator(); + playback_menu->addAction("Go to In Point", this, SLOT(go_to_in()), QKeySequence("Shift+I")); + playback_menu->addAction("Go to Out Point", this, SLOT(go_to_out()), QKeySequence("Shift+O")); // INITIALIZE WINDOW MENU @@ -769,13 +772,13 @@ void MainWindow::setup_menus() { set_name_and_marker->setCheckable(true); set_name_and_marker->setData(reinterpret_cast(&config.set_name_with_marker)); - loop_action = tools_menu->addAction("Loop", this, SLOT(toggle_bool_action())); - loop_action->setCheckable(true); - loop_action->setData(reinterpret_cast(&config.loop)); + loop_action = tools_menu->addAction("Loop", this, SLOT(toggle_bool_action())); + loop_action->setCheckable(true); + loop_action->setData(reinterpret_cast(&config.loop)); - pause_at_out_point_action = tools_menu->addAction("Pause At Out Point", this, SLOT(toggle_bool_action())); - pause_at_out_point_action->setCheckable(true); - pause_at_out_point_action->setData(reinterpret_cast(&config.pause_at_out_point)); + pause_at_out_point_action = tools_menu->addAction("Pause At Out Point", this, SLOT(toggle_bool_action())); + pause_at_out_point_action->setCheckable(true); + pause_at_out_point_action->setData(reinterpret_cast(&config.pause_at_out_point)); tools_menu->addSeparator(); @@ -939,6 +942,28 @@ void MainWindow::reset_layout() { setup_layout(true); } +void MainWindow::go_to_in() { + 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_in(); + } else if (panel_footage_viewer->is_focused()) { + panel_footage_viewer->go_to_in(); + } +} + +void MainWindow::go_to_out() { + 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_out(); + } else if (panel_footage_viewer->is_focused()) { + panel_footage_viewer->go_to_out(); + } +} + void MainWindow::go_to_start() { if (panel_timeline->focused() || panel_sequence_viewer->is_focused() @@ -1074,8 +1099,8 @@ void MainWindow::toolMenu_About_To_Be_Shown() { 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_bool_action_checked(loop_action); - set_bool_action_checked(pause_at_out_point_action); + set_bool_action_checked(loop_action); + set_bool_action_checked(pause_at_out_point_action); 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 da96851fe..80b0592d3 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -56,6 +56,8 @@ private slots: bool save_project_as(); bool save_project(); + void go_to_in(); + void go_to_out(); void go_to_start(); void prev_frame(); void playpause(); @@ -172,8 +174,8 @@ private: QAction* enable_drop_on_media_to_replace; QAction* enable_hover_focus; QAction* set_name_and_marker; - QAction* loop_action; - QAction* pause_at_out_point_action; + QAction* loop_action; + QAction* pause_at_out_point_action; // edit menu actions QAction* undo_action; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 5cf5d6d41..b8fee4edc 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -248,11 +248,19 @@ void Viewer::seek(long p) { } void Viewer::go_to_start() { + if (seq != NULL) seek(0); +} + +void Viewer::go_to_end() { + if (seq != NULL) seek(seq->getEndFrame()); +} + +void Viewer::go_to_in() { if (seq != NULL) { - if (seq->using_workarea && seq->playhead != seq->workarea_in) { + if (seq->using_workarea) { seek(seq->workarea_in); } else { - seek(0); + go_to_start(); } } } @@ -265,12 +273,12 @@ void Viewer::next_frame() { if (seq != NULL) seek(seq->playhead+1); } -void Viewer::go_to_end() { +void Viewer::go_to_out() { if (seq != NULL) { - if (seq->using_workarea && seq->playhead != seq->workarea_out) { + if (seq->using_workarea) { seek(seq->workarea_out); } else { - seek(seq->getEndFrame()); + go_to_end(); } } } @@ -307,11 +315,11 @@ void Viewer::play() { if (panel_footage_viewer->playing) panel_footage_viewer->pause(); if (seq != NULL) { - if (!is_recording_cued() - && seq->playhead >= get_seq_out() - && (config.loop || !main_sequence)) { - seek(get_seq_in()); - } + if (!is_recording_cued() + && seq->playhead >= get_seq_out() + && (config.loop || !main_sequence)) { + seek(get_seq_in()); + } reset_all_audio(); if (is_recording_cued() && !start_recording()) { @@ -458,19 +466,19 @@ void Viewer::set_zoom_value(double d) { } void Viewer::set_sb_max() { - headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width()); + headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width()); } long Viewer::get_seq_in() { - return (seq->using_workarea) - ? seq->workarea_in - : 0; + return (seq->using_workarea) + ? seq->workarea_in + : 0; } long Viewer::get_seq_out() { - return (seq->using_workarea && previous_playhead < seq->workarea_out) - ? seq->workarea_out - : seq->getEndFrame(); + return (seq->using_workarea && previous_playhead < seq->workarea_out) + ? seq->workarea_out + : seq->getEndFrame(); } void Viewer::setup_ui() { @@ -517,7 +525,7 @@ void Viewer::setup_ui() { goToStartIcon.addFile(QStringLiteral(":/icons/prev.png"), QSize(), QIcon::Normal, QIcon::Off); goToStartIcon.addFile(QStringLiteral(":/icons/prev-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off); btnSkipToStart->setIcon(goToStartIcon); - connect(btnSkipToStart, SIGNAL(clicked(bool)), this, SLOT(go_to_start())); + connect(btnSkipToStart, SIGNAL(clicked(bool)), this, SLOT(go_to_in())); playback_control_layout->addWidget(btnSkipToStart); btnRewind = new QPushButton(playback_controls); @@ -548,7 +556,7 @@ void Viewer::setup_ui() { nextIcon.addFile(QStringLiteral(":/icons/next.png"), QSize(), QIcon::Normal, QIcon::Off); nextIcon.addFile(QStringLiteral(":/icons/next-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off); btnSkipToEnd->setIcon(nextIcon); - connect(btnSkipToEnd, SIGNAL(clicked(bool)), this, SLOT(go_to_end())); + connect(btnSkipToEnd, SIGNAL(clicked(bool)), this, SLOT(go_to_out())); playback_control_layout->addWidget(btnSkipToEnd); lower_control_layout->addWidget(playback_controls); @@ -655,25 +663,25 @@ void Viewer::update_playhead() { } void Viewer::timer_update() { - previous_playhead = seq->playhead; + previous_playhead = seq->playhead; seq->playhead = qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate)); update_parents(); - long end_frame = get_seq_out(); - if (!recording + long end_frame = get_seq_out(); + if (!recording && playing && seq->playhead >= end_frame - && previous_playhead < end_frame) { - if (!config.pause_at_out_point && config.loop) { - seek(get_seq_in()); - play(); - } else if (config.pause_at_out_point || !main_sequence) { - pause(); - } - } else if (recording && recording_start != recording_end && seq->playhead >= recording_end) { - pause(); - } + && previous_playhead < end_frame) { + if (!config.pause_at_out_point && config.loop) { + seek(get_seq_in()); + play(); + } else if (config.pause_at_out_point || !main_sequence) { + pause(); + } + } else if (recording && recording_start != recording_end && seq->playhead >= recording_end) { + pause(); + } } void Viewer::recording_flasher_update() { diff --git a/panels/viewer.h b/panels/viewer.h index 8cca7b7c0..f08b19e30 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -72,9 +72,11 @@ public: public slots: void play_wake(); void go_to_start(); + void go_to_in(); void previous_frame(); void toggle_play(); void next_frame(); + void go_to_out(); void go_to_end(); private slots: @@ -94,8 +96,8 @@ private: void set_zoom_value(double d); void set_sb_max(); - long get_seq_in(); - long get_seq_out(); + long get_seq_in(); + long get_seq_out(); QIcon playIcon; @@ -116,7 +118,7 @@ private: bool cue_recording_internal; QTimer recording_flasher; - long previous_playhead; + long previous_playhead; }; #endif // VIEWER_H From a0e03f550f872f31678a6152a35583da917f38b0 Mon Sep 17 00:00:00 2001 From: Alexandre Prokoudine Date: Thu, 10 Jan 2019 03:15:54 +0300 Subject: [PATCH 06/14] Add default shortcut for Action Search --- mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 77d83fb42..e95053e9b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -806,7 +806,7 @@ void MainWindow::setup_menus() { QMenu* help_menu = menuBar->addMenu("&Help"); - help_menu->addAction("A&ction Search", this, SLOT(show_action_search())); + help_menu->addAction("A&ction Search", this, SLOT(show_action_search()), QKeySequence("/")); help_menu->addSeparator(); From d4384035e6e7c3bddff2ca56d57467a994eeddb6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 11:25:35 +1100 Subject: [PATCH 07/14] fixed clear recent in shortcut prefs --- dialogs/preferencesdialog.cpp | 130 ++++++++++++++-------------- mainwindow.cpp | 154 ++++++++++++++++++---------------- mainwindow.h | 7 +- 3 files changed, 149 insertions(+), 142 deletions(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index c85724086..8b39fa2b0 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -31,23 +31,23 @@ KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a) } void KeySequenceEditor::set_action_shortcut() { - action->setShortcut(keySequence()); + action->setShortcut(keySequence()); } void KeySequenceEditor::reset_to_default() { - setKeySequence(action->property("default").toString()); + setKeySequence(action->property("default").toString()); } QString KeySequenceEditor::action_name() { - return action->text().replace("&", ""); + return action->text().replace("&", ""); } QString KeySequenceEditor::export_shortcut() { - QString ks = keySequence().toString(); - if (ks != action->property("default")) { - return action->text().replace("&", "") + "\t" + keySequence().toString(); - } - return 0; + QString ks = keySequence().toString(); + if (ks != action->property("default")) { + return action->text().replace("&", "") + "\t" + keySequence().toString(); + } + return 0; } PreferencesDialog::PreferencesDialog(QWidget *parent) : @@ -69,7 +69,7 @@ void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* for (int i=0;iisSeparator()) { + if (!a->isSeparator() && a->property("keyignore").isNull()) { QTreeWidgetItem* item = new QTreeWidgetItem(); item->setText(0, a->text().replace("&", "")); @@ -78,7 +78,7 @@ void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* if (a->menu() != NULL) { item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); setup_kbd_shortcut_worker(a->menu(), item); - } else { + } else { key_shortcut_items.append(item); key_shortcut_actions.append(a); } @@ -129,7 +129,7 @@ void PreferencesDialog::reset_default_shortcut() { QList items = keyboard_tree->selectedItems(); for (int i=0;iselectedItems().at(i); - static_cast(keyboard_tree->itemWidget(item, 1))->reset_to_default(); + static_cast(keyboard_tree->itemWidget(item, 1))->reset_to_default(); } } @@ -170,59 +170,59 @@ bool PreferencesDialog::refine_shortcut_list(const QString &s, QTreeWidgetItem* parent->setHidden(all_children_are_hidden); return all_children_are_hidden; - } - return true; + } + return true; } void PreferencesDialog::load_shortcut_file() { - QString fn = QFileDialog::getOpenFileName(this, "Import Keyboard Shortcuts"); - if (!fn.isEmpty()) { - QFile f(fn); - if (f.exists() && f.open(QFile::ReadOnly)) { - QByteArray ba = f.readAll(); - f.close(); - for (int i=0;iaction_name()); - if (index == 0 || (index > 0 && ba.at(index-1) == '\n')) { - while (index < ba.size() && ba.at(index) != '\t') index++; - QString ks; - index++; - while (index < ba.size() && ba.at(index) != '\n') { - ks.append(ba.at(index)); - index++; - } - dout << "set" << key_shortcut_fields.at(i)->action_name() << "to" << ks; - key_shortcut_fields.at(i)->setKeySequence(ks); - } else { - key_shortcut_fields.at(i)->reset_to_default(); - } - } - } else { - QMessageBox::critical(this, "Error saving shortcuts", "Failed to open file for reading"); - } - } + QString fn = QFileDialog::getOpenFileName(this, "Import Keyboard Shortcuts"); + if (!fn.isEmpty()) { + QFile f(fn); + if (f.exists() && f.open(QFile::ReadOnly)) { + QByteArray ba = f.readAll(); + f.close(); + for (int i=0;iaction_name()); + if (index == 0 || (index > 0 && ba.at(index-1) == '\n')) { + while (index < ba.size() && ba.at(index) != '\t') index++; + QString ks; + index++; + while (index < ba.size() && ba.at(index) != '\n') { + ks.append(ba.at(index)); + index++; + } + dout << "set" << key_shortcut_fields.at(i)->action_name() << "to" << ks; + key_shortcut_fields.at(i)->setKeySequence(ks); + } else { + key_shortcut_fields.at(i)->reset_to_default(); + } + } + } else { + QMessageBox::critical(this, "Error saving shortcuts", "Failed to open file for reading"); + } + } } void PreferencesDialog::save_shortcut_file() { - QString fn = QFileDialog::getSaveFileName(this, "Export Keyboard Shortcuts"); - if (!fn.isEmpty()) { - QFile f(fn); - if (f.open(QFile::WriteOnly)) { - bool start = true; - for (int i=0;iexport_shortcut(); - if (!s.isEmpty()) { - if (!start) f.write("\n"); - f.write(s.toUtf8()); - start = false; - } - } - QMessageBox::information(this, "Export Shortcuts", "Shortcuts exported successfully"); - f.close(); - } else { - QMessageBox::critical(this, "Error saving shortcuts", "Failed to open file for writing"); - } - } + QString fn = QFileDialog::getSaveFileName(this, "Export Keyboard Shortcuts"); + if (!fn.isEmpty()) { + QFile f(fn); + if (f.open(QFile::WriteOnly)) { + bool start = true; + for (int i=0;iexport_shortcut(); + if (!s.isEmpty()) { + if (!start) f.write("\n"); + f.write(s.toUtf8()); + start = false; + } + } + QMessageBox::information(this, "Export Shortcuts", "Shortcuts exported successfully"); + f.close(); + } else { + QMessageBox::critical(this, "Error saving shortcuts", "Failed to open file for writing"); + } + } } void PreferencesDialog::setup_ui() { @@ -313,15 +313,15 @@ void PreferencesDialog::setup_ui() { tree_header->setText(1, "Shortcut"); shortcut_layout->addWidget(keyboard_tree); - QHBoxLayout* reset_shortcut_layout = new QHBoxLayout(); + QHBoxLayout* reset_shortcut_layout = new QHBoxLayout(); - QPushButton* import_shortcut_button = new QPushButton("Import"); - reset_shortcut_layout->addWidget(import_shortcut_button); - connect(import_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(load_shortcut_file())); + QPushButton* import_shortcut_button = new QPushButton("Import"); + reset_shortcut_layout->addWidget(import_shortcut_button); + connect(import_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(load_shortcut_file())); - QPushButton* export_shortcut_button = new QPushButton("Export"); - reset_shortcut_layout->addWidget(export_shortcut_button); - connect(export_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(save_shortcut_file())); + QPushButton* export_shortcut_button = new QPushButton("Export"); + reset_shortcut_layout->addWidget(export_shortcut_button); + connect(export_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(save_shortcut_file())); reset_shortcut_layout->addStretch(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 050b92184..bd94ea924 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -245,85 +245,85 @@ void MainWindow::make_new_menu(QMenu *parent) { void MainWindow::make_inout_menu(QMenu *parent) { parent->addAction("Set In Point", this, SLOT(set_in_point()), QKeySequence("I")); parent->addAction("Set Out Point", this, SLOT(set_out_point()), QKeySequence("O")); - parent->addAction("Clear In/Out Point", this, SLOT(clear_inout()), QKeySequence("G")); + parent->addAction("Clear In/Out Point", this, SLOT(clear_inout()), QKeySequence("G")); } void kbd_shortcut_processor(QByteArray& file, QMenu* menu, bool save, bool first) { - QList actions = menu->actions(); - for (int i=0;imenu() != NULL) { - kbd_shortcut_processor(file, a->menu(), save, first); - } 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 - if (first) { - // store default shortcut - a->setProperty("default", a->shortcut().toString()); - } else { - // restore default shortcut - a->setShortcut(a->property("default").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); - } - } - } - } - } + QList actions = menu->actions(); + for (int i=0;imenu() != NULL) { + kbd_shortcut_processor(file, a->menu(), save, first); + } 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 + if (first) { + // store default shortcut + a->setProperty("default", a->shortcut().toString()); + } else { + // restore default shortcut + a->setShortcut(a->property("default").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::load_shortcuts(const QString& fn, bool first) { - QByteArray shortcut_bytes; - QFile shortcut_path(fn); - if (shortcut_path.exists() && shortcut_path.open(QFile::ReadOnly)) { - shortcut_bytes = shortcut_path.readAll(); - shortcut_path.close(); - } - QList menus = menuBar()->actions(); - for (int i=0;imenu(); - kbd_shortcut_processor(shortcut_bytes, menu, false, first); - } + QByteArray shortcut_bytes; + QFile shortcut_path(fn); + if (shortcut_path.exists() && shortcut_path.open(QFile::ReadOnly)) { + shortcut_bytes = shortcut_path.readAll(); + shortcut_path.close(); + } + QList menus = menuBar()->actions(); + for (int i=0;imenu(); + kbd_shortcut_processor(shortcut_bytes, menu, false, first); + } } void MainWindow::save_shortcuts(const QString& fn) { - // save main menu actions - QList menus = menuBar()->actions(); - QByteArray shortcut_file; - for (int i=0;imenu(); - kbd_shortcut_processor(shortcut_file, menu, true, false); - } - QFile shortcut_file_io(fn); - 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"; - } + // save main menu actions + QList menus = menuBar()->actions(); + QByteArray shortcut_file; + for (int i=0;imenu(); + kbd_shortcut_processor(shortcut_file, menu, true, false); + } + QFile shortcut_file_io(fn); + 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"; + } } void MainWindow::show_about() { @@ -534,8 +534,13 @@ void MainWindow::setup_menus() { file_menu->addAction("&Open Project", this, SLOT(open_project()), QKeySequence("Ctrl+O")); + clear_open_recent_action = new QAction("Clear Recent List"); + connect(clear_open_recent_action, SIGNAL(triggered()), panel_project, SLOT(clear_recent_projects())); + open_recent = file_menu->addMenu("Open Recent"); + open_recent->addAction(clear_open_recent_action); + file_menu->addAction("&Save Project", this, SLOT(save_project()), QKeySequence("Ctrl+S")); file_menu->addAction("Save Project &As", this, SLOT(save_project_as()), QKeySequence("Ctrl+Shift+S")); @@ -849,7 +854,7 @@ void MainWindow::setup_menus() { help_menu->addAction("&About...", this, SLOT(show_about())); - load_shortcuts(get_config_path() + "/shortcuts", true); + load_shortcuts(get_config_path() + "/shortcuts", true); } void MainWindow::set_bool_action_checked(QAction *a) { @@ -902,7 +907,7 @@ void MainWindow::closeEvent(QCloseEvent *e) { dout << "[ERROR] Failed to save layout"; } - save_shortcuts(config_dir + "/shortcuts"); + save_shortcuts(config_dir + "/shortcuts"); } stop_audio(); @@ -1154,12 +1159,13 @@ void MainWindow::fileMenu_About_To_Be_Shown() { open_recent->setEnabled(true); for (int i=0;iaddAction(recent_projects.at(i)); + action->setProperty("keyignore", true); action->setData(i); connect(action, SIGNAL(triggered()), this, SLOT(load_recent_project())); } open_recent->addSeparator(); - QAction* clear_action = open_recent->addAction("Clear Recent List"); - connect(clear_action, SIGNAL(triggered()), panel_project, SLOT(clear_recent_projects())); + + open_recent->addAction(clear_open_recent_action); } else { open_recent->setEnabled(false); } diff --git a/mainwindow.h b/mainwindow.h index 4b917181e..124b862c5 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -18,10 +18,10 @@ public: void launch_with_project(const QString& s); void make_new_menu(QMenu* parent); - void make_inout_menu(QMenu* parent); + void make_inout_menu(QMenu* parent); - void load_shortcuts(const QString &fn, bool first = false); - void save_shortcuts(const QString &fn); + void load_shortcuts(const QString &fn, bool first = false); + void save_shortcuts(const QString &fn); QString appName; @@ -136,6 +136,7 @@ private: // file menu actions QMenu* open_recent; + QAction* clear_open_recent_action; // view menu actions QAction* track_lines; From ece51234887bb9a0934780c60a151eabc79322d0 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 11:28:38 +1100 Subject: [PATCH 08/14] fixed crash when ripple deleting with no active sequence --- mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index bd94ea924..e46999e45 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -397,7 +397,7 @@ void MainWindow::export_dialog() { } void MainWindow::ripple_delete() { - panel_timeline->delete_selection(sequence->selections, true); + if (sequence != NULL) panel_timeline->delete_selection(sequence->selections, true); } void MainWindow::editMenu_About_To_Be_Shown() { From 290fbf5232d9486912c2979d6ee850eb540c5b36 Mon Sep 17 00:00:00 2001 From: Alexandre Prokoudine Date: Thu, 10 Jan 2019 03:32:20 +0300 Subject: [PATCH 09/14] More proportional font size in the Action Search dialog --- dialogs/actionsearch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dialogs/actionsearch.cpp b/dialogs/actionsearch.cpp index 5bf7461d8..3f5593dbe 100644 --- a/dialogs/actionsearch.cpp +++ b/dialogs/actionsearch.cpp @@ -21,7 +21,7 @@ ActionSearch::ActionSearch(QWidget *parent) : ActionSearchEntry* entry_field = new ActionSearchEntry(); QFont entry_field_font = entry_field->font(); - entry_field_font.setPointSize(entry_field_font.pointSize()*2); + entry_field_font.setPointSize(entry_field_font.pointSize()*1.2); entry_field->setFont(entry_field_font); entry_field->setPlaceholderText("Search for action..."); connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &))); @@ -32,7 +32,7 @@ ActionSearch::ActionSearch(QWidget *parent) : list_widget = new ActionSearchList(); QFont list_widget_font = list_widget->font(); - list_widget_font.setPointSize(list_widget_font.pointSize()*1.5); + list_widget_font.setPointSize(list_widget_font.pointSize()*1.2); list_widget->setFont(list_widget_font); layout->addWidget(list_widget); connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action())); From 82a4265b4a4098bf3502222209d9586b4eb0024e Mon Sep 17 00:00:00 2001 From: app4soft Date: Thu, 10 Jan 2019 03:15:37 +0200 Subject: [PATCH 10/14] Appdata: add Ukrainian translation --- packaging/linux/org.olivevideoeditor.Olive.appdata.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packaging/linux/org.olivevideoeditor.Olive.appdata.xml b/packaging/linux/org.olivevideoeditor.Olive.appdata.xml index 62fa50e1b..bcb54e5a6 100644 --- a/packaging/linux/org.olivevideoeditor.Olive.appdata.xml +++ b/packaging/linux/org.olivevideoeditor.Olive.appdata.xml @@ -9,10 +9,14 @@ Editor de vídeo não-linear Editor de video no lineal Нелинейный видеоредактор + Нелінійний відеоредактор + Нелінійний відеоредактор

Olive is a free non-linear video editor aiming to provide a fully-featured alternative to high-end professional video editing software.

Olive é um editor de vídeo não-linear com o objetivo de fornecer uma alternativa completa para softwares profissionais de edição de vídeo.

Olive es un editor de video no lineal libre que apunta a brindar una alternativa completa al software de edición de video profesional.

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

+

Olive — вільний нелінійний відеоредактор, задуманий як повноцінна заміна закритим комерційним продуктам.

+

Olive — вільний нелінійний відеоредактор, задуманий як повноцінна заміна закритим комерційним продуктам.

https://www.olivevideoeditor.org https://www.patreon.com/olivevideoeditor https://github.com/olive-editor/olive/issues From 69ccb55d615c8e097358d2adb98c620ef48bd4d2 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 13:14:49 +1100 Subject: [PATCH 11/14] split passage get into separate io device --- effects/internal/voideffect.cpp | 82 ++++--- io/loadthread.cpp | 60 ++--- project/effect.cpp | 412 ++++++++++++++++---------------- 3 files changed, 280 insertions(+), 274 deletions(-) diff --git a/effects/internal/voideffect.cpp b/effects/internal/voideffect.cpp index eee3b4e87..11a8c25ff 100644 --- a/effects/internal/voideffect.cpp +++ b/effects/internal/voideffect.cpp @@ -2,55 +2,61 @@ #include #include +#include #include "ui/collapsiblewidget.h" #include "debug.h" VoidEffect::VoidEffect(Clip *c, const QString& n) : Effect(c, NULL) { - name = n; - QString display_name; - if (n.isEmpty()) { - display_name = "(unknown)"; - } else { - display_name = n; - } - EffectRow* row = add_row("Missing Effect", false, false); - row->add_widget(new QLabel(display_name)); - container->setText(display_name); + name = n; + QString display_name; + if (n.isEmpty()) { + display_name = "(unknown)"; + } else { + display_name = n; + } + EffectRow* row = add_row("Missing Effect", false, false); + row->add_widget(new QLabel(display_name)); + container->setText(display_name); } void VoidEffect::load(QXmlStreamReader &stream) { - QString tag = stream.name().toString(); - qint64 start_index = stream.characterOffset(); - qint64 end_index = start_index; - while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) { - end_index = stream.characterOffset(); - stream.readNext(); - } - qint64 passage_length = end_index - start_index; - if (passage_length > 0) { - // store xml data verbatim - QIODevice* device = stream.device(); - device->seek(start_index); - bytes = device->read(passage_length); - int passage_end = bytes.lastIndexOf('>')+1; - bytes.remove(passage_end, bytes.size()-passage_end); - } + QString tag = stream.name().toString(); + qint64 start_index = stream.characterOffset(); + qint64 end_index = start_index; + while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) { + end_index = stream.characterOffset(); + stream.readNext(); + } + qint64 passage_length = end_index - start_index; + if (passage_length > 0) { + // store xml data verbatim + QFile* device = static_cast(stream.device()); + + QFile passage_get(device->fileName()); + if (passage_get.open(QFile::ReadOnly)) { + passage_get.seek(start_index); + bytes = passage_get.read(passage_length); + int passage_end = bytes.lastIndexOf('>')+1; + bytes.remove(passage_end, bytes.size()-passage_end); + passage_get.close(); + } + } } void VoidEffect::save(QXmlStreamWriter &stream) { - if (!name.isEmpty()) { - stream.writeAttribute("name", name); - stream.writeAttribute("enabled", QString::number(is_enabled())); + if (!name.isEmpty()) { + stream.writeAttribute("name", name); + stream.writeAttribute("enabled", QString::number(is_enabled())); - // force xml writer to expand tag, ignored when loading - stream.writeStartElement("void"); - stream.writeEndElement(); + // force xml writer to expand tag, ignored when loading + stream.writeStartElement("void"); + stream.writeEndElement(); - if (!bytes.isEmpty()) { - // write stored data - QIODevice* device = stream.device(); - device->write(bytes); - } - } + if (!bytes.isEmpty()) { + // write stored data + QIODevice* device = stream.device(); + device->write(bytes); + } + } } diff --git a/io/loadthread.cpp b/io/loadthread.cpp index 71fa7035a..3ae94d103 100644 --- a/io/loadthread.cpp +++ b/io/loadthread.cpp @@ -34,7 +34,7 @@ LoadThread::LoadThread(LoadDialog* l, bool a) : ld(l), autorecovery(a), cancelle 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 QString*, const EffectMeta*, long, bool)), this, SLOT(create_effect_ui(QXmlStreamReader*, Clip*, int, const QString*, const EffectMeta*, long, bool))); + connect(this, SIGNAL(start_create_effect_ui(QXmlStreamReader*, Clip*, int, const QString*, const EffectMeta*, long, bool)), this, SLOT(create_effect_ui(QXmlStreamReader*, Clip*, int, const QString*, const EffectMeta*, long, bool))); } const EffectMeta* get_meta_from_name(const QString& name) { @@ -62,10 +62,10 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { } else if (attr.name() == "length") { effect_length = attr.value().toLong(); } - } + } // wait for effects to be loaded - panel_effect_controls->effects_loaded.lock(); + panel_effect_controls->effects_loaded.lock(); const EffectMeta* meta = NULL; @@ -74,21 +74,21 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { meta = get_meta_from_name(effect_name); } - panel_effect_controls->effects_loaded.unlock(); + panel_effect_controls->effects_loaded.unlock(); - QString tag = stream.name().toString(); + 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, &effect_name, meta, effect_length, effect_enabled); - waitCond.wait(&mutex); + emit start_create_effect_ui(&stream, c, type, &effect_name, meta, effect_length, effect_enabled); + waitCond.wait(&mutex); } void LoadThread::read_next(QXmlStreamReader &stream) { @@ -162,7 +162,7 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { internal_proj_url = stream.readElementText(); internal_proj_dir = QFileInfo(internal_proj_url).absoluteDir(); } else { - while (!cancelled && !(stream.name() == root_search && stream.isEndElement())) { + while (!cancelled && !stream.atEnd() && !(stream.name() == root_search && stream.isEndElement())) { read_next(stream); if (stream.name() == child_search && stream.isStartElement()) { switch (type) { @@ -582,7 +582,7 @@ void LoadThread::run() { xml_error = false; if (show_err) emit error(); } else if (stream.hasError()) { - error_str = stream.errorString(); + error_str = stream.errorString() + " - Line: " + QString::number(stream.lineNumber()) + " Col:" + QString::number(stream.columnNumber()); xml_error = true; emit error(); cont = false; @@ -657,7 +657,7 @@ void LoadThread::create_effect_ui( QXmlStreamReader* stream, Clip* c, int type, - const QString* effect_name, + const QString* effect_name, const EffectMeta* meta, long effect_length, bool effect_enabled) @@ -685,19 +685,19 @@ void LoadThread::create_effect_ui( if (cancelled) return; if (type == TA_NO_TRANSITION) { - if (meta == NULL) { - // create void effect - VoidEffect* ve = new VoidEffect(c, *effect_name); - ve->set_enabled(effect_enabled); - ve->load(*stream); - c->effects.append(ve); - } else { - Effect* e = create_effect(c, meta); - e->set_enabled(effect_enabled); - e->load(*stream); + if (meta == NULL) { + // create void effect + VoidEffect* ve = new VoidEffect(c, *effect_name); + ve->set_enabled(effect_enabled); + ve->load(*stream); + c->effects.append(ve); + } else { + Effect* e = create_effect(c, meta); + e->set_enabled(effect_enabled); + e->load(*stream); - c->effects.append(e); - } + c->effects.append(e); + } } else { int transition_index = create_transition(c, NULL, meta); Transition* t = c->sequence->transitions.at(transition_index); diff --git a/project/effect.cpp b/project/effect.cpp index f4ec6e3ab..7468a757d 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -93,7 +93,7 @@ void load_internal_effects() { em.name = "Volume"; em.internal = EFFECT_INTERNAL_VOLUME; - effects.append(em); + effects.append(em); em.name = "Pan"; em.internal = EFFECT_INTERNAL_PAN; @@ -244,7 +244,7 @@ void init_effects() { } EffectInit::EffectInit() { - panel_effect_controls->effects_loaded.lock(); + panel_effect_controls->effects_loaded.lock(); } void EffectInit::run() { @@ -252,7 +252,7 @@ void EffectInit::run() { load_internal_effects(); load_shader_effects(); load_vst_effects(); - panel_effect_controls->effects_loaded.unlock(); + panel_effect_controls->effects_loaded.unlock(); dout << "[INFO] Finished initializing effects"; } @@ -280,195 +280,195 @@ Effect::Effect(Clip* c, const EffectMeta *em) : connect(container->title_bar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); - if (em != NULL) { - // set up UI from effect file - container->setText(em->name); + if (em != NULL) { + // set up UI from effect file + container->setText(em->name); - if (!em->filename.isEmpty()) { - QFile effect_file(em->filename); - if (effect_file.open(QFile::ReadOnly)) { - QXmlStreamReader reader(&effect_file); + if (!em->filename.isEmpty()) { + QFile effect_file(em->filename); + if (effect_file.open(QFile::ReadOnly)) { + QXmlStreamReader reader(&effect_file); - while (!reader.atEnd()) { - if (reader.name() == "row" && reader.isStartElement()) { - QString row_name; - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename << "- ID cannot be empty."; - } else if (type > -1) { - EffectField* field = row->add_field(type, id); - connect(field, SIGNAL(changed()), this, SLOT(field_changed())); - switch (type) { - case EFFECT_FIELD_DOUBLE: - for (int i=0;iset_double_default_value(attr.value().toDouble()); - } else if (attr.name() == "min") { - field->set_double_minimum_value(attr.value().toDouble()); - } else if (attr.name() == "max") { - field->set_double_maximum_value(attr.value().toDouble()); - } - } - break; - case EFFECT_FIELD_COLOR: - { - QColor color; - for (int i=0;iset_color_value(color); - } - break; - case EFFECT_FIELD_STRING: - for (int i=0;iset_string_value(attr.value().toString()); - } - } - break; - case EFFECT_FIELD_BOOL: - for (int i=0;iset_bool_value(attr.value() == "1"); - } - } - break; - case EFFECT_FIELD_COMBO: - { - int combo_index = 0; - for (int i=0;iadd_combo_item(reader.text().toString(), 0); - } - } - field->set_combo_index(combo_index); - } - break; - case EFFECT_FIELD_FONT: - for (int i=0;iset_font_name(attr.value().toString()); - } - } - break; - case EFFECT_FIELD_FILE: - for (int i=0;iset_filename(attr.value().toString()); - } - } - break; - } - } - } - } - } - } else if (reader.name() == "shader" && reader.isStartElement()) { - enable_shader = true; - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename; - enable_superimpose = false; - } - break; - } - } - }*/ - reader.readNext(); - } + 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); + connect(field, SIGNAL(changed()), this, SLOT(field_changed())); + switch (type) { + case EFFECT_FIELD_DOUBLE: + for (int i=0;iset_double_default_value(attr.value().toDouble()); + } else if (attr.name() == "min") { + field->set_double_minimum_value(attr.value().toDouble()); + } else if (attr.name() == "max") { + field->set_double_maximum_value(attr.value().toDouble()); + } + } + break; + case EFFECT_FIELD_COLOR: + { + QColor color; + for (int i=0;iset_color_value(color); + } + break; + case EFFECT_FIELD_STRING: + for (int i=0;iset_string_value(attr.value().toString()); + } + } + break; + case EFFECT_FIELD_BOOL: + for (int i=0;iset_bool_value(attr.value() == "1"); + } + } + break; + case EFFECT_FIELD_COMBO: + { + int combo_index = 0; + for (int i=0;iadd_combo_item(reader.text().toString(), 0); + } + } + field->set_combo_index(combo_index); + } + break; + case EFFECT_FIELD_FONT: + for (int i=0;iset_font_name(attr.value().toString()); + } + } + break; + case EFFECT_FIELD_FILE: + for (int i=0;iset_filename(attr.value().toString()); + } + } + break; + } + } + } + } + } + } else if (reader.name() == "shader" && reader.isStartElement()) { + enable_shader = true; + const QXmlStreamAttributes& attributes = reader.attributes(); + for (int i=0;ifilename; + enable_superimpose = false; + } + break; + } + } + }*/ + reader.readNext(); + } - effect_file.close(); - } else { - dout << "[ERROR] Failed to open effect file" << em->filename; - } - } - } + effect_file.close(); + } else { + dout << "[ERROR] Failed to open effect file" << em->filename; + } + } + } } Effect::~Effect() { @@ -794,28 +794,28 @@ void Effect::open() { validate_meta_path(); bool glsl_compiled = true; if (!vertPath.isEmpty()) { - if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath)) { - dout << "[INFO] Vertex shader added successfully"; - } else { - glsl_compiled = false; - dout << "[WARNING] Vertex shader could not be added"; - } + if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath)) { + dout << "[INFO] Vertex shader added successfully"; + } else { + glsl_compiled = false; + dout << "[WARNING] Vertex shader could not be added"; + } } if (!fragPath.isEmpty()) { - if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath)) { - dout << "[INFO] Fragment shader added successfully"; - } else { - glsl_compiled = false; - dout << "[WARNING] Fragment shader could not be added"; - } + if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath)) { + dout << "[INFO] Fragment shader added successfully"; + } else { + glsl_compiled = false; + dout << "[WARNING] Fragment shader could not be added"; + } + } + if (glsl_compiled) { + if (glslProgram->link()) { + dout << "[INFO] Shader program linked successfully"; + } else { + dout << "[WARNING] Shader program failed to link"; + } } - if (glsl_compiled) { - if (glslProgram->link()) { - dout << "[INFO] Shader program linked successfully"; - } else { - dout << "[WARNING] Shader program failed to link"; - } - } isOpen = true; } } else { From 22f7411eacd914ce46e0bb75135e4145a3fc831d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 13:19:54 +1100 Subject: [PATCH 12/14] added git to debian control file --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 7608033f8..e46c0c253 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: olive-editor Section: video Priority: optional Maintainer: Olive Team -Build-Depends: debhelper (>=9), build-essential, qt5-default, qtmultimedia5-dev, libqt5opengl5-dev, libqt5multimedia5-plugins, libavformat-dev, libavcodec-dev, libavutil-dev, libswscale-dev, libswresample-dev, libavfilter-dev, libpostproc-dev +Build-Depends: debhelper (>=9), build-essential, qt5-default, qtmultimedia5-dev, libqt5opengl5-dev, libqt5multimedia5-plugins, libavformat-dev, libavcodec-dev, libavutil-dev, libswscale-dev, libswresample-dev, libavfilter-dev, libpostproc-dev, git Standards-Version: 3.9.6 Homepage: https://olivevideoeditor.org/ From 9f3469857af14cf6d73148cb8c7d977d4f5a2016 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 13:38:59 +1100 Subject: [PATCH 13/14] text effect only bind shader if shadow is enabled --- effects/internal/texteffect.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index cb3ea3155..862dca8a9 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -24,7 +24,7 @@ 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); @@ -189,6 +189,9 @@ void TextEffect::redraw(double timecode) { } void TextEffect::shadow_enable(bool e) { + enable_shader = e; + close(); + shadow_color->set_enabled(e); shadow_distance->set_enabled(e); shadow_softness->set_enabled(e); From a00b14e13c1045f8b90e679a1e63a9c698c16b83 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 10 Jan 2019 15:42:42 +1100 Subject: [PATCH 14/14] added reset all shortcuts button --- dialogs/preferencesdialog.cpp | 18 +++++++++++++++--- dialogs/preferencesdialog.h | 13 ++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 8b39fa2b0..b7df9ddaa 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -133,6 +133,14 @@ void PreferencesDialog::reset_default_shortcut() { } } +void PreferencesDialog::reset_all_shortcuts() { + if (QMessageBox::question(this, "Confirm Reset All Shortcuts", "Are you sure you wish to reset all keyboard shortcuts to their defaults?", QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + for (int i=0;ireset_to_default(); + } + } +} + bool PreferencesDialog::refine_shortcut_list(const QString &s, QTreeWidgetItem* parent) { if (parent == NULL) { for (int i=0;itopLevelItemCount();i++) { @@ -325,9 +333,13 @@ void PreferencesDialog::setup_ui() { 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())); + QPushButton* reset_selected_shortcut_button = new QPushButton("Reset Selected"); + reset_shortcut_layout->addWidget(reset_selected_shortcut_button); + connect(reset_selected_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(reset_default_shortcut())); + + QPushButton* reset_all_shortcut_button = new QPushButton("Reset All"); + reset_shortcut_layout->addWidget(reset_all_shortcut_button); + connect(reset_all_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(reset_all_shortcuts())); shortcut_layout->addLayout(reset_shortcut_layout); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 1bdb37b12..5647997f5 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -18,9 +18,9 @@ class KeySequenceEditor : public QKeySequenceEdit { public: KeySequenceEditor(QWidget *parent, QAction* a); void set_action_shortcut(); - void reset_to_default(); - QString action_name(); - QString export_shortcut(); + void reset_to_default(); + QString action_name(); + QString export_shortcut(); private: QAction* action; }; @@ -38,9 +38,10 @@ public: private slots: void save(); void reset_default_shortcut(); + void reset_all_shortcuts(); bool refine_shortcut_list(const QString &, QTreeWidgetItem* parent = NULL); - void load_shortcut_file(); - void save_shortcut_file(); + void load_shortcut_file(); + void save_shortcut_file(); private: void setup_ui(); @@ -60,8 +61,6 @@ private: QVector key_shortcut_actions; QVector key_shortcut_items; QVector key_shortcut_fields; - - QPushButton* reset_shortcut_button; }; #endif // PREFERENCESDIALOG_H