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/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index a9b8d2d6d..74d262b45 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -20,13 +20,6 @@ #include "preferencesdialog.h" -#include "global/global.h" -#include "global/config.h" -#include "global/path.h" -#include "rendering/audio.h" -#include "panels/panels.h" -#include "ui/mainwindow.h" - #include #include #include @@ -51,6 +44,14 @@ #include #include +#include "global/global.h" +#include "global/config.h" +#include "global/path.h" +#include "rendering/audio.h" +#include "panels/panels.h" +#include "ui/columnedgridlayout.h" +#include "ui/mainwindow.h" + KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a) : QKeySequenceEdit(parent), action(a) { setKeySequence(action->shortcut()); @@ -142,6 +143,15 @@ void PreferencesDialog::delete_previews(char type) { } } +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(); @@ -186,14 +196,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() || olive::CurrentConfig.css_path != custom_css_fn->text() -#ifdef Q_OS_WIN32 - || olive::CurrentConfig.use_native_menu_styling != native_menus->isChecked() -#endif || 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 @@ -241,22 +257,19 @@ void PreferencesDialog::accept() { 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.style = static_cast(ui_style->currentData().toInt()); -#ifdef Q_OS_WIN - olive::CurrentConfig.use_native_menu_styling = native_menus->isChecked(); -#endif + for (int i=0;iisChecked(); + } - olive::CurrentConfig.auto_seek_to_beginning = auto_seek_to_beginning->isChecked(); + olive::CurrentConfig.style = static_cast(ui_style->currentData().toInt()); // Check if the thumbnail or waveform icon if (olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value() @@ -539,9 +552,8 @@ 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++; @@ -556,15 +568,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); - auto_seek_to_beginning = new QCheckBox(tr("Automatically Seek to the Beginning When Playing at the End of a Sequence")); - auto_seek_to_beginning->setChecked(olive::CurrentConfig.auto_seek_to_beginning); - behavior_tab_layout->addWidget(auto_seek_to_beginning); + 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); @@ -590,8 +659,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); appearance_layout->addWidget(native_menus, row, 0, 1, 3); row++; diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index fa87c457b..38efcdf7e 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -208,11 +208,6 @@ private: */ QSpinBox* effect_textbox_lines_field; - /** - * @brief UI widget for enabling/disabling software fallbacks - */ - QCheckBox* use_software_fallbacks_checkbox; - /** * @brief UI widget for selecting the output audio device */ @@ -243,26 +238,10 @@ private: */ QSpinBox* waveform_res_spinbox; - /** - * @brief UI widget for enabling/disabling default effects - */ - QCheckBox* add_default_effects_to_clips; - - /** - * @brief UI widget for enabling/disabling Config::auto_seek_to_beginning - */ - QCheckBox* auto_seek_to_beginning; - /** * @brief UI widget for selecting the current UI style */ QComboBox* ui_style; -#ifdef Q_OS_WIN - /** - * @brief UI widget for forcing native menu styling on Windows - */ - QCheckBox* native_menus; -#endif /** * @brief List of keyboard shortcut actions that can be triggered (links with key_shortcut_items and @@ -281,6 +260,45 @@ private: * 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; }; /** diff --git a/global/config.cpp b/global/config.cpp index 667b178a9..cc2e2b441 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -57,7 +57,7 @@ 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), diff --git a/global/global.cpp b/global/global.cpp index 8980721d9..048a57e9b 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" @@ -301,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(); } @@ -377,6 +373,22 @@ void OliveGlobal::OpenProjectWorker(const QString& fn, bool 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) { @@ -426,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 17c88f535..ac05b21bf 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. */ @@ -340,6 +345,18 @@ private: */ void OpenProjectWorker(const 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 * diff --git a/olive.pro b/olive.pro index 2b4b15559..9adf6c8b9 100644 --- a/olive.pro +++ b/olive.pro @@ -174,7 +174,9 @@ SOURCES += \ effects/internal/richtexteffect.cpp \ ui/blur.cpp \ ui/menu.cpp \ - timeline/mediaimportdata.cpp + timeline/mediaimportdata.cpp \ + dialogs/autocutsilencedialog.cpp \ + ui/columnedgridlayout.cpp HEADERS += \ ui/mainwindow.h \ @@ -302,7 +304,9 @@ HEADERS += \ effects/internal/richtexteffect.h \ ui/blur.h \ ui/menu.h \ - timeline/mediaimportdata.h + timeline/mediaimportdata.h \ + dialogs/autocutsilencedialog.h \ + ui/columnedgridlayout.h FORMS += diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 75caf3148..977de2aa8 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) { diff --git a/panels/timeline.h b/panels/timeline.h index e1b103e6c..93260f377 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -117,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); 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/mainwindow.cpp b/ui/mainwindow.cpp index f7955e319..4f49058fd 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -760,61 +760,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(); @@ -952,21 +898,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")); @@ -1173,6 +1105,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); @@ -1208,22 +1142,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 e06987228..e38410291 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/timelinewidget.cpp b/ui/timelinewidget.cpp index 30b07e443..768a1a94f 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());