X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=gtk2_ardour%2Feditor_ops.cc;h=f974792d50d59503e1335764b519713c56fb0047;hb=41c892802f75e62e452399f0b1c7319fee61e677;hp=9cee32cebbb3de5e0812e588a7ccadf0ad484e94;hpb=bb457bb960c5bd7ed538f9d31477293415739f68;p=ardour.git diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 9cee32cebb..f974792d50 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,8 @@ #include #include #include +#include + #include #include @@ -45,18 +48,23 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include "ardour_ui.h" #include "editor.h" #include "time_axis_view.h" +#include "route_time_axis.h" #include "audio_time_axis.h" #include "automation_time_axis.h" #include "streamview.h" +#include "audio_streamview.h" #include "audio_region_view.h" #include "midi_region_view.h" #include "rgb_macros.h" @@ -65,6 +73,8 @@ #include "editing.h" #include "gtk-custom-hruler.h" #include "gui_thread.h" +#include "keyboard.h" +#include "utils.h" #include "i18n.h" @@ -103,13 +113,16 @@ Editor::split_region () } void -Editor::split_region_at (nframes_t where) +Editor::split_region_at (nframes64_t where) { + RegionSelection rs; + + get_regions_for_action (rs); split_regions_at (where, selection->regions); } void -Editor::split_regions_at (nframes_t where, RegionSelection& regions) +Editor::split_regions_at (nframes64_t where, RegionSelection& regions) { list > used_playlists; @@ -135,6 +148,8 @@ Editor::split_regions_at (nframes_t where, RegionSelection& regions) snap_to (where); } + cerr << "Split " << regions.size() << " at " << where << endl; + for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) { RegionSelection::iterator tmp; @@ -210,21 +225,44 @@ Editor::remove_clicked_region () void Editor::remove_selected_regions () { - if (selection->regions.empty()) { + RegionSelection rs; + get_regions_for_action (rs); + + if (!session) { + return; + } + + if (rs.empty()) { return; } - /* XXX: should be called remove regions if we're removing more than one */ begin_reversible_command (_("remove region")); - - while (!selection->regions.empty()) { - boost::shared_ptr region = selection->regions.front()->region (); - boost::shared_ptr playlist = region->playlist (); + list > regions_to_remove; - XMLNode &before = playlist->get_state(); - playlist->remove_region (region); - XMLNode &after = playlist->get_state(); + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + // we can't just remove the region(s) in this loop because + // this removes them from the RegionSelection, and they thus + // disappear from underneath the iterator, and the ++i above + // SEGVs in a puzzling fashion. + + // so, first iterate over the regions to be removed from rs and + // add them to the regions_to_remove list, and then + // iterate over the list to actually remove them. + + regions_to_remove.push_back ((*i)->region()); + } + + for (list >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) { + boost::shared_ptr playlist = (*rl)->playlist(); + if (!playlist) { + // is this check necessary? + continue; + } + + XMLNode &before = playlist->get_state(); + playlist->remove_region (*rl); + XMLNode &after = playlist->get_state(); session->add_command(new MementoCommand(*playlist, &before, &after)); } @@ -236,7 +274,7 @@ Editor::select_region_for_operation (int dir, TimeAxisView **tv) { RegionView* rv; boost::shared_ptr region; - nframes_t start = 0; + nframes64_t start = 0; if (selection->time.start () == selection->time.end_frame ()) { @@ -279,7 +317,7 @@ Editor::extend_selection_to_end_of_region (bool next) { TimeAxisView *tv; boost::shared_ptr region; - nframes_t start; + nframes64_t start; if ((region = select_region_for_operation (next ? 1 : 0, &tv)) == 0) { return; @@ -307,7 +345,7 @@ Editor::extend_selection_to_start_of_region (bool previous) { TimeAxisView *tv; boost::shared_ptr region; - nframes_t end; + nframes64_t end; if ((region = select_region_for_operation (previous ? -1 : 0, &tv)) == 0) { return; @@ -330,20 +368,45 @@ Editor::extend_selection_to_start_of_region (bool previous) commit_reversible_command (); } +bool +Editor::nudge_forward_release (GdkEventButton* ev) +{ + if (ev->state & Keyboard::PrimaryModifier) { + nudge_forward (false, true); + } else { + nudge_forward (false, false); + } + return false; +} + +bool +Editor::nudge_backward_release (GdkEventButton* ev) +{ + if (ev->state & Keyboard::PrimaryModifier) { + nudge_backward (false, true); + } else { + nudge_backward (false, false); + } + return false; +} + void -Editor::nudge_forward (bool next) +Editor::nudge_forward (bool next, bool force_playhead) { - nframes_t distance; - nframes_t next_distance; + nframes64_t distance; + nframes64_t next_distance; + RegionSelection rs; + + get_regions_for_action (rs); if (!session) return; - if (!selection->regions.empty()) { + if (!force_playhead && !rs.empty()) { begin_reversible_command (_("nudge regions forward")); - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { boost::shared_ptr r ((*i)->region()); distance = get_nudge_distance (r->position(), next_distance); @@ -361,43 +424,48 @@ Editor::nudge_forward (bool next) commit_reversible_command (); - } else if (!selection->markers.empty()) { + } else if (!force_playhead && !selection->markers.empty()) { bool is_start; - Location* loc = find_location_from_marker (selection->markers.front(), is_start); - - if (loc) { - - begin_reversible_command (_("nudge location forward")); - XMLNode& before (loc->get_state()); - - if (is_start) { - distance = get_nudge_distance (loc->start(), next_distance); - if (next) { - distance = next_distance; - } - if (max_frames - distance > loc->start() + loc->length()) { - loc->set_start (loc->start() + distance); - } else { - loc->set_start (max_frames - loc->length()); - } - } else { - distance = get_nudge_distance (loc->end(), next_distance); - if (next) { - distance = next_distance; - } - if (max_frames - distance > loc->end()) { - loc->set_end (loc->end() + distance); + begin_reversible_command (_("nudge location forward")); + + for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) { + + Location* loc = find_location_from_marker ((*i), is_start); + + if (loc) { + + XMLNode& before (loc->get_state()); + + if (is_start) { + distance = get_nudge_distance (loc->start(), next_distance); + if (next) { + distance = next_distance; + } + if (max_frames - distance > loc->start() + loc->length()) { + loc->set_start (loc->start() + distance); + } else { + loc->set_start (max_frames - loc->length()); + } } else { - loc->set_end (max_frames); + distance = get_nudge_distance (loc->end(), next_distance); + if (next) { + distance = next_distance; + } + if (max_frames - distance > loc->end()) { + loc->set_end (loc->end() + distance); + } else { + loc->set_end (max_frames); + } } + XMLNode& after (loc->get_state()); + session->add_command (new MementoCommand(*loc, &before, &after)); } - XMLNode& after (loc->get_state()); - session->add_command (new MementoCommand(*loc, &before, &after)); - commit_reversible_command (); } + commit_reversible_command (); + } else { distance = get_nudge_distance (playhead_cursor->current_frame, next_distance); session->request_locate (playhead_cursor->current_frame + distance); @@ -405,18 +473,21 @@ Editor::nudge_forward (bool next) } void -Editor::nudge_backward (bool next) +Editor::nudge_backward (bool next, bool force_playhead) { - nframes_t distance; - nframes_t next_distance; + nframes64_t distance; + nframes64_t next_distance; + RegionSelection rs; + + get_regions_for_action (rs); if (!session) return; - if (!selection->regions.empty()) { + if (!force_playhead && !rs.empty()) { begin_reversible_command (_("nudge regions backward")); - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { boost::shared_ptr r ((*i)->region()); distance = get_nudge_distance (r->position(), next_distance); @@ -438,44 +509,51 @@ Editor::nudge_backward (bool next) commit_reversible_command (); - } else if (!selection->markers.empty()) { + } else if (!force_playhead && !selection->markers.empty()) { bool is_start; - Location* loc = find_location_from_marker (selection->markers.front(), is_start); - if (loc) { - - begin_reversible_command (_("nudge location forward")); - XMLNode& before (loc->get_state()); - - if (is_start) { - distance = get_nudge_distance (loc->start(), next_distance); - if (next) { - distance = next_distance; - } - if (distance < loc->start()) { - loc->set_start (loc->start() - distance); - } else { - loc->set_start (0); - } - } else { - distance = get_nudge_distance (loc->end(), next_distance); + begin_reversible_command (_("nudge location forward")); - if (next) { - distance = next_distance; - } + for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) { - if (distance < loc->end() - loc->length()) { - loc->set_end (loc->end() - distance); + Location* loc = find_location_from_marker ((*i), is_start); + + if (loc) { + + XMLNode& before (loc->get_state()); + + if (is_start) { + distance = get_nudge_distance (loc->start(), next_distance); + if (next) { + distance = next_distance; + } + if (distance < loc->start()) { + loc->set_start (loc->start() - distance); + } else { + loc->set_start (0); + } } else { - loc->set_end (loc->length()); + distance = get_nudge_distance (loc->end(), next_distance); + + if (next) { + distance = next_distance; + } + + if (distance < loc->end() - loc->length()) { + loc->set_end (loc->end() - distance); + } else { + loc->set_end (loc->length()); + } } + + XMLNode& after (loc->get_state()); + session->add_command (new MementoCommand(*loc, &before, &after)); } - - XMLNode& after (loc->get_state()); - session->add_command (new MementoCommand(*loc, &before, &after)); } - + + commit_reversible_command (); + } else { distance = get_nudge_distance (playhead_cursor->current_frame, next_distance); @@ -491,17 +569,20 @@ Editor::nudge_backward (bool next) void Editor::nudge_forward_capture_offset () { - nframes_t distance; + nframes64_t distance; + RegionSelection rs; + + get_regions_for_action (rs); if (!session) return; - if (!selection->regions.empty()) { + if (!rs.empty()) { begin_reversible_command (_("nudge forward")); distance = session->worst_output_latency(); - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { boost::shared_ptr r ((*i)->region()); XMLNode &before = r->playlist()->get_state(); @@ -518,17 +599,20 @@ Editor::nudge_forward_capture_offset () void Editor::nudge_backward_capture_offset () { - nframes_t distance; + nframes64_t distance; + RegionSelection rs; + + get_regions_for_action (rs); if (!session) return; - if (!selection->regions.empty()) { + if (!rs.empty()) { begin_reversible_command (_("nudge forward")); distance = session->worst_output_latency(); - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { boost::shared_ptr r ((*i)->region()); XMLNode &before = r->playlist()->get_state(); @@ -564,7 +648,7 @@ Editor::move_to_end () void Editor::build_region_boundary_cache () { - nframes_t pos = 0; + nframes64_t pos = 0; vector interesting_points; boost::shared_ptr r; TrackViewList tracks; @@ -607,8 +691,8 @@ Editor::build_region_boundary_cache () while (pos < session->current_end_frame() && !at_end) { - nframes_t rpos; - nframes_t lpos = max_frames; + nframes64_t rpos; + nframes64_t lpos = max_frames; for (vector::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) { @@ -656,7 +740,7 @@ Editor::build_region_boundary_cache () to sort later. */ - vector::iterator ri; + vector::iterator ri; for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) { if (*ri == rpos) { @@ -678,20 +762,20 @@ Editor::build_region_boundary_cache () } boost::shared_ptr -Editor::find_next_region (nframes_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack) +Editor::find_next_region (nframes64_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack) { TrackViewList::iterator i; - nframes_t closest = max_frames; + nframes64_t closest = max_frames; boost::shared_ptr ret; - nframes_t rpos = 0; + nframes64_t rpos = 0; float track_speed; - nframes_t track_frame; + nframes64_t track_frame; RouteTimeAxisView *rtav; for (i = tracks.begin(); i != tracks.end(); ++i) { - nframes_t distance; + nframes64_t distance; boost::shared_ptr r; track_speed = 1.0f; @@ -823,7 +907,7 @@ void Editor::cursor_to_region_point (Cursor* cursor, RegionPoint point, int32_t dir) { boost::shared_ptr r; - nframes_t pos = cursor->current_frame; + nframes64_t pos = cursor->current_frame; if (!session) { return; @@ -902,11 +986,15 @@ Editor::cursor_to_previous_region_point (Cursor* cursor, RegionPoint point) void Editor::cursor_to_selection_start (Cursor *cursor) { - nframes_t pos = 0; + nframes64_t pos = 0; + RegionSelection rs; + + get_regions_for_action (rs); + switch (mouse_mode) { case MouseObject: - if (!selection->regions.empty()) { - pos = selection->regions.start(); + if (!rs.empty()) { + pos = rs.start(); } break; @@ -930,12 +1018,15 @@ Editor::cursor_to_selection_start (Cursor *cursor) void Editor::cursor_to_selection_end (Cursor *cursor) { - nframes_t pos = 0; + nframes64_t pos = 0; + RegionSelection rs; + + get_regions_for_action (rs); switch (mouse_mode) { case MouseObject: - if (!selection->regions.empty()) { - pos = selection->regions.end_frame(); + if (!rs.empty()) { + pos = rs.end_frame(); } break; @@ -1021,7 +1112,7 @@ void Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir) { boost::shared_ptr r; - nframes_t pos; + nframes64_t pos; Location* loc; bool ignored; @@ -1097,7 +1188,7 @@ Editor::selected_marker_to_previous_region_point (RegionPoint point) void Editor::selected_marker_to_selection_start () { - nframes_t pos = 0; + nframes64_t pos = 0; Location* loc; bool ignored; @@ -1109,10 +1200,14 @@ Editor::selected_marker_to_selection_start () return; } + RegionSelection rs; + + get_regions_for_action (rs); + switch (mouse_mode) { case MouseObject: - if (!selection->regions.empty()) { - pos = selection->regions.start(); + if (!rs.empty()) { + pos = rs.start(); } break; @@ -1132,7 +1227,7 @@ Editor::selected_marker_to_selection_start () void Editor::selected_marker_to_selection_end () { - nframes_t pos = 0; + nframes64_t pos = 0; Location* loc; bool ignored; @@ -1144,10 +1239,14 @@ Editor::selected_marker_to_selection_end () return; } + RegionSelection rs; + + get_regions_for_action (rs); + switch (mouse_mode) { case MouseObject: - if (!selection->regions.empty()) { - pos = selection->regions.end_frame(); + if (!rs.empty()) { + pos = rs.end_frame(); } break; @@ -1167,8 +1266,8 @@ Editor::selected_marker_to_selection_end () void Editor::scroll_playhead (bool forward) { - nframes_t pos = playhead_cursor->current_frame; - nframes_t delta = (nframes_t) floor (current_page_frames() / 0.8); + nframes64_t pos = playhead_cursor->current_frame; + nframes64_t delta = (nframes64_t) floor (current_page_frames() / 0.8); if (forward) { if (pos == max_frames) { @@ -1200,8 +1299,8 @@ Editor::scroll_playhead (bool forward) void Editor::playhead_backward () { - nframes_t pos; - nframes_t cnt; + nframes64_t pos; + nframes64_t cnt; float prefix; bool was_floating; @@ -1209,15 +1308,15 @@ Editor::playhead_backward () cnt = 1; } else { if (was_floating) { - cnt = (nframes_t) floor (prefix * session->frame_rate ()); + cnt = (nframes64_t) floor (prefix * session->frame_rate ()); } else { - cnt = (nframes_t) prefix; + cnt = (nframes64_t) prefix; } } pos = playhead_cursor->current_frame; - if ((nframes_t) pos < cnt) { + if ((nframes64_t) pos < cnt) { pos = 0; } else { pos -= cnt; @@ -1234,8 +1333,8 @@ Editor::playhead_backward () void Editor::playhead_forward () { - nframes_t pos; - nframes_t cnt; + nframes64_t pos; + nframes64_t cnt; bool was_floating; float prefix; @@ -1243,9 +1342,9 @@ Editor::playhead_forward () cnt = 1; } else { if (was_floating) { - cnt = (nframes_t) floor (prefix * session->frame_rate ()); + cnt = (nframes64_t) floor (prefix * session->frame_rate ()); } else { - cnt = (nframes_t) floor (prefix); + cnt = (nframes64_t) floor (prefix); } } @@ -1304,9 +1403,9 @@ Editor::edit_cursor_backward () cnt = 1; } else { if (was_floating) { - cnt = (nframes_t) floor (prefix * session->frame_rate ()); + cnt = (nframes64_t) floor (prefix * session->frame_rate ()); } else { - cnt = (nframes_t) prefix; + cnt = (nframes64_t) prefix; } } @@ -1326,8 +1425,8 @@ Editor::edit_cursor_backward () void Editor::edit_cursor_forward () { - //nframes_t pos; - nframes_t cnt; + //nframes64_t pos; + nframes64_t cnt; bool was_floating; float prefix; @@ -1335,9 +1434,9 @@ Editor::edit_cursor_forward () cnt = 1; } else { if (was_floating) { - cnt = (nframes_t) floor (prefix * session->frame_rate ()); + cnt = (nframes64_t) floor (prefix * session->frame_rate ()); } else { - cnt = (nframes_t) floor (prefix); + cnt = (nframes64_t) floor (prefix); } } @@ -1350,16 +1449,16 @@ Editor::goto_frame () { float prefix; bool was_floating; - nframes_t frame; + nframes64_t frame; if (get_prefix (prefix, was_floating)) { return; } if (was_floating) { - frame = (nframes_t) floor (prefix * session->frame_rate()); + frame = (nframes64_t) floor (prefix * session->frame_rate()); } else { - frame = (nframes_t) floor (prefix); + frame = (nframes64_t) floor (prefix); } session->request_locate (frame); @@ -1368,19 +1467,19 @@ Editor::goto_frame () void Editor::scroll_backward (float pages) { - nframes_t frame; - nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit); + nframes64_t frame; + nframes64_t one_page = (nframes64_t) rint (canvas_width * frames_per_unit); bool was_floating; float prefix; - nframes_t cnt; + nframes64_t cnt; if (get_prefix (prefix, was_floating)) { - cnt = (nframes_t) floor (pages * one_page); + cnt = (nframes64_t) floor (pages * one_page); } else { if (was_floating) { - cnt = (nframes_t) floor (prefix * session->frame_rate()); + cnt = (nframes64_t) floor (prefix * session->frame_rate()); } else { - cnt = (nframes_t) floor (prefix * one_page); + cnt = (nframes64_t) floor (prefix * one_page); } } @@ -1396,19 +1495,19 @@ Editor::scroll_backward (float pages) void Editor::scroll_forward (float pages) { - nframes_t frame; - nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit); + nframes64_t frame; + nframes64_t one_page = (nframes64_t) rint (canvas_width * frames_per_unit); bool was_floating; float prefix; - nframes_t cnt; + nframes64_t cnt; if (get_prefix (prefix, was_floating)) { - cnt = (nframes_t) floor (pages * one_page); + cnt = (nframes64_t) floor (pages * one_page); } else { if (was_floating) { - cnt = (nframes_t) floor (prefix * session->frame_rate()); + cnt = (nframes64_t) floor (prefix * session->frame_rate()); } else { - cnt = (nframes_t) floor (prefix * one_page); + cnt = (nframes64_t) floor (prefix * one_page); } } @@ -1463,7 +1562,7 @@ Editor::scroll_tracks_down_line () { Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment(); - double vert_value = adj->get_value() + 20; + double vert_value = adj->get_value() + 60; if (vert_value>adj->get_upper() - canvas_height) { vert_value = adj->get_upper() - canvas_height; @@ -1475,7 +1574,7 @@ void Editor::scroll_tracks_up_line () { Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment(); - adj->set_value (adj->get_value() - 20); + adj->set_value (adj->get_value() - 60); } /* ZOOM */ @@ -1515,9 +1614,15 @@ Editor::temporal_zoom (gdouble fpu) double nfpu; double l; + /* XXX this limit is also in ::set_frames_per_unit() */ + + if (frames_per_unit <= 2.0 && fpu <= frames_per_unit) { + return; + } + nfpu = fpu; - new_page_size = (nframes_t) floor (canvas_width * nfpu); + new_page_size = (nframes64_t) floor (canvas_width * nfpu); half_page_size = new_page_size / 2; switch (zoom_focus) { @@ -1549,7 +1654,7 @@ Editor::temporal_zoom (gdouble fpu) where = playhead_cursor->current_frame; l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where); - + if (l < 0) { leftmost_after_zoom = 0; } else if (l > max_frames) { @@ -1617,26 +1722,36 @@ Editor::temporal_zoom (gdouble fpu) } void -Editor::temporal_zoom_region () +Editor::temporal_zoom_region (bool both_axes) { nframes64_t start = max_frames; nframes64_t end = 0; + RegionSelection rs; + set tracks; + double top_y_position = DBL_MAX; - ensure_entered_region_selected (true); + get_regions_for_action (rs); - if (selection->regions.empty()) { - info << _("cannot set loop: no region selected") << endmsg; + if (rs.empty()) { return; } - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + if ((*i)->region()->position() < start) { start = (*i)->region()->position(); } + if ((*i)->region()->last_frame() + 1 > end) { end = (*i)->region()->last_frame() + 1; } + + tracks.insert (&((*i)->get_time_axis_view())); + + if ((*i)->get_time_axis_view().y_position < top_y_position) { + top_y_position = (*i)->get_time_axis_view().y_position; + } } /* now comes an "interesting" hack ... make sure we leave a little space @@ -1649,8 +1764,15 @@ Editor::temporal_zoom_region () gint mmwidth = gdk_screen_get_width_mm (screen); double pix_per_mm = (double) pixwidth/ (double) mmwidth; double one_centimeter_in_pixels = pix_per_mm * 10.0; - nframes_t extra_samples = unit_to_frame (one_centimeter_in_pixels); - + + if ((start == 0 && end == 0) || end < start) { + return; + } + + nframes64_t range = end - start; + double new_fpu = (double)range / (double)canvas_width; + nframes64_t extra_samples = (nframes64_t) floor (one_centimeter_in_pixels * new_fpu); + if (start > extra_samples) { start -= extra_samples; } else { @@ -1663,17 +1785,53 @@ Editor::temporal_zoom_region () end = max_frames; } + if (both_axes) { + /* save visual state with track states included, and prevent + set_frames_per_unit() from doing it again. + */ + undo_visual_stack.push_back (current_visual_state(true)); + no_save_visual = true; + } + temporal_zoom_by_frame (start, end, "zoom to region"); + + if (both_axes) { + uint32_t per_track_height = (uint32_t) floor ((canvas_height - 10.0) / tracks.size()); + + /* set visible track heights appropriately */ + + for (set::iterator t = tracks.begin(); t != tracks.end(); ++t) { + (*t)->set_height (per_track_height); + } + + /* hide irrelevant tracks */ + + no_route_list_redisplay = true; + + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) { + hide_track_in_display (**i, true); + } + } + + no_route_list_redisplay = false; + redisplay_route_list (); + + vertical_adjustment.set_value (std::max (top_y_position - 5.0, 0.0)); + no_save_visual = false; + } + zoomed_to_region = true; + redo_visual_stack.push_back (current_visual_state()); } void -Editor::toggle_zoom_region () +Editor::toggle_zoom_region (bool both_axes) { if (zoomed_to_region) { swap_visual_state (); } else { - temporal_zoom_region (); + temporal_zoom_region (both_axes); } } @@ -1686,8 +1844,8 @@ Editor::temporal_zoom_selection () return; } - nframes_t start = selection->time[clicked_selection].start; - nframes_t end = selection->time[clicked_selection].end; + nframes64_t start = selection->time[clicked_selection].start; + nframes64_t end = selection->time[clicked_selection].end; temporal_zoom_by_frame (start, end, "zoom to selection"); } @@ -1698,12 +1856,13 @@ Editor::temporal_zoom_session () ENSURE_GUI_THREAD (mem_fun (*this, &Editor::temporal_zoom_session)); if (session) { + last_canvas_frame = ((session->current_end_frame() - session->current_start_frame()) + (current_page_frames() / 24)); temporal_zoom_by_frame (session->current_start_frame(), session->current_end_frame(), "zoom to session"); } } void -Editor::temporal_zoom_by_frame (nframes_t start, nframes_t end, const string & op) +Editor::temporal_zoom_by_frame (nframes64_t start, nframes64_t end, const string & op) { if (!session) return; @@ -1711,35 +1870,27 @@ Editor::temporal_zoom_by_frame (nframes_t start, nframes_t end, const string & o return; } - nframes_t range = end - start; + nframes64_t range = end - start; double new_fpu = (double)range / (double)canvas_width; -// double p2 = 1.0; - -// while (p2 < new_fpu) { -// p2 *= 2.0; -// } -// new_fpu = p2; - nframes_t new_page = (nframes_t) floor (canvas_width * new_fpu); - nframes_t middle = (nframes_t) floor( (double)start + ((double)range / 2.0f )); - nframes_t new_leftmost = (nframes_t) floor( (double)middle - ((double)new_page/2.0f)); + nframes64_t new_page = (nframes64_t) floor (canvas_width * new_fpu); + nframes64_t middle = (nframes64_t) floor( (double)start + ((double)range / 2.0f )); + nframes64_t new_leftmost = (nframes64_t) floor( (double)middle - ((double)new_page/2.0f)); - if (new_leftmost > middle) new_leftmost = 0; - -// begin_reversible_command (op); -// session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit)); -// session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu)); -// commit_reversible_command (); + if (new_leftmost > middle) { + new_leftmost = 0; + } reposition_and_zoom (new_leftmost, new_fpu); } void -Editor::temporal_zoom_to_frame (bool coarser, nframes_t frame) +Editor::temporal_zoom_to_frame (bool coarser, nframes64_t frame) { - if (!session) return; - + if (!session) { + return; + } double range_before = frame - leftmost_frame; double new_fpu; @@ -1753,12 +1904,15 @@ Editor::temporal_zoom_to_frame (bool coarser, nframes_t frame) range_before /= 1.61803399; } - if (new_fpu == frames_per_unit) return; - - nframes_t new_leftmost = frame - (nframes_t)range_before; + if (new_fpu == frames_per_unit) { + return; + } - if (new_leftmost > frame) new_leftmost = 0; + nframes64_t new_leftmost = frame - (nframes64_t)range_before; + if (new_leftmost > frame) { + new_leftmost = 0; + } // begin_reversible_command (_("zoom to frame")); // session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit)); // session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu)); @@ -1767,6 +1921,46 @@ Editor::temporal_zoom_to_frame (bool coarser, nframes_t frame) reposition_and_zoom (new_leftmost, new_fpu); } + +bool +Editor::choose_new_marker_name(string &name) { + + if (!Config->get_name_new_markers()) { + /* don't prompt user for a new name */ + return true; + } + + ArdourPrompter dialog (true); + + dialog.set_prompt (_("New Name:")); + + WindowTitle title(Glib::get_application_name()); + title += _("Name New Location Marker"); + + dialog.set_title(title.get_string()); + + dialog.set_name ("MarkNameWindow"); + dialog.set_size_request (250, -1); + dialog.set_position (Gtk::WIN_POS_MOUSE); + + dialog.add_button (Stock::OK, RESPONSE_ACCEPT); + dialog.set_initial_text (name); + + dialog.show (); + + switch (dialog.run ()) { + case RESPONSE_ACCEPT: + break; + default: + return false; + } + + dialog.get_result(name); + return true; + +} + + void Editor::add_location_from_selection () { @@ -1780,8 +1974,8 @@ Editor::add_location_from_selection () return; } - nframes_t start = selection->time[clicked_selection].start; - nframes_t end = selection->time[clicked_selection].end; + nframes64_t start = selection->time[clicked_selection].start; + nframes64_t end = selection->time[clicked_selection].end; session->locations()->next_available_name(rangename,"selection"); Location *location = new Location (start, end, rangename, Location::IsRangeMarker); @@ -1802,6 +1996,9 @@ Editor::add_location_mark (nframes64_t where) select_new_marker = true; session->locations()->next_available_name(markername,"mark"); + if (!choose_new_marker_name(markername)) { + return; + } Location *location = new Location (where, where, markername, Location::IsMark); session->begin_reversible_command (_("add marker")); XMLNode &before = session->locations()->get_state(); @@ -1820,11 +2017,15 @@ Editor::add_location_from_playhead_cursor () void Editor::add_location_from_audio_region () { - if (selection->regions.empty()) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { return; } - RegionView* rv = *(selection->regions.begin()); + RegionView* rv = *(rs.begin()); boost::shared_ptr region = rv->region(); Location *location = new Location (region->position(), region->last_frame(), region->name(), Location::IsRangeMarker); @@ -1926,7 +2127,7 @@ Editor::jump_backward_to_mark () void Editor::set_mark () { - nframes_t pos; + nframes64_t pos; float prefix; bool was_floating; string markername; @@ -1935,13 +2136,16 @@ Editor::set_mark () pos = session->audible_frame (); } else { if (was_floating) { - pos = (nframes_t) floor (prefix * session->frame_rate ()); + pos = (nframes64_t) floor (prefix * session->frame_rate ()); } else { - pos = (nframes_t) floor (prefix); + pos = (nframes64_t) floor (prefix); } } session->locations()->next_available_name(markername,"mark"); + if (!choose_new_marker_name(markername)) { + return; + } session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true); } @@ -2021,13 +2225,13 @@ Editor::insert_region_list_drag (boost::shared_ptr region, int x, int y) double wx, wy; double cx, cy; TimeAxisView *tv; - nframes_t where; + nframes64_t where; RouteTimeAxisView *rtv = 0; boost::shared_ptr playlist; - track_canvas.window_to_world (x, y, wx, wy); - wx += horizontal_adjustment.get_value(); - wy += vertical_adjustment.get_value(); + track_canvas->window_to_world (x, y, wx, wy); + //wx += horizontal_adjustment.get_value(); + //wy += vertical_adjustment.get_value(); GdkEvent event; event.type = GDK_BUTTON_RELEASE; @@ -2062,6 +2266,48 @@ Editor::insert_region_list_drag (boost::shared_ptr region, int x, int y) commit_reversible_command (); } +void +Editor::insert_route_list_drag (boost::shared_ptr route, int x, int y) { + double wx, wy; + double cx, cy; + TimeAxisView *tv; + nframes_t where; + RouteTimeAxisView *dest_rtv = 0; + RouteTimeAxisView *source_rtv = 0; + + track_canvas->window_to_world (x, y, wx, wy); + wx += horizontal_adjustment.get_value(); + wy += vertical_adjustment.get_value(); + + GdkEvent event; + event.type = GDK_BUTTON_RELEASE; + event.button.x = wx; + event.button.y = wy; + + where = event_frame (&event, &cx, &cy); + + if ((tv = trackview_by_y_position (cy)) == 0) { + return; + } + + if ((dest_rtv = dynamic_cast(tv)) == 0) { + return; + } + + /* use this drag source to add underlay to a track. But we really don't care + about the Route, only the view of the route, so find it first */ + for(TrackViewList::iterator it = track_views.begin(); it != track_views.end(); ++it) { + if((source_rtv = dynamic_cast(*it)) == 0) { + continue; + } + + if(source_rtv->route() == route && source_rtv != dest_rtv) { + dest_rtv->add_underlay(source_rtv->view()); + break; + } + } +} + void Editor::insert_region_list_selection (float times) { @@ -2200,14 +2446,19 @@ Editor::play_from_edit_point_and_return () nframes64_t start_frame; nframes64_t return_frame; + start_frame = get_preferred_edit_position (true); + + if (session->transport_rolling()) { + session->request_locate (start_frame, false); + return; + } + /* don't reset the return frame if its already set */ if ((return_frame = session->requested_return_frame()) < 0) { return_frame = session->audible_frame(); } - start_frame = get_preferred_edit_position (true); - if (start_frame >= 0) { session->request_roll_at_and_return (start_frame, return_frame); } @@ -2226,8 +2477,12 @@ Editor::play_selection () void Editor::loop_selected_region () { - if (!selection->regions.empty()) { - RegionView *rv = *(selection->regions.begin()); + RegionSelection rs; + + get_regions_for_action (rs); + + if (!rs.empty()) { + RegionView *rv = *(rs.begin()); Location* tll; if ((tll = transport_loop_location()) != 0) { @@ -2305,7 +2560,11 @@ Editor::edit_region () void Editor::rename_region() { - if (selection->regions.empty()) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { return; } @@ -2330,7 +2589,7 @@ Editor::rename_region() d.set_size_request (300, -1); d.set_position (Gtk::WIN_POS_MOUSE); - entry.set_text (selection->regions.front()->region()->name()); + entry.set_text (rs.front()->region()->name()); entry.select_region (0, -1); entry.signal_activate().connect (bind (mem_fun (d, &Dialog::response), RESPONSE_OK)); @@ -2347,7 +2606,7 @@ Editor::rename_region() std::string str = entry.get_text(); strip_whitespace_edges (str); if (!str.empty()) { - selection->regions.front()->region()->set_name (str); + rs.front()->region()->set_name (str); redisplay_regions (); } } @@ -2388,14 +2647,15 @@ Editor::play_selected_region () { nframes64_t start = max_frames; nframes64_t end = 0; + RegionSelection rs; - ensure_entered_region_selected (true); - - if (selection->regions.empty()) { + get_regions_for_action (rs); + + if (rs.empty()) { return; } - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { if ((*i)->region()->position() < start) { start = (*i)->region()->position(); } @@ -2404,6 +2664,7 @@ Editor::play_selected_region () } } + session->request_stop (); session->request_bounded_roll (start, end); } @@ -2420,6 +2681,11 @@ Editor::build_interthread_progress_window () interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT); + interthread_progress_window->set_border_width (12); + interthread_progress_window->get_vbox()->set_spacing (6); + + interthread_progress_label.set_alignment (0.5, 0.5); + interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false); interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false); @@ -2452,17 +2718,17 @@ Editor::region_from_selection () return; } - nframes_t start = selection->time[clicked_selection].start; - nframes_t end = selection->time[clicked_selection].end; + nframes64_t start = selection->time[clicked_selection].start; + nframes64_t end = selection->time[clicked_selection].end; - nframes_t selection_cnt = end - start + 1; + nframes64_t selection_cnt = end - start + 1; for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { boost::shared_ptr current; boost::shared_ptr current_r; boost::shared_ptr pl; - nframes_t internal_start; + nframes64_t internal_start; string new_name; if ((pl = (*i)->playlist()) == 0) { @@ -2484,14 +2750,14 @@ Editor::region_from_selection () } void -Editor::create_region_from_selection (vector >& new_regions) +Editor::create_region_from_selection (vector >& new_regions) { if (selection->time.empty() || selection->tracks.empty()) { return; } - nframes_t start = selection->time[clicked_selection].start; - nframes_t end = selection->time[clicked_selection].end; + nframes64_t start = selection->time[clicked_selection].start; + nframes64_t end = selection->time[clicked_selection].end; sort_track_selection (); @@ -2500,7 +2766,7 @@ Editor::create_region_from_selection (vector >& n boost::shared_ptr current; boost::shared_ptr current_r; boost::shared_ptr playlist; - nframes_t internal_start; + nframes64_t internal_start; string new_name; if ((playlist = (*i)->playlist()) == 0) { @@ -2525,13 +2791,17 @@ Editor::create_region_from_selection (vector >& n void Editor::split_multichannel_region () { - if (selection->regions.empty()) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { return; } vector > v; - for (list::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { + for (list::iterator x = rs.begin(); x != rs.end(); ++x) { AudioRegionView* arv = dynamic_cast(*x); @@ -2567,27 +2837,65 @@ Editor::separate_regions_between (const TimeSelection& ts) bool in_command = false; boost::shared_ptr playlist; RegionSelection new_selection; + TrackSelection tmptracks; + + if (selection->tracks.empty()) { - sort_track_selection (); + /* use tracks with selected regions */ - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { + RegionSelection rs; - RouteTimeAxisView* rtv; - - if ((rtv = dynamic_cast ((*i))) != 0) { + get_regions_for_action (rs); - boost::shared_ptr t = rtv->track(); + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + TimeAxisView* tv = &(*i)->get_time_axis_view(); - if (t != 0 && ! t->diskstream()->destructive()) { - - if ((playlist = rtv->playlist()) != 0) { + if (find (tmptracks.begin(), tmptracks.end(), tv) == tmptracks.end()) { + tmptracks.push_back (tv); + } + } - XMLNode *before = &(playlist->get_state()); - bool got_some = false; - - /* XXX need to consider musical time selections here at some point */ + if (tmptracks.empty()) { + /* no regions selected: do nothing */ + return; + } - double speed = t->diskstream()->speed(); + } else { + + tmptracks = selection->tracks; + + } + + sort_track_selection (&tmptracks); + + + + + for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) { + + RouteTimeAxisView* rtv; + + if ((rtv = dynamic_cast ((*i))) != 0) { + + if (rtv->is_track()) { + + /* no edits to destructive tracks */ + + if (rtv->track()->diskstream()->destructive()) { + continue; + } + + if ((playlist = rtv->playlist()) != 0) { + + XMLNode *before; + bool got_some; + + before = &(playlist->get_state()); + got_some = false; + + /* XXX need to consider musical time selections here at some point */ + + double speed = rtv->get_diskstream()->speed(); for (list::const_iterator t = ts.begin(); t != ts.end(); ++t) { @@ -2595,7 +2903,7 @@ Editor::separate_regions_between (const TimeSelection& ts) sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); latest_regionviews.clear (); - playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true); + playlist->partition ((nframes64_t)((*t).start * speed), (nframes64_t)((*t).end * speed), true); c.disconnect (); @@ -2638,6 +2946,7 @@ Editor::separate_region_from_selection () to allow discontiguous operation, since get_edit_op_range() currently returns a single range. */ + if (mouse_mode == MouseRange && !selection->time.empty()) { separate_regions_between (selection->time); @@ -2653,15 +2962,29 @@ Editor::separate_region_from_selection () TimeSelection ts; ts.push_back (ar); - /* force track selection */ - - ensure_entered_region_selected (); - separate_regions_between (ts); } } } +void +Editor::separate_region_from_punch () +{ + Location* loc = session->locations()->auto_punch_location(); + if (loc) { + separate_regions_using_location (*loc); + } +} + +void +Editor::separate_region_from_loop () +{ + Location* loc = session->locations()->auto_loop_location(); + if (loc) { + separate_regions_using_location (*loc); + } +} + void Editor::separate_regions_using_location (Location& loc) { @@ -2680,8 +3003,6 @@ Editor::separate_regions_using_location (Location& loc) void Editor::crop_region_to_selection () { - ensure_entered_region_selected (true); - if (!selection->time.empty()) { crop_region_to (selection->time.start(), selection->time.end_frame()); @@ -2699,7 +3020,7 @@ Editor::crop_region_to_selection () } void -Editor::crop_region_to (nframes_t start, nframes_t end) +Editor::crop_region_to (nframes64_t start, nframes64_t end) { vector > playlists; boost::shared_ptr playlist; @@ -2733,9 +3054,9 @@ Editor::crop_region_to (nframes_t start, nframes_t end) return; } - nframes_t the_start; - nframes_t the_end; - nframes_t cnt; + nframes64_t the_start; + nframes64_t the_end; + nframes64_t cnt; begin_reversible_command (_("trim to selection")); @@ -2753,7 +3074,7 @@ Editor::crop_region_to (nframes_t start, nframes_t end) if the selection extends beyond the region */ - the_start = max (the_start, region->position()); + the_start = max (the_start, (nframes64_t) region->position()); if (max_frames - the_start < region->length()) { the_end = the_start + region->length() - 1; } else { @@ -2774,9 +3095,12 @@ Editor::crop_region_to (nframes_t start, nframes_t end) void Editor::region_fill_track () { - nframes_t end; + nframes64_t end; + RegionSelection rs; + + get_regions_for_action (rs); - if (!session || selection->regions.empty()) { + if (!session || rs.empty()) { return; } @@ -2784,7 +3108,7 @@ Editor::region_fill_track () begin_reversible_command (_("region fill")); - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { boost::shared_ptr region ((*i)->region()); @@ -2833,8 +3157,8 @@ Editor::region_fill_selection () TreeModel::iterator i = region_list_display.get_selection()->get_selected(); boost::shared_ptr region = (*i)[region_list_columns.region]; - nframes_t start = selection->time[clicked_selection].start; - nframes_t end = selection->time[clicked_selection].end; + nframes64_t start = selection->time[clicked_selection].start; + nframes64_t end = selection->time[clicked_selection].end; boost::shared_ptr playlist; @@ -2842,7 +3166,7 @@ Editor::region_fill_selection () return; } - nframes_t selection_length = end - start; + nframes64_t selection_length = end - start; float times = (float)selection_length / region->length(); begin_reversible_command (_("fill selection")); @@ -2865,8 +3189,9 @@ void Editor::set_region_sync_from_edit_point () { nframes64_t where = get_preferred_edit_position (); - ensure_entered_region_selected (true); - set_sync_point (where, selection->regions); + RegionSelection rs; + get_regions_for_action (rs); + set_sync_point (where, rs); } void @@ -2902,27 +3227,38 @@ Editor::set_sync_point (nframes64_t where, const RegionSelection& rs) void Editor::remove_region_sync () { - begin_reversible_command (_("remove sync")); + RegionSelection rs; - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - boost::shared_ptr r = (*i)->region(); - XMLNode &before = r->playlist()->get_state(); - r->clear_sync_position (); - XMLNode &after = r->playlist()->get_state(); - session->add_command(new MementoCommand(*(r->playlist()), &before, &after)); + get_regions_for_action (rs); + + if (rs.empty()) { + return; } + begin_reversible_command (_("remove sync")); + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + + XMLNode &before = (*i)->region()->playlist()->get_state(); + (*i)->region()->clear_sync_position (); + XMLNode &after = (*i)->region()->playlist()->get_state(); + session->add_command(new MementoCommand(*((*i)->region()->playlist()), &before, &after)); + } commit_reversible_command (); } void Editor::naturalize () { - if (selection->regions.empty()) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { return; } + begin_reversible_command (_("naturalize")); - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { XMLNode &before = (*i)->region()->get_state(); (*i)->region()->move_to_natural_position (this); XMLNode &after = (*i)->region()->get_state(); @@ -2934,16 +3270,17 @@ Editor::naturalize () void Editor::align (RegionPoint what) { - ensure_entered_region_selected (); + RegionSelection rs; + get_regions_for_action (rs); nframes64_t where = get_preferred_edit_position(); - if (!selection->regions.empty()) { - align_selection (what, where, selection->regions); + if (!rs.empty()) { + align_selection (what, where, rs); } else { RegionSelection rs; - rs = get_regions_at (where, selection->tracks); + get_regions_at (rs, where, selection->tracks); align_selection (what, where, rs); } } @@ -2952,15 +3289,13 @@ void Editor::align_relative (RegionPoint what) { nframes64_t where = get_preferred_edit_position(); + RegionSelection rs; - if (!selection->regions.empty()) { - align_selection_relative (what, where, selection->regions); - } else { + get_regions_for_action (rs); - RegionSelection rs; - rs = get_regions_at (where, selection->tracks); + if (!rs.empty()) { align_selection_relative (what, where, rs); - } + } } struct RegionSortByTime { @@ -2970,45 +3305,72 @@ struct RegionSortByTime { }; void -Editor::align_selection_relative (RegionPoint point, nframes_t position, const RegionSelection& rs) +Editor::align_selection_relative (RegionPoint point, nframes64_t position, const RegionSelection& rs) { if (rs.empty()) { return; } - nframes_t distance; - nframes_t pos = 0; - int dir; + nframes64_t distance = 0; + nframes64_t pos = 0; + int dir = 1; list sorted; rs.by_position (sorted); + boost::shared_ptr r ((*sorted.begin())->region()); switch (point) { case Start: - pos = r->first_frame (); + pos = position; + if (position > r->position()) { + distance = position - r->position(); + } else { + distance = r->position() - position; + dir = -1; + } break; - + case End: - pos = r->last_frame(); + if (position > r->last_frame()) { + distance = position - r->last_frame(); + pos = r->position() + distance; + } else { + distance = r->last_frame() - position; + pos = r->position() - distance; + dir = -1; + } break; case SyncPoint: - pos = r->adjust_to_sync (r->first_frame()); + pos = r->adjust_to_sync (position); + if (pos > r->position()) { + distance = pos - r->position(); + } else { + distance = r->position() - pos; + dir = -1; + } break; } - if (pos > position) { - distance = pos - position; - dir = -1; - } else { - distance = position - pos; - dir = 1; + if (pos == r->position()) { + return; } begin_reversible_command (_("align selection (relative)")); - for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) { + /* move first one specially */ + + XMLNode &before = r->playlist()->get_state(); + r->set_position (pos, this); + XMLNode &after = r->playlist()->get_state(); + session->add_command(new MementoCommand(*(r->playlist()), &before, &after)); + + /* move rest by the same amount */ + + sorted.pop_front(); + + for (list::iterator i = sorted.begin(); i != sorted.end(); ++i) { boost::shared_ptr region ((*i)->region()); @@ -3029,7 +3391,7 @@ Editor::align_selection_relative (RegionPoint point, nframes_t position, const R } void -Editor::align_selection (RegionPoint point, nframes_t position, const RegionSelection& rs) +Editor::align_selection (RegionPoint point, nframes64_t position, const RegionSelection& rs) { if (rs.empty()) { return; @@ -3045,7 +3407,7 @@ Editor::align_selection (RegionPoint point, nframes_t position, const RegionSele } void -Editor::align_region (boost::shared_ptr region, RegionPoint point, nframes_t position) +Editor::align_region (boost::shared_ptr region, RegionPoint point, nframes64_t position) { begin_reversible_command (_("align region")); align_region_internal (region, point, position); @@ -3053,7 +3415,7 @@ Editor::align_region (boost::shared_ptr region, RegionPoint point, nfram } void -Editor::align_region_internal (boost::shared_ptr region, RegionPoint point, nframes_t position) +Editor::align_region_internal (boost::shared_ptr region, RegionPoint point, nframes64_t position) { XMLNode &before = region->playlist()->get_state(); @@ -3100,9 +3462,9 @@ Editor::trim_region_to_punch () void Editor::trim_region_to_location (const Location& loc, const char* str) { - ensure_entered_region_selected (); + RegionSelection rs; - RegionSelection& rs (get_regions_for_action ()); + get_regions_for_action (rs); begin_reversible_command (str); @@ -3129,8 +3491,8 @@ Editor::trim_region_to_location (const Location& loc, const char* str) } float speed = 1.0; - nframes_t start; - nframes_t end; + nframes64_t start; + nframes64_t end; if (atav->get_diskstream() != 0) { speed = atav->get_diskstream()->speed(); @@ -3151,7 +3513,10 @@ Editor::trim_region_to_location (const Location& loc, const char* str) void Editor::trim_region_to_edit_point () { - RegionSelection& rs (get_regions_for_action ()); + RegionSelection rs; + + get_regions_for_action (rs); + nframes64_t where = get_preferred_edit_position(); begin_reversible_command (_("trim region start to edit point")); @@ -3193,7 +3558,10 @@ Editor::trim_region_to_edit_point () void Editor::trim_region_from_edit_point () { - RegionSelection& rs (get_regions_for_action ()); + RegionSelection rs; + + get_regions_for_action (rs); + nframes64_t where = get_preferred_edit_position(); begin_reversible_command (_("trim region end to edit point")); @@ -3232,16 +3600,14 @@ Editor::trim_region_from_edit_point () commit_reversible_command (); } -/** Unfreeze selected routes */ void -Editor::unfreeze_routes () +Editor::unfreeze_route () { - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - AudioTimeAxisView* atv = dynamic_cast(*i); - if (atv && atv->is_audio_track()) { - atv->audio_track()->unfreeze (); - } + if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) { + return; } + + clicked_routeview->audio_track()->unfreeze (); } void* @@ -3254,15 +3620,7 @@ Editor::_freeze_thread (void* arg) void* Editor::freeze_thread () { - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - AudioTimeAxisView* atv = dynamic_cast(*i); - if (atv && atv->is_audio_track()) { - atv->audio_track()->freeze (*current_interthread_info); - } - } - - current_interthread_info->done = true; - + clicked_routeview->audio_track()->freeze (*current_interthread_info); return 0; } @@ -3273,10 +3631,13 @@ Editor::freeze_progress_timeout (void *arg) return !(current_interthread_info->done || current_interthread_info->cancel); } -/** Freeze selected routes */ void -Editor::freeze_routes () +Editor::freeze_route () { + if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) { + return; + } + InterThreadInfo itt; if (interthread_progress_window == 0) { @@ -3308,7 +3669,7 @@ Editor::freeze_routes () pthread_attr_destroy(&attr); - track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH)); + track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH)); while (!itt.done && !itt.cancel) { gtk_main_iteration (); @@ -3317,11 +3678,11 @@ Editor::freeze_routes () interthread_progress_connection.disconnect (); interthread_progress_window->hide_all (); current_interthread_info = 0; - track_canvas.get_window()->set_cursor (*current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } void -Editor::bounce_range_selection () +Editor::bounce_range_selection (bool replace) { if (selection->time.empty()) { return; @@ -3329,9 +3690,9 @@ Editor::bounce_range_selection () TrackSelection views = selection->tracks; - nframes_t start = selection->time[clicked_selection].start; - nframes_t end = selection->time[clicked_selection].end; - nframes_t cnt = end - start + 1; + nframes64_t start = selection->time[clicked_selection].start; + nframes64_t end = selection->time[clicked_selection].end; + nframes64_t cnt = end - start + 1; begin_reversible_command (_("bounce range")); @@ -3355,9 +3716,17 @@ Editor::bounce_range_selection () itt.cancel = false; itt.progress = false; - XMLNode &before = playlist->get_state(); - rtv->track()->bounce_range (start, cnt, itt); - XMLNode &after = playlist->get_state(); + XMLNode &before = playlist->get_state(); + boost::shared_ptr r = rtv->track()->bounce_range (start, start+cnt, itt); + + if (replace) { + list ranges; + ranges.push_back (AudioRange (start, start+cnt, 0)); + playlist->cut (ranges); // discard result + playlist->add_region (r, start); + } + + XMLNode &after = playlist->get_state(); session->add_command (new MementoCommand (*playlist, &before, &after)); } @@ -3426,6 +3795,19 @@ Editor::cut_copy (CutCopyOp op) opname = _("clear"); break; } + + /* if we're deleting something, and the mouse is still pressed, + the thing we started a drag for will be gone when we release + the mouse button(s). avoid this. see part 2 at the end of + this function. + */ + + if (op == Cut || op == Clear) { + if (drag_info.item) { + drag_info.item->ungrab (0); + drag_info.item = 0; + } + } cut_buffer->clear (); @@ -3440,19 +3822,27 @@ Editor::cut_copy (CutCopyOp op) Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc)); } + break_drag (); + return; } + RegionSelection rs; + + /* we only want to cut regions if some are selected */ + + if (!selection->regions.empty()) { + get_regions_for_action (rs); + } + switch (current_mouse_mode()) { case MouseObject: - cerr << "cutting in object mode\n"; - if (!selection->regions.empty() || !selection->points.empty()) { + if (!rs.empty() || !selection->points.empty()) { begin_reversible_command (opname + _(" objects")); - if (!selection->regions.empty()) { - cerr << "have regions to cut" << endl; - cut_copy_regions (op); + if (!rs.empty()) { + cut_copy_regions (op, rs); if (op == Cut) { selection->clear_regions (); @@ -3470,7 +3860,6 @@ Editor::cut_copy (CutCopyOp op) commit_reversible_command (); break; // terminate case statement here } - cerr << "nope, now cutting time range" << endl; if (!selection->time.empty()) { /* don't cause suprises */ break; @@ -3480,9 +3869,7 @@ Editor::cut_copy (CutCopyOp op) case MouseRange: if (selection->time.empty()) { nframes64_t start, end; - cerr << "no time selection, get edit op range" << endl; if (!get_edit_op_range (start, end)) { - cerr << "no edit op range" << endl; return; } selection->set ((TimeAxisView*) 0, start, end); @@ -3501,6 +3888,11 @@ Editor::cut_copy (CutCopyOp op) default: break; } + + + if (op == Cut || op == Clear) { + break_drag (); + } } /** Cut, copy or clear selected automation points. @@ -3542,7 +3934,7 @@ struct PlaylistMapping { * @param op Operation (Cut, Copy or Clear) */ void -Editor::cut_copy_regions (CutCopyOp op) +Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs) { /* we can't use a std::map here because the ordering is important, and we can't trivially sort a map when we want ordered access to both elements. i think. @@ -3550,18 +3942,18 @@ Editor::cut_copy_regions (CutCopyOp op) vector pmap; - nframes_t first_position = max_frames; + nframes64_t first_position = max_frames; set freezelist; pair::iterator,bool> insert_result; /* get ordering correct before we cut/copy */ - selection->regions.sort_by_position_and_track (); + rs.sort_by_position_and_track (); - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { + for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) { - first_position = min ((*x)->region()->position(), first_position); + first_position = min ((nframes64_t) (*x)->region()->position(), first_position); if (op == Cut || op == Clear) { boost::shared_ptr pl = (*x)->region()->playlist(); @@ -3594,7 +3986,7 @@ Editor::cut_copy_regions (CutCopyOp op) } } - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) { + for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) { boost::shared_ptr pl = (*x)->region()->playlist(); @@ -3677,9 +4069,14 @@ void Editor::cut_copy_ranges (CutCopyOp op) { TrackSelection* ts; + TrackSelection entered; if (selection->tracks.empty()) { - ts = &track_views; + if (!entered_track) { + return; + } + entered.push_back (entered_track); + ts = &entered; } else { ts = &selection->tracks; } @@ -3710,7 +4107,7 @@ Editor::mouse_paste () } void -Editor::paste_internal (nframes_t position, float times) +Editor::paste_internal (nframes64_t position, float times) { bool commit = false; @@ -3853,8 +4250,8 @@ Editor::duplicate_selection (float times) } boost::shared_ptr playlist; - vector > new_regions; - vector >::iterator ri; + vector > new_regions; + vector >::iterator ri; create_region_from_selection (new_regions); @@ -3870,9 +4267,9 @@ Editor::duplicate_selection (float times) if ((playlist = (*i)->playlist()) == 0) { continue; } - XMLNode &before = playlist->get_state(); + XMLNode &before = playlist->get_state(); playlist->duplicate (*ri, selection->time[clicked_selection].end, times); - XMLNode &after = playlist->get_state(); + XMLNode &after = playlist->get_state(); session->add_command (new MementoCommand(*playlist, &before, &after)); ++ri; @@ -3925,12 +4322,12 @@ Editor::clear_playlist (boost::shared_ptr playlist) } void -Editor::nudge_selected_tracks (bool use_edit, bool forwards) +Editor::nudge_track (bool use_edit, bool forwards) { boost::shared_ptr playlist; - nframes_t distance; - nframes_t next_distance; - nframes_t start; + nframes64_t distance; + nframes64_t next_distance; + nframes64_t start; if (use_edit) { start = get_preferred_edit_position(); @@ -3992,22 +4389,26 @@ Editor::remove_last_capture () } void -Editor::normalize_regions () +Editor::normalize_region () { + RegionSelection rs; + + get_regions_for_action (rs); + if (!session) { return; } - if (selection->regions.empty()) { + if (rs.empty()) { return; } begin_reversible_command (_("normalize")); - track_canvas.get_window()->set_cursor (*wait_cursor); + track_canvas->get_window()->set_cursor (*wait_cursor); gdk_flush (); - for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) { + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) { AudioRegionView* const arv = dynamic_cast(*r); if (!arv) continue; @@ -4017,24 +4418,28 @@ Editor::normalize_regions () } commit_reversible_command (); - track_canvas.get_window()->set_cursor (*current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } void -Editor::denormalize_regions () +Editor::denormalize_region () { if (!session) { return; } - if (selection->regions.empty()) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { return; } begin_reversible_command ("denormalize"); - for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) { + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) { AudioRegionView* const arv = dynamic_cast(*r); if (!arv) continue; @@ -4046,9 +4451,61 @@ Editor::denormalize_regions () commit_reversible_command (); } +void +Editor::adjust_region_scale_amplitude (bool up) +{ + if (!session) { + return; + } + + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + + begin_reversible_command ("denormalize"); + + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) { + AudioRegionView* const arv = dynamic_cast(*r); + if (!arv) + continue; + XMLNode &before = arv->region()->get_state(); + + double fraction = gain_to_slider_position (arv->audio_region()->scale_amplitude ()); + + if (up) { + fraction += 0.05; + fraction = min (fraction, 1.0); + } else { + fraction -= 0.05; + fraction = max (fraction, 0.0); + } + + if (!up && fraction <= 0) { + continue; + } + + fraction = slider_position_to_gain (fraction); + fraction = coefficient_to_dB (fraction); + fraction = dB_to_coefficient (fraction); + + if (up && fraction >= 2.0) { + continue; + } + + arv->audio_region()->set_scale_amplitude (fraction); + session->add_command (new MementoCommand(*(arv->region().get()), &before, &arv->region()->get_state())); + } + + commit_reversible_command (); +} + void -Editor::reverse_regions () +Editor::reverse_region () { if (!session) { return; @@ -4060,7 +4517,7 @@ Editor::reverse_regions () void -Editor::quantize_regions () +Editor::quantize_region () { if (!session) { return; @@ -4074,17 +4531,20 @@ Editor::quantize_regions () void Editor::apply_filter (Filter& filter, string command) { - if (selection->regions.empty()) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { return; } begin_reversible_command (command); - track_canvas.get_window()->set_cursor (*wait_cursor); + track_canvas->get_window()->set_cursor (*wait_cursor); gdk_flush (); - /* this is ugly. */ - for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) { + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) { RegionSelection::iterator tmp = r; ++tmp; @@ -4114,10 +4574,10 @@ Editor::apply_filter (Filter& filter, string command) } commit_reversible_command (); - selection->regions.clear (); + rs.clear (); out: - track_canvas.get_window()->set_cursor (*current_canvas_cursor); + track_canvas->get_window()->set_cursor (*current_canvas_cursor); } void @@ -4155,22 +4615,20 @@ Editor::external_edit_region () } void -Editor::brush (nframes_t pos) +Editor::brush (nframes64_t pos) { RegionSelection sel; - snap_to (pos); + RegionSelection rs; - if (selection->regions.empty()) { - /* XXX get selection from region list */ - } else { - sel = selection->regions; - } + get_regions_for_action (rs); - if (sel.empty()) { + snap_to (pos); + + if (rs.empty()) { return; } - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { mouse_brush_insert_region ((*i), pos); } } @@ -4178,13 +4636,17 @@ Editor::brush (nframes_t pos) void Editor::reset_region_gain_envelopes () { - if (!session || selection->regions.empty()) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (!session || rs.empty()) { return; } session->begin_reversible_command (_("reset region gain")); - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { AudioRegionView* const arv = dynamic_cast(*i); if (arv) { boost::shared_ptr alist (arv->audio_region()->envelope()); @@ -4201,13 +4663,14 @@ Editor::reset_region_gain_envelopes () void Editor::toggle_gain_envelope_visibility () { - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + RegionSelection rs; + + get_regions_for_action (rs); + + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { AudioRegionView* const arv = dynamic_cast(*i); if (arv) { - bool x = region_envelope_visible_item->get_active(); - if (x != arv->envelope_visible()) { - arv->set_envelope_visible (x); - } + arv->set_envelope_visible (!arv->envelope_visible()); } } } @@ -4215,100 +4678,94 @@ Editor::toggle_gain_envelope_visibility () void Editor::toggle_gain_envelope_active () { - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + RegionSelection rs; + + get_regions_for_action (rs); + + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { AudioRegionView* const arv = dynamic_cast(*i); if (arv) { - bool x = region_envelope_active_item->get_active(); - if (x != arv->audio_region()->envelope_active()) { - arv->audio_region()->set_envelope_active (x); - } + arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active()); } } } -/** Set the position-locked state of all selected regions to a particular value. - * @param yn true to make locked, false to make unlocked. - */ void -Editor::toggle_region_position_lock () +Editor::toggle_region_lock () { - bool x = region_lock_position_item->get_active(); + RegionSelection rs; - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - AudioRegionView* const arv = dynamic_cast(*i); - if (arv) { - if (x != arv->audio_region()->locked()) { - arv->audio_region()->set_position_locked (x); - } - } + get_regions_for_action (rs); + + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + (*i)->region()->set_locked (!(*i)->region()->locked()); } } void -Editor::toggle_region_lock () +Editor::set_region_lock_style (Region::PositionLockStyle ps) { - bool x = region_lock_item->get_active(); + RegionSelection rs; - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - AudioRegionView* const arv = dynamic_cast(*i); - if (arv) { - if (x != arv->audio_region()->locked()) { - arv->audio_region()->set_locked (x); - } - } + get_regions_for_action (rs); + + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + (*i)->region()->set_position_lock_style (ps); } } + void Editor::toggle_region_mute () { - bool x = region_mute_item->get_active(); + RegionSelection rs; - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - AudioRegionView* const arv = dynamic_cast(*i); - if (arv) { - if (x != arv->audio_region()->muted()) { - arv->audio_region()->set_muted (x); - } - } + get_regions_for_action (rs); + + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + (*i)->region()->set_muted (!(*i)->region()->muted()); } } void Editor::toggle_region_opaque () { - bool x = region_opaque_item->get_active(); + RegionSelection rs; - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - AudioRegionView* const arv = dynamic_cast(*i); - if (arv) { - if (x != arv->audio_region()->opaque()) { - arv->audio_region()->set_opaque (x); - } - } + get_regions_for_action (rs); + + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + (*i)->region()->set_opaque (!(*i)->region()->opaque()); } } void Editor::set_fade_length (bool in) { - ensure_entered_region_selected (true); + RegionSelection rs; + + get_regions_for_action (rs); /* we need a region to measure the offset from the start */ RegionView* rv; - if (entered_regionview) { + if (!rs.empty()) { + rv = rs.front(); + } else if (entered_regionview) { rv = entered_regionview; - } else if (!selection->regions.empty()) { - rv = selection->regions.front(); } else { return; } nframes64_t pos = get_preferred_edit_position(); - nframes_t len; + nframes64_t len; char* cmd; + + if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) { + /* edit point is outside the relevant region */ + return; + } if (in) { if (pos <= rv->region()->position()) { @@ -4328,8 +4785,6 @@ Editor::set_fade_length (bool in) begin_reversible_command (cmd); - RegionSelection& rs (get_regions_for_action()); - for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) { AudioRegionView* tmp = dynamic_cast (*x); @@ -4337,40 +4792,48 @@ Editor::set_fade_length (bool in) return; } - boost::shared_ptr alist = tmp->audio_region()->fade_in(); + boost::shared_ptr alist; + if (in) { + alist = tmp->audio_region()->fade_in(); + } else { + alist = tmp->audio_region()->fade_out(); + } + XMLNode &before = alist->get_state(); if (in) { tmp->audio_region()->set_fade_in_length (len); + tmp->audio_region()->set_fade_in_active (true); } else { tmp->audio_region()->set_fade_out_length (len); + tmp->audio_region()->set_fade_out_active (true); } XMLNode &after = alist->get_state(); - session->add_command(new MementoCommand(*alist.get(), &before, &after)); + session->add_command(new MementoCommand(*alist, &before, &after)); } commit_reversible_command (); } - void Editor::toggle_fade_active (bool in) { - ensure_entered_region_selected (true); + RegionSelection rs; + + get_regions_for_action (rs); - if (selection->regions.empty()) { + if (rs.empty()) { return; } const char* cmd = (in ? _("toggle fade in active") : _("toggle fade out active")); bool have_switch = false; - bool yn; - bool in_command = false; + bool yn = false; begin_reversible_command (cmd); - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { + for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) { AudioRegionView* tmp = dynamic_cast (*x); if (!tmp) { @@ -4382,28 +4845,41 @@ Editor::toggle_fade_active (bool in) /* make the behaviour consistent across all regions */ if (!have_switch) { - yn = region->fade_in_active(); + if (in) { + yn = region->fade_in_active(); + } else { + yn = region->fade_out_active(); + } have_switch = true; } XMLNode &before = region->get_state(); - region->set_fade_in_active (!yn); + if (in) { + region->set_fade_in_active (!yn); + } else { + region->set_fade_out_active (!yn); + } XMLNode &after = region->get_state(); session->add_command(new MementoCommand(*region.get(), &before, &after)); - in_command = true; } - if (in_command) { - commit_reversible_command (); - } + commit_reversible_command (); } void Editor::set_fade_in_shape (AudioRegion::FadeShape shape) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + begin_reversible_command (_("set fade in shape")); - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { + for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) { AudioRegionView* tmp = dynamic_cast (*x); if (!tmp) { @@ -4420,17 +4896,26 @@ Editor::set_fade_in_shape (AudioRegion::FadeShape shape) } commit_reversible_command (); + } void Editor::set_fade_out_shape (AudioRegion::FadeShape shape) { - begin_reversible_command (_("set fade out shape")); + RegionSelection rs; - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { - AudioRegionView* tmp = dynamic_cast (*x); + get_regions_for_action (rs); - if (!tmp) { + if (rs.empty()) { + return; + } + + begin_reversible_command (_("set fade out shape")); + + for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) { + AudioRegionView* tmp = dynamic_cast (*x); + + if (!tmp) { return; } @@ -4449,9 +4934,17 @@ Editor::set_fade_out_shape (AudioRegion::FadeShape shape) void Editor::set_fade_in_active (bool yn) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + begin_reversible_command (_("set fade in active")); - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { + for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) { AudioRegionView* tmp = dynamic_cast (*x); if (!tmp) { @@ -4468,14 +4961,24 @@ Editor::set_fade_in_active (bool yn) XMLNode &after = ar->get_state(); session->add_command(new MementoCommand(*ar, &before, &after)); } + + commit_reversible_command (); } void Editor::set_fade_out_active (bool yn) { + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + begin_reversible_command (_("set fade out active")); - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { + for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) { AudioRegionView* tmp = dynamic_cast (*x); if (!tmp) { @@ -4491,9 +4994,74 @@ Editor::set_fade_out_active (bool yn) XMLNode &after = ar->get_state(); session->add_command(new MementoCommand(*ar, &before, &after)); } + + commit_reversible_command (); +} + +void +Editor::toggle_selected_region_fades (int dir) +{ + RegionSelection rs; + RegionSelection::iterator i; + boost::shared_ptr ar; + bool yn; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + + for (i = rs.begin(); i != rs.end(); ++i) { + if ((ar = boost::dynamic_pointer_cast((*i)->region())) != 0) { + if (dir == -1) { + yn = ar->fade_out_active (); + } else { + yn = ar->fade_in_active (); + } + break; + } + } + + if (i == rs.end()) { + return; + } + + /* XXX should this undo-able? */ + + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + if ((ar = boost::dynamic_pointer_cast((*i)->region())) == 0) { + continue; + } + if (dir == 1 || dir == 0) { + ar->set_fade_in_active (!yn); + } + + if (dir == -1 || dir == 0) { + ar->set_fade_out_active (!yn); + } + } } +/** Update region fade visibility after its configuration has been changed */ +void +Editor::update_region_fade_visibility () +{ + bool _fade_visibility = Config->get_show_region_fades (); + + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + AudioTimeAxisView* v = dynamic_cast(*i); + if (v) { + if (_fade_visibility) { + v->audio_view()->show_all_fades (); + } else { + v->audio_view()->hide_all_fades (); + } + } + } +} + /** Update crossfade visibility after its configuration has been changed */ void Editor::update_xfade_visibility () @@ -4563,20 +5131,17 @@ Editor::set_playhead_cursor () void Editor::split () { - ensure_entered_region_selected (); + RegionSelection rs; - nframes64_t where = get_preferred_edit_position(); + get_regions_for_action (rs); - if (!selection->regions.empty()) { - - split_regions_at (where, selection->regions); + nframes64_t where = get_preferred_edit_position(); - } else { - - RegionSelection rs; - rs = get_regions_at (where, selection->tracks); - split_regions_at (where, rs); + if (rs.empty()) { + return; } + + split_regions_at (where, rs); } void @@ -4597,34 +5162,6 @@ Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_ar } } -void -Editor::ensure_entered_region_selected (bool op_really_wants_one_region_if_none_are_selected) -{ - if (entered_regionview && mouse_mode == MouseObject) { - - /* heuristic: - - - if there is no existing selection, don't change it. the operation will thus apply to "all" - - - if there is an existing selection, but the entered regionview isn't in it, add it. this - avoids key-mouse ops on unselected regions from interfering with an existing selection, - but also means that the operation will apply to the pointed-at region. - */ - - if (!selection->regions.empty()) { - if (find (selection->regions.begin(), selection->regions.end(), entered_regionview) != selection->regions.end()) { - selection->add (entered_regionview); - } - } else { - /* there is no selection, but this operation requires/prefers selected objects */ - - if (op_really_wants_one_region_if_none_are_selected) { - selection->set (entered_regionview, false); - } - } - } -} - void Editor::trim_region_front () { @@ -4641,7 +5178,9 @@ void Editor::trim_region (bool front) { nframes64_t where = get_preferred_edit_position(); - RegionSelection& rs = get_regions_for_action (); + RegionSelection rs; + + get_regions_for_action (rs); if (rs.empty()) { return; @@ -4662,6 +5201,7 @@ Editor::trim_region (bool front) session->add_command(new MementoCommand(*pl.get(), &before, &after)); } } + commit_reversible_command (); } @@ -4725,8 +5265,8 @@ Editor::set_loop_from_selection (bool play) return; } - nframes_t start = selection->time[clicked_selection].start; - nframes_t end = selection->time[clicked_selection].end; + nframes64_t start = selection->time[clicked_selection].start; + nframes64_t end = selection->time[clicked_selection].end; set_loop_range (start, end, _("set loop range from selection")); @@ -4764,14 +5304,15 @@ Editor::set_loop_from_region (bool play) nframes64_t start = max_frames; nframes64_t end = 0; - ensure_entered_region_selected (true); + RegionSelection rs; + + get_regions_for_action (rs); - if (selection->regions.empty()) { - info << _("cannot set loop: no region selected") << endmsg; + if (rs.empty()) { return; } - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { if ((*i)->region()->position() < start) { start = (*i)->region()->position(); } @@ -4795,8 +5336,8 @@ Editor::set_punch_from_selection () return; } - nframes_t start = selection->time[clicked_selection].start; - nframes_t end = selection->time[clicked_selection].end; + nframes64_t start = selection->time[clicked_selection].start; + nframes64_t end = selection->time[clicked_selection].end; set_punch_range (start, end, _("set punch range from selection")); } @@ -4818,15 +5359,829 @@ Editor::set_punch_from_edit_range () set_punch_range (start, end, _("set punch range from edit range")); } +void +Editor::set_punch_from_region () +{ + nframes64_t start = max_frames; + nframes64_t end = 0; + + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) { + if ((*i)->region()->position() < start) { + start = (*i)->region()->position(); + } + if ((*i)->region()->last_frame() + 1 > end) { + end = (*i)->region()->last_frame() + 1; + } + } + + set_punch_range (start, end, _("set punch range from region")); +} + void Editor::pitch_shift_regions () { - ensure_entered_region_selected (true); + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + + pitch_shift (rs, 1.2); +} + +void +Editor::use_region_as_bar () +{ + if (!session) { + return; + } + + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + + RegionView* rv = rs.front(); + + define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1); +} + +void +Editor::use_range_as_bar () +{ + nframes64_t start, end; + if (get_edit_op_range (start, end)) { + define_one_bar (start, end); + } +} + +void +Editor::define_one_bar (nframes64_t start, nframes64_t end) +{ + nframes64_t length = end - start; + + const Meter& m (session->tempo_map().meter_at (start)); + + /* length = 1 bar */ + + /* now we want frames per beat. + we have frames per bar, and beats per bar, so ... + */ + + double frames_per_beat = length / m.beats_per_bar(); - if (selection->regions.empty()) { + /* beats per minute = */ + + double beats_per_minute = (session->frame_rate() * 60.0) / frames_per_beat; + + /* now decide whether to: + + (a) set global tempo + (b) add a new tempo marker + + */ + + const TempoSection& t (session->tempo_map().tempo_section_at (start)); + + bool do_global = false; + + if ((session->tempo_map().n_tempos() == 1) && (session->tempo_map().n_meters() == 1)) { + + /* only 1 tempo & 1 meter: ask if the user wants to set the tempo + at the start, or create a new marker + */ + + vector options; + options.push_back (_("Cancel")); + options.push_back (_("Add new marker")); + options.push_back (_("Set global tempo")); + Choice c (_("Do you want to set the global tempo or add new tempo marker?"), + options); + c.set_default_response (2); + + switch (c.run()) { + case 0: + return; + + case 2: + do_global = true; + break; + + default: + do_global = false; + } + + } else { + + /* more than 1 tempo and/or meter section already, go ahead do the "usual": + if the marker is at the region starter, change it, otherwise add + a new tempo marker + */ + } + + begin_reversible_command (_("set tempo from region")); + XMLNode& before (session->tempo_map().get_state()); + + if (do_global) { + session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type()); + } else if (t.frame() == start) { + session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type()); + } else { + session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start); + } + + XMLNode& after (session->tempo_map().get_state()); + + session->add_command (new MementoCommand(session->tempo_map(), &before, &after)); + commit_reversible_command (); +} + +void +Editor::split_region_at_transients () +{ + AnalysisFeatureList positions; + + if (!session) { return; } - pitch_shift (selection->regions, 1.2); + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + + session->begin_reversible_command (_("split regions")); + + for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) { + + RegionSelection::iterator tmp; + + tmp = i; + ++tmp; + + boost::shared_ptr ar = boost::dynamic_pointer_cast ((*i)->region()); + + if (ar && (ar->get_transients (positions) == 0)) { + split_region_at_points ((*i)->region(), positions, true); + positions.clear (); + } + + i = tmp; + } + + session->commit_reversible_command (); + } + +void +Editor::split_region_at_points (boost::shared_ptr r, AnalysisFeatureList& positions, bool can_ferret) +{ + bool use_rhythmic_rodent = false; + + boost::shared_ptr pl = r->playlist(); + + if (!pl) { + return; + } + + if (positions.empty()) { + return; + } + + + if (positions.size() > 20) { + Glib::ustring msgstr = string_compose (_("You are about to split\n%1\ninto %2 pieces.\nThis could take a long time."), r->name(), positions.size() + 1); + MessageDialog msg (msgstr, + false, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_OK_CANCEL); + + if (can_ferret) { + msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY); + msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis")); + } else { + msg.set_secondary_text (_("Press OK to continue with this split operation")); + } + + msg.set_title (_("Excessive split?")); + msg.present (); + + int response = msg.run(); + msg.hide (); + switch (response) { + case RESPONSE_OK: + break; + case RESPONSE_APPLY: + use_rhythmic_rodent = true; + break; + default: + return; + } + } + + if (use_rhythmic_rodent) { + show_rhythm_ferret (); + return; + } + + AnalysisFeatureList::const_iterator x; + + nframes64_t pos = r->position(); + + XMLNode& before (pl->get_state()); + x = positions.begin(); + + while (x != positions.end()) { + if ((*x) > pos) { + break; + } + ++x; + } + + if (x == positions.end()) { + return; + } + + pl->freeze (); + pl->remove_region (r); + + while (x != positions.end()) { + + /* file start = original start + how far we from the initial position ? + */ + + nframes64_t file_start = r->start() + (pos - r->position()); + + /* length = next position - current position + */ + + nframes64_t len = (*x) - pos; + + /* XXX we do we really want to allow even single-sample regions? + shouldn't we have some kind of lower limit on region size? + */ + + if (len <= 0) { + break; + } + + string new_name; + + if (session->region_name (new_name, r->name())) { + break; + } + + /* do NOT announce new regions 1 by one, just wait till they are all done */ + + boost::shared_ptr nr = RegionFactory::create (r->sources(), file_start, len, new_name, 0, Region::DefaultFlags, false); + pl->add_region (nr, pos); + + pos += len; + ++x; + + if (*x > r->last_frame()) { + + /* add final fragment */ + + file_start = r->start() + (pos - r->position()); + len = r->last_frame() - pos; + + nr = RegionFactory::create (r->sources(), file_start, len, new_name, 0, Region::DefaultFlags); + pl->add_region (nr, pos); + + break; + } + } + + pl->thaw (); + + XMLNode& after (pl->get_state()); + + session->add_command (new MementoCommand(*pl, &before, &after)); +} + +void +Editor::tab_to_transient (bool forward) +{ + AnalysisFeatureList positions; + + if (!session) { + return; + } + + nframes64_t pos = session->audible_frame (); + + if (!selection->tracks.empty()) { + + for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) { + + RouteTimeAxisView* rtv = dynamic_cast (*t); + + if (rtv) { + boost::shared_ptr ds = rtv->get_diskstream(); + if (ds) { + boost::shared_ptr pl = rtv->get_diskstream()->playlist (); + if (pl) { + nframes64_t result = pl->find_next_transient (pos, forward ? 1 : -1); + + if (result >= 0) { + positions.push_back (result); + } + } + } + } + } + + } else { + + RegionSelection rs; + + get_regions_for_action (rs); + + if (rs.empty()) { + return; + } + + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) { + (*r)->region()->get_transients (positions); + } + } + + TransientDetector::cleanup_transients (positions, session->frame_rate(), 3.0); + + if (forward) { + AnalysisFeatureList::iterator x; + + for (x = positions.begin(); x != positions.end(); ++x) { + if ((*x) > pos) { + break; + } + } + + if (x != positions.end ()) { + session->request_locate (*x); + } + + } else { + AnalysisFeatureList::reverse_iterator x; + + for (x = positions.rbegin(); x != positions.rend(); ++x) { + if ((*x) < pos) { + break; + } + } + + if (x != positions.rend ()) { + session->request_locate (*x); + } + } +} +void +Editor::playhead_forward_to_grid () +{ + if (!session) return; + nframes64_t pos = playhead_cursor->current_frame; + if (pos < max_frames - 1) { + pos += 2; + snap_to_internal (pos, 1, false); + session->request_locate (pos); + } +} + + +void +Editor::playhead_backward_to_grid () +{ + if (!session) return; + nframes64_t pos = playhead_cursor->current_frame; + if (pos > 2) { + pos -= 2; + snap_to_internal (pos, -1, false); + session->request_locate (pos); + } +} + +void +Editor::set_track_height (uint32_t h) +{ + TrackSelection& ts (selection->tracks); + + if (ts.empty()) { + return; + } + + for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) { + (*x)->set_height (h); + } +} + +void +Editor::set_track_height_largest () +{ + set_track_height (TimeAxisView::hLargest); +} +void +Editor::set_track_height_large () +{ + set_track_height (TimeAxisView::hLarge); +} +void +Editor::set_track_height_larger () +{ + set_track_height (TimeAxisView::hLarger); +} +void +Editor::set_track_height_normal () +{ + set_track_height (TimeAxisView::hNormal); +} +void +Editor::set_track_height_smaller () +{ + set_track_height (TimeAxisView::hSmaller); +} +void +Editor::set_track_height_small () +{ + set_track_height (TimeAxisView::hSmall); +} + +void +Editor::toggle_tracks_active () +{ + TrackSelection& ts (selection->tracks); + bool first = true; + bool target = false; + + if (ts.empty()) { + return; + } + + for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) { + RouteTimeAxisView* rtv = dynamic_cast(*x); + + if (rtv) { + if (first) { + target = !rtv->_route->active(); + first = false; + } + rtv->_route->set_active (target); + } + } +} + +void +Editor::remove_tracks () +{ + TrackSelection& ts (selection->tracks); + + if (ts.empty()) { + return; + } + + vector choices; + string prompt; + int ntracks = 0; + int nbusses = 0; + const char* trackstr; + const char* busstr; + vector > routes; + + for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) { + RouteTimeAxisView* rtv = dynamic_cast (*x); + if (rtv) { + if (rtv->is_track()) { + ntracks++; + } else { + nbusses++; + } + } + routes.push_back (rtv->_route); + } + + if (ntracks + nbusses == 0) { + return; + } + + if (ntracks > 1) { + trackstr = _("tracks"); + } else { + trackstr = _("track"); + } + + if (nbusses > 1) { + busstr = _("busses"); + } else { + busstr = _("bus"); + } + + if (ntracks) { + if (nbusses) { + prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n" + "(You may also lose the playlists associated with the %2)\n\n" + "This action cannot be undone!"), + ntracks, trackstr, nbusses, busstr); + } else { + prompt = string_compose (_("Do you really want to remove %1 %2?\n" + "(You may also lose the playlists associated with the %2)\n\n" + "This action cannot be undone!"), + ntracks, trackstr); + } + } else if (nbusses) { + prompt = string_compose (_("Do you really want to remove %1 %2?"), + nbusses, busstr); + } + + choices.push_back (_("No, do nothing.")); + if (ntracks + nbusses > 1) { + choices.push_back (_("Yes, remove them.")); + } else { + choices.push_back (_("Yes, remove it.")); + } + + Choice prompter (prompt, choices); + + if (prompter.run () != 1) { + return; + } + + for (vector >::iterator x = routes.begin(); x != routes.end(); ++x) { + session->remove_route (*x); + } +} + +void +Editor::set_waveform_scale (WaveformScale ws) +{ + TrackSelection& ts (selection->tracks); + + if (ts.empty()) { + return; + } + + for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) { + AudioTimeAxisView* atv = dynamic_cast (*x); + if (atv) { + atv->set_waveform_scale (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:")); + CheckButton glue_button (_("Move Glued Regions")); + CheckButton marker_button (_("Move Markers")); + 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); + option_box.pack_start (marker_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 (); + marker_button.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; + } + + nframes64_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(), marker_button.get_active()); +} + +void +Editor::insert_time (nframes64_t pos, nframes64_t frames, InsertTimeOption opt, + bool ignore_music_glue, bool markers_too) +{ + 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 (markers_too) { + bool moved = false; + XMLNode& before (session->locations()->get_state()); + Locations::LocationList copy (session->locations()->list()); + + for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) { + + Locations::LocationList::const_iterator tmp; + + if ((*i)->start() >= pos) { + (*i)->set_start ((*i)->start() + frames); + if (!(*i)->is_mark()) { + (*i)->set_end ((*i)->end() + frames); + } + moved = true; + } + } + + if (moved) { + XMLNode& after (session->locations()->get_state()); + session->add_command (new MementoCommand(*session->locations(), &before, &after)); + } + } + + if (commit) { + commit_reversible_command (); + } +} + +void +Editor::fit_tracks () +{ + if (selection->tracks.empty()) { + return; + } + + uint32_t child_heights = 0; + + for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) { + + if (!(*t)->marked_for_display()) { + continue; + } + + child_heights += ((*t)->effective_height - (*t)->current_height()); + } + + uint32_t h = (uint32_t) floor ((canvas_height - child_heights)/selection->tracks.size()); + double first_y_pos = DBL_MAX; + + undo_visual_stack.push_back (current_visual_state()); + + for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) { + (*t)->set_height (h); + first_y_pos = std::min ((*t)->y_position, first_y_pos); + } + + vertical_adjustment.set_value (first_y_pos); + + redo_visual_stack.push_back (current_visual_state()); +} + +void +Editor::save_visual_state (uint32_t n) +{ + while (visual_states.size() <= n) { + visual_states.push_back (0); + } + + if (visual_states[n] != 0) { + delete visual_states[n]; + } + + visual_states[n] = current_visual_state (true); + gdk_beep (); +} + +void +Editor::goto_visual_state (uint32_t n) +{ + if (visual_states.size() <= n) { + return; + } + + if (visual_states[n] == 0) { + return; + } + + use_visual_state (*visual_states[n]); +} + +void +Editor::start_visual_state_op (uint32_t n) +{ + cerr << "Start\n"; + if (visual_state_op_connection.empty()) { + cerr << "\tqueue\n"; + visual_state_op_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::end_visual_state_op), n), 1000); + } +} + +void +Editor::cancel_visual_state_op (uint32_t n) +{ + cerr << "Cancel\n"; + if (!visual_state_op_connection.empty()) { + cerr << "\tgoto\n"; + visual_state_op_connection.disconnect(); + goto_visual_state (n); + } +} + +bool +Editor::end_visual_state_op (uint32_t n) +{ + visual_state_op_connection.disconnect(); + save_visual_state (n); + + PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true); + char buf[32]; + snprintf (buf, sizeof (buf), _("Saved view %u"), n+1); + pup->set_text (buf); + pup->touch(); + + return false; // do not call again +} +