diff --git a/dialogs/aboutdialog.h b/dialogs/aboutdialog.h
index d895e0f8e..d50a1b751 100644
--- a/dialogs/aboutdialog.h
+++ b/dialogs/aboutdialog.h
@@ -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
{
diff --git a/dialogs/actionsearch.h b/dialogs/actionsearch.h
index 3e785b869..af82320ad 100644
--- a/dialogs/actionsearch.h
+++ b/dialogs/actionsearch.h
@@ -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
{
diff --git a/dialogs/advancedvideodialog.h b/dialogs/advancedvideodialog.h
index 122812f56..22f607c19 100644
--- a/dialogs/advancedvideodialog.h
+++ b/dialogs/advancedvideodialog.h
@@ -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
diff --git a/dialogs/autocutsilencedialog.cpp b/dialogs/autocutsilencedialog.cpp
new file mode 100644
index 000000000..b1d801b99
--- /dev/null
+++ b/dialogs/autocutsilencedialog.cpp
@@ -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 .
+
+***/
+
+#include "autocutsilencedialog.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "timeline/sequence.h"
+#include "rendering/renderfunctions.h"
+#include "panels/panels.h"
+#include "panels/timeline.h"
+
+AutoCutSilenceDialog::AutoCutSilenceDialog(QWidget *parent, QVector 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;jtrack() >= 0
+ && clip->media() != nullptr
+ && clip->media_stream()->preview_done) { // TODO provide warning for preview not being done
+
+ QVector 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 vols;
+ vols.resize(sample_size);
+ vols.fill(0);
+
+ // loop through the entire sequence
+ for (long i=clip_start;iaudio_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 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: "<= current_release_time){ // must be <= sample_size
+ attack = false;
+ release = true;
+ split_positions.append(i);
+ //qInfo() << "\n\n Current vol: "<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);
+ }
+
+
+ }
+}
diff --git a/dialogs/autocutsilencedialog.h b/dialogs/autocutsilencedialog.h
new file mode 100644
index 000000000..32c572e16
--- /dev/null
+++ b/dialogs/autocutsilencedialog.h
@@ -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 .
+
+***/
+
+#ifndef SILENCEDIALOG_H
+#define SILENCEDIALOG_H
+
+#include
+#include
+
+#include "timeline/clip.h"
+#include "ui/labelslider.h"
+
+class AutoCutSilenceDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ AutoCutSilenceDialog(QWidget* parent, QVector clips);
+public slots:
+ virtual int exec() override;
+private slots:
+ virtual void accept() override;
+private:
+ void cut_silence();
+
+ QVector 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
diff --git a/dialogs/clippropertiesdialog.h b/dialogs/clippropertiesdialog.h
index 62ebabcb0..0a08d9de8 100644
--- a/dialogs/clippropertiesdialog.h
+++ b/dialogs/clippropertiesdialog.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
diff --git a/dialogs/debugdialog.h b/dialogs/debugdialog.h
index d0e5cad59..d6bade443 100644
--- a/dialogs/debugdialog.h
+++ b/dialogs/debugdialog.h
@@ -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;
}
diff --git a/dialogs/demonotice.h b/dialogs/demonotice.h
index 37f89f757..39f14dc0b 100644
--- a/dialogs/demonotice.h
+++ b/dialogs/demonotice.h
@@ -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
{
diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp
index 051befafa..ac4ba0a73 100644
--- a/dialogs/exportdialog.cpp
+++ b/dialogs/exportdialog.cpp
@@ -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());
diff --git a/dialogs/exportdialog.h b/dialogs/exportdialog.h
index 66de17963..0d92fd4b2 100644
--- a/dialogs/exportdialog.h
+++ b/dialogs/exportdialog.h
@@ -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
{
diff --git a/dialogs/loaddialog.h b/dialogs/loaddialog.h
index 0638c682b..24acca8af 100644
--- a/dialogs/loaddialog.h
+++ b/dialogs/loaddialog.h
@@ -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
{
diff --git a/dialogs/mediapropertiesdialog.h b/dialogs/mediapropertiesdialog.h
index 330192cb3..f18dcee9a 100644
--- a/dialogs/mediapropertiesdialog.h
+++ b/dialogs/mediapropertiesdialog.h
@@ -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
diff --git a/dialogs/newsequencedialog.cpp b/dialogs/newsequencedialog.cpp
index 8f6c18621..6860a77cd 100644
--- a/dialogs/newsequencedialog.cpp
+++ b/dialogs/newsequencedialog.cpp
@@ -39,19 +39,26 @@
#include "panels/timeline.h"
#include "project/media.h"
#include "rendering/audio.h"
+#include "global/config.h"
extern "C" {
#include
}
-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;icount();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;icount();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);
diff --git a/dialogs/newsequencedialog.h b/dialogs/newsequencedialog.h
index b5e5f6709..301173b32 100644
--- a/dialogs/newsequencedialog.h
+++ b/dialogs/newsequencedialog.h
@@ -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
*/
diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp
index d2db861b2..e6ecf7405 100644
--- a/dialogs/preferencesdialog.cpp
+++ b/dialogs/preferencesdialog.cpp
@@ -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 menus = menubar->actions();
@@ -299,13 +315,20 @@ void PreferencesDialog::accept() {
reload_effects = true;
}
+ bool bool_requires_restart = false;
+ for (int i=0;iisChecked() != *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(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;iisChecked();
+ }
+
olive::CurrentConfig.style = static_cast(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++;
diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h
index da03290ed..157cf01d8 100644
--- a/dialogs/preferencesdialog.h
+++ b/dialogs/preferencesdialog.h
@@ -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 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 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 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 bool_ui;
+
+ /**
+ * @brief Internal array managed by AddBoolPair(). Do not access this directly.
+ */
+ QVector bool_value;
+
+ /**
+ * @brief Internal array managed by AddBoolPair(). Do not access this directly.
+ */
+ QVector 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;
};
diff --git a/dialogs/proxydialog.h b/dialogs/proxydialog.h
index 7e2cd5f33..a883b2085 100644
--- a/dialogs/proxydialog.h
+++ b/dialogs/proxydialog.h
@@ -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
diff --git a/dialogs/replaceclipmediadialog.cpp b/dialogs/replaceclipmediadialog.cpp
index 9afd37a1f..67e73e386 100644
--- a/dialogs/replaceclipmediadialog.cpp
+++ b/dialogs/replaceclipmediadialog.cpp
@@ -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(selected_items.at(0).internalPointer());
+ Media* new_item = static_cast(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;iclips.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();
}
}
diff --git a/dialogs/replaceclipmediadialog.h b/dialogs/replaceclipmediadialog.h
index 48925423c..32e6788fb 100644
--- a/dialogs/replaceclipmediadialog.h
+++ b/dialogs/replaceclipmediadialog.h
@@ -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;
};
diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp
index 35d9de06c..ff3f9ddd5 100644
--- a/dialogs/speeddialog.cpp
+++ b/dialogs/speeddialog.cpp
@@ -85,16 +85,16 @@ SpeedDialog::SpeedDialog(QWidget *parent, QVector 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;iSetDefault(default_length);
duration->SetValue((current_length == -1) ? qSNaN() : current_length);
- exec();
+ return QDialog::exec();
}
void SpeedDialog::percent_update() {
diff --git a/dialogs/speeddialog.h b/dialogs/speeddialog.h
index 3d7246eb6..84a5832c6 100644
--- a/dialogs/speeddialog.h
+++ b/dialogs/speeddialog.h
@@ -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 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 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
diff --git a/dialogs/texteditdialog.cpp b/dialogs/texteditdialog.cpp
index 4cf77828b..fc5638f1d 100644
--- a/dialogs/texteditdialog.cpp
+++ b/dialogs/texteditdialog.cpp
@@ -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(sender()->property("a").toInt()));
diff --git a/dialogs/texteditdialog.h b/dialogs/texteditdialog.h
index 9ab3eac79..ff3cd8776 100644
--- a/dialogs/texteditdialog.h
+++ b/dialogs/texteditdialog.h
@@ -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;
};
diff --git a/effects/effect.cpp b/effects/effect.cpp
index 68d082300..5ee89d7dc 100644
--- a/effects/effect.cpp
+++ b/effects/effect.cpp
@@ -85,9 +85,7 @@ EffectPtr Effect::Create(Clip* c, const EffectMeta* em) {
case EFFECT_INTERNAL_SHAKE: return std::make_shared(c, em);
case EFFECT_INTERNAL_CORNERPIN: return std::make_shared(c, em);
case EFFECT_INTERNAL_FILLLEFTRIGHT: return std::make_shared(c, em);
-#ifndef NOVST
case EFFECT_INTERNAL_VST: return std::make_shared(c, em);
-#endif
case EFFECT_INTERNAL_RICHTEXT: return std::make_shared(c, em);
}
} else if (!em->filename.isEmpty()) {
@@ -597,6 +595,9 @@ void Effect::load(QXmlStreamReader& stream) {
field->keyframes.append(key);
}
}
+
+ field->Changed();
+
}
}
}
diff --git a/effects/effectloaders.cpp b/effects/effectloaders.cpp
index ee694a73f..c1c682068 100644
--- a/effects/effectloaders.cpp
+++ b/effects/effectloaders.cpp
@@ -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;
diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp
index 33a9cab60..f78a017ce 100644
--- a/effects/effectrow.cpp
+++ b/effects/effectrow.cpp
@@ -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);
}
diff --git a/effects/fields/boolfield.h b/effects/fields/boolfield.h
index 3949f7edf..4cf78164a 100644
--- a/effects/fields/boolfield.h
+++ b/effects/fields/boolfield.h
@@ -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);
};
diff --git a/effects/fields/buttonfield.h b/effects/fields/buttonfield.h
index 47f2e5890..e31b110bc 100644
--- a/effects/fields/buttonfield.h
+++ b/effects/fields/buttonfield.h
@@ -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_;
};
diff --git a/effects/fields/filefield.cpp b/effects/fields/filefield.cpp
index 4bb04dd26..2756fa8a5 100644
--- a/effects/fields/filefield.cpp
+++ b/effects/fields/filefield.cpp
@@ -20,12 +20,15 @@
#include "filefield.h"
+#include
+
#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(widget);
+
+ efc->blockSignals(true);
+ efc->setFilename(GetFileAt(timecode));
+ efc->blockSignals(false);
+}
+
void FileField::UpdateFromWidget(const QString &s)
{
KeyframeDataChange* kdc = new KeyframeDataChange(this);
diff --git a/effects/fields/filefield.h b/effects/fields/filefield.h
index 1b6870c35..4abcd0e6f 100644
--- a/effects/fields/filefield.h
+++ b/effects/fields/filefield.h
@@ -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);
};
diff --git a/effects/internal/frei0reffect.cpp b/effects/internal/frei0reffect.cpp
new file mode 100644
index 000000000..c01ee0835
--- /dev/null
+++ b/effects/internal/frei0reffect.cpp
@@ -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 .
+
+***/
+
+#include "frei0reffect.h"
+
+#ifndef NOFREI0R
+
+#include
+#include
+
+#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(handle.resolve("f0r_init"));
+ init();
+
+ construct_module();
+
+ f0r_plugin_info_t info;
+ f0rGetPluginInfo info_func = reinterpret_cast(handle.resolve("f0r_get_plugin_info"));
+ info_func(&info);
+
+ param_count = info.num_params;
+
+ get_param_info = reinterpret_cast(handle.resolve("f0r_get_param_info"));
+ for (int i=0;i= 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(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(handle.resolve("f0r_update"));
+
+ for (int i=0;i(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();
+
+ 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(input), reinterpret_cast(output));
+}
+
+void Frei0rEffect::refresh() {
+ destruct_module();
+ construct_module();
+}
+
+void Frei0rEffect::destruct_module() {
+ if (open) {
+ f0rDestructFunc destruct = reinterpret_cast(handle.resolve("f0r_destruct"));
+ destruct(instance);
+
+ open = false;
+ }
+}
+
+void Frei0rEffect::construct_module() {
+ f0rConstructFunc construct = reinterpret_cast(handle.resolve("f0r_construct"));
+ instance = construct(parent_clip->media_width(), parent_clip->media_height());
+
+ open = true;
+}
+
+#endif
diff --git a/effects/internal/frei0reffect.h b/effects/internal/frei0reffect.h
new file mode 100644
index 000000000..2078289b0
--- /dev/null
+++ b/effects/internal/frei0reffect.h
@@ -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 .
+
+***/
+
+#ifndef FREI0REFFECT_H
+#define FREI0REFFECT_H
+
+#ifndef NOFREI0R
+
+#include
+#include
+
+#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
diff --git a/effects/internal/vsthost.cpp b/effects/internal/vsthost.cpp
index 077cd17e4..a9f53e478 100644
--- a/effects/internal/vsthost.cpp
+++ b/effects/internal/vsthost.cpp
@@ -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
+#elif defined(Q_OS_MACOS)
+#include
+class NSWindow;
+#elif defined(Q_OS_LINUX)
#include
#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(modulePtr.resolve("VSTPluginMain"));
+
+ if (mainEntryPoint == nullptr) {
+ // If there's no VSTPluginMain(), the plugin may use main() instead
+ mainEntryPoint = reinterpret_cast(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(LibAddress(modulePtr, "VSTPluginMain"));
-
- if (mainEntryPoint == nullptr) {
- // if there's no VSTPluginMain(), fallback to main()
- mainEntryPoint = reinterpret_cast(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(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(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(dialog->windowHandle()->winId()), 0);
-#elif defined(__APPLE__)
+#elif defined(Q_OS_MACOS)
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast(dialog->windowHandle()->winId()), 0);
-#elif defined(__linux__) || defined(__HAIKU__)
+#elif defined(Q_OS_LINUX) || defined(__HAIKU__)
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast(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);
diff --git a/effects/internal/vsthost.h b/effects/internal/vsthost.h
index 2354d1977..d004d892f 100644
--- a/effects/internal/vsthost.h
+++ b/effects/internal/vsthost.h
@@ -21,19 +21,15 @@
#ifndef VSTHOSTWIN_H
#define VSTHOSTWIN_H
-#ifndef NOVST
+#include
+#include
#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
-
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
diff --git a/global/config.cpp b/global/config.cpp
index 370251f23..295fdac05 100644
--- a/global/config.cpp
+++ b/global/config.cpp
@@ -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
diff --git a/global/config.h b/global/config.h
index 7a1f82fdf..3d192cdad 100644
--- a/global/config.h
+++ b/global/config.h
@@ -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
*
diff --git a/global/global.cpp b/global/global.cpp
index 1ca317fec..4c1913df8 100644
--- a/global/global.cpp
+++ b/global/global.cpp
@@ -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 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();
}
diff --git a/global/global.h b/global/global.h
index af44a786a..ab5f342a4 100644
--- a/global/global.h
+++ b/global/global.h
@@ -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.
diff --git a/global/math.cpp b/global/math.cpp
index 805d1de01..f19a22b02 100644
--- a/global/math.cpp
+++ b/global/math.cpp
@@ -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);
+ }
}
diff --git a/global/math.h b/global/math.h
index 37bd85198..face5003d 100644
--- a/global/math.h
+++ b/global/math.h
@@ -21,6 +21,8 @@
#ifndef MATH_H
#define MATH_H
+#include
+
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);
diff --git a/icons/icons.qrc b/icons/icons.qrc
index 8d00cf945..341bbf1b1 100644
--- a/icons/icons.qrc
+++ b/icons/icons.qrc
@@ -52,5 +52,6 @@
align-right.svg
justify-center.svg
bold.svg
+ listview.svg
diff --git a/icons/listview.svg b/icons/listview.svg
new file mode 100644
index 000000000..062cebc39
--- /dev/null
+++ b/icons/listview.svg
@@ -0,0 +1,829 @@
+
+
+
+
diff --git a/olive.pro b/olive.pro
index e38cea4e7..43cede51f 100644
--- a/olive.pro
+++ b/olive.pro
@@ -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 \
diff --git a/panels/panels.cpp b/panels/panels.cpp
index 663aeab00..2b50d04e8 100644
--- a/panels/panels.cpp
+++ b/panels/panels.cpp
@@ -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);
diff --git a/panels/project.cpp b/panels/project.cpp
index 0898f3fb7..c0ad3439a 100644
--- a/panels/project.cpp
+++ b/panels/project.cpp
@@ -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_list) {
+SequencePtr create_sequence_from_media(QVector& 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;iget_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(sorter->mapToSource(index).internalPointer());
+ return static_cast(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& items, QList& 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() {
diff --git a/panels/project.h b/panels/project.h
index fab8bccf1..44914600c 100644
--- a/panels/project.h
+++ b/panels/project.h
@@ -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_list);
+SequencePtr create_sequence_from_media(QVector &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 list_all_project_sequences();
- SourceTable* tree_view;
- SourceIconView* icon_view;
- SourcesCommon* sources_common;
-
- ProjectFilter* sorter;
-
QVector last_imported_media;
QModelIndexList get_current_selected();
void get_all_media_from_table(QList &items, QList &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);
diff --git a/panels/timeline.cpp b/panels/timeline.cpp
index 4f59548d1..30ae02da1 100644
--- a/panels/timeline.cpp
+++ b/panels/timeline.cpp
@@ -138,6 +138,53 @@ void Timeline::Retranslate() {
UpdateTitle();
}
+void Timeline::split_clip_at_positions(ComboAction* ca, int clip_index, QVector positions) {
+
+ QVector 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;ilinked.size();i++) {
+ pre_splits.append(clip->linked.at(i));
+ }
+
+ std::sort(positions.begin(), positions.end());
+
+ // Remove any duplicate positions
+ for (int i=1;i > post_splits(positions.size());
+
+ for (int i=positions.size()-1;i>=0;i--) {
+
+ post_splits[i].resize(pre_splits.size());
+
+ for (int j=0;jset_timeline_out(positions.at(i+1));
+ }
+ }
+ }
+
+ for (int i=0;iappend(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_list) {
+void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector& media_list) {
video_ghosts = false;
audio_ghosts = false;
for (int i=0;iready;
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;jaudio_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;jaudio_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;jvideo_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;jvideo_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_list;
+ QVector 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;jclips.size();j++) {
+ Clip* c = olive::ActiveSequence->clips.at(j).get();
+ if (c != nullptr && !selected_clips.contains(j)) {
+ for (int i=0;itrack() == 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);
diff --git a/panels/timeline.h b/panels/timeline.h
index 27c3aa15e..cd64c75a5 100644
--- a/panels/timeline.h
+++ b/panels/timeline.h
@@ -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 positions);
void clean_up_selections(QVector& areas);
void deselect_area(long in, long out, int track);
void delete_areas_and_relink(ComboAction *ca, QVector& 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_list);
+ void create_ghosts_from_media(Sequence *seq, long entry_point, QVector &media_list);
void add_clips_from_ghosts(ComboAction *ca, Sequence *s);
int getTimelineScreenPointFromFrame(long frame);
diff --git a/panels/viewer.cpp b/panels/viewer.cpp
index fb67a1bde..7b6bc1710 100644
--- a/panels/viewer.cpp
+++ b/panels/viewer.cpp
@@ -20,6 +20,21 @@
#include "viewer.h"
+extern "C" {
+#include
+#include
+}
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
#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
-#include
-}
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
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();
+ new_sequence = std::make_shared();
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(seq.get());
+ ClipPtr c = std::make_shared(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(seq.get());
+ ClipPtr c = std::make_shared(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);
diff --git a/panels/viewer.h b/panels/viewer.h
index 166b7fa38..061623fe0 100644
--- a/panels/viewer.h
+++ b/panels/viewer.h
@@ -27,6 +27,7 @@
#include
#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;
diff --git a/project/footage.cpp b/project/footage.cpp
index 693bc6b94..88c600589 100644
--- a/project/footage.cpp
+++ b/project/footage.cpp
@@ -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);
-}
diff --git a/project/footage.h b/project/footage.h
index ff709950b..dcd84b009 100644
--- a/project/footage.h
+++ b/project/footage.h
@@ -62,9 +62,7 @@ struct FootageStream {
// preview thumbnail/waveform
bool preview_done;
QImage video_preview;
- QIcon video_preview_square;
QVector audio_preview;
- void make_square_thumb();
};
struct Footage {
diff --git a/project/loadthread.cpp b/project/loadthread.cpp
index e4ce8665f..65d869bd2 100644
--- a/project/loadthread.cpp
+++ b/project/loadthread.cpp
@@ -34,10 +34,9 @@
#include
#include
-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;itemp_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);
}
diff --git a/project/loadthread.h b/project/loadthread.h
index cf8445dc1..7f57f60cc 100644
--- a/project/loadthread.h
+++ b/project/loadthread.h
@@ -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);
diff --git a/project/media.cpp b/project/media.cpp
index 847ed2a56..1c9546bb2 100644
--- a/project/media.cpp
+++ b/project/media.cpp
@@ -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();
}
diff --git a/project/media.h b/project/media.h
index e56ac34b8..7462b28f9 100644
--- a/project/media.h
+++ b/project/media.h
@@ -87,6 +87,8 @@ private:
int type;
VoidPtr object;
+ QString GetStringDuration();
+
// item functions
QList children;
Media* parent;
diff --git a/project/previewgenerator.cpp b/project/previewgenerator.cpp
index 4ef532b8e..035166577 100644
--- a/project/previewgenerator.cpp
+++ b/project/previewgenerator.cpp
@@ -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;
diff --git a/project/projectmodel.cpp b/project/projectmodel.cpp
index a0282de90..7903ad89d 100644
--- a/project/projectmodel.cpp
+++ b/project/projectmodel.cpp
@@ -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();
diff --git a/project/sourcescommon.cpp b/project/sourcescommon.cpp
index 46c8e1dea..27103830c 100644
--- a/project/sourcescommon.cpp
+++ b/project/sourcescommon.cpp
@@ -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_list;
+ QVector media_list;
for (int i=0;iitem_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(
diff --git a/project/sourcescommon.h b/project/sourcescommon.h
index b013b7032..4b00071ed 100644
--- a/project/sourcescommon.h
+++ b/project/sourcescommon.h
@@ -26,6 +26,7 @@
#include
#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 cached_selected_footage;
+
+ ProjectFilter& sort_filter_;
};
#endif // SOURCESCOMMON_H
diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp
index 86075538e..117ac8f26 100644
--- a/rendering/cacher.cpp
+++ b/rendering/cacher.cpp
@@ -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) {
diff --git a/rendering/exportthread.cpp b/rendering/exportthread.cpp
index fe42fddc8..e5f48d2df 100644
--- a/rendering/exportthread.cpp
+++ b/rendering/exportthread.cpp
@@ -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
#include
@@ -42,6 +31,17 @@ extern "C" {
#include
#include
#include
+#include
+
+#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);
diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp
index 27aedecec..0a0aee87c 100644
--- a/rendering/renderfunctions.cpp
+++ b/rendering/renderfunctions.cpp
@@ -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();
diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp
index 5f2b86876..552958e94 100644
--- a/rendering/renderthread.cpp
+++ b/rendering/renderthread.cpp
@@ -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();
diff --git a/rendering/renderthread.h b/rendering/renderthread.h
index 74c86cfaf..da6259b62 100644
--- a/rendering/renderthread.h
+++ b/rendering/renderthread.h
@@ -55,7 +55,7 @@ public:
int idivider = 0);
bool did_texture_fail();
void cancel();
-
+ void wait_until_paused();
public slots:
// cleanup functions
diff --git a/timeline/mediaimportdata.cpp b/timeline/mediaimportdata.cpp
new file mode 100644
index 000000000..1358576e7
--- /dev/null
+++ b/timeline/mediaimportdata.cpp
@@ -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_;
+}
diff --git a/timeline/mediaimportdata.h b/timeline/mediaimportdata.h
new file mode 100644
index 000000000..866ea37dd
--- /dev/null
+++ b/timeline/mediaimportdata.h
@@ -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
diff --git a/ts/olive_id.ts b/ts/olive_id.ts
new file mode 100644
index 000000000..236f8b59a
--- /dev/null
+++ b/ts/olive_id.ts
@@ -0,0 +1,3625 @@
+
+
+
+
+ AboutDialog
+
+
+ Olive is a non-linear video editor. This software is free and protected by the GNU GPL.
+ Olive adalah aplikasi pengedit video yang bersifat non-linier. Aplikasi ini bebas, gratis, dan terlindungi GNU GPL.
+
+
+
+ Olive Team is obliged to inform users that Olive source code is available for download from its website.
+ Olive Team berkewajiban memberitahu pengguna bahwa kode sumber aplikasi ini dapat diunduh dari situs resminya.
+
+
+
+ ActionSearch
+
+
+ Search for action...
+ Cari Aksi...
+
+
+
+ AdvancedVideoDialog
+
+
+ Advanced Video Settings
+ Pengaturan Video Lanjutan
+
+
+
+ Pixel Format:
+ Bentuk piksel:
+
+
+
+ Threads:
+ Jumlah thread/utas:
+
+
+
+ Audio
+
+
+ %1 Audio
+ Audio %1
+
+
+
+ Recording %1
+ Merekam %1
+
+
+
+ AudioNoiseEffect
+
+
+ Amount
+ Kenyaringan
+
+
+
+ Mix
+
+
+
+
+ Cacher
+
+
+
+ Could not open %1 - %2
+ Tidak dapat membuka %1 - %2
+
+
+
+ ChannelLayoutName
+
+
+ Invalid
+ Salah
+
+
+
+ Mono
+ Mono
+
+
+
+ Stereo
+ Stereo
+
+
+
+ ClipPropertiesDialog
+
+
+ "%1" Properties
+ Properti untuk "%1"
+
+
+
+ Multiple Clip Properties
+ Properti untuk Beberapa Klip
+
+
+
+ Name:
+ Nama:
+
+
+
+ Duration:
+ Durasi:
+
+
+
+ (multiple)
+ (beberapa)
+
+
+
+ CollapsibleWidget
+
+
+ <untitled>
+ <belum dinamai>
+
+
+
+ ColorButton
+
+
+ Set Color
+ Pilih Warna
+
+
+
+ CornerPinEffect
+
+
+ Top Left
+ Kiri Atas
+
+
+
+ Top Right
+ Kanan Atas
+
+
+
+ Bottom Left
+ Kiri Bawah
+
+
+
+ Bottom Right
+ Kanan Bawah
+
+
+
+ Perspective
+ Perspektif
+
+
+
+ DebugDialog
+
+
+ Debug Log
+ Awakutu / Debug
+
+
+
+ DemoNotice
+
+
+
+ Welcome to Olive!
+ Selamat datang di Olive!
+
+
+
+ Olive is a free open-source video editor released under the GNU GPL. If you have paid for this software, you have been scammed.
+ differentiate "free" as in "free of charge" and "free" as in "freedom/libre"
+ Olive adalah aplikasi edit video yang bebas, gratis dan terbuka sumbernya, terlisensi GNU GPL. Jika Anda membayar untuk aplikasi ini, Anda telah tertipu.
+
+
+
+ This software is currently in ALPHA which means it is unstable and very likely to crash, have bugs, and have missing features. We offer no warranty so use at your own risk. Please report any bugs or feature requests at %1
+ Aplikasi ini masih dalam tahap ALPHA, artinya aplikasi ini belum stabil dan kemungkinan besar akan crash, memiliki bug/kutu, dan banyak fitur yang belum ada. Kami tidak menjamin apapun, jadi Anda dipersilahkan menggunakan aplikasi ini dengan menanggung resikonya. Jika menemukan bug/kutu atau ingin meminta suatu fitur, silahkan lapor di %1
+
+
+
+ Thank you for trying Olive and we hope you enjoy it!
+ Terima kasih Anda telah mencoba Olive dan kami harap Anda menyukainya!
+
+
+
+ Effect
+
+
+ Invalid effect
+ Efek tidak ada
+
+
+
+ No candidate for effect '%1'. This effect may be corrupt. Try reinstalling it or Olive.
+ Tidak ada kandidat untuk efek '%1'. Efek mungkin korup. Coba menginstal ulang efek tersebut, atau menginstal ulang Olive.
+
+
+ Cu&t
+ &Potong
+
+
+ Move &Up
+ Pindah ke &Atas
+
+
+ Move &Down
+ Pindah ke &Bawah
+
+
+ D&elete
+ &Hapus
+
+
+ Load Settings From File
+ Buka Pengaturan Efek dari File
+
+
+ Save Settings to File
+ Simpan Pengaturan ke File
+
+
+
+ Save Effect Settings
+ Simpan Pengaturan Efek
+
+
+
+
+ Effect XML Settings %1
+ Pengaturan XML Efek %1
+
+
+
+ Save Settings Failed
+ Gagal Menyimpan Pengaturan
+
+
+
+ Failed to open "%1" for writing.
+ Gagal menulis file "%1"
+
+
+
+ Load Effect Settings
+ Buka Pengaturan Efek
+
+
+
+
+ Load Settings Failed
+ Gagal Membuka Pengaturan
+
+
+
+ Failed to open "%1" for reading.
+ considering changing "file" to the defined equivalent "berkas", but it might not be familiar to most people
+ Gagal membaca file "%1"
+
+
+
+ This settings file doesn't match this effect.
+ File pengaturan ini tidak cocok dengan efek yang dipilih.
+
+
+
+ EffectControls
+
+ &Paste
+ &Tempel
+
+
+
+ (none)
+ (tidak ada)
+
+
+
+ Effects:
+ Efek:
+
+
+
+ Add Video Effect
+ Masukkan Efek Video
+
+
+
+ VIDEO EFFECTS
+ EFEK VIDEO
+
+
+
+ Add Video Transition
+ Masukkan Transisi Video
+
+
+
+ Add Audio Effect
+ Masukkan Efek Audio
+
+
+
+ AUDIO EFFECTS
+ EFEK AUDIO
+
+
+
+ Add Audio Transition
+ Masukkan Transisi Audio
+
+
+ (Multiple clips selected)
+ (Beberapa klip terseleksi)
+
+
+
+ EffectRow
+
+
+ Disable Keyframes
+ Matikan Keyframe
+
+
+
+ Disabling keyframes will delete all current keyframes. Are you sure you want to do this?
+ Mematikan keyframe akan menghapus semua keyframe di efek ini. Benarkah Anda ingin melakukan hal tersebut?
+
+
+
+ EffectUI
+
+
+ %1 (Opening)
+ %1 (Membuka)
+
+
+
+ %1 (Closing)
+ %1 (Menutup)
+
+
+
+ %1 (multiple)
+ %1 (beberapa)
+
+
+
+ Cu&t
+ &Potong
+
+
+
+ &Copy
+ &Salin
+
+
+
+ Move &Up
+ Pindah ke &Atas
+
+
+
+ Move &Down
+ Pindah ke &Bawah
+
+
+
+ D&elete
+ &Hapus
+
+
+
+ Load Settings From File
+ Buka Pengaturan Efek dari File
+
+
+
+ Save Settings to File
+ Simpan Pengaturan ke File
+
+
+
+ EmbeddedFileChooser
+
+
+ File:
+
+
+
+
+ ExportDialog
+
+
+ Export "%1"
+ Ekspor "%1"
+
+
+
+ Unknown codec name %1
+ Kodek %1 tidak diketahui
+
+
+
+ Export Failed
+ Gagal Mengekspor
+
+
+
+ Export failed - %1
+ Gagal mengekspor - %1
+
+
+
+ Invalid dimensions
+ Dimensi salah
+
+
+
+ Export width and height must both be even numbers/divisible by 2.
+ Lebar dan tinggi video ekspor harus genap/habis dibagi 2.
+
+
+
+ Invalid codec
+ Kodek salah
+
+
+
+ Couldn't determine output parameters for the selected codec. This is a bug, please contact the developers.
+ Tidak dapat menset pengaturan keluaran/output. Ini merupakan kesalahan, silahkan hubungi pengembang aplikasi.
+
+
+
+ Invalid format
+ Format salah
+
+
+
+ Couldn't determine output format. This is a bug, please contact the developers.
+ Tidak dapat memilih format keluaran/output. Ini merupakan kutu/bug, silahkan hubungi pengembang aplikasi.
+
+
+
+ Export Media
+ Ekspor Media
+
+
+
+ %p% (Total: %1:%2:%3)
+ %p% (lama: %1:%2:%3)
+
+
+
+ %p% (ETA: %1:%2:%3)
+ %p% (perkiraan: %1:%2:%3)
+
+
+
+ Quality-based (Constant Rate Factor)
+ Berbasis kualitas (CRF)
+
+
+
+ Constant Bitrate
+ Laju bit konstan (CBR)
+
+
+
+
+ Invalid Codec
+ Kodek Salah
+
+
+
+ Failed to find a suitable encoder for this codec. Export will likely fail.
+ Tidak dapat mencari enkoder yang cocok untuk kodek ini. Ekspor kemungkinan gagal.
+
+
+
+ Failed to find pixel format for this encoder. Export will likely fail.
+ Tidak dapat menentukan format piksel untuk enkoder ini. Ekspor kemungkinan gagal.
+
+
+
+ Bitrate (Mbps):
+ Laju bit (Mbps):
+
+
+
+ Quality (CRF):
+ Kualitas (CRF):
+
+
+
+ Quality Factor:
+
+0 = lossless
+17-18 = visually lossless (compressed, but unnoticeable)
+23 = high quality
+51 = lowest quality possible
+ Faktor kualitas:
+
+0 = lossless / tidak terkompresi
+17-18 = lossless secara visual (masih terkompresi namun tidak terlihat pecah-pecah)
+23 = kualitas tinggi
+51 = kualitas paling rendah
+
+
+
+ Target File Size (MB):
+ Ukuran File yang Ditargetkan (MB):
+
+
+
+ Format:
+
+
+
+
+ Range:
+ Sepanjang:
+
+
+
+ Entire Sequence
+ Seluruh rangkaian
+
+
+
+ In to Out
+ Masuk hingga Keluar
+
+
+
+ Video
+
+
+
+
+
+ Codec:
+ Kodek:
+
+
+
+ Width:
+ Lebar:
+
+
+
+ Height:
+ Tinggi:
+
+
+
+ Frame Rate:
+ Laju frame (fps):
+
+
+
+ Compression Type:
+ Jenis Kompresi:
+
+
+
+ Advanced
+ Pengaturan Lanjut
+
+
+
+ Audio
+
+
+
+
+ Sampling Rate:
+ Laju sampel:
+
+
+
+ Bitrate (Kbps/CBR):
+ Laju bit (Kbps/CBR):
+
+
+
+ ExportThread
+
+
+ failed to send frame to encoder (%1)
+ gagal mengirim frame ke enkoder (%1)
+
+
+
+ failed to receive packet from encoder (%1)
+ gagal menerima paket dari enkoder (%1)
+
+
+
+ could not video encoder for %1
+ tidak dapat mencari enkoder video untuk %1
+
+
+
+ could not allocate video stream
+ tidak dapat mengalokasikan stream video
+
+
+
+ could not allocate video encoding context
+ tidak dapat mengalokasikan konteks mengenkode video
+
+
+
+ could not open output video encoder (%1)
+ tidak dapat membuka enkoder video keluaran (%1)
+
+
+
+ could not copy video encoder parameters to output stream (%1)
+ tidak dapat menyalin parameter enkoder video ke stream keluaran (%1)
+
+
+
+ could not audio encoder for %1
+ tidak dapat mencari enkoder audio untuk %1
+
+
+
+ could not allocate audio stream
+ tidak dapat mengalokasikan stream audio
+
+
+
+ could not allocate audio encoding context
+ tidak dapat mengalokasikan konteks mengenkode audio
+
+
+
+ could not open output audio encoder (%1)
+ tidak dapat membuka enkoder audio keluaran (%1)
+
+
+
+ could not copy audio encoder parameters to output stream (%1)
+ tidak dapat menyalin parameter enkoder audio ke stream keluaran (%1)
+
+
+
+ could not allocate audio buffer (%1)
+ tidak dapat mengalokasikan buffer audio (%1)
+
+
+
+ could not create output format context
+ tidak dapat membuat konteks format keluaran
+
+
+
+ could not open output file (%1)
+ tidak dapat membuka file keluaran (%1)
+
+
+
+ could not write output file header (%1)
+ tidak dapat menulis header untuk file keluaran (%1)
+
+
+
+ could not write output file trailer (%1)
+ tidak dapat menulis trailer untuk file keluaran (%1)
+
+
+
+ FillLeftRightEffect
+
+
+ Type
+ Tipe
+
+
+
+ Fill Left with Right
+ Penuhi Suara Kiri dengan Kanan
+
+
+
+ Fill Right with Left
+ Penuhi Suara Kanan dengan Kiri
+
+
+
+ Frei0rEffect
+
+
+ Failed to load Frei0r plugin "%1": %2
+ Gagal membuka plugin Frei0r "%1": %2
+
+
+ NOTE: You can't load 32-bit Frei0r 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.
+ CATATAN: Plugin Frei0r 32-bit tidak dapat dibuka dalam Olive versi 64-bit. Silahkan mencari versi 64-bit dari plugin ini atau instal Olive versi 32-bit.
+
+
+ NOTE: You can't load 64-bit Frei0r 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.
+ CATATAN: Plugin Frei0r 64-bit tidak dapat dibuka dalam Olive versi 32-bit. Silahkan mencari versi 32-bit dari plugin ini atau instal Olive versi 64-bit.
+
+
+
+ Error loading Frei0r plugin
+ Gagal membuka plugin Frei0r
+
+
+
+ GraphEditor
+
+
+ Graph Editor
+ Pengedit Grafik
+
+
+
+ Linear
+ Linier
+
+
+
+ Bezier
+
+
+
+
+ Hold
+ Tahan
+
+
+
+ GraphView
+
+
+ Zoom to Selection
+ Perbesar ke Seleksi
+
+
+
+ Zoom to Show All
+ Perlihatkan Semua
+
+
+
+ Reset View
+ Kembalikan Seperti Semula
+
+
+
+ InterlacingName
+
+
+ None (Progressive)
+ Tidak ada (Progresif)
+
+
+
+ Top Field First
+ Utamakan Bidang Atas
+
+
+
+ Bottom Field First
+ Utamakan Bidang Bawah
+
+
+
+ Invalid
+ Salah
+
+
+
+ KeyframeNavigator
+
+
+ Enable Keyframes
+ Nyalakan Keyframe
+
+
+
+ KeyframeView
+
+
+ Linear
+ Linier
+
+
+
+ Bezier
+
+
+
+
+ Hold
+ Tahan
+
+
+
+ LabelSlider
+
+
+ &Edit
+
+
+
+
+ &Reset to Default
+ &Kembalikan seperti Semula
+
+
+
+
+ Set Value
+ Ubah Jumlah
+
+
+
+
+ New value:
+ "value" actually would be "harga" or "nilai" but it probably won't fit
+ Jumlah:
+
+
+
+ LoadDialog
+
+
+ Loading...
+ Memuat...
+
+
+
+ Loading '%1'...
+ Memuat '%1'...
+
+
+
+ Cancel
+ Batalkan
+
+
+
+ LoadThread
+
+
+ Version Mismatch
+ Versi tak Cocok
+
+
+
+ This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?
+ Proyek ini disimpan menggunakan versi Olive yang lain dan kemungkinan tidak sepenuhnya kompatibel dengan versi ini. Tetap dibuka?
+
+
+
+ Invalid Clip Link
+ Tautan Klip Salah
+
+
+
+ This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?
+ Proyek ini terdapat tautan klip yang salah, kemungkinan korup. Tetap memuat?
+
+
+
+ %1 - Line: %2 Col: %3
+ %1 - Baris: %2 Kolom: %3
+
+
+
+ User aborted loading
+ Pengguna membatalkan pemuatan proyek
+
+
+
+ XML Parsing Error
+ Gagal Membaca XML
+
+
+
+ Couldn't load '%1'. %2
+ Tidak dapat membaca '%1'. %2
+
+
+
+ Project Load Error
+ Gagal Memuat Proyek
+
+
+
+ Error loading project: %1
+ Gagal memuat proyek: %1
+
+
+
+ MainWindow
+
+
+ Welcome to %1
+ Selamat datang di %1
+
+
+
+ &File
+
+
+
+
+ &New
+ &Baru
+
+
+
+ &Open Project
+ Buka &Proyek
+
+
+
+ Clear Recent List
+ Hapus Daftar "Terakhir Dibuka"
+
+
+
+ Open Recent
+ Buka Terakhir
+
+
+
+ &Save Project
+ &Simpan Proyek
+
+
+
+ Save Project &As
+ Simpan Proyek Seba&gai
+
+
+
+ &Import...
+ &Impor...
+
+
+
+ &Export...
+ &Ekspor...
+
+
+
+ E&xit
+ &Keluar
+
+
+
+ &Edit
+
+
+
+
+ &Undo
+ &Urung
+
+
+
+ Redo
+ Ulangi
+
+
+
+ Select &All
+ Seleksi &Semua
+
+
+
+ Deselect All
+ Batalkan Semua Pilihan
+
+
+
+ Ripple to In Point
+ Atur hingga Titik Masuk
+
+
+
+ Ripple to Out Point
+ Atur hingga Titik Keluar
+
+
+
+ Edit to In Point
+ Edit ke Titik Masuk
+
+
+
+ Edit to Out Point
+ Edit ke Titik Keluar
+
+
+
+ Delete In/Out Point
+ Hapus Titik Masuk/Keluar
+
+
+
+ Ripple Delete In/Out Point
+ Hapus dan Sesuaikan Titik Masuk/Keluar
+
+
+
+ Set/Edit Marker
+ Set/Edit Penanda
+
+
+
+ &View
+ &Tampilan
+
+
+
+ Zoom In
+ Perbesar Tampilan
+
+
+
+ Zoom Out
+ Perkecil Tampilan
+
+
+
+ Increase Track Height
+ Lebarkan Trek
+
+
+
+ Decrease Track Height
+ Persempit Trek
+
+
+
+ Toggle Show All
+ "show all"
+ Perlihatkan Semua
+
+
+
+ Track Lines
+ Garis Trek
+
+
+
+ Rectified Waveforms
+ "flatten" or "center at bottom"
+ Visualisasi Audio Rata Bawah
+
+
+
+ Frames
+ Frame
+
+
+
+ Drop Frame
+
+
+
+
+ Non-Drop Frame
+
+
+
+
+ Milliseconds
+ Milisekon
+
+
+
+ Title/Action Safe Area
+
+
+
+
+ Off
+ Matikan
+
+
+
+ Default
+
+
+
+
+ 4:3
+
+
+
+
+ 16:9
+
+
+
+
+ Custom
+ Kustom
+
+
+
+ Full Screen
+ Layar Penuh
+
+
+
+ Full Screen Viewer
+ Penampil Layar Penuh
+
+
+
+ &Playback
+ &Pemutaran
+
+
+
+ Go to Start
+ Lompat ke Awal
+
+
+
+ Previous Frame
+ Frame sebelumnya
+
+
+
+ Play/Pause
+ Mainkan/Berhenti
+
+
+
+ Play In to Out
+ Mainkan dari Titik Masuk hingga Keluar
+
+
+
+ Next Frame
+ Frame berikutnya
+
+
+
+ Go to End
+ Lompat ke Akhir
+
+
+
+ Go to Previous Cut
+ Lompat ke Cut Sebelumnya
+
+
+
+ Go to Next Cut
+ Lompat ke Cut Berikutnya
+
+
+
+ Go to In Point
+ Lompat ke Titik Masuk
+
+
+
+ Go to Out Point
+ Lompat ke Titik Keluar
+
+
+
+ Shuttle Left
+ Jalankan ke Kiri
+
+
+
+ Shuttle Stop
+ Hentikan jalan
+
+
+
+ Shuttle Right
+ Jalankan ke Kanan
+
+
+
+ Loop
+ Putar secara Berulang
+
+
+
+ &Window
+ &Jendela
+
+
+
+ Project
+ Proyek
+
+
+
+ Effect Controls
+ Pengaturan Efek
+
+
+
+ Timeline
+ Garis Waktu
+
+
+
+ Graph Editor
+ Pengedit Grafik
+
+
+
+ Media Viewer
+ Penampil Media
+
+
+
+ Sequence Viewer
+ Penampil Rangkaian
+
+
+
+ Maximize Panel
+ Lebarkan Panel
+
+
+
+ Lock Panels
+ Kunci Panel
+
+
+
+ Reset to Default Layout
+ Kembalikan Layout Semula
+
+
+
+ &Tools
+ &Alat
+
+
+
+ Pointer Tool
+ Alat Tunjuk
+
+
+
+ Edit Tool
+ Alat Edit
+
+
+
+ Ripple Tool
+ Alat Pengatur
+
+
+
+ Razor Tool
+ Alat Potong
+
+
+
+ Slip Tool
+ Alat Slip
+
+
+
+ Slide Tool
+ Alat Geser Klip
+
+
+
+ Hand Tool
+ Alat Geser Tampilan
+
+
+
+ Transition Tool
+ Alat Transisi
+
+
+
+ Enable Snapping
+ Nyalakan Lekatan
+
+
+
+ Selecting Also Seeks
+ idk how to translate this
+ Menyeleksi Juga Menggeser
+
+
+
+ Edit Tool Also Seeks
+ Alat Edit Juga Menggeser
+
+
+
+ Edit Tool Selects Links
+ Alat Edit Menyeleksi Tautan
+
+
+
+ Seek Also Selects
+ Menggeser Juga Menyeleksi
+
+
+
+ Seek to the End of Pastes
+ Geser hingga Akhir Tempelan
+
+
+
+ Scroll Wheel Zooms
+ Scroll Wheel Memperbesar/Memperkecil Tampilan
+
+
+
+ Hold CTRL to toggle this setting
+ Tekan CTRL untuk mengaktifkan pengaturan ini
+
+
+
+ Invert Timeline Scroll Axes
+ Balikkan Arah Gulir Garis Waktu
+
+
+
+ Enable Drag Files to Timeline
+ Seret dan Lepas file ke Timeline
+
+
+
+ Auto-Scale By Default
+ Atur Ukuran Video secara Default
+
+
+
+ Enable Seek to Import
+ Nyalakan Geser-untuk-Impor
+
+
+
+ Audio Scrubbing
+ Nyalakan Audio Scrubbing
+
+
+
+ Enable Drop on Media to Replace
+ Seret pada Media untuk Menggantikan
+
+
+
+ Enable Hover Focus
+ Nyalakan Fokus Melayang
+
+
+
+ Ask For Name When Setting Marker
+ Tanyakan Nama ketika Menaruh Penanda
+
+
+
+ No Auto-Scroll
+ Matikan Gulir Otomatis
+
+
+
+ Page Auto-Scroll
+ Gulir Halaman Otomatis
+
+
+
+ Smooth Auto-Scroll
+ Gulir Halus Otomatis
+
+
+
+ Preferences
+ Preferensi
+
+
+
+ Clear Undo
+ Hapus Daftar Urung (Undo)
+
+
+
+ &Help
+ &Bantuan
+
+
+
+ A&ction Search
+ &Cari Aksi
+
+
+
+ Debug Log
+ Awakutu / Debug
+
+
+
+ &About...
+ &Tentang...
+
+
+
+ <untitled>
+ <belum dinamai>
+
+
+
+ Marker
+
+
+ Set Marker
+ Masukkan Penanda
+
+
+
+ Set clip marker name:
+ Masukkan nama penanda:
+
+
+
+ Set sequence marker name:
+ Masukkan nama penanda rangkaian:
+
+
+
+ Media
+
+
+ New Folder
+ Folder Baru
+
+
+
+ Name:
+ Nama:
+
+
+
+ Filename:
+ Nama file:
+
+
+
+ Video Dimensions:
+ Dimensi Video:
+
+
+
+ Frame Rate:
+ Laju frame:
+
+
+
+ %1 field(s) (%2 frame(s))
+ %1 baris (%2 frame)
+
+
+
+ Interlacing:
+ Mode interlace:
+
+
+
+ Audio Frequency:
+ Frekuensi Audio:
+
+
+
+ Audio Channels:
+ Kanal Audio:
+
+
+
+ Name: %1
+Video Dimensions: %2x%3
+Frame Rate: %4
+Audio Frequency: %5
+Audio Layout: %6
+ Nama: %1
+Dimensi Video: %2x%3
+Laju Frame: %4
+Frekuensi Audio: %5
+Tata Audio: %6
+
+
+
+ Name
+ Nama
+
+
+
+ Duration
+ Durasi
+
+
+
+ Rate
+ Laju
+
+
+
+ MediaPropertiesDialog
+
+
+ "%1" Properties
+ Properti "%1"
+
+
+
+ Tracks:
+ Jumlah trek:
+
+
+
+ Video %1: %2x%3 %4FPS
+
+
+
+
+ Audio %1: %2Hz %3
+
+
+
+
+ %n channel(s)
+
+ %n kanal
+
+
+
+
+ Conform to Frame Rate:
+ Ubah laju frame jadi:
+
+
+
+ Alpha is Premultiplied
+ Idk how to translate this either
+ Alpha dipremultiplikasi
+
+
+
+ Auto (%1)
+
+
+
+
+ Interlacing:
+ Mode interlace:
+
+
+
+ Name:
+ Nama:
+
+
+
+ MenuHelper
+
+
+ &Project
+ &Proyek
+
+
+
+ &Sequence
+ &Rangkaian
+
+
+
+ &Folder
+
+
+
+
+ Set In Point
+ Set Titik Masuk
+
+
+
+ Set Out Point
+ Set Titik Keluar
+
+
+
+ Reset In Point
+ Kembalikan Titik Masuk
+
+
+
+ Reset Out Point
+ Kembalikan Titik Keluar
+
+
+
+ Clear In/Out Point
+ Hapus Titik Masuk/Keluar
+
+
+
+ Add Default Transition
+ Masukkan Transisi Biasa
+
+
+
+ Link/Unlink
+ Tautkan/Lepaskan
+
+
+
+ Enable/Disable
+ Nyalakan/Matikan
+
+
+
+ Nest
+ Sarangkan
+
+
+
+ Cu&t
+ &Potong
+
+
+
+ Cop&y
+ &Salin
+
+
+
+
+ &Paste
+ &Tempel
+
+
+
+ Paste Insert
+ Tempel dan Masukkan
+
+
+
+ Duplicate
+ Gandakan
+
+
+
+ Delete
+ Hapus
+
+
+
+ Ripple Delete
+ literally the function of ripple delete: "delete and adjust"
+ Hapus dan Sesuaikan
+
+
+
+ Split
+ Pisahkan
+
+
+
+ Invalid aspect ratio
+ Rasio aspek salah
+
+
+
+ The aspect ratio '%1' is invalid. Please try again.
+ Rasio aspek '%1' salah. Silahkan coba lagi.
+
+
+
+ Enter custom aspect ratio
+ Masukkan rasio aspek kustom
+
+
+
+ Enter the aspect ratio to use for the title/action safe area (e.g. 16:9):
+ Masukkan rasio aspek yang ingin dipakai untuk safe area judul/aksi (contohnya 16:9)
+
+
+
+ NewSequenceDialog
+
+
+ Editing "%1"
+ Mengedit "%1"
+
+
+
+ New Sequence
+ Rangkaian Baru
+
+
+
+ Preset:
+
+
+
+
+ Film 4K
+
+
+
+
+ TV 4K (Ultra HD/2160p)
+
+
+
+
+ 1080p
+
+
+
+
+ 720p
+
+
+
+
+ 480p
+
+
+
+
+ 360p
+
+
+
+
+ 240p
+
+
+
+
+ 144p
+
+
+
+
+ NTSC (480i)
+
+
+
+
+ PAL (576i)
+
+
+
+
+ Custom
+ Kustom
+
+
+
+ Video
+
+
+
+
+ Width:
+ Lebar:
+
+
+
+ Height:
+ Tinggi:
+
+
+
+ Frame Rate:
+ Laju frame (fps):
+
+
+
+ Pixel Aspect Ratio:
+ Rasio aspek piksel:
+
+
+
+ Square Pixels (1.0)
+ Persegi (1.0)
+
+
+
+ Interlacing:
+ Mode interlace:
+
+
+
+ None (Progressive)
+ Tidak ada (Progresif)
+
+
+
+ Audio
+
+
+
+
+ Sample Rate:
+ Laju sampel:
+
+
+
+ Name:
+ Nama:
+
+
+
+ OliveGlobal
+
+
+ Olive Project %1
+ Proyek Olive %1
+
+
+
+ Auto-recovery
+ Auto-pulih
+
+
+
+ Olive didn't close properly and an autorecovery file was detected. Would you like to open it?
+ Olive tidak ditutup sebagaimana mestinya, dan ditemukan sebuah file auto-pulih. Buka?
+
+
+
+ Open Project...
+ Buka Proyek...
+
+
+
+ Missing recent project
+ Proyek Terakhir Tidak Ada
+
+
+
+ The project '%1' no longer exists. Would you like to remove it from the recent projects list?
+ Proyek '%1' tidak ada lagi. Hapus dari daftar "proyek terakhir"?
+
+
+
+ Save Project As...
+ Simpan Proyek Sebagai...
+
+
+
+ Unsaved Project
+ Proyek Belum Disimpan
+
+
+
+ This project has changed since it was last saved. Would you like to save it before closing?
+ Proyek ini diubah sejak terakhir disimpan. Simpan sebelum ditutup?
+
+
+
+ No active sequence
+ Tidak ada rangkaian aktif
+
+
+
+ Please open the sequence you wish to export.
+ Buka dahulu rangkaian/sequence yang ingin diekspor.
+
+
+
+ Missing Project File
+ File Proyek Tidak Ada
+
+
+
+ Specified project '%1' does not exist.
+ Proyek yang dipilih, '%1', tidak ditemukan.
+
+
+
+ PanEffect
+
+
+ Pan
+ Geser/Pan
+
+
+
+ PreferencesDialog
+
+
+ Preferences
+ Preferensi
+
+
+
+ Invalid CSS File
+ File CSS Salah
+
+
+
+ CSS file '%1' does not exist.
+ Tidak ditemukan file CSS '%1'
+
+
+
+ Confirm Reset All Shortcuts
+ Konfirmasi
+
+
+
+ Are you sure you wish to reset all keyboard shortcuts to their defaults?
+ Anda ingin mengembalikan semua pintasan keyboard seperti semula. Yakin?
+
+
+
+ Import Keyboard Shortcuts
+ Impor Pintasan Keyboard
+
+
+
+
+ Error saving shortcuts
+ Gagal menyimpan pintasan
+
+
+
+ Failed to open file for reading
+ Gagal membuka file
+
+
+
+ Export Keyboard Shortcuts
+ Ekspor Pintasan Keyboard
+
+
+
+ Export Shortcuts
+ Ekspor Pintasan
+
+
+
+ Shortcuts exported successfully
+ Pintasan berhasil diekspor
+
+
+
+ Failed to open file for writing
+ Gagal membaca file
+
+
+
+ Browse for CSS file
+ Telusuri file CSS
+
+
+
+ Delete All Previews
+ Hapus Semua Pratinjau
+
+
+
+ Are you sure you want to delete all previews?
+ Yakin menghapus semua pratinjau?
+
+
+
+ Previews Deleted
+ Pratinjau Dihapus
+
+
+
+ All previews deleted succesfully. You may have to re-open your current project for changes to take effect.
+ Semua pratinjau berhasil dihapus. Anda mungkin perlu membuka proyek kembali.
+
+
+
+ Language:
+ Bahasa:
+
+
+
+ Automatically Seek to the Beginning When Playing at the End of a Sequence
+ Pindahkan Kursor secara Otomatis ke Awal Ketika Mencapai Akhir Rangkaian
+
+
+
+ Custom CSS:
+ CSS Custom:
+
+
+
+ Browse
+ Telusur
+
+
+
+ Image sequence formats:
+ Format rangkaian gambar:
+
+
+
+ Audio Recording:
+ Rekaman Audio:
+
+
+
+ Mono
+
+
+
+
+ Stereo
+ Stereo
+
+
+
+ Effect Textbox Lines:
+ Baris Teks Efek:
+
+
+
+ Thumbnail Resolution:
+ according to kbbi it should be "keluku" but not a lot of people know that
+ Resolusi Thumbnail:
+
+
+
+ Waveform Resolution:
+ Resolusi Waveform:
+
+
+
+ Delete Previews
+ Hapus Pratinjau
+
+
+
+ Use Software Fallbacks When Possible
+ Gunakan Software Fallback Sebisa Mungkin
+
+
+
+ Default Sequence Settings
+ Pengaturan Rangkaian
+
+
+
+ General
+
+
+
+
+ Behavior
+ Kelakuan
+
+
+
+ Add Default Effects to New Clips
+ Tambahkan Efek-Efek Biasa pada Klip Baru
+
+
+
+ Appearance
+ Penampilan
+
+
+
+ Theme
+ Tema
+
+
+
+ Olive Dark (Default)
+ Gelap (Default)
+
+
+
+ Olive Light
+ Terang
+
+
+
+ Native
+ Selaras/native
+
+
+
+ Native (Light Icons)
+ Selaras (Ikon Terang)
+
+
+
+ Use Native Menu Styling
+ Gunakan Gaya Menu Selaras
+
+
+ Seeking
+ "geser" may not be understood well
+ Tampilan Frame
+
+
+ Accurate Seeking
+Always show the correct frame (visual may pause briefly as correct frame is retrieved)
+ Tampilan Akurat
+Selalu tampilkan frame yang sebenarnya (dapat terhenti sejenak sembari mencari frame yang benar)
+
+
+ Fast Seeking
+Seek quickly (may briefly show inaccurate frames when seeking - doesn't affect playback/export)
+ Tampilan Cepat
+Tampilkan frame dengan cepat (dapat menampilkan frame yang salah ketika menggeser kursor di timeline - tidak berpengaruh pada pemutaran/ekspor)
+
+
+
+ Memory Usage
+ Pemakaian Memori
+
+
+
+ Upcoming Frame Queue:
+ Antri Frame Ke Depan:
+
+
+
+
+ frames
+ frame
+
+
+
+
+ seconds
+ detik
+
+
+
+ Previous Frame Queue:
+ Antri Frame Ke Belakang:
+
+
+
+ Playback
+ Pemutaran
+
+
+
+ Output Device:
+ Peranti Output:
+
+
+
+
+ Default
+
+
+
+
+ Input Device:
+ Peranti Masukan:
+
+
+
+ Sample Rate:
+ Laju sampel:
+
+
+
+ Audio
+
+
+
+
+ Search for action or shortcut
+ Cari aksi atau pintasan
+
+
+
+ Action
+ Aksi
+
+
+
+ Shortcut
+ Pintasan
+
+
+
+ Import
+ Impor
+
+
+
+ Export
+ Ekspor
+
+
+
+ Reset Selected
+ Kembalikan Seleksi
+
+
+
+ Reset All
+ Kembalikan Semua
+
+
+
+ Keyboard
+
+
+
+
+ PreviewGenerator
+
+
+ Failed to find any valid video/audio streams
+ Gagal mencari stream video/audio yang benar
+
+
+
+ Could not open file - %1
+ Tidak dapat membuka file - %1
+
+
+
+ Could not find stream information - %1
+ Tidak dapat mencari informasi stream - %1
+
+
+
+ Project
+
+
+ Search media, markers, etc.
+ Cari media, penanda, dll.
+
+
+
+ Project
+ Proyek
+
+
+
+ Sequence
+ Rangkaian
+
+
+
+ Replace '%1'
+ Ganti '%1'
+
+
+
+
+ All Files
+ Semua file
+
+
+
+
+ No active sequence
+ Tidak ada rangkaian aktif
+
+
+
+ No sequence is active, please open the sequence you want to replace clips from.
+ Tidak ada rangkaian aktif, silahkan buka rangkaian yang akan diganti klipnya.
+
+
+
+ Active sequence selected
+ Rangkaian aktif terseleksi
+
+
+
+ You cannot insert a sequence into itself, so no clips of this media would be in this sequence.
+ Anda tak dapat memasukkan rangkaian ke dalam rangkaian itu sendiri, jadi tidak ada klip sejenis ini dalam rangkaian.
+
+
+
+ Rename '%1'
+ Ganti nama '%1'
+
+
+
+ Enter new name:
+ Masukkan nama pengganti:
+
+
+
+ Delete media in use?
+ Hapus media yang sedang dipakai?
+
+
+
+ The media '%1' is currently used in '%2'. Deleting it will remove all instances in the sequence. Are you sure you want to do this?
+ Media '%1' sedang dipakai dalam '%2'. Menghapus media tersebut akan menghapus semua instans media dalam rangkaian. Yakin akan melakukan hal tersebut?
+
+
+
+ Skip
+ Lewati
+
+
+
+ Import a Project
+ Impor Proyek
+
+
+
+ "%1" is an Olive project file. It will merge with this project. Do you wish to continue?
+ "%1" adalah file proyek Olive. File tersebut akan bergabung dengan proyek ini. Lanjutkan?
+
+
+
+ Image sequence detected
+ Rangkaian gambar terdeteksi
+
+
+
+ The file '%1' appears to be part of an image sequence. Would you like to import it as such?
+ File '%1' sepertinya merupakan rangkaian gambar. Apakah Anda ingin mengimpornya sebaga rangkaian gambar?
+
+
+
+ Import media...
+ Impor media...
+
+
+
+ No sequence is active, please open the sequence you want to delete clips from.
+ Tidak ada rangkaian aktif, silahkan buka rangkaian yang Anda ingin hapus klipnya.
+
+
+
+ ProxyDialog
+
+
+ Create Proxy
+ Buat Proksi
+
+
+
+ Proxy
+ Proksi
+
+
+
+ Dimensions:
+ Ukuran:
+
+
+
+ Same Size as Source
+ Sama dengan Sumber
+
+
+
+ Half Resolution (1/2)
+ Resolusi setengah (1/2)
+
+
+
+ Quarter Resolution (1/4)
+ Resolusi seperempat (1/4)
+
+
+
+ Eighth Resolution (1/8)
+ Resolusi seperdelapan (1/8)
+
+
+
+ Sixteenth Resolution (1/16)
+ Resolusi seperenambelas (1/16)
+
+
+
+ Format:
+
+
+
+
+ ProRes HQ
+
+
+
+
+ Location:
+ Lokasi:
+
+
+
+ Same as Source (in "%1" folder)
+ Sama dengan Sumber (dalam folder "%1")
+
+
+
+ Proxy file exists
+ File proksi sudah ada
+
+
+
+ The file "%1" already exists. Do you wish to replace it?
+ File "%1" sudah ada. Ganti?
+
+
+
+ Custom Location
+ Lokasi Kustom
+
+
+
+ ProxyGenerator
+
+
+ Finished generating proxy for "%1"
+ Selesai membuat proksi untuk "%1"
+
+
+
+ ReplaceClipMediaDialog
+
+
+ Replace clips using "%1"
+ Ganti klip dengan "%1"
+
+
+
+ Select which media you want to replace this media's clips with:
+ Pilih media pengganti media dari klip:
+
+
+
+ Keep the same media in-points
+ Samakan titik masuk media
+
+
+
+ Replace
+ Ganti
+
+
+
+ Cancel
+ Batalkan
+
+
+
+ No media selected
+ Tidak ada media yang dipilih
+
+
+
+ Please select a media to replace with or click 'Cancel'.
+ Pilih media pengganti atau klik "Batalkan".
+
+
+
+ Same media selected
+ Terpilih media sama
+
+
+
+ You selected the same media that you're replacing. Please select a different one or click 'Cancel'.
+ Anda memilih media yang sama dengan yang akan diganti. Silahkan pilih yang lain atau klik "Batalkan".
+
+
+
+ Folder selected
+ Folder terpilih
+
+
+
+ You cannot replace footage with a folder.
+ Anda tidak dapat mengganti media dengan folder.
+
+
+
+ Active sequence selected
+ Rangkaian aktif terseleksi
+
+
+
+ You cannot insert a sequence into itself.
+ Anda tidak dapat memasukkan rangkaian pada rangkaian itu sendiri.
+
+
+
+ RichTextEffect
+
+
+ Text
+ Teks
+
+
+
+ Padding
+ Ruang Border
+
+
+
+ Position
+ Posisi
+
+
+
+ Vertical Align:
+ Rata Vertikal:
+
+
+
+ Top
+ Atas
+
+
+
+ Center
+ Tengah
+
+
+
+ Bottom
+ Bawah
+
+
+
+ Auto-Scroll
+ Gulir otomatis
+
+
+
+ Off
+ Matikan
+
+
+
+ Up
+ Ke atas
+
+
+
+ Down
+ Ke bawah
+
+
+
+ Left
+ Ke kiri
+
+
+
+ Right
+ Ke kanan
+
+
+
+ Shadow
+ Bayangan
+
+
+
+ Shadow Color
+ Warna Bayangan
+
+
+
+ Shadow Angle
+ Arah Bayangan
+
+
+
+ Shadow Distance
+ Jarak Bayangan
+
+
+
+ Shadow Softness
+ Kehalusan Bayangan
+
+
+
+ Shadow Opacity
+ "opacity" is a hard word to find a suitable meaning for
+ Intensitas Bayangan
+
+
+
+ Sequence
+
+
+ %1 (copy)
+ %1 (salinan)
+
+
+
+ ShakeEffect
+
+
+ Intensity
+ Intensitas
+
+
+
+ Rotation
+ Rotasi
+
+
+
+ Frequency
+ Frekuensi
+
+
+
+ SolidEffect
+
+
+ Type
+ Tipe
+
+
+
+ Solid Color
+ Warna
+
+
+
+ SMPTE Bars
+
+
+
+
+ Checkerboard
+ Kotak-Kotak
+
+
+
+ Opacity
+
+
+
+
+ Color
+ Warna
+
+
+
+ Checkerboard Size
+ Ukuran Kotak-Kotak
+
+
+
+ SourcesCommon
+
+
+ Import...
+ Impor...
+
+
+
+ New
+ Baru
+
+
+
+ View
+ Tampilan
+
+
+
+ Tree View
+ Tampilan Pohon
+
+
+
+ Icon View
+ Tampilan Ikon
+
+
+
+ Show Toolbar
+ Tampilkan Toolbar
+
+
+
+ Show Sequences
+ Tampilkan Rangkaian
+
+
+
+ Replace/Relink Media
+ Ganti/Taut Media
+
+
+
+ Reveal in Explorer
+ Buka di Explorer
+
+
+
+ Reveal in Finder
+ Buka di Finder
+
+
+
+ Reveal in File Manager
+ Buka di Manajer Berkas
+
+
+
+ Replace Clips Using This Media
+ Ganti Klip dengan Media Ini
+
+
+
+ Create Sequence With This Media
+ Buat Rangkaian dengan Media Ini
+
+
+
+ Duplicate
+ Gandakan
+
+
+
+ Delete All Clips Using This Media
+ Hapus Semua Klip yang Menggunakan Media Ini
+
+
+
+ Proxy
+ Proksi
+
+
+
+ Generating proxy: %1% complete
+ Membuat proksi: %1%
+
+
+
+ Create/Modify Proxy
+ Buat/Ubah Proksi
+
+
+
+ Create Proxy
+ Buat Proksi
+
+
+
+ Modify Proxy
+ Ubah Proksi
+
+
+
+ Restore Original
+ Kembalikan Seperti Semula
+
+
+
+ Delete
+ Hapus
+
+
+
+ Preview in Media Viewer
+ Pratayang di Penampil Media
+
+
+
+ Properties...
+ Properti...
+
+
+
+ Replace Media
+ Ganti Media
+
+
+
+ You dropped a file onto '%1'. Would you like to replace it with the dropped file?
+ Anda menjatuhkan file ke '%1'. Ganti klip dengan file tersebut?
+
+
+
+ Delete proxy
+ Hapus proksi
+
+
+
+ Would you like to delete the proxy file "%1" as well?
+ Hapus file proksi "%1" juga?
+
+
+
+ SpeedDialog
+
+
+ Speed/Duration
+ Kecepatan/Durasi
+
+
+
+ Speed:
+ Kecepatan:
+
+
+
+ Frame Rate:
+ Laju frame (fps):
+
+
+
+ Duration:
+ Durasi:
+
+
+
+ Reverse
+ Terbalik
+
+
+
+ Maintain Audio Pitch
+ Tahan Pitch
+
+
+
+ Ripple Changes
+
+
+
+
+ TextEditDialog
+
+
+ Edit Text
+ Edit Teks
+
+
+
+ Thin
+
+
+
+
+ Extra Light
+
+
+
+
+ Light
+
+
+
+
+ Normal
+
+
+
+
+ Medium
+
+
+
+
+ Demi Bold
+
+
+
+
+ Bold
+
+
+
+
+ Extra Bold
+
+
+
+
+ Black
+
+
+
+
+ TextEditEx
+
+
+ Edit Text
+ Edit Teks
+
+
+
+ &Edit Text
+ &Edit Teks
+
+
+
+ TextEffect
+
+
+ Text
+ Teks
+
+
+
+ Font
+ Fon
+
+
+
+ Size
+ Ukuran
+
+
+
+ Color
+ Warna
+
+
+
+ Alignment
+ Rata
+
+
+
+ Left
+ Kiri
+
+
+
+
+ Center
+ Tengah
+
+
+
+ Right
+ Kanan
+
+
+
+ Justify
+ Kanan-Kiri
+
+
+
+ Top
+ Atas
+
+
+
+ Bottom
+ Bawah
+
+
+
+ Word Wrap
+ "bungkus kata" is also possible but feels weird
+ Sesuaikan Lebar Kata
+
+
+
+ Padding
+ Ruang Border
+
+
+
+ Position
+ Posisi
+
+
+
+ Outline
+ Garis Teks
+
+
+
+ Outline Color
+ Warna Garis
+
+
+
+ Outline Width
+ Ketebalan Garis
+
+
+
+ Shadow
+ Bayangan
+
+
+
+ Shadow Color
+ Warna Bayangan
+
+
+
+ Shadow Angle
+ Arah Bayangan
+
+
+
+ Shadow Distance
+ Jarak Bayangan
+
+
+
+ Shadow Softness
+ Kehalusan Bayangan
+
+
+
+ Shadow Opacity
+ Intensitas Bayangan
+
+
+
+ Sample Text
+ Masukkan teks disini
+
+
+
+ TimecodeEffect
+
+
+ Timecode
+ Kode Waktu
+
+
+
+ Sequence
+ Rangkaian
+
+
+
+ Media
+
+
+
+
+ Scale
+ Ukuran
+
+
+
+ Color
+ Warna
+
+
+
+ Background Color
+ Warna Latar
+
+
+
+ Background Opacity
+ Transparansi Latar
+
+
+
+ Offset
+
+
+
+
+ Prepend
+ Teks Sebelum
+
+
+
+ Timeline
+
+
+ Pointer Tool
+ Alat Tunjuk
+
+
+
+ Edit Tool
+ Alat Edit
+
+
+
+ Ripple Tool
+ Alat Pengatur
+
+
+
+ Razor Tool
+ Alat Potong
+
+
+
+ Slip Tool
+ Alat Slip
+
+
+
+ Slide Tool
+ Alat Geser Klip
+
+
+
+ Hand Tool
+ Alat Geser Tampilan
+
+
+
+ Transition Tool
+ Alat Transisi
+
+
+
+ Snapping
+ Lekatan
+
+
+
+ Zoom In
+ Perbesar Tampilan
+
+
+
+ Zoom Out
+ Perkecil Tampilan
+
+
+
+ Record audio
+ Rekam suara
+
+
+
+ Add title, solid, bars, etc.
+ Masukkan judul, warna, bars, dll.
+
+
+
+ Nested Sequence
+ Rangkaian Bersarang
+
+
+
+ Effect already exists
+ Efek sudah ada
+
+
+
+ Clip '%1' already contains a '%2' effect. Would you like to replace it with the pasted one or add it as a separate effect?
+ Klip '%1' sudah memiliki efek '%2'. Ganti dengan yang akan ditempel atau tambahkan sebagai efek sendiri?
+
+
+
+ Add
+ Tambah
+
+
+
+ Replace
+ Ganti
+
+
+
+ Skip
+ Lewati
+
+
+
+ Do this for all conflicts found
+ Lakukan untuk semua konflik yang ditemukan
+
+
+
+ Title...
+ Judul...
+
+
+
+ Solid Color...
+ Warna...
+
+
+
+ Bars...
+
+
+
+
+ Tone...
+ Nada...
+
+
+
+ Noise...
+
+
+
+
+ Unsaved Project
+ Proyek Belum Disimpan
+
+
+
+ You must save this project before you can record audio in it.
+ Proyek ini harus disimpan sebelum merekam suara.
+
+
+
+ Click on the timeline where you want to start recording (drag to limit the recording to a certain timeframe)
+ Klik tempat dimana Anda akan mulai merekam (seret untuk membatasi rekaman dalam waktu tertentu)
+
+
+
+ Timeline:
+ Garis Waktu:
+
+
+
+ (none)
+ (tidak ada)
+
+
+
+ TimelineHeader
+
+
+ Center Timecodes
+ Ratakan Kode Waktu
+
+
+
+ TimelineWidget
+
+
+ &Undo
+ "takjadi" and "batalkan" are also possible translations
+ &Urung
+
+
+
+ &Redo
+ "kembalikan" is also possible
+ &Ulangi
+
+
+ &Paste
+ &Tempel
+
+
+
+ R&ipple Delete Empty Space
+ Hapus dan Sesuaikan Ruang Kosong
+
+
+
+ Sequence Settings
+ Pengaturan Rangkaian
+
+
+
+ &Speed/Duration
+ &Kecepatan/Durasi
+
+
+
+ Auto-s&cale
+ Per&besar otomatis
+
+
+
+ &Reveal in Project
+ &Buka di Proyek
+
+
+
+ Properties
+ Properti
+
+
+
+ %1
+Start: %2
+End: %3
+Duration: %4
+ %1
+Mulai: %2
+Akhir: %3
+Durasi: %4
+
+
+
+ Error
+
+
+
+
+ Couldn't locate media wrapper for sequence.
+
+
+
+
+ Title
+ Judul
+
+
+
+ Solid Color
+ Warna
+
+
+
+ Bars
+
+
+
+
+ Tone
+ Nada
+
+
+
+ Noise
+ Kebisingan/Noise
+
+
+
+ Duration:
+ Durasi:
+
+
+
+ ToneEffect
+
+
+ Type
+ Tipe
+
+
+
+ Sine
+ Sinus
+
+
+
+ Frequency
+ Frekuensi
+
+
+
+ Amount
+ Kenyaringan
+
+
+
+ Mix
+
+
+
+
+ TransformEffect
+
+
+ Position
+ Posisi
+
+
+
+ Scale
+ Ukuran
+
+
+
+ Uniform Scale
+ Ukuran Merata
+
+
+
+ Rotation
+ Rotasi
+
+
+
+ Anchor Point
+ Titik Poros
+
+
+
+ Opacity
+
+
+
+
+ Blend Mode
+ Mode Penggabungan
+
+
+
+ Normal
+
+
+
+
+ Transition
+
+
+ Length
+ Panjang
+
+
+
+ UpdateNotification
+
+
+ An update is available from the Olive website. Visit www.olivevideoeditor.org to download it.
+ Pembaruan aplikasi telah tersedia. Silahkan kunjungi www.olivevideoeditor.org untuk mengunduhnya.
+
+
+
+ VSTHost
+
+
+
+ Error loading VST plugin
+ Gagal membuka plugin VST
+
+
+
+ Failed to load VST plugin "%1": %2
+ Gagal membuka plugin VST "%1": %2
+
+
+
+ Failed to locate entry point for dynamic library.
+ Gagal mencari titik masuk untuk pustaka dinamis (dynamic library)
+
+
+
+ VST Error
+ Galat VST
+
+
+
+ Plugin's magic number is invalid
+ Identifikasi plugin salah
+
+
+
+ Plugin
+
+
+
+
+ Interface
+ Antarmuka
+
+
+
+ Show
+ Tampilkan
+
+
+
+ VST Plugin
+ Plugin VST
+
+
+
+ Viewer
+
+
+ Sequence Viewer
+ Tampilan Rangkaian
+
+
+
+ Media Viewer
+ Tampilan Media
+
+
+
+ (none)
+ (tidak ada)
+
+
+
+ Drag video only
+ Tarik video saja
+
+
+
+ Drag audio only
+ Tarik audio saja
+
+
+
+ ViewerWidget
+
+
+ Save Frame as Image...
+ Simpan Frame sebagai Gambar...
+
+
+
+ Show Fullscreen
+ Tampilkan Layar Penuh
+
+
+
+ Disable
+ Matikan
+
+
+
+ Screen %1: %2x%3
+ Layar %1: %2x%3
+
+
+
+ Zoom
+ Pembesaran
+
+
+
+ Fit
+ Pas
+
+
+
+ Custom
+ Kustom
+
+
+
+ Close Media
+ Tutup Media
+
+
+
+ Save Frame
+ Simpan Frame
+
+
+
+ Viewer Zoom
+ Pembesaran Tampilan
+
+
+
+ Set Custom Zoom Value:
+ Masukkan pembesaran kustom:
+
+
+
+ ViewerWindow
+
+
+ Exit Fullscreen
+ Keluar dari Layar Penuh
+
+
+
+ VoidEffect
+
+
+ (unknown)
+ (tidak diketahui)
+
+
+
+ Missing Effect
+ Efek Hilang
+
+
+
+ VolumeEffect
+
+
+ Volume
+
+
+
+
+ transition
+
+
+ Invalid transition
+ Transisi salah
+
+
+
+ No candidate for transition '%1'. This transition may be corrupt. Try reinstalling it or Olive.
+ Tidak ada kandidat untuk efek '%1'. Efek mungkin korup. Coba menginstal ulang efek tersebut, atau menginstal ulang Olive.
+
+
+
diff --git a/ts/olive_uk.ts b/ts/olive_uk.ts
index cfecbc67c..569c29024 100644
--- a/ts/olive_uk.ts
+++ b/ts/olive_uk.ts
@@ -43,13 +43,13 @@
Audio
-
+
%1 Audio
- Потрібно уточнити
+ Уточнити
%1 Аудіо
-
+
Recording %1
Запис %1
@@ -62,7 +62,7 @@
Кількість
-
+
Mix
Змішування
@@ -91,7 +91,8 @@
"%1" Properties
- "%1" Параметри
+ Уточнити
+ Параметри "%1"
@@ -127,35 +128,35 @@
ColorButton
-
+
Set Color
- Встановити колір
+ Визначити колір
CornerPinEffect
-
+
Top Left
Верхній Лівий
-
+
Top Right
Верхній Правий
-
+
Bottom Left
Нижній Лівий
-
+
Bottom Right
Нижній Правий
-
+
Perspective
Перспектива
@@ -195,163 +196,174 @@
Effect
-
+
Invalid effect
Некоректний ефект
-
+
No candidate for effect '%1'. This effect may be corrupt. Try reinstalling it or Olive.
Відсутній відповідник для ефекту '%1'. Цей ефект можливо пошкоджений. Спробуйте перевстановити його або ж Olive.
-
- Cu&t
- Ви&різати
-
-
-
- &Copy
- &Копіювати
-
-
-
- Move &Up
- Перемістити В&гору
-
-
-
- Move &Down
- Перемістити В&низ
-
-
-
- D&elete
- Ви&далити
-
-
-
- Load Settings From File
- Завантажити налаштування з файлу
-
-
-
- Save Settings to File
- Зберегти налаштування у файл
-
-
-
+
Save Effect Settings
Зберегти налаштування ефектів
-
-
+
+
Effect XML Settings %1
Файли з налаштуваннями ефектів %1
-
+
Save Settings Failed
Не вдалося зберегти налаштування
-
+
Failed to open "%1" for writing.
Не вдалося відкрити "%1" для запису.
-
+
Load Effect Settings
Завантажити налаштування ефектів
-
-
+
+
Load Settings Failed
Не вдалося завантажити налаштування
-
+
Failed to open "%1" for reading.
Не вдалося відкрити "%1" для зчитування.
-
+
This settings file doesn't match this effect.
- Цей файл налаштувань не підходить для цього ефекта.
+ Цей файл налаштувань не підходить для даного ефекта.
EffectControls
-
- &Paste
- В&ставити
-
-
-
+
(none)
(пусто)
-
+
Effects:
Ефекти:
-
+
Add Video Effect
Додати відеоефект
-
+
VIDEO EFFECTS
ВІДЕОЕФЕКТИ
-
+
Add Video Transition
Додати відеоперехід
-
+
Add Audio Effect
Додати аудіоефект
-
+
AUDIO EFFECTS
АУДІОЕФЕКТИ
-
+
Add Audio Transition
Додати аудіоперехід
-
-
- (Multiple clips selected)
- (виділено множину кліпів)
-
EffectRow
-
+
Disable Keyframes
Вимкнути ключові кадри
-
+
Disabling keyframes will delete all current keyframes. Are you sure you want to do this?
Вимкнення ключових кадрів видалить усі існуючі ключові кадри. Ви впевнені що хочете зробити це?
+
+ EffectUI
+
+
+ %1 (Opening)
+ Уточнити
+ %1 (Відкривання)
+
+
+
+ %1 (Closing)
+ Уточнити
+ %1 (Закривання)
+
+
+
+ %1 (multiple)
+ Уточнити
+ %1 (множинний)
+
+
+
+ Cu&t
+ Ви&різати
+
+
+
+ &Copy
+ &Копіювати
+
+
+
+ Move &Up
+ Перемістити В&низ
+
+
+
+ Move &Down
+ Перемістити В&гору
+
+
+
+ D&elete
+ Ви&далити
+
+
+
+ Load Settings From File
+ Завантажити налаштування з файла
+
+
+
+ Save Settings to File
+ Зберегти налаштування у файл
+
+
EmbeddedFileChooser
-
+
File:
Файл:
@@ -359,109 +371,111 @@
ExportDialog
-
+
Export "%1"
Експортувати "%1"
-
+
Unknown codec name %1
Невідома назва кодека %1
-
+
Export Failed
Не вдалося експортувати
-
+
Export failed - %1
Не вдалося експортувати - %1
-
+
Invalid dimensions
Некоректні розміри кадра
-
+
Export width and height must both be even numbers/divisible by 2.
Для експорту значення ширини та висоти повинні бути цілими парними числами.
-
+
Invalid codec
Некоректний кодек
-
+
Couldn't determine output parameters for the selected codec. This is a bug, please contact the developers.
Неможливо визначити вихідні параметри для обраного кодека. Це помилка, будь-ласка, зв'яжітся з розробниками.
-
+
Invalid format
Некоректний формат
-
+
Couldn't determine output format. This is a bug, please contact the developers.
Неможливо визначити вихідний формат. Це помилка, будь-ласка, зв'яжітся з розробниками.
-
+
Export Media
Уточнити
Експортувати медіафайл
-
+
%p% (Total: %1:%2:%3)
- %p% (Час: %1:%2:%3)
+ Уточнити
+ %p% (Загалом: %1:%2:%3)
-
+
%p% (ETA: %1:%2:%3)
%p% (Залишилося: %1:%2:%3)
-
+
Quality-based (Constant Rate Factor)
+ Уточнити
Якість (Constant Rate Factor)
-
+
Constant Bitrate
- Сталий швидкість потоку
+ Стала швидкість потока
-
-
+
+
Invalid Codec
Некоректний кодек
-
+
Failed to find a suitable encoder for this codec. Export will likely fail.
Не вдалося знайти відповідний кодувальник для цього кодека. Експорт може бути некоректним.
-
+
Failed to find pixel format for this encoder. Export will likely fail.
Не вдалося знайти формат пікселів для цього кодувальника. Експорт може бути некоректним.
-
+
Bitrate (Mbps):
Швидкість потока (Мбіт/с):
-
+
Quality (CRF):
Якість (CRF):
-
+
Quality Factor:
0 = lossless
@@ -476,79 +490,78 @@
51 = найнижча можлива якість
-
+
Target File Size (MB):
Кінцевий розмір файла (Мб):
-
+
Format:
Формат:
-
+
Range:
Діапазон:
-
+
Entire Sequence
- Уточнити
- Вся послідовність
+ Уся послідовність
-
+
In to Out
Від входу до виходу
-
+
Video
Відео
-
-
+
+
Codec:
Кодек:
-
+
Width:
Ширина:
-
+
Height:
Висота:
-
+
Frame Rate:
Частота кадрів:
-
+
Compression Type:
Тип cтискання:
-
+
Advanced
Додатково
-
+
Audio
Аудіо
-
+
Sampling Rate:
Частота дискретизації:
-
+
Bitrate (Kbps/CBR):
Швидкість потока (Кбіт/с / CBR):
@@ -556,90 +569,90 @@
ExportThread
-
+
failed to send frame to encoder (%1)
не вдалося надіслати кадр до кодувальника (%1)
-
+
failed to receive packet from encoder (%1)
не вдалося отримати пакет від кодувальника (%1)
-
+
could not video encoder for %1
не вдалося знайти кодувальник відео для %1
-
+
could not allocate video stream
не вдалося встановити поток відео
-
+
could not allocate video encoding context
не вдалося встановити контекст кодувльника відео
-
+
could not open output video encoder (%1)
не вдалося відкрити вихідний кодувальник відео (%1)
-
+
could not copy video encoder parameters to output stream (%1)
не вдалося скопіювати параметри кодувальника відео для вихідного потоку (%1)
-
+
could not audio encoder for %1
не вдалося знайти кодувальник аудіо для %1
-
+
could not allocate audio stream
не вдалося встановити поток аудіо
-
+
could not allocate audio encoding context
не вдалося встановити контекст кодувльника аудіо
-
+
could not open output audio encoder (%1)
не вдалося відкрити вихідний кодувальник аудіо (%1)
-
+
could not copy audio encoder parameters to output stream (%1)
не вдалося скопіювати параметри кодувальника аудіо для вихідного потоку (%1)
-
+
could not allocate audio buffer (%1)
не вдалося встановити буфер аудіо (%1)
-
+
could not create output format context
не вдалося створити контекст вихідного формату
-
+
could not open output file (%1)
не вдалося відкрити вихідний файл (%1)
-
+
could not write output file header (%1)
не вдалося записати заголовок вихідного файлу (%1)
-
+
could not write output file trailer (%1)
Уточнити
- не вдалося записати кінець вихідного файлу (%1)
+ не вдалося записати кінець вихідного файла (%1)
@@ -686,40 +699,41 @@
GraphEditor
-
+
Graph Editor
Редактор графів
-
+
Linear
Лінійний
-
+
Bezier
Безьє
-
+
Hold
+ Уточнити
Стала
GraphView
-
+
Zoom to Selection
Масштабувати до виділеного
-
+
Zoom to Show All
Масштабувати і показати все
-
+
Reset View
Скинути масштабування
@@ -750,7 +764,7 @@
KeyframeNavigator
-
+
Enable Keyframes
Увімкнути ключові кадри
@@ -758,18 +772,19 @@
KeyframeView
-
+
Linear
Лінійний
-
+
Bezier
Безьє
-
+
Hold
+ Уточнити
Стала
@@ -787,14 +802,14 @@
&Скинути до стандартних
-
-
+
+
Set Value
Встановити значення
-
-
+
+
New value:
Нове значення:
@@ -814,58 +829,59 @@
Cancel
+ Уточнити
Відміна
LoadThread
-
+
Version Mismatch
Невідповіність версій
-
+
This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?
Цей проект булр збережено в іншій версії Olive, котра неповністью сумісна з наявною версією. Ви все ж хочете спробувати завантажити цей проект?
-
+
Invalid Clip Link
Некоректний зв'язок кліпів
-
+
This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?
У проекті виявлено некоректний зв'язок кліпів. Ви хочете продовжити завантаження?
-
+
%1 - Line: %2 Col: %3
%1 - Рядок: %2 Стовпчик: %3
-
+
User aborted loading
Завантаження зупинено користувачем
-
+
XML Parsing Error
Помилка розбору XML
-
+
Couldn't load '%1'. %2
Не вдалося завантажити '%1'. %2
-
+
Project Load Error
Помилка при завантаженні проекта
-
+
Error loading project: %1
Помилка при завантаженні проекта: %1
@@ -873,532 +889,533 @@
MainWindow
-
+
Welcome to %1
Вітаємо в %1
-
+
&File
&Файл
-
+
&New
&Новий
-
+
&Open Project
&Відкрити проект
-
+
Clear Recent List
Очистити історію
-
+
Open Recent
Відкрити недавній
-
+
&Save Project
&Зберегти проект
-
+
Save Project &As
Зберегти проект &як
-
+
&Import...
&Імпортувати...
-
+
&Export...
&Експортувати...
-
+
E&xit
Ви&хід
-
+
&Edit
&Редагування
-
+
&Undo
&Відмінити
-
+
Redo
Повернути
-
+
Select &All
Виділити &усе
-
+
Deselect All
Скасувати виділення
-
+
Ripple to In Point
Зсунути до точки входу
-
+
Ripple to Out Point
Зсунути до точки виходу
-
+
Edit to In Point
Редагування до точки входу
-
+
Edit to Out Point
Редагування до точки виходу
-
+
Delete In/Out Point
Видалити точку входу/виходу
-
+
Ripple Delete In/Out Point
Видалити зі зміщенням точку входу/виходу
-
+
Set/Edit Marker
Встановити/Редагувати маркер
-
+
&View
&Вигляд
-
+
Zoom In
Наблизити
-
+
Zoom Out
Віддалити
-
+
Increase Track Height
Збільшити висоту доріжки
-
+
Decrease Track Height
Зменшити висоту доріжки
-
+
Toggle Show All
Уточнити
Показувати увесь проект
-
+
Track Lines
Лінії доріжок
-
+
Rectified Waveforms
Хвильова форма від низу
-
+
Frames
Кадри
-
+
Drop Frame
З пропусканням кадрів
-
+
Non-Drop Frame
Без пропускання кадрів
-
+
Milliseconds
Мілісекунди
-
+
Title/Action Safe Area
Уточнити
Безпечна зона титрів/ефекта
-
+
Off
- Вимк.
+ Вимкнено
-
+
Default
Типово
-
+
4:3
4:3
-
+
16:9
16:9
-
+
Custom
Інше
-
+
Full Screen
Повноекранний режим
-
+
Full Screen Viewer
Перегляд в повноекранному режимі
-
+
&Playback
Від&творення
-
+
Go to Start
На початок
-
+
Previous Frame
Попередній кадр
-
+
Play/Pause
Відтворення/Пауза
-
+
Play In to Out
Відтворити від входу до виходу
-
+
Next Frame
Наступний кадр
-
+
Go to End
У кінець
-
+
Go to Previous Cut
До попереднього розрізу
-
+
Go to Next Cut
До наступного розрізу
-
+
Go to In Point
До точки входу
-
+
Go to Out Point
До точки виходу
-
+
Shuttle Left
Уточнити
Зменшити швидкість
-
+
Shuttle Stop
Уточнити
Пауза
-
+
Shuttle Right
Уточнити
Збільшити швидкість
-
+
Loop
- Петля
+ Уточнити
+ Повторення петлі
-
+
&Window
&Вікно
-
+
Project
Проект
-
+
Effect Controls
Керування ефектами
-
+
Timeline
Монтажний стіл
-
+
Graph Editor
Редактор графів
-
+
Media Viewer
Уточнити
Переглядач медіа файлів
-
+
Sequence Viewer
Уточнити
Переглядач послідовності
-
+
Maximize Panel
Розгорнути панель
-
+
Lock Panels
Зафіксувати панель
-
+
Reset to Default Layout
Повернути початкове розташування панелей
-
+
&Tools
&Інструменти
-
+
Pointer Tool
Уточнити
Вказівник
-
+
Edit Tool
Виділення
-
+
Ripple Tool
Монтаж зі зсувом
-
+
Razor Tool
Підрізка
-
+
Slip Tool
Прокручування зі зміщенням
-
+
Slide Tool
Прокручування
-
+
Hand Tool
Уточнити
Навігація
-
+
Transition Tool
Перехід
-
+
Enable Snapping
Увімкнути прилипання
-
+
Selecting Also Seeks
Виділення з прокручуванням
-
+
Edit Tool Also Seeks
Уточнити
Виділення з прокручуванням
-
+
Edit Tool Selects Links
Виділення обирає зв'язки
-
+
Seek Also Selects
Прокручування з виділенням
-
+
Seek to the End of Pastes
Прокручування до кінця вставок
-
+
Scroll Wheel Zooms
Уточнити
Колесо миші масштабує монтажний стіл
-
+
Hold CTRL to toggle this setting
- Утримуйте CTRL для перемикання цього ноалаштування
+ Утримуйте CTRL для перемикання цього налаштування
-
+
Invert Timeline Scroll Axes
Уточнити
Інвертувати напрямки прокручування монтажного столу
-
+
Enable Drag Files to Timeline
Уточнити
Дозволити переміщення файлів на монтажний стіл
-
+
Auto-Scale By Default
Автомасштабування за умовчанням
-
+
Enable Seek to Import
Уточнити
Увімкнути прокручування для імпортування
-
+
Audio Scrubbing
Відтворювати звук під час прокручування
-
+
Enable Drop on Media to Replace
Уточнити
Увімкнути переміщення на медіа для заміни
-
+
Enable Hover Focus
Увімкнути фокус наведенням
-
+
Ask For Name When Setting Marker
Запитувати назву маркера при додаванні
-
+
No Auto-Scroll
Без автопрокручування
-
+
Page Auto-Scroll
- Прокручувати перегортанням
+ Авторокручування перегортанням
-
+
Smooth Auto-Scroll
- Прокручувати плавно
+ Плавне автопрокручування
-
+
Preferences
Параметри
-
+
Clear Undo
Очистити історію змін
-
+
&Help
&Довідка
-
+
A&ction Search
По&шук дії
-
+
Debug Log
Журнал злагодження
-
+
&About...
&Про програму...
-
+
<untitled>
<без назви>
@@ -1406,17 +1423,17 @@
Marker
-
+
Set Marker
Встановити маркер
-
+
Set clip marker name:
Назва маркера кліпу:
-
+
Set sequence marker name:
Назва маркера послідовності:
@@ -1424,75 +1441,76 @@
Media
-
+
New Folder
Нова тека
-
+
Name:
Назва:
-
+
Filename:
Ім'я файла:
-
+
Video Dimensions:
Розмір кадрів:
-
+
Frame Rate:
Частота кадрів:
-
+
%1 field(s) (%2 frame(s))
+ Уточнити
полів: %1 (кадрів: %2)
-
+
Interlacing:
Черезрядковість:
-
+
Audio Frequency:
Частота звука:
-
+
Audio Channels:
- Звукових каналів:
+ Звукові канали:
-
+
Name: %1
Video Dimensions: %2x%3
Frame Rate: %4
Audio Frequency: %5
Audio Layout: %6
Назва: %1
-Розмер кадрів: %2x%3
+Розмір кадрів: %2x%3
Частота кадрів: %4
Частота звука: %5
-Звукових каналів: %6
+Звукові канали: %6
-
+
Name
Назва
-
+
Duration
Тривалість
-
+
Rate
Частота
@@ -1500,27 +1518,27 @@ Audio Layout: %6
MediaPropertiesDialog
-
+
"%1" Properties
Властивості "%1"
-
+
Tracks:
Доріжок:
-
+
Video %1: %2x%3 %4FPS
Відео %1: %2x%3 %4к/c
-
+
Audio %1: %2Hz %3
Аудіо %1: %2Гц %3
-
+
%n channel(s)
%n канал
@@ -1529,28 +1547,28 @@ Audio Layout: %6
-
+
Conform to Frame Rate:
Підігнати до частоти кадрів:
-
+
Alpha is Premultiplied
Уточнити
Альфа-значення помножено у зворотньому порядку
-
+
Auto (%1)
Авто (%1)
-
+
Interlacing:
Черезрядковість:
-
+
Name:
Назва:
@@ -1558,123 +1576,124 @@ Audio Layout: %6
MenuHelper
-
+
&Project
&Проект
-
+
&Sequence
П&ослідовність
-
+
&Folder
Т&ека
-
+
Set In Point
Встановити точку входа
-
+
Set Out Point
Встановити точку вихода
-
+
Reset In Point
Скинути точку входа
-
+
Reset Out Point
Скинути точку вихода
-
+
Clear In/Out Point
Очистити точку входа/вихода
-
+
Add Default Transition
Додати типовий перехід
-
+
Link/Unlink
Зв'язати/Прибрати зв'язок
-
+
Enable/Disable
Увімкнути/Вимкнути
-
+
Nest
Вкласти
-
+
Cu&t
Ви&різати
-
+
Cop&y
С&копіювати
-
+
+
&Paste
В&ставити
-
+
Paste Insert
Уточнити
Вставити з заміною
-
+
Duplicate
Дюблювати
-
+
Delete
Видалити
-
+
Ripple Delete
Видалити зі зміщенням
-
+
Split
Розділити
-
+
Invalid aspect ratio
Некоректні пропорції сторін
-
+
The aspect ratio '%1' is invalid. Please try again.
Пропорції сторін '%1' є некоректними. Будь-ласка, спробуйте ще раз.
-
+
Enter custom aspect ratio
Встановіть інші пропорції сторін
-
+
Enter the aspect ratio to use for the title/action safe area (e.g. 16:9):
Встановіть пропорції сторін для безпечної зони титрів/ефекта (наприклад, 16:9):
@@ -1682,128 +1701,128 @@ Audio Layout: %6
NewSequenceDialog
-
+
Editing "%1"
Редагування "%1"
-
+
New Sequence
Нова послідовність
-
+
Preset:
Уточнити
Профіль:
-
+
Film 4K
Фільм 4К
-
+
TV 4K (Ultra HD/2160p)
TV 4K (Ultra HD/2160p)
-
+
1080p
1080p
-
+
720p
720p
-
+
480p
480p
-
+
360p
- 360p
+ 360p
-
+
240p
240p
-
+
144p
144p
-
+
NTSC (480i)
NTSC (480i)
-
+
PAL (576i)
PAL (576i)
-
+
Custom
Інше
-
+
Video
Відео
-
+
Width:
Ширина:
-
+
Height:
Висота:
-
+
Frame Rate:
Частота кадрів:
-
+
Pixel Aspect Ratio:
Пропорції сторін пікселів:
-
+
Square Pixels (1.0)
Квадратні пікселі (1.0)
-
+
Interlacing:
Черезрядковість:
-
+
None (Progressive)
Ні (прогресивно)
-
+
Audio
Аудіо
-
+
Sample Rate:
Частота дискретизації:
-
+
Name:
Назва:
@@ -1811,67 +1830,67 @@ Audio Layout: %6
OliveGlobal
-
+
Olive Project %1
- Проект Olive %1
+ Olive Проект %1
-
+
Auto-recovery
Автовідновлення
-
+
Olive didn't close properly and an autorecovery file was detected. Would you like to open it?
Olive аварійно завершив роботу і виявив файл автовідновлення. Відкрити його?
-
+
Open Project...
Відкрити проект...
-
+
Missing recent project
Відсутній недавній проект
-
+
The project '%1' no longer exists. Would you like to remove it from the recent projects list?
Проект '%1' більше не існує. Видалити його з історії?
-
+
Save Project As...
Зберегти проект як...
-
+
Unsaved Project
Незбережений проект
-
+
This project has changed since it was last saved. Would you like to save it before closing?
Проект було змінено з момента останнього збереження. Хочете зберегти його перед закриттям?
-
+
No active sequence
Немає активних послідовностей
-
+
Please open the sequence you wish to export.
Будь-ласка, відкрийте послідовність котру хочете експортувати.
-
+
Missing Project File
Відсутній файл проекта
-
+
Specified project '%1' does not exist.
Вказаний проект '%1' не існує.
@@ -1893,272 +1912,315 @@ Audio Layout: %6
Параметри
-
+
Invalid CSS File
Некоректний файл CSS
-
+
CSS file '%1' does not exist.
Файл CSS '%1' не існує.
-
+
Confirm Reset All Shortcuts
Підтвердіть скидання всіх комбінацій клавіш
-
+
Are you sure you wish to reset all keyboard shortcuts to their defaults?
Ви дійсно хочете скинути всі комбінації клавіш до типових значень?
-
+
Import Keyboard Shortcuts
Імпортувати комбінації клавіш
-
-
+
+
Error saving shortcuts
Помилка при збереженні комбінацій клавіш
-
+
Failed to open file for reading
Не вдалося відкрити файл для читання
-
+
Export Keyboard Shortcuts
Експортувати комбінації клавіш
-
+
Export Shortcuts
Експортувати комбінації клавіш
-
+
Shortcuts exported successfully
Комбінації клавіш експортовано
-
+
Failed to open file for writing
Не вдалося відкрити файл для запису
-
+
Browse for CSS file
Обрати файл CSS
-
+
Delete All Previews
Видалити усі мініатюри
-
+
Are you sure you want to delete all previews?
Дійсно видалити усі мініатюри?
-
+
Previews Deleted
Мініатюри видалено
-
+
All previews deleted succesfully. You may have to re-open your current project for changes to take effect.
Уточнити
Усі мініатюри видалено. Можливо знадобится перевідкрити поточний проект для того щоб зміни вступили в силу.
-
+
Language:
Мова:
-
+
+ Image sequence formats:
+ Формати зображень:
+
+
+
+ Thumbnail Resolution:
+ Розмір мініатюр:
+
+
+
+ Waveform Resolution:
+ Деталізація хвильових форм:
+
+
+
+ Delete Previews
+ Видалити мініатюри
+
+
+
+ Use Software Fallbacks When Possible
+ По можливості використовувати програмну реалізацію
+
+
+
+ Default Sequence Settings
+ Типові налаштування послідовності
+
+
+
+ General
+ Загальні
+
+
+
+ Behavior
+ Поведінка
+
+
+
+ Appearance
+ Вигляд
+
+
+
+ Theme
+ Тема
+
+
+
+ Olive Dark (Default)
+ Olive Dark (типово)
+
+
+
+ Olive Light
+ Olive Light
+
+
+
+ Native
+ Уточнити
+ Native
+
+
+
+ Native (Light Icons)
+ Уточнити
+ Native (світлі іконки)
+
+
+
+ Use Native Menu Styling
+ Уточнити
+ Використовувати стиль меню Native
+
+
+
Custom CSS:
Інший CSS:
-
+
Browse
Уточнити
Обрати
-
- Image sequence formats:
- Формати зображень:
-
-
-
- Audio Recording:
- Запис звука:
-
-
-
- Mono
- Моно
-
-
-
- Stereo
- Стерео
-
-
-
+
Effect Textbox Lines:
Кількість рядків у полі вводу тексту:
-
- Thumbnail Resolution:
- Уточнити
- Роздільна здатність мініатюр:
-
-
-
- Waveform Resolution:
- Роздільна здатність хвильових форм:
-
-
-
- Delete Previews
- Видалити мініатюри
-
-
-
- Use Software Fallbacks When Possible
- По можливості використовувати програмну реалізацію
-
-
-
- General
- Загальні
-
-
-
- Behavior
- Поведінка
-
-
-
+
Seeking
- Позіціонування
+ Позиціонування
-
+
Accurate Seeking
Always show the correct frame (visual may pause briefly as correct frame is retrieved)
Точне позиціонування
Завжди показувати правильний кадр (відображення може уповільнюватися)
-
+
Fast Seeking
Seek quickly (may briefly show inaccurate frames when seeking - doesn't affect playback/export)
- Швидке позиціонування (можливе неточне відображення кадрів - не впливає на відтворення)
+ Швидке позиціонування
+Позиціонувати швидко (можливе неточне відображення кадрів - не впливає на відтворення)
-
+
Memory Usage
Використання пам'яті
-
+
Upcoming Frame Queue:
Резервування послідуючих кадрів:
-
-
+
+
frames
кадрів
-
-
+
+
seconds
секунд
-
+
Previous Frame Queue:
Резервування попередніх кадрів:
-
+
Playback
Відтворення
-
+
Output Device:
Пристрій виводу:
-
-
+
+
Default
Типово
-
+
Input Device:
Пристрій вводу:
-
+
Sample Rate:
Частота дискретизації:
-
+
+ Audio Recording:
+ Запис звука:
+
+
+
+ Mono
+ Моно
+
+
+
+ Stereo
+ Стерео
+
+
+
Audio
Аудіо
-
+
Search for action or shortcut
Знайти дію або комбінацію клавіш
-
+
Action
Дія
-
+
Shortcut
Комбінація клавіш
-
+
Import
Імпортувати
-
+
Export
Експортувати
-
+
Reset Selected
Скинути виділення
-
+
Reset All
Скинути все
-
+
Keyboard
Комбінації клавіш
@@ -2166,17 +2228,17 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
PreviewGenerator
-
+
Failed to find any valid video/audio streams
Не вдалося знайти коректні відео/аудіо потоки
-
+
Could not open file - %1
Не вдалося відкрити файл — %1
-
+
Could not find stream information - %1
Не вдалося знайти інформацію потоку — %1
@@ -2199,91 +2261,91 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Послідовність
-
+
Replace '%1'
Замінити '%1'
-
-
+
+
All Files
Усі файли
-
-
+
+
No active sequence
Немає активних послідовностей
-
+
No sequence is active, please open the sequence you want to replace clips from.
Немає активних послідовносте. Відкрийте послідовність в якій хочете замінити кліпи.
-
+
Active sequence selected
Обрано активну послідовність
-
+
You cannot insert a sequence into itself, so no clips of this media would be in this sequence.
Уточнити
- Ви не можете вставити послідовність в саму себе, тож кліпи з цих файлов не можуть бути вставлені в цю послідовність.
+ Ви не можете вставити послідовність в саму себе, тож кліпи з цих файлів не можуть бути вставлені в цю послідовність.
-
+
Rename '%1'
Перейменувати '%1'
-
+
Enter new name:
Введіть нову назву:
-
+
Delete media in use?
Уточнити
Видалити використані у проекті файли?
-
+
The media '%1' is currently used in '%2'. Deleting it will remove all instances in the sequence. Are you sure you want to do this?
Файл '%1' вже використовується у '%2'. Його видалення приведе до видалення усіх його копій у вибраній послідовності. Ви точно цього хочете?
-
+
Skip
Пропустити
-
+
Import a Project
Імпортувати проект
-
+
"%1" is an Olive project file. It will merge with this project. Do you wish to continue?
"%1" є файлом проекту Olive. Його буде об'єднано з поточним проектом. Ви хочете продовжити?
-
+
Image sequence detected
Виявлено послідовність зображень
-
+
The file '%1' appears to be part of an image sequence. Would you like to import it as such?
Схоже що файл '%1' є частиною послідовності зображень. Імпортувати його як є?
-
+
Import media...
Імпортувати медіафайли...
-
+
No sequence is active, please open the sequence you want to delete clips from.
Немає активних послідовносте. Відкрийте послідовність з якої хочете видалити кліпи.
@@ -2303,7 +2365,7 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Dimensions:
- Розмір:
+ Розміри:
@@ -2318,7 +2380,7 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Quarter Resolution (1/4)
- Четверть оригіналу (1/4)
+ Чверть оригіналу (1/4)
@@ -2363,13 +2425,13 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Custom Location
- Інше розміщення
+ Інше місцезнаходження
ProxyGenerator
-
+
Finished generating proxy for "%1"
Завершено створення проксі для "%1"
@@ -2389,7 +2451,6 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Keep the same media in-points
- Уточнити
Зберегти існуючі точки входу
@@ -2430,7 +2491,6 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
You cannot replace footage with a folder.
- Уточнити
Ви не можете замінити відеоряд текою.
@@ -2444,10 +2504,109 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Ви не можете вставити послідовність в саму себе.
+
+ RichTextEffect
+
+
+ Text
+ Текст
+
+
+
+ Padding
+ Уточнити
+ Відступ
+
+
+
+ Position
+ Позиція
+
+
+
+ Vertical Align:
+ Верктикальне вирівнювання:
+
+
+
+ Top
+ Вгорі
+
+
+
+ Center
+ По центру
+
+
+
+ Bottom
+ Внизу
+
+
+
+ Auto-Scroll
+ Автопрокручування
+
+
+
+ Off
+ Вимкнено
+
+
+
+ Up
+ Вгору
+
+
+
+ Down
+ Вниз
+
+
+
+ Left
+ Вліво
+
+
+
+ Right
+ Вправо
+
+
+
+ Shadow
+ Тінь
+
+
+
+ Shadow Color
+ Колір тіні
+
+
+
+ Shadow Angle
+ Кут падіння тіні
+
+
+
+ Shadow Distance
+ Відстань до тіні
+
+
+
+ Shadow Softness
+ Розсіювання тіні
+
+
+
+ Shadow Opacity
+ Непрозорість тіні
+
+
Sequence
-
+
%1 (copy)
%1 (копія)
@@ -2455,7 +2614,7 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
ShakeEffect
-
+
Intensity
Інтенсивність
@@ -2465,7 +2624,7 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Обертання
-
+
Frequency
Частота
@@ -2473,7 +2632,7 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
SolidEffect
-
+
Type
Тип
@@ -2498,12 +2657,12 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Непрозорість
-
+
Color
Колір
-
+
Checkerboard Size
Розмір клітинок
@@ -2521,135 +2680,135 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Створити
-
+
View
Вигляд
-
+
Tree View
У вигляді таблиці
-
+
Icon View
У вигляді мініатюр
-
+
Show Toolbar
Показувати панель
-
+
Show Sequences
Показувати послідовності
-
+
Replace/Relink Media
Уточнити
Замінити/Перезв'язати файли
-
+
Reveal in Explorer
Відкрити у Explorer
-
+
Reveal in Finder
Відкрити у Finder
-
+
Reveal in File Manager
Відкрити у менеджері файлів
-
+
Replace Clips Using This Media
Уточнити
Замінити кліпи з цими файлами
-
+
Create Sequence With This Media
Створити послідовність з цими файлами
-
+
Duplicate
Дублювати
-
+
Delete All Clips Using This Media
Уточнити
Видалити усі кліпи з цими файлами
-
+
Proxy
Проксі
-
+
Generating proxy: %1% complete
Створення проксі: завершено на %1%
-
+
Create/Modify Proxy
Створити/Змінити проксі
-
+
Create Proxy
Створити проксі
-
+
Modify Proxy
Змінити проксі
-
+
Restore Original
Відновити оригінал
-
+
Delete
Видалити
-
+
Preview in Media Viewer
Переглянути у Переглядачі медіа файлів
-
+
Properties...
Властивості...
-
+
Replace Media
Замінити медіафайли
-
+
You dropped a file onto '%1'. Would you like to replace it with the dropped file?
Ви перетягнули файл на '%1'. Ви хочете замінити на цей файл?
-
+
Delete proxy
Видалити проксі
-
+
Would you like to delete the proxy file "%1" as well?
Заразом видалити проксі-файл "%1"?
@@ -2657,45 +2816,37 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
SpeedDialog
-
- Dialog
- Діалог
-
-
-
-
- Speed:
- Швидкість:
-
-
-
-
- Frame Rate:
- Частота кадрів:
-
-
-
-
- Duration:
- Тривалість:
-
-
-
+
Speed/Duration
Швидкість/Тривалість
-
+
+ Speed:
+ Швидкість:
+
+
+
+ Frame Rate:
+ Частота кадрів:
+
+
+
+ Duration:
+ Тривалість:
+
+
+
Reverse
Реверс
-
+
Maintain Audio Pitch
Зберегти висоту тона
-
+
Ripple Changes
Змінювати зі зміщенням
@@ -2703,20 +2854,82 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
TextEditDialog
-
+
Edit Text
Змінити текст
+
+
+ Thin
+ Уточнити
+ Thin
+
+
+
+ Extra Light
+ Уточнити
+ Extra Light
+
+
+
+ Light
+ Уточнити
+ Light
+
+
+
+ Normal
+ Уточнити
+ Normal
+
+
+
+ Medium
+ Уточнити
+ Medium
+
+
+
+ Demi Bold
+ Уточнити
+ Demi Bold
+
+
+
+ Bold
+ Уточнити
+ Bold
+
+
+
+ Extra Bold
+ Уточнити
+ Extra Bold
+
+
+
+ Black
+ Уточнити
+ Black
+
+
+
+ TextEditEx
+
+
+ &Edit Text
+ &Редагувати Текст
+
TextEffect
-
+
Text
Текст
-
+
Font
Шрифт
@@ -2726,151 +2939,156 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Розмір
-
+
Color
Колір
-
+
Alignment
Вирівнювання
-
+
Left
Ліворуч
-
-
+
+
Center
По центру
-
+
Right
Праворуч
-
+
Justify
По ширині
-
+
Top
Вгорі
-
+
Bottom
Внизу
-
+
Word Wrap
- Перенесення рядка
+ Перенесення слів
-
+
+ Padding
+ Відступ
+
+
+
+ Position
+ Позиція
+
+
+
Outline
Контури
-
+
Outline Color
Колір контурів
-
+
Outline Width
Ширина контурів
-
+
Shadow
Тінь
-
+
Shadow Color
Колір тіні
-
+
Shadow Angle
Кут падіння тіні
-
+
Shadow Distance
Відстань до тіні
-
+
Shadow Softness
Розсіювання тіні
-
+
Shadow Opacity
Непрозорість тіні
-
+
Sample Text
Зразок тексту
-
-
- &Edit Text
- &Змінити текст
-
TimecodeEffect
-
+
Timecode
Тайм-код
-
+
Sequence
Послідовність
-
+
Media
Файл
-
+
Scale
Масштаб
-
+
Color
Колір
-
+
Background Color
Колір фону
-
+
Background Opacity
Непрозорість фону
-
+
Offset
Зміщення
-
+
Prepend
Префікс
@@ -2878,152 +3096,152 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
Timeline
-
+
Pointer Tool
Вказівник
-
+
Edit Tool
Виділення
-
+
Ripple Tool
Монтаж зі зміщенням
-
+
Razor Tool
Підрізання
-
+
Slip Tool
Прокручування зі зміщенням
-
+
Slide Tool
Прокручування
-
+
Hand Tool
Навігація
-
+
Transition Tool
Перехід
-
+
Snapping
Прилипання
-
+
Zoom In
Наблизити
-
+
Zoom Out
Віддалити
-
+
Record audio
Запис звука
-
+
Add title, solid, bars, etc.
- Додати титри, заливку, тестову таблицю і т.п.
+ Додати титри, заливку, тестову таблицю, і т.п.
-
+
Nested Sequence
Вкладена послідовність
-
+
Effect already exists
- Ефект вже додано
-
-
-
- Clip '%1' already contains a '%2' effect. Would you like to replace it with the pasted one or add it as a separate effect?
- Кліп '%1' вже містить ефект '%2'. Хочете замінити його на вставлюваний чи додати цей ефект як окремий?
+ Ефект уже додано
+ Clip '%1' already contains a '%2' effect. Would you like to replace it with the pasted one or add it as a separate effect?
+ Кліп '%1' уже містить ефект '%2'. Хочете замінити його на вставлюваний чи додати цей ефект як окремий?
+
+
+
Add
Додати
-
+
Replace
Замінити
-
+
Skip
Пропустити
-
+
Do this for all conflicts found
Застосувати для всіх конфліктів
-
+
Title...
Титри...
-
+
Solid Color...
Суцільна заливка...
-
+
Bars...
Тестова таблиця...
-
+
Tone...
Звуковой сигнал…
-
+
Noise...
Шум...
-
+
Unsaved Project
Незбережений проект
-
+
You must save this project before you can record audio in it.
Перед записом звука необхідно зберегти проект.
-
+
Click on the timeline where you want to start recording (drag to limit the recording to a certain timeframe)
Клікніть на монтажному столі у точці, куди хочете почати запис звука (перетягніть курсор після кліка щоб відразу встановити тривалість запису)
-
+
Timeline:
Монтажний стіл:
-
+
(none)
(пусто)
@@ -3031,7 +3249,7 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
TimelineHeader
-
+
Center Timecodes
Центрувати тайм-код
@@ -3039,62 +3257,49 @@ Seek quickly (may briefly show inaccurate frames when seeking - doesn't aff
TimelineWidget
-
+
&Undo
&Відмінити
-
+
&Redo
По&вернути
-
-
- C&ut
- Ви&різати
-
-
-
- Cop&y
- С&копіювати
-
- &Paste
- В&ставити
+ R&ipple Delete Empty Space
+ Уточнити
+ Видалити зі зміщенням порожнє &місце
-
- R&ipple Delete
- Ви&далити зі зміщенням
-
-
-
+
Sequence Settings
- Параметри послідовності
+ Налаштування послідовності
-
+
&Speed/Duration
&Швидкість/Тривалість
-
+
Auto-s&cale
Авто&масштабування
-
+
&Reveal in Project
- &Показати в проекті
+ Уточнити
+ &Показати у проекті
-
+
Properties
Властивості
-
+
%1
Start: %2
End: %3
@@ -3105,43 +3310,42 @@ Duration: %4
Тривалість: %4
-
+
Error
Помилка
-
+
Couldn't locate media wrapper for sequence.
- Уточнити
Не вдається визначити обробник медіа для послідовності.
-
+
Title
Титри
-
+
Solid Color
Суцільна заливка
-
+
Bars
Тестова таблиця
-
+
Tone
Звуковой сигнал
-
+
Noise
Шум
-
+
Duration:
Тривалість:
@@ -3149,22 +3353,27 @@ Duration: %4
ToneEffect
-
+
Type
Тип
+
+
+ Sine
+ Синусоїда
+
Frequency
Частота
-
+
Amount
Кількість
-
+
Mix
Змішування
@@ -3177,162 +3386,45 @@ Duration: %4
Позиція
-
+
Scale
Масштаб
-
+
Uniform Scale
- Зберігати масштаб
+ Пропорційний масштаб
-
+
Rotation
Обертання
-
+
Anchor Point
Якірна точка
-
+
Opacity
Непрозорість
-
+
Blend Mode
Режим змішування
-
+
Normal
Звичайний
-
-
- Darken
- Уточнити
- Заміна темним
-
-
-
- Multiply
- Множення
-
-
-
- Color Burn
- Затемнення основи
-
-
-
- Linear Burn
- Лінійне затемнення
-
-
-
- Lighten
- Заміна світлим
-
-
-
- Screen
- Екран
-
-
-
- Color Dodge
- Висвітлення основи
-
-
-
- Linear Dodge (Add)
- Лінійне освітлення (додати)
-
-
-
- Overlay
- Перекриття
-
-
-
- Soft Light
- Розсіяне світло
-
-
-
- Hard Light
- Напрямлене світло
-
-
-
- Vivid Light
- Яскраве світло
-
-
-
- Linear Light
- Лінійне світло
-
-
-
- Pin Light
- Точкове світло
-
-
-
- Hard Mix
- Жорстке зміщення
-
-
-
- Difference
- Різниця
-
-
-
- Exclusion
- Виключення
-
-
-
- Reflect
- Відзеркалення
-
-
-
- Substract
- Віднімання
-
-
-
- Average
- Середнє
-
-
-
- Glow
- Свічення
-
-
-
- Negation
- Уточнити
- Відкидання
-
-
-
- Phoenix
- Фенікс
-
Transition
-
+
Length
Тривалість
@@ -3390,43 +3482,43 @@ Duration: %4
Магічний номер плагіна некоректний
-
+
+ VST Plugin
+ Плагін VST
+
+
+
Plugin
Плагін
-
+
Interface
Інтерфейс
-
+
Show
Показати
-
-
- VST Plugin
- Плагін VST
-
Viewer
-
+
+ (none)
+ (пусто)
+
+
+
Sequence Viewer
Переглядач послідовності
-
+
Media Viewer
Переглядач медіа файлів
-
-
- (none)
- (пусто)
-
ViewerWidget
@@ -3441,47 +3533,47 @@ Duration: %4
Повноекранний режим
-
+
Disable
Вимкнути
-
+
Screen %1: %2x%3
Екран %1: %2x%3
-
+
Zoom
Масштаб
-
+
Fit
Підігнати
-
+
Custom
Інше
-
+
Close Media
Закрити файл
-
+
Save Frame
Зберегти кадр
-
+
Viewer Zoom
Масштаб перегляду
-
+
Set Custom Zoom Value:
Інше значення масштаба:
@@ -3497,12 +3589,12 @@ Duration: %4
VoidEffect
-
+
(unknown)
(невідомо)
-
+
Missing Effect
Відсутній ефект
@@ -3518,12 +3610,12 @@ Duration: %4
transition
-
+
Invalid transition
Некоректний перехід
-
+
No candidate for transition '%1'. This transition may be corrupt. Try reinstalling it or Olive.
Немає кандидата для переходу '%1'. Цей перехід може бути некоректний. Спробуйте перевстановити його або ж Olive.
diff --git a/ui/columnedgridlayout.cpp b/ui/columnedgridlayout.cpp
new file mode 100644
index 000000000..6404e6c96
--- /dev/null
+++ b/ui/columnedgridlayout.cpp
@@ -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;
+}
diff --git a/ui/columnedgridlayout.h b/ui/columnedgridlayout.h
new file mode 100644
index 000000000..d62eaa6d7
--- /dev/null
+++ b/ui/columnedgridlayout.h
@@ -0,0 +1,27 @@
+#ifndef COLUMNEDGRIDLAYOUT_H
+#define COLUMNEDGRIDLAYOUT_H
+
+#include
+
+/**
+ * @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
diff --git a/ui/icons.cpp b/ui/icons.cpp
index 28b88835d..4e2342cfd 100644
--- a/ui/icons.cpp
+++ b/ui/icons.cpp
@@ -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);
diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp
index 8da6a4ee8..74bf60fd7 100644
--- a/ui/mainwindow.cpp
+++ b/ui/mainwindow.cpp
@@ -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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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);
diff --git a/ui/mainwindow.h b/ui/mainwindow.h
index 749088190..7ffd76a32 100644
--- a/ui/mainwindow.h
+++ b/ui/mainwindow.h
@@ -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_;
diff --git a/ui/sourceiconview.cpp b/ui/sourceiconview.cpp
index 3016ad774..140f54931 100644
--- a/ui/sourceiconview.cpp
+++ b/ui/sourceiconview.cpp
@@ -21,32 +21,38 @@
#include "sourceiconview.h"
#include
+#include
#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();
+ 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();
+ 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);
+
}
}
diff --git a/ui/sourceiconview.h b/ui/sourceiconview.h
index cd8acbe84..bbd7e3236 100644
--- a/ui/sourceiconview.h
+++ b/ui/sourceiconview.h
@@ -22,25 +22,40 @@
#define SOURCEICONVIEW_H
#include
+#include
+#include
+
+#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
diff --git a/ui/sourcetable.cpp b/ui/sourcetable.cpp
index c173182b9..d3f74f449 100644
--- a/ui/sourcetable.cpp
+++ b/ui/sourcetable.cpp
@@ -45,7 +45,7 @@
#include
#include
-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());
}
diff --git a/ui/sourcetable.h b/ui/sourcetable.h
index 8668036a3..aeea9dda3 100644
--- a/ui/sourcetable.h
+++ b/ui/sourcetable.h
@@ -25,6 +25,8 @@
#include
#include
+#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
diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp
index d3e73113c..7bfa44f96 100644
--- a/ui/timelinewidget.cpp
+++ b/ui/timelinewidget.cpp
@@ -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;itrack() < 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_list;
+ QVector 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;isource() == 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(event->mimeData()->text().toInt())));
import_init = true;
+
}
}
diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp
index ad96a758d..81099abf3 100644
--- a/ui/viewerwidget.cpp
+++ b/ui/viewerwidget.cpp
@@ -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) {
diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h
index 863446562..d5cb80bc2 100644
--- a/ui/viewerwidget.h
+++ b/ui/viewerwidget.h
@@ -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();
diff --git a/ui/viewerwindow.cpp b/ui/viewerwindow.cpp
index f6148fc61..a6c5308eb 100644
--- a/ui/viewerwindow.cpp
+++ b/ui/viewerwindow.cpp
@@ -31,6 +31,7 @@
#include
#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& shortcuts, QMenu* menu) {
+ QList menu_action = menu->actions();
+ for (int i=0;imenu() != 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 menubar_actions = olive::MainWindow->menuBar()->actions();
+ for (int i=0;imenu());
+ }
+}
+
void ViewerWindow::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
hide();
diff --git a/ui/viewerwindow.h b/ui/viewerwindow.h
index ad46fee40..0da945bee 100644
--- a/ui/viewerwindow.h
+++ b/ui/viewerwindow.h
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#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& shortcuts, QMenu* menu);
+ QVector shortcuts_;
+
// exit full screen message
QTimer fullscreen_msg_timer_;
bool show_fullscreen_msg_;