From 1fded74767fa8daf3e5efee1d6248ba6ebaaabf7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 22 Jul 2018 19:57:03 +0100 Subject: [PATCH] workarea in/out added #108 --- dialogs/exportdialog.cpp | 11 ++++- dialogs/exportdialog.ui | 29 ++++++++++- io/exportthread.cpp | 12 ++--- io/exportthread.h | 2 + mainwindow.cpp | 44 ++++++++++++++--- mainwindow.h | 10 ++++ mainwindow.ui | 40 +++++++++++++++ panels/project.cpp | 17 +++++-- panels/project.h | 2 +- panels/timeline.cpp | 40 ++++++++++++--- panels/timeline.h | 9 ++-- project/sequence.cpp | 6 ++- project/sequence.h | 4 ++ project/undo.cpp | 41 +++++++++++++++- project/undo.h | 11 +++++ ui/timelineheader.cpp | 102 ++++++++++++++++++++++++++++++++------- ui/timelineheader.h | 8 +++ ui/timelinewidget.cpp | 2 +- 18 files changed, 335 insertions(+), 55 deletions(-) diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index 59bab8c06..80f444a21 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -49,6 +49,9 @@ ExportDialog::ExportDialog(QWidget *parent) : { ui->setupUi(this); + ui->rangeCombobox->setCurrentIndex(0); + ui->rangeCombobox->setEnabled(sequence->using_workarea); + format_strings.resize(FORMAT_SIZE); format_strings[FORMAT_3GPP] = "3GPP"; format_strings[FORMAT_AIFF] = "AIFF"; @@ -454,7 +457,6 @@ void ExportDialog::on_pushButton_clicked() } } - et = new ExportThread(); et->surface.create(); @@ -487,6 +489,13 @@ void ExportDialog::on_pushButton_clicked() et->audio_bitrate = ui->audiobitrateSpinbox->value(); } + et->start_frame = 0; + et->end_frame = sequence->getEndFrame(); // entire sequence + if (ui->rangeCombobox->currentIndex() == 1) { + et->start_frame = qMax(sequence->workarea_in, et->start_frame); + et->end_frame = qMin(sequence->workarea_out, et->end_frame); + } + et->ed = this; cancelled = false; diff --git a/dialogs/exportdialog.ui b/dialogs/exportdialog.ui index 3434cb213..df0d18d2f 100644 --- a/dialogs/exportdialog.ui +++ b/dialogs/exportdialog.ui @@ -6,8 +6,8 @@ 0 0 - 299 - 372 + 557 + 659 @@ -28,6 +28,31 @@ + + + + + + Range: + + + + + + + + Entire Sequence + + + + + In to Out + + + + + + diff --git a/io/exportthread.cpp b/io/exportthread.cpp index 0ba0ac752..05b069942 100644 --- a/io/exportthread.cpp +++ b/io/exportthread.cpp @@ -53,10 +53,6 @@ void ExportThread::run() { panel_timeline->pause(); // av_log_set_level(AV_LOG_DEBUG); - // TODO make customizable - long start = 0; - long end = sequence->getEndFrame(); - if (!panel_viewer->viewer_widget->context()->makeCurrent(&surface)) { qDebug() << "[ERROR] Make current failed"; ed->export_error = "could not make OpenGL context current"; @@ -283,7 +279,7 @@ void ExportThread::run() { qDebug() << "[ERROR] Could not write output file header." << ret; ed->export_error = "could not write output file header (" + QString::number(ret) + ")"; } else { - panel_timeline->seek(start); + panel_timeline->seek(start_frame); QOpenGLFramebufferObject fbo(video_width, video_height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE); fbo.bind(); @@ -296,10 +292,10 @@ void ExportThread::run() { long file_audio_samples = 0; - while (panel_timeline->playhead < end && !fail) { + while (panel_timeline->playhead < end_frame && !fail) { panel_viewer->viewer_widget->paintGL(); - double timecode_secs = (double) panel_timeline->playhead / sequence->frame_rate; + double timecode_secs = (double) (panel_timeline->playhead-start_frame) / sequence->frame_rate; if (video_enabled) { // get image from opengl glReadPixels(0, 0, video_width, video_height, GL_RGBA, GL_UNSIGNED_BYTE, video_frame->data[0]); @@ -338,7 +334,7 @@ void ExportThread::run() { file_audio_samples += swr_frame->nb_samples; } } - emit progress_changed(((float) panel_timeline->playhead / (float) end) * 100); + emit progress_changed(((double) (panel_timeline->playhead-start_frame) / (double) (end_frame-start_frame)) * 100); panel_timeline->playhead++; } diff --git a/io/exportthread.h b/io/exportthread.h index bcd516030..31f768af2 100644 --- a/io/exportthread.h +++ b/io/exportthread.h @@ -28,6 +28,8 @@ public: int audio_codec; int audio_sampling_rate; int audio_bitrate; + long start_frame; + long end_frame; QOffscreenSurface surface; diff --git a/mainwindow.cpp b/mainwindow.cpp index ed91a4058..556edaf7c 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -357,11 +357,9 @@ void MainWindow::on_actionProject_triggered() { if (can_close_project()) { panel_effect_controls->clear_effects(true); - panel_project->new_project(); undo_stack.clear(); - panel_timeline->redraw_all_clips(false); project_url.clear(); - project_changed = false; + panel_project->new_project(); } } @@ -617,15 +615,49 @@ void MainWindow::on_actionScroll_Wheel_Zooms_triggered() void MainWindow::on_actionLink_Unlink_triggered() { - panel_timeline->toggle_links(); + if (panel_timeline->focused()) panel_timeline->toggle_links(); } void MainWindow::on_actionRipple_To_In_Point_triggered() { - panel_timeline->ripple_to_in_point(true); + if (panel_timeline->focused()) panel_timeline->ripple_to_in_point(true); } void MainWindow::on_actionRipple_to_Out_Point_triggered() { - panel_timeline->ripple_to_in_point(false); + if (panel_timeline->focused()) panel_timeline->ripple_to_in_point(false); +} + +void MainWindow::on_actionSet_In_Point_triggered() +{ + if (panel_timeline->focused()) panel_timeline->set_in_point(); +} + +void MainWindow::on_actionSet_Out_Point_triggered() +{ + if (panel_timeline->focused()) panel_timeline->set_out_point(); +} + +void MainWindow::on_actionClear_In_Out_triggered() +{ + if (panel_timeline->focused() && sequence->using_workarea) { + TimelineAction* ta = new TimelineAction(); + ta->set_in_out(sequence, false, 0, 0); + undo_stack.push(ta); + panel_timeline->repaint_timeline(); + } +} + +void MainWindow::on_actionDelete_In_Out_triggered() +{ + if (panel_timeline->focused()) { + panel_timeline->delete_in_out(false); + } +} + +void MainWindow::on_actionRipple_Delete_In_Out_triggered() +{ + if (panel_timeline->focused()) { + panel_timeline->delete_in_out(true); + } } diff --git a/mainwindow.h b/mainwindow.h index 62f6d0f1d..7b97d60a3 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -158,6 +158,16 @@ private slots: void on_actionRipple_to_Out_Point_triggered(); + void on_actionSet_In_Point_triggered(); + + void on_actionSet_Out_Point_triggered(); + + void on_actionClear_In_Out_triggered(); + + void on_actionDelete_In_Out_triggered(); + + void on_actionRipple_Delete_In_Out_triggered(); + private: Ui::MainWindow *ui; void setup_layout(); diff --git a/mainwindow.ui b/mainwindow.ui index 1a7cbd7c6..7e0f78bf4 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -79,6 +79,12 @@ + + + + + + @@ -639,6 +645,40 @@ W + + + Set In Point + + + I + + + + + Set Out Point + + + O + + + + + Clear In/Out + + + G + + + + + Delete In/Out + + + + + Ripple Delete In/Out + + diff --git a/panels/project.cpp b/panels/project.cpp index 37774aec3..88c9879dc 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -406,9 +406,9 @@ void Project::clear() { void Project::new_project() { // clear existing project - set_sequence(NULL); clear(); project_changed = false; + set_sequence(NULL); } QTreeWidgetItem* Project::find_loaded_folder_by_id(int id) { @@ -579,6 +579,11 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { s->audio_layout = attr.value().toInt(); } else if (attr.name() == "open") { open_sequence = true; + } else if (attr.name() == "workareaIn") { + s->using_workarea = true; + s->workarea_in = attr.value().toLong(); + } else if (attr.name() == "workareaOut") { + s->workarea_out = attr.value().toLong(); } } @@ -596,11 +601,11 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { } else if (attr.name() == "id") { c->load_id = attr.value().toInt(); } else if (attr.name() == "clipin") { - c->clip_in = attr.value().toInt(); + c->clip_in = attr.value().toLong(); } else if (attr.name() == "in") { - c->timeline_in = attr.value().toInt(); + c->timeline_in = attr.value().toLong(); } else if (attr.name() == "out") { - c->timeline_out = attr.value().toInt(); + c->timeline_out = attr.value().toLong(); } else if (attr.name() == "track") { c->track = attr.value().toInt(); } else if (attr.name() == "r") { @@ -842,6 +847,10 @@ void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int if (s == sequence) { stream.writeAttribute("open", "1"); } + if (s->using_workarea) { + stream.writeAttribute("workareaIn", QString::number(s->workarea_in)); + stream.writeAttribute("workareaOut", QString::number(s->workarea_out)); + } for (int j=0;jclip_count();j++) { Clip* c = s->get_clip(j); if (c != NULL) { diff --git a/panels/project.h b/panels/project.h index 95a881c8b..13d49f7f0 100644 --- a/panels/project.h +++ b/panels/project.h @@ -15,7 +15,7 @@ class QXmlStreamWriter; class QXmlStreamReader; class QFile; -#define SAVE_VERSION "180719" +#define SAVE_VERSION "180722" #define MEDIA_TYPE_FOOTAGE 0 #define MEDIA_TYPE_SEQUENCE 1 diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 0a86edf1e..bfb763a72 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -35,7 +35,6 @@ Timeline::Timeline(QWidget *parent) : edit_tool_selects_links(false), edit_tool_also_seeks(false), select_also_seeks(false), - using_workarea(false), paste_seeks(true), snapping(true), last_frame(0), @@ -206,6 +205,14 @@ int Timeline::calculate_track_height(int track, int value) { return vector.at(index); } +void Timeline::set_in_point() { + ui->headers->set_in_point(playhead); +} + +void Timeline::set_out_point() { + ui->headers->set_out_point(playhead); +} + void Timeline::update_sequence() { bool null_sequence = (sequence == NULL); @@ -216,16 +223,14 @@ void Timeline::update_sequence() { ui->pushButton_4->setEnabled(!null_sequence); ui->pushButton_5->setEnabled(!null_sequence); ui->headers->setEnabled(!null_sequence); -// ui->video_area->setEnabled(!null_sequence); -// ui->audio_area->setEnabled(!null_sequence); if (null_sequence) { setWindowTitle("Timeline: "); } else { setWindowTitle("Timeline: " + sequence->name); + reset_all_audio(); redraw_all_clips(false); playback_updater.setInterval(qFloor(1000 / sequence->frame_rate)); - reset_all_audio(); } } @@ -279,6 +284,27 @@ void Timeline::select_all() { repaint_timeline(); } +void Timeline::delete_in_out(bool ripple) { + if (sequence != NULL && sequence->using_workarea) { + QVector areas; + int video_tracks = 0, audio_tracks = 0; + sequence->get_track_limits(&video_tracks, &audio_tracks); + for (int i=video_tracks;i<=audio_tracks;i++) { + Selection s; + s.in = sequence->workarea_in; + s.out = sequence->workarea_out; + s.track = i; + areas.append(s); + } + TimelineAction* ta = new TimelineAction(); + panel_timeline->delete_areas_and_relink(ta, areas); + if (ripple) ta->ripple(sequence, sequence->workarea_in, sequence->workarea_in - sequence->workarea_out); + ta->set_in_out(sequence, false, 0, 0); + undo_stack.push(ta); + redraw_all_clips(true); + } +} + void Timeline::delete_selection(bool ripple_delete) { if (selections.size() > 0) { panel_effect_controls->clear_effects(true); @@ -301,7 +327,6 @@ void Timeline::delete_selection(bool ripple_delete) { // it feels a little more intuitive with this here ripple_point++; - QVector ignore; bool can_ripple = true; for (int i=0;iclip_count();i++) { Clip* c = sequence->get_clip(i); @@ -329,7 +354,7 @@ void Timeline::delete_selection(bool ripple_delete) { } } } - if (can_ripple) ta->ripple(sequence, ripple_point, -ripple_length, ignore); + if (can_ripple) ta->ripple(sequence, ripple_point, -ripple_length); } selections.clear(); @@ -743,10 +768,9 @@ void Timeline::ripple_to_in_point(bool in) { } // trim and move clips around the in point - QVector ignore; TimelineAction* ta = new TimelineAction(); delete_areas_and_relink(ta, areas); - ta->ripple(sequence, in_point, (in) ? (in_point - playhead) : (playhead - in_point), ignore); + ta->ripple(sequence, in_point, (in) ? (in_point - playhead) : (playhead - in_point)); undo_stack.push(ta); redraw_all_clips(true); diff --git a/panels/timeline.h b/panels/timeline.h index 7310dc753..abf143fb1 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -85,6 +85,9 @@ public: bool has_clip_been_split(int c); void toggle_links(); void ripple_to_in_point(bool in); + void set_in_point(); + void set_out_point(); + void delete_in_out(bool ripple); int get_snap_range(); int getScreenPointFromFrame(long frame); @@ -93,11 +96,7 @@ public: bool snap_to_point(long point, long* l); void snap_to_clip(long* l, bool playhead_inclusive); - long playhead; - - bool using_workarea; - long workarea_in; - long workarea_out; + long playhead; // playback functions void go_to_start(); diff --git a/project/sequence.cpp b/project/sequence.cpp index 788a35a1b..eefeb643d 100644 --- a/project/sequence.cpp +++ b/project/sequence.cpp @@ -5,7 +5,11 @@ #include -Sequence::Sequence() {} +Sequence::Sequence() : + using_workarea(false), + workarea_in(0), + workarea_out(0) +{} Sequence::~Sequence() { // dealloc all clips diff --git a/project/sequence.h b/project/sequence.h index 408b68e10..0381901d4 100644 --- a/project/sequence.h +++ b/project/sequence.h @@ -23,6 +23,10 @@ public: double frame_rate; int audio_frequency; int audio_layout; + + bool using_workarea; + long workarea_in; + long workarea_out; private: QVector clips; }; diff --git a/project/undo.cpp b/project/undo.cpp index 14656b451..d0bb1c8b9 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -24,7 +24,12 @@ QUndoStack undo_stack; #define TA_ADD_CLIP_IN 7 #define TA_ADD_TRACK 8 -TimelineAction::TimelineAction() : done(false), change_seq(false), ripple_enabled(false) {} +TimelineAction::TimelineAction() : + done(false), + change_seq(false), + ripple_enabled(false), + change_in_out(false) +{} TimelineAction::~TimelineAction() { for (int i=0;i i; + ripple(s, point, length, i); +} + +void TimelineAction::set_in_out(Sequence* s, bool enabled, long in, long out) { + change_in_out = true; + change_in_out_sequence = s; + + old_in_out_enabled = s->using_workarea; + old_sequence_in = s->workarea_in; + old_sequence_out = s->workarea_out; + + new_in_out_enabled = enabled; + new_sequence_in = in; + new_sequence_out = out; + +} + void TimelineAction::ripple(Sequence* s, long point, long length, QVector& ignore) { ripple_enabled = true; ripple_point = point; @@ -220,6 +244,14 @@ void TimelineAction::undo() { } } + // un-change workarea + if (change_in_out) { + change_in_out_sequence->using_workarea = old_in_out_enabled; + change_in_out_sequence->workarea_in = old_sequence_in; + change_in_out_sequence->workarea_out = old_sequence_out; + } + + // un-change current active sequence if (change_seq) { set_sequence(old_seq); } @@ -388,6 +420,13 @@ void TimelineAction::redo() { } } + // change sequence workarea + if (change_in_out) { + change_in_out_sequence->using_workarea = new_in_out_enabled; + change_in_out_sequence->workarea_in = new_sequence_in; + change_in_out_sequence->workarea_out = new_sequence_out; + } + // change current sequence if (change_seq) { old_seq = sequence; diff --git a/project/undo.h b/project/undo.h index 7ca08ea76..67195a8be 100644 --- a/project/undo.h +++ b/project/undo.h @@ -30,7 +30,9 @@ public: void delete_clip(Sequence* s, int clip); void add_media(QTreeWidgetItem* item); void delete_media(QTreeWidgetItem* item); + void ripple(Sequence* s, long point, long length); void ripple(Sequence* s, long point, long length, QVector &ignore); + void set_in_out(Sequence* s, bool enabled, long in, long out); void undo(); void redo(); private: @@ -73,6 +75,15 @@ private: QVector rippled_clips; QVector ripple_ignores; + bool change_in_out; + Sequence* change_in_out_sequence; + bool old_in_out_enabled; + bool new_in_out_enabled; + long old_sequence_in; + long new_sequence_in; + long old_sequence_out; + long new_sequence_out; + void new_action(Sequence* s, int action, int clip, long old_val, long new_val); void offset_links(QVector& clips, int offset); }; diff --git a/ui/timelineheader.cpp b/ui/timelineheader.cpp index ed16c65c8..1ed76bb7a 100644 --- a/ui/timelineheader.cpp +++ b/ui/timelineheader.cpp @@ -3,42 +3,110 @@ #include "panels/panels.h" #include "panels/timeline.h" #include "project/sequence.h" +#include "project/undo.h" #include #include +#include #define CLICK_RANGE 5 #define PLAYHEAD_SIZE 6 -TimelineHeader::TimelineHeader(QWidget *parent) : QWidget(parent) -{ - dragging = false; +TimelineHeader::TimelineHeader(QWidget *parent) : QWidget(parent), dragging(false), resizing_workarea(false) { setCursor(Qt::ArrowCursor); + setMouseTracking(true); } -void set_playhead(QMouseEvent* event) { - long frame = panel_timeline->getFrameFromScreenPoint(event->pos().x()); +void set_playhead(int mouse_x) { + long frame = panel_timeline->getFrameFromScreenPoint(mouse_x); panel_timeline->snap_to_clip(&frame, false); panel_timeline->seek(frame); panel_timeline->repaint_timeline(); } -void TimelineHeader::mousePressEvent(QMouseEvent* event) { - /*if (panel_timeline->using_workarea) { - event->pos().x() - if (panel_timeline->workarea_in) - } else { +void TimelineHeader::set_in_point(long new_in) { + long new_out = sequence->workarea_out; + if (new_out == new_in) { + new_in--; + } else if (new_out < new_in) { + new_out = sequence->getEndFrame(); + } - }*/ - set_playhead(event); + TimelineAction* ta = new TimelineAction(); + ta->set_in_out(sequence, true, new_in, new_out); + undo_stack.push(ta); + + update(); +} + +void TimelineHeader::set_out_point(long new_out) { + long new_in = sequence->workarea_in; + if (new_out == new_in) { + new_out++; + } else if (new_in > new_out) { + new_in = 0; + } + + TimelineAction* ta = new TimelineAction(); + ta->set_in_out(sequence, true, new_in, new_out); + undo_stack.push(ta); + + update(); +} + +void TimelineHeader::mousePressEvent(QMouseEvent* event) { + if (resizing_workarea) { + sequence_end = sequence->getEndFrame(); + } else { + set_playhead(event->pos().x()); + } dragging = true; } void TimelineHeader::mouseMoveEvent(QMouseEvent* event) { - if (dragging) set_playhead(event); + if (dragging) { + if (resizing_workarea) { + long frame = panel_timeline->getFrameFromScreenPoint(event->pos().x()); + panel_timeline->snap_to_clip(&frame, true); + if (resizing_workarea_in) { + temp_workarea_in = qMax(qMin(temp_workarea_out-1, frame), 0L); + } else { + temp_workarea_out = qMin(qMax(temp_workarea_in+1, frame), sequence_end); + } + panel_timeline->repaint_timeline(); + } else { + set_playhead(event->pos().x()); + } + } else { + resizing_workarea = false; + unsetCursor(); + if (sequence->using_workarea) { + long min_frame = panel_timeline->getFrameFromScreenPoint(event->pos().x() - CLICK_RANGE) - 1; + long max_frame = panel_timeline->getFrameFromScreenPoint(event->pos().x() + CLICK_RANGE) + 1; + if (sequence->workarea_in > min_frame && sequence->workarea_in < max_frame) { + resizing_workarea = true; + resizing_workarea_in = true; + } else if (sequence->workarea_out > min_frame && sequence->workarea_out < max_frame) { + resizing_workarea = true; + resizing_workarea_in = false; + } + if (resizing_workarea) { + temp_workarea_in = sequence->workarea_in; + temp_workarea_out = sequence->workarea_out; + setCursor(Qt::SizeHorCursor); + } + } + } } -void TimelineHeader::mouseReleaseEvent(QMouseEvent *) { +void TimelineHeader::mouseReleaseEvent(QMouseEvent*) { + if (resizing_workarea) { + TimelineAction* ta = new TimelineAction(); + ta->set_in_out(sequence, true, temp_workarea_in, temp_workarea_out); + undo_stack.push(ta); + } + + resizing_workarea = false; dragging = false; panel_timeline->snapped = false; panel_timeline->repaint_timeline(); @@ -60,9 +128,9 @@ void TimelineHeader::paintEvent(QPaintEvent*) { // draw in/out selection int in_x; - if (panel_timeline->using_workarea) { - in_x = panel_timeline->getScreenPointFromFrame(panel_timeline->workarea_in); - int out_x = panel_timeline->getScreenPointFromFrame(panel_timeline->workarea_out); + if (sequence->using_workarea) { + in_x = panel_timeline->getScreenPointFromFrame(resizing_workarea ? temp_workarea_in : sequence->workarea_in); + int out_x = panel_timeline->getScreenPointFromFrame(resizing_workarea ? temp_workarea_out :sequence->workarea_out); p.fillRect(QRect(in_x, 0, out_x-in_x, height()), QColor(0, 192, 255, 128)); p.setPen(Qt::white); p.drawLine(in_x, 0, in_x, height()); diff --git a/ui/timelineheader.h b/ui/timelineheader.h index 9dbead229..2c5dd27d5 100644 --- a/ui/timelineheader.h +++ b/ui/timelineheader.h @@ -8,6 +8,8 @@ class TimelineHeader : public QWidget Q_OBJECT public: explicit TimelineHeader(QWidget *parent = 0); + void set_in_point(long p); + void set_out_point(long p); protected: void paintEvent(QPaintEvent*); @@ -18,6 +20,12 @@ protected: private: bool dragging; + bool resizing_workarea; + bool resizing_workarea_in; + long temp_workarea_in; + long temp_workarea_out; + long sequence_end; + signals: public slots: diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 9be2b8871..9f7d01443 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -755,7 +755,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { } } } - panel_timeline->snapped = (temp_frame_diff == frame_diff); + if (temp_frame_diff != frame_diff) panel_timeline->snapped = false; // apply changes to ghosts for (int i=0;ighosts.size();i++) {