diff --git a/mainwindow.cpp b/mainwindow.cpp index 0924c91fb..6e55864f1 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -196,7 +196,9 @@ void MainWindow::on_actionAbout_triggered() void MainWindow::on_actionDelete_triggered() { - if (panel_timeline->focused()) { + if (panel_timeline->ui->headers->hasFocus()) { + panel_timeline->ui->headers->delete_markers(); + } else if (panel_timeline->focused()) { panel_timeline->delete_selection(false); } else if (panel_effect_controls->is_focused()) { panel_effect_controls->delete_effects(); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index f9a727eee..a2318f599 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -1065,31 +1065,44 @@ bool Timeline::snap_to_point(long point, long* l) { return false; } -void Timeline::snap_to_clip(long* l, bool playhead_inclusive) { +bool Timeline::snap_to_timeline(long* l, bool use_playhead, bool use_markers, bool use_workarea) { snapped = false; - if (snapping) { - if (playhead_inclusive && !playing) { - playhead_inclusive = snap_to_point(sequence->playhead, l); - } else { - playhead_inclusive = false; - } - if (!playhead_inclusive) { - for (int i=0;iclips.size();i++) { - Clip* c = sequence->clips.at(i); - if (c != NULL) { - if (snap_to_point(c->timeline_in, l)) { - break; - } else if (snap_to_point(c->timeline_out, l)) { - break; - } else if (c->opening_transition != NULL && snap_to_point(c->timeline_in + c->opening_transition->length, l)) { - break; - } else if (c->closing_transition != NULL && snap_to_point(c->timeline_out - c->closing_transition->length, l)) { - break; - } - } - } - } + if (snapping) { + if (use_playhead && !playing) { + // snap to playhead + if (snap_to_point(sequence->playhead, l)) return true; + } + + // snap to marker + if (use_markers) { + for (int i=0;imarkers.size();i++) { + if (snap_to_point(sequence->markers.at(i).frame, l)) return true; + } + } + + // snap to in/out + if (use_workarea && sequence->using_workarea) { + if (snap_to_point(sequence->workarea_in, l)) return true; + if (snap_to_point(sequence->workarea_out, l)) return true; + } + + // snap to clip/transition + for (int i=0;iclips.size();i++) { + Clip* c = sequence->clips.at(i); + if (c != NULL) { + if (snap_to_point(c->timeline_in, l)) { + return true; + } else if (snap_to_point(c->timeline_out, l)) { + return true; + } else if (c->opening_transition != NULL && snap_to_point(c->timeline_in + c->opening_transition->length, l)) { + return true; + } else if (c->closing_transition != NULL && snap_to_point(c->timeline_out - c->closing_transition->length, l)) { + return true; + } + } + } } + return false; } void Timeline::set_marker() { diff --git a/panels/timeline.h b/panels/timeline.h index 86173200f..197093fd0 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -102,7 +102,7 @@ public: int get_snap_range(); bool snap_to_point(long point, long* l); - void snap_to_clip(long* l, bool playhead_inclusive); + bool snap_to_timeline(long* l, bool use_playhead, bool use_markers, bool use_workarea); void set_marker(); // playback functions diff --git a/project/undo.cpp b/project/undo.cpp index f9720f78e..529648410 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -19,6 +19,7 @@ #include "effects/transition.h" #include "ui/labelslider.h" #include "ui/viewerwidget.h" +#include "project/marker.h" QUndoStack undo_stack; @@ -1416,3 +1417,44 @@ void AddMarkerAction::redo() { project_changed = true; } + +MoveMarkerAction::MoveMarkerAction(Marker* m, long o, long n) : + marker(m), + old_time(o), + new_time(n) +{} + +void MoveMarkerAction::undo() { + marker->frame = old_time; +} + +void MoveMarkerAction::redo() { + marker->frame = new_time; +} + +DeleteMarkerAction::DeleteMarkerAction(Sequence* s) : + seq(s), + sorted(false) +{} + +void DeleteMarkerAction::undo() { + for (int i=markers.size()-1;i>=0;i--) { + seq->markers.insert(markers.at(i), copies.at(i)); + } +} + +void DeleteMarkerAction::redo() { + for (int i=0;imarkers.at(markers.at(i))); + for (int j=i+1;j markers.at(i)) { + markers[j]--; + } + } + } + seq->markers.removeAt(markers.at(i)); + } + sorted = true; +} diff --git a/project/undo.h b/project/undo.h index 6ef023498..60e38292f 100644 --- a/project/undo.h +++ b/project/undo.h @@ -13,6 +13,8 @@ struct Clip; struct Sequence; struct Media; +#include "project/marker.h" + #include #include #include @@ -477,4 +479,27 @@ private: int index; }; +class MoveMarkerAction : public QUndoCommand { +public: + MoveMarkerAction(Marker* m, long o, long n); + void undo(); + void redo(); +private: + Marker* marker; + long old_time; + long new_time; +}; + +class DeleteMarkerAction : public QUndoCommand { +public: + DeleteMarkerAction(Sequence* s); + void undo(); + void redo(); + QVector markers; +private: + Sequence* seq; + QVector copies; + bool sorted; +}; + #endif // UNDO_H diff --git a/ui/timelineheader.cpp b/ui/timelineheader.cpp index 64b90129b..b3434426c 100644 --- a/ui/timelineheader.cpp +++ b/ui/timelineheader.cpp @@ -18,15 +18,32 @@ #define LINE_MIN_PADDING 50 #define MARKER_SIZE 4 -TimelineHeader::TimelineHeader(QWidget *parent) : QWidget(parent), dragging(false), resizing_workarea(false), zoom(1), in_visible(0), snapping(true), fm(font()) { +TimelineHeader::TimelineHeader(QWidget *parent) : + QWidget(parent), + dragging(false), + resizing_workarea(false), + zoom(1), + in_visible(0), + snapping(true), + fm(font()), + dragging_markers(false) +{ setCursor(Qt::ArrowCursor); setMouseTracking(true); setMinimumHeight(fm.height()*2); } +long TimelineHeader::getHeaderFrameFromScreenPoint(int x) { + return getFrameFromScreenPoint(zoom, x) + in_visible; +} + +int TimelineHeader::getHeaderScreenPointFromFrame(long frame) { + return getScreenPointFromFrame(zoom, frame - in_visible); +} + void TimelineHeader::set_playhead(int mouse_x) { - long frame = getFrameFromScreenPoint(zoom, mouse_x) + in_visible; - if (snapping) panel_timeline->snap_to_clip(&frame, false); + long frame = getHeaderFrameFromScreenPoint(mouse_x); + if (snapping) panel_timeline->snap_to_timeline(&frame, false, true, true); panel_timeline->seek(frame); } @@ -44,8 +61,7 @@ void TimelineHeader::set_in_point(long new_in) { } undo_stack.push(new SetTimelineInOutCommand(sequence, true, new_in, new_out)); - - update(); + panel_timeline->repaint_timeline(); } void TimelineHeader::set_out_point(long new_out) { @@ -55,41 +71,107 @@ void TimelineHeader::set_out_point(long new_out) { } else if (new_in > new_out) { new_in = 0; } - undo_stack.push(new SetTimelineInOutCommand(sequence, true, new_in, new_out)); - - update(); + panel_timeline->repaint_timeline(); } void TimelineHeader::mousePressEvent(QMouseEvent* event) { if (resizing_workarea) { - sequence_end = sequence->getEndFrame(); - } else { - set_playhead(event->pos().x()); + sequence_end = sequence->getEndFrame(); + } else { + bool shift = (event->modifiers() & Qt::ShiftModifier); + bool clicked_on_marker = false; + for (int i=0;imarkers.size();i++) { + int marker_pos = getHeaderScreenPointFromFrame(sequence->markers.at(i).frame); + if (event->pos().x() > marker_pos - MARKER_SIZE && event->pos().x() < marker_pos + MARKER_SIZE) { + bool found = false; + for (int j=0;jmarkers.at(selected_markers.at(i)).frame; + } + drag_start = event->pos().x(); + dragging_markers = true; + } else { + if (selected_markers.size() > 0) { + selected_markers.clear(); + update(); + } + set_playhead(event->pos().x()); + } } dragging = true; } void TimelineHeader::mouseMoveEvent(QMouseEvent* event) { if (dragging) { - if (resizing_workarea) { - long frame = getFrameFromScreenPoint(zoom, event->pos().x()); - panel_timeline->snap_to_clip(&frame, true); + if (resizing_workarea) { + long frame = getHeaderFrameFromScreenPoint(event->pos().x()); + panel_timeline->snap_to_timeline(&frame, true, true, false); + 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 { + + panel_timeline->repaint_timeline(); + } else if (dragging_markers) { + long frame_movement = getHeaderFrameFromScreenPoint(event->pos().x()) - getHeaderFrameFromScreenPoint(drag_start); + + // snap markers + for (int i=0;isnap_to_timeline(&fm, true, false, true)) { + frame_movement = fm - selected_marker_original_times.at(i); + break; + } + } + + // validate markers (ensure none go below 0) + long validator; + for (int i=0;imarkers[selected_markers.at(i)].frame = selected_marker_original_times.at(i) + frame_movement; + } + + panel_timeline->repaint_timeline(); + } else { set_playhead(event->pos().x()); } } else { - resizing_workarea = false; + resizing_workarea = false; unsetCursor(); if (sequence->using_workarea) { - long min_frame = getFrameFromScreenPoint(zoom, event->pos().x() - CLICK_RANGE) - 1; - long max_frame = getFrameFromScreenPoint(zoom, event->pos().x() + CLICK_RANGE) + 1; + long min_frame = getHeaderFrameFromScreenPoint(event->pos().x() - CLICK_RANGE) - 1; + long max_frame = getHeaderFrameFromScreenPoint(event->pos().x() + CLICK_RANGE) + 1; if (sequence->workarea_in > min_frame && sequence->workarea_in < max_frame) { resizing_workarea = true; resizing_workarea_in = true; @@ -102,19 +184,40 @@ void TimelineHeader::mouseMoveEvent(QMouseEvent* event) { temp_workarea_out = sequence->workarea_out; setCursor(Qt::SizeHorCursor); } - } + } } } void TimelineHeader::mouseReleaseEvent(QMouseEvent*) { if (resizing_workarea) { undo_stack.push(new SetTimelineInOutCommand(sequence, true, temp_workarea_in, temp_workarea_out)); - } + } else if (dragging_markers && selected_markers.size() > 0) { + bool moved = false; + ComboAction* ca = new ComboAction(); + for (int i=0;imarkers[selected_markers.at(i)]; + if (selected_marker_original_times.at(i) != m->frame) { + ca->append(new MoveMarkerAction(m, selected_marker_original_times.at(i), m->frame)); + moved = true; + } + } + if (moved) { + undo_stack.push(ca); + } else { + delete ca; + } + } resizing_workarea = false; dragging = false; + dragging_markers = false; panel_timeline->snapped = false; - panel_timeline->repaint_timeline(); + panel_timeline->repaint_timeline(); +} + +void TimelineHeader::focusOutEvent(QFocusEvent*) { + selected_markers.clear(); + update(); } void TimelineHeader::update_header(double z) { @@ -122,6 +225,17 @@ void TimelineHeader::update_header(double z) { update(); } +void TimelineHeader::delete_markers() { + if (selected_markers.size() > 0) { + DeleteMarkerAction* dma = new DeleteMarkerAction(sequence); + for (int i=0;imarkers.append(selected_markers.at(i)); + } + undo_stack.push(dma); + panel_timeline->repaint_timeline(); + } +} + void TimelineHeader::paintEvent(QPaintEvent*) { if (sequence != NULL) { QPainter p(this); @@ -177,8 +291,8 @@ void TimelineHeader::paintEvent(QPaintEvent*) { // draw in/out selection int in_x; if (sequence->using_workarea) { - in_x = getScreenPointFromFrame(zoom, resizing_workarea ? temp_workarea_in : sequence->workarea_in); - int out_x = getScreenPointFromFrame(zoom, resizing_workarea ? temp_workarea_out :sequence->workarea_out); + in_x = getHeaderScreenPointFromFrame((resizing_workarea ? temp_workarea_in : sequence->workarea_in)); + int out_x = getHeaderScreenPointFromFrame((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()); @@ -188,7 +302,7 @@ void TimelineHeader::paintEvent(QPaintEvent*) { // draw markers for (int i=0;imarkers.size();i++) { const Marker& m = sequence->markers.at(i); - int marker_x = getScreenPointFromFrame(zoom, m.frame); + int marker_x = getHeaderScreenPointFromFrame(m.frame); const QPoint points[5] = { QPoint(marker_x, height()-1), QPoint(marker_x + MARKER_SIZE, height() - MARKER_SIZE - 1), @@ -197,12 +311,23 @@ void TimelineHeader::paintEvent(QPaintEvent*) { QPoint(marker_x - MARKER_SIZE, height() - MARKER_SIZE - 1) }; p.setPen(Qt::black); - p.setBrush(QColor(128, 224, 128)); + bool selected = false; + for (int j=0;jplayhead - in_visible); + in_x = getHeaderScreenPointFromFrame(sequence->playhead); QPoint start(in_x, height()+2); QPainterPath path; path.moveTo(start); diff --git a/ui/timelineheader.h b/ui/timelineheader.h index 305d71e93..2f4d8cdb6 100644 --- a/ui/timelineheader.h +++ b/ui/timelineheader.h @@ -17,11 +17,14 @@ public: void update_header(double z); + void delete_markers(); + protected: void paintEvent(QPaintEvent*); void mousePressEvent(QMouseEvent*); void mouseMoveEvent(QMouseEvent*); void mouseReleaseEvent(QMouseEvent*); + void focusOutEvent(QFocusEvent *event); private: bool dragging; @@ -40,6 +43,14 @@ private: QFontMetrics fm; + int drag_start; + bool dragging_markers; + QVector selected_markers; + QVector selected_marker_original_times; + + long getHeaderFrameFromScreenPoint(int x); + int getHeaderScreenPointFromFrame(long frame); + signals: public slots: diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 9e42c509e..417da5b04 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -945,60 +945,32 @@ void TimelineWidget::init_ghosts() { } } -bool subvalidate_snapping(const Ghost& g, long* frame_diff, long snap_point) { - int snap_range = panel_timeline->get_snap_range(); - long in_validator = g.old_in + *frame_diff - snap_point; - long out_validator = g.old_out + *frame_diff - snap_point; - - if ((panel_timeline->trim_target == -1 || g.trim_in) && in_validator > -snap_range && in_validator < snap_range) { - *frame_diff -= in_validator; - panel_timeline->snap_point = snap_point; - panel_timeline->snapped = true; - return true; - } else if ((panel_timeline->trim_target == -1 || !g.trim_in) && out_validator > -snap_range && out_validator < snap_range) { - *frame_diff -= out_validator; - panel_timeline->snap_point = snap_point; - panel_timeline->snapped = true; - return true; - } - return false; -} - -void validate_snapping(const Ghost& g, long* frame_diff) { - if (panel_timeline->snapping) { - bool snap_to_clip = true; - if (!panel_timeline->playing) { - snap_to_clip = !subvalidate_snapping(g, frame_diff, sequence->playhead); - } - if (snap_to_clip) { - for (int j=0;jclips.size();j++) { - Clip* c = sequence->clips.at(j); - if (c != NULL) { - if (!subvalidate_snapping(g, frame_diff, c->timeline_in) - && !subvalidate_snapping(g, frame_diff, c->timeline_out) - && (c->opening_transition != NULL && !subvalidate_snapping(g, frame_diff, c->timeline_in + c->opening_transition->length)) - && (c->closing_transition != NULL && !subvalidate_snapping(g, frame_diff, c->timeline_out - c->closing_transition->length))) {} - if (panel_timeline->snapped) break; - } - } - } - } -} - void TimelineWidget::update_ghosts(QPoint& mouse_pos) { int mouse_track = getTrackFromScreenPoint(mouse_pos.y()); long frame_diff = panel_timeline->getTimelineFrameFromScreenPoint(mouse_pos.x()) - panel_timeline->drag_frame_start; int track_diff = (panel_timeline->tool == TIMELINE_TOOL_SLIDE || panel_timeline->transition_select != TA_NO_TRANSITION) ? 0 : mouse_track - panel_timeline->drag_track_start; long validator; - // first try to snap - panel_timeline->snapped = false; + // first try to snap + long fm; if (panel_timeline->tool != TIMELINE_TOOL_SLIP) { // slipping doesn't move the clips so we don't bother snapping for it for (int i=0;ighosts.size();i++) { - Ghost& g = panel_timeline->ghosts[i]; - validate_snapping(g, &frame_diff); - if (panel_timeline->snapped) break; + Ghost& g = panel_timeline->ghosts[i]; + if (panel_timeline->trim_target == -1 || g.trim_in) { + fm = g.old_in + frame_diff; + if (panel_timeline->snap_to_timeline(&fm, true, true, true)) { + frame_diff = fm - g.old_in; + break; + } + } + if (panel_timeline->trim_target == -1 || !g.trim_in) { + fm = g.old_out + frame_diff; + if (panel_timeline->snap_to_timeline(&fm, true, true, true)) { + frame_diff = fm - g.old_out; + break; + } + } } } @@ -1199,7 +1171,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { panel_timeline->cursor_track = getTrackFromScreenPoint(event->pos().y()); if (isLiveEditing()) { - panel_timeline->snap_to_clip(&panel_timeline->cursor_frame, !config.edit_tool_also_seeks || !panel_timeline->selecting); + panel_timeline->snap_to_timeline(&panel_timeline->cursor_frame, !config.edit_tool_also_seeks || !panel_timeline->selecting, true, true); } 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) + panel_timeline->selection_offset;