diff --git a/olive.pro.user b/olive.pro.user index 672010027..e8aadb4de 100644 --- a/olive.pro.user +++ b/olive.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 91652fc04..e8d30a610 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -198,23 +198,21 @@ bool Timeline::focused() { } void Timeline::undo() { - qDebug() << "[INFO] Undo/redo was so buggy, it's not been disabled. Sorry for any inconvenience"; -// if (sequence != NULL) { -// current_clips.clear(); -// panel_effect_controls->set_clip(NULL); -// sequence->undo(); -// redraw_all_clips(false); -// } +// qDebug() << "[INFO] Undo/redo was so buggy, it's been disabled. Sorry for any inconvenience"; + if (sequence != NULL) { + panel_effect_controls->clear_effects(); + sequence->undo(); + redraw_all_clips(false); + } } void Timeline::redo() { - qDebug() << "[INFO] Undo/redo was so buggy, it's not been disabled. Sorry for any inconvenience"; -// if (sequence != NULL) { -// current_clips.clear(); -// panel_effect_controls->set_clip(NULL); -// sequence->redo(); -// redraw_all_clips(false); -// } +// qDebug() << "[INFO] Undo/redo was so buggy, it's been disabled. Sorry for any inconvenience"; + if (sequence != NULL) { + panel_effect_controls->clear_effects(); + sequence->redo(); + redraw_all_clips(false); + } } QString frame_to_timecode(long f) { @@ -256,6 +254,7 @@ void Timeline::redraw_all_clips(bool changed) { project_changed = true; if (!playing) reset_all_audio(); panel_viewer->viewer_widget->update(); + sequence->undo_add_current(); } ui->video_area->redraw_clips(); diff --git a/panels/timeline.h b/panels/timeline.h index 0712a2d59..142b7d1e2 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -121,6 +121,7 @@ public: // selecting functions bool selecting; + int selection_offset; QVector selections; bool is_clip_selected(Clip* clip); void delete_selection(bool ripple); diff --git a/playback/cacher.cpp b/playback/cacher.cpp index de9706e77..a7751cd8f 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -326,7 +326,6 @@ void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bo } void close_clip_worker(Clip* clip) { - cc_lock.lock(); // closes ffmpeg file handle and frees any memory used for caching if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { sws_freeContext(clip->sws_ctx); @@ -345,19 +344,7 @@ void close_clip_worker(Clip* clip) { delete [] clip->cache_A.frames; if (!clip->media_stream->infinite_length) delete [] clip->cache_B.frames; - av_frame_free(&clip->frame); - - // remove clip from current_clips - bool found = false; - for (int i=0;iframe); clip->reset(); diff --git a/playback/playback.cpp b/playback/playback.cpp index 460c16719..d5bc17a15 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -22,47 +22,8 @@ extern "C" { #include #include -QList current_clips; bool texture_failed = false; -QMutex cc_lock; - -void handle_media(Sequence* sequence, long playhead, bool multithreaded) { - for (int i=0;iclip_count();i++) { - Clip* c = sequence->get_clip(i); - - // if clip starts within one second and/or hasn't finished yet - if (c != NULL) { - if (is_clip_active(c, playhead)) { - // if thread is already working, we don't want to touch this, - // but we also don't want to hang the UI thread - if (!c->open) { - if (c->open_lock.tryLock()) { - open_clip(c, multithreaded); - - // add to current_clips, (insertion) sorted by track so composite them in order - cc_lock.lock(); - bool found = false; - for (int j=0;jtrack < c->track) { - current_clips.insert(current_clips.begin()+j, c); - found = true; - break; - } - } - if (!found) { - current_clips.push_back(c); - } - cc_lock.unlock(); - } - } - } else if (c->open) { - close_clip(c); - } - } - } -} - void open_clip(Clip* clip, bool multithreaded) { clip->multithreaded = multithreaded; if (multithreaded) { diff --git a/playback/playback.h b/playback/playback.h index 7cdf1fb13..0604c5a49 100644 --- a/playback/playback.h +++ b/playback/playback.h @@ -9,9 +9,6 @@ struct ClipCache; struct Sequence; struct AVFrame; -extern QList current_clips; -extern QMutex cc_lock; - extern bool texture_failed; void open_clip(Clip* clip, bool multithreaded); diff --git a/project/sequence.cpp b/project/sequence.cpp index 4def2bf67..32affc9e9 100644 --- a/project/sequence.cpp +++ b/project/sequence.cpp @@ -6,6 +6,7 @@ Sequence::Sequence() { undo_pointer = -1; + undo_add_current(); } Sequence::~Sequence() { @@ -95,52 +96,60 @@ Clip* Sequence::split_clip(Clip* pre, long frame) { } void Sequence::undo_add_current() { -// if (undo_stack.size() == UNDO_LIMIT) { -// delete undo_stack.at(0); -// undo_stack.removeFirst(); -// } else { -// undo_pointer++; -// while (undo_stack.size() > undo_pointer+1) { -// delete undo_stack.last(); -// undo_stack.removeLast(); -// } -// } + if (undo_stack.size() == UNDO_LIMIT) { + delete undo_stack.at(0); + undo_stack.removeFirst(); + } else { + undo_pointer++; + while (undo_stack.size() > undo_pointer+1) { + delete undo_stack.last(); + undo_stack.removeLast(); + } + } -// // copy clips -// QVector* copied_clips = new QVector(); -// for (int i=0;iappend(get_clip(i)->copy()); -// } -// undo_stack.append(copied_clips); + // copy clips + QVector* copied_clips = new QVector(); + for (int i=0;icopy(); + copy->linked = original->linked; + copied_clips->append(copy); + } + undo_stack.append(copied_clips); } -void Sequence::set_undo(int i) { -// for (int i=0;i* copy_from_list = undo_stack.at(undo_pointer); -// for (int i=0;isize();i++) { -// add_clip(copy_from_list->at(i)->copy()); -// } + for (int i=0;i* copy_from_list = undo_stack.at(undo_pointer); + for (int i=0;isize();i++) { + Clip* original = copy_from_list->at(i); + Clip* copy = original->copy(); + copy->linked = original->linked; + add_clip(copy); + } } void Sequence::undo() { -// if (undo_pointer > 0) { -// undo_pointer--; -// set_undo(undo_pointer); -// } else { -// qDebug() << "[INFO] No more undos"; -// } + if (undo_pointer > 0) { + undo_pointer--; + set_undo(); + } else { + qDebug() << "[INFO] No more undos"; + } } void Sequence::redo() { -// if (undo_pointer == undo_stack.size() - 1) { -// qDebug() << "[INFO] No more redos"; -// } else { -// undo_pointer++; -// set_undo(undo_pointer); -// } + if (undo_pointer == undo_stack.size() - 1) { + qDebug() << "[INFO] No more redos"; + } else { + undo_pointer++; + set_undo(); + } } // static variable for the currently active sequence diff --git a/project/sequence.h b/project/sequence.h index 4f7c3b7e3..28f193672 100644 --- a/project/sequence.h +++ b/project/sequence.h @@ -31,7 +31,7 @@ public: private: QVector clips; - void set_undo(int i); + void set_undo(); QVector*> undo_stack; int undo_pointer; int undo_stack_start; diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index f90a09748..47404fed6 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -231,6 +231,11 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) { break; case TIMELINE_TOOL_EDIT: if (panel_timeline->edit_tool_also_seeks) panel_timeline->seek(panel_timeline->drag_frame_start); + if ((event->modifiers() & Qt::ShiftModifier)) { + panel_timeline->selection_offset = panel_timeline->selections.size(); + } else { + panel_timeline->selection_offset = 0; + } panel_timeline->selecting = true; break; case TIMELINE_TOOL_RAZOR: @@ -249,6 +254,8 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) { void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { + bool repaint = false; + if (panel_timeline->moving_proc) { if (event->modifiers() & Qt::AltModifier) { // if holding alt, duplicate rather than move // duplicate clips @@ -340,6 +347,37 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { } panel_timeline->redraw_all_clips(true); + } else if (panel_timeline->selecting) { + // remove duplicate selections + for (int i=0;iselections.size();i++) { + Selection& s = panel_timeline->selections[i]; + for (int j=0;jselections.size();j++) { + if (i != j) { + Selection& ss = panel_timeline->selections[j]; + if (s.track == ss.track) { + bool remove = false; + if (s.in < ss.in && s.out > ss.out) { + // do nothing + } else if (s.in >= ss.in && s.out <= ss.out) { + remove = true; + } else if (s.in <= ss.out && s.out > ss.out) { + ss.out = s.out; + remove = true; + } else if (s.out >= ss.in && s.in < ss.in) { + ss.in = s.in; + remove = true; + } + if (remove) { + panel_timeline->selections.removeAt(i); + i--; + repaint = true; + break; + } + } + } + } + } + } // destroy all ghosts @@ -353,10 +391,12 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { pre_clips.clear(); post_clips.clear(); -// panel_timeline->repaint_timeline(); + if (repaint) panel_timeline->repaint_timeline(); // SEND CLIPS TO EFFECT CONTROLS // find out how many clips are selected + // limits to one video clip and one audio clip and only if they're linked + // one of these days it might be nice to have multiple clips in the effects panel bool got_vclip = false; bool got_aclip = false; Clip* vclip = NULL; @@ -632,7 +672,6 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { // prevent slip moving clip beyond media length validator += g.ghost_length; - qDebug() << "validator:" << validator; if (validator > g.media_length) frame_diff += validator - g.media_length; } } @@ -654,18 +693,18 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { panel_timeline->snap_to_clip(&panel_timeline->cursor_frame, !panel_timeline->edit_tool_also_seeks || !panel_timeline->selecting); } if (panel_timeline->selecting) { - int selection_count = 1 + qMax(panel_timeline->cursor_track, panel_timeline->drag_track_start) - qMin(panel_timeline->cursor_track, panel_timeline->drag_track_start); + int selection_count = 1 + qMax(panel_timeline->cursor_track, panel_timeline->drag_track_start) - qMin(panel_timeline->cursor_track, panel_timeline->drag_track_start) + panel_timeline->selection_offset; if (panel_timeline->selections.size() != selection_count) { panel_timeline->selections.resize(selection_count); } int minimum_selection_track = qMin(panel_timeline->cursor_track, panel_timeline->drag_track_start); - for (int i=0;iselections[i]; - s->track = minimum_selection_track + i; + for (int i=panel_timeline->selection_offset;iselections[i]; + s.track = minimum_selection_track + i - panel_timeline->selection_offset; long in = panel_timeline->drag_frame_start; long out = panel_timeline->cursor_frame; - s->in = qMin(in, out); - s->out = qMax(in, out); + s.in = qMin(in, out); + s.out = qMax(in, out); } if (panel_timeline->edit_tool_also_seeks) { panel_timeline->seek(qMin(panel_timeline->drag_frame_start, panel_timeline->cursor_frame)); diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 3d19c1077..ad203598b 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -61,30 +61,58 @@ void ViewerWidget::paintGL() glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - long playhead = panel_timeline->playhead; + current_clips.clear(); + + for (int i=0;iclip_count();i++) { + Clip* c = sequence->get_clip(i); + + // if clip starts within one second and/or hasn't finished yet + if (c != NULL) { + if (is_clip_active(c, panel_timeline->playhead)) { + // if thread is already working, we don't want to touch this, + // but we also don't want to hang the UI thread + if (!c->open) { + if (c->open_lock.tryLock()) { + open_clip(c, multithreaded); + } + } + + bool added = false; + for (int j=0;jtrack > c->track) { + current_clips.insert(j, c); + added = true; + break; + } + } + if (!added) { + current_clips.append(c); + } + } else if (c->open) { + close_clip(c); + } + } + } - handle_media(sequence, playhead, multithreaded); texture_failed = false; bool render_audio = (panel_timeline->playing || force_audio); - cc_lock.lock(); - for (int i=0;ifinished_opening) { qDebug() << "[WARNING] Tried to display clip" << i << "but it's closed"; texture_failed = true; - } else if (is_clip_active(c, playhead)) { + } else if (is_clip_active(c, panel_timeline->playhead)) { if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { // start preparing cache - get_clip_frame(c, playhead); + get_clip_frame(c, panel_timeline->playhead); if (c->texture == NULL) { qDebug() << "[WARNING] Texture hasn't been created yet"; texture_failed = true; - } else if (playhead >= c->timeline_in) { + } else if (panel_timeline->playhead >= c->timeline_in) { glLoadIdentity(); int half_width = c->sequence->width/2; int half_height = c->sequence->height/2; @@ -119,7 +147,7 @@ void ViewerWidget::paintGL() c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && c->lock.tryLock()) { // clip is not caching, start caching audio - cache_clip(c, playhead, false, false, c->reset_audio); + cache_clip(c, panel_timeline->playhead, false, false, c->reset_audio); c->lock.unlock(); } } @@ -143,8 +171,6 @@ void ViewerWidget::paintGL() p.drawText({0, 0, 200, 200}, "text!"); p.end();*/ - cc_lock.unlock(); - if (texture_failed) { if (multithreaded) { retry_timer.start(); diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index bc2ac695d..37879150a 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -6,6 +6,7 @@ #include #include #include +struct Clip; class Viewer; @@ -25,6 +26,7 @@ protected: void paintGL(); private: QTimer retry_timer; + QVector current_clips; private slots: void retry(); };