diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 08bc48629..f386c87f3 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -639,10 +639,10 @@ void EffectControls::Load() { QVBoxLayout* layout; - if (c->track() < 0) { + if (c->type() == Track::kTypeVideo) { vcontainer->setVisible(true); layout = video_effect_layout; - } else { + } else if (c->type() == Track::kTypeAudio) { acontainer->setVisible(true); layout = audio_effect_layout; } diff --git a/panels/project.cpp b/panels/project.cpp index 1c423a0ef..0e0c614ac 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -449,7 +449,7 @@ void Project::delete_selected_media() { } } if (confirm_delete) { - ca->append(new DeleteClipAction(s, k)); + ca->append(new DeleteClipAction(c)); } } } @@ -589,13 +589,12 @@ void Project::delete_clips_using_selected_media() { QVector sequence_clips = olive::ActiveSequence->GetAllClips(); for (int i=0;imedia() == m) { - ca->append(new DeleteClipAction(olive::ActiveSequence.get(), i)); - deleted = true; - } + + for (int j=0;jmedia() == m) { + ca->append(new DeleteClipAction(c)); + deleted = true; } } } diff --git a/panels/timeline.cpp b/panels/timeline.cpp index b0858baee..df96b89ce 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "global/global.h" #include "panels/panels.h" @@ -90,9 +91,7 @@ Timeline::Timeline(QWidget *parent) : headers->viewer = panel_sequence_viewer; - video_area->bottom_align = true; - video_area->scrollBar = videoScrollbar; - audio_area->scrollBar = audioScrollbar; + video_area->SetAlignment(olive::timeline::kAlignmentBottom); tool_buttons.append(toolArrowButton); tool_buttons.append(toolEditButton); @@ -103,11 +102,21 @@ Timeline::Timeline(QWidget *parent) : tool_buttons.append(toolTransitionButton); tool_buttons.append(toolHandButton); + tool_button_group = new QButtonGroup(this); + tool_button_group->addButton(toolArrowButton); + tool_button_group->addButton(toolEditButton); + tool_button_group->addButton(toolRippleButton); + tool_button_group->addButton(toolRazorButton); + tool_button_group->addButton(toolSlipButton); + tool_button_group->addButton(toolSlideButton); + tool_button_group->addButton(toolTransitionButton); + tool_button_group->addButton(toolHandButton); + toolArrowButton->click(); connect(horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(setScroll(int))); - connect(videoScrollbar, SIGNAL(valueChanged(int)), video_area, SLOT(setScroll(int))); - connect(audioScrollbar, SIGNAL(valueChanged(int)), audio_area, SLOT(setScroll(int))); + //connect(videoScrollbar, SIGNAL(valueChanged(int)), video_area, SLOT(setScroll(int))); + //connect(audioScrollbar, SIGNAL(valueChanged(int)), audio_area, SLOT(setScroll(int))); connect(horizontalScrollBar, SIGNAL(resize_move(double)), this, SLOT(resize_move(double))); update_sequence(); @@ -133,91 +142,6 @@ 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) { - long p_cut = 0; - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - if (c->timeline_out() > p_cut && c->timeline_out() < olive::ActiveSequence->playhead) { - p_cut = c->timeline_out(); - } else if (c->timeline_in() > p_cut && c->timeline_in() < olive::ActiveSequence->playhead) { - p_cut = c->timeline_in(); - } - } - } - panel_sequence_viewer->seek(p_cut); - } -} - -void Timeline::next_cut() { - if (olive::ActiveSequence != nullptr) { - bool seek_enabled = false; - long n_cut = LONG_MAX; - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - if (c->timeline_in() < n_cut && c->timeline_in() > olive::ActiveSequence->playhead) { - n_cut = c->timeline_in(); - seek_enabled = true; - } else if (c->timeline_out() < n_cut && c->timeline_out() > olive::ActiveSequence->playhead) { - n_cut = c->timeline_out(); - seek_enabled = true; - } - } - } - if (seek_enabled) panel_sequence_viewer->seek(n_cut); - } -} - void ripple_clips(ComboAction* ca, Sequence* s, long point, long length, const QVector& ignore) { ca->append(new RippleAction(s, point, length, ignore)); } @@ -227,7 +151,7 @@ void Timeline::toggle_show_all() { showing_all = !showing_all; if (showing_all) { old_zoom = zoom; - set_zoom_value(double(timeline_area->width() - 200) / double(olive::ActiveSequence->getEndFrame())); + set_zoom_value(double(timeline_area->width() - 200) / double(olive::ActiveSequence->GetEndFrame())); } else { set_zoom_value(old_zoom); } @@ -302,7 +226,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector || 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.track = seq->GetTrackList(Track::kTypeAudio)->First() + j; g.media_stream = m->audio_tracks.at(j).file_index; ghosts.append(g); audio_ghosts = true; @@ -314,7 +238,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector || 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.track = seq->GetTrackList(Track::kTypeVideo)->First() + j; g.media_stream = m->video_tracks.at(j).file_index; ghosts.append(g); video_ghosts = true; @@ -331,13 +255,13 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector if (import_data.type() == olive::timeline::kImportVideoOnly || import_data.type() == olive::timeline::kImportBoth) { - g.track = -1; + g.track = seq->GetTrackList(Track::kTypeVideo)->First(); ghosts.append(g); } if (import_data.type() == olive::timeline::kImportAudioOnly || import_data.type() == olive::timeline::kImportBoth) { - g.track = 0; + g.track = seq->GetTrackList(Track::kTypeAudio)->First(); ghosts.append(g); } @@ -401,15 +325,15 @@ void Timeline::add_clips_from_ghosts(ComboAction* ca, Sequence* s) { for (int j=0;jmedia() == cc->media()) { - c->linked.append(j); + c->linked.append(cc.get()); } } if (olive::CurrentConfig.add_default_effects_to_clips) { - if (c->track() < 0) { + if (c->type() == Track::kTypeVideo) { // add default video effects c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_TRANSFORM, EFFECT_TYPE_EFFECT))); - } else { + } else if (c->type() == Track::kTypeAudio) { // add default audio effects c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_VOLUME, EFFECT_TYPE_EFFECT))); c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_PAN, EFFECT_TYPE_EFFECT))); @@ -428,26 +352,30 @@ void Timeline::add_transition() { ComboAction* ca = new ComboAction(); bool adding = false; - for (int i=0;iclips.size();i++) { - Clip* c = olive::ActiveSequence->clips.at(i).get(); - if (c != nullptr && c->IsSelected()) { - int transition_to_add = (c->track() < 0) ? TRANSITION_INTERNAL_CROSSDISSOLVE : TRANSITION_INTERNAL_LINEARFADE; - if (c->opening_transition == nullptr) { - ca->append(new AddTransitionCommand(c, - nullptr, - nullptr, - Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), - olive::CurrentConfig.default_transition_length)); - adding = true; - } - if (c->closing_transition == nullptr) { - ca->append(new AddTransitionCommand(nullptr, - c, - nullptr, - Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), - olive::CurrentConfig.default_transition_length)); - adding = true; - } + QVector selected_clips = olive::ActiveSequence->SelectedClips(); + + for (int i=0;itype() == Track::kTypeVideo) ? TRANSITION_INTERNAL_CROSSDISSOLVE + : TRANSITION_INTERNAL_LINEARFADE; + + if (c->opening_transition == nullptr) { + ca->append(new AddTransitionCommand(c, + nullptr, + nullptr, + Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), + olive::CurrentConfig.default_transition_length)); + adding = true; + } + + if (c->closing_transition == nullptr) { + ca->append(new AddTransitionCommand(nullptr, + c, + nullptr, + Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), + olive::CurrentConfig.default_transition_length)); + adding = true; } } @@ -463,7 +391,7 @@ void Timeline::add_transition() { void Timeline::nest() { if (olive::ActiveSequence != nullptr) { // get selected clips - QVector selected_clips = olive::ActiveSequence->SelectedClipIndexes(); + QVector selected_clips = olive::ActiveSequence->SelectedClips(); // nest them if (!selected_clips.isEmpty()) { @@ -471,7 +399,7 @@ void Timeline::nest() { // get earliest point in selected clips long earliest_point = LONG_MAX; for (int i=0;iclips.at(selected_clips.at(i))->timeline_in(), earliest_point); + earliest_point = qMin(selected_clips.first()->timeline_in(), earliest_point); } ComboAction* ca = new ComboAction(); @@ -479,30 +407,37 @@ void Timeline::nest() { // create "nest" sequence with the same attributes as the current sequence SequencePtr s = std::make_shared(); - s->name = panel_project->get_next_sequence_name(tr("Nested Sequence")); + s->name = olive::project_model.GetNextSequenceName(tr("Nested Sequence")); s->width = olive::ActiveSequence->width; s->height = olive::ActiveSequence->height; s->frame_rate = olive::ActiveSequence->frame_rate; s->audio_frequency = olive::ActiveSequence->audio_frequency; s->audio_layout = olive::ActiveSequence->audio_layout; + QVector new_clips; + // copy all selected clips to the nest for (int i=0;iappend(new DeleteClipAction(olive::ActiveSequence.get(), selected_clips.at(i))); + ca->append(new DeleteClipAction(c)); // copy to new - ClipPtr copy(olive::ActiveSequence->clips.at(selected_clips.at(i))->copy(s.get())); + Track* track = s->GetTrackList(c->type())->TrackAt(c->track()->Index()); + ClipPtr copy = selected_clips.at(i)->copy(track); copy->set_timeline_in(copy->timeline_in() - earliest_point); copy->set_timeline_out(copy->timeline_out() - earliest_point); - s->clips.append(copy); + track->AddClip(copy); + + new_clips.append(copy); } // relink clips in new nested sequences - relink_clips_using_ids(selected_clips, s->clips); + olive::timeline::RelinkClips(selected_clips, new_clips); // add sequence to project - MediaPtr m = panel_project->create_sequence_internal(ca, s, false, nullptr); + MediaPtr m = olive::project_model.CreateSequence(ca, s, false, nullptr); // add nested sequence to active sequence QVector media_list; @@ -510,9 +445,10 @@ void Timeline::nest() { 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)) { + QVector all_sequence_clips = olive::ActiveSequence->GetAllClips(); + for (int j=0;jtrack() == g.track @@ -520,14 +456,14 @@ void Timeline::nest() { && 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++; - } + + // There's a clip occupied by the space taken up by this ghost. Move up a track, and seek again. + g.track = g.track->track_list()->TrackAt(g.track->Index() + 1); + + // Restart entire loop again j = -1; break; + } } } @@ -538,7 +474,7 @@ void Timeline::nest() { panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); - olive::ActiveSequence->selections.clear(); + olive::ActiveSequence->ClearSelections(); olive::UndoStack.push(ca); @@ -551,7 +487,7 @@ void Timeline::update_sequence() { bool null_sequence = (olive::ActiveSequence == nullptr); for (int i=0;isetEnabled(!null_sequence); + tool_buttons.at(i)->setEnabled(!null_sequence); } snappingButton->setEnabled(!null_sequence); zoomInButton->setEnabled(!null_sequence); @@ -563,7 +499,7 @@ void Timeline::update_sequence() { UpdateTitle(); } -int Timeline::get_snap_range() { +long Timeline::get_snap_range() { return getFrameFromScreenPoint(zoom, 10); } @@ -583,7 +519,7 @@ void Timeline::repaint_timeline() { // auto scroll if (olive::CurrentConfig.autoscroll == olive::AUTOSCROLL_PAGE_SCROLL) { int playhead_x = getTimelineScreenPointFromFrame(olive::ActiveSequence->playhead); - if (playhead_x < 0 || playhead_x > (editAreas->width() - videoScrollbar->width())) { + if (playhead_x < 0 || playhead_x > (editAreas->width())) { horizontalScrollBar->setValue(getScreenPointFromFrame(zoom, olive::ActiveSequence->playhead)); draw = false; } @@ -611,7 +547,7 @@ void Timeline::repaint_timeline() { void Timeline::select_all() { if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->selections.clear(); + olive::ActiveSequence->ClearSelections(); for (int i=0;iclips.size();i++) { ClipPtr c = olive::ActiveSequence->clips.at(i); if (c != nullptr) { @@ -864,12 +800,6 @@ void Timeline::multiply_zoom(double m) { set_zoom_value(zoom * m); } -void Timeline::decheck_tool_buttons(QObject* sender) { - for (int i=0;isetChecked(tool_buttons.at(i) == sender); - } -} - void Timeline::zoom_in() { multiply_zoom(2.0); } @@ -928,83 +858,7 @@ void Timeline::snapping_clicked(bool checked) { snapping = checked; } -ClipPtr Timeline::split_clip(ComboAction* ca, bool transitions, int p, long frame) { - return split_clip(ca, transitions, p, frame, frame); -} - -ClipPtr Timeline::split_clip(ComboAction* ca, bool transitions, int p, long frame, long post_in) { - Clip* pre = olive::ActiveSequence->clips.at(p).get(); - if (pre != nullptr) { - - if (pre->timeline_in() < frame && pre->timeline_out() > frame) { - // duplicate clip without duplicating its transitions, we'll restore them later - - ClipPtr post = pre->copy(olive::ActiveSequence.get()); - - long new_clip_length = frame - pre->timeline_in(); - - post->set_timeline_in(post_in); - post->set_clip_in(pre->clip_in() + (post->timeline_in() - pre->timeline_in())); - - pre->move(ca, pre->timeline_in(), frame, pre->clip_in(), pre->track(), false); - - if (transitions) { - - // check if this clip has a closing transition - if (pre->closing_transition != nullptr) { - - // if so, move closing transition to the post clip - post->closing_transition = pre->closing_transition; - - // and set the original clip's closing transition to nothing - ca->append(new SetPointer(reinterpret_cast(&pre->closing_transition), nullptr)); - - // and set the transition's reference to the post clip - if (post->closing_transition->parent_clip == pre) { - ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->parent_clip), post.get())); - } - if (post->closing_transition->secondary_clip == pre) { - ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->secondary_clip), post.get())); - } - - // and make sure it's at the correct size to the closing clip - if (post->closing_transition != nullptr && post->closing_transition->get_true_length() > post->length()) { - ca->append(new ModifyTransitionCommand(post->closing_transition, post->length())); - post->closing_transition->set_length(post->length()); - } - - } - - // we're keeping the opening clip, so ensure that's a correct size too - if (pre->opening_transition != nullptr && pre->opening_transition->get_true_length() > new_clip_length) { - ca->append(new ModifyTransitionCommand(pre->opening_transition, new_clip_length)); - } - } - - return post; - - } else if (frame == pre->timeline_in() - && pre->opening_transition != nullptr - && pre->opening_transition->secondary_clip != nullptr) { - // special case for shared transitions to split it into two - - // set transition to single-clip mode - ca->append(new SetPointer(reinterpret_cast(&pre->opening_transition->secondary_clip), nullptr)); - - // clone transition for other clip - ca->append(new AddTransitionCommand(nullptr, - pre->opening_transition->secondary_clip, - pre->opening_transition, - nullptr, - 0) - ); - - } - - } - return nullptr; -} - +/* bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool relink) { Clip* c = olive::ActiveSequence->clips.at(clip).get(); if (c != nullptr) { @@ -1045,121 +899,9 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool } return false; } +*/ -void Timeline::clean_up_selections(QVector& areas) { - for (int i=0;i 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) { - areas.removeAt(i); - i--; - break; - } - } - } - } - } -} -bool selection_contains_transition(const Selection& s, Clip* c, int type) { - if (type == kTransitionOpening) { - return c->opening_transition != nullptr - && s.out == c->timeline_in() + c->opening_transition->get_true_length() - && ((c->opening_transition->secondary_clip == nullptr && s.in == c->timeline_in()) - || (c->opening_transition->secondary_clip != nullptr && s.in == c->timeline_in() - c->opening_transition->get_true_length())); - } else { - return c->closing_transition != nullptr - && s.in == c->timeline_out() - c->closing_transition->get_true_length() - && ((c->closing_transition->secondary_clip == nullptr && s.out == c->timeline_out()) - || (c->closing_transition->secondary_clip != nullptr && s.out == c->timeline_out() + c->closing_transition->get_true_length())); - } -} - -void Timeline::delete_areas_and_relink(ComboAction* ca, QVector& areas, bool deselect_areas) { - clean_up_selections(areas); - - panel_graph_editor->set_row(nullptr); - panel_effect_controls->Clear(true); - - QVector pre_clips; - QVector post_clips; - - for (int i=0;iclips.size();j++) { - Clip* c = olive::ActiveSequence->clips.at(j).get(); - if (c != nullptr && c->track() == s.track && !c->undeletable) { - if (selection_contains_transition(s, c, kTransitionOpening)) { - // delete opening transition - ca->append(new DeleteTransitionCommand(c->opening_transition)); - } else if (selection_contains_transition(s, c, kTransitionClosing)) { - // delete closing transition - ca->append(new DeleteTransitionCommand(c->closing_transition)); - } else if (c->timeline_in() >= s.in && c->timeline_out() <= s.out) { - // clips falls entirely within deletion area - ca->append(new DeleteClipAction(olive::ActiveSequence.get(), j)); - } else if (c->timeline_in() < s.in && c->timeline_out() > s.out) { - // middle of clip is within deletion area - - // duplicate clip - ClipPtr post = split_clip(ca, true, j, s.in, s.out); - - pre_clips.append(j); - post_clips.append(post); - } else if (c->timeline_in() < s.in && c->timeline_out() > s.in) { - // only out point is in deletion area - c->move(ca, c->timeline_in(), s.in, c->clip_in(), c->track()); - - if (c->closing_transition != nullptr) { - if (s.in < c->timeline_out() - c->closing_transition->get_true_length()) { - ca->append(new DeleteTransitionCommand(c->closing_transition)); - } else { - ca->append(new ModifyTransitionCommand(c->closing_transition, c->closing_transition->get_true_length() - (c->timeline_out() - s.in))); - } - } - } else if (c->timeline_in() < s.out && c->timeline_out() > s.out) { - // only in point is in deletion area - c->move(ca, s.out, c->timeline_out(), c->clip_in() + (s.out - c->timeline_in()), c->track()); - - if (c->opening_transition != nullptr) { - if (s.out > c->timeline_in() + c->opening_transition->get_true_length()) { - ca->append(new DeleteTransitionCommand(c->opening_transition)); - } else { - ca->append(new ModifyTransitionCommand(c->opening_transition, c->opening_transition->get_true_length() - (s.out - c->timeline_in()))); - } - } - } - } - } - } - - // deselect selected clip areas - if (deselect_areas) { - QVector area_copy = areas; - for (int i=0;iappend(new AddClipCommand(olive::ActiveSequence.get(), post_clips)); -} void Timeline::copy(bool del) { bool cleared = false; @@ -1220,23 +962,6 @@ void Timeline::copy(bool del) { } } -void Timeline::relink_clips_using_ids(QVector& old_clips, QVector& new_clips) { - // relink pasted clips - for (int i=0;iclips.at(old_clips.at(i)); - for (int j=0;jlinked.size();j++) { - for (int k=0;klinked.at(j) == old_clips.at(k)) { - if (new_clips.at(i) != nullptr) { - new_clips.at(i)->linked.append(k); - } - } - } - } - } -} - void Timeline::paste(bool insert) { if (clipboard.size() > 0) { if (clipboard_type == CLIPBOARD_TYPE_CLIP) { @@ -1536,20 +1261,6 @@ bool Timeline::split_selection(ComboAction* ca) { return false; } -bool Timeline::split_all_clips_at_point(ComboAction* ca, long point) { - bool split = false; - for (int j=0;jclips.size();j++) { - ClipPtr c = olive::ActiveSequence->clips.at(j); - if (c != nullptr) { - // always relinks - if (split_clip_and_relink(ca, j, point, true)) { - split = true; - } - } - } - return split; -} - void Timeline::split_at_playhead() { ComboAction* ca = new ComboAction(); bool split_selected = false; @@ -1904,7 +1615,6 @@ void Timeline::transition_menu_select(QAction* a) { transition_tool_side = 1; } - decheck_tool_buttons(sender()); timeline_area->setCursor(Qt::CrossCursor); tool = TIMELINE_TOOL_TRANSITION; toolTransitionButton->setChecked(true); @@ -2052,41 +1762,11 @@ void Timeline::setup_ui() { splitter->setChildrenCollapsible(false); splitter->setOrientation(Qt::Vertical); - QWidget* videoContainer = new QWidget(); + video_area = new TimelineArea(); + splitter->addWidget(video_area); - QHBoxLayout* videoContainerLayout = new QHBoxLayout(videoContainer); - videoContainerLayout->setSpacing(0); - videoContainerLayout->setContentsMargins(0, 0, 0, 0); - - video_area = new TimelineView(); - video_area->setFocusPolicy(Qt::ClickFocus); - videoContainerLayout->addWidget(video_area); - - videoScrollbar = new QScrollBar(); - videoScrollbar->setMaximum(0); - videoScrollbar->setSingleStep(20); - videoScrollbar->setOrientation(Qt::Vertical); - videoContainerLayout->addWidget(videoScrollbar); - - splitter->addWidget(videoContainer); - - QWidget* audioContainer = new QWidget(); - QHBoxLayout* audioContainerLayout = new QHBoxLayout(audioContainer); - audioContainerLayout->setSpacing(0); - audioContainerLayout->setContentsMargins(0, 0, 0, 0); - - audio_area = new TimelineView(); - audio_area->setFocusPolicy(Qt::ClickFocus); - - audioContainerLayout->addWidget(audio_area); - - audioScrollbar = new QScrollBar(); - audioScrollbar->setMaximum(0); - audioScrollbar->setOrientation(Qt::Vertical); - - audioContainerLayout->addWidget(audioScrollbar); - - splitter->addWidget(audioContainer); + audio_area = new TimelineArea(); + splitter->addWidget(audio_area); editAreaLayout->addWidget(splitter); @@ -2111,7 +1791,6 @@ void Timeline::setup_ui() { void Timeline::set_tool() { QPushButton* button = static_cast(sender()); - decheck_tool_buttons(button); tool = button->property("tool").toInt(); creating = false; switch (tool) { diff --git a/panels/timeline.h b/panels/timeline.h index c6c8915e9..45cc380ad 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -54,16 +54,9 @@ public: virtual bool focused() override; void multiply_zoom(double m); void copy(bool del); - ClipPtr split_clip(ComboAction* ca, bool transitions, int p, long frame); - ClipPtr split_clip(ComboAction* ca, bool transitions, int p, long frame, long post_in); - 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); - void relink_clips_using_ids(QVector& old_clips, QVector& new_clips); void update_sequence(); void edit_to_point_internal(bool in, bool ripple); @@ -77,7 +70,7 @@ public: int getDisplayScreenPointFromFrame(long frame); long getDisplayFrameFromScreenPoint(int x); - int get_snap_range(); + long get_snap_range(); bool snap_to_point(long point, long* l); bool snap_to_timeline(long* l, bool use_playhead, bool use_markers, bool use_workarea); void set_marker(); @@ -152,6 +145,8 @@ public: AudioMonitor* audio_monitor; ResizableScrollBar* horizontalScrollBar; + QVector tool_buttons; + QButtonGroup* tool_button_group; QPushButton* toolArrowButton; QPushButton* toolEditButton; QPushButton* toolRippleButton; @@ -216,8 +211,6 @@ private slots: private: void ChangeTrackHeightUniformly(int diff); void set_zoom_value(double v); - QVector tool_buttons; - void decheck_tool_buttons(QObject* sender); void set_tool(int tool); int scroll; void set_sb_max(); @@ -233,8 +226,6 @@ private: TimelineArea* video_area; TimelineArea* audio_area; QWidget* editAreas; - QScrollBar* videoScrollbar; - QScrollBar* audioScrollbar; QPushButton* zoomInButton; QPushButton* zoomOutButton; QPushButton* recordButton; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index afe282de2..04e9187e8 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -348,13 +348,12 @@ void Viewer::pause() { // Make a clip out of it and add it to the Sequence - ClipPtr c = std::make_shared(seq.get()); + ClipPtr c = std::make_shared(recording_track); c->set_media(m, 0); // latest media c->set_timeline_in(recording_start); c->set_timeline_out(recording_start + f->get_length_in_frames(seq->frame_rate)); c->set_clip_in(0); - c->set_track(recording_track); c->set_color(128, 192, 128); c->set_name(m->get_name()); @@ -382,7 +381,7 @@ void Viewer::update_header_zoom() { if (seq != nullptr) { long sequenceEndFrame = seq->GetEndFrame(); if (cached_end_frame != sequenceEndFrame) { - minimum_zoom = (sequenceEndFrame > 0) ? ((double) headers->width() / (double) sequenceEndFrame) : 1; + minimum_zoom = (sequenceEndFrame > 0) ? (double(headers->width()) / double(sequenceEndFrame)) : 1; headers->update_zoom(qMax(headers->get_zoom(), minimum_zoom)); set_sb_max(); viewer_widget_->waveform_zoom = headers->get_zoom(); @@ -431,6 +430,51 @@ void Viewer::update_viewer() { update_end_timecode(); } +void Viewer::prev_cut() +{ + if (seq != nullptr + && seq->playhead > 0) { + + QVector sequence_clips = olive::ActiveSequence->GetAllClips(); + + long p_cut = 0; + for (int i=0;itimeline_out() > p_cut && c->timeline_out() < seq->playhead) { + p_cut = c->timeline_out(); + } else if (c->timeline_in() > p_cut && c->timeline_in() < seq->playhead) { + p_cut = c->timeline_in(); + } + } + panel_sequence_viewer->seek(p_cut); + } +} + +void Viewer::next_cut() +{ + if (seq != nullptr) { + + QVector sequence_clips = seq->GetAllClips(); + + bool seek_enabled = false; + long n_cut = LONG_MAX; + for (int i=0;itimeline_in() < n_cut && c->timeline_in() > seq->playhead) { + n_cut = c->timeline_in(); + seek_enabled = true; + } else if (c->timeline_out() < n_cut && c->timeline_out() > seq->playhead) { + n_cut = c->timeline_out(); + seek_enabled = true; + } + } + + if (seek_enabled) { + panel_sequence_viewer->seek(n_cut); + } + } +} + void Viewer::initiate_drag(olive::timeline::MediaImportType drag_type) { // FIXME: This should contain actual metadata rather than fake metadata @@ -703,7 +747,7 @@ void Viewer::set_media(Media* m) { new_sequence->frame_rate = video_stream.video_frame_rate * footage->speed; } - ClipPtr c = std::make_shared(new_sequence.get()); + ClipPtr c = std::make_shared(new_sequence->GetTrackList(Track::kTypeVideo)->First()); c->set_media(media, video_stream.file_index); c->set_timeline_in(0); c->set_timeline_out(footage->get_length_in_frames(new_sequence->frame_rate)); diff --git a/panels/viewer.h b/panels/viewer.h index fb5835737..e5659ea4c 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -115,7 +115,8 @@ public slots: void go_to_end(); void close_media(); void update_viewer(); - + void prev_cut(); + void next_cut(); private slots: diff --git a/project/footage.cpp b/project/footage.cpp index 8048e9a0c..9e4dc17f2 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -54,7 +54,7 @@ void Footage::Save(QXmlStreamWriter &stream) QDir proj_dir = QFileInfo(olive::ActiveProjectFilename).absoluteDir(); stream.writeStartElement("footage"); - stream.writeAttribute("id", QString::number(media_id)); + stream.writeAttribute("id", QString::number(save_id)); stream.writeAttribute("name", name); stream.writeAttribute("url", proj_dir.relativeFilePath(url)); stream.writeAttribute("duration", QString::number(length)); @@ -70,8 +70,8 @@ void Footage::Save(QXmlStreamWriter &stream) stream.writeAttribute("proxypath", proxy_path); // save video stream metadata - for (int j=0;jvideo_tracks.size();j++) { - const FootageStream& ms = f->video_tracks.at(j); + for (int j=0;jaudio_tracks.size();j++) { - const FootageStream& ms = f->audio_tracks.at(j); + for (int j=0;jmarkers.size();j++) { - f->markers.at(j).Save(stream); + for (int j=0;jset_clip_in(clip_in()); copy->set_timeline_in(timeline_in()); copy->set_timeline_out(timeline_out()); - copy->set_track(track()); copy->set_color(color()); copy->set_media(media(), media_stream_index()); copy->set_autoscaled(autoscaled()); @@ -75,7 +74,7 @@ ClipPtr Clip::copy(Track* s) { copy->effects.append(effects.at(i)->copy(copy.get())); } - copy->set_cached_frame_rate((this->track_ == nullptr) ? cached_frame_rate() : this->track_->frame_rate); + copy->set_cached_frame_rate((this->track_ == nullptr) ? cached_frame_rate() : this->track_->sequence()->frame_rate); copy->refresh(); @@ -207,13 +206,13 @@ void Clip::reset_audio() { cacher.ResetAudio(); } if (media() != nullptr && media()->get_type() == MEDIA_TYPE_SEQUENCE) { - Sequence* nested_sequence = media()->to_sequence().get(); - for (int i=0;iclips.size();i++) { - Clip* c = nested_sequence->clips.at(i).get(); - if (c != nullptr) { - c->reset_audio(); - } + + QVector nested_sequence_clips = media()->to_sequence()->GetAllClips(); + + for (int i=0;ireset_audio(); } + } } @@ -266,7 +265,6 @@ void Clip::Save(QXmlStreamWriter &stream) stream.writeAttribute("clipin", QString::number(clip_in())); stream.writeAttribute("in", QString::number(timeline_in())); stream.writeAttribute("out", QString::number(timeline_out())); - stream.writeAttribute("track", QString::number(track())); stream.writeAttribute("r", QString::number(color().red())); stream.writeAttribute("g", QString::number(color().green())); @@ -277,9 +275,9 @@ void Clip::Save(QXmlStreamWriter &stream) stream.writeAttribute("maintainpitch", QString::number(speed().maintain_audio_pitch)); stream.writeAttribute("reverse", QString::number(reversed())); - if (c->media() != nullptr) { + if (media() != nullptr) { stream.writeAttribute("type", QString::number(media()->get_type())); - switch (c->media()->get_type()) { + switch (media()->get_type()) { case MEDIA_TYPE_FOOTAGE: stream.writeAttribute("media", QString::number(media()->to_footage()->save_id)); stream.writeAttribute("stream", QString::number(media_stream_index())); @@ -302,7 +300,7 @@ void Clip::Save(QXmlStreamWriter &stream) stream.writeStartElement("linked"); // linked for (int k=0;kload_id)); stream.writeEndElement(); // link } stream.writeEndElement(); // linked @@ -457,13 +455,13 @@ double Clip::media_frame_rate() { double rate = media_->get_frame_rate(media_stream_index()); if (!qIsNaN(rate)) return rate; } - if (sequence != nullptr) return sequence->frame_rate; + if (track() != nullptr) return track()->sequence()->frame_rate; return qSNaN(); } long Clip::media_length() { - if (this->sequence != nullptr) { - double fr = this->sequence->frame_rate; + if (this->track() != nullptr) { + double fr = this->track()->sequence()->frame_rate; fr /= speed_.value; @@ -474,7 +472,7 @@ long Clip::media_length() { case MEDIA_TYPE_FOOTAGE: { Footage* m = media_->to_footage(); - const FootageStream* ms = m->get_stream_from_file_index(track_ < 0, media_stream_index()); + const FootageStream* ms = m->get_stream_from_file_index(type() == Track::kTypeVideo, media_stream_index()); if (ms != nullptr && ms->infinite_length) { return LONG_MAX; } else { @@ -493,13 +491,13 @@ long Clip::media_length() { } int Clip::media_width() { - if (media_ == nullptr && sequence != nullptr) return sequence->width; + if (media_ == nullptr && track() != nullptr) return track()->sequence()->width; switch (media_->get_type()) { case MEDIA_TYPE_FOOTAGE: { const FootageStream* ms = media_stream(); if (ms != nullptr) return ms->video_width; - if (sequence != nullptr) return sequence->width; + if (track() != nullptr) return track()->sequence()->width; break; } case MEDIA_TYPE_SEQUENCE: diff --git a/timeline/ghost.h b/timeline/ghost.h index 4ee9a3806..cf89b6412 100644 --- a/timeline/ghost.h +++ b/timeline/ghost.h @@ -3,17 +3,18 @@ #include "effects/transition.h" #include "timelinefunctions.h" +#include "track.h" struct Ghost { int clip; long in; long out; - int track; + Track* track; long clip_in; long old_in; long old_out; - int old_track; + Track* old_track; long old_clip_in; // importing variables diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index ae474e07d..8f31a783e 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -33,10 +33,10 @@ Sequence::Sequence() : wrapper_sequence(false) { // Set up tracks - track_lists.resize(Track::kTypeCount); + track_lists_.resize(Track::kTypeCount); - for (int i=0;i(this, i); + for (int i=0;i(i)); } } @@ -50,8 +50,8 @@ SequencePtr Sequence::copy() { s->audio_layout = audio_layout; // deep copy all of the sequence's clips - for (int i=0;itrack_lists[i] = track_lists.at(i).copy(s.get()); + for (int i=0;itrack_lists_[i] = track_lists_.at(i)->copy(s.get()); } // copy all of the sequence's markers @@ -62,6 +62,12 @@ SequencePtr Sequence::copy() { void Sequence::Save(QXmlStreamWriter &stream) { + // Provide unique IDs for each Clip + QVector all_clips = GetAllClips(); + for (int i=0;iload_id = i; + } + stream.writeStartElement("sequence"); stream.writeAttribute("id", QString::number(save_id)); stream.writeAttribute("name", name); @@ -80,22 +86,28 @@ void Sequence::Save(QXmlStreamWriter &stream) QVector transition_save_cache; QVector transition_clip_save_cache; - for (int j=0;jSave(stream); + for (int j=0;jSave(stream); } - for (int j=0;jGetEndFrame(), end_frame); + for (int j=0;jTrackCount();i++) { + end_frame = qMax(track_list->TrackAt(i)->GetEndFrame(), end_frame); + } + } return end_frame; @@ -105,8 +117,14 @@ QVector Sequence::GetAllClips() { QVector all_clips; - for (int i=0;iGetAllClips()); + for (int j=0;jTrackCount();i++) { + all_clips.append(track_list->TrackAt(i)->GetAllClips()); + } + } return all_clips; @@ -114,7 +132,7 @@ QVector Sequence::GetAllClips() TrackList *Sequence::GetTrackList(Track::Type type) { - return track_lists.at(type).get(); + return track_lists_.at(type); } void Sequence::Close() @@ -143,20 +161,158 @@ void Sequence::RefreshClipsUsingMedia(Media *m) { QVector Sequence::SelectedClips(bool containing) { - QVector all_clips = GetAllClips(); - QVector selected_clips; - for (int i=0;iTrackCount();j++) { + Track* t = tl->TrackAt(j); + + selected_clips.append(t->GetAllClips()); } } return selected_clips; } +void Sequence::DeleteAreas(ComboAction* ca, QVector& areas, bool deselect_areas) +{ + clean_up_selections(areas); + + panel_graph_editor->set_row(nullptr); + panel_effect_controls->Clear(true); + + QVector pre_clips; + QVector post_clips; + + QVector all_clips = GetAllClips(); + + for (int i=0;itrack() == s.track && !c->undeletable) { + if (selection_contains_transition(s, c, kTransitionOpening)) { + // delete opening transition + ca->append(new DeleteTransitionCommand(c->opening_transition)); + } else if (selection_contains_transition(s, c, kTransitionClosing)) { + // delete closing transition + ca->append(new DeleteTransitionCommand(c->closing_transition)); + } else if (c->timeline_in() >= s.in && c->timeline_out() <= s.out) { + // clips falls entirely within deletion area + ca->append(new DeleteClipAction(c)); + } else if (c->timeline_in() < s.in && c->timeline_out() > s.out) { + // middle of clip is within deletion area + + // duplicate clip + ClipPtr post = SplitClip(ca, true, c, s.in, s.out); + + pre_clips.append(j); + post_clips.append(post); + } else if (c->timeline_in() < s.in && c->timeline_out() > s.in) { + // only out point is in deletion area + c->move(ca, c->timeline_in(), s.in, c->clip_in(), c->track()); + + if (c->closing_transition != nullptr) { + if (s.in < c->timeline_out() - c->closing_transition->get_true_length()) { + ca->append(new DeleteTransitionCommand(c->closing_transition)); + } else { + ca->append(new ModifyTransitionCommand(c->closing_transition, c->closing_transition->get_true_length() - (c->timeline_out() - s.in))); + } + } + } else if (c->timeline_in() < s.out && c->timeline_out() > s.out) { + // only in point is in deletion area + c->move(ca, s.out, c->timeline_out(), c->clip_in() + (s.out - c->timeline_in()), c->track()); + + if (c->opening_transition != nullptr) { + if (s.out > c->timeline_in() + c->opening_transition->get_true_length()) { + ca->append(new DeleteTransitionCommand(c->opening_transition)); + } else { + ca->append(new ModifyTransitionCommand(c->opening_transition, c->opening_transition->get_true_length() - (s.out - c->timeline_in()))); + } + } + } + } + } + } + + // deselect selected clip areas + if (deselect_areas) { + QVector area_copy = areas; + for (int i=0;iappend(new AddClipCommand(olive::ActiveSequence.get(), post_clips)); +} + +bool Sequence::SplitAllClipsAtPoint(ComboAction *ca, long point) +{ + bool split = false; + + QVector all_clips = GetAllClips(); + + for (int j=0;jIsActiveAt(point)) { + SplitClipAtPositions(ca, c, {point}, true); + split = true; + } + } + + return split; +} + +void Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector positions, bool relink) +{ + // Add the clip and each of its links to the pre_splits array + + QVector pre_splits; + pre_splits.append(clip); + + if (relink) { + 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])); + } +} + +/* QVector Sequence::SelectedClipIndexes() { QVector selected_clips; @@ -170,15 +326,17 @@ QVector Sequence::SelectedClipIndexes() return selected_clips; } +*/ Effect *Sequence::GetSelectedGizmo() { Effect* gizmo_ptr = nullptr; + QVector clips = GetAllClips(); + for (int i=0;iIsActiveAt(playhead) + Clip* c = clips.at(i); + if (c->IsActiveAt(playhead) && IsClipSelected(c, true)) { // This clip is selected and currently active - we'll use this for gizmos @@ -214,10 +372,93 @@ Effect *Sequence::GetSelectedGizmo() void Sequence::ClearSelections() { - for (int i=0;iClearSelections(); + for (int j=0;jTrackCount();i++) { + tl->TrackAt(i)->ClearSelections(); + } } } +ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long frame) +{ + return SplitClip(ca, transitions, pre, frame, frame); +} + +ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long frame, long post_in) +{ + if (pre == nullptr) { + return nullptr; + } + + if (pre->timeline_in() < frame && pre->timeline_out() > frame) { + // duplicate clip without duplicating its transitions, we'll restore them later + + ClipPtr post = pre->copy(pre->track()); + + long new_clip_length = frame - pre->timeline_in(); + + post->set_timeline_in(post_in); + post->set_clip_in(pre->clip_in() + (post->timeline_in() - pre->timeline_in())); + + pre->move(ca, pre->timeline_in(), frame, pre->clip_in(), pre->track(), false); + + if (transitions) { + + // check if this clip has a closing transition + if (pre->closing_transition != nullptr) { + + // if so, move closing transition to the post clip + post->closing_transition = pre->closing_transition; + + // and set the original clip's closing transition to nothing + ca->append(new SetPointer(reinterpret_cast(&pre->closing_transition), nullptr)); + + // and set the transition's reference to the post clip + if (post->closing_transition->parent_clip == pre) { + ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->parent_clip), post.get())); + } + if (post->closing_transition->secondary_clip == pre) { + ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->secondary_clip), post.get())); + } + + // and make sure it's at the correct size to the closing clip + if (post->closing_transition != nullptr && post->closing_transition->get_true_length() > post->length()) { + ca->append(new ModifyTransitionCommand(post->closing_transition, post->length())); + post->closing_transition->set_length(post->length()); + } + + } + + // we're keeping the opening clip, so ensure that's a correct size too + if (pre->opening_transition != nullptr && pre->opening_transition->get_true_length() > new_clip_length) { + ca->append(new ModifyTransitionCommand(pre->opening_transition, new_clip_length)); + } + } + + return post; + + } else if (frame == pre->timeline_in() + && pre->opening_transition != nullptr + && pre->opening_transition->secondary_clip != nullptr) { + // special case for shared transitions to split it into two + + // set transition to single-clip mode + ca->append(new SetPointer(reinterpret_cast(&pre->opening_transition->secondary_clip), nullptr)); + + // clone transition for other clip + ca->append(new AddTransitionCommand(nullptr, + pre->opening_transition->secondary_clip, + pre->opening_transition, + nullptr, + 0) + ); + + } + + return nullptr; +} + // static variable for the currently active sequence SequencePtr olive::ActiveSequence = nullptr; diff --git a/timeline/sequence.h b/timeline/sequence.h index 0998858ef..5ae8cce88 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -63,7 +63,12 @@ public: void RefreshClipsUsingMedia(Media* m = nullptr); QVector SelectedClips(bool containing = true); - QVector SelectedClipIndexes(); + //QVector SelectedClipIndexes(); + + void DeleteAreas(); + + bool SplitAllClipsAtPoint(ComboAction *ca, long point); + void SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector positions, bool relink = true); Effect* GetSelectedGizmo(); @@ -84,7 +89,10 @@ public: QVector markers; private: - QVector track_lists; + QVector track_lists_; + + ClipPtr SplitClip(ComboAction* ca, bool transitions, Clip *clip, long frame); + ClipPtr SplitClip(ComboAction* ca, bool transitions, Clip *clip, long frame, long post_in); }; using SequencePtr = std::shared_ptr; diff --git a/timeline/timelinefunctions.cpp b/timeline/timelinefunctions.cpp index b3f3b4b86..b70e6c08c 100644 --- a/timeline/timelinefunctions.cpp +++ b/timeline/timelinefunctions.cpp @@ -1,6 +1,30 @@ #include "timelinefunctions.h" -TimelineFunctions::TimelineFunctions() -{ + +void olive::timeline::RelinkClips(QVector &pre_clips, QVector &post_clips) +{ + // Loop through each "pre" clip + for (int i=0;ilinked.size();j++) { + + // Loop again through the "pre" clips to determine which are linked to each other + for (int l=0;llinked.at(j) == pre_clips.at(l)) { + + // Check if we have an equivalent in the post_clips, and link it if so + if (post_clips.at(l) != nullptr) { + post_clips.at(i)->linked.append(post_clips.at(l).get()); + } + + } + } + } + } } diff --git a/timeline/timelinefunctions.h b/timeline/timelinefunctions.h index 6cf5cb75e..23c6bbbdf 100644 --- a/timeline/timelinefunctions.h +++ b/timeline/timelinefunctions.h @@ -1,6 +1,10 @@ #ifndef TIMELINEFUNCTIONS_H #define TIMELINEFUNCTIONS_H +#include + +#include "timeline/clip.h" + namespace olive { namespace timeline { @@ -25,13 +29,9 @@ enum Alignment { kAlignmentSingle }; -} -} +void RelinkClips(QVector& pre_clips, QVector &post_clips); -class TimelineFunctions -{ -public: - TimelineFunctions(); -}; +} +} #endif // TIMELINEFUNCTIONS_H diff --git a/timeline/track.cpp b/timeline/track.cpp index 8a2f3fa31..088d09832 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -1,6 +1,8 @@ #include "track.h" #include "timeline/clip.h" +#include "timeline/tracklist.h" +#include "timeline/sequence.h" int olive::timeline::kTrackDefaultHeight = 40; int olive::timeline::kTrackMinHeight = 30; @@ -30,20 +32,29 @@ Track *Track::copy(TrackList *parent) return t; } +Sequence *Track::sequence() +{ + return parent_->GetParent(); +} + +TrackList *Track::track_list() +{ + return parent_; +} + void Track::Save(QXmlStreamWriter &stream) { stream.writeStartElement("track"); for (int j=0;jSave(stream); + stream.writeStartElement("clip"); + stream.writeAttribute("id", QString::number(c->load_id)); - stream.writeEndElement(); // clip - } + c->Save(stream); + + stream.writeEndElement(); // clip } stream.writeEndElement(); // track @@ -93,9 +104,49 @@ void Track::ResizeClipArray(int new_size) clips_.resize(new_size); } -QVector Track::GetAllClips() +QVector Track::GetAllClips() { - return clips_; + QVector clips; + + clips.resize(clips_.size()); + for (int i=0;i Track::GetSelectedClips(bool containing) +{ + QVector selected_clips; + + for (int i=0;iIndexOfTrack(this); } bool Track::IsClipSelected(int clip_index, bool containing) @@ -157,6 +208,37 @@ bool Track::IsTransitionSelected(Transition *t) return false; } +void Track::TidySelections() +{ + for (int i=0;i 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) { + areas.removeAt(i); + i--; + break; + } + } + } + } + } +} + void Track::ClearSelections() { selections_.clear(); diff --git a/timeline/track.h b/timeline/track.h index 4a947363e..e3d642e8f 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -43,6 +43,9 @@ public: Track(TrackList* parent, Type type); Track* copy(TrackList* parent); + Sequence* sequence(); + TrackList* track_list(); + void Save(QXmlStreamWriter& stream); Type type(); @@ -55,13 +58,17 @@ public: ClipPtr GetClip(int i); void RemoveClip(int i); void RemoveClip(Clip* c); - QVector GetAllClips(); - QVector GetSelectedClips(); + QVector GetAllClips(); + QVector GetSelectedClips(bool containing); + ClipPtr GetClipObjectFromRawPtr(Clip* c); + + int Index(); bool IsClipSelected(int clip_index, bool containing = true); bool IsClipSelected(Clip* clip, bool containing = true); bool IsTransitionSelected(Transition* t); + void TidySelections(); void ClearSelections(); long GetEndFrame(); diff --git a/timeline/tracklist.cpp b/timeline/tracklist.cpp index 45bf5418b..8215121a0 100644 --- a/timeline/tracklist.cpp +++ b/timeline/tracklist.cpp @@ -1,5 +1,7 @@ #include "tracklist.h" +#include "timeline/sequence.h" + TrackList::TrackList(Sequence *parent, Track::Type type) : QObject(parent), type_(type) @@ -8,13 +10,24 @@ TrackList::TrackList(Sequence *parent, Track::Type type) : AddTrack(); } -TrackListPtr TrackList::copy(Sequence *parent) +void TrackList::Save(QXmlStreamWriter &stream) { - TrackListPtr t = std::make_shared(parent, type_); + stream.writeStartElement("Tracks"); + + for (int i=0;iSave(stream); + } + + stream.writeEndElement(); // Tracks +} + +TrackList* TrackList::copy(Sequence *parent) +{ + TrackList* t = new TrackList(parent, type_); t->ResizeTrackArray(tracks_.size()); for (int i=0;itracks_[i] = tracks_.at(i)->copy(t.get()); + t->tracks_[i] = tracks_.at(i)->copy(t); } return t; @@ -22,7 +35,7 @@ TrackListPtr TrackList::copy(Sequence *parent) void TrackList::AddTrack() { - TrackPtr track = std::make_shared(this, type_); + Track* track = new Track(this, type_); tracks_.append(track); } @@ -36,7 +49,7 @@ void TrackList::RemoveTrack(int i) Track *TrackList::First() { - return tracks_.first().get(); + return tracks_.first(); } int TrackList::TrackCount() @@ -44,7 +57,27 @@ int TrackList::TrackCount() return tracks_.size(); } -QVector TrackList::tracks() +int TrackList::IndexOfTrack(Track *track) +{ + for (int i=0;i= tracks_.size()) { + AddTrack(); + } + + return tracks_.at(i); +} + +QVector TrackList::tracks() { return tracks_; } diff --git a/timeline/tracklist.h b/timeline/tracklist.h index 40975c55c..558114e30 100644 --- a/timeline/tracklist.h +++ b/timeline/tracklist.h @@ -10,10 +10,14 @@ public: TrackList(Sequence* parent, Track::Type type); TrackList* copy(Sequence* parent); + void Save(QXmlStreamWriter& stream); + void AddTrack(); void RemoveTrack(int i); Track* First(); int TrackCount(); + int IndexOfTrack(Track* track); + Track* TrackAt(int i); QVector tracks(); Sequence* GetParent(); diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index ae3ac7c6f..7f290b425 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -1,9 +1,28 @@ #include "timelinearea.h" TimelineArea::TimelineArea() : - track_list_(nullptr) + track_list_(nullptr), + alignment_(olive::timeline::kAlignmentTop) { + QHBoxLayout* layout = new QHBoxLayout(this); + // LABELS + QWidget* label_container = new QWidget(); + QVBoxLayout* label_container_layout = new QVBoxLayout(label_container); + layout->addWidget(label_container); + + // VIEW + view_ = new TimelineView(); + layout->addWidget(view_); + + // SCROLLBAR + QScrollBar* scrollbar = new QScrollBar(Qt::Vertical); + layout->addWidget(scrollbar); +} + +void TimelineArea::SetAlignment(olive::timeline::Alignment alignment) +{ + alignment_ = alignment; } void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) @@ -29,7 +48,7 @@ void TimelineArea::RefreshLabels() labels_.resize(track_list_->TrackCount()); for (int i=0;iTrackAt(i)); } } diff --git a/ui/timelinearea.h b/ui/timelinearea.h index 1e35b46f9..dd8fe0ccc 100644 --- a/ui/timelinearea.h +++ b/ui/timelinearea.h @@ -4,6 +4,7 @@ #include "timeline/sequence.h" #include "timeline/track.h" #include "timeline/tracklist.h" +#include "timeline/timelinefunctions.h" #include "ui/timelineview.h" #include "ui/timelinelabel.h" @@ -13,6 +14,7 @@ class TimelineArea : public QWidget public: TimelineArea(); + void SetAlignment(olive::timeline::Alignment alignment); void SetTrackList(Sequence* sequence, Track::Type track_list); public slots: void RefreshLabels(); @@ -20,6 +22,7 @@ private: TrackList* track_list_; TimelineView* view_; QVector labels_; + olive::timeline::Alignment alignment_; }; #endif // TIMELINEAREA_H diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 8616461ec..f9e8cd484 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -72,6 +72,8 @@ TimelineView::TimelineView(QWidget *parent) : QWidget(parent) { track_resizing = false; setMouseTracking(true); + setFocusPolicy(Qt::ClickFocus); + setAcceptDrops(true); setContextMenuPolicy(Qt::CustomContextMenu); diff --git a/ui/timelineview.h b/ui/timelineview.h index 539f291a2..6cbf22773 100644 --- a/ui/timelineview.h +++ b/ui/timelineview.h @@ -43,7 +43,7 @@ void draw_waveform(ClipPtr clip, const FootageStream *ms, long media_length, QPa class TimelineView : public QWidget { Q_OBJECT public: - explicit TimelineView(QWidget *parent); + explicit TimelineView(QWidget *parent = nullptr); QScrollBar* scrollBar; bool bottom_align; diff --git a/undo/undo.cpp b/undo/undo.cpp index a8baada80..e562f7e77 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -90,14 +90,12 @@ void MoveClipAction::doRedo() { } } -DeleteClipAction::DeleteClipAction(Sequence *s, int clip) { - seq = s; - index = clip; - opening_transition = -1; - closing_transition = -1; -} +DeleteClipAction::DeleteClipAction(Clip *clip) +{ + // Get shared_ptr object to take ownership of this Clip -DeleteClipAction::~DeleteClipAction() {} + clip_ = clip->track()->GetClipObjectFromRawPtr(clip); +} void DeleteClipAction::doUndo() { // restore ref to clip @@ -113,13 +111,14 @@ void DeleteClipAction::doUndo() { void DeleteClipAction::doRedo() { // remove ref to clip - ref = seq->clips.at(index); - if (ref->IsOpen()) { - ref->Close(true); + if (clip_->IsOpen()) { + clip_->Close(true); } - seq->clips[index] = nullptr; + + clip_->track()->RemoveClip(clip_.get()); // delete link to this clip + QVector clips = clip_->track()-> linkClipIndex.clear(); linkLinkIndex.clear(); for (int i=0;iclips.size();i++) { diff --git a/undo/undo.h b/undo/undo.h index 73b5c43e1..cb58f304a 100644 --- a/undo/undo.h +++ b/undo/undo.h @@ -113,20 +113,13 @@ private: class DeleteClipAction : public OliveAction { public: - DeleteClipAction(Sequence* s, int clip); - virtual ~DeleteClipAction() override; + DeleteClipAction(Clip* clip); virtual void doUndo() override; virtual void doRedo() override; private: - Sequence* seq; - ClipPtr ref; - int index; + ClipPtr clip_; - int opening_transition; - int closing_transition; - - QVector linkClipIndex; - QVector linkLinkIndex; + QVector clips_linked_to_this_one_; }; class ChangeSequenceAction : public OliveAction {