+ 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<Region> 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<Playlist>(*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<RouteTimeAxisView*> (*t);
+
+ if (rtv) {
+ boost::shared_ptr<Diskstream> ds = rtv->get_diskstream();
+ if (ds) {
+ boost::shared_ptr<Playlist> 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<RouteTimeAxisView*>(*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<string> choices;
+ string prompt;
+ int ntracks = 0;
+ int nbusses = 0;
+ const char* trackstr;
+ const char* busstr;
+ vector<boost::shared_ptr<Route> > routes;
+
+ for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*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<boost::shared_ptr<Route> >::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<AudioTimeAxisView*> (*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<Playlist> 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<Playlist> (*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<Locations>(*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
+}
+