Merge branch 'master' of https://github.com/olive-editor/olive into project_icons
This commit is contained in:
Vendored
+1
-1
@@ -2,7 +2,7 @@ Source: olive-editor
|
||||
Section: video
|
||||
Priority: optional
|
||||
Maintainer: Olive Team <itsmattkc@gmail.com>
|
||||
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/
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QList>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@@ -32,6 +34,22 @@ void KeySequenceEditor::set_action_shortcut() {
|
||||
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) :
|
||||
QDialog(parent)
|
||||
{
|
||||
@@ -51,7 +69,7 @@ void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem*
|
||||
for (int i=0;i<actions.size();i++) {
|
||||
QAction* a = actions.at(i);
|
||||
|
||||
if (!a->isSeparator()) {
|
||||
if (!a->isSeparator() && a->property("keyignore").isNull()) {
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
||||
item->setText(0, a->text().replace("&", ""));
|
||||
|
||||
@@ -61,7 +79,6 @@ void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem*
|
||||
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
||||
setup_kbd_shortcut_worker(a->menu(), item);
|
||||
} else {
|
||||
item->setData(0, Qt::UserRole + 1, reinterpret_cast<quintptr>(a));
|
||||
key_shortcut_items.append(item);
|
||||
key_shortcut_actions.append(a);
|
||||
}
|
||||
@@ -112,11 +129,14 @@ void PreferencesDialog::reset_default_shortcut() {
|
||||
QList<QTreeWidgetItem*> items = keyboard_tree->selectedItems();
|
||||
for (int i=0;i<items.size();i++) {
|
||||
QTreeWidgetItem* item = keyboard_tree->selectedItems().at(i);
|
||||
const QVariant& data = item->data(0, Qt::UserRole + 1);
|
||||
if (!data.isNull()) {
|
||||
QAction* a = reinterpret_cast<QAction*>(data.value<quintptr>());
|
||||
QKeySequence ks(a->property("default").toString());
|
||||
static_cast<QKeySequenceEdit*>(keyboard_tree->itemWidget(item, 1))->setKeySequence(ks);
|
||||
static_cast<KeySequenceEditor*>(keyboard_tree->itemWidget(item, 1))->reset_to_default();
|
||||
}
|
||||
}
|
||||
|
||||
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;i<key_shortcut_fields.size();i++) {
|
||||
key_shortcut_fields.at(i)->reset_to_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +179,58 @@ bool PreferencesDialog::refine_shortcut_list(const QString &s, QTreeWidgetItem*
|
||||
|
||||
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;i<key_shortcut_fields.size();i++) {
|
||||
int index = ba.indexOf(key_shortcut_fields.at(i)->action_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;i<key_shortcut_fields.size();i++) {
|
||||
QString s = key_shortcut_fields.at(i)->export_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() {
|
||||
@@ -250,11 +322,24 @@ void PreferencesDialog::setup_ui() {
|
||||
shortcut_layout->addWidget(keyboard_tree);
|
||||
|
||||
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");
|
||||
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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -35,7 +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();
|
||||
|
||||
private:
|
||||
void setup_ui();
|
||||
@@ -55,8 +61,6 @@ private:
|
||||
QVector<QAction*> key_shortcut_actions;
|
||||
QVector<QTreeWidgetItem*> key_shortcut_items;
|
||||
QVector<KeySequenceEditor*> key_shortcut_fields;
|
||||
|
||||
QPushButton* reset_shortcut_button;
|
||||
};
|
||||
|
||||
#endif // PREFERENCESDIALOG_H
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "voideffect.h"
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
#include <QLabel>
|
||||
#include <QFile>
|
||||
|
||||
#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
|
||||
QFile* device = static_cast<QFile*>(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()));
|
||||
|
||||
// force xml writer to expand <effect> tag, ignored when loading
|
||||
stream.writeStartElement("void");
|
||||
stream.writeEndElement();
|
||||
|
||||
if (!bytes.isEmpty()) {
|
||||
// write stored data
|
||||
QIODevice* device = stream.device();
|
||||
device->write(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
+12
-2
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
+31
-43
@@ -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 <QFile>
|
||||
@@ -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) {
|
||||
@@ -62,26 +64,8 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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.";
|
||||
QString tag = stream.name().toString();
|
||||
|
||||
int type;
|
||||
if (tag == "opening") {
|
||||
type = TA_OPENING_TRANSITION;
|
||||
} else if (tag == "closing") {
|
||||
type = TA_CLOSING_TRANSITION;
|
||||
} else {
|
||||
QString tag = stream.name().toString();
|
||||
|
||||
int type;
|
||||
if (tag == "opening") {
|
||||
type = TA_OPENING_TRANSITION;
|
||||
} else if (tag == "closing") {
|
||||
type = TA_CLOSING_TRANSITION;
|
||||
} else {
|
||||
type = TA_NO_TRANSITION;
|
||||
}
|
||||
|
||||
emit start_create_effect_ui(&stream, c, type, meta, effect_length, effect_enabled);
|
||||
|
||||
waitCond.wait(&mutex);
|
||||
type = TA_NO_TRANSITION;
|
||||
}
|
||||
|
||||
emit start_create_effect_ui(&stream, c, type, &effect_name, meta, effect_length, effect_enabled);
|
||||
waitCond.wait(&mutex);
|
||||
}
|
||||
|
||||
void LoadThread::read_next(QXmlStreamReader &stream) {
|
||||
@@ -183,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) {
|
||||
@@ -603,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;
|
||||
@@ -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);
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+125
-68
@@ -248,6 +248,84 @@ void MainWindow::make_inout_menu(QMenu *parent) {
|
||||
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<QAction*> actions = menu->actions();
|
||||
for (int i=0;i<actions.size();i++) {
|
||||
QAction* a = actions.at(i);
|
||||
if (a->menu() != 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<QAction*> menus = menuBar()->actions();
|
||||
for (int i=0;i<menus.size();i++) {
|
||||
QMenu* menu = menus.at(i)->menu();
|
||||
kbd_shortcut_processor(shortcut_bytes, menu, false, first);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::save_shortcuts(const QString& fn) {
|
||||
// save main menu actions
|
||||
QList<QAction*> menus = menuBar()->actions();
|
||||
QByteArray shortcut_file;
|
||||
for (int i=0;i<menus.size();i++) {
|
||||
QMenu* menu = menus.at(i)->menu();
|
||||
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() {
|
||||
AboutDialog a(this);
|
||||
a.exec();
|
||||
@@ -319,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() {
|
||||
@@ -442,47 +520,6 @@ bool MainWindow::can_close_project() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void kbd_shortcut_processor(QByteArray& file, QMenu* menu, bool save) {
|
||||
QList<QAction*> actions = menu->actions();
|
||||
for (int i=0;i<actions.size();i++) {
|
||||
QAction* a = actions.at(i);
|
||||
if (a->menu() != 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);
|
||||
@@ -497,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"));
|
||||
|
||||
@@ -642,6 +684,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,6 +814,14 @@ void MainWindow::setup_menus() {
|
||||
set_name_and_marker->setCheckable(true);
|
||||
set_name_and_marker->setData(reinterpret_cast<quintptr>(&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<quintptr>(&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<quintptr>(&config.pause_at_out_point));
|
||||
|
||||
tools_menu->addSeparator();
|
||||
|
||||
no_autoscroll = tools_menu->addAction("No Auto-Scroll", this, SLOT(set_autoscroll()));
|
||||
@@ -795,21 +848,13 @@ 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();
|
||||
|
||||
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<QAction*> menus = menuBar->actions();
|
||||
QByteArray shortcut_bytes = shortcut_path.readAll();
|
||||
for (int i=0;i<menus.size();i++) {
|
||||
QMenu* menu = menus.at(i)->menu();
|
||||
kbd_shortcut_processor(shortcut_bytes, menu, false);
|
||||
}
|
||||
}
|
||||
load_shortcuts(get_config_path() + "/shortcuts", true);
|
||||
}
|
||||
|
||||
void MainWindow::set_bool_action_checked(QAction *a) {
|
||||
@@ -862,20 +907,7 @@ void MainWindow::closeEvent(QCloseEvent *e) {
|
||||
dout << "[ERROR] Failed to save layout";
|
||||
}
|
||||
|
||||
// save main menu actions
|
||||
QList<QAction*> menus = menuBar()->actions();
|
||||
QByteArray shortcut_file;
|
||||
for (int i=0;i<menus.size();i++) {
|
||||
QMenu* menu = menus.at(i)->menu();
|
||||
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();
|
||||
@@ -931,6 +963,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()
|
||||
@@ -1066,6 +1120,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);
|
||||
@@ -1103,12 +1159,13 @@ void MainWindow::fileMenu_About_To_Be_Shown() {
|
||||
open_recent->setEnabled(true);
|
||||
for (int i=0;i<recent_projects.size();i++) {
|
||||
QAction* action = open_recent->addAction(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);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ public:
|
||||
void make_new_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;
|
||||
|
||||
public slots:
|
||||
@@ -56,6 +59,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();
|
||||
@@ -131,6 +136,7 @@ private:
|
||||
|
||||
// file menu actions
|
||||
QMenu* open_recent;
|
||||
QAction* clear_open_recent_action;
|
||||
|
||||
// view menu actions
|
||||
QAction* track_lines;
|
||||
@@ -172,6 +178,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;
|
||||
|
||||
@@ -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 +=
|
||||
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
<summary xml:lang="pt_BR">Editor de vídeo não-linear</summary>
|
||||
<summary xml:lang="es">Editor de video no lineal</summary>
|
||||
<summary xml:lang="ru">Нелинейный видеоредактор</summary>
|
||||
<summary xml:lang="uk">Нелінійний відеоредактор</summary>
|
||||
<summary xml:lang="uk_UA">Нелінійний відеоредактор</summary>
|
||||
<description><p>Olive is a free non-linear video editor aiming to provide a fully-featured alternative to high-end professional video editing software.</p></description>
|
||||
<description xml:lang="pt_BR"><p>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.</p></description>
|
||||
<description xml:lang="es"><p>Olive es un editor de video no lineal libre que apunta a brindar una alternativa completa al software de edición de video profesional.</p></description>
|
||||
<description xml:lang="ru"><p>Olive — свободный нелинейный видеоредактор, задуманный как полноценная замена закрытым коммерческим продуктам.</p></description>
|
||||
<description xml:lang="uk"><p>Olive — вільний нелінійний відеоредактор, задуманий як повноцінна заміна закритим комерційним продуктам.</p></description>
|
||||
<description xml:lang="uk_UA"><p>Olive — вільний нелінійний відеоредактор, задуманий як повноцінна заміна закритим комерційним продуктам.</p></description>
|
||||
<url type="homepage">https://www.olivevideoeditor.org</url>
|
||||
<url type="donation">https://www.patreon.com/olivevideoeditor</url>
|
||||
<url type="bugtracker">https://github.com/olive-editor/olive/issues</url>
|
||||
|
||||
@@ -41,7 +41,6 @@ EffectControls::EffectControls(QWidget *parent) :
|
||||
|
||||
setup_ui();
|
||||
|
||||
init_effects();
|
||||
clear_effects(false);
|
||||
headers->viewer = panel_sequence_viewer;
|
||||
headers->snapping = false;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QUndoCommand>
|
||||
#include <QMutex>
|
||||
|
||||
struct Clip;
|
||||
class QMenu;
|
||||
@@ -50,6 +51,8 @@ public:
|
||||
|
||||
ResizableScrollBar* horizontalScrollBar;
|
||||
QScrollBar* verticalScrollBar;
|
||||
|
||||
QMutex effects_loaded;
|
||||
public slots:
|
||||
void update_keyframes();
|
||||
private slots:
|
||||
|
||||
@@ -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");
|
||||
|
||||
+44
-12
@@ -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,6 +315,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";
|
||||
@@ -455,6 +469,18 @@ void Viewer::set_sb_max() {
|
||||
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() {
|
||||
QWidget* contents = new QWidget();
|
||||
|
||||
@@ -499,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);
|
||||
@@ -530,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);
|
||||
@@ -637,17 +663,23 @@ 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)) {
|
||||
&& 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,6 +96,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 +117,8 @@ private:
|
||||
|
||||
bool cue_recording_internal;
|
||||
QTimer recording_flasher;
|
||||
|
||||
long previous_playhead;
|
||||
};
|
||||
|
||||
#endif // VIEWER_H
|
||||
|
||||
+178
-165
@@ -45,7 +45,6 @@
|
||||
#include <QMenu>
|
||||
|
||||
QVector<EffectMeta> effects;
|
||||
QMutex effects_loaded;
|
||||
|
||||
Effect* create_effect(Clip* c, const EffectMeta* em) {
|
||||
if (!em->filename.isEmpty()) {
|
||||
@@ -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,191 +280,193 @@ 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;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "name") {
|
||||
row_name = attr.value().toString();
|
||||
while (!reader.atEnd()) {
|
||||
if (reader.name() == "row" && reader.isStartElement()) {
|
||||
QString row_name;
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "name") {
|
||||
row_name = attr.value().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!row_name.isEmpty()) {
|
||||
EffectRow* row = add_row(row_name);
|
||||
while (!reader.atEnd() && !(reader.name() == "row" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
if (reader.name() == "field" && reader.isStartElement()) {
|
||||
int type = EFFECT_TYPE_VIDEO;
|
||||
QString id;
|
||||
if (!row_name.isEmpty()) {
|
||||
EffectRow* row = add_row(row_name);
|
||||
while (!reader.atEnd() && !(reader.name() == "row" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
if (reader.name() == "field" && reader.isStartElement()) {
|
||||
int type = EFFECT_TYPE_VIDEO;
|
||||
QString id;
|
||||
|
||||
// get field type
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "type") {
|
||||
QString comp = attr.value().toString().toUpper();
|
||||
if (comp == "DOUBLE") {
|
||||
type = EFFECT_FIELD_DOUBLE;
|
||||
} else if (comp == "BOOL") {
|
||||
type = EFFECT_FIELD_BOOL;
|
||||
} else if (comp == "COLOR") {
|
||||
type = EFFECT_FIELD_COLOR;
|
||||
} else if (comp == "COMBO") {
|
||||
type = EFFECT_FIELD_COMBO;
|
||||
} else if (comp == "FONT") {
|
||||
type = EFFECT_FIELD_FONT;
|
||||
} else if (comp == "STRING") {
|
||||
type = EFFECT_FIELD_STRING;
|
||||
} else if (comp == "FILE") {
|
||||
type = EFFECT_FIELD_FILE;
|
||||
// get field type
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "type") {
|
||||
QString comp = attr.value().toString().toUpper();
|
||||
if (comp == "DOUBLE") {
|
||||
type = EFFECT_FIELD_DOUBLE;
|
||||
} else if (comp == "BOOL") {
|
||||
type = EFFECT_FIELD_BOOL;
|
||||
} else if (comp == "COLOR") {
|
||||
type = EFFECT_FIELD_COLOR;
|
||||
} else if (comp == "COMBO") {
|
||||
type = EFFECT_FIELD_COMBO;
|
||||
} else if (comp == "FONT") {
|
||||
type = EFFECT_FIELD_FONT;
|
||||
} else if (comp == "STRING") {
|
||||
type = EFFECT_FIELD_STRING;
|
||||
} else if (comp == "FILE") {
|
||||
type = EFFECT_FIELD_FILE;
|
||||
}
|
||||
} else if (attr.name() == "id") {
|
||||
id = attr.value().toString();
|
||||
}
|
||||
} else if (attr.name() == "id") {
|
||||
id = attr.value().toString();
|
||||
}
|
||||
}
|
||||
|
||||
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;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_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());
|
||||
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;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_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;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "r") {
|
||||
color.setRed(attr.value().toInt());
|
||||
} else if (attr.name() == "g") {
|
||||
color.setGreen(attr.value().toInt());
|
||||
} else if (attr.name() == "b") {
|
||||
color.setBlue(attr.value().toInt());
|
||||
} else if (attr.name() == "rf") {
|
||||
color.setRedF(attr.value().toFloat());
|
||||
} else if (attr.name() == "gf") {
|
||||
color.setGreenF(attr.value().toFloat());
|
||||
} else if (attr.name() == "bf") {
|
||||
color.setBlueF(attr.value().toFloat());
|
||||
} else if (attr.name() == "hex") {
|
||||
color.setNamedColor(attr.value().toString());
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
QColor color;
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "r") {
|
||||
color.setRed(attr.value().toInt());
|
||||
} else if (attr.name() == "g") {
|
||||
color.setGreen(attr.value().toInt());
|
||||
} else if (attr.name() == "b") {
|
||||
color.setBlue(attr.value().toInt());
|
||||
} else if (attr.name() == "rf") {
|
||||
color.setRedF(attr.value().toFloat());
|
||||
} else if (attr.name() == "gf") {
|
||||
color.setGreenF(attr.value().toFloat());
|
||||
} else if (attr.name() == "bf") {
|
||||
color.setBlueF(attr.value().toFloat());
|
||||
} else if (attr.name() == "hex") {
|
||||
color.setNamedColor(attr.value().toString());
|
||||
}
|
||||
}
|
||||
field->set_color_value(color);
|
||||
}
|
||||
field->set_color_value(color);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_string_value(attr.value().toString());
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_string_value(attr.value().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_bool_value(attr.value() == "1");
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_bool_value(attr.value() == "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
{
|
||||
int combo_index = 0;
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
combo_index = attr.value().toInt();
|
||||
break;
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
{
|
||||
int combo_index = 0;
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
combo_index = attr.value().toInt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!reader.atEnd() && !(reader.name() == "field" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
if (reader.name() == "option" && reader.isStartElement()) {
|
||||
while (!reader.atEnd() && !(reader.name() == "field" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
field->add_combo_item(reader.text().toString(), 0);
|
||||
if (reader.name() == "option" && reader.isStartElement()) {
|
||||
reader.readNext();
|
||||
field->add_combo_item(reader.text().toString(), 0);
|
||||
}
|
||||
}
|
||||
field->set_combo_index(combo_index);
|
||||
}
|
||||
field->set_combo_index(combo_index);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_font_name(attr.value().toString());
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_font_name(attr.value().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_FILE:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "filename") {
|
||||
field->set_filename(attr.value().toString());
|
||||
break;
|
||||
case EFFECT_FIELD_FILE:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "filename") {
|
||||
field->set_filename(attr.value().toString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (reader.name() == "shader" && reader.isStartElement()) {
|
||||
enable_shader = true;
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "vert") {
|
||||
vertPath = attr.value().toString();
|
||||
} else if (attr.name() == "frag") {
|
||||
fragPath = attr.value().toString();
|
||||
}
|
||||
}
|
||||
}/* else if (reader.name() == "superimpose" && reader.isStartElement()) {
|
||||
enable_superimpose = true;
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "script") {
|
||||
QFile script_file(get_effects_dir() + "/" + attr.value().toString());
|
||||
if (script_file.open(QFile::ReadOnly)) {
|
||||
script = script_file.readAll();
|
||||
} else {
|
||||
dout << "[ERROR] Failed to open superimpose script file for" << em->filename;
|
||||
enable_superimpose = false;
|
||||
} else if (reader.name() == "shader" && reader.isStartElement()) {
|
||||
enable_shader = true;
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "vert") {
|
||||
vertPath = attr.value().toString();
|
||||
} else if (attr.name() == "frag") {
|
||||
fragPath = attr.value().toString();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
reader.readNext();
|
||||
}
|
||||
}/* else if (reader.name() == "superimpose" && reader.isStartElement()) {
|
||||
enable_superimpose = true;
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "script") {
|
||||
QFile script_file(get_effects_dir() + "/" + attr.value().toString());
|
||||
if (script_file.open(QFile::ReadOnly)) {
|
||||
script = script_file.readAll();
|
||||
} else {
|
||||
dout << "[ERROR] Failed to open superimpose script file for" << em->filename;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -793,16 +794,28 @@ void Effect::open() {
|
||||
validate_meta_path();
|
||||
bool glsl_compiled = true;
|
||||
if (!vertPath.isEmpty()) {
|
||||
if (!glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath)) {
|
||||
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)) {
|
||||
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) glslProgram->link();
|
||||
isOpen = true;
|
||||
}
|
||||
} else {
|
||||
|
||||
+1
-3
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user