merged with master
This commit is contained in:
@@ -26,7 +26,8 @@
|
||||
/**
|
||||
* @brief The AboutDialog class
|
||||
*
|
||||
* The About dialog (accessible through Help > About). Contains license and version information.
|
||||
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from
|
||||
* anywhere
|
||||
*/
|
||||
class AboutDialog : public QDialog
|
||||
{
|
||||
|
||||
@@ -32,7 +32,7 @@ class ActionSearchList;
|
||||
* @brief The ActionSearch class
|
||||
*
|
||||
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
|
||||
* rather than browsing through the menu bar.
|
||||
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
|
||||
*/
|
||||
class ActionSearch : public QDialog
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
* @brief The AdvancedVideoDialog class
|
||||
*
|
||||
* A dialog for interfacing with VideoCodecParams, a struct for more advanced video settings sometimes specific to
|
||||
* one codec.
|
||||
* one codec. Primarily a companion to ExportDialog which will provide the VideoCodecParams reference,
|
||||
*/
|
||||
class AdvancedVideoDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "autocutsilencedialog.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include "timeline/sequence.h"
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/timeline.h"
|
||||
|
||||
AutoCutSilenceDialog::AutoCutSilenceDialog(QWidget *parent, QVector<Clip*> clips) :
|
||||
QDialog(parent),
|
||||
clips_(clips)
|
||||
{
|
||||
setWindowTitle(tr("Cut Silence"));
|
||||
|
||||
QVBoxLayout* main_layout = new QVBoxLayout(this);
|
||||
QGridLayout* grid = new QGridLayout();
|
||||
grid->setSpacing(6);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Attack Threshold:"), this), 0, 0);
|
||||
attack_threshold = new LabelSlider(this);
|
||||
attack_threshold->SetDecimalPlaces(0);
|
||||
grid->addWidget(attack_threshold, 0, 1);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Attack Time:"), this), 1, 0);
|
||||
attack_time = new LabelSlider(this);
|
||||
attack_time->SetDecimalPlaces(0);
|
||||
grid->addWidget(attack_time, 1, 1);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Release Threshold:"), this), 2, 0);
|
||||
release_threshold = new LabelSlider(this);
|
||||
release_threshold->SetDecimalPlaces(0);
|
||||
grid->addWidget(release_threshold, 2, 1);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Release Time:"), this), 3, 0);
|
||||
release_time = new LabelSlider(this);
|
||||
release_time->SetDecimalPlaces(0);
|
||||
grid->addWidget(release_time, 3, 1);
|
||||
|
||||
main_layout->addLayout(grid);
|
||||
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
buttonBox->setCenterButtons(true);
|
||||
main_layout->addWidget(buttonBox);
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
|
||||
}
|
||||
|
||||
int AutoCutSilenceDialog::exec()
|
||||
{
|
||||
default_attack_threshold = 5;
|
||||
current_attack_threshold = 5;
|
||||
default_attack_time = 2;
|
||||
current_attack_time = 2;
|
||||
default_release_threshold = 2;
|
||||
current_release_threshold = 2;
|
||||
default_release_time = 5;
|
||||
current_release_time = 5;
|
||||
|
||||
attack_threshold->SetMinimum(1);
|
||||
attack_threshold->setEnabled(true);
|
||||
attack_threshold->SetDefault(default_attack_threshold);
|
||||
attack_threshold->SetValue(current_attack_threshold);
|
||||
|
||||
attack_time->SetMinimum(1);
|
||||
attack_time->setEnabled(true);
|
||||
attack_time->SetDefault(default_attack_time);
|
||||
attack_time->SetValue(current_attack_time);
|
||||
|
||||
release_threshold->SetMinimum(1);
|
||||
release_threshold->setEnabled(true);
|
||||
release_threshold->SetDefault(default_release_threshold);
|
||||
release_threshold->SetValue(current_release_threshold);
|
||||
|
||||
release_time->SetMinimum(1);
|
||||
release_time->setEnabled(true);
|
||||
release_time->SetDefault(default_release_time);
|
||||
release_time->SetValue(current_release_time);
|
||||
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
||||
void AutoCutSilenceDialog::accept() {
|
||||
|
||||
current_attack_threshold = attack_threshold->value();
|
||||
current_attack_time = attack_time->value();
|
||||
current_release_threshold = release_threshold->value();
|
||||
current_release_time = release_time->value();
|
||||
|
||||
cut_silence();
|
||||
|
||||
update_ui(true);
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void AutoCutSilenceDialog::cut_silence() {
|
||||
// Loop over clips provided to this dialog
|
||||
for (int j=0;j<clips_.size();j++) {
|
||||
|
||||
Clip* clip = clips_.at(j);
|
||||
|
||||
// Check if this clip is an audio footage clip
|
||||
if (clip->track() >= 0
|
||||
&& clip->media() != nullptr
|
||||
&& clip->media_stream()->preview_done) { // TODO provide warning for preview not being done
|
||||
|
||||
QVector<long> split_positions;
|
||||
|
||||
int clip_start = clip->timeline_in();
|
||||
const FootageStream* ms = clip->media_stream();
|
||||
|
||||
long media_length = clip->media_length();
|
||||
int preview_size = ms->audio_preview.length();
|
||||
float chunk_size = (float)preview_size/media_length; // how many audio samples to read for each fotogram
|
||||
|
||||
int sample_size = qMax(current_attack_time, current_release_time)+1;
|
||||
|
||||
bool attack = false; // status flags
|
||||
bool release = false;
|
||||
|
||||
QVector<qint8> vols;
|
||||
vols.resize(sample_size);
|
||||
vols.fill(0);
|
||||
|
||||
// loop through the entire sequence
|
||||
for (long i=clip_start;i<media_length+clip_start;i++) {
|
||||
long start = ((i-clip_start)*chunk_size); //audio samples are read relative to the clip, not absolute to the timeline
|
||||
int circular_index = i%sample_size;
|
||||
|
||||
// read the current sample into the circular array
|
||||
qint8 tmp = 0;
|
||||
for (int k=start; k<start+chunk_size; k++){
|
||||
tmp = qMax(tmp, qint8(qRound(double(ms->audio_preview.at(k)))));
|
||||
}
|
||||
vols[circular_index] = tmp;
|
||||
|
||||
//for debug:
|
||||
//qInfo() << "i:" << i <<" - "<< i/30 <<":"<< i%30 << " - volume:" << vols[circular_index] <<"\n";
|
||||
|
||||
int overthreshold = 0;
|
||||
int cut_idx = 0; //how much to cut (backwards)
|
||||
|
||||
// if current volume value is above threshold
|
||||
if (vols[circular_index] >= current_attack_threshold && !attack){ // if we get one sample over the threshold
|
||||
for(int k=0; k<sample_size; k++){ // count how many times this happened before within sample_size range (avoid false activations)
|
||||
int back_idx = (((circular_index-k)%sample_size)+sample_size)%sample_size; // positive modulus
|
||||
if(vols[back_idx] > current_attack_threshold){
|
||||
overthreshold++;
|
||||
cut_idx = k+1;
|
||||
}
|
||||
}
|
||||
// if we reached threshold over the set tolerance
|
||||
if(overthreshold >= current_attack_time){
|
||||
split_positions.append(i-cut_idx);
|
||||
attack = true;
|
||||
release = false;
|
||||
//qInfo() << "\n\n Current vol: "<<vols[circular_index]<<" attack at " << i-cut_idx << "\n\n";
|
||||
}
|
||||
overthreshold = 0;
|
||||
cut_idx = 0;
|
||||
}else if (vols[circular_index] < current_release_threshold && !release){ // if we get one sample under the threshold
|
||||
for(int k=0; k<sample_size; k++){ // count how many times this happened before within sample_size range
|
||||
int back_idx = (((circular_index-k)%sample_size)+sample_size)%sample_size; // positive modulus
|
||||
if(vols[back_idx] < current_release_threshold)
|
||||
overthreshold++;
|
||||
}
|
||||
// if we reached threshold over the set tolerance
|
||||
if(overthreshold >= current_release_time){ // must be <= sample_size
|
||||
attack = false;
|
||||
release = true;
|
||||
split_positions.append(i);
|
||||
//qInfo() << "\n\n Current vol: "<<vols[circular_index]<<" release at " << i << "\n\n";
|
||||
}
|
||||
overthreshold = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
// NO GOOD VERY BAD TEST CODE
|
||||
int clip_index = -1;
|
||||
for (int i=0;i<olive::ActiveSequence->clips.size();i++) {
|
||||
if (olive::ActiveSequence->clips.at(i).get() == clip) {
|
||||
clip_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(clip_index > -1);
|
||||
|
||||
panel_timeline->split_clip_at_positions(ca, clip_index, split_positions);
|
||||
olive::UndoStack.push(ca);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SILENCEDIALOG_H
|
||||
#define SILENCEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "timeline/clip.h"
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
class AutoCutSilenceDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AutoCutSilenceDialog(QWidget* parent, QVector<Clip*> clips);
|
||||
public slots:
|
||||
virtual int exec() override;
|
||||
private slots:
|
||||
virtual void accept() override;
|
||||
private:
|
||||
void cut_silence();
|
||||
|
||||
QVector<Clip*> clips_;
|
||||
|
||||
LabelSlider* attack_threshold;
|
||||
LabelSlider* release_threshold;
|
||||
LabelSlider* attack_time;
|
||||
LabelSlider* release_time;
|
||||
|
||||
int default_attack_threshold;
|
||||
int current_attack_threshold;
|
||||
int default_release_threshold;
|
||||
int current_release_threshold;
|
||||
int default_attack_time;
|
||||
int current_attack_time;
|
||||
int default_release_time;
|
||||
int current_release_time;
|
||||
};
|
||||
|
||||
#endif // SILENCEDIALOG_H
|
||||
@@ -30,7 +30,8 @@
|
||||
/**
|
||||
* @brief The ClipPropertiesDialog class
|
||||
*
|
||||
* A dialog for setting Clip properties, accessible by right clicking a Clip and clicking "Properties".
|
||||
* A dialog for setting Clip properties, accessible by right clicking a Clip and clicking "Properties". This can be
|
||||
* run from anywhere provided it's given a valid array of Clip objects.
|
||||
*/
|
||||
class ClipPropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
/**
|
||||
* @brief The DebugDialog class
|
||||
*
|
||||
* A dialog to display the current debug output.
|
||||
* A dialog to display the current debug output. This dialog is omnipresent and shown and hidden when the user wants
|
||||
* to see it. For efficiency, it will not update if it's hidden.
|
||||
*/
|
||||
class DebugDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
@@ -69,6 +70,9 @@ private:
|
||||
};
|
||||
|
||||
namespace olive {
|
||||
/**
|
||||
* @brief Omnipresent instance of DebugDialog to be shown or hidden as the user wants
|
||||
*/
|
||||
extern DebugDialog* DebugDialog;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
/**
|
||||
* @brief The DemoNotice class
|
||||
*
|
||||
* Simple dialog shown on startup to introduce Olive as alpha software (in release builds).
|
||||
* Simple dialog shown on startup to introduce Olive as alpha software (in release builds). Can be run from anywhere,
|
||||
* but there should be no reason to create it outside of the application launch.
|
||||
*
|
||||
* To be phased out as Olive gains maturity.
|
||||
*/
|
||||
class DemoNotice : public QDialog
|
||||
{
|
||||
|
||||
@@ -569,6 +569,9 @@ void ExportDialog::StartExport() {
|
||||
connect(export_thread_, SIGNAL(ProgressChanged(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
|
||||
connect(renderCancel, SIGNAL(clicked(bool)), export_thread_, SLOT(Interrupt()));
|
||||
|
||||
// Close all effects in effect controls (prevents UI threading issues)
|
||||
panel_effect_controls->Clear();
|
||||
|
||||
// Close all currently open clips
|
||||
close_active_clips(olive::ActiveSequence.get());
|
||||
|
||||
|
||||
@@ -35,7 +35,9 @@
|
||||
/**
|
||||
* @brief The ExportDialog class
|
||||
*
|
||||
* The dialog to initiate an export.
|
||||
* The dialog to initiate an export. Requires a valid Sequence to be set in olive::ActiveSequence or the result is
|
||||
* defined (most likely a crash), so you should always do a `nullptr` check on olive::ActiveSequence before constructing
|
||||
* this dialog.
|
||||
*/
|
||||
class ExportDialog : public QDialog
|
||||
{
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
/**
|
||||
* @brief The LoadDialog class
|
||||
*
|
||||
* Shows a modal dialog for loading a project. Designed to be connected to a LoadThread object.
|
||||
* Shows a modal dialog for loading a project. Designed to be connected to a LoadThread object. This dialog should
|
||||
* generally not be created directly, use OliveGlobal::LoadProject (or its variants) to correctly set up a LoadDialog
|
||||
* and LoadThread and connect them to each other.
|
||||
*/
|
||||
class LoadDialog : public QDialog
|
||||
{
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
/**
|
||||
* @brief The MediaPropertiesDialog class
|
||||
*
|
||||
* A dialog for setting properties on Media.
|
||||
* A dialog for setting properties on Media. This can be loaded from any part of the application provided it's given
|
||||
* a valid Media object.
|
||||
*/
|
||||
class MediaPropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -39,19 +39,26 @@
|
||||
#include "panels/timeline.h"
|
||||
#include "project/media.h"
|
||||
#include "rendering/audio.h"
|
||||
#include "global/config.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
NewSequenceDialog::NewSequenceDialog(QWidget *parent, Media *existing) :
|
||||
NewSequenceDialog::NewSequenceDialog(QWidget *parent, Media *existing, Sequence* iexisting_sequence) :
|
||||
QDialog(parent),
|
||||
existing_item(existing)
|
||||
existing_item(existing),
|
||||
existing_sequence(iexisting_sequence)
|
||||
{
|
||||
Q_ASSERT(!(existing != nullptr && iexisting_sequence != nullptr));
|
||||
|
||||
setup_ui();
|
||||
|
||||
if (existing != nullptr) {
|
||||
existing_sequence = existing->to_sequence();
|
||||
existing_sequence = existing->to_sequence().get();
|
||||
}
|
||||
|
||||
if (existing_sequence != nullptr) {
|
||||
setWindowTitle(tr("Editing \"%1\"").arg(existing_sequence->name));
|
||||
|
||||
width_numeric->setValue(existing_sequence->width);
|
||||
@@ -80,6 +87,12 @@ void NewSequenceDialog::set_sequence_name(const QString& s) {
|
||||
sequence_name_edit->setText(s);
|
||||
}
|
||||
|
||||
void NewSequenceDialog::SetNameEditable(bool enabled)
|
||||
{
|
||||
sequence_name_edit->setVisible(enabled);
|
||||
sequence_name_label->setVisible(enabled);
|
||||
}
|
||||
|
||||
void NewSequenceDialog::accept() {
|
||||
if (existing_sequence == nullptr) {
|
||||
|
||||
@@ -98,7 +111,7 @@ void NewSequenceDialog::accept() {
|
||||
panel_project->create_sequence_internal(ca, s, true, nullptr);
|
||||
olive::UndoStack.push(ca);
|
||||
|
||||
} else {
|
||||
} else if (existing_item != nullptr) {
|
||||
|
||||
// The dialog was given an existing Sequence object, so we'll apply the changes to it
|
||||
|
||||
@@ -106,7 +119,7 @@ void NewSequenceDialog::accept() {
|
||||
|
||||
double multiplier = frame_rate_combobox->currentData().toDouble() / existing_sequence->frame_rate;
|
||||
|
||||
EditSequenceCommand* esc = new EditSequenceCommand(existing_item, existing_sequence);
|
||||
EditSequenceCommand* esc = new EditSequenceCommand(existing_item, existing_item->to_sequence());
|
||||
esc->name = sequence_name_edit->text();
|
||||
esc->width = width_numeric->value();
|
||||
esc->height = height_numeric->value();
|
||||
@@ -123,6 +136,18 @@ void NewSequenceDialog::accept() {
|
||||
}
|
||||
|
||||
olive::UndoStack.push(ca);
|
||||
|
||||
} else if (existing_sequence != nullptr) {
|
||||
|
||||
// This dialog was given an existing Sequence without a Media wrapper - therefore just directly apply the settings
|
||||
|
||||
existing_sequence->name = sequence_name_edit->text();
|
||||
existing_sequence->width = width_numeric->value();
|
||||
existing_sequence->height = height_numeric->value();
|
||||
existing_sequence->frame_rate = frame_rate_combobox->currentData().toDouble();
|
||||
existing_sequence->audio_frequency = audio_frequency_combobox->currentData().toInt();
|
||||
existing_sequence->audio_layout = AV_CH_LAYOUT_STEREO;
|
||||
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
@@ -210,13 +235,13 @@ void NewSequenceDialog::setup_ui() {
|
||||
videoLayout->addWidget(new QLabel(tr("Width:"), this), 0, 0, 1, 1);
|
||||
width_numeric = new QSpinBox(videoGroupBox);
|
||||
width_numeric->setMaximum(9999);
|
||||
width_numeric->setValue(1920);
|
||||
width_numeric->setValue(olive::CurrentConfig.default_sequence_width);
|
||||
videoLayout->addWidget(width_numeric, 0, 2, 1, 2);
|
||||
|
||||
videoLayout->addWidget(new QLabel(tr("Height:"), this), 1, 0, 1, 2);
|
||||
height_numeric = new QSpinBox(videoGroupBox);
|
||||
height_numeric->setMaximum(9999);
|
||||
height_numeric->setValue(1080);
|
||||
height_numeric->setValue(olive::CurrentConfig.default_sequence_height);
|
||||
videoLayout->addWidget(height_numeric, 1, 2, 1, 2);
|
||||
|
||||
videoLayout->addWidget(new QLabel(tr("Frame Rate:"), this), 2, 0, 1, 1);
|
||||
@@ -232,7 +257,11 @@ void NewSequenceDialog::setup_ui() {
|
||||
frame_rate_combobox->addItem("50 FPS", 50.0);
|
||||
frame_rate_combobox->addItem("59.94 FPS", 59.94);
|
||||
frame_rate_combobox->addItem("60 FPS", 60.0);
|
||||
frame_rate_combobox->setCurrentIndex(6);
|
||||
for (int i=0;i<frame_rate_combobox->count();i++) {
|
||||
if (qFuzzyCompare(frame_rate_combobox->itemData(i).toDouble(), olive::CurrentConfig.default_sequence_framerate)) {
|
||||
frame_rate_combobox->setCurrentIndex(i);
|
||||
}
|
||||
}
|
||||
videoLayout->addWidget(frame_rate_combobox, 2, 2, 1, 2);
|
||||
|
||||
videoLayout->addWidget(new QLabel(tr("Pixel Aspect Ratio:"), this), 4, 0, 1, 1);
|
||||
@@ -258,7 +287,11 @@ void NewSequenceDialog::setup_ui() {
|
||||
|
||||
audio_frequency_combobox = new QComboBox(audioGroupBox);
|
||||
combobox_audio_sample_rates(audio_frequency_combobox);
|
||||
audio_frequency_combobox->setCurrentIndex(4);
|
||||
for (int i=0;i<audio_frequency_combobox->count();i++) {
|
||||
if (audio_frequency_combobox->itemData(i) == olive::CurrentConfig.default_sequence_audio_frequency) {
|
||||
audio_frequency_combobox->setCurrentIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
audioLayout->addWidget(audio_frequency_combobox, 0, 1, 1, 1);
|
||||
|
||||
@@ -268,7 +301,8 @@ void NewSequenceDialog::setup_ui() {
|
||||
QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget);
|
||||
nameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
nameLayout->addWidget(new QLabel(tr("Name:"), this));
|
||||
sequence_name_label = new QLabel(tr("Name:"));
|
||||
nameLayout->addWidget(sequence_name_label);
|
||||
|
||||
sequence_name_edit = new QLineEdit(nameWidget);
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
/**
|
||||
* @brief The NewSequenceDialog class
|
||||
*
|
||||
* A dialog that creates a new (or edits an existing) Sequence object.
|
||||
* A dialog that creates a new (or edits an existing) Sequence object. Can be run from any part of the application.
|
||||
*/
|
||||
class NewSequenceDialog : public QDialog
|
||||
{
|
||||
@@ -50,8 +50,13 @@ public:
|
||||
*
|
||||
* Set this to a Sequence object (wrapped in a Media object) to edit an existing Sequence,
|
||||
* or leave as nullptr to create a new one.
|
||||
*
|
||||
* @param existing_sequence
|
||||
*
|
||||
* If your Sequence object is not wrapped in a Media object, use this to reference a raw Sequence pointer. You must
|
||||
* not use both existing_sequence AND existing - one must be nullptr.
|
||||
*/
|
||||
explicit NewSequenceDialog(QWidget *parent = nullptr, Media* existing = nullptr);
|
||||
explicit NewSequenceDialog(QWidget *parent = nullptr, Media* existing = nullptr, Sequence* iexisting_sequence = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Set the name for the new Sequence
|
||||
@@ -68,6 +73,17 @@ public:
|
||||
*/
|
||||
void set_sequence_name(const QString& s);
|
||||
|
||||
/**
|
||||
* @brief Set whether the Sequence's name can be edited
|
||||
*
|
||||
* This defaults to TRUE.
|
||||
*
|
||||
* @param enabled
|
||||
*
|
||||
* TRUE to allow the user to edit the Sequence's name. FALSE if not.
|
||||
*/
|
||||
void SetNameEditable(bool enabled);
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Override accept function to create/edit a Sequence
|
||||
@@ -86,16 +102,16 @@ private slots:
|
||||
void preset_changed(int index);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Internal reference to an existing Sequence (if one was provided to the constructor)
|
||||
*/
|
||||
SequencePtr existing_sequence;
|
||||
|
||||
/**
|
||||
* @brief Internal reference to an existing Media wrapper (if one was provided to the constructor)
|
||||
*/
|
||||
Media* existing_item;
|
||||
|
||||
/**
|
||||
* @brief Internal reference to an existing Sequence (if one was provided to the constructor)
|
||||
*/
|
||||
Sequence* existing_sequence;
|
||||
|
||||
/**
|
||||
* @brief Internal function to create the dialog's UI
|
||||
*/
|
||||
@@ -136,6 +152,13 @@ private:
|
||||
*/
|
||||
QComboBox* audio_frequency_combobox;
|
||||
|
||||
/**
|
||||
* @brief Label marker for setting the Sequence's name
|
||||
*
|
||||
* Primarily a persistent class reference so it can be hidden with SetNameEditable() alongside sequence_name_edit.
|
||||
*/
|
||||
QLabel* sequence_name_label;
|
||||
|
||||
/**
|
||||
* @brief Line edit to set the Sequence's name
|
||||
*/
|
||||
|
||||
+121
-28
@@ -50,7 +50,9 @@
|
||||
#include "rendering/audio.h"
|
||||
#include "rendering/bitdepths.h"
|
||||
#include "panels/panels.h"
|
||||
#include "ui/columnedgridlayout.h"
|
||||
#include "ui/mainwindow.h"
|
||||
#include "dialogs/newsequencedialog.h"
|
||||
|
||||
KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a)
|
||||
: QKeySequenceEdit(parent), action(a) {
|
||||
@@ -59,7 +61,6 @@ KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a)
|
||||
|
||||
void KeySequenceEditor::set_action_shortcut() {
|
||||
action->setShortcut(keySequence());
|
||||
action->setShortcutContext(Qt::ApplicationShortcut);
|
||||
}
|
||||
|
||||
void KeySequenceEditor::reset_to_default() {
|
||||
@@ -73,7 +74,7 @@ QString KeySequenceEditor::action_name() {
|
||||
QString KeySequenceEditor::export_shortcut() {
|
||||
QString ks = keySequence().toString();
|
||||
if (ks != action->property("default")) {
|
||||
return action->property("id").toString() + "\t" + keySequence().toString();
|
||||
return action->property("id").toString() + "\t" + ks;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -82,12 +83,18 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Preferences"));
|
||||
|
||||
setup_ui();
|
||||
|
||||
recordingComboBox->setCurrentIndex(olive::CurrentConfig.recording_mode - 1);
|
||||
imgSeqFormatEdit->setText(olive::CurrentConfig.img_seq_formats);
|
||||
|
||||
setup_kbd_shortcuts(olive::MainWindow->menuBar());
|
||||
|
||||
// set up default sequence
|
||||
default_sequence.name = tr("Default Sequence");
|
||||
default_sequence.width = olive::CurrentConfig.default_sequence_width;
|
||||
default_sequence.height = olive::CurrentConfig.default_sequence_height;
|
||||
default_sequence.frame_rate = olive::CurrentConfig.default_sequence_framerate;
|
||||
default_sequence.audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency;
|
||||
default_sequence.audio_layout = olive::CurrentConfig.default_sequence_audio_channel_layout;
|
||||
}
|
||||
|
||||
void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* parent) {
|
||||
@@ -235,6 +242,15 @@ void PreferencesDialog::update_ocio_config(const QString &s)
|
||||
}
|
||||
#endif
|
||||
|
||||
void PreferencesDialog::AddBoolPair(QCheckBox *ui, bool *value, bool restart_required)
|
||||
{
|
||||
bool_ui.append(ui);
|
||||
bool_value.append(value);
|
||||
bool_restart_required.append(restart_required);
|
||||
|
||||
ui->setChecked(*value);
|
||||
}
|
||||
|
||||
void PreferencesDialog::setup_kbd_shortcuts(QMenuBar* menubar) {
|
||||
QList<QAction*> menus = menubar->actions();
|
||||
|
||||
@@ -299,13 +315,20 @@ void PreferencesDialog::accept() {
|
||||
reload_effects = true;
|
||||
}
|
||||
|
||||
bool bool_requires_restart = false;
|
||||
for (int i=0;i<bool_restart_required.size();i++) {
|
||||
if (bool_restart_required.at(i)
|
||||
&& bool_ui.at(i)->isChecked() != *bool_value.at(i)) {
|
||||
bool_requires_restart = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any settings will require a restart of Olive
|
||||
if (olive::CurrentConfig.use_software_fallback != use_software_fallbacks_checkbox->isChecked()
|
||||
if (bool_requires_restart
|
||||
|| olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value()
|
||||
|| olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()
|
||||
#ifdef Q_OS_WIN32
|
||||
|| olive::CurrentConfig.use_native_menu_styling != native_menus->isChecked()
|
||||
#endif
|
||||
|| olive::CurrentConfig.css_path != custom_css_fn->text()
|
||||
|| olive::CurrentConfig.style != static_cast<olive::styling::Style>(ui_style->currentData().toInt())) {
|
||||
|
||||
// any changes to these settings will require a restart - ask the user if we should do one now or later
|
||||
@@ -346,25 +369,19 @@ void PreferencesDialog::accept() {
|
||||
}
|
||||
|
||||
// save settings from UI to backend
|
||||
if (olive::CurrentConfig.css_path != custom_css_fn->text()) {
|
||||
olive::CurrentConfig.css_path = custom_css_fn->text();
|
||||
olive::MainWindow->Restyle();
|
||||
}
|
||||
|
||||
olive::CurrentConfig.css_path = custom_css_fn->text();
|
||||
olive::CurrentConfig.recording_mode = recordingComboBox->currentIndex() + 1;
|
||||
olive::CurrentConfig.img_seq_formats = imgSeqFormatEdit->text();
|
||||
olive::CurrentConfig.upcoming_queue_size = upcoming_queue_spinbox->value();
|
||||
olive::CurrentConfig.upcoming_queue_type = upcoming_queue_type->currentIndex();
|
||||
olive::CurrentConfig.previous_queue_size = previous_queue_spinbox->value();
|
||||
olive::CurrentConfig.previous_queue_type = previous_queue_type->currentIndex();
|
||||
olive::CurrentConfig.add_default_effects_to_clips = add_default_effects_to_clips->isChecked();
|
||||
|
||||
olive::CurrentConfig.preferred_audio_output = audio_output_devices->currentData().toString();
|
||||
olive::CurrentConfig.preferred_audio_input = audio_input_devices->currentData().toString();
|
||||
olive::CurrentConfig.audio_rate = audio_sample_rate->currentData().toInt();
|
||||
|
||||
olive::CurrentConfig.effect_textbox_lines = effect_textbox_lines_field->value();
|
||||
olive::CurrentConfig.use_software_fallback = use_software_fallbacks_checkbox->isChecked();
|
||||
olive::CurrentConfig.language_file = language_combobox->currentData().toString();
|
||||
|
||||
olive::CurrentConfig.enable_color_management = enable_color_management->isChecked();
|
||||
@@ -394,10 +411,17 @@ void PreferencesDialog::accept() {
|
||||
#endif
|
||||
|
||||
|
||||
olive::CurrentConfig.default_sequence_width = default_sequence.width;
|
||||
olive::CurrentConfig.default_sequence_height = default_sequence.height;
|
||||
olive::CurrentConfig.default_sequence_framerate = default_sequence.frame_rate;
|
||||
olive::CurrentConfig.default_sequence_audio_frequency = default_sequence.audio_frequency;
|
||||
olive::CurrentConfig.default_sequence_audio_channel_layout = default_sequence.audio_layout;
|
||||
|
||||
for (int i=0;i<bool_ui.size();i++) {
|
||||
*bool_value[i] = bool_ui.at(i)->isChecked();
|
||||
}
|
||||
|
||||
olive::CurrentConfig.style = static_cast<olive::styling::Style>(ui_style->currentData().toInt());
|
||||
#ifdef Q_OS_WIN
|
||||
olive::CurrentConfig.use_native_menu_styling = native_menus->isChecked();
|
||||
#endif
|
||||
|
||||
// Check if the thumbnail or waveform icon
|
||||
if (olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value()
|
||||
@@ -615,6 +639,13 @@ void PreferencesDialog::delete_all_previews() {
|
||||
}
|
||||
}
|
||||
|
||||
void PreferencesDialog::edit_default_sequence_settings()
|
||||
{
|
||||
NewSequenceDialog nsd(this, nullptr, &default_sequence);
|
||||
nsd.SetNameEditable(false);
|
||||
nsd.exec();
|
||||
}
|
||||
|
||||
void PreferencesDialog::setup_ui() {
|
||||
QVBoxLayout* verticalLayout = new QVBoxLayout(this);
|
||||
QTabWidget* tabWidget = new QTabWidget(this);
|
||||
@@ -696,15 +727,15 @@ void PreferencesDialog::setup_ui() {
|
||||
row++;
|
||||
|
||||
// General -> Use Software Fallbacks When Possible
|
||||
use_software_fallbacks_checkbox = new QCheckBox(general_tab);
|
||||
use_software_fallbacks_checkbox->setText(tr("Use Software Fallbacks When Possible"));
|
||||
use_software_fallbacks_checkbox->setChecked(olive::CurrentConfig.use_software_fallback);
|
||||
QCheckBox* use_software_fallbacks_checkbox = new QCheckBox(tr("Use Software Fallbacks When Possible"));
|
||||
AddBoolPair(use_software_fallbacks_checkbox, &olive::CurrentConfig.use_software_fallback, true);
|
||||
general_layout->addWidget(use_software_fallbacks_checkbox, row, 0, 1, 4);
|
||||
|
||||
row++;
|
||||
|
||||
// General -> Default Sequence Settings
|
||||
QPushButton* default_sequence_settings = new QPushButton(tr("Default Sequence Settings"));
|
||||
connect(default_sequence_settings, SIGNAL(clicked(bool)), this, SLOT(edit_default_sequence_settings()));
|
||||
general_layout->addWidget(default_sequence_settings);
|
||||
|
||||
tabWidget->addTab(general_tab, tr("General"));
|
||||
@@ -713,11 +744,72 @@ void PreferencesDialog::setup_ui() {
|
||||
QWidget* behavior_tab = new QWidget(this);
|
||||
tabWidget->addTab(behavior_tab, tr("Behavior"));
|
||||
|
||||
QVBoxLayout* behavior_tab_layout = new QVBoxLayout(behavior_tab);
|
||||
ColumnedGridLayout* behavior_tab_layout = new ColumnedGridLayout(behavior_tab, 2);
|
||||
|
||||
add_default_effects_to_clips = new QCheckBox(tr("Add Default Effects to New Clips"));
|
||||
add_default_effects_to_clips->setChecked(olive::CurrentConfig.add_default_effects_to_clips);
|
||||
behavior_tab_layout->addWidget(add_default_effects_to_clips);
|
||||
QCheckBox* add_default_effects_to_clips = new QCheckBox(tr("Add Default Effects to New Clips"));
|
||||
AddBoolPair(add_default_effects_to_clips, &olive::CurrentConfig.add_default_effects_to_clips);
|
||||
behavior_tab_layout->Add(add_default_effects_to_clips);
|
||||
|
||||
QCheckBox* auto_seek_to_beginning = new QCheckBox(tr("Automatically Seek to the Beginning When Playing at the End of a Sequence"));
|
||||
AddBoolPair(auto_seek_to_beginning, &olive::CurrentConfig.auto_seek_to_beginning);
|
||||
behavior_tab_layout->Add(auto_seek_to_beginning);
|
||||
|
||||
QCheckBox* selecting_also_seeks = new QCheckBox(tr("Selecting Also Seeks"));
|
||||
AddBoolPair(selecting_also_seeks, &olive::CurrentConfig.select_also_seeks);
|
||||
behavior_tab_layout->Add(selecting_also_seeks);
|
||||
|
||||
QCheckBox* edit_tool_also_seeks = new QCheckBox(tr("Edit Tool Also Seeks"));
|
||||
AddBoolPair(edit_tool_also_seeks, &olive::CurrentConfig.edit_tool_also_seeks);
|
||||
behavior_tab_layout->Add(edit_tool_also_seeks);
|
||||
|
||||
QCheckBox* edit_tool_selects_links = new QCheckBox(tr("Edit Tool Selects Links"));
|
||||
AddBoolPair(edit_tool_selects_links, &olive::CurrentConfig.edit_tool_selects_links);
|
||||
behavior_tab_layout->Add(edit_tool_selects_links);
|
||||
|
||||
QCheckBox* seek_also_selects = new QCheckBox(tr("Seek Also Selects"));
|
||||
AddBoolPair(seek_also_selects, &olive::CurrentConfig.seek_also_selects);
|
||||
behavior_tab_layout->Add(seek_also_selects);
|
||||
|
||||
QCheckBox* seek_to_end_of_pastes = new QCheckBox(tr("Seek to the End of Pastes"));
|
||||
AddBoolPair(seek_to_end_of_pastes, &olive::CurrentConfig.paste_seeks);
|
||||
behavior_tab_layout->Add(seek_to_end_of_pastes);
|
||||
|
||||
QCheckBox* scroll_wheel_zooms = new QCheckBox(tr("Scroll Wheel Zooms"));
|
||||
scroll_wheel_zooms->setToolTip(tr("Hold CTRL to toggle this setting"));
|
||||
AddBoolPair(scroll_wheel_zooms, &olive::CurrentConfig.scroll_zooms);
|
||||
behavior_tab_layout->Add(scroll_wheel_zooms);
|
||||
|
||||
QCheckBox* invert_timeline_scroll_axes = new QCheckBox(tr("Invert Timeline Scroll Axes"));
|
||||
AddBoolPair(invert_timeline_scroll_axes, &olive::CurrentConfig.invert_timeline_scroll_axes);
|
||||
behavior_tab_layout->Add(invert_timeline_scroll_axes);
|
||||
|
||||
QCheckBox* enable_drag_files_to_timeline = new QCheckBox(tr("Enable Drag Files to Timeline"));
|
||||
AddBoolPair(enable_drag_files_to_timeline, &olive::CurrentConfig.enable_drag_files_to_timeline);
|
||||
behavior_tab_layout->Add(enable_drag_files_to_timeline);
|
||||
|
||||
QCheckBox* autoscale_by_default = new QCheckBox(tr("Auto-Scale By Default"));
|
||||
AddBoolPair(autoscale_by_default, &olive::CurrentConfig.autoscale_by_default);
|
||||
behavior_tab_layout->Add(autoscale_by_default);
|
||||
|
||||
QCheckBox* enable_seek_to_import = new QCheckBox(tr("Auto-Seek to Imported Clips"));
|
||||
AddBoolPair(enable_seek_to_import, &olive::CurrentConfig.enable_seek_to_import);
|
||||
behavior_tab_layout->Add(enable_seek_to_import);
|
||||
|
||||
QCheckBox* enable_audio_scrubbing = new QCheckBox(tr("Audio Scrubbing"));
|
||||
AddBoolPair(enable_audio_scrubbing, &olive::CurrentConfig.enable_audio_scrubbing);
|
||||
behavior_tab_layout->Add(enable_audio_scrubbing);
|
||||
|
||||
QCheckBox* enable_drop_on_media_to_replace = new QCheckBox(tr("Drop Files on Media to Replace"));
|
||||
AddBoolPair(enable_drop_on_media_to_replace, &olive::CurrentConfig.drop_on_media_to_replace);
|
||||
behavior_tab_layout->Add(enable_drop_on_media_to_replace);
|
||||
|
||||
QCheckBox* enable_hover_focus = new QCheckBox(tr("Enable Hover Focus"));
|
||||
AddBoolPair(enable_hover_focus, &olive::CurrentConfig.hover_focus);
|
||||
behavior_tab_layout->Add(enable_hover_focus);
|
||||
|
||||
QCheckBox* set_name_and_marker = new QCheckBox(tr("Ask For Name When Setting Marker"));
|
||||
AddBoolPair(set_name_and_marker, &olive::CurrentConfig.set_name_with_marker);
|
||||
behavior_tab_layout->Add(set_name_and_marker);
|
||||
|
||||
// Appearance
|
||||
QWidget* appearance_tab = new QWidget(this);
|
||||
@@ -743,8 +835,8 @@ void PreferencesDialog::setup_ui() {
|
||||
#ifdef Q_OS_WIN
|
||||
// Native menu styling is only available on Windows. Environments like Ubuntu and Mac use the native menu system by
|
||||
// default
|
||||
native_menus = new QCheckBox(tr("Use Native Menu Styling"));
|
||||
native_menus->setChecked(olive::CurrentConfig.use_native_menu_styling);
|
||||
QCheckBox* native_menus = new QCheckBox(tr("Use Native Menu Styling"));
|
||||
AddBoolPair(native_menus, &olive::CurrentConfig.use_native_menu_styling, true);
|
||||
appearance_layout->addWidget(native_menus, row, 0, 1, 3);
|
||||
|
||||
row++;
|
||||
@@ -879,6 +971,7 @@ void PreferencesDialog::setup_ui() {
|
||||
recordingComboBox = new QComboBox(general_tab);
|
||||
recordingComboBox->addItem(tr("Mono"));
|
||||
recordingComboBox->addItem(tr("Stereo"));
|
||||
recordingComboBox->setCurrentIndex(olive::CurrentConfig.recording_mode - 1);
|
||||
audio_tab_layout->addWidget(recordingComboBox, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
+193
-7
@@ -45,7 +45,8 @@ class KeySequenceEditor;
|
||||
/**
|
||||
* @brief The PreferencesDialog class
|
||||
*
|
||||
* A dialog for the global application settings. Mostly an interface for Config.
|
||||
* A dialog for the global application settings. Mostly an interface for Config. Can be loaded from any part of the
|
||||
* application.
|
||||
*/
|
||||
class PreferencesDialog : public QDialog
|
||||
{
|
||||
@@ -130,6 +131,11 @@ private slots:
|
||||
void update_ocio_config(const QString&);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Shows a NewSequenceDialog attached to default_sequence
|
||||
*/
|
||||
void edit_default_sequence_settings();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@@ -186,23 +192,81 @@ private:
|
||||
void populate_ocio_menus(OCIO::ConstConfigRcPtr config);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UI widget for editing the CSS filename
|
||||
*/
|
||||
QLineEdit* custom_css_fn;
|
||||
|
||||
/**
|
||||
* @brief UI widget for editing the list of extensions to detect image sequences from
|
||||
*/
|
||||
QLineEdit* imgSeqFormatEdit;
|
||||
|
||||
/**
|
||||
* @brief UI widget for editing the recording channels
|
||||
*/
|
||||
QComboBox* recordingComboBox;
|
||||
|
||||
|
||||
/**
|
||||
* @brief UI widget for editing keyboard shortcuts
|
||||
*/
|
||||
QTreeWidget* keyboard_tree;
|
||||
|
||||
/**
|
||||
* @brief UI widget for editing the upcoming queue size
|
||||
*/
|
||||
QDoubleSpinBox* upcoming_queue_spinbox;
|
||||
|
||||
/**
|
||||
* @brief UI widget for editing the upcoming queue type
|
||||
*/
|
||||
QComboBox* upcoming_queue_type;
|
||||
|
||||
/**
|
||||
* @brief UI widget for editing the previous queue size
|
||||
*/
|
||||
QDoubleSpinBox* previous_queue_spinbox;
|
||||
|
||||
/**
|
||||
* @brief UI widget for editing the previous queue type
|
||||
*/
|
||||
QComboBox* previous_queue_type;
|
||||
|
||||
/**
|
||||
* @brief UI widget for editing the size of textboxes in the EffectControls panel
|
||||
*/
|
||||
QSpinBox* effect_textbox_lines_field;
|
||||
QCheckBox* use_software_fallbacks_checkbox;
|
||||
|
||||
/**
|
||||
* @brief UI widget for selecting the output audio device
|
||||
*/
|
||||
QComboBox* audio_output_devices;
|
||||
|
||||
/**
|
||||
* @brief UI widget for selecting the input audio device
|
||||
*/
|
||||
QComboBox* audio_input_devices;
|
||||
|
||||
/**
|
||||
* @brief UI widget for selecting the audio sampling rates
|
||||
*/
|
||||
QComboBox* audio_sample_rate;
|
||||
|
||||
/**
|
||||
* @brief UI widget for selecting the UI language
|
||||
*/
|
||||
QComboBox* language_combobox;
|
||||
|
||||
/**
|
||||
* @brief UI widget for selecting the resolution of the thumbnails to generate
|
||||
*/
|
||||
QSpinBox* thumbnail_res_spinbox;
|
||||
|
||||
/**
|
||||
* @brief UI widget for selecting the resolution of the waveforms to generate
|
||||
*/
|
||||
QSpinBox* waveform_res_spinbox;
|
||||
QCheckBox* add_default_effects_to_clips;
|
||||
|
||||
QCheckBox* enable_color_management;
|
||||
QLineEdit* ocio_config_file;
|
||||
@@ -210,26 +274,148 @@ private:
|
||||
QComboBox* ocio_view;
|
||||
QComboBox* ocio_look;
|
||||
|
||||
/**
|
||||
* @brief UI widget for selecting the current UI style
|
||||
*/
|
||||
QComboBox* ui_style;
|
||||
Sequence sequence_settings;
|
||||
#ifdef Q_OS_WIN
|
||||
QCheckBox* native_menus;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Stored default Sequence object
|
||||
*
|
||||
* Default Sequence settings are loaded into an actual Sequence object that can be loaded into NewSequenceDialog
|
||||
* for the sake of familiarity with the user.
|
||||
*/
|
||||
Sequence default_sequence;
|
||||
|
||||
/**
|
||||
* @brief List of keyboard shortcut actions that can be triggered (links with key_shortcut_items and
|
||||
* key_shortcut_fields)
|
||||
*/
|
||||
QVector<QAction*> key_shortcut_actions;
|
||||
|
||||
/**
|
||||
* @brief List of keyboard shortcut items in keyboard_tree corresponding to existing actions (links with
|
||||
* key_shortcut_actions and key_shortcut_fields)
|
||||
*/
|
||||
QVector<QTreeWidgetItem*> key_shortcut_items;
|
||||
|
||||
/**
|
||||
* @brief List of keyboard shortcut editing fields in keyboard_tree corresponding to existing actions (links with
|
||||
* key_shortcut_actions and key_shortcut_fields)
|
||||
*/
|
||||
QVector<KeySequenceEditor*> key_shortcut_fields;
|
||||
|
||||
/**
|
||||
* @brief Add an automated QCheckBox+boolean value pair
|
||||
*
|
||||
* Many preferences are simple true/false (or on/off) options. Rather than adding a QCheckBox for each one and
|
||||
* manually setting its checked value to the configuration setting (and vice versa when saving), this convenience
|
||||
* function will add it to an automated set of checkboxes, automatically setting the checked state to the current
|
||||
* setting, and then saving the new checked state back to the setting when the user accepts the changes (clicks OK).
|
||||
*
|
||||
* @param ui
|
||||
*
|
||||
* A valid QCheckBox item. This function does not take ownership of the QWidget or place it in a layout anywhere.
|
||||
*
|
||||
* @param value
|
||||
*
|
||||
* A pointer to the Boolean value this QCheckBox should be shared with. The QCheckBox widget's checked state will be
|
||||
* set to the value of this pointer.
|
||||
*
|
||||
* @param restart_required
|
||||
*
|
||||
* Defaults to FALSE, set this to TRUE if changing this setting should prompt the user for a restart of Olive before
|
||||
* the setting change takes effect.
|
||||
*/
|
||||
void AddBoolPair(QCheckBox* ui, bool* value, bool restart_required = false);
|
||||
|
||||
/**
|
||||
* @brief Internal array managed by AddBoolPair(). Do not access this directly.
|
||||
*/
|
||||
QVector<QCheckBox*> bool_ui;
|
||||
|
||||
/**
|
||||
* @brief Internal array managed by AddBoolPair(). Do not access this directly.
|
||||
*/
|
||||
QVector<bool*> bool_value;
|
||||
|
||||
/**
|
||||
* @brief Internal array managed by AddBoolPair(). Do not access this directly.
|
||||
*/
|
||||
QVector<bool> bool_restart_required;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The KeySequenceEditor class
|
||||
*
|
||||
* Simple derived class of QKeySequenceEdit that attaches to a QAction and provides functions for transferring
|
||||
* keyboard shortcuts to and from it.
|
||||
*/
|
||||
class KeySequenceEditor : public QKeySequenceEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief KeySequenceEditor Constructor
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* QWidget parent.
|
||||
*
|
||||
* @param a
|
||||
*
|
||||
* The QAction to link to. This cannot be changed throughout the lifetime of a KeySequenceEditor.
|
||||
*/
|
||||
KeySequenceEditor(QWidget *parent, QAction* a);
|
||||
|
||||
/**
|
||||
* @brief Sets the attached QAction's shortcut to the shortcut entered in this field.
|
||||
*
|
||||
* This is not done automatically in case the user cancels out of the Preferences dialog, in which case the
|
||||
* expectation is that the changes made will not be saved. Therefore, this needs to be triggered manually when
|
||||
* PreferencesDialog saves.
|
||||
*/
|
||||
void set_action_shortcut();
|
||||
|
||||
/**
|
||||
* @brief Set this shortcut back to the QAction's default shortcut
|
||||
*
|
||||
* Each QAction contains the default shortcut in its `property("default")` and can be used to restore the default
|
||||
* "hard-coded" shortcut with this function.
|
||||
*
|
||||
* This function does not save the default shortcut back into the QAction, it simply loads the default shortcut from
|
||||
* the QAction into this edit field. To save it into the QAction, it's necessary to call set_action_shortcut() after
|
||||
* calling this function.
|
||||
*/
|
||||
void reset_to_default();
|
||||
|
||||
/**
|
||||
* @brief Return attached QAction's unique ID
|
||||
*
|
||||
* Each of Olive's menu actions has a unique string ID (that, unlike the text, is not translated) for matching with
|
||||
* an external shortcut configuration file. The ID is stored in the QAction's `property("id")`. This function returns
|
||||
* that ID.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The QAction's unique ID.
|
||||
*/
|
||||
QString action_name();
|
||||
|
||||
/**
|
||||
* @brief Serialize this shortcut entry into a string that can be saved to a file
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A string serialization of this shortcut. The format is "[ID]\t[SEQUENCE]" where [ID] is the attached QAction's
|
||||
* unique identifier and [SEQUENCE] is the current keyboard shortcut in the field (NOT necessarily the shortcut in
|
||||
* the QAction). If the entered shortcut is the same as the QAction's default shortcut, the return value is empty
|
||||
* because a default shortcut does not need to be saved to a file.
|
||||
*/
|
||||
QString export_shortcut();
|
||||
private:
|
||||
/**
|
||||
* @brief Internal reference to the linked QAction
|
||||
*/
|
||||
QAction* action;
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
/**
|
||||
* @brief The ProxyDialog class
|
||||
*
|
||||
* Dialog to set up proxy generation of footage
|
||||
* Dialog to set up proxy generation of footage. This dialog can be called from anywhere provided it's given a valid
|
||||
* array of Media and will start all proxy generation.
|
||||
*/
|
||||
class ProxyDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -50,16 +50,16 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media* old_media
|
||||
use_same_media_in_points->setChecked(true);
|
||||
layout->addWidget(use_same_media_in_points);
|
||||
|
||||
QHBoxLayout* buttons = new QHBoxLayout();
|
||||
QHBoxLayout* buttons = new QHBoxLayout();
|
||||
|
||||
buttons->addStretch();
|
||||
|
||||
QPushButton* replace_button = new QPushButton(tr("Replace"), this);
|
||||
connect(replace_button, SIGNAL(clicked(bool)), this, SLOT(replace()));
|
||||
connect(replace_button, SIGNAL(clicked(bool)), this, SLOT(accept()));
|
||||
buttons->addWidget(replace_button);
|
||||
|
||||
QPushButton* cancel_button = new QPushButton(tr("Cancel"), this);
|
||||
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(close()));
|
||||
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject()));
|
||||
buttons->addWidget(cancel_button);
|
||||
|
||||
buttons->addStretch();
|
||||
@@ -69,7 +69,7 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media* old_media
|
||||
tree->setModel(&olive::project_model);
|
||||
}
|
||||
|
||||
void ReplaceClipMediaDialog::replace() {
|
||||
void ReplaceClipMediaDialog::accept() {
|
||||
QModelIndexList selected_items = tree->selectionModel()->selectedRows();
|
||||
if (selected_items.size() != 1) {
|
||||
QMessageBox::critical(
|
||||
@@ -77,23 +77,23 @@ void ReplaceClipMediaDialog::replace() {
|
||||
tr("No media selected"),
|
||||
tr("Please select a media to replace with or click 'Cancel'."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
);
|
||||
} else {
|
||||
Media* new_item = static_cast<Media*>(selected_items.at(0).internalPointer());
|
||||
Media* new_item = static_cast<Media*>(selected_items.at(0).internalPointer());
|
||||
if (media == new_item) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Same media selected"),
|
||||
tr("You selected the same media that you're replacing. Please select a different one or click 'Cancel'."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
);
|
||||
} else if (new_item->get_type() == MEDIA_TYPE_FOLDER) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Folder selected"),
|
||||
tr("You cannot replace footage with a folder."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
);
|
||||
} else {
|
||||
if (new_item->get_type() == MEDIA_TYPE_SEQUENCE && olive::ActiveSequence == new_item->to_sequence()) {
|
||||
QMessageBox::critical(
|
||||
@@ -101,16 +101,16 @@ void ReplaceClipMediaDialog::replace() {
|
||||
tr("Active sequence selected"),
|
||||
tr("You cannot insert a sequence into itself."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
);
|
||||
} else {
|
||||
ReplaceClipMediaCommand* rcmc = new ReplaceClipMediaCommand(
|
||||
media,
|
||||
new_item,
|
||||
use_same_media_in_points->isChecked()
|
||||
);
|
||||
);
|
||||
|
||||
for (int i=0;i<olive::ActiveSequence->clips.size();i++) {
|
||||
ClipPtr c = olive::ActiveSequence->clips.at(i);
|
||||
ClipPtr c = olive::ActiveSequence->clips.at(i);
|
||||
if (c != nullptr && c->media() == media) {
|
||||
rcmc->clips.append(c);
|
||||
}
|
||||
@@ -118,7 +118,7 @@ void ReplaceClipMediaDialog::replace() {
|
||||
|
||||
olive::UndoStack.push(rcmc);
|
||||
|
||||
close();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,15 +28,54 @@
|
||||
#include "ui/sourcetable.h"
|
||||
#include "project/projectelements.h"
|
||||
|
||||
/**
|
||||
* @brief The ReplaceClipMediaDialog class
|
||||
*
|
||||
* A dialog to replace all Clips using a certain Media with a different Media. This dialog can be run from anywhere
|
||||
* provided it's given a valid Media object.
|
||||
*/
|
||||
class ReplaceClipMediaDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ReplaceClipMediaDialog(QWidget* parent, Media* old_media);
|
||||
/**
|
||||
* @brief ReplaceClipMediaDialog Constructor
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* QWidget parent. Usually MainWindow or Project panel.
|
||||
*
|
||||
* @param old_media
|
||||
*
|
||||
* A valid Media object which will be used to scan the currently active Sequence for Clips using it.
|
||||
*/
|
||||
ReplaceClipMediaDialog(QWidget* parent, Media* old_media);
|
||||
private slots:
|
||||
void replace();
|
||||
/**
|
||||
* @brief Overrided accept for when the user clicks "Replace"
|
||||
*
|
||||
* Checks whether the requested replace is valid using the following criteria:
|
||||
* * Any Media is selected
|
||||
* * The selected Media is not the same Media that the user is trying to replace
|
||||
* * The Media is not a folder
|
||||
* * The Media is not the currently active Sequence
|
||||
*/
|
||||
virtual void accept() override;
|
||||
private:
|
||||
Media* media;
|
||||
/**
|
||||
* @brief Internal pointer to the Media we're replacing
|
||||
*/
|
||||
Media* media;
|
||||
|
||||
/**
|
||||
* @brief Tree widget to show Project's media
|
||||
*/
|
||||
QTreeView* tree;
|
||||
|
||||
/**
|
||||
* @brief CheckBox for using the same media in points
|
||||
*
|
||||
* When the starting point of a Clip is trimmed (i.e. the Clip no longer starts at 0),
|
||||
*/
|
||||
QCheckBox* use_same_media_in_points;
|
||||
};
|
||||
|
||||
|
||||
@@ -85,16 +85,16 @@ SpeedDialog::SpeedDialog(QWidget *parent, QVector<Clip*> clips) : QDialog(parent
|
||||
connect(duration, SIGNAL(valueChanged(double)), this, SLOT(duration_update()));
|
||||
}
|
||||
|
||||
void SpeedDialog::run() {
|
||||
int SpeedDialog::exec() {
|
||||
bool enable_frame_rate = false;
|
||||
bool multiple_audio = false;
|
||||
maintain_pitch->setEnabled(false);
|
||||
|
||||
default_frame_rate = qSNaN();
|
||||
current_frame_rate = qSNaN();
|
||||
current_percent = qSNaN();
|
||||
default_length = -1;
|
||||
current_length = -1;
|
||||
double default_frame_rate = qSNaN();
|
||||
double current_frame_rate = qSNaN();
|
||||
double current_percent = qSNaN();
|
||||
long default_length = -1;
|
||||
long current_length = -1;
|
||||
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
Clip* c = clips_.at(i);
|
||||
@@ -180,7 +180,7 @@ void SpeedDialog::run() {
|
||||
duration->SetDefault(default_length);
|
||||
duration->SetValue((current_length == -1) ? qSNaN() : current_length);
|
||||
|
||||
exec();
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
||||
void SpeedDialog::percent_update() {
|
||||
|
||||
+82
-10
@@ -27,34 +27,106 @@
|
||||
#include "timeline/clip.h"
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
/**
|
||||
* @brief The SpeedDialog class
|
||||
*
|
||||
* A dialog for setting the speed of one or more Clips. This can be run from anywhere provided it's given a valid
|
||||
* array of Clips.
|
||||
*
|
||||
* It's preferable ot
|
||||
*/
|
||||
class SpeedDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief SpeedDialog Constructor
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* QWidget parent. Usually MainWindow or Timeline panel.
|
||||
*
|
||||
* @param clips
|
||||
*
|
||||
* A valid array of Clips to change the speed of.
|
||||
*/
|
||||
SpeedDialog(QWidget* parent, QVector<Clip*> clips);
|
||||
|
||||
void run();
|
||||
public slots:
|
||||
/**
|
||||
* @brief Override of exec() to set up current Clip speed data just before opening
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The result of QDialog::exec(), a DialogCode result.
|
||||
*/
|
||||
virtual int exec() override;
|
||||
private slots:
|
||||
/**
|
||||
* @brief Override of accept() to perform the selected changes on the Clips
|
||||
*/
|
||||
virtual void accept() override;
|
||||
|
||||
/**
|
||||
* @brief Slot when the speed percentage field is changed by the user
|
||||
*
|
||||
* The three fields (percent, duration, and frame rate) all work in tandem to create a speed multipler for the
|
||||
* Clip. Each has a slot for when one of the fields changes to update the others appropriately so they all have the
|
||||
* same speed multipler.
|
||||
*/
|
||||
void percent_update();
|
||||
|
||||
/**
|
||||
* @brief Slot when the duration field is changed by the user
|
||||
*
|
||||
* The three fields (percent, duration, and frame rate) all work in tandem to create a speed multipler for the
|
||||
* Clip. Each has a slot for when one of the fields changes to update the others appropriately so they all have the
|
||||
* same speed multipler.
|
||||
*/
|
||||
void duration_update();
|
||||
|
||||
/**
|
||||
* @brief Slot when the frame rate field is changed by the user
|
||||
*
|
||||
* The three fields (percent, duration, and frame rate) all work in tandem to create a speed multipler for the
|
||||
* Clip. Each has a slot for when one of the fields changes to update the others appropriately so they all have the
|
||||
* same speed multipler.
|
||||
*/
|
||||
void frame_rate_update();
|
||||
void accept();
|
||||
private:
|
||||
/**
|
||||
* @brief Internal array of Clip objects
|
||||
*/
|
||||
QVector<Clip*> clips_;
|
||||
|
||||
/**
|
||||
* @brief Speed percentage field
|
||||
*/
|
||||
LabelSlider* percent;
|
||||
|
||||
/**
|
||||
* @brief Duration field
|
||||
*/
|
||||
LabelSlider* duration;
|
||||
|
||||
/**
|
||||
* @brief Frame rate field
|
||||
*/
|
||||
LabelSlider* frame_rate;
|
||||
|
||||
/**
|
||||
* @brief UI widget for setting the Clip's reverse value
|
||||
*/
|
||||
QCheckBox* reverse;
|
||||
QCheckBox* maintain_pitch;
|
||||
QCheckBox* ripple;
|
||||
|
||||
double default_frame_rate;
|
||||
double current_frame_rate;
|
||||
double current_percent;
|
||||
long default_length;
|
||||
long current_length;
|
||||
/**
|
||||
* @brief UI widget for setting the Clip's maintain pitch value
|
||||
*/
|
||||
QCheckBox* maintain_pitch;
|
||||
|
||||
/**
|
||||
* @brief UI widget for setting whether to ripple Clips around these changes or not
|
||||
*/
|
||||
QCheckBox* ripple;
|
||||
};
|
||||
|
||||
#endif // SPEEDDIALOG_H
|
||||
|
||||
@@ -44,15 +44,6 @@ TextEditDialog::TextEditDialog(QWidget *parent, const QString &s, bool rich_text
|
||||
if (rich_text) {
|
||||
QHBoxLayout* toolbar = new QHBoxLayout();
|
||||
|
||||
// Bold Button
|
||||
/*
|
||||
bold_button = new QPushButton();
|
||||
bold_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/bold.svg", false));
|
||||
bold_button->setCheckable(true);
|
||||
connect(bold_button, SIGNAL(clicked(bool)), this, SLOT(SetBold(bool)));
|
||||
toolbar->addWidget(bold_button);
|
||||
*/
|
||||
|
||||
// Italic Button
|
||||
italic_button = new QPushButton();
|
||||
italic_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/italic.svg", false));
|
||||
@@ -156,10 +147,14 @@ TextEditDialog::TextEditDialog(QWidget *parent, const QString &s, bool rich_text
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
buttons->setCenterButtons(true);
|
||||
layout->addWidget(buttons);
|
||||
connect(buttons, SIGNAL(accepted()), this, SLOT(save()));
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(cancel()));
|
||||
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
|
||||
// Connect the cursor position changing to the rich text toolbar buttons updating (so for example, when italic text
|
||||
// is selected, the italic button will be pressed)
|
||||
connect(textEdit, SIGNAL(cursorPositionChanged()), this, SLOT(UpdateUIFromTextCursor()));
|
||||
|
||||
// Set the widget's text based on the rich text mode
|
||||
if (rich_text_) {
|
||||
textEdit->setHtml(s);
|
||||
} else {
|
||||
@@ -167,7 +162,7 @@ TextEditDialog::TextEditDialog(QWidget *parent, const QString &s, bool rich_text
|
||||
}
|
||||
|
||||
// Helps ensure the UI elements update correctly at the beginning - when the cursor is at the start, the UI elements
|
||||
// show up blank...
|
||||
// show up blank. Setting it to the end is probably more expected behavior anyway.
|
||||
textEdit->moveCursor(QTextCursor::End);
|
||||
}
|
||||
|
||||
@@ -175,21 +170,9 @@ const QString& TextEditDialog::get_string() {
|
||||
return result_str;
|
||||
}
|
||||
|
||||
void TextEditDialog::save() {
|
||||
void TextEditDialog::accept() {
|
||||
result_str = rich_text_ ? textEdit->toHtml() : textEdit->toPlainText();
|
||||
accept();
|
||||
}
|
||||
|
||||
void TextEditDialog::cancel() {
|
||||
reject();
|
||||
}
|
||||
|
||||
void TextEditDialog::SetBold(bool bold)
|
||||
{
|
||||
QFont f = textEdit->currentFont();
|
||||
f.setBold(bold);
|
||||
textEdit->setCurrentFont(f);
|
||||
UpdateUIFromTextCursor();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void TextEditDialog::SetFontWeight(int i)
|
||||
@@ -197,13 +180,6 @@ void TextEditDialog::SetFontWeight(int i)
|
||||
textEdit->setFontWeight(font_weight->itemData(i).toInt());
|
||||
}
|
||||
|
||||
void TextEditDialog::SetLetterSpacing(qreal spacing)
|
||||
{
|
||||
QFont f = textEdit->currentFont();
|
||||
f.setLetterSpacing(f.letterSpacingType(), spacing);
|
||||
textEdit->setCurrentFont(f);
|
||||
}
|
||||
|
||||
void TextEditDialog::SetAlignmentFromProperty()
|
||||
{
|
||||
textEdit->setAlignment(static_cast<Qt::Alignment>(sender()->property("a").toInt()));
|
||||
|
||||
+123
-8
@@ -28,36 +28,151 @@
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/colorbutton.h"
|
||||
|
||||
/**
|
||||
* @brief The TextEditDialog class
|
||||
*
|
||||
* A separate window for editing text. This window can be resized arbitrarily and also provides a toolbar for rich text
|
||||
* editing (if rich text is enabled). This dialog can be run from anywhere. Once the dialog has closed (i.e. returned
|
||||
* from exec() ), the text entered into it can be retrieved using get_string().
|
||||
*
|
||||
* TODO: Add a live signal for updating the calling function.
|
||||
*/
|
||||
class TextEditDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief TextEditDialog Constructor
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* QWidget parent. Usually MainWindow.
|
||||
*
|
||||
* @param s
|
||||
*
|
||||
* The starting string when the dialog opens. It'll be read as rich text HTML or plain text based on the `rich_text`
|
||||
* parameter (which defaults to rich text HTML). It can also be left empty to start blank.
|
||||
*
|
||||
* @param rich_text
|
||||
*
|
||||
* Set the editing mode of the editor. If TRUE, the dialog will interpret the string in `s` as rich text HTML and also
|
||||
* return rich text HTML through get_string(). It'll also show a toolbar with rich text options (i.e. font, italic,
|
||||
* underline, size, etc.) If FALSE, the dialog will run in plain text mode interpreting the string in `s` as plain
|
||||
* text and returning plain text through get_string(). It also will not show the rich text editing toolbar.
|
||||
*/
|
||||
TextEditDialog(QWidget* parent = nullptr, const QString& s = nullptr, bool rich_text = true);
|
||||
const QString& get_string();
|
||||
signals:
|
||||
void cursorPositionChanged();
|
||||
private slots:
|
||||
void save();
|
||||
void cancel();
|
||||
|
||||
void SetBold(bool bold);
|
||||
/**
|
||||
* @brief Retrieve the current text in the dialog
|
||||
*
|
||||
* This function can be called after the user has accepted the dialog (i.e. made changes and clicked OK).
|
||||
* This will return either plain text or rich text (HTML) depending on the mode it's running in (rich/plain text mode
|
||||
* is set in the constructor). The value this returns only gets updated when the user clicks OK so it cannot be
|
||||
* used to retrieve live text updates from the dialog.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The text entered once the user accepted this dialog.
|
||||
*/
|
||||
const QString& get_string();
|
||||
private slots:
|
||||
/**
|
||||
* @brief Override of accept() to store the entered text string so it can be retrieved by get_string().
|
||||
*/
|
||||
virtual void accept() override;
|
||||
|
||||
/**
|
||||
* @brief Slot for the font_weight combobox to set the font weight based on its data value
|
||||
*
|
||||
* @param i
|
||||
*
|
||||
* Index of the font_weight to retrieve the desired font weight from
|
||||
*/
|
||||
void SetFontWeight(int i);
|
||||
void SetLetterSpacing(qreal spacing);
|
||||
|
||||
/**
|
||||
* @brief Slot for text alignment buttons to set alignment based on their properties
|
||||
*
|
||||
* Intended slot for left_align_button, center_align_button, right_align_button, and justify_align_button. Pulls
|
||||
* from their property("a") value which should be a member of the Qt::Alignment enum.
|
||||
*/
|
||||
void SetAlignmentFromProperty();
|
||||
|
||||
/**
|
||||
* @brief Slot for when the text edit widget's cursor moves so the rich text toolbar can stay up to date
|
||||
*
|
||||
* In rich text mode, different parts of a text document can be formatted in different ways. As the user moves
|
||||
* around the text, the UI buttons should be consistent with whatever text is currently selected. This slot should
|
||||
* therefore be connected to QTextEdit::cursorPositionChanged() and will change the "checked" state of the formatting
|
||||
* buttons and current index of the comboboxes to match the currently selected text.
|
||||
*/
|
||||
void UpdateUIFromTextCursor();
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Internal rich text mode value
|
||||
*
|
||||
* This is set in the constructor and cannot be changed during the lifetime of this dialog.
|
||||
*/
|
||||
bool rich_text_;
|
||||
|
||||
/**
|
||||
* @brief Internal storage of text entered, saved when the user clicks OK
|
||||
*/
|
||||
QString result_str;
|
||||
|
||||
/**
|
||||
* @brief Main text editing widget
|
||||
*/
|
||||
QTextEdit* textEdit;
|
||||
|
||||
/**
|
||||
* @brief Toggle button for setting the italic state of the currently selected text
|
||||
*/
|
||||
QPushButton* italic_button;
|
||||
|
||||
/**
|
||||
* @brief Toggle button for setting the underlined state of the currently selected text
|
||||
*/
|
||||
QPushButton* underline_button;
|
||||
|
||||
/**
|
||||
* @brief ComboBox for the list of font families that the selected text can be set to
|
||||
*/
|
||||
QFontComboBox* font_list;
|
||||
|
||||
/**
|
||||
* @brief ComboBox for the list of font weights that the selected text can be set to
|
||||
*/
|
||||
QComboBox* font_weight;
|
||||
|
||||
/**
|
||||
* @brief A slider to set the current font size
|
||||
*/
|
||||
LabelSlider* font_size;
|
||||
|
||||
/**
|
||||
* @brief A color selector for setting the current text color
|
||||
*/
|
||||
ColorButton* font_color;
|
||||
|
||||
/**
|
||||
* @brief Button for setting the current text row(s) to left alignment
|
||||
*/
|
||||
QPushButton* left_align_button;
|
||||
|
||||
/**
|
||||
* @brief Button for setting the current text row(s) to center alignment
|
||||
*/
|
||||
QPushButton* center_align_button;
|
||||
|
||||
/**
|
||||
* @brief Button for setting the current text row(s) to right alignment
|
||||
*/
|
||||
QPushButton* right_align_button;
|
||||
|
||||
/**
|
||||
* @brief Button for setting the current text row(s) to justified alignment
|
||||
*/
|
||||
QPushButton* justify_align_button;
|
||||
};
|
||||
|
||||
|
||||
+3
-2
@@ -85,9 +85,7 @@ EffectPtr Effect::Create(Clip* c, const EffectMeta* em) {
|
||||
case EFFECT_INTERNAL_SHAKE: return std::make_shared<ShakeEffect>(c, em);
|
||||
case EFFECT_INTERNAL_CORNERPIN: return std::make_shared<CornerPinEffect>(c, em);
|
||||
case EFFECT_INTERNAL_FILLLEFTRIGHT: return std::make_shared<FillLeftRightEffect>(c, em);
|
||||
#ifndef NOVST
|
||||
case EFFECT_INTERNAL_VST: return std::make_shared<VSTHost>(c, em);
|
||||
#endif
|
||||
case EFFECT_INTERNAL_RICHTEXT: return std::make_shared<RichTextEffect>(c, em);
|
||||
}
|
||||
} else if (!em->filename.isEmpty()) {
|
||||
@@ -597,6 +595,9 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
field->keyframes.append(key);
|
||||
}
|
||||
}
|
||||
|
||||
field->Changed();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "global/path.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "global/crossplatformlib.h"
|
||||
#include "global/config.h"
|
||||
|
||||
QMutex olive::effects_loaded;
|
||||
@@ -55,11 +54,9 @@ void load_internal_effects() {
|
||||
em.internal = EFFECT_INTERNAL_PAN;
|
||||
olive::effects.append(em);
|
||||
|
||||
#ifndef NOVST
|
||||
em.name = "VST Plugin 2.x";
|
||||
em.internal = EFFECT_INTERNAL_VST;
|
||||
olive::effects.append(em);
|
||||
#endif
|
||||
|
||||
em.name = "Tone";
|
||||
em.internal = EFFECT_INTERNAL_TONE;
|
||||
|
||||
@@ -60,11 +60,6 @@ bool EffectRow::IsKeyframing() {
|
||||
}
|
||||
|
||||
void EffectRow::SetKeyframingInternal(bool b) {
|
||||
// No need to run this function if the keyframing state isn't actually changing.
|
||||
if (b == keyframing_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetParentEffect()->meta->type != EFFECT_TYPE_TRANSITION) {
|
||||
keyframing_ = b;
|
||||
emit KeyframingSetChanged(keyframing_);
|
||||
@@ -127,7 +122,6 @@ void EffectRow::SetKeyframingEnabled(bool enabled) {
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
SetKeyframingInternal(true);
|
||||
|
||||
}
|
||||
|
||||
@@ -32,18 +32,67 @@ class BoolField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief See Effect::Effect().
|
||||
*/
|
||||
BoolField(EffectRow* parent, const QString& id);
|
||||
|
||||
/**
|
||||
* @brief Get the boolean value at a given timecode
|
||||
*
|
||||
* A convenience function, equivalent to GetValueAt(timecode).toBool()
|
||||
*
|
||||
* @param timecode
|
||||
*
|
||||
* The timecode to retrieve the value at
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The boolean value at this timecode
|
||||
*/
|
||||
bool GetBoolAt(double timecode);
|
||||
|
||||
/**
|
||||
* @brief See EffectField::CreateWidget()
|
||||
*/
|
||||
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
|
||||
|
||||
/**
|
||||
* @brief See EffectField::UpdateWidgetValue()
|
||||
*/
|
||||
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
|
||||
|
||||
/**
|
||||
* @brief See EffectField::ConvertStringToValue()
|
||||
*/
|
||||
virtual QVariant ConvertStringToValue(const QString& s) override;
|
||||
|
||||
/**
|
||||
* @brief See EffectField::ConvertValueToString()
|
||||
*/
|
||||
virtual QString ConvertValueToString(const QVariant& v) override;
|
||||
signals:
|
||||
/**
|
||||
* @brief Emitted whenever the UI widget's boolean value has changed
|
||||
*
|
||||
* For any QCheckBox created through this field's CreateWidget() function, this signal is emitted any time the
|
||||
* checkbox value changes (either through user intervention or keyframing). It is mostly useful for
|
||||
* enabling/disabling/changing other UI elements based on the checked
|
||||
* state of this field's value (e.g. enabling other fields if this field is checked).
|
||||
*
|
||||
* It is NOT a reliable signal that the value has changed at all, as it is only emitted if a widget (created
|
||||
* from CreateWidget() ) is currently active.
|
||||
*/
|
||||
void Toggled(bool);
|
||||
private slots:
|
||||
/**
|
||||
* @brief Internal function connected to any QWidget made from CreateWidget() to update the value based on user input
|
||||
*
|
||||
* @param b
|
||||
*
|
||||
* The current checked state of the QWidget (QCheckBox in this case). Automatically set when this slot is connected
|
||||
* to the QCheckBox::toggled() signal.
|
||||
*/
|
||||
void UpdateFromWidget(bool b);
|
||||
};
|
||||
|
||||
|
||||
@@ -23,26 +23,86 @@
|
||||
|
||||
#include "../effectfield.h"
|
||||
|
||||
/**
|
||||
* @brief The ButtonField class
|
||||
*
|
||||
* A UI-type EffectField. This field is largely an EffectField wrapper around a QPushButton and provides no data that's
|
||||
* usable in the Effect. It's primarily useful for other UI functions (e.g. showing/hiding a dialog or other UI
|
||||
* elements). This field is not exposed to the external shader API as it requires raw C++ code to connect it to other
|
||||
* elements.
|
||||
*
|
||||
* As with all widgets created from EffectField::CreateWidget(), you should never interface with the resulting widget
|
||||
* directly (apart from adding it to a layout and deleting it when it's unnecessary). All signals/slots should pass
|
||||
* through ButtonField instead to keep consistency with every layer involved.
|
||||
*/
|
||||
class ButtonField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief See Effect::Effect().
|
||||
*/
|
||||
ButtonField(EffectRow* parent, const QString& string);
|
||||
|
||||
/**
|
||||
* @brief Set whether this pushbutton is checkable
|
||||
*
|
||||
* This function is mainly a wrapper around QPushButton::setCheckable().
|
||||
*
|
||||
* "Checkable" means the button can be toggled between a state of being "normal" and being "pressed". In checkable
|
||||
* mode this field still cannot be used as a value in an Effect. Instead use BoolField (which uses a QCheckBox
|
||||
* representation) for passing values to the Effect that can only be true or false.
|
||||
*
|
||||
* @param c
|
||||
*
|
||||
* TRUE if this button should be checkable or not.
|
||||
*/
|
||||
void SetCheckable(bool c);
|
||||
|
||||
/**
|
||||
* @brief See EffectField::CreateWidget()
|
||||
*/
|
||||
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief A slot for when a widget's (created and connected from CreateWidget() ) checked state is changed
|
||||
*
|
||||
* @param c
|
||||
*
|
||||
* The current checked state (automatically filled by the QPushButton::toggled() signal)
|
||||
*/
|
||||
void SetChecked(bool c);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief A signal emitted whenever the field's internal checked state is changed
|
||||
*
|
||||
* Primarily used to set any connected widget's checked state to be consistent with the field's.
|
||||
*/
|
||||
void CheckedChanged(bool);
|
||||
|
||||
/**
|
||||
* @brief A signal emitted whenever the checked state of a connected widget changes
|
||||
*
|
||||
* Any widgets associated with this field will emit this signal when their checked state changes.
|
||||
*/
|
||||
void Toggled(bool);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Internal button text string passed to widgets created by CreateWidget()
|
||||
*/
|
||||
bool checkable_;
|
||||
|
||||
/**
|
||||
* @brief Internal checked value passed to and from widgets created by CreateWidget()
|
||||
*/
|
||||
bool checked_;
|
||||
|
||||
/**
|
||||
* @brief Internal button text string passed to widgets created by CreateWidget()
|
||||
*/
|
||||
QString button_text_;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,12 +20,15 @@
|
||||
|
||||
#include "filefield.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/embeddedfilechooser.h"
|
||||
|
||||
FileField::FileField(EffectRow* parent, const QString &id) :
|
||||
EffectField(parent, id, EFFECT_FIELD_FILE)
|
||||
{
|
||||
|
||||
// Set default value to an empty string
|
||||
SetValueAt(0, "");
|
||||
}
|
||||
|
||||
QString FileField::GetFileAt(double timecode)
|
||||
@@ -43,6 +46,15 @@ QWidget *FileField::CreateWidget(QWidget *existing)
|
||||
return efc;
|
||||
}
|
||||
|
||||
void FileField::UpdateWidgetValue(QWidget *widget, double timecode)
|
||||
{
|
||||
EmbeddedFileChooser* efc = static_cast<EmbeddedFileChooser*>(widget);
|
||||
|
||||
efc->blockSignals(true);
|
||||
efc->setFilename(GetFileAt(timecode));
|
||||
efc->blockSignals(false);
|
||||
}
|
||||
|
||||
void FileField::UpdateFromWidget(const QString &s)
|
||||
{
|
||||
KeyframeDataChange* kdc = new KeyframeDataChange(this);
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
QString GetFileAt(double timecode);
|
||||
|
||||
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
|
||||
virtual void UpdateWidgetValue(QWidget *widget, double timecode) override;
|
||||
private slots:
|
||||
void UpdateFromWidget(const QString &s);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "frei0reffect.h"
|
||||
|
||||
#ifndef NOFREI0R
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QDir>
|
||||
|
||||
#include "timeline/clip.h"
|
||||
|
||||
typedef f0r_instance_t (*f0rConstructFunc)(unsigned int width, unsigned int height);
|
||||
typedef int (*f0rInitFunc) ();
|
||||
typedef void (*f0rDeinitFunc) ();
|
||||
typedef void (*f0rUpdateFunc) (f0r_instance_t instance,
|
||||
double time, const uint32_t* inframe, uint32_t* outframe);
|
||||
typedef void (*f0rDestructFunc)(f0r_instance_t instance);
|
||||
typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info);
|
||||
typedef void (*f0rSetParamValue) (f0r_instance_t instance,
|
||||
f0r_param_t param, int param_index);
|
||||
|
||||
Frei0rEffect::Frei0rEffect(Clip* c, const EffectMeta *em) :
|
||||
Effect(c, em),
|
||||
open(false)
|
||||
{
|
||||
SetFlags(ImageFlag);
|
||||
|
||||
// Windows DLL loading routine
|
||||
QString dll_fn = QDir(em->path).filePath(em->filename);
|
||||
|
||||
handle.setFileName(dll_fn);
|
||||
|
||||
|
||||
if (!handle.load()) {
|
||||
QString dll_error = handle.errorString();
|
||||
QMessageBox::critical(nullptr, tr("Error loading Frei0r plugin"),
|
||||
tr("Failed to load Frei0r plugin \"%1\": %2").arg(dll_fn, dll_error));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
f0rInitFunc init = reinterpret_cast<f0rInitFunc>(handle.resolve("f0r_init"));
|
||||
init();
|
||||
|
||||
construct_module();
|
||||
|
||||
f0r_plugin_info_t info;
|
||||
f0rGetPluginInfo info_func = reinterpret_cast<f0rGetPluginInfo>(handle.resolve("f0r_get_plugin_info"));
|
||||
info_func(&info);
|
||||
|
||||
param_count = info.num_params;
|
||||
|
||||
get_param_info = reinterpret_cast<f0rGetParamInfo>(handle.resolve("f0r_get_param_info"));
|
||||
for (int i=0;i<param_count;i++) {
|
||||
f0r_param_info_t param_info;
|
||||
get_param_info(¶m_info, i);
|
||||
|
||||
if (param_info.type >= 0 && param_info.type <= F0R_PARAM_STRING) {
|
||||
EffectRow* row = new EffectRow(this, param_info.name);
|
||||
switch (param_info.type) {
|
||||
case F0R_PARAM_BOOL:
|
||||
new BoolField(row, QString::number(i));
|
||||
break;
|
||||
case F0R_PARAM_DOUBLE:
|
||||
{
|
||||
DoubleField* f = new DoubleField(row, QString::number(i));
|
||||
f->SetMinimum(0);
|
||||
f->SetMaximum(100);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_COLOR:
|
||||
new ColorField(row, QString::number(i));
|
||||
break;
|
||||
case F0R_PARAM_POSITION:
|
||||
{
|
||||
DoubleField* fx = new DoubleField(row, QString("%1X").arg(QString::number(i)));
|
||||
fx->SetMinimum(0);
|
||||
fx->SetMaximum(100);
|
||||
DoubleField* fy = new DoubleField(row, QString("%1Y").arg(QString::number(i)));
|
||||
fy->SetMinimum(0);
|
||||
fy->SetMaximum(100);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_STRING:
|
||||
new StringField(row, QString::number(i), false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Frei0rEffect::~Frei0rEffect() {
|
||||
if (handle.isLoaded()) {
|
||||
f0rDeinitFunc deinit = reinterpret_cast<f0rDeinitFunc>(handle.resolve("f0r_deinit"));
|
||||
deinit();
|
||||
|
||||
handle.unload();
|
||||
}
|
||||
}
|
||||
|
||||
void Frei0rEffect::process_image(double timecode, uint8_t *input, uint8_t *output, int) {
|
||||
f0rUpdateFunc update_func = reinterpret_cast<f0rUpdateFunc>(handle.resolve("f0r_update"));
|
||||
|
||||
for (int i=0;i<param_count;i++) {
|
||||
EffectRow* param_row = row(i);
|
||||
|
||||
f0r_param_info_t param_info;
|
||||
get_param_info(¶m_info, i);
|
||||
|
||||
f0rSetParamValue set_param = reinterpret_cast<f0rSetParamValue>(handle.resolve("f0r_set_param_value"));
|
||||
switch (param_info.type) {
|
||||
case F0R_PARAM_BOOL:
|
||||
{
|
||||
double b = param_row->Field(0)->GetValueAt(timecode).toBool();
|
||||
set_param(instance, &b, i);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_DOUBLE:
|
||||
{
|
||||
double d = param_row->Field(0)->GetValueAt(timecode).toDouble()*0.01;
|
||||
set_param(instance, &d, i);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_COLOR:
|
||||
{
|
||||
QColor qcolor = param_row->Field(0)->GetValueAt(timecode).value<QColor>();
|
||||
|
||||
f0r_param_color fcolor;
|
||||
fcolor.r = float(qcolor.redF());
|
||||
fcolor.g = float(qcolor.greenF());
|
||||
fcolor.b = float(qcolor.blueF());
|
||||
|
||||
set_param(instance, &fcolor, i);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_POSITION:
|
||||
{
|
||||
f0r_param_position pos;
|
||||
pos.x = param_row->Field(0)->GetValueAt(timecode).toDouble();
|
||||
pos.y = param_row->Field(1)->GetValueAt(timecode).toDouble();
|
||||
set_param(instance, &pos, i);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_STRING:
|
||||
{
|
||||
QByteArray bytes = param_row->Field(0)->GetValueAt(timecode).toString().toUtf8();
|
||||
char* byte_data = bytes.data();
|
||||
set_param(instance, &byte_data, i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
update_func(instance, timecode, reinterpret_cast<uint32_t*>(input), reinterpret_cast<uint32_t*>(output));
|
||||
}
|
||||
|
||||
void Frei0rEffect::refresh() {
|
||||
destruct_module();
|
||||
construct_module();
|
||||
}
|
||||
|
||||
void Frei0rEffect::destruct_module() {
|
||||
if (open) {
|
||||
f0rDestructFunc destruct = reinterpret_cast<f0rDestructFunc>(handle.resolve("f0r_destruct"));
|
||||
destruct(instance);
|
||||
|
||||
open = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Frei0rEffect::construct_module() {
|
||||
f0rConstructFunc construct = reinterpret_cast<f0rConstructFunc>(handle.resolve("f0r_construct"));
|
||||
instance = construct(parent_clip->media_width(), parent_clip->media_height());
|
||||
|
||||
open = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef FREI0REFFECT_H
|
||||
#define FREI0REFFECT_H
|
||||
|
||||
#ifndef NOFREI0R
|
||||
|
||||
#include <QLibrary>
|
||||
#include <frei0r.h>
|
||||
|
||||
#include "effects/effect.h"
|
||||
|
||||
typedef void (*f0rGetParamInfo)(f0r_param_info_t * info,
|
||||
int param_index );
|
||||
|
||||
class Frei0rEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Frei0rEffect(Clip* c, const EffectMeta* em);
|
||||
~Frei0rEffect();
|
||||
|
||||
virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size);
|
||||
|
||||
virtual void refresh();
|
||||
private:
|
||||
QLibrary handle;
|
||||
f0r_instance_t instance;
|
||||
int param_count;
|
||||
f0rGetParamInfo get_param_info;
|
||||
void destruct_module();
|
||||
void construct_module();
|
||||
bool open;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // FREI0REFFECT_H
|
||||
@@ -34,7 +34,14 @@
|
||||
#include "global/global.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
#ifdef __linux__
|
||||
// Load libraries for retrieving the native window handle. Used for VST plugins that have a separate window
|
||||
// dedicated to controls.
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <Windows.h>
|
||||
#elif defined(Q_OS_MACOS)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
class NSWindow;
|
||||
#elif defined(Q_OS_LINUX)
|
||||
#include <X11/X.h>
|
||||
#endif
|
||||
|
||||
@@ -105,94 +112,53 @@ typedef int32_t (*processEventsFuncPtr)(VstEvents *events);
|
||||
typedef void (*processFuncPtr)(AEffect *effect, float **inputs, float **outputs, int32_t sampleFrames);
|
||||
|
||||
void VSTHost::loadPlugin() {
|
||||
|
||||
QString dll_fn = file_field->GetFileAt(0);
|
||||
|
||||
if (dll_fn.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
bundle = BundleLoad(dll_fn);
|
||||
// Try to load the plugin
|
||||
modulePtr.setFileName(dll_fn);
|
||||
if (!modulePtr.load()) {
|
||||
|
||||
if (bundle == NULL) {
|
||||
QMessageBox::critical(nullptr, tr("Error loading VST plugin"), tr("Failed to create VST reference"));
|
||||
// Show an error if the plugin fails to load
|
||||
|
||||
qCritical() << "Failed to load VST plugin" << dll_fn << "-" << modulePtr.errorString();
|
||||
QMessageBox::critical(olive::MainWindow,
|
||||
tr("Error loading VST plugin"),
|
||||
tr("Failed to load VST plugin \"%1\": %2").arg(dll_fn, modulePtr.errorString()));
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
vstPluginFuncPtr mainEntryPoint = NULL;
|
||||
mainEntryPoint = (vstPluginFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("VSTPluginMain"));
|
||||
// VST plugins previous to the 2.4 SDK used main_macho for the entry point name
|
||||
if(mainEntryPoint == NULL) {
|
||||
mainEntryPoint = (vstPluginFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("main_macho"));
|
||||
// Try to find the VST entry point (first using VSTPluginMain() )
|
||||
vstPluginFuncPtr mainEntryPoint = reinterpret_cast<vstPluginFuncPtr>(modulePtr.resolve("VSTPluginMain"));
|
||||
|
||||
if (mainEntryPoint == nullptr) {
|
||||
// If there's no VSTPluginMain(), the plugin may use main() instead
|
||||
mainEntryPoint = reinterpret_cast<vstPluginFuncPtr>(modulePtr.resolve("main"));
|
||||
}
|
||||
if(mainEntryPoint == NULL) {
|
||||
qCritical() << "Couldn't get a pointer to VST plugin's main()";
|
||||
BundleClose(bundle);
|
||||
|
||||
if (mainEntryPoint == nullptr) {
|
||||
QMessageBox::critical(olive::MainWindow,
|
||||
tr("Error loading VST plugin"),
|
||||
tr("Failed to locate entry point for dynamic library."));
|
||||
modulePtr.unload();
|
||||
return;
|
||||
}
|
||||
|
||||
// Instantiate the plugin
|
||||
plugin = mainEntryPoint(hostCallback);
|
||||
if(plugin == NULL) {
|
||||
qCritical() << "Plugin's main() returns null";
|
||||
BundleClose(bundle);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
modulePtr = LibLoad(dll_fn);
|
||||
if(modulePtr == nullptr) {
|
||||
QString dll_error;
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD dll_err = GetLastError();
|
||||
dll_error = QString::number(dll_err);
|
||||
#elif defined(__linux__) || defined(__HAIKU__)
|
||||
dll_error = dlerror();
|
||||
#endif
|
||||
qCritical() << "Failed to load VST plugin" << dll_fn << "-" << dll_error;
|
||||
|
||||
QString msg_err = tr("Failed to load VST plugin \"%1\": %2").arg(dll_fn, dll_error);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (dll_err == 193) {
|
||||
#ifdef _WIN64
|
||||
msg_err += "\n\n" + tr("NOTE: You can't load 32-bit VST plugins into a 64-bit build of Olive. Please find a 64-bit version of this plugin or switch to a 32-bit build of Olive.");
|
||||
#elif _WIN32
|
||||
msg_err += "\n\n" + tr("NOTE: You can't load 64-bit VST plugins into a 32-bit build of Olive. Please find a 32-bit version of this plugin or switch to a 64-bit build of Olive.");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
QMessageBox::critical(nullptr, tr("Error loading VST plugin"), msg_err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
vstPluginFuncPtr mainEntryPoint = reinterpret_cast<vstPluginFuncPtr>(LibAddress(modulePtr, "VSTPluginMain"));
|
||||
|
||||
if (mainEntryPoint == nullptr) {
|
||||
// if there's no VSTPluginMain(), fallback to main()
|
||||
mainEntryPoint = reinterpret_cast<vstPluginFuncPtr>(LibAddress(modulePtr, "main"));
|
||||
}
|
||||
|
||||
if (mainEntryPoint == nullptr) {
|
||||
QMessageBox::critical(nullptr, tr("Error loading VST plugin"), tr("Failed to locate entry point for dynamic library."));
|
||||
LibClose(modulePtr);
|
||||
} else {
|
||||
// Instantiate the plugin
|
||||
plugin = mainEntryPoint(hostCallback);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VSTHost::freePlugin() {
|
||||
if (plugin != nullptr) {
|
||||
stopPlugin();
|
||||
#if defined(__APPLE__)
|
||||
CFBundleUnloadExecutable(bundle);
|
||||
CFRelease(bundle);
|
||||
#else
|
||||
LibClose(modulePtr);
|
||||
#endif
|
||||
data_cache.clear();
|
||||
modulePtr.unload();
|
||||
plugin = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -266,6 +232,11 @@ void VSTHost::CreateDialogIfNull()
|
||||
}
|
||||
}
|
||||
|
||||
void VSTHost::send_data_cache_to_plugin()
|
||||
{
|
||||
dispatcher(plugin, effSetChunk, 0, int32_t(data_cache.size()), static_cast<void*>(data_cache.data()), 0);
|
||||
}
|
||||
|
||||
VSTHost::VSTHost(Clip* c, const EffectMeta *em) :
|
||||
Effect(c, em),
|
||||
plugin(nullptr),
|
||||
@@ -282,7 +253,7 @@ VSTHost::VSTHost(Clip* c, const EffectMeta *em) :
|
||||
|
||||
EffectRow* file_row = new EffectRow(this, tr("Plugin"), true, false);
|
||||
file_field = new FileField(file_row, "filename");
|
||||
connect(file_field, SIGNAL(Changed()), this, SLOT(change_plugin()));
|
||||
connect(file_field, SIGNAL(Changed()), this, SLOT(change_plugin()), Qt::QueuedConnection);
|
||||
|
||||
EffectRow* interface_row = new EffectRow(this, tr("Interface"), false, false);
|
||||
|
||||
@@ -344,7 +315,7 @@ void VSTHost::custom_load(QXmlStreamReader &stream) {
|
||||
stream.readNext();
|
||||
data_cache = QByteArray::fromBase64(stream.text().toUtf8());
|
||||
if (plugin != nullptr) {
|
||||
dispatcher(plugin, effSetChunk, 0, int32_t(data_cache.size()), static_cast<void*>(data_cache.data()), 0);
|
||||
send_data_cache_to_plugin();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,11 +337,11 @@ void VSTHost::show_interface(bool show) {
|
||||
dialog->setVisible(show);
|
||||
|
||||
if (show) {
|
||||
#if defined(_WIN32)
|
||||
#if defined(Q_OS_WIN)
|
||||
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<HWND>(dialog->windowHandle()->winId()), 0);
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(Q_OS_MACOS)
|
||||
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<NSWindow*>(dialog->windowHandle()->winId()), 0);
|
||||
#elif defined(__linux__) || defined(__HAIKU__)
|
||||
#elif defined(Q_OS_LINUX) || defined(__HAIKU__)
|
||||
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<void*>(dialog->windowHandle()->winId()), 0);
|
||||
#endif
|
||||
} else {
|
||||
@@ -392,17 +363,18 @@ void VSTHost::change_plugin() {
|
||||
VSTRect* eRect = nullptr;
|
||||
plugin->dispatcher(plugin, effEditGetRect, 0, 0, &eRect, 0);
|
||||
|
||||
if (!data_cache.isEmpty()) {
|
||||
send_data_cache_to_plugin();
|
||||
}
|
||||
|
||||
CreateDialogIfNull();
|
||||
dialog->setFixedSize(eRect->right - eRect->left, eRect->bottom - eRect->top);
|
||||
|
||||
} else {
|
||||
#ifdef __APPLE__
|
||||
CFBundleUnloadExecutable(bundle);
|
||||
CFRelease(bundle);
|
||||
#else
|
||||
LibClose(modulePtr);
|
||||
#endif
|
||||
|
||||
modulePtr.unload();
|
||||
plugin = nullptr;
|
||||
|
||||
}
|
||||
}
|
||||
show_interface_btn->SetEnabled(plugin != nullptr);
|
||||
|
||||
@@ -21,19 +21,15 @@
|
||||
#ifndef VSTHOSTWIN_H
|
||||
#define VSTHOSTWIN_H
|
||||
|
||||
#ifndef NOVST
|
||||
#include <QDialog>
|
||||
#include <QLibrary>
|
||||
|
||||
#include "effects/effect.h"
|
||||
|
||||
#include "global/crossplatformlib.h"
|
||||
|
||||
#include "include/vestige.h"
|
||||
|
||||
// Plugin's dispatcher function
|
||||
typedef intptr_t (*dispatcherFuncPtr)(AEffect *effect, int32_t opCode, int32_t index, int32_t value, void *ptr, float opt);
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class VSTHost : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -68,13 +64,9 @@ private:
|
||||
QDialog* dialog;
|
||||
QByteArray data_cache;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
CFBundleRef bundle;
|
||||
#else
|
||||
ModulePtr modulePtr;
|
||||
#endif
|
||||
void send_data_cache_to_plugin();
|
||||
|
||||
QLibrary modulePtr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // VSTHOSTWIN_H
|
||||
|
||||
+33
-3
@@ -57,13 +57,14 @@ Config::Config()
|
||||
hover_focus(false),
|
||||
project_view_type(olive::PROJECT_VIEW_TREE),
|
||||
set_name_with_marker(true),
|
||||
show_project_toolbar(false),
|
||||
show_project_toolbar(true),
|
||||
previous_queue_size(3),
|
||||
previous_queue_type(olive::FRAME_QUEUE_TYPE_FRAMES),
|
||||
upcoming_queue_size(0.5),
|
||||
upcoming_queue_type(olive::FRAME_QUEUE_TYPE_SECONDS),
|
||||
loop(false),
|
||||
seek_also_selects(false),
|
||||
auto_seek_to_beginning(true),
|
||||
effect_textbox_lines(3),
|
||||
use_software_fallback(false),
|
||||
center_timeline_timecodes(true),
|
||||
@@ -73,7 +74,12 @@ Config::Config()
|
||||
invert_timeline_scroll_axes(true),
|
||||
enable_color_management(false),
|
||||
style(olive::styling::kOliveDefaultDark),
|
||||
use_native_menu_styling(true)
|
||||
use_native_menu_styling(true),
|
||||
default_sequence_width(1920),
|
||||
default_sequence_height(1080),
|
||||
default_sequence_framerate(29.97),
|
||||
default_sequence_audio_frequency(48000),
|
||||
default_sequence_audio_channel_layout(3)
|
||||
{}
|
||||
|
||||
void Config::load(QString path) {
|
||||
@@ -180,6 +186,9 @@ void Config::load(QString path) {
|
||||
} else if (stream.name() == "SeekAlsoSelects") {
|
||||
stream.readNext();
|
||||
seek_also_selects = (stream.text() == "1");
|
||||
} else if (stream.name() == "AutoSeekToBeginning") {
|
||||
stream.readNext();
|
||||
auto_seek_to_beginning = (stream.text() == "1");
|
||||
} else if (stream.name() == "CSSPath") {
|
||||
stream.readNext();
|
||||
css_path = stream.text().toString();
|
||||
@@ -222,6 +231,21 @@ void Config::load(QString path) {
|
||||
} else if (stream.name() == "NativeMenuStyling") {
|
||||
stream.readNext();
|
||||
use_native_menu_styling = (stream.text() == "1");
|
||||
} else if (stream.name() == "DefaultSequenceWidth") {
|
||||
stream.readNext();
|
||||
default_sequence_width = stream.text().toInt();
|
||||
} else if (stream.name() == "DefaultSequenceHeight") {
|
||||
stream.readNext();
|
||||
default_sequence_height = stream.text().toInt();
|
||||
} else if (stream.name() == "DefaultSequenceFrameRate") {
|
||||
stream.readNext();
|
||||
default_sequence_framerate = stream.text().toDouble();
|
||||
} else if (stream.name() == "DefaultSequenceAudioFrequency") {
|
||||
stream.readNext();
|
||||
default_sequence_audio_frequency = stream.text().toInt();
|
||||
} else if (stream.name() == "DefaultSequenceAudioLayout") {
|
||||
stream.readNext();
|
||||
default_sequence_audio_channel_layout = stream.text().toInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,13 +295,14 @@ void Config::save(QString path) {
|
||||
stream.writeTextElement("HoverFocus", QString::number(hover_focus));
|
||||
stream.writeTextElement("ProjectViewType", QString::number(project_view_type));
|
||||
stream.writeTextElement("SetNameWithMarker", QString::number(set_name_with_marker));
|
||||
stream.writeTextElement("ShowProjectToolbar", QString::number(panel_project->toolbar_widget->isVisible()));
|
||||
stream.writeTextElement("ShowProjectToolbar", QString::number(panel_project->IsToolbarVisible()));
|
||||
stream.writeTextElement("PreviousFrameQueueSize", QString::number(previous_queue_size));
|
||||
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("SeekAlsoSelects", QString::number(seek_also_selects));
|
||||
stream.writeTextElement("AutoSeekToBeginning", QString::number(auto_seek_to_beginning));
|
||||
stream.writeTextElement("CSSPath", css_path);
|
||||
stream.writeTextElement("EffectTextboxLines", QString::number(effect_textbox_lines));
|
||||
stream.writeTextElement("UseSoftwareFallback", QString::number(use_software_fallback));
|
||||
@@ -292,6 +317,11 @@ void Config::save(QString path) {
|
||||
stream.writeTextElement("OCIOConfigPath", ocio_config_path);
|
||||
stream.writeTextElement("Style", QString::number(style));
|
||||
stream.writeTextElement("NativeMenuStyling", QString::number(use_native_menu_styling));
|
||||
stream.writeTextElement("DefaultSequenceWidth", QString::number(default_sequence_width));
|
||||
stream.writeTextElement("DefaultSequenceHeight", QString::number(default_sequence_height));
|
||||
stream.writeTextElement("DefaultSequenceFrameRate", QString::number(default_sequence_framerate));
|
||||
stream.writeTextElement("DefaultSequenceAudioFrequency", QString::number(default_sequence_audio_frequency));
|
||||
stream.writeTextElement("DefaultSequenceAudioLayout", QString::number(default_sequence_audio_channel_layout));
|
||||
|
||||
stream.writeEndElement(); // configuration
|
||||
stream.writeEndDocument(); // doc
|
||||
|
||||
+36
-1
@@ -120,7 +120,10 @@ namespace olive {
|
||||
PROJECT_VIEW_TREE,
|
||||
|
||||
/** Display project media in icon browser */
|
||||
PROJECT_VIEW_ICON
|
||||
PROJECT_VIEW_ICON,
|
||||
|
||||
/** Display project media in list browser */
|
||||
PROJECT_VIEW_LIST
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -413,6 +416,13 @@ struct Config {
|
||||
*/
|
||||
bool seek_also_selects;
|
||||
|
||||
/**
|
||||
* @brief Automatically seek to the beginning of a sequence if the user plays beyond the end of it
|
||||
*
|
||||
* TRUE if this behavior should be enabled.
|
||||
*/
|
||||
bool auto_seek_to_beginning;
|
||||
|
||||
/**
|
||||
* @brief CSS Path
|
||||
*
|
||||
@@ -558,6 +568,31 @@ struct Config {
|
||||
*/
|
||||
bool use_native_menu_styling;
|
||||
|
||||
/**
|
||||
* @brief Default Sequence video width
|
||||
*/
|
||||
int default_sequence_width;
|
||||
|
||||
/**
|
||||
* @brief Default Sequence video height
|
||||
*/
|
||||
int default_sequence_height;
|
||||
|
||||
/**
|
||||
* @brief Default Sequence video frame rate
|
||||
*/
|
||||
double default_sequence_framerate;
|
||||
|
||||
/**
|
||||
* @brief Default Sequence audio frequency
|
||||
*/
|
||||
int default_sequence_audio_frequency;
|
||||
|
||||
/**
|
||||
* @brief Default Sequence audio channel layout
|
||||
*/
|
||||
int default_sequence_audio_channel_layout;
|
||||
|
||||
/**
|
||||
* @brief Load config from file
|
||||
*
|
||||
|
||||
+85
-42
@@ -39,6 +39,7 @@
|
||||
#include "dialogs/speeddialog.h"
|
||||
#include "dialogs/actionsearch.h"
|
||||
#include "dialogs/loaddialog.h"
|
||||
#include "dialogs/autocutsilencedialog.h"
|
||||
#include "project/loadthread.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "ui/mediaiconservice.h"
|
||||
@@ -91,7 +92,12 @@ void OliveGlobal::check_for_autorecovery_file() {
|
||||
// detect auto-recovery file
|
||||
autorecovery_filename = data_dir + "/autorecovery.ove";
|
||||
if (QFile::exists(autorecovery_filename)) {
|
||||
if (QMessageBox::question(nullptr, tr("Auto-recovery"), tr("Olive didn't close properly and an autorecovery file was detected. Would you like to open it?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
if (QMessageBox::question(nullptr,
|
||||
tr("Auto-recovery"),
|
||||
tr("Olive didn't close properly and an autorecovery file "
|
||||
"was detected. Would you like to open it?"),
|
||||
QMessageBox::Yes,
|
||||
QMessageBox::No) == QMessageBox::Yes) {
|
||||
enable_load_project_on_init = false;
|
||||
OpenProjectWorker(autorecovery_filename, true);
|
||||
}
|
||||
@@ -166,18 +172,19 @@ void OliveGlobal::SetNativeStyling(QWidget *w)
|
||||
#endif
|
||||
}
|
||||
|
||||
void OliveGlobal::LoadProject(const QString &fn, bool autorecovery, bool clear)
|
||||
void OliveGlobal::LoadProject(const QString &fn, bool autorecovery)
|
||||
{
|
||||
// Normally, the user will be closing the previous project to load a new one, but just in case the user
|
||||
// is importing a new project
|
||||
// QSortFilterProxyModels are not thread-safe, and as we'll be loading in another thread, leaving it connected
|
||||
// can cause glitches in its presentation. Therefore for the duration of the loading process, we disconnect it,
|
||||
// and reconnect it later once the loading is complete.
|
||||
|
||||
if (clear) {
|
||||
new_project();
|
||||
}
|
||||
panel_project->DisconnectFilterToModel();
|
||||
|
||||
LoadDialog ld(olive::MainWindow);
|
||||
|
||||
LoadThread* lt = new LoadThread(fn, autorecovery, clear);
|
||||
ld.open();
|
||||
|
||||
LoadThread* lt = new LoadThread(fn, autorecovery);
|
||||
connect(&ld, SIGNAL(cancel()), lt, SLOT(cancel()));
|
||||
connect(lt, SIGNAL(success()), &ld, SLOT(accept()));
|
||||
connect(lt, SIGNAL(error()), &ld, SLOT(reject()));
|
||||
@@ -185,40 +192,46 @@ void OliveGlobal::LoadProject(const QString &fn, bool autorecovery, bool clear)
|
||||
connect(lt, SIGNAL(report_progress(int)), &ld, SLOT(setValue(int)));
|
||||
lt->start();
|
||||
|
||||
ld.exec();
|
||||
panel_project->ConnectFilterToModel();
|
||||
}
|
||||
|
||||
void OliveGlobal::ClearProject()
|
||||
{
|
||||
// clear graph editor
|
||||
panel_graph_editor->set_row(nullptr);
|
||||
|
||||
// clear effects panel
|
||||
panel_effect_controls->Clear(true);
|
||||
|
||||
// clear existing project
|
||||
olive::Global->set_sequence(nullptr);
|
||||
panel_footage_viewer->set_media(nullptr);
|
||||
|
||||
// clear project contents (footage, sequences, etc.)
|
||||
panel_project->clear();
|
||||
|
||||
// clear undo stack
|
||||
olive::UndoStack.clear();
|
||||
|
||||
// empty current project filename
|
||||
update_project_filename("");
|
||||
|
||||
// full update of all panels
|
||||
update_ui(false);
|
||||
|
||||
// set to unmodified
|
||||
olive::Global->set_modified(false);
|
||||
}
|
||||
|
||||
void OliveGlobal::ImportProject(const QString &fn)
|
||||
{
|
||||
LoadProject(fn, false, false);
|
||||
LoadProject(fn, false);
|
||||
set_modified(true);
|
||||
}
|
||||
|
||||
void OliveGlobal::new_project() {
|
||||
if (can_close_project()) {
|
||||
// clear graph editor
|
||||
panel_graph_editor->set_row(nullptr);
|
||||
|
||||
// clear effects panel
|
||||
panel_effect_controls->Clear(true);
|
||||
|
||||
// clear existing project
|
||||
olive::Global->set_sequence(nullptr);
|
||||
panel_footage_viewer->set_media(nullptr);
|
||||
|
||||
// clear project contents (footage, sequences, etc.)
|
||||
panel_project->clear();
|
||||
|
||||
// clear undo stack
|
||||
olive::UndoStack.clear();
|
||||
|
||||
// empty current project filename
|
||||
update_project_filename("");
|
||||
|
||||
// full update of all panels
|
||||
update_ui(false);
|
||||
|
||||
// set to unmodified
|
||||
olive::Global->set_modified(false);
|
||||
ClearProject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,12 +302,7 @@ bool OliveGlobal::can_close_project() {
|
||||
}
|
||||
|
||||
void OliveGlobal::open_export_dialog() {
|
||||
if (olive::ActiveSequence == nullptr) {
|
||||
QMessageBox::information(olive::MainWindow,
|
||||
tr("No active sequence"),
|
||||
tr("Please open the sequence you wish to export."),
|
||||
QMessageBox::Ok);
|
||||
} else {
|
||||
if (CheckForActiveSequence()) {
|
||||
ExportDialog e(olive::MainWindow);
|
||||
e.exec();
|
||||
}
|
||||
@@ -358,12 +366,29 @@ void OliveGlobal::set_sequence(SequencePtr s)
|
||||
panel_timeline->setFocus();
|
||||
}
|
||||
|
||||
void OliveGlobal::OpenProjectWorker(const QString& fn, bool autorecovery) {
|
||||
void OliveGlobal::OpenProjectWorker(QString fn, bool autorecovery) {
|
||||
ClearProject();
|
||||
update_project_filename(fn);
|
||||
LoadProject(fn, autorecovery, true);
|
||||
LoadProject(fn, autorecovery);
|
||||
olive::UndoStack.clear();
|
||||
}
|
||||
|
||||
bool OliveGlobal::CheckForActiveSequence(bool show_msg)
|
||||
{
|
||||
if (olive::ActiveSequence == nullptr) {
|
||||
|
||||
if (show_msg) {
|
||||
QMessageBox::information(olive::MainWindow,
|
||||
tr("No active sequence"),
|
||||
tr("Please open the sequence to perform this action."),
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OliveGlobal::undo() {
|
||||
// workaround to prevent crash (and also users should never need to do this)
|
||||
if (!panel_timeline->importing) {
|
||||
@@ -413,6 +438,24 @@ void OliveGlobal::open_speed_dialog() {
|
||||
}
|
||||
}
|
||||
|
||||
void OliveGlobal::open_autocut_silence_dialog() {
|
||||
if (CheckForActiveSequence()) {
|
||||
|
||||
QVector<Clip*> selected_clips = olive::ActiveSequence->SelectedClips();
|
||||
|
||||
if (selected_clips.isEmpty()) {
|
||||
QMessageBox::critical(olive::MainWindow,
|
||||
tr("No clips selected"),
|
||||
tr("Select the clips you wish to auto-cut"),
|
||||
QMessageBox::Ok);
|
||||
} else {
|
||||
AutoCutSilenceDialog s(olive::MainWindow, selected_clips);
|
||||
s.exec();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void OliveGlobal::clear_undo_stack() {
|
||||
olive::UndoStack.clear();
|
||||
}
|
||||
|
||||
+27
-2
@@ -275,6 +275,11 @@ public slots:
|
||||
*/
|
||||
void open_speed_dialog();
|
||||
|
||||
/**
|
||||
* @brief Open the auto-cut silence dialog.
|
||||
*/
|
||||
void open_autocut_silence_dialog();
|
||||
|
||||
/**
|
||||
* @brief Open the Action Search overlay.
|
||||
*/
|
||||
@@ -338,7 +343,19 @@ private:
|
||||
* beside the original project file so that it does not overwrite the original and so that the user is not working
|
||||
* on the autorecovery project in Olive's application data directory.
|
||||
*/
|
||||
void OpenProjectWorker(const QString& fn, bool autorecovery);
|
||||
void OpenProjectWorker(QString fn, bool autorecovery);
|
||||
|
||||
/**
|
||||
* @brief Returns whether a Sequence is currently active or not, and optionally displays a messagebox if not
|
||||
*
|
||||
* Checks whether a Sequence is active and can display a messagebox if not to inform users to make one active in
|
||||
* order to perform said action.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* TRUE if there is an active Sequence, FALSE if not.
|
||||
*/
|
||||
bool CheckForActiveSequence(bool show_msg = true);
|
||||
|
||||
/**
|
||||
* @brief Create a LoadDialog and start a LoadThread to load data from a project
|
||||
@@ -366,7 +383,15 @@ private:
|
||||
* TRUE if the current project should be closed before opening, FALSE if the project should be imported into the
|
||||
* currently open one.
|
||||
*/
|
||||
void LoadProject(const QString& fn, bool autorecovery, bool clear);
|
||||
void LoadProject(const QString& fn, bool autorecovery);
|
||||
|
||||
/**
|
||||
* @brief Indiscriminately clear the project without prompting the user
|
||||
*
|
||||
* Will clear the entire project without prompting to save. This is dangerous, use new_project() instead for
|
||||
* anything initiated by the user.
|
||||
*/
|
||||
void ClearProject();
|
||||
|
||||
/**
|
||||
* @brief File filter used for any file dialogs relating to Olive project files.
|
||||
|
||||
+21
-2
@@ -74,9 +74,28 @@ double cubic_t_from_x(double x_target, double a, double b, double c, double d) {
|
||||
}
|
||||
|
||||
double amplitude_to_db(double amplitude) {
|
||||
return (20.0*(qLn(amplitude)/qLn(10.0)));
|
||||
return (20.0*(qLn(amplitude)/qLn(10.0)));
|
||||
}
|
||||
|
||||
double db_to_amplitude(double db) {
|
||||
return qPow(M_E, (db*qLn(10.0))/20.0);
|
||||
return qPow(M_E, (db*qLn(10.0))/20.0);
|
||||
}
|
||||
|
||||
QRect fit_size_into_rect(const QRect &r, int width, int height)
|
||||
{
|
||||
// Get aspect ratio of object we're fitting
|
||||
double inner_ar = double(width) / double(height);
|
||||
|
||||
// Get aspect ratio of rectangle
|
||||
double rect_ar = double(r.width()) / double(r.height());
|
||||
|
||||
if (rect_ar > inner_ar) {
|
||||
// The rect is wider than the object, so we'll be limiting by height and scaling by width
|
||||
int new_width = qRound(r.height() * inner_ar);
|
||||
return QRect(r.x() + (r.width() / 2 - new_width / 2), r.y(), new_width, r.height());
|
||||
} else {
|
||||
// The rect is taller than the object, so we'll be limiting by width and scaling by height
|
||||
int new_height = qRound(r.width() / inner_ar);
|
||||
return QRect(r.x(), r.y() + (r.height() / 2 - new_height / 2), r.width(), new_height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#ifndef MATH_H
|
||||
#define MATH_H
|
||||
|
||||
#include <QRect>
|
||||
|
||||
int lerp(int a, int b, double t);
|
||||
float float_lerp(float a, float b, float t);
|
||||
double double_lerp(double a, double b, double t);
|
||||
@@ -30,6 +32,8 @@ double cubic_from_t(double a, double b, double c, double d, double t);
|
||||
double cubic_t_from_x(double x_target, double a, double b, double c, double d);
|
||||
double solveCubicBezier(double p0, double p1, double p2, double p3, double x);
|
||||
|
||||
QRect fit_size_into_rect(const QRect& r, int width, int height);
|
||||
|
||||
// decibel conversion functions
|
||||
double amplitude_to_db(double amplitude);
|
||||
double db_to_amplitude(double db);
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
<file>align-right.svg</file>
|
||||
<file>justify-center.svg</file>
|
||||
<file>bold.svg</file>
|
||||
<file>listview.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -0,0 +1,829 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
sodipodi:docname="iconview.svg"
|
||||
inkscape:version="0.91+devel+osxmenu r12922"
|
||||
sodipodi:version="0.32"
|
||||
id="svg2"
|
||||
height="64"
|
||||
width="64"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
inkscape:export-filename="/Users/pablo/olive_pablo/icons/iconview.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96">
|
||||
<defs
|
||||
id="defs14">
|
||||
<linearGradient
|
||||
id="linearGradient3253">
|
||||
<stop
|
||||
id="stop3255"
|
||||
offset="0"
|
||||
style="stop-color:#89d5f8;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3257"
|
||||
offset="1"
|
||||
style="stop-color:#00899e;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11979">
|
||||
<stop
|
||||
id="stop11981"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11983"
|
||||
offset="1"
|
||||
style="stop-color:#729fcf;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
id="perspective5736"
|
||||
inkscape:persp3d-origin="24 : 16 : 1"
|
||||
inkscape:vp_z="48 : 24 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 24 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
id="linearGradient2846">
|
||||
<stop
|
||||
style="stop-color:#8a8a8a;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop2848" />
|
||||
<stop
|
||||
style="stop-color:#484848;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop2850" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2366">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2368" />
|
||||
<stop
|
||||
id="stop2374"
|
||||
offset="0.50000000"
|
||||
style="stop-color:#ffffff;stop-opacity:0.21904762;" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop2370" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4467">
|
||||
<stop
|
||||
id="stop4469"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop4471"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#ffffff;stop-opacity:0.24761905;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4454">
|
||||
<stop
|
||||
id="stop4456"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#729fcf;stop-opacity:0.20784314;" />
|
||||
<stop
|
||||
id="stop4458"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#729fcf;stop-opacity:0.67619050;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4440">
|
||||
<stop
|
||||
id="stop4442"
|
||||
offset="0"
|
||||
style="stop-color:#7d7d7d;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#b1b1b1;stop-opacity:1.0000000;"
|
||||
offset="0.50000000"
|
||||
id="stop4448" />
|
||||
<stop
|
||||
id="stop4444"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#686868;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4467"
|
||||
id="radialGradient2097"
|
||||
cx="23.070683"
|
||||
cy="35.127438"
|
||||
fx="23.070683"
|
||||
fy="35.127438"
|
||||
r="10.31934"
|
||||
gradientTransform="matrix(0.914812,0.01265023,-0.00821502,0.213562,2.253914,27.18889)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4467"
|
||||
id="linearGradient7922"
|
||||
x1="16.874998"
|
||||
y1="22.851799"
|
||||
x2="27.900846"
|
||||
y2="34.976799"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2871"
|
||||
id="linearGradient7186"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="46.834816"
|
||||
y1="45.264122"
|
||||
x2="45.380436"
|
||||
y2="50.939667" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2402"
|
||||
id="linearGradient7184"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="18.935766"
|
||||
y1="23.667896"
|
||||
x2="53.588623"
|
||||
y2="26.649363" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2380"
|
||||
id="linearGradient7180"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="62.513836"
|
||||
y1="36.061237"
|
||||
x2="15.984863"
|
||||
y2="20.60858" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4467"
|
||||
id="linearGradient7189"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="13.435029"
|
||||
y1="13.604306"
|
||||
x2="22.374878"
|
||||
y2="23.554308"
|
||||
gradientTransform="rotate(180,23.96967,25.01237)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4467"
|
||||
id="linearGradient7185"
|
||||
x1="13.435029"
|
||||
y1="13.604306"
|
||||
x2="22.374878"
|
||||
y2="23.554308"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1322"
|
||||
id="linearGradient4975"
|
||||
x1="34.892849"
|
||||
y1="36.422989"
|
||||
x2="45.918697"
|
||||
y2="48.547989"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-18.01785,-13.57119)" />
|
||||
<linearGradient
|
||||
id="linearGradient1322">
|
||||
<stop
|
||||
id="stop1324"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#729fcf" />
|
||||
<stop
|
||||
id="stop1326"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#5187d6;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4467"
|
||||
id="linearGradient1491"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="5.9649177"
|
||||
y1="26.048164"
|
||||
x2="52.854095"
|
||||
y2="26.048164" />
|
||||
<linearGradient
|
||||
id="linearGradient2402">
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2404" />
|
||||
<stop
|
||||
style="stop-color:#528ac5;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2406" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2871">
|
||||
<stop
|
||||
style="stop-color:#3465a4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2873" />
|
||||
<stop
|
||||
style="stop-color:#3465a4;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop2875" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2690"
|
||||
id="linearGradient2696"
|
||||
x1="32.647972"
|
||||
y1="30.748846"
|
||||
x2="37.124462"
|
||||
y2="24.842253"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-48.77039,-5.765705)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2690">
|
||||
<stop
|
||||
style="stop-color:#c4d7eb;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2692" />
|
||||
<stop
|
||||
style="stop-color:#c4d7eb;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2694" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2682"
|
||||
id="linearGradient2688"
|
||||
x1="36.713837"
|
||||
y1="31.455952"
|
||||
x2="37.124462"
|
||||
y2="24.842253"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-48.77039,-5.765705)" />
|
||||
<linearGradient
|
||||
id="linearGradient2682">
|
||||
<stop
|
||||
style="stop-color:#3977c3;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2684" />
|
||||
<stop
|
||||
style="stop-color:#89aedc;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2686" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2380">
|
||||
<stop
|
||||
style="stop-color:#b9cfe7;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop2382" />
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop2384" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2831"
|
||||
id="linearGradient1486"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-48.30498,-6.043298)"
|
||||
x1="13.478554"
|
||||
y1="10.612206"
|
||||
x2="15.419417"
|
||||
y2="19.115122" />
|
||||
<linearGradient
|
||||
id="linearGradient2831">
|
||||
<stop
|
||||
style="stop-color:#3465a4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2833" />
|
||||
<stop
|
||||
id="stop2855"
|
||||
offset="0.33333334"
|
||||
style="stop-color:#5b86be;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#83a8d8;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2835" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2871"
|
||||
id="linearGradient1488"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(180,-0.62124,20.04085)"
|
||||
x1="37.128052"
|
||||
y1="29.729605"
|
||||
x2="37.065414"
|
||||
y2="26.194071" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4467"
|
||||
id="radialGradient1503"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.536723,0,16.87306)"
|
||||
cx="24.837126"
|
||||
cy="36.421127"
|
||||
fx="24.837126"
|
||||
fy="36.421127"
|
||||
r="15.644737" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective2833" />
|
||||
<linearGradient
|
||||
id="linearGradient3864">
|
||||
<stop
|
||||
id="stop3866"
|
||||
offset="0"
|
||||
style="stop-color:#71b2f8;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3868"
|
||||
offset="1"
|
||||
style="stop-color:#002795;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient2571"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="342.58258"
|
||||
cy="27.256668"
|
||||
fx="342.58258"
|
||||
fy="27.256668"
|
||||
r="19.571428"
|
||||
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-215.02413,-170.90186)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3352"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="345.28433"
|
||||
cy="15.560534"
|
||||
fx="345.28433"
|
||||
fy="15.560534"
|
||||
r="19.571428"
|
||||
gradientTransform="translate(-0.1767767,-2.6516504)" />
|
||||
<linearGradient
|
||||
id="linearGradient3593">
|
||||
<stop
|
||||
style="stop-color:#c8e0f9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3595" />
|
||||
<stop
|
||||
style="stop-color:#637dca;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3597" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3354"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="330.63791"
|
||||
cy="39.962704"
|
||||
fx="330.63791"
|
||||
fy="39.962704"
|
||||
r="19.571428"
|
||||
gradientTransform="translate(-0.1767767,-2.6516504)" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective3372" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient3369"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-461.81066,-173.06271)"
|
||||
cx="342.58258"
|
||||
cy="27.256668"
|
||||
fx="342.58258"
|
||||
fy="27.256668"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3372"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
|
||||
cx="345.28433"
|
||||
cy="15.560534"
|
||||
fx="345.28433"
|
||||
fy="15.560534"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3375"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
|
||||
cx="330.63791"
|
||||
cy="39.962704"
|
||||
fx="330.63791"
|
||||
fy="39.962704"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient3380"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
|
||||
cx="320.44025"
|
||||
cy="113.23357"
|
||||
fx="320.44025"
|
||||
fy="113.23357"
|
||||
r="19.571428" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="linearGradient3914"
|
||||
x1="6.94525"
|
||||
y1="36.838673"
|
||||
x2="48.691113"
|
||||
y2="36.838673"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-4.8699606,-2.3863162)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="linearGradient3792"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-4.8699606,-2.3863162)"
|
||||
x1="6.8300767"
|
||||
y1="34.146042"
|
||||
x2="48.691113"
|
||||
y2="36.838673" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient3812"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
|
||||
cx="330.63791"
|
||||
cy="39.962704"
|
||||
fx="330.63791"
|
||||
fy="39.962704"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3814"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
|
||||
cx="345.28433"
|
||||
cy="15.560534"
|
||||
fx="345.28433"
|
||||
fy="15.560534"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient3816"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
|
||||
cx="320.44025"
|
||||
cy="113.23357"
|
||||
fx="320.44025"
|
||||
fy="113.23357"
|
||||
r="19.571428" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective2734" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4467"
|
||||
id="radialGradient3850"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
|
||||
cx="225.26402"
|
||||
cy="672.79736"
|
||||
fx="225.26402"
|
||||
fy="672.79736"
|
||||
r="34.345188" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4467"
|
||||
id="radialGradient3850-4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
|
||||
cx="225.26402"
|
||||
cy="672.79736"
|
||||
fx="225.26402"
|
||||
fy="672.79736"
|
||||
r="34.345188" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 24 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="48 : 24 : 1"
|
||||
inkscape:persp3d-origin="24 : 16 : 1"
|
||||
id="perspective5736-4" />
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.914812,0.01265023,-0.00821502,0.213562,2.253914,27.18889)"
|
||||
r="10.31934"
|
||||
fy="35.127438"
|
||||
fx="23.070683"
|
||||
cy="35.127438"
|
||||
cx="23.070683"
|
||||
id="radialGradient2097-2"
|
||||
xlink:href="#linearGradient4467"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="15.644737"
|
||||
fy="36.421127"
|
||||
fx="24.837126"
|
||||
cy="36.421127"
|
||||
cx="24.837126"
|
||||
gradientTransform="matrix(1,0,0,0.536723,0,16.87306)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1503-3"
|
||||
xlink:href="#linearGradient4467"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective2833-5"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-215.02413,-170.90186)"
|
||||
r="19.571428"
|
||||
fy="27.256668"
|
||||
fx="342.58258"
|
||||
cy="27.256668"
|
||||
cx="342.58258"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient2571-4"
|
||||
xlink:href="#linearGradient3864"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="translate(-0.1767767,-2.6516504)"
|
||||
r="19.571428"
|
||||
fy="15.560534"
|
||||
fx="345.28433"
|
||||
cy="15.560534"
|
||||
cx="345.28433"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3352-1"
|
||||
xlink:href="#linearGradient3593"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="translate(-0.1767767,-2.6516504)"
|
||||
r="19.571428"
|
||||
fy="39.962704"
|
||||
fx="330.63791"
|
||||
cy="39.962704"
|
||||
cx="330.63791"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3354-1"
|
||||
xlink:href="#linearGradient3593"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective3372-5"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<radialGradient
|
||||
r="19.571428"
|
||||
fy="27.256668"
|
||||
fx="342.58258"
|
||||
cy="27.256668"
|
||||
cx="342.58258"
|
||||
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-461.81066,-173.06271)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3369-3"
|
||||
xlink:href="#linearGradient3864"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="19.571428"
|
||||
fy="15.560534"
|
||||
fx="345.28433"
|
||||
cy="15.560534"
|
||||
cx="345.28433"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3372-9"
|
||||
xlink:href="#linearGradient3593"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="19.571428"
|
||||
fy="39.962704"
|
||||
fx="330.63791"
|
||||
cy="39.962704"
|
||||
cx="330.63791"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3375-2"
|
||||
xlink:href="#linearGradient3593"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="19.571428"
|
||||
fy="113.23357"
|
||||
fx="320.44025"
|
||||
cy="113.23357"
|
||||
cx="320.44025"
|
||||
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3380-0"
|
||||
xlink:href="#linearGradient3864"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="19.571428"
|
||||
fy="39.962704"
|
||||
fx="330.63791"
|
||||
cy="39.962704"
|
||||
cx="330.63791"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3812-9"
|
||||
xlink:href="#linearGradient3864"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="19.571428"
|
||||
fy="15.560534"
|
||||
fx="345.28433"
|
||||
cy="15.560534"
|
||||
cx="345.28433"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3814-9"
|
||||
xlink:href="#linearGradient3593"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="19.571428"
|
||||
fy="113.23357"
|
||||
fx="320.44025"
|
||||
cy="113.23357"
|
||||
cx="320.44025"
|
||||
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3816-2"
|
||||
xlink:href="#linearGradient3864"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective2734-9"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<radialGradient
|
||||
r="34.345188"
|
||||
fy="672.79736"
|
||||
fx="225.26402"
|
||||
cy="672.79736"
|
||||
cx="225.26402"
|
||||
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3850-8"
|
||||
xlink:href="#linearGradient4467"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="34.345188"
|
||||
fy="672.79736"
|
||||
fx="225.26402"
|
||||
cy="672.79736"
|
||||
cx="225.26402"
|
||||
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3850-4-2"
|
||||
xlink:href="#linearGradient4467"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
stroke="#3465a4"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-x="47"
|
||||
inkscape:window-height="927"
|
||||
inkscape:window-width="1633"
|
||||
inkscape:showpageshadow="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
showgrid="true"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:cy="30.888147"
|
||||
inkscape:cx="33.014148"
|
||||
inkscape:zoom="10.726351"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1"
|
||||
bordercolor="#ffffff"
|
||||
pagecolor="#000000"
|
||||
id="base"
|
||||
fill="#729fcf"
|
||||
inkscape:window-maximized="0"
|
||||
borderlayer="true"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-bbox-midpoints="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid0001"
|
||||
color="#ffffff"
|
||||
opacity="0.15686275"
|
||||
empcolor="#ffffff"
|
||||
empopacity="0.31372549"
|
||||
dotted="false"
|
||||
empspacing="8"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
visible="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid0002"
|
||||
spacingx="32"
|
||||
spacingy="32"
|
||||
empspacing="2"
|
||||
empcolor="#ffffff"
|
||||
empopacity="0.47058824"
|
||||
color="#ffffff"
|
||||
opacity="0.31372549"
|
||||
visible="true"
|
||||
enabled="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Martin Ruskov</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:source>http://commons.wikimedia.org/wiki/Tango_icon</dc:source>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Layer 1"
|
||||
id="layer1"
|
||||
transform="translate(0,16)">
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.86274511;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="rect14587"
|
||||
width="56"
|
||||
height="25"
|
||||
x="4"
|
||||
y="-12"
|
||||
rx="4" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.8627451;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="rect14591"
|
||||
width="56"
|
||||
height="25"
|
||||
x="4"
|
||||
y="19"
|
||||
rx="4"
|
||||
ry="4" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 25 KiB |
@@ -133,7 +133,6 @@ SOURCES += \
|
||||
ui/viewerwindow.cpp \
|
||||
project/projectfilter.cpp \
|
||||
effects/effectloaders.cpp \
|
||||
global/crossplatformlib.cpp \
|
||||
effects/internal/vsthost.cpp \
|
||||
ui/flowlayout.cpp \
|
||||
dialogs/proxydialog.cpp \
|
||||
@@ -174,7 +173,10 @@ SOURCES += \
|
||||
ui/blur.cpp \
|
||||
ui/menu.cpp \
|
||||
rendering/qopenglshaderprogramptr.cpp \
|
||||
rendering/bitdepths.cpp
|
||||
rendering/bitdepths.cpp \
|
||||
timeline/mediaimportdata.cpp \
|
||||
dialogs/autocutsilencedialog.cpp \
|
||||
ui/columnedgridlayout.cpp
|
||||
|
||||
HEADERS += \
|
||||
ui/mainwindow.h \
|
||||
@@ -259,7 +261,6 @@ HEADERS += \
|
||||
ui/viewerwindow.h \
|
||||
project/projectfilter.h \
|
||||
effects/effectloaders.h \
|
||||
global/crossplatformlib.h \
|
||||
effects/internal/vsthost.h \
|
||||
ui/flowlayout.h \
|
||||
dialogs/proxydialog.h \
|
||||
@@ -302,7 +303,10 @@ HEADERS += \
|
||||
ui/blur.h \
|
||||
ui/menu.h \
|
||||
rendering/qopenglshaderprogramptr.h \
|
||||
rendering/bitdepths.h
|
||||
rendering/bitdepths.h \
|
||||
timeline/mediaimportdata.h \
|
||||
dialogs/autocutsilencedialog.h \
|
||||
ui/columnedgridlayout.h
|
||||
|
||||
FORMS +=
|
||||
|
||||
@@ -316,7 +320,8 @@ TRANSLATIONS += \
|
||||
ts/olive_ru.ts \
|
||||
ts/olive_uk.ts \
|
||||
ts/olive_bs.ts \
|
||||
ts/olive_sr.ts
|
||||
ts/olive_sr.ts \
|
||||
ts/olive_id.ts
|
||||
|
||||
win32 {
|
||||
RC_FILE = packaging/windows/resources.rc
|
||||
@@ -342,9 +347,6 @@ unix:!mac {
|
||||
LIBS += -lOpenColorIO
|
||||
}
|
||||
}
|
||||
unix:!mac:!haiku {
|
||||
LIBS += -ldl
|
||||
}
|
||||
|
||||
RESOURCES += \
|
||||
icons/icons.qrc \
|
||||
|
||||
@@ -86,6 +86,7 @@ void alloc_panels(QWidget* parent) {
|
||||
panel_sequence_viewer->setObjectName("seq_viewer");
|
||||
panel_footage_viewer = new Viewer(parent);
|
||||
panel_footage_viewer->setObjectName("footage_viewer");
|
||||
panel_footage_viewer->show_videoaudio_buttons(true);
|
||||
panel_project = new Project(parent);
|
||||
panel_project->setObjectName("proj_root");
|
||||
panel_effect_controls = new EffectControls(parent);
|
||||
|
||||
+87
-48
@@ -65,20 +65,15 @@ extern "C" {
|
||||
#include "global/debug.h"
|
||||
#include "ui/menu.h"
|
||||
|
||||
// TODO make these configurable
|
||||
const int kDefaultSequenceWidth = 1920;
|
||||
const int kDefaultSequenceHeight = 1080;
|
||||
const double kDefaultSequenceFrameRate = 29.97;
|
||||
const int kDefaultSequenceFrequency = 48000;
|
||||
const int kDefaultSequenceChannelLayout = 3;
|
||||
|
||||
#define MAXIMUM_RECENT_PROJECTS 10 // FIXME: should be configurable
|
||||
|
||||
QString autorecovery_filename;
|
||||
QStringList recent_projects;
|
||||
|
||||
Project::Project(QWidget *parent) :
|
||||
Panel(parent)
|
||||
Panel(parent),
|
||||
sorter(this),
|
||||
sources_common(this, sorter)
|
||||
{
|
||||
QWidget* dockWidgetContents = new QWidget(this);
|
||||
|
||||
@@ -88,10 +83,7 @@ Project::Project(QWidget *parent) :
|
||||
|
||||
setWidget(dockWidgetContents);
|
||||
|
||||
sources_common = new SourcesCommon(this);
|
||||
|
||||
sorter = new ProjectFilter(this);
|
||||
sorter->setSourceModel(&olive::project_model);
|
||||
ConnectFilterToModel();
|
||||
|
||||
// optional toolbar
|
||||
toolbar_widget = new QWidget();
|
||||
@@ -104,57 +96,63 @@ Project::Project(QWidget *parent) :
|
||||
|
||||
QPushButton* toolbar_new = new QPushButton();
|
||||
toolbar_new->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/add-button.svg")));
|
||||
toolbar_new->setToolTip("New");
|
||||
toolbar_new->setToolTip(tr("New"));
|
||||
connect(toolbar_new, SIGNAL(clicked(bool)), this, SLOT(make_new_menu()));
|
||||
toolbar->addWidget(toolbar_new);
|
||||
|
||||
QPushButton* toolbar_open = new QPushButton();
|
||||
toolbar_open->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/open.svg")));
|
||||
toolbar_open->setToolTip("Open Project");
|
||||
toolbar_open->setToolTip(tr("Open Project"));
|
||||
connect(toolbar_open, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(OpenProject()));
|
||||
toolbar->addWidget(toolbar_open);
|
||||
|
||||
QPushButton* toolbar_save = new QPushButton();
|
||||
toolbar_save->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/save.svg")));
|
||||
toolbar_save->setToolTip("Save Project");
|
||||
toolbar_save->setToolTip(tr("Save Project"));
|
||||
connect(toolbar_save, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(save_project()));
|
||||
toolbar->addWidget(toolbar_save);
|
||||
|
||||
QPushButton* toolbar_undo = new QPushButton();
|
||||
toolbar_undo->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/undo.svg")));
|
||||
toolbar_undo->setToolTip("Undo");
|
||||
toolbar_undo->setToolTip(tr("Undo"));
|
||||
connect(toolbar_undo, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(undo()));
|
||||
toolbar->addWidget(toolbar_undo);
|
||||
|
||||
QPushButton* toolbar_redo = new QPushButton();
|
||||
toolbar_redo->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/redo.svg")));
|
||||
toolbar_redo->setToolTip("Redo");
|
||||
toolbar_redo->setToolTip(tr("Redo"));
|
||||
connect(toolbar_redo, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(redo()));
|
||||
toolbar->addWidget(toolbar_redo);
|
||||
|
||||
toolbar_search = new QLineEdit();
|
||||
toolbar_search->setClearButtonEnabled(true);
|
||||
connect(toolbar_search, SIGNAL(textChanged(QString)), sorter, SLOT(update_search_filter(const QString&)));
|
||||
connect(toolbar_search, SIGNAL(textChanged(QString)), &sorter, SLOT(update_search_filter(const QString&)));
|
||||
toolbar->addWidget(toolbar_search);
|
||||
|
||||
QPushButton* toolbar_tree_view = new QPushButton();
|
||||
toolbar_tree_view->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/treeview.svg")));
|
||||
toolbar_tree_view->setToolTip("Tree View");
|
||||
toolbar_tree_view->setToolTip(tr("Tree View"));
|
||||
connect(toolbar_tree_view, SIGNAL(clicked(bool)), this, SLOT(set_tree_view()));
|
||||
toolbar->addWidget(toolbar_tree_view);
|
||||
|
||||
QPushButton* toolbar_icon_view = new QPushButton();
|
||||
toolbar_icon_view->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/iconview.svg")));
|
||||
toolbar_icon_view->setToolTip("Icon View");
|
||||
toolbar_icon_view->setToolTip(tr("Icon View"));
|
||||
connect(toolbar_icon_view, SIGNAL(clicked(bool)), this, SLOT(set_icon_view()));
|
||||
toolbar->addWidget(toolbar_icon_view);
|
||||
|
||||
QPushButton* toolbar_list_view = new QPushButton();
|
||||
toolbar_list_view->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/listview.svg")));
|
||||
toolbar_list_view->setToolTip(tr("List View"));
|
||||
connect(toolbar_list_view, SIGNAL(clicked(bool)), this, SLOT(set_list_view()));
|
||||
toolbar->addWidget(toolbar_list_view);
|
||||
|
||||
verticalLayout->addWidget(toolbar_widget);
|
||||
|
||||
// tree view
|
||||
tree_view = new SourceTable();
|
||||
tree_view = new SourceTable(sources_common);
|
||||
tree_view->project_parent = this;
|
||||
tree_view->setModel(sorter);
|
||||
tree_view->setModel(&sorter);
|
||||
verticalLayout->addWidget(tree_view);
|
||||
|
||||
// Set the first column width
|
||||
@@ -181,23 +179,23 @@ Project::Project(QWidget *parent) :
|
||||
|
||||
icon_view_controls->addStretch();
|
||||
|
||||
QSlider* icon_size_slider = new QSlider(Qt::Horizontal);
|
||||
icon_size_slider = new QSlider(Qt::Horizontal);
|
||||
icon_size_slider->setMinimum(16);
|
||||
icon_size_slider->setMaximum(120);
|
||||
icon_size_slider->setMaximum(256);
|
||||
icon_view_controls->addWidget(icon_size_slider);
|
||||
connect(icon_size_slider, SIGNAL(valueChanged(int)), this, SLOT(set_icon_view_size(int)));
|
||||
|
||||
icon_view_container_layout->addLayout(icon_view_controls);
|
||||
|
||||
icon_view = new SourceIconView();
|
||||
icon_view = new SourceIconView(sources_common);
|
||||
icon_view->project_parent = this;
|
||||
icon_view->setModel(sorter);
|
||||
icon_view->setIconSize(QSize(100, 100));
|
||||
icon_view->setModel(&sorter);
|
||||
icon_view->setGridSize(QSize(100, 100));
|
||||
icon_view->setViewMode(QListView::IconMode);
|
||||
icon_view->setUniformItemSizes(true);
|
||||
icon_view_container_layout->addWidget(icon_view);
|
||||
|
||||
icon_size_slider->setValue(icon_view->iconSize().height());
|
||||
icon_size_slider->setValue(icon_view->gridSize().height());
|
||||
|
||||
verticalLayout->addWidget(icon_view_container);
|
||||
|
||||
@@ -212,8 +210,14 @@ Project::Project(QWidget *parent) :
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
Project::~Project() {
|
||||
delete sorter;
|
||||
void Project::ConnectFilterToModel()
|
||||
{
|
||||
sorter.setSourceModel(&olive::project_model);
|
||||
}
|
||||
|
||||
void Project::DisconnectFilterToModel()
|
||||
{
|
||||
sorter.setSourceModel(nullptr);
|
||||
}
|
||||
|
||||
void Project::Retranslate() {
|
||||
@@ -245,22 +249,22 @@ QString Project::get_next_sequence_name(QString start) {
|
||||
return name;
|
||||
}
|
||||
|
||||
SequencePtr create_sequence_from_media(QVector<Media*>& media_list) {
|
||||
SequencePtr create_sequence_from_media(QVector<olive::timeline::MediaImportData>& media_list) {
|
||||
SequencePtr s(new Sequence());
|
||||
|
||||
s->name = panel_project->get_next_sequence_name();
|
||||
|
||||
// shitty hardcoded default values
|
||||
s->width = kDefaultSequenceWidth;
|
||||
s->height = kDefaultSequenceHeight;
|
||||
s->frame_rate = kDefaultSequenceFrameRate;
|
||||
s->audio_frequency = kDefaultSequenceFrequency;
|
||||
s->audio_layout = kDefaultSequenceChannelLayout;
|
||||
// Retrieve default Sequence settings from Config
|
||||
s->width = olive::CurrentConfig.default_sequence_width;
|
||||
s->height = olive::CurrentConfig.default_sequence_height;
|
||||
s->frame_rate = olive::CurrentConfig.default_sequence_framerate;
|
||||
s->audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency;
|
||||
s->audio_layout = olive::CurrentConfig.default_sequence_audio_channel_layout;
|
||||
|
||||
bool got_video_values = false;
|
||||
bool got_audio_values = false;
|
||||
for (int i=0;i<media_list.size();i++) {
|
||||
Media* media = media_list.at(i);
|
||||
Media* media = media_list.at(i).media();
|
||||
switch (media->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
@@ -418,10 +422,10 @@ void Project::new_folder() {
|
||||
QModelIndex index = olive::project_model.create_index(m->row(), 0, m.get());
|
||||
switch (olive::CurrentConfig.project_view_type) {
|
||||
case olive::PROJECT_VIEW_TREE:
|
||||
tree_view->edit(sorter->mapFromSource(index));
|
||||
tree_view->edit(sorter.mapFromSource(index));
|
||||
break;
|
||||
case olive::PROJECT_VIEW_ICON:
|
||||
icon_view->edit(sorter->mapFromSource(index));
|
||||
icon_view->edit(sorter.mapFromSource(index));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -475,7 +479,7 @@ MediaPtr Project::create_folder_internal(QString name) {
|
||||
}
|
||||
|
||||
Media* Project::item_to_media(const QModelIndex &index) {
|
||||
return static_cast<Media*>(sorter->mapToSource(index).internalPointer());
|
||||
return static_cast<Media*>(sorter.mapToSource(index).internalPointer());
|
||||
}
|
||||
|
||||
MediaPtr Project::item_to_media_ptr(const QModelIndex &index) {
|
||||
@@ -503,6 +507,21 @@ void Project::get_all_media_from_table(QList<Media*>& items, QList<Media*>& list
|
||||
}
|
||||
}
|
||||
|
||||
bool Project::IsToolbarVisible()
|
||||
{
|
||||
return toolbar_widget->isVisible();
|
||||
}
|
||||
|
||||
void Project::SetToolbarVisible(bool visible)
|
||||
{
|
||||
toolbar_widget->setVisible(visible);
|
||||
}
|
||||
|
||||
bool Project::IsProjectWidget(QObject *child)
|
||||
{
|
||||
return (child == tree_view || child == icon_view);
|
||||
}
|
||||
|
||||
bool delete_clips_in_clipboard_with_media(ComboAction* ca, Media* m) {
|
||||
int delete_count = 0;
|
||||
if (clipboard_type == CLIPBOARD_TYPE_CLIP) {
|
||||
@@ -944,7 +963,7 @@ bool Project::reveal_media(Media *media, QModelIndex parent) {
|
||||
// if m == media, then we found the media object we were looking for
|
||||
|
||||
// get sorter proxy item (the item that's "visible")
|
||||
QModelIndex sorted_index = sorter->mapFromSource(item);
|
||||
QModelIndex sorted_index = sorter.mapFromSource(item);
|
||||
|
||||
// retrieve its parent item
|
||||
QModelIndex hierarchy = sorted_index.parent();
|
||||
@@ -959,8 +978,8 @@ bool Project::reveal_media(Media *media, QModelIndex parent) {
|
||||
|
||||
// select item (requires a QItemSelection object to select the whole row)
|
||||
QItemSelection row_select(
|
||||
sorter->index(sorted_index.row(), 0, sorted_index.parent()),
|
||||
sorter->index(sorted_index.row(), sorter->columnCount()-1, sorted_index.parent())
|
||||
sorter.index(sorted_index.row(), 0, sorted_index.parent()),
|
||||
sorter.index(sorted_index.row(), sorter.columnCount()-1, sorted_index.parent())
|
||||
);
|
||||
|
||||
tree_view->selectionModel()->select(row_select, QItemSelectionModel::Select);
|
||||
@@ -1307,14 +1326,23 @@ void Project::save_project(bool autorecovery) {
|
||||
|
||||
void Project::update_view_type() {
|
||||
tree_view->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_TREE);
|
||||
icon_view_container->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON);
|
||||
icon_view_container->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON
|
||||
|| olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_LIST);
|
||||
|
||||
|
||||
switch (olive::CurrentConfig.project_view_type) {
|
||||
case olive::PROJECT_VIEW_TREE:
|
||||
sources_common->view = tree_view;
|
||||
sources_common.view = tree_view;
|
||||
break;
|
||||
case olive::PROJECT_VIEW_ICON:
|
||||
sources_common->view = icon_view;
|
||||
case olive::PROJECT_VIEW_LIST:
|
||||
icon_view->setViewMode(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON ?
|
||||
QListView::IconMode : QListView::ListMode);
|
||||
|
||||
// update list/grid size since they use this value slightly differently
|
||||
set_icon_view_size(icon_size_slider->value());
|
||||
|
||||
sources_common.view = icon_view;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1324,6 +1352,12 @@ void Project::set_icon_view() {
|
||||
update_view_type();
|
||||
}
|
||||
|
||||
void Project::set_list_view()
|
||||
{
|
||||
olive::CurrentConfig.project_view_type = olive::PROJECT_VIEW_LIST;
|
||||
update_view_type();
|
||||
}
|
||||
|
||||
void Project::set_tree_view() {
|
||||
olive::CurrentConfig.project_view_type = olive::PROJECT_VIEW_TREE;
|
||||
update_view_type();
|
||||
@@ -1352,7 +1386,12 @@ void Project::clear_recent_projects() {
|
||||
}
|
||||
|
||||
void Project::set_icon_view_size(int s) {
|
||||
icon_view->setIconSize(QSize(s, s));
|
||||
if (icon_view->viewMode() == QListView::IconMode) {
|
||||
icon_view->setGridSize(QSize(s, s));
|
||||
} else {
|
||||
icon_view->setGridSize(QSize());
|
||||
icon_view->setIconSize(QSize(s, s));
|
||||
}
|
||||
}
|
||||
|
||||
void Project::set_up_dir_enabled() {
|
||||
|
||||
+18
-9
@@ -35,6 +35,7 @@
|
||||
#include "project/sourcescommon.h"
|
||||
#include "ui/panel.h"
|
||||
#include "ui/sourceiconview.h"
|
||||
#include "timeline/mediaimportdata.h"
|
||||
#include "undo/undo.h"
|
||||
|
||||
#include "ui/sourcetable.h"
|
||||
@@ -45,7 +46,7 @@
|
||||
extern QString autorecovery_filename;
|
||||
extern QStringList recent_projects;
|
||||
|
||||
SequencePtr create_sequence_from_media(QVector<Media *> &media_list);
|
||||
SequencePtr create_sequence_from_media(QVector<olive::timeline::MediaImportData> &media_list);
|
||||
|
||||
QString get_channel_layout_name(int channels, uint64_t layout);
|
||||
QString get_interlacing_name(int interlacing);
|
||||
@@ -54,7 +55,9 @@ class Project : public Panel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Project(QWidget *parent = nullptr);
|
||||
~Project();
|
||||
|
||||
void ConnectFilterToModel();
|
||||
void DisconnectFilterToModel();
|
||||
|
||||
bool is_focused();
|
||||
void clear();
|
||||
@@ -77,19 +80,14 @@ public:
|
||||
|
||||
QVector<Media*> list_all_project_sequences();
|
||||
|
||||
SourceTable* tree_view;
|
||||
SourceIconView* icon_view;
|
||||
SourcesCommon* sources_common;
|
||||
|
||||
ProjectFilter* sorter;
|
||||
|
||||
QVector<Media*> last_imported_media;
|
||||
|
||||
QModelIndexList get_current_selected();
|
||||
|
||||
void get_all_media_from_table(QList<Media *> &items, QList<Media *> &list, int type = -1);
|
||||
|
||||
QWidget* toolbar_widget;
|
||||
bool IsToolbarVisible();
|
||||
bool IsProjectWidget(QObject *child);
|
||||
|
||||
virtual void Retranslate() override;
|
||||
protected:
|
||||
@@ -103,6 +101,8 @@ public slots:
|
||||
void open_properties();
|
||||
void new_folder();
|
||||
void new_sequence();
|
||||
|
||||
void SetToolbarVisible(bool visible);
|
||||
private:
|
||||
void save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, const QModelIndex &parent = QModelIndex());
|
||||
int folder_id;
|
||||
@@ -112,11 +112,20 @@ private:
|
||||
QString get_file_name_from_path(const QString &path);
|
||||
QDir proj_dir;
|
||||
QWidget* icon_view_container;
|
||||
QSlider* icon_size_slider;
|
||||
QPushButton* directory_up;
|
||||
QLineEdit* toolbar_search;
|
||||
|
||||
QWidget* toolbar_widget;
|
||||
SourceTable* tree_view;
|
||||
SourceIconView* icon_view;
|
||||
|
||||
ProjectFilter sorter;
|
||||
SourcesCommon sources_common;
|
||||
private slots:
|
||||
void update_view_type();
|
||||
void set_icon_view();
|
||||
void set_list_view();
|
||||
void set_tree_view();
|
||||
void clear_recent_projects();
|
||||
void set_icon_view_size(int);
|
||||
|
||||
+110
-20
@@ -138,6 +138,53 @@ void Timeline::Retranslate() {
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
void Timeline::split_clip_at_positions(ComboAction* ca, int clip_index, QVector<long> positions) {
|
||||
|
||||
QVector<int> pre_splits;
|
||||
|
||||
// Add the clip and each of its links to the pre_splits array
|
||||
Clip* clip = olive::ActiveSequence->clips.at(clip_index).get();
|
||||
pre_splits.append(clip_index);
|
||||
for (int i=0;i<clip->linked.size();i++) {
|
||||
pre_splits.append(clip->linked.at(i));
|
||||
}
|
||||
|
||||
std::sort(positions.begin(), positions.end());
|
||||
|
||||
// Remove any duplicate positions
|
||||
for (int i=1;i<positions.size();i++) {
|
||||
if (positions.at(i-1) == positions.at(i)) {
|
||||
positions.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=1;i<positions.size();i++) {
|
||||
Q_ASSERT(positions.at(i-1) < positions.at(i));
|
||||
}
|
||||
|
||||
QVector< QVector<ClipPtr> > post_splits(positions.size());
|
||||
|
||||
for (int i=positions.size()-1;i>=0;i--) {
|
||||
|
||||
post_splits[i].resize(pre_splits.size());
|
||||
|
||||
for (int j=0;j<pre_splits.size();j++) {
|
||||
post_splits[i][j] = split_clip(ca, true, pre_splits.at(j), positions.at(i));
|
||||
|
||||
if (post_splits[i][j] != nullptr && i + 1 < positions.size()) {
|
||||
post_splits[i][j]->set_timeline_out(positions.at(i+1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0;i<post_splits.size();i++) {
|
||||
relink_clips_using_ids(pre_splits, post_splits[i]);
|
||||
ca->append(new AddClipCommand(olive::ActiveSequence.get(), post_splits[i]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Timeline::previous_cut() {
|
||||
if (olive::ActiveSequence != nullptr
|
||||
&& olive::ActiveSequence->playhead > 0) {
|
||||
@@ -192,14 +239,15 @@ void Timeline::toggle_show_all() {
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector<Media*>& media_list) {
|
||||
void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector<olive::timeline::MediaImportData>& media_list) {
|
||||
video_ghosts = false;
|
||||
audio_ghosts = false;
|
||||
|
||||
for (int i=0;i<media_list.size();i++) {
|
||||
bool can_import = true;
|
||||
|
||||
Media* medium = media_list.at(i);
|
||||
const olive::timeline::MediaImportData import_data = media_list.at(i);
|
||||
Media* medium = import_data.media();
|
||||
Footage* m = nullptr;
|
||||
Sequence* s = nullptr;
|
||||
long sequence_length = 0;
|
||||
@@ -212,7 +260,9 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
|
||||
can_import = m->ready;
|
||||
if (m->using_inout) {
|
||||
double source_fr = 30;
|
||||
if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate * m->speed;
|
||||
if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) {
|
||||
source_fr = m->video_tracks.at(0).video_frame_rate * m->speed;
|
||||
}
|
||||
default_clip_in = rescale_frame_number(m->in, source_fr, seq->frame_rate);
|
||||
default_clip_out = rescale_frame_number(m->out, source_fr, seq->frame_rate);
|
||||
}
|
||||
@@ -253,20 +303,27 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
|
||||
}
|
||||
}
|
||||
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
if (m->audio_tracks.at(j).enabled) {
|
||||
g.track = j;
|
||||
g.media_stream = m->audio_tracks.at(j).file_index;
|
||||
ghosts.append(g);
|
||||
audio_ghosts = true;
|
||||
if (import_data.type() == olive::timeline::kImportAudioOnly
|
||||
|| import_data.type() == olive::timeline::kImportBoth) {
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
if (m->audio_tracks.at(j).enabled) {
|
||||
g.track = j;
|
||||
g.media_stream = m->audio_tracks.at(j).file_index;
|
||||
ghosts.append(g);
|
||||
audio_ghosts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
if (m->video_tracks.at(j).enabled) {
|
||||
g.track = -1-j;
|
||||
g.media_stream = m->video_tracks.at(j).file_index;
|
||||
ghosts.append(g);
|
||||
video_ghosts = true;
|
||||
|
||||
if (import_data.type() == olive::timeline::kImportVideoOnly
|
||||
|| import_data.type() == olive::timeline::kImportBoth) {
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
if (m->video_tracks.at(j).enabled) {
|
||||
g.track = -1-j;
|
||||
g.media_stream = m->video_tracks.at(j).file_index;
|
||||
ghosts.append(g);
|
||||
video_ghosts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -277,10 +334,17 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
|
||||
g.out -= (sequence_length - default_clip_out);
|
||||
}
|
||||
|
||||
g.track = -1;
|
||||
ghosts.append(g);
|
||||
g.track = 0;
|
||||
ghosts.append(g);
|
||||
if (import_data.type() == olive::timeline::kImportVideoOnly
|
||||
|| import_data.type() == olive::timeline::kImportBoth) {
|
||||
g.track = -1;
|
||||
ghosts.append(g);
|
||||
}
|
||||
|
||||
if (import_data.type() == olive::timeline::kImportAudioOnly
|
||||
|| import_data.type() == olive::timeline::kImportBoth) {
|
||||
g.track = 0;
|
||||
ghosts.append(g);
|
||||
}
|
||||
|
||||
video_ghosts = true;
|
||||
audio_ghosts = true;
|
||||
@@ -446,9 +510,35 @@ void Timeline::nest() {
|
||||
MediaPtr m = panel_project->create_sequence_internal(ca, s, false, nullptr);
|
||||
|
||||
// add nested sequence to active sequence
|
||||
QVector<Media*> media_list;
|
||||
QVector<olive::timeline::MediaImportData> media_list;
|
||||
media_list.append(m.get());
|
||||
create_ghosts_from_media(olive::ActiveSequence.get(), earliest_point, media_list);
|
||||
|
||||
// ensure ghosts won't overlap anything
|
||||
for (int j=0;j<olive::ActiveSequence->clips.size();j++) {
|
||||
Clip* c = olive::ActiveSequence->clips.at(j).get();
|
||||
if (c != nullptr && !selected_clips.contains(j)) {
|
||||
for (int i=0;i<ghosts.size();i++) {
|
||||
Ghost& g = ghosts[i];
|
||||
if (c->track() == g.track
|
||||
&& !((c->timeline_in() < g.in
|
||||
&& c->timeline_out() < g.in)
|
||||
|| (c->timeline_in() > g.out
|
||||
&& c->timeline_out() > g.out))) {
|
||||
// There's a clip occupied by the space taken up by this ghost. Move up/down a track, and seek again
|
||||
if (g.track < 0) {
|
||||
g.track--;
|
||||
} else {
|
||||
g.track++;
|
||||
}
|
||||
j = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
add_clips_from_ghosts(ca, olive::ActiveSequence.get());
|
||||
|
||||
panel_graph_editor->set_row(nullptr);
|
||||
|
||||
+3
-1
@@ -29,6 +29,7 @@
|
||||
#include "ui/timelinetools.h"
|
||||
#include "timeline/selection.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/mediaimportdata.h"
|
||||
#include "undo/undo.h"
|
||||
#include "ui/timelineheader.h"
|
||||
#include "ui/resizablescrollbar.h"
|
||||
@@ -116,6 +117,7 @@ public:
|
||||
bool split_selection(ComboAction* ca);
|
||||
bool split_all_clips_at_point(ComboAction *ca, long point);
|
||||
bool split_clip_and_relink(ComboAction* ca, int clip, long frame, bool relink);
|
||||
void split_clip_at_positions(ComboAction* ca, int clip_index, QVector<long> positions);
|
||||
void clean_up_selections(QVector<Selection>& areas);
|
||||
void deselect_area(long in, long out, int track);
|
||||
void delete_areas_and_relink(ComboAction *ca, QVector<Selection>& areas, bool deselect_areas);
|
||||
@@ -125,7 +127,7 @@ public:
|
||||
void edit_to_point_internal(bool in, bool ripple);
|
||||
void delete_in_out_internal(bool ripple);
|
||||
|
||||
void create_ghosts_from_media(Sequence *seq, long entry_point, QVector<Media *> &media_list);
|
||||
void create_ghosts_from_media(Sequence *seq, long entry_point, QVector<olive::timeline::MediaImportData> &media_list);
|
||||
void add_clips_from_ghosts(ComboAction *ca, Sequence *s);
|
||||
|
||||
int getTimelineScreenPointFromFrame(long frame);
|
||||
|
||||
+133
-46
@@ -20,6 +20,21 @@
|
||||
|
||||
#include "viewer.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
#include <QtMath>
|
||||
#include <QAudioOutput>
|
||||
#include <QPainter>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QDrag>
|
||||
#include <QMimeData>
|
||||
|
||||
#include "rendering/audio.h"
|
||||
#include "timeline.h"
|
||||
#include "panels/project.h"
|
||||
@@ -45,19 +60,6 @@
|
||||
#define FRAMES_IN_ONE_MINUTE 1798 // 1800 - 2
|
||||
#define FRAMES_IN_TEN_MINUTES 17978 // (FRAMES_IN_ONE_MINUTE * 10) - 2
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
#include <QtMath>
|
||||
#include <QAudioOutput>
|
||||
#include <QPainter>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
|
||||
Viewer::Viewer(QWidget *parent) :
|
||||
Panel(parent),
|
||||
playing(false),
|
||||
@@ -120,7 +122,6 @@ bool Viewer::is_main_sequence() {
|
||||
}
|
||||
|
||||
void Viewer::set_main_sequence() {
|
||||
clean_created_seq();
|
||||
set_sequence(true, olive::ActiveSequence);
|
||||
}
|
||||
|
||||
@@ -401,7 +402,7 @@ void Viewer::play(bool in_to_out) {
|
||||
if (!is_recording_cued()
|
||||
&& playback_speed >= 0
|
||||
&& (playing_in_to_out
|
||||
|| seq->playhead >= sequence_end_frame
|
||||
|| (olive::CurrentConfig.auto_seek_to_beginning && seq->playhead >= sequence_end_frame)
|
||||
|| (seek_to_in && seq->playhead >= seq->workarea_out))) {
|
||||
seek(seek_to_in ? seq->workarea_in : 0);
|
||||
}
|
||||
@@ -539,6 +540,17 @@ void Viewer::update_viewer() {
|
||||
update_end_timecode();
|
||||
}
|
||||
|
||||
void Viewer::initiate_drag(olive::timeline::MediaImportType drag_type)
|
||||
{
|
||||
// FIXME: This should contain actual metadata rather than fake metadata
|
||||
|
||||
QDrag* drag = new QDrag(this);
|
||||
QMimeData* mimeData = new QMimeData;
|
||||
mimeData->setText(QString::number(drag_type));
|
||||
drag->setMimeData(mimeData);
|
||||
drag->exec();
|
||||
}
|
||||
|
||||
void Viewer::clear_in() {
|
||||
if (seq != nullptr
|
||||
&& seq->using_workarea) {
|
||||
@@ -586,6 +598,12 @@ void Viewer::set_panel_name(const QString &n) {
|
||||
update_window_title();
|
||||
}
|
||||
|
||||
void Viewer::show_videoaudio_buttons(bool s)
|
||||
{
|
||||
video_only_button->setVisible(s);
|
||||
audio_only_button->setVisible(s);
|
||||
}
|
||||
|
||||
void Viewer::update_window_title() {
|
||||
QString name;
|
||||
if (seq == nullptr) {
|
||||
@@ -659,8 +677,12 @@ void Viewer::setup_ui() {
|
||||
QHBoxLayout* lower_control_layout = new QHBoxLayout(lower_controls);
|
||||
lower_control_layout->setMargin(0);
|
||||
|
||||
// current time code
|
||||
QSizePolicy timecode_container_policy(QSizePolicy::Minimum, QSizePolicy::Maximum);
|
||||
QSizePolicy lower_control_policy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||
|
||||
// Current time code container
|
||||
QWidget* current_timecode_container = new QWidget();
|
||||
current_timecode_container->setSizePolicy(timecode_container_policy);
|
||||
QHBoxLayout* current_timecode_container_layout = new QHBoxLayout(current_timecode_container);
|
||||
current_timecode_container_layout->setSpacing(0);
|
||||
current_timecode_container_layout->setMargin(0);
|
||||
@@ -668,11 +690,19 @@ void Viewer::setup_ui() {
|
||||
current_timecode_container_layout->addWidget(current_timecode_slider);
|
||||
lower_control_layout->addWidget(current_timecode_container);
|
||||
|
||||
// Left controls container
|
||||
QWidget* left_controls = new QWidget();
|
||||
left_controls->setSizePolicy(lower_control_policy);
|
||||
lower_control_layout->addWidget(left_controls);
|
||||
|
||||
// Playback controls container
|
||||
QWidget* playback_controls = new QWidget();
|
||||
playback_controls->setSizePolicy(lower_control_policy);
|
||||
|
||||
QHBoxLayout* playback_control_layout = new QHBoxLayout(playback_controls);
|
||||
playback_control_layout->setSpacing(0);
|
||||
playback_control_layout->setMargin(0);
|
||||
playback_control_layout->addStretch();
|
||||
|
||||
go_to_start_button = new QPushButton();
|
||||
go_to_start_button->setIcon(olive::icon::ViewerGoToStart);
|
||||
@@ -699,9 +729,40 @@ void Viewer::setup_ui() {
|
||||
connect(go_to_end_frame, SIGNAL(clicked(bool)), this, SLOT(go_to_out()));
|
||||
playback_control_layout->addWidget(go_to_end_frame);
|
||||
|
||||
playback_control_layout->addStretch();
|
||||
|
||||
lower_control_layout->addWidget(playback_controls);
|
||||
|
||||
// Right controls container
|
||||
QWidget* right_controls = new QWidget();
|
||||
right_controls->setSizePolicy(lower_control_policy);
|
||||
|
||||
QHBoxLayout* right_control_layout = new QHBoxLayout(right_controls);
|
||||
right_control_layout->setSpacing(0);
|
||||
right_control_layout->setMargin(0);
|
||||
right_control_layout->addStretch();
|
||||
|
||||
video_only_button = new QPushButton();
|
||||
video_only_button->setToolTip(tr("Drag video only"));
|
||||
video_only_button->setIcon(olive::icon::MediaVideo);
|
||||
video_only_button->setVisible(false);
|
||||
right_control_layout->addWidget(video_only_button);
|
||||
connect(video_only_button, SIGNAL(pressed()), this, SLOT(drag_video_only()));
|
||||
|
||||
audio_only_button = new QPushButton();
|
||||
audio_only_button->setToolTip(tr("Drag audio only"));
|
||||
audio_only_button->setIcon(olive::icon::MediaAudio);
|
||||
audio_only_button->setVisible(false);
|
||||
right_control_layout->addWidget(audio_only_button);
|
||||
connect(audio_only_button, SIGNAL(pressed()), this, SLOT(drag_audio_only()));
|
||||
|
||||
right_control_layout->addStretch();
|
||||
|
||||
lower_control_layout->addWidget(right_controls);
|
||||
|
||||
// End time code container
|
||||
QWidget* end_timecode_container = new QWidget();
|
||||
end_timecode_container->setSizePolicy(timecode_container_policy);
|
||||
|
||||
QHBoxLayout* end_timecode_layout = new QHBoxLayout(end_timecode_container);
|
||||
end_timecode_layout->setSpacing(0);
|
||||
@@ -720,7 +781,8 @@ void Viewer::set_media(Media* m) {
|
||||
main_sequence = false;
|
||||
media = m;
|
||||
|
||||
clean_created_seq();
|
||||
SequencePtr new_sequence = nullptr;
|
||||
|
||||
if (media != nullptr) {
|
||||
switch (media->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
@@ -729,30 +791,31 @@ void Viewer::set_media(Media* m) {
|
||||
|
||||
marker_ref = &footage->markers;
|
||||
|
||||
seq = std::make_shared<Sequence>();
|
||||
new_sequence = std::make_shared<Sequence>();
|
||||
created_sequence = true;
|
||||
seq->wrapper_sequence = true;
|
||||
seq->name = footage->name;
|
||||
new_sequence->wrapper_sequence = true;
|
||||
new_sequence->name = footage->name;
|
||||
|
||||
seq->using_workarea = footage->using_inout;
|
||||
new_sequence->using_workarea = footage->using_inout;
|
||||
if (footage->using_inout) {
|
||||
seq->workarea_in = footage->in;
|
||||
seq->workarea_out = footage->out;
|
||||
new_sequence->workarea_in = footage->in;
|
||||
new_sequence->workarea_out = footage->out;
|
||||
}
|
||||
|
||||
// FIXME: Move this magic number to Config
|
||||
seq->frame_rate = 30;
|
||||
new_sequence->frame_rate = olive::CurrentConfig.default_sequence_framerate;
|
||||
|
||||
if (footage->video_tracks.size() > 0) {
|
||||
const FootageStream& video_stream = footage->video_tracks.at(0);
|
||||
seq->width = video_stream.video_width;
|
||||
seq->height = video_stream.video_height;
|
||||
if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate * footage->speed;
|
||||
new_sequence->width = video_stream.video_width;
|
||||
new_sequence->height = video_stream.video_height;
|
||||
if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) {
|
||||
new_sequence->frame_rate = video_stream.video_frame_rate * footage->speed;
|
||||
}
|
||||
|
||||
ClipPtr c = std::make_shared<Clip>(seq.get());
|
||||
ClipPtr c = std::make_shared<Clip>(new_sequence.get());
|
||||
c->set_media(media, video_stream.file_index);
|
||||
c->set_timeline_in(0);
|
||||
c->set_timeline_out(footage->get_length_in_frames(seq->frame_rate));
|
||||
c->set_timeline_out(footage->get_length_in_frames(new_sequence->frame_rate));
|
||||
if (c->timeline_out() <= 0) {
|
||||
// FIXME: Move this magic number to Config
|
||||
c->set_timeline_out(150);
|
||||
@@ -760,25 +823,24 @@ void Viewer::set_media(Media* m) {
|
||||
c->set_track(-1);
|
||||
c->set_clip_in(0);
|
||||
c->refresh();
|
||||
seq->clips.append(c);
|
||||
new_sequence->clips.append(c);
|
||||
} else {
|
||||
// FIXME: Move this magic number to Config
|
||||
seq->width = 1920;
|
||||
seq->height = 1080;
|
||||
new_sequence->width = olive::CurrentConfig.default_sequence_width;
|
||||
new_sequence->height = olive::CurrentConfig.default_sequence_height;
|
||||
}
|
||||
|
||||
if (footage->audio_tracks.size() > 0) {
|
||||
const FootageStream& audio_stream = footage->audio_tracks.at(0);
|
||||
seq->audio_frequency = audio_stream.audio_frequency;
|
||||
new_sequence->audio_frequency = audio_stream.audio_frequency;
|
||||
|
||||
ClipPtr c = std::make_shared<Clip>(seq.get());
|
||||
ClipPtr c = std::make_shared<Clip>(new_sequence.get());
|
||||
c->set_media(media, audio_stream.file_index);
|
||||
c->set_timeline_in(0);
|
||||
c->set_timeline_out(footage->get_length_in_frames(seq->frame_rate));
|
||||
c->set_timeline_out(footage->get_length_in_frames(new_sequence->frame_rate));
|
||||
c->set_track(0);
|
||||
c->set_clip_in(0);
|
||||
c->refresh();
|
||||
seq->clips.append(c);
|
||||
new_sequence->clips.append(c);
|
||||
|
||||
if (footage->video_tracks.size() == 0) {
|
||||
viewer_widget->waveform = true;
|
||||
@@ -787,19 +849,19 @@ void Viewer::set_media(Media* m) {
|
||||
viewer_widget->frame_update();
|
||||
}
|
||||
} else {
|
||||
// FIXME: Move this magic number to Config
|
||||
seq->audio_frequency = 48000;
|
||||
new_sequence->audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency;
|
||||
}
|
||||
|
||||
seq->audio_layout = AV_CH_LAYOUT_STEREO;
|
||||
new_sequence->audio_layout = AV_CH_LAYOUT_STEREO;
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
seq = media->to_sequence();
|
||||
new_sequence = media->to_sequence();
|
||||
break;
|
||||
}
|
||||
}
|
||||
set_sequence(false, seq);
|
||||
|
||||
set_sequence(false, new_sequence);
|
||||
}
|
||||
|
||||
void Viewer::update_playhead() {
|
||||
@@ -810,7 +872,11 @@ void Viewer::timer_update() {
|
||||
previous_playhead = seq->playhead;
|
||||
|
||||
seq->playhead = qMax(0, qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate * playback_speed)));
|
||||
if (olive::CurrentConfig.seek_also_selects) panel_timeline->select_from_playhead();
|
||||
|
||||
if (olive::CurrentConfig.seek_also_selects) {
|
||||
panel_timeline->select_from_playhead();
|
||||
}
|
||||
|
||||
update_parents(olive::CurrentConfig.seek_also_selects);
|
||||
|
||||
if (playing) {
|
||||
@@ -821,7 +887,8 @@ void Viewer::timer_update() {
|
||||
pause();
|
||||
}
|
||||
} else if (playback_speed > 0) {
|
||||
if (seq->playhead >= seq->getEndFrame()) {
|
||||
long end_frame = seq->getEndFrame();
|
||||
if ((olive::CurrentConfig.auto_seek_to_beginning || previous_playhead < end_frame) && seq->playhead >= end_frame) {
|
||||
pause();
|
||||
}
|
||||
if (seq->using_workarea && seq->playhead >= seq->workarea_out) {
|
||||
@@ -848,6 +915,16 @@ void Viewer::resize_move(double d) {
|
||||
set_zoom_value(headers->get_zoom()*d);
|
||||
}
|
||||
|
||||
void Viewer::drag_video_only()
|
||||
{
|
||||
initiate_drag(olive::timeline::kImportVideoOnly);
|
||||
}
|
||||
|
||||
void Viewer::drag_audio_only()
|
||||
{
|
||||
initiate_drag(olive::timeline::kImportAudioOnly);
|
||||
}
|
||||
|
||||
void Viewer::clean_created_seq() {
|
||||
viewer_widget->waveform = false;
|
||||
|
||||
@@ -861,7 +938,9 @@ void Viewer::clean_created_seq() {
|
||||
}
|
||||
*/
|
||||
|
||||
// Delete the current sequence
|
||||
seq.reset();
|
||||
|
||||
created_sequence = false;
|
||||
}
|
||||
}
|
||||
@@ -871,13 +950,19 @@ void Viewer::set_sequence(bool main, SequencePtr s) {
|
||||
|
||||
reset_all_audio();
|
||||
|
||||
main_sequence = main;
|
||||
viewer_widget->wait_until_render_is_paused();
|
||||
|
||||
// If we had a current sequence open, close it
|
||||
if (seq != nullptr) {
|
||||
close_active_clips(seq.get());
|
||||
}
|
||||
|
||||
clean_created_seq();
|
||||
|
||||
main_sequence = main;
|
||||
|
||||
|
||||
|
||||
seq = (main) ? olive::ActiveSequence : s;
|
||||
|
||||
bool null_sequence = (seq == nullptr);
|
||||
@@ -891,6 +976,8 @@ void Viewer::set_sequence(bool main, SequencePtr s) {
|
||||
play_button->setEnabled(!null_sequence);
|
||||
next_frame_button->setEnabled(!null_sequence);
|
||||
go_to_end_frame->setEnabled(!null_sequence);
|
||||
video_only_button->setEnabled(!null_sequence);
|
||||
audio_only_button->setEnabled(!null_sequence);
|
||||
|
||||
if (!null_sequence) {
|
||||
current_timecode_slider->SetFrameRate(seq->frame_rate);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <QPushButton>
|
||||
|
||||
#include "timeline/marker.h"
|
||||
#include "timeline/mediaimportdata.h"
|
||||
#include "project/media.h"
|
||||
|
||||
#include "ui/panel.h"
|
||||
@@ -64,6 +65,7 @@ public:
|
||||
void set_out_point();
|
||||
void set_zoom(bool in);
|
||||
void set_panel_name(const QString& n);
|
||||
void show_videoaudio_buttons(bool s);
|
||||
|
||||
// playback functions
|
||||
void seek(long p);
|
||||
@@ -98,6 +100,10 @@ public:
|
||||
|
||||
TimelineHeader* headers;
|
||||
|
||||
|
||||
|
||||
void initiate_drag(olive::timeline::MediaImportType drag_type);
|
||||
|
||||
virtual void Retranslate() override;
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
@@ -116,12 +122,17 @@ public slots:
|
||||
void close_media();
|
||||
void update_viewer();
|
||||
|
||||
|
||||
|
||||
private slots:
|
||||
void update_playhead();
|
||||
void timer_update();
|
||||
void recording_flasher_update();
|
||||
void resize_move(double d);
|
||||
|
||||
void drag_video_only();
|
||||
void drag_audio_only();
|
||||
|
||||
private:
|
||||
|
||||
void update_window_title();
|
||||
@@ -155,6 +166,9 @@ private:
|
||||
QPushButton* next_frame_button;
|
||||
QPushButton* go_to_end_frame;
|
||||
|
||||
QPushButton* video_only_button;
|
||||
QPushButton* audio_only_button;
|
||||
|
||||
bool cue_recording_internal;
|
||||
QTimer recording_flasher;
|
||||
|
||||
|
||||
@@ -77,17 +77,3 @@ FootageStream* Footage::get_stream_from_file_index(bool video, int index) {
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FootageStream::make_square_thumb() {
|
||||
// generate square version for QListView?
|
||||
int square_size = qMax(video_preview.width(), video_preview.height());
|
||||
QPixmap pixmap(square_size, square_size);
|
||||
pixmap.fill(Qt::transparent);
|
||||
QPainter p(&pixmap);
|
||||
int diff = (video_preview.width() - video_preview.height())>>1;
|
||||
int sqx = (diff < 0) ? -diff : 0;
|
||||
int sqy = (diff > 0) ? diff : 0;
|
||||
p.drawImage(sqx, sqy, video_preview);
|
||||
p.end();
|
||||
video_preview_square = QIcon(pixmap);
|
||||
}
|
||||
|
||||
@@ -62,9 +62,7 @@ struct FootageStream {
|
||||
// preview thumbnail/waveform
|
||||
bool preview_done;
|
||||
QImage video_preview;
|
||||
QIcon video_preview_square;
|
||||
QVector<char> audio_preview;
|
||||
void make_square_thumb();
|
||||
};
|
||||
|
||||
struct Footage {
|
||||
|
||||
+5
-10
@@ -34,10 +34,9 @@
|
||||
#include <QFile>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
LoadThread::LoadThread(const QString& filename, bool autorecovery, bool clear) :
|
||||
LoadThread::LoadThread(const QString& filename, bool autorecovery) :
|
||||
filename_(filename),
|
||||
autorecovery_(autorecovery),
|
||||
clear_(clear),
|
||||
cancelled_(false)
|
||||
{
|
||||
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
|
||||
@@ -101,7 +100,7 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) {
|
||||
c->closing_transition = (sharing_clip->opening_transition);
|
||||
|
||||
// since this is the closed clip, make this clip the secondary
|
||||
c->opening_transition->secondary_clip = c;
|
||||
c->closing_transition->secondary_clip = c;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -606,8 +605,6 @@ Media* LoadThread::find_loaded_folder_by_id(int id) {
|
||||
}
|
||||
|
||||
void LoadThread::OrganizeFolders(int folder) {
|
||||
qDebug() << "starting with" << folder;
|
||||
|
||||
for (int i=0;i<loaded_folders.size();i++) {
|
||||
MediaPtr item = loaded_folders.at(i);
|
||||
int parent_id = item->temp_id2;
|
||||
@@ -615,7 +612,7 @@ void LoadThread::OrganizeFolders(int folder) {
|
||||
if (parent_id == folder) {
|
||||
olive::project_model.appendChild(find_loaded_folder_by_id(parent_id), item);
|
||||
|
||||
OrganizeFolders(parent_id);
|
||||
OrganizeFolders(item->temp_id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -776,14 +773,12 @@ void LoadThread::success_func() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (clear_) {
|
||||
olive::Global->update_project_filename(orig_filename);
|
||||
}
|
||||
olive::Global->update_project_filename(orig_filename);
|
||||
} else {
|
||||
panel_project->add_recent_project(filename_);
|
||||
}
|
||||
|
||||
olive::Global->set_modified(autorecovery_ || !clear_);
|
||||
olive::Global->set_modified(autorecovery_);
|
||||
if (open_seq != nullptr) {
|
||||
olive::Global->set_sequence(open_seq);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class LoadThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LoadThread(const QString& filename, bool autorecovery, bool clear);
|
||||
LoadThread(const QString& filename, bool autorecovery);
|
||||
void run();
|
||||
public slots:
|
||||
void cancel();
|
||||
@@ -50,7 +50,6 @@ private slots:
|
||||
void success_func();
|
||||
private:
|
||||
bool autorecovery_;
|
||||
bool clear_;
|
||||
QString filename_;
|
||||
|
||||
bool load_worker(QFile& f, QXmlStreamReader& stream, int type);
|
||||
|
||||
+24
-15
@@ -284,6 +284,24 @@ int Media::columnCount() const {
|
||||
return 3;
|
||||
}
|
||||
|
||||
QString Media::GetStringDuration() {
|
||||
if (get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
Sequence* s = to_sequence().get();
|
||||
return frame_to_timecode(s->getEndFrame(), olive::CurrentConfig.timecode_view, s->frame_rate);
|
||||
}
|
||||
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* f = to_footage();
|
||||
double r = 30;
|
||||
|
||||
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate))
|
||||
r = f->video_tracks.at(0).video_frame_rate * f->speed;
|
||||
|
||||
long len = f->get_length_in_frames(r);
|
||||
if (len > 0) return frame_to_timecode(len, olive::CurrentConfig.timecode_view, r);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QVariant Media::data(int column, int role) {
|
||||
switch (role) {
|
||||
case Qt::DecorationRole:
|
||||
@@ -292,7 +310,7 @@ QVariant Media::data(int column, int role) {
|
||||
Footage* f = to_footage();
|
||||
if (f->video_tracks.size() > 0
|
||||
&& f->video_tracks.at(0).preview_done) {
|
||||
return f->video_tracks.at(0).video_preview_square;
|
||||
return QIcon(QPixmap::fromImage(f->video_tracks.at(0).video_preview));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,20 +322,7 @@ QVariant Media::data(int column, int role) {
|
||||
case 0: return (root) ? QCoreApplication::translate("Media", "Name") : get_name();
|
||||
case 1:
|
||||
if (root) return QCoreApplication::translate("Media", "Duration");
|
||||
if (get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
Sequence* s = to_sequence().get();
|
||||
return frame_to_timecode(s->getEndFrame(), olive::CurrentConfig.timecode_view, s->frame_rate);
|
||||
}
|
||||
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* f = to_footage();
|
||||
double r = 30;
|
||||
|
||||
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate))
|
||||
r = f->video_tracks.at(0).video_frame_rate * f->speed;
|
||||
|
||||
long len = f->get_length_in_frames(r);
|
||||
if (len > 0) return frame_to_timecode(len, olive::CurrentConfig.timecode_view, r);
|
||||
}
|
||||
return GetStringDuration();
|
||||
break;
|
||||
case 2:
|
||||
if (root) return QCoreApplication::translate("Media", "Rate");
|
||||
@@ -336,6 +341,10 @@ QVariant Media::data(int column, int role) {
|
||||
break;
|
||||
case Qt::ToolTipRole:
|
||||
return tooltip;
|
||||
|
||||
case Qt::UserRole:
|
||||
// User role returns the duration
|
||||
return GetStringDuration();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ private:
|
||||
int type;
|
||||
VoidPtr object;
|
||||
|
||||
QString GetStringDuration();
|
||||
|
||||
// item functions
|
||||
QList<MediaPtr> children;
|
||||
Media* parent;
|
||||
|
||||
@@ -153,8 +153,6 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
|
||||
QString thumb_path = get_thumbnail_path(hash, ms);
|
||||
QFile f(thumb_path);
|
||||
if (f.exists() && ms.video_preview.load(thumb_path)) {
|
||||
//dout << "loaded thumb" << ms->file_index << "from" << thumb_path;
|
||||
ms.make_square_thumb();
|
||||
ms.preview_done = true;
|
||||
} else {
|
||||
found = false;
|
||||
@@ -364,8 +362,6 @@ void PreviewGenerator::generate_waveform() {
|
||||
&data,
|
||||
linesize);
|
||||
|
||||
s->make_square_thumb();
|
||||
|
||||
// is video interlaced?
|
||||
s->video_auto_interlacing = (temp_frame->interlaced_frame) ? ((temp_frame->top_field_first) ? VIDEO_TOP_FIELD_FIRST : VIDEO_BOTTOM_FIELD_FIRST) : VIDEO_PROGRESSIVE;
|
||||
s->video_interlacing = s->video_auto_interlacing;
|
||||
|
||||
@@ -44,10 +44,10 @@ void ProjectModel::make_root() {
|
||||
|
||||
void ProjectModel::destroy_root() {
|
||||
if (panel_sequence_viewer != nullptr) {
|
||||
panel_sequence_viewer->viewer_widget->delete_function();
|
||||
panel_sequence_viewer->set_media(nullptr);
|
||||
}
|
||||
if (panel_footage_viewer != nullptr) {
|
||||
panel_footage_viewer->viewer_widget->delete_function();
|
||||
panel_footage_viewer->set_media(nullptr);
|
||||
}
|
||||
|
||||
root_item_ = std::make_shared<Media>();
|
||||
|
||||
@@ -47,9 +47,10 @@
|
||||
#include "ui/menu.h"
|
||||
#include "undo/undostack.h"
|
||||
|
||||
SourcesCommon::SourcesCommon(Project* parent) :
|
||||
SourcesCommon::SourcesCommon(Project* parent, ProjectFilter &sort_filter) :
|
||||
editing_item(nullptr),
|
||||
project_parent(parent)
|
||||
project_parent(parent),
|
||||
sort_filter_(sort_filter)
|
||||
{
|
||||
rename_timer.setInterval(1000);
|
||||
connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
|
||||
@@ -57,7 +58,7 @@ SourcesCommon::SourcesCommon(Project* parent) :
|
||||
|
||||
void SourcesCommon::create_seq_from_selected() {
|
||||
if (!selected_items.isEmpty()) {
|
||||
QVector<Media*> media_list;
|
||||
QVector<olive::timeline::MediaImportData> media_list;
|
||||
for (int i=0;i<selected_items.size();i++) {
|
||||
media_list.append(project_parent->item_to_media(selected_items.at(i)));
|
||||
}
|
||||
@@ -97,13 +98,13 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it
|
||||
|
||||
QAction* toolbar_action = view_menu->addAction(tr("Show Toolbar"));
|
||||
toolbar_action->setCheckable(true);
|
||||
toolbar_action->setChecked(project_parent->toolbar_widget->isVisible());
|
||||
connect(toolbar_action, SIGNAL(triggered(bool)), project_parent->toolbar_widget, SLOT(setVisible(bool)));
|
||||
toolbar_action->setChecked(project_parent->IsToolbarVisible());
|
||||
connect(toolbar_action, SIGNAL(triggered(bool)), project_parent, SLOT(SetToolbarVisible(bool)));
|
||||
|
||||
QAction* show_sequences = view_menu->addAction(tr("Show Sequences"));
|
||||
show_sequences->setCheckable(true);
|
||||
show_sequences->setChecked(panel_project->sorter->get_show_sequences());
|
||||
connect(show_sequences, SIGNAL(triggered(bool)), panel_project->sorter, SLOT(set_show_sequences(bool)));
|
||||
show_sequences->setChecked(sort_filter_.get_show_sequences());
|
||||
connect(show_sequences, SIGNAL(triggered(bool)), &sort_filter_, SLOT(set_show_sequences(bool)));
|
||||
|
||||
if (items.size() > 0) {
|
||||
if (items.size() == 1) {
|
||||
@@ -282,7 +283,7 @@ void SourcesCommon::dropEvent(QWidget* parent,
|
||||
bool replace = false;
|
||||
if (urls.size() == 1
|
||||
&& drop_item.isValid()
|
||||
&& (m != nullptr && m->get_type() == MEDIA_TYPE_FOOTAGE)
|
||||
&& m->get_type() == MEDIA_TYPE_FOOTAGE
|
||||
&& !QFileInfo(paths.at(0)).isDir()
|
||||
&& olive::CurrentConfig.drop_on_media_to_replace
|
||||
&& QMessageBox::question(
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QVector>
|
||||
|
||||
#include "project/footage.h"
|
||||
#include "project/projectfilter.h"
|
||||
|
||||
class Project;
|
||||
class QMouseEvent;
|
||||
@@ -36,7 +37,7 @@ class QDropEvent;
|
||||
class SourcesCommon : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SourcesCommon(Project *parent);
|
||||
SourcesCommon(Project *parent, ProjectFilter& sort_filter);
|
||||
QAbstractItemView* view;
|
||||
void show_context_menu(QWidget* parent, const QModelIndexList &items);
|
||||
|
||||
@@ -45,6 +46,8 @@ public:
|
||||
void dropEvent(QWidget *parent, QDropEvent* e, const QModelIndex& drop_item, const QModelIndexList &items);
|
||||
|
||||
void item_click(Media* m, const QModelIndex &index);
|
||||
public slots:
|
||||
void stop_rename_timer();
|
||||
private slots:
|
||||
void create_seq_from_selected();
|
||||
void reveal_in_browser();
|
||||
@@ -61,11 +64,12 @@ private:
|
||||
QModelIndex editing_index;
|
||||
QModelIndexList selected_items;
|
||||
Project* project_parent;
|
||||
void stop_rename_timer();
|
||||
QTimer rename_timer;
|
||||
|
||||
// we cache the selected footage items for open_create_proxy_dialog()
|
||||
QVector<Media*> cached_selected_footage;
|
||||
|
||||
ProjectFilter& sort_filter_;
|
||||
};
|
||||
|
||||
#endif // SOURCESCOMMON_H
|
||||
|
||||
@@ -653,6 +653,7 @@ void Cacher::CacheVideoWorker() {
|
||||
// again, an EOF isn't an "error" but will how we add frames (see below)
|
||||
|
||||
qCritical() << "Failed to retrieve frame from buffersink." << retrieve_code;
|
||||
break;
|
||||
|
||||
} else if (decoded_frame->pts != AV_NOPTS_VALUE) {
|
||||
|
||||
|
||||
+26
-15
@@ -20,17 +20,6 @@
|
||||
|
||||
#include "exportthread.h"
|
||||
|
||||
#include "global/global.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "panels/panels.h"
|
||||
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "rendering/renderthread.h"
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "rendering/audio.h"
|
||||
#include "ui/mainwindow.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/opt.h>
|
||||
@@ -42,6 +31,17 @@ extern "C" {
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLPaintDevice>
|
||||
#include <QPainter>
|
||||
#include <QtMath>
|
||||
|
||||
#include "global/global.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "panels/panels.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "rendering/renderthread.h"
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "rendering/audio.h"
|
||||
#include "ui/mainwindow.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
ExportThread::ExportThread(const ExportParams ¶ms,
|
||||
const VideoCodecParams& vparams,
|
||||
@@ -91,7 +91,18 @@ bool ExportThread::Encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx,
|
||||
}
|
||||
|
||||
packet->stream_index = stream->index;
|
||||
if (rescale) av_packet_rescale_ts(packet, codec_ctx->time_base, stream->time_base);
|
||||
if (rescale) {
|
||||
if (packet->pts != AV_NOPTS_VALUE) {
|
||||
packet->pts = qRound(packet->pts * av_q2d(codec_ctx->time_base) / av_q2d(stream->time_base));
|
||||
}
|
||||
if (packet->dts != AV_NOPTS_VALUE) {
|
||||
packet->dts = qRound(packet->dts * av_q2d(codec_ctx->time_base) / av_q2d(stream->time_base));
|
||||
}
|
||||
if (packet->duration > 0) {
|
||||
packet->duration = qRound(packet->duration * av_q2d(codec_ctx->time_base) / av_q2d(stream->time_base));
|
||||
}
|
||||
//av_packet_rescale_ts(packet, codec_ctx->time_base, stream->time_base);
|
||||
}
|
||||
av_interleaved_write_frame(ofmt_ctx, packet);
|
||||
av_packet_unref(packet);
|
||||
}
|
||||
@@ -468,10 +479,10 @@ void ExportThread::Export()
|
||||
|
||||
// Convert raw RGBA buffer to format expected by the encoder
|
||||
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
|
||||
sws_frame->pts = qRound(timecode_secs/av_q2d(video_stream->time_base));
|
||||
sws_frame->pts = qRound(timecode_secs/av_q2d(vcodec_ctx->time_base));
|
||||
|
||||
// Send frame to encoder
|
||||
if (!Encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream, false)) {
|
||||
if (!Encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -577,7 +588,7 @@ void ExportThread::Export()
|
||||
// Flush remaining packets out of video and audio encoders
|
||||
while (continueVideo && continueAudio) {
|
||||
if (continueVideo) {
|
||||
continueVideo = Encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream, false);
|
||||
continueVideo = Encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream, true);
|
||||
}
|
||||
if (continueAudio) {
|
||||
continueAudio = Encode(fmt_ctx, acodec_ctx, nullptr, &audio_pkt, audio_stream, true);
|
||||
|
||||
@@ -696,8 +696,10 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) {
|
||||
params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, textureID);
|
||||
|
||||
// set texture filter to bilinear
|
||||
params.ctx->functions()->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
params.ctx->functions()->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
//params.ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
|
||||
//params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// draw clip on screen according to gl coordinates
|
||||
params.pipeline->bind();
|
||||
|
||||
@@ -370,6 +370,22 @@ void RenderThread::cancel() {
|
||||
wait();
|
||||
}
|
||||
|
||||
void RenderThread::wait_until_paused()
|
||||
{
|
||||
|
||||
// Wait for thread to finish whatever it's doing before proceeding.
|
||||
//
|
||||
// FIXME: This is slow. Perhaps there's a better way...
|
||||
|
||||
if (wait_lock_.tryLock()) {
|
||||
wait_lock_.unlock();
|
||||
return;
|
||||
} else {
|
||||
wait_lock_.lock();
|
||||
wait_lock_.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderThread::delete_buffers() {
|
||||
composite_buffer.Destroy();
|
||||
front_buffer_1.Destroy();
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
int idivider = 0);
|
||||
bool did_texture_fail();
|
||||
void cancel();
|
||||
|
||||
void wait_until_paused();
|
||||
|
||||
public slots:
|
||||
// cleanup functions
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "mediaimportdata.h"
|
||||
|
||||
olive::timeline::MediaImportData::MediaImportData(Media *media, olive::timeline::MediaImportType import_type) :
|
||||
media_(media),
|
||||
import_type_(import_type)
|
||||
{
|
||||
}
|
||||
|
||||
Media *olive::timeline::MediaImportData::media() const
|
||||
{
|
||||
return media_;
|
||||
}
|
||||
|
||||
olive::timeline::MediaImportType olive::timeline::MediaImportData::type() const
|
||||
{
|
||||
return import_type_;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef MEDIAIMPORTDATA_H
|
||||
#define MEDIAIMPORTDATA_H
|
||||
|
||||
#include "project/media.h"
|
||||
|
||||
namespace olive {
|
||||
namespace timeline {
|
||||
|
||||
enum MediaImportType {
|
||||
kImportVideoOnly,
|
||||
kImportAudioOnly,
|
||||
kImportBoth
|
||||
};
|
||||
|
||||
class MediaImportData {
|
||||
public:
|
||||
MediaImportData(Media* media = nullptr, MediaImportType import_type = kImportBoth);
|
||||
Media* media() const;
|
||||
MediaImportType type() const;
|
||||
private:
|
||||
Media* media_;
|
||||
MediaImportType import_type_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // MEDIAIMPORTDATA_H
|
||||
+3625
File diff suppressed because it is too large
Load Diff
+913
-821
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
#include "columnedgridlayout.h"
|
||||
|
||||
ColumnedGridLayout::ColumnedGridLayout(QWidget* parent,
|
||||
int maximum_columns) :
|
||||
QGridLayout (parent),
|
||||
maximum_columns_(maximum_columns)
|
||||
{
|
||||
}
|
||||
|
||||
void ColumnedGridLayout::Add(QWidget *widget)
|
||||
{
|
||||
if (maximum_columns_ > 0) {
|
||||
|
||||
int row = count() / maximum_columns_;
|
||||
int column = count() % maximum_columns_;
|
||||
|
||||
addWidget(widget, row, column);
|
||||
|
||||
} else {
|
||||
|
||||
addWidget(widget);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int ColumnedGridLayout::MaximumColumns() const
|
||||
{
|
||||
return maximum_columns_;
|
||||
}
|
||||
|
||||
void ColumnedGridLayout::SetMaximumColumns(int maximum_columns)
|
||||
{
|
||||
maximum_columns_ = maximum_columns;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef COLUMNEDGRIDLAYOUT_H
|
||||
#define COLUMNEDGRIDLAYOUT_H
|
||||
|
||||
#include <QGridLayout>
|
||||
|
||||
/**
|
||||
* @brief The ColumnedGridLayout class
|
||||
*
|
||||
* A simple derivative of QGridLayout that provides a automatic row/column layout based on a specified maximum
|
||||
* column count.
|
||||
*/
|
||||
class ColumnedGridLayout : public QGridLayout
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColumnedGridLayout(QWidget* parent = nullptr,
|
||||
int maximum_columns = 0);
|
||||
|
||||
void Add(QWidget* widget);
|
||||
int MaximumColumns() const;
|
||||
void SetMaximumColumns(int maximum_columns);
|
||||
|
||||
private:
|
||||
int maximum_columns_;
|
||||
};
|
||||
|
||||
#endif // COLUMNEDGRIDLAYOUT_H
|
||||
+2
-2
@@ -96,8 +96,8 @@ void olive::icon::Initialize()
|
||||
Diamond = CreateIconFromSVG(":/icons/diamond.svg", false);
|
||||
Clock = CreateIconFromSVG(":/icons/clock.svg", false);
|
||||
|
||||
MediaVideo = CreateIconFromSVG(":/icons/videosource.svg", false);
|
||||
MediaAudio = CreateIconFromSVG(":/icons/audiosource.svg", false);
|
||||
MediaVideo = CreateIconFromSVG(":/icons/videosource.svg");
|
||||
MediaAudio = CreateIconFromSVG(":/icons/audiosource.svg");
|
||||
MediaImage = CreateIconFromSVG(":/icons/imagesource.svg", false);
|
||||
MediaError = CreateIconFromSVG(":/icons/error.svg", false);
|
||||
MediaSequence = CreateIconFromSVG(":/icons/sequence.svg", false);
|
||||
|
||||
+17
-92
@@ -358,7 +358,6 @@ void kbd_shortcut_processor(QByteArray& file, QMenu* menu, bool save, bool first
|
||||
}
|
||||
}
|
||||
}
|
||||
a->setShortcutContext(Qt::ApplicationShortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,16 +407,14 @@ bool MainWindow::load_css_from_file(const QString &fn) {
|
||||
void MainWindow::Restyle()
|
||||
{
|
||||
// Set up UI style
|
||||
if (olive::styling::UseNativeUI()) {
|
||||
qApp->setStyle(QStyleFactory::create(""));
|
||||
} else {
|
||||
if (!olive::styling::UseNativeUI()) {
|
||||
qApp->setStyle(QStyleFactory::create("Fusion"));
|
||||
|
||||
// Set up whether to load custom CSS or default CSS+palette
|
||||
if (!olive::CurrentConfig.css_path.isEmpty()
|
||||
&& load_css_from_file(olive::CurrentConfig.css_path)) {
|
||||
|
||||
setPalette(QPalette());
|
||||
qApp->setPalette(qApp->style()->standardPalette());
|
||||
|
||||
} else {
|
||||
|
||||
@@ -476,7 +473,17 @@ void MainWindow::Restyle()
|
||||
palette.setColor(QPalette::HighlightedText, Qt::white);
|
||||
|
||||
// set default CSS
|
||||
setStyleSheet("QPushButton::checked { background: rgb(25, 25, 25); }");
|
||||
QString stylesheet = "QPushButton::checked { background: rgb(25, 25, 25); }";
|
||||
|
||||
// Windows menus have the option of being native, so we may not need this CSS
|
||||
#ifdef Q_OS_WIN
|
||||
if (!olive::CurrentConfig.use_native_menu_styling) {
|
||||
#endif
|
||||
stylesheet.append("QMenu::separator { background: #404040; }");
|
||||
#ifdef Q_OS_WIN
|
||||
}
|
||||
#endif
|
||||
setStyleSheet(stylesheet);
|
||||
|
||||
}
|
||||
|
||||
@@ -779,61 +786,7 @@ void MainWindow::setup_menus() {
|
||||
|
||||
tools_menu->addSeparator();
|
||||
|
||||
selecting_also_seeks = MenuHelper::create_menu_action(tools_menu, "selectingalsoseeks", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
selecting_also_seeks->setCheckable(true);
|
||||
selecting_also_seeks->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.select_also_seeks));
|
||||
|
||||
edit_tool_also_seeks = MenuHelper::create_menu_action(tools_menu, "editalsoseeks", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
edit_tool_also_seeks->setCheckable(true);
|
||||
edit_tool_also_seeks->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.edit_tool_also_seeks));
|
||||
|
||||
edit_tool_selects_links = MenuHelper::create_menu_action(tools_menu, "editselectslinks", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
edit_tool_selects_links->setCheckable(true);
|
||||
edit_tool_selects_links->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.edit_tool_selects_links));
|
||||
|
||||
seek_also_selects = MenuHelper::create_menu_action(tools_menu, "seekalsoselects", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
seek_also_selects->setCheckable(true);
|
||||
seek_also_selects->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.seek_also_selects));
|
||||
|
||||
seek_to_end_of_pastes = MenuHelper::create_menu_action(tools_menu, "seektoendofpastes", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
seek_to_end_of_pastes->setCheckable(true);
|
||||
seek_to_end_of_pastes->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.paste_seeks));
|
||||
|
||||
scroll_wheel_zooms = MenuHelper::create_menu_action(tools_menu, "scrollwheelzooms", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
scroll_wheel_zooms->setCheckable(true);
|
||||
scroll_wheel_zooms->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.scroll_zooms));
|
||||
|
||||
invert_timeline_scroll_axes = MenuHelper::create_menu_action(tools_menu, "inverttimelinescrollaxes", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
invert_timeline_scroll_axes->setCheckable(true);
|
||||
invert_timeline_scroll_axes->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.invert_timeline_scroll_axes));
|
||||
|
||||
enable_drag_files_to_timeline = MenuHelper::create_menu_action(tools_menu, "enabledragfilestotimeline", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
enable_drag_files_to_timeline->setCheckable(true);
|
||||
enable_drag_files_to_timeline->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.enable_drag_files_to_timeline));
|
||||
|
||||
autoscale_by_default = MenuHelper::create_menu_action(tools_menu, "autoscalebydefault", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
autoscale_by_default->setCheckable(true);
|
||||
autoscale_by_default->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.autoscale_by_default));
|
||||
|
||||
enable_seek_to_import = MenuHelper::create_menu_action(tools_menu, "enableseektoimport", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
enable_seek_to_import->setCheckable(true);
|
||||
enable_seek_to_import->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.enable_seek_to_import));
|
||||
|
||||
enable_audio_scrubbing = MenuHelper::create_menu_action(tools_menu, "audioscrubbing", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
enable_audio_scrubbing->setCheckable(true);
|
||||
enable_audio_scrubbing->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.enable_audio_scrubbing));
|
||||
|
||||
enable_drop_on_media_to_replace = MenuHelper::create_menu_action(tools_menu, "enabledropmediareplace", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
enable_drop_on_media_to_replace->setCheckable(true);
|
||||
enable_drop_on_media_to_replace->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.drop_on_media_to_replace));
|
||||
|
||||
enable_hover_focus = MenuHelper::create_menu_action(tools_menu, "hoverfocus", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
enable_hover_focus->setCheckable(true);
|
||||
enable_hover_focus->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.hover_focus));
|
||||
|
||||
set_name_and_marker = MenuHelper::create_menu_action(tools_menu, "asknamemarkerset", &olive::MenuHelper, SLOT(toggle_bool_action()));
|
||||
set_name_and_marker->setCheckable(true);
|
||||
set_name_and_marker->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.set_name_with_marker));
|
||||
autocut_silence_ = MenuHelper::create_menu_action(tools_menu, "autocutsilence", olive::Global.get(), SLOT(open_autocut_silence_dialog()));
|
||||
|
||||
tools_menu->addSeparator();
|
||||
|
||||
@@ -971,21 +924,7 @@ void MainWindow::Retranslate()
|
||||
hand_tool_action->setText(tr("Hand Tool"));
|
||||
transition_tool_action->setText(tr("Transition Tool"));
|
||||
snap_toggle->setText(tr("Enable Snapping"));
|
||||
selecting_also_seeks->setText(tr("Selecting Also Seeks"));
|
||||
edit_tool_also_seeks->setText(tr("Edit Tool Also Seeks"));
|
||||
edit_tool_selects_links->setText(tr("Edit Tool Selects Links"));
|
||||
seek_also_selects->setText(tr("Seek Also Selects"));
|
||||
seek_to_end_of_pastes->setText(tr("Seek to the End of Pastes"));
|
||||
scroll_wheel_zooms->setText(tr("Scroll Wheel Zooms"));
|
||||
scroll_wheel_zooms->setToolTip(tr("Hold CTRL to toggle this setting"));
|
||||
invert_timeline_scroll_axes->setText(tr("Invert Timeline Scroll Axes"));
|
||||
enable_drag_files_to_timeline->setText(tr("Enable Drag Files to Timeline"));
|
||||
autoscale_by_default->setText(tr("Auto-Scale By Default"));
|
||||
enable_seek_to_import->setText(tr("Enable Seek to Import"));
|
||||
enable_audio_scrubbing->setText(tr("Audio Scrubbing"));
|
||||
enable_drop_on_media_to_replace->setText(tr("Enable Drop on Media to Replace"));
|
||||
enable_hover_focus->setText(tr("Enable Hover Focus"));
|
||||
set_name_and_marker->setText(tr("Ask For Name When Setting Marker"));
|
||||
autocut_silence_->setText(tr("Auto-Cut Silence"));
|
||||
|
||||
no_autoscroll->setText(tr("No Auto-Scroll"));
|
||||
page_autoscroll->setText(tr("Page Auto-Scroll"));
|
||||
@@ -1194,6 +1133,8 @@ void MainWindow::playbackMenu_About_To_Be_Shown() {
|
||||
void MainWindow::viewMenu_About_To_Be_Shown() {
|
||||
olive::MenuHelper.set_bool_action_checked(track_lines);
|
||||
|
||||
olive::MenuHelper.set_bool_action_checked(rectified_waveforms);
|
||||
|
||||
olive::MenuHelper.set_int_action_checked(frames_action, olive::CurrentConfig.timecode_view);
|
||||
olive::MenuHelper.set_int_action_checked(drop_frame_action, olive::CurrentConfig.timecode_view);
|
||||
olive::MenuHelper.set_int_action_checked(nondrop_frame_action, olive::CurrentConfig.timecode_view);
|
||||
@@ -1229,22 +1170,6 @@ void MainWindow::toolMenu_About_To_Be_Shown() {
|
||||
olive::MenuHelper.set_button_action_checked(transition_tool_action);
|
||||
olive::MenuHelper.set_button_action_checked(snap_toggle);
|
||||
|
||||
olive::MenuHelper.set_bool_action_checked(selecting_also_seeks);
|
||||
olive::MenuHelper.set_bool_action_checked(edit_tool_also_seeks);
|
||||
olive::MenuHelper.set_bool_action_checked(edit_tool_selects_links);
|
||||
olive::MenuHelper.set_bool_action_checked(seek_to_end_of_pastes);
|
||||
olive::MenuHelper.set_bool_action_checked(scroll_wheel_zooms);
|
||||
olive::MenuHelper.set_bool_action_checked(invert_timeline_scroll_axes);
|
||||
olive::MenuHelper.set_bool_action_checked(rectified_waveforms);
|
||||
olive::MenuHelper.set_bool_action_checked(enable_drag_files_to_timeline);
|
||||
olive::MenuHelper.set_bool_action_checked(autoscale_by_default);
|
||||
olive::MenuHelper.set_bool_action_checked(enable_seek_to_import);
|
||||
olive::MenuHelper.set_bool_action_checked(enable_audio_scrubbing);
|
||||
olive::MenuHelper.set_bool_action_checked(enable_drop_on_media_to_replace);
|
||||
olive::MenuHelper.set_bool_action_checked(enable_hover_focus);
|
||||
olive::MenuHelper.set_bool_action_checked(set_name_and_marker);
|
||||
olive::MenuHelper.set_bool_action_checked(seek_also_selects);
|
||||
|
||||
olive::MenuHelper.set_int_action_checked(no_autoscroll, olive::CurrentConfig.autoscroll);
|
||||
olive::MenuHelper.set_int_action_checked(page_autoscroll, olive::CurrentConfig.autoscroll);
|
||||
olive::MenuHelper.set_int_action_checked(smooth_autoscroll, olive::CurrentConfig.autoscroll);
|
||||
|
||||
+1
-14
@@ -328,21 +328,8 @@ private:
|
||||
QAction* hand_tool_action;
|
||||
QAction* transition_tool_action;
|
||||
QAction* snap_toggle;
|
||||
QAction* selecting_also_seeks;
|
||||
QAction* edit_tool_also_seeks;
|
||||
QAction* edit_tool_selects_links;
|
||||
QAction* seek_to_end_of_pastes;
|
||||
QAction* scroll_wheel_zooms;
|
||||
QAction* invert_timeline_scroll_axes;
|
||||
QAction* rectified_waveforms;
|
||||
QAction* enable_drag_files_to_timeline;
|
||||
QAction* autoscale_by_default;
|
||||
QAction* enable_seek_to_import;
|
||||
QAction* enable_audio_scrubbing;
|
||||
QAction* enable_drop_on_media_to_replace;
|
||||
QAction* enable_hover_focus;
|
||||
QAction* set_name_and_marker;
|
||||
QAction* seek_also_selects;
|
||||
QAction* autocut_silence_;
|
||||
QAction* preferences_action_;
|
||||
QAction* clear_undo_action_;
|
||||
|
||||
|
||||
+135
-9
@@ -21,32 +21,38 @@
|
||||
#include "sourceiconview.h"
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QImage>
|
||||
|
||||
#include "panels/project.h"
|
||||
#include "project/media.h"
|
||||
#include "project/sourcescommon.h"
|
||||
#include "global/debug.h"
|
||||
#include "global/math.h"
|
||||
|
||||
SourceIconView::SourceIconView(QWidget *parent) : QListView(parent) {
|
||||
SourceIconView::SourceIconView(SourcesCommon &commons) :
|
||||
commons_(commons)
|
||||
{
|
||||
setMovement(QListView::Free);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
setResizeMode(QListView::Adjust);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
setItemDelegate(&delegate_);
|
||||
connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(item_click(const QModelIndex&)));
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu()));
|
||||
}
|
||||
|
||||
void SourceIconView::show_context_menu() {
|
||||
project_parent->sources_common->show_context_menu(this, selectedIndexes());
|
||||
commons_.show_context_menu(this, selectedIndexes());
|
||||
}
|
||||
|
||||
void SourceIconView::item_click(const QModelIndex& index) {
|
||||
if (selectedIndexes().size() == 1 && index.column() == 0) {
|
||||
project_parent->sources_common->item_click(project_parent->item_to_media(index), index);
|
||||
commons_.item_click(project_parent->item_to_media(index), index);
|
||||
}
|
||||
}
|
||||
|
||||
void SourceIconView::mousePressEvent(QMouseEvent* event) {
|
||||
project_parent->sources_common->mousePressEvent(event);
|
||||
commons_.mousePressEvent(event);
|
||||
if (!indexAt(event->pos()).isValid()) selectionModel()->clear();
|
||||
QListView::mousePressEvent(event);
|
||||
}
|
||||
@@ -70,20 +76,140 @@ void SourceIconView::dragMoveEvent(QDragMoveEvent *event) {
|
||||
void SourceIconView::dropEvent(QDropEvent* event) {
|
||||
QModelIndex drop_item = indexAt(event->pos());
|
||||
if (!drop_item.isValid()) drop_item = rootIndex();
|
||||
project_parent->sources_common->dropEvent(this, event, drop_item, selectedIndexes());
|
||||
commons_.dropEvent(this, event, drop_item, selectedIndexes());
|
||||
}
|
||||
|
||||
void SourceIconView::mouseDoubleClickEvent(QMouseEvent *) {
|
||||
bool default_behavior = true;
|
||||
if (selectedIndexes().size() == 1) {
|
||||
Media* m = project_parent->item_to_media(selectedIndexes().at(0));
|
||||
if (m->get_type() == MEDIA_TYPE_FOLDER) {
|
||||
default_behavior = false;
|
||||
setRootIndex(selectedIndexes().at(0));
|
||||
emit changed_root();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (default_behavior) {
|
||||
project_parent->sources_common->mouseDoubleClickEvent(selectedIndexes());
|
||||
|
||||
// Double click was not a folder, so we perform the default behavior (sending the double click to SourcesCommon)
|
||||
commons_.mouseDoubleClickEvent(selectedIndexes());
|
||||
}
|
||||
|
||||
SourceIconDelegate::SourceIconDelegate(QObject *parent) :
|
||||
QStyledItemDelegate (parent)
|
||||
{
|
||||
}
|
||||
|
||||
QSize SourceIconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
if (option.decorationPosition == QStyleOptionViewItem::Top) { // Icon Mode
|
||||
|
||||
return QSize(256, 256);
|
||||
|
||||
} else {
|
||||
|
||||
return QSize(option.decorationSize.height(), option.decorationSize.height());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void SourceIconDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QFontMetrics fm = painter->fontMetrics();
|
||||
QRect img_rect = option.rect;
|
||||
|
||||
if (option.decorationPosition == QStyleOptionViewItem::Top) { // Icon Mode
|
||||
|
||||
// Draw Text
|
||||
if (fm.height() < option.rect.height() / 2) {
|
||||
img_rect.setHeight(img_rect.height()-fm.height());
|
||||
|
||||
QRect text_rect = option.rect;
|
||||
text_rect.setTop(text_rect.top() + option.rect.height() - fm.height());
|
||||
|
||||
QColor text_bgcolor;
|
||||
QColor text_fgcolor;
|
||||
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
text_bgcolor = option.palette.highlight().color();
|
||||
text_fgcolor = option.palette.highlightedText().color();
|
||||
} else {
|
||||
text_bgcolor = Qt::white;
|
||||
text_fgcolor = Qt::black;
|
||||
}
|
||||
|
||||
painter->fillRect(text_rect, text_bgcolor);
|
||||
painter->setPen(text_fgcolor);
|
||||
|
||||
QString duration_str = index.data(Qt::UserRole).toString();
|
||||
int timecode_width = fm.width(duration_str);
|
||||
int max_name_width = option.rect.width();
|
||||
|
||||
if (timecode_width < option.rect.width() / 2) {
|
||||
painter->drawText(text_rect, Qt::AlignBottom | Qt::AlignRight, index.data(Qt::UserRole).toString());
|
||||
max_name_width -= timecode_width;
|
||||
}
|
||||
|
||||
painter->drawText(text_rect,
|
||||
Qt::AlignBottom | Qt::AlignLeft,
|
||||
fm.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, max_name_width));
|
||||
|
||||
}
|
||||
|
||||
// Draw image
|
||||
QIcon ico = index.data(Qt::DecorationRole).value<QIcon>();
|
||||
QSize icon_size = ico.actualSize(img_rect.size());
|
||||
img_rect = QRect(img_rect.x() + (img_rect.width() / 2 - icon_size.width() / 2),
|
||||
img_rect.y() + (img_rect.height() / 2 - icon_size.height() / 2),
|
||||
icon_size.width(),
|
||||
icon_size.height());
|
||||
painter->drawPixmap(img_rect, ico.pixmap(icon_size));
|
||||
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
QColor highlight_color = option.palette.highlight().color();
|
||||
highlight_color.setAlphaF(0.5);
|
||||
|
||||
painter->setCompositionMode(QPainter::CompositionMode_SourceAtop);
|
||||
painter->fillRect(img_rect, highlight_color);
|
||||
}
|
||||
} else if (option.decorationPosition == QStyleOptionViewItem::Left) { // List Mode
|
||||
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
painter->fillRect(option.rect, option.palette.highlight());
|
||||
}
|
||||
|
||||
img_rect.setWidth(qMin(img_rect.width(), img_rect.height()));
|
||||
|
||||
QIcon ico = index.data(Qt::DecorationRole).value<QIcon>();
|
||||
QSize icon_size = ico.actualSize(img_rect.size());
|
||||
img_rect = QRect(img_rect.x() + (img_rect.width() / 2 - icon_size.width() / 2),
|
||||
img_rect.y() + (img_rect.height() / 2 - icon_size.height() / 2),
|
||||
icon_size.width(),
|
||||
icon_size.height());
|
||||
painter->drawPixmap(img_rect, ico.pixmap(icon_size));
|
||||
|
||||
QRect text_rect = option.rect;
|
||||
text_rect.setLeft(text_rect.left() + option.rect.height());
|
||||
|
||||
int maximum_line_count = qMax(1, option.rect.height() / fm.height() - 1);
|
||||
QString text;
|
||||
if (maximum_line_count == 1) {
|
||||
text = index.data(Qt::DisplayRole).toString();
|
||||
} else {
|
||||
text = index.data(Qt::ToolTipRole).toString();
|
||||
if (text.isEmpty()) {
|
||||
text = index.data(Qt::DisplayRole).toString();
|
||||
} else {
|
||||
QStringList strings = text.split("\n");
|
||||
while (strings.size() > maximum_line_count) {
|
||||
strings.removeLast();
|
||||
}
|
||||
text = strings.join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
painter->setPen(option.state & QStyle::State_Selected ?
|
||||
option.palette.highlightedText().color() : option.palette.text().color());
|
||||
|
||||
painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, text);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+26
-11
@@ -22,25 +22,40 @@
|
||||
#define SOURCEICONVIEW_H
|
||||
|
||||
#include <QListView>
|
||||
#include <QDropEvent>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#include "project/sourcescommon.h"
|
||||
|
||||
class Project;
|
||||
class SourceIconDelegate;
|
||||
|
||||
class SourceIconDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
SourceIconDelegate(QObject *parent = nullptr);
|
||||
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
class SourceIconView : public QListView {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
SourceIconView(QWidget* parent = 0);
|
||||
Project* project_parent;
|
||||
SourceIconView(SourcesCommon& commons);
|
||||
Project* project_parent;
|
||||
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseDoubleClickEvent(QMouseEvent *event);
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void dragMoveEvent(QDragMoveEvent *event);
|
||||
void dropEvent(QDropEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseDoubleClickEvent(QMouseEvent *event);
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void dragMoveEvent(QDragMoveEvent *event);
|
||||
void dropEvent(QDropEvent* event);
|
||||
signals:
|
||||
void changed_root();
|
||||
void changed_root();
|
||||
private slots:
|
||||
void show_context_menu();
|
||||
void item_click(const QModelIndex& index);
|
||||
void show_context_menu();
|
||||
void item_click(const QModelIndex& index);
|
||||
private:
|
||||
SourcesCommon& commons_;
|
||||
SourceIconDelegate delegate_;
|
||||
};
|
||||
|
||||
#endif // SOURCEICONVIEW_H
|
||||
|
||||
+6
-6
@@ -45,7 +45,7 @@
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
|
||||
SourceTable::SourceTable(QWidget* parent) : QTreeView(parent) {
|
||||
SourceTable::SourceTable(SourcesCommon& commons) : commons_(commons) {
|
||||
setSortingEnabled(true);
|
||||
setAcceptDrops(true);
|
||||
sortByColumn(0, Qt::AscendingOrder);
|
||||
@@ -58,22 +58,22 @@ SourceTable::SourceTable(QWidget* parent) : QTreeView(parent) {
|
||||
}
|
||||
|
||||
void SourceTable::show_context_menu() {
|
||||
project_parent->sources_common->show_context_menu(this, selectionModel()->selectedRows());
|
||||
commons_.show_context_menu(this, selectionModel()->selectedRows());
|
||||
}
|
||||
|
||||
void SourceTable::item_click(const QModelIndex& index) {
|
||||
if (selectionModel()->selectedRows().size() == 1 && index.column() == 0) {
|
||||
project_parent->sources_common->item_click(project_parent->item_to_media(index), index);
|
||||
commons_.item_click(project_parent->item_to_media(index), index);
|
||||
}
|
||||
}
|
||||
|
||||
void SourceTable::mousePressEvent(QMouseEvent* event) {
|
||||
project_parent->sources_common->mousePressEvent(event);
|
||||
commons_.mousePressEvent(event);
|
||||
QTreeView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void SourceTable::mouseDoubleClickEvent(QMouseEvent* ) {
|
||||
project_parent->sources_common->mouseDoubleClickEvent(selectionModel()->selectedRows());
|
||||
commons_.mouseDoubleClickEvent(selectionModel()->selectedRows());
|
||||
}
|
||||
|
||||
void SourceTable::dragEnterEvent(QDragEnterEvent *event) {
|
||||
@@ -93,5 +93,5 @@ void SourceTable::dragMoveEvent(QDragMoveEvent *event) {
|
||||
}
|
||||
|
||||
void SourceTable::dropEvent(QDropEvent* event) {
|
||||
project_parent->sources_common->dropEvent(this, event, indexAt(event->pos()), selectionModel()->selectedRows());
|
||||
commons_.dropEvent(this, event, indexAt(event->pos()), selectionModel()->selectedRows());
|
||||
}
|
||||
|
||||
+5
-1
@@ -25,6 +25,8 @@
|
||||
#include <QTimer>
|
||||
#include <QUndoCommand>
|
||||
|
||||
#include "project/sourcescommon.h"
|
||||
|
||||
class Project;
|
||||
class Media;
|
||||
|
||||
@@ -32,7 +34,7 @@ class SourceTable : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SourceTable(QWidget* parent = 0);
|
||||
SourceTable(SourcesCommon& commons);
|
||||
Project* project_parent;
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent*);
|
||||
@@ -43,6 +45,8 @@ protected:
|
||||
private slots:
|
||||
void item_click(const QModelIndex& index);
|
||||
void show_context_menu();
|
||||
private:
|
||||
SourcesCommon& commons_;
|
||||
};
|
||||
|
||||
#endif // SOURCETABLE_H
|
||||
|
||||
+23
-5
@@ -118,11 +118,26 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
|
||||
|
||||
if (!selected_clips.isEmpty()) {
|
||||
|
||||
bool video_clips_are_selected = false;
|
||||
bool audio_clips_are_selected = false;
|
||||
|
||||
for (int i=0;i<selected_clips.size();i++) {
|
||||
if (selected_clips.at(i)->track() < 0) {
|
||||
video_clips_are_selected = true;
|
||||
} else {
|
||||
audio_clips_are_selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addAction(tr("&Speed/Duration"), olive::Global.get(), SLOT(open_speed_dialog()));
|
||||
|
||||
QAction* autoscaleAction = menu.addAction(tr("Auto-s&cale"), this, SLOT(toggle_autoscale()));
|
||||
if (audio_clips_are_selected) {
|
||||
menu.addAction(tr("Auto-Cut Silence"), olive::Global.get(), SLOT(open_autocut_silence_dialog()));
|
||||
}
|
||||
|
||||
QAction* autoscaleAction = menu.addAction(tr("Auto-S&cale"), this, SLOT(toggle_autoscale()));
|
||||
autoscaleAction->setCheckable(true);
|
||||
// set autoscale to the first selected clip
|
||||
autoscaleAction->setChecked(selected_clips.at(0)->autoscaled());
|
||||
@@ -237,10 +252,10 @@ bool same_sign(int a, int b) {
|
||||
void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
bool import_init = false;
|
||||
|
||||
QVector<Media*> media_list;
|
||||
QVector<olive::timeline::MediaImportData> media_list;
|
||||
panel_timeline->importing_files = false;
|
||||
|
||||
if (event->source() == panel_project->tree_view || event->source() == panel_project->icon_view) {
|
||||
if (panel_project->IsProjectWidget(event->source())) {
|
||||
QModelIndexList items = panel_project->get_current_selected();
|
||||
media_list.resize(items.size());
|
||||
for (int i=0;i<items.size();i++) {
|
||||
@@ -249,10 +264,13 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
import_init = true;
|
||||
}
|
||||
|
||||
if (event->source() == panel_footage_viewer->viewer_widget) {
|
||||
if (event->source() == panel_footage_viewer) {
|
||||
if (panel_footage_viewer->seq != olive::ActiveSequence) { // don't allow nesting the same sequence
|
||||
media_list.append(panel_footage_viewer->media);
|
||||
|
||||
media_list.append(olive::timeline::MediaImportData(panel_footage_viewer->media,
|
||||
static_cast<olive::timeline::MediaImportType>(event->mimeData()->text().toInt())));
|
||||
import_init = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-9
@@ -90,10 +90,6 @@ ViewerWidget::~ViewerWidget() {
|
||||
renderer.cancel();
|
||||
}
|
||||
|
||||
void ViewerWidget::delete_function() {
|
||||
close_active_clips(viewer->seq.get());
|
||||
}
|
||||
|
||||
void ViewerWidget::set_waveform_scroll(int s) {
|
||||
if (waveform) {
|
||||
waveform_scroll = s;
|
||||
@@ -383,11 +379,7 @@ void ViewerWidget::mouseMoveEvent(QMouseEvent* event) {
|
||||
container->dragScrollMove(event->pos()*container->zoom);
|
||||
} else if (event->buttons() & Qt::LeftButton) {
|
||||
if (gizmos == nullptr) {
|
||||
QDrag* drag = new QDrag(this);
|
||||
QMimeData* mimeData = new QMimeData;
|
||||
mimeData->setText("h"); // QMimeData will fail without some kind of data
|
||||
drag->setMimeData(mimeData);
|
||||
drag->exec();
|
||||
viewer->initiate_drag(olive::timeline::kImportBoth);
|
||||
dragging = false;
|
||||
} else {
|
||||
move_gizmos(event, false);
|
||||
@@ -421,6 +413,11 @@ void ViewerWidget::close_window() {
|
||||
window->hide();
|
||||
}
|
||||
|
||||
void ViewerWidget::wait_until_render_is_paused()
|
||||
{
|
||||
renderer.wait_until_paused();
|
||||
}
|
||||
|
||||
void ViewerWidget::draw_waveform_func() {
|
||||
QPainter p(this);
|
||||
if (viewer->seq->using_workarea) {
|
||||
|
||||
+1
-1
@@ -50,8 +50,8 @@ public:
|
||||
ViewerWidget(QWidget *parent = nullptr);
|
||||
~ViewerWidget();
|
||||
|
||||
void delete_function();
|
||||
void close_window();
|
||||
void wait_until_render_is_paused();
|
||||
|
||||
void paintGL();
|
||||
void initializeGL();
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <QDebug>
|
||||
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "ui/mainwindow.h"
|
||||
|
||||
ViewerWindow::ViewerWindow(QWidget *parent) :
|
||||
QOpenGLWidget(parent, Qt::Window),
|
||||
@@ -51,6 +52,41 @@ void ViewerWindow::set_texture(GLuint t, double iar, QMutex* imutex) {
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerWindow::shortcut_copier(QVector<QShortcut*>& shortcuts, QMenu* menu) {
|
||||
QList<QAction*> menu_action = menu->actions();
|
||||
for (int i=0;i<menu_action.size();i++) {
|
||||
if (menu_action.at(i)->menu() != nullptr) {
|
||||
shortcut_copier(shortcuts, menu_action.at(i)->menu());
|
||||
} else if (!menu_action.at(i)->isSeparator() && !menu_action.at(i)->shortcut().isEmpty()) {
|
||||
QShortcut* sc = new QShortcut(this);
|
||||
sc->setKey(menu_action.at(i)->shortcut());
|
||||
connect(sc, SIGNAL(activated()), menu_action.at(i), SLOT(trigger()));
|
||||
shortcuts.append(sc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWindow::showEvent(QShowEvent *)
|
||||
{
|
||||
// Here, we copy all shortcuts from the MainWindow to this window. I don't like this solution, but messing around
|
||||
// with Qt's event system proved fruitless. Also setting the shortcuts to ApplicationShortcut rather than
|
||||
// WindowShortcut caused issues elsewhere (shortcuts being picked up in comboboxes and dialog boxes - we only
|
||||
// want the shortcuts to be shared to this window). Therefore, this and shortcut_copier() are so far the best
|
||||
// solutions I can find.
|
||||
|
||||
// Clear any existing shortcuts in case they've changed since the last showing
|
||||
for (int i=0;i<shortcuts_.size();i++) {
|
||||
delete shortcuts_.at(i);
|
||||
}
|
||||
shortcuts_.clear();
|
||||
|
||||
// Recursively copy all shortcuts from MainWindow to this window
|
||||
QList<QAction*> menubar_actions = olive::MainWindow->menuBar()->actions();
|
||||
for (int i=0;i<menubar_actions.size();i++) {
|
||||
shortcut_copier(shortcuts_, menubar_actions.at(i)->menu());
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWindow::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Escape) {
|
||||
hide();
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <QOpenGLWidget>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QMenu>
|
||||
|
||||
#include "rendering/qopenglshaderprogramptr.h"
|
||||
|
||||
@@ -33,6 +34,7 @@ public:
|
||||
ViewerWindow(QWidget *parent);
|
||||
void set_texture(GLuint t, double iar, QMutex *imutex);
|
||||
protected:
|
||||
virtual void showEvent(QShowEvent*) override;
|
||||
virtual void keyPressEvent(QKeyEvent*) override;
|
||||
virtual void mousePressEvent(QMouseEvent*) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent*) override;
|
||||
@@ -45,6 +47,10 @@ private:
|
||||
QMutex* mutex_;
|
||||
QOpenGLShaderProgramPtr pipeline_;
|
||||
|
||||
// shortcuts
|
||||
void shortcut_copier(QVector<QShortcut*>& shortcuts, QMenu* menu);
|
||||
QVector<QShortcut*> shortcuts_;
|
||||
|
||||
// exit full screen message
|
||||
QTimer fullscreen_msg_timer_;
|
||||
bool show_fullscreen_msg_;
|
||||
|
||||
Reference in New Issue
Block a user