merged with master

This commit is contained in:
itsmattkc
2019-03-25 12:43:23 +11:00
83 changed files with 7823 additions and 1469 deletions
+2 -1
View File
@@ -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
{
+1 -1
View File
@@ -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
{
+1 -1
View File
@@ -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
+222
View File
@@ -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);
}
}
}
+59
View File
@@ -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
+2 -1
View File
@@ -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
+5 -1
View File
@@ -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;
}
+4 -1
View File
@@ -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
{
+3
View File
@@ -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());
+3 -1
View File
@@ -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
{
+3 -1
View File
@@ -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
{
+2 -1
View File
@@ -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
+44 -10
View File
@@ -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);
+30 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+2 -1
View File
@@ -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
+12 -12
View File
@@ -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();
}
}
+42 -3
View File
@@ -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;
};
+7 -7
View File
@@ -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
View File
@@ -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
+9 -33
View File
@@ -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
View File
@@ -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;
};