diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus index 868e2a1171..a6f42d9281 100644 --- a/gtk2_ardour/ardour.menus +++ b/gtk2_ardour/ardour.menus @@ -232,6 +232,7 @@ + diff --git a/gtk2_ardour/ardour2_ui_dark.rc.in b/gtk2_ardour/ardour2_ui_dark.rc.in index 1af6c0f68c..9d354c6348 100644 --- a/gtk2_ardour/ardour2_ui_dark.rc.in +++ b/gtk2_ardour/ardour2_ui_dark.rc.in @@ -1341,6 +1341,7 @@ widget "*EditPointClock" style:highest "default_clock_display" widget "*PreRollClock" style:highest "default_clock_display" widget "*PostRollClock" style:highest "default_clock_display" widget "*NudgeClock" style:highest "default_clock_display" +widget "*InsertTimeClock" style:highest "default_clock_display" widget "*ZoomRangeClock" style:highest "default_clock_display" widget "*SMPTEOffsetClock" style:highest "default_clock_display" widget "*TransportLabel" style:highest "small_bold_text" diff --git a/gtk2_ardour/ardour2_ui_light.rc.in b/gtk2_ardour/ardour2_ui_light.rc.in index 5a5ab6cc3f..68474b12d1 100644 --- a/gtk2_ardour/ardour2_ui_light.rc.in +++ b/gtk2_ardour/ardour2_ui_light.rc.in @@ -1344,6 +1344,7 @@ widget "*EditPointClock" style:highest "default_clock_display" widget "*PreRollClock" style:highest "default_clock_display" widget "*PostRollClock" style:highest "default_clock_display" widget "*NudgeClock" style:highest "default_clock_display" +widget "*InsertTimeClock" style:highest "default_clock_display" widget "*ZoomRangeClock" style:highest "default_clock_display" widget "*SMPTEOffsetClock" style:highest "default_clock_display" widget "*TransportLabel" style:highest "small_bold_text" diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index b00a4c9214..bbdebf097d 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -83,7 +83,8 @@ AudioClock::AudioClock (std::string clock_name, bool transient, std::string widg key_entry_state = 0; ops_menu = 0; dragging = false; - + bbt_reference_time = -1; + if (with_info) { frames_upper_info_label = manage (new Label); frames_lower_info_label = manage (new Label); @@ -641,7 +642,16 @@ AudioClock::set_bbt (nframes_t when, bool force) ticks_label.set_text (buf); if (bbt_upper_info_label) { - TempoMap::Metric m (session->tempo_map().metric_at (when)); + nframes64_t pos; + + if (bbt_reference_time < 0) { + pos = when; + } else { + pos = bbt_reference_time; + } + + TempoMap::Metric m (session->tempo_map().metric_at (pos)); + sprintf (buf, "%-5.2f", m.tempo().beats_per_minute()); if (bbt_lower_info_label->get_text() != buf) { bbt_lower_info_label->set_text (buf); @@ -1995,3 +2005,9 @@ AudioClock::set_size_requests () } } + +void +AudioClock::set_bbt_reference (nframes64_t pos) +{ + bbt_reference_time = pos; +} diff --git a/gtk2_ardour/audio_clock.h b/gtk2_ardour/audio_clock.h index f9060b8933..06ec43f621 100644 --- a/gtk2_ardour/audio_clock.h +++ b/gtk2_ardour/audio_clock.h @@ -50,7 +50,8 @@ class AudioClock : public Gtk::HBox void set (nframes_t, bool force = false, nframes_t offset = 0, int which = 0); void set_mode (Mode); - + void set_bbt_reference (nframes64_t); + void set_widget_name (std::string); std::string name() const { return _name; } @@ -154,6 +155,7 @@ class AudioClock : public Gtk::HBox Gtk::EventBox clock_base; Gtk::Frame clock_frame; + nframes64_t bbt_reference_time; nframes_t last_when; bool last_pdelta; bool last_sdelta; diff --git a/gtk2_ardour/editing.h b/gtk2_ardour/editing.h index bdd8e92f10..e1876b1151 100644 --- a/gtk2_ardour/editing.h +++ b/gtk2_ardour/editing.h @@ -38,6 +38,7 @@ #define EDITPOINT(a) /*empty*/ #define WAVEFORMSCALE(a) /*empty*/ #define WAVEFORMSHAPE(a) /*empty*/ +#define INSERTTIMEOPT(a) /*empty*/ namespace Editing { @@ -186,6 +187,18 @@ enum WaveformShape { #undef WAVEFORMSHAPE #define WAVEFORMSHAPE(a) /*empty*/ + +// INSERTTIMEOPT +#undef INSERTTIMEOPT +#define INSERTTIMEOPT(a) a, +enum InsertTimeOption { + #include "editing_syms.h" +}; + +#undef INSERTTIMEOPT +#define INSERTTIMEOPT(a) /*empty*/ + + ///////////////////// // These don't need their state saved. yet... enum CutCopyOp { diff --git a/gtk2_ardour/editing_syms.h b/gtk2_ardour/editing_syms.h index 6820b16b12..e540d57c3e 100644 --- a/gtk2_ardour/editing_syms.h +++ b/gtk2_ardour/editing_syms.h @@ -102,3 +102,6 @@ WAVEFORMSHAPE(Traditional) WAVEFORMSHAPE(Rectified) +INSERTTIMEOPT(LeaveIntersected) +INSERTTIMEOPT(MoveIntersected) +INSERTTIMEOPT(SplitIntersected) diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 2d7ce4c10d..3b4ba9e5ff 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1012,6 +1012,9 @@ class Editor : public PublicEditor void denormalize_region (); void adjust_region_scale_amplitude (bool up); + void do_insert_time (); + void insert_time (nframes64_t pos, nframes64_t distance, Editing::InsertTimeOption opt, bool ignore_music_glue); + void tab_to_transient (bool forward); void use_region_as_bar (); diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 520f5205c6..54ebd6f95f 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -502,6 +502,10 @@ Editor::register_actions () act = ActionManager::register_action (editor_actions, "remove-last-capture", _("Remove Last Capture"), (mem_fun(*this, &Editor::remove_last_capture))); ActionManager::session_sensitive_actions.push_back (act); + act = ActionManager::register_action (editor_actions, "insert-time", _("Insert Time"), (mem_fun(*this, &Editor::do_insert_time))); + ActionManager::session_sensitive_actions.push_back (act); + ActionManager::track_selection_sensitive_actions.push_back (act); + act = ActionManager::register_action (editor_actions, "toggle-track-active", _("Toggle Active"), (mem_fun(*this, &Editor::toggle_tracks_active))); ActionManager::session_sensitive_actions.push_back (act); ActionManager::track_selection_sensitive_actions.push_back (act); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index be3f5f77f6..0f4d1d2f6d 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -5639,4 +5639,118 @@ Editor::set_waveform_scale (WaveformScale ws) } } } + +void +Editor::do_insert_time () +{ + if (selection->tracks.empty()) { + return; + } + + nframes64_t pos = get_preferred_edit_position (); + ArdourDialog d (*this, _("Insert Time")); + VButtonBox button_box; + VBox option_box; + RadioButtonGroup group; + RadioButton leave_button (group, _("Stay in position")); + RadioButton move_button (group, _("Move")); + RadioButton split_button (group, _("Split & Later Section Moves")); + Label intersect_option_label (_("Intersected regions should:")); + ToggleButton glue_button (_("Move Glued Regions")); + AudioClock clock ("insertTimeClock", true, X_("InsertTimeClock"), true, true, true); + HBox clock_box; + + clock.set (0); + clock.set_session (session); + clock.set_bbt_reference (pos); + + clock_box.pack_start (clock, false, true); + + option_box.set_spacing (6); + option_box.pack_start (intersect_option_label, false, false); + option_box.pack_start (button_box, false, false); + option_box.pack_start (glue_button, false, false); + + button_box.pack_start (leave_button, false, false); + button_box.pack_start (move_button, false, false); + button_box.pack_start (split_button, false, false); + + d.get_vbox()->set_border_width (12); + d.get_vbox()->pack_start (clock_box, false, false); + d.get_vbox()->pack_start (option_box, false, false); + + leave_button.show (); + move_button.show (); + split_button.show (); + intersect_option_label.show (); + option_box.show (); + button_box.show (); + glue_button.show (); + clock.show_all(); + clock_box.show (); + + d.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + d.add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK); + d.show (); + + int response = d.run (); + + if (response != RESPONSE_OK) { + return; + } + + nframes_t distance = clock.current_duration (pos); + + if (distance == 0) { + return; + } + + InsertTimeOption opt; + + if (leave_button.get_active()) { + opt = LeaveIntersected; + } else if (move_button.get_active()) { + opt = MoveIntersected; + } else { + opt = SplitIntersected; + } + + insert_time (pos, distance, opt, glue_button.get_active()); +} +void +Editor::insert_time (nframes64_t pos, nframes64_t frames, InsertTimeOption opt, bool ignore_music_glue) +{ + bool commit = false; + + if (Config->get_edit_mode() == Lock) { + return; + } + + begin_reversible_command (_("insert time")); + + for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) { + boost::shared_ptr pl = (*x)->playlist(); + + if (!pl) { + continue; + } + + XMLNode &before = pl->get_state(); + + if (opt == SplitIntersected) { + pl->split (pos); + } + + pl->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue); + + XMLNode &after = pl->get_state(); + + session->add_command (new MementoCommand (*pl, &before, &after)); + commit = true; + } + + if (commit) { + commit_reversible_command (); + } +} diff --git a/gtk2_ardour/mnemonic-us.bindings.in b/gtk2_ardour/mnemonic-us.bindings.in index 0799f1b06c..dd5e2d38d4 100644 --- a/gtk2_ardour/mnemonic-us.bindings.in +++ b/gtk2_ardour/mnemonic-us.bindings.in @@ -57,6 +57,7 @@ (gtk_accel_path "/Editor/redo" "<%PRIMARY%>r") (gtk_accel_path "/Transport/Record" "<%TERTIARY%>r") (gtk_accel_path "/MouseMode/set-mouse-mode-timefx" "t") +(gtk_accel_path "/Editor/insert-time" "<%PRIMARY%>t") (gtk_accel_path "/Editor/select-all-between-cursors" "u") (gtk_accel_path "/Editor/insert-region" "i") (gtk_accel_path "/Editor/invert-selection" "<%TERTIARY%>i") diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 0e7bf4eff9..232c0c15d4 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -89,6 +89,8 @@ class Playlist : public PBD::StatefulDestructible, public boost::enable_shared_f void get_region_list_equivalent_regions (boost::shared_ptr, std::vector >&); void replace_region (boost::shared_ptr old, boost::shared_ptr newr, nframes_t pos); void split_region (boost::shared_ptr, nframes_t position); + void split (nframes64_t at); + void shift (nframes64_t at, nframes64_t distance, bool move_intersected, bool ignore_music_glue); void partition (nframes_t start, nframes_t end, bool just_top_level); void duplicate (boost::shared_ptr, nframes_t position, float times); void nudge_after (nframes_t start, nframes_t distance, bool forwards); @@ -276,6 +278,8 @@ class Playlist : public PBD::StatefulDestructible, public boost::enable_shared_f void unset_freeze_child (Playlist*); void timestamp_layer_op (boost::shared_ptr); + + void _split_region (boost::shared_ptr, nframes_t position); }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 19600f3db6..e4d913082e 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -128,6 +128,7 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost:: PositionLockStyle positional_lock_style() const { return _positional_lock_style; } void set_position_lock_style (PositionLockStyle ps); + void recompute_position_from_lock_style (); virtual bool should_save_state () const { return !(_flags & DoNotSaveState); }; diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 09fd035c6e..9052f873db 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -1013,11 +1013,68 @@ Playlist::duplicate (boost::shared_ptr region, nframes_t position, float } } +void +Playlist::shift (nframes64_t at, nframes64_t distance, bool move_intersected, bool ignore_music_glue) +{ + RegionLock rlock (this); + RegionList copy (regions); + RegionList fixup; + + for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) { + + if ((*r)->last_frame() < at) { + /* too early */ + continue; + } + + if (at > (*r)->first_frame() && at < (*r)->last_frame()) { + /* intersected region */ + if (!move_intersected) { + continue; + } + } + + /* do not move regions glued to music time - that + has to be done separately. + */ + + if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) { + fixup.push_back (*r); + continue; + } + + (*r)->set_position ((*r)->position() + distance, this); + } + + for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) { + (*r)->recompute_position_from_lock_style (); + } +} + +void +Playlist::split (nframes64_t at) +{ + RegionLock rlock (this); + RegionList copy (regions); + + /* use a copy since this operation can modify the region list + */ + + for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) { + _split_region (*r, at); + } +} + void Playlist::split_region (boost::shared_ptr region, nframes_t playlist_position) { RegionLock rl (this); + _split_region (region, playlist_position); +} +void +Playlist::_split_region (boost::shared_ptr region, nframes_t playlist_position) +{ if (!region->covers (playlist_position)) { return; } diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 0819704bcf..3f41662000 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -352,11 +352,8 @@ Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute) _length = max_frames - _position; } - if (allow_bbt_recompute && _positional_lock_style == MusicTime) { - boost::shared_ptr pl (playlist()); - if (pl) { - pl->session().tempo_map().bbt_time (_position, _bbt_time); - } + if (allow_bbt_recompute) { + recompute_position_from_lock_style (); } invalidate_transients (); @@ -394,6 +391,17 @@ Region::set_position_on_top (nframes_t pos, void *src) send_change (PositionChanged); } +void +Region::recompute_position_from_lock_style () +{ + if (_positional_lock_style == MusicTime) { + boost::shared_ptr pl (playlist()); + if (pl) { + pl->session().tempo_map().bbt_time (_position, _bbt_time); + } + } +} + void Region::nudge_position (nframes64_t n, void *src) { diff --git a/svn_revision.h b/svn_revision.h index bada45458b..35346c9267 100644 --- a/svn_revision.h +++ b/svn_revision.h @@ -1,4 +1,4 @@ #ifndef __ardour_svn_revision_h__ #define __ardour_svn_revision_h__ -static const char* ardour_svn_revision = "3200"; +static const char* ardour_svn_revision = "3201"; #endif