2 Copyright (C) 2000-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 $Id: editor.cc 1353 2007-01-18 03:06:15Z paul $
21 #include <ardour/diskstream.h>
22 #include <ardour/playlist.h>
23 #include <ardour/route_group.h>
27 #include "audio_time_axis.h"
28 #include "audio_region_view.h"
29 #include "audio_streamview.h"
30 #include "automation_line.h"
36 using namespace ARDOUR;
40 using namespace Gtkmm2ext;
41 using namespace Editing;
43 struct TrackViewByPositionSorter
45 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
46 return a->y_position < b->y_position;
51 Editor::extend_selection_to_track (TimeAxisView& view)
53 if (selection->selected (&view)) {
54 /* already selected, do nothing */
58 if (selection->tracks.empty()) {
60 if (!selection->selected (&view)) {
61 selection->set (&view);
68 /* something is already selected, so figure out which range of things to add */
70 TrackViewList to_be_added;
71 TrackViewList sorted = track_views;
72 TrackViewByPositionSorter cmp;
73 bool passed_clicked = false;
78 if (!selection->selected (&view)) {
79 to_be_added.push_back (&view);
82 /* figure out if we should go forward or backwards */
84 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
87 passed_clicked = true;
90 if (selection->selected (*i)) {
100 passed_clicked = false;
104 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
107 passed_clicked = true;
111 if (passed_clicked) {
112 if ((*i)->hidden()) {
115 if (selection->selected (*i)) {
117 } else if (!(*i)->hidden()) {
118 to_be_added.push_back (*i);
125 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
128 passed_clicked = true;
132 if (passed_clicked) {
134 if ((*r)->hidden()) {
138 if (selection->selected (*r)) {
140 } else if (!(*r)->hidden()) {
141 to_be_added.push_back (*r);
147 if (!to_be_added.empty()) {
148 selection->add (to_be_added);
157 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
162 case Selection::Toggle:
163 if (selection->selected (&view)) {
165 selection->remove (&view);
169 selection->add (&view);
175 if (!selection->selected (&view)) {
176 selection->add (&view);
182 if (selection->selected (&view) && selection->tracks.size() == 1) {
183 /* no commit necessary */
186 /* reset track selection if there is only 1 other track
187 selected OR if no_remove is not set (its there to
188 prevent deselecting a multi-track selection
189 when clicking on an already selected track
193 if (selection->tracks.empty()) {
194 selection->set (&view);
196 } else if (selection->tracks.size() == 1 || !no_remove) {
197 selection->set (&view);
203 case Selection::Extend:
204 commit = extend_selection_to_track (view);
212 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
214 if (!clicked_trackview) {
222 return set_selected_track (*clicked_trackview, op, no_remove);
226 Editor::set_selected_control_point_from_click (Selection::Operation op, bool no_remove)
228 if (!clicked_control_point) {
232 /* select this point and any others that it represents */
237 x1 = pixel_to_frame (clicked_control_point->get_x() - 10);
238 x2 = pixel_to_frame (clicked_control_point->get_x() + 10);
239 y1 = clicked_control_point->get_x() - 10;
240 y2 = clicked_control_point->get_y() + 10;
242 return select_all_within (x1, x2, y1, y2, op);
246 Editor::get_relevant_audio_tracks (set<AudioTimeAxisView*>& relevant_tracks)
248 /* step one: get all selected tracks and all tracks in the relevant edit groups */
250 for (TrackSelection::iterator ti = selection->tracks.begin(); ti != selection->tracks.end(); ++ti) {
252 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*ti);
258 RouteGroup* group = atv->route()->edit_group();
260 if (group && group->is_active()) {
262 /* active group for this track, loop over all tracks and get every member of the group */
264 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
266 AudioTimeAxisView* tatv;
268 if ((tatv = dynamic_cast<AudioTimeAxisView*> (*i)) != 0) {
270 if (tatv->route()->edit_group() == group) {
271 relevant_tracks.insert (tatv);
276 relevant_tracks.insert (atv);
282 Editor::mapover_audio_tracks (slot<void,AudioTimeAxisView&,uint32_t> sl)
284 set<AudioTimeAxisView*> relevant_tracks;
286 get_relevant_audio_tracks (relevant_tracks);
288 uint32_t sz = relevant_tracks.size();
290 for (set<AudioTimeAxisView*>::iterator ati = relevant_tracks.begin(); ati != relevant_tracks.end(); ++ati) {
296 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t ignored, RegionView* basis, vector<RegionView*>* all_equivs)
298 boost::shared_ptr<Playlist> pl;
299 vector<boost::shared_ptr<Region> > results;
301 boost::shared_ptr<Diskstream> ds;
303 if ((ds = tv.get_diskstream()) == 0) {
308 if (&tv == &basis->get_time_axis_view()) {
309 /* looking in same track as the original */
313 if ((pl = ds->playlist()) != 0) {
314 pl->get_equivalent_regions (basis->region(), results);
317 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
318 if ((marv = tv.view()->find_view (*ir)) != 0) {
319 all_equivs->push_back (marv);
325 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions)
327 mapover_audio_tracks (bind (mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions));
329 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
331 equivalent_regions.push_back (basis);
335 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool no_track_remove)
337 vector<RegionView*> all_equivalent_regions;
340 if (!clicked_regionview || !clicked_audio_trackview) {
345 button_release_can_deselect = false;
348 if (op == Selection::Toggle || op == Selection::Set) {
350 get_equivalent_regions (clicked_regionview, all_equivalent_regions);
353 case Selection::Toggle:
355 if (clicked_regionview->get_selected()) {
358 /* whatever was clicked was selected already; do nothing here but allow
359 the button release to deselect it
362 button_release_can_deselect = true;
366 if (button_release_can_deselect) {
368 /* just remove this one region, but only on a permitted button release */
370 selection->remove (clicked_regionview);
373 /* no more deselect action on button release till a new press
374 finds an already selected object.
377 button_release_can_deselect = false;
384 /* add all the equivalent regions, but only on button press */
386 if (!all_equivalent_regions.empty()) {
390 selection->add (all_equivalent_regions);
396 if (!clicked_regionview->get_selected()) {
397 selection->set (all_equivalent_regions);
400 /* no commit necessary: clicked on an already selected region */
410 } else if (op == Selection::Extend) {
412 list<Selectable*> results;
413 nframes_t last_frame;
414 nframes_t first_frame;
416 /* 1. find the last selected regionview in the track that was clicked in */
419 first_frame = max_frames;
421 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
422 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
424 if ((*x)->region()->last_frame() > last_frame) {
425 last_frame = (*x)->region()->last_frame();
428 if ((*x)->region()->first_frame() < first_frame) {
429 first_frame = (*x)->region()->first_frame();
434 /* 2. figure out the boundaries for our search for new objects */
436 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
438 if (last_frame < clicked_regionview->region()->first_frame()) {
439 first_frame = last_frame;
440 last_frame = clicked_regionview->region()->last_frame();
442 last_frame = first_frame;
443 first_frame = clicked_regionview->region()->first_frame();
447 case OverlapExternal:
448 if (last_frame < clicked_regionview->region()->first_frame()) {
449 first_frame = last_frame;
450 last_frame = clicked_regionview->region()->last_frame();
452 last_frame = first_frame;
453 first_frame = clicked_regionview->region()->first_frame();
457 case OverlapInternal:
458 if (last_frame < clicked_regionview->region()->first_frame()) {
459 first_frame = last_frame;
460 last_frame = clicked_regionview->region()->last_frame();
462 last_frame = first_frame;
463 first_frame = clicked_regionview->region()->first_frame();
469 /* nothing to do except add clicked region to selection, since it
470 overlaps with the existing selection in this track.
475 /* 2. find all selectable objects (regionviews in this case) between that one and the end of the
476 one that was clicked.
479 set<AudioTimeAxisView*> relevant_tracks;
481 get_relevant_audio_tracks (relevant_tracks);
483 for (set<AudioTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
484 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
487 /* 3. convert to a vector of audio regions */
489 vector<RegionView*> regions;
491 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
494 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
495 regions.push_back (arv);
499 if (!regions.empty()) {
500 selection->add (regions);
510 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
512 vector<RegionView*> all_equivalent_regions;
514 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
516 RouteTimeAxisView* tatv;
518 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
520 boost::shared_ptr<Playlist> pl;
521 vector<boost::shared_ptr<Region> > results;
523 boost::shared_ptr<Diskstream> ds;
525 if ((ds = tatv->get_diskstream()) == 0) {
530 if ((pl = (ds->playlist())) != 0) {
531 pl->get_region_list_equivalent_regions (region, results);
534 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
535 if ((marv = tatv->view()->find_view (*ir)) != 0) {
536 all_equivalent_regions.push_back (marv);
543 begin_reversible_command (_("set selected regions"));
546 case Selection::Toggle:
547 /* XXX this is not correct */
548 selection->toggle (all_equivalent_regions);
551 selection->set (all_equivalent_regions);
553 case Selection::Extend:
554 selection->add (all_equivalent_regions);
557 selection->add (all_equivalent_regions);
561 commit_reversible_command () ;
565 Editor::set_selected_regionview_from_map_event (GdkEventAny* ev, StreamView* sv, boost::weak_ptr<Region> weak_r)
568 boost::shared_ptr<Region> r (weak_r.lock());
574 boost::shared_ptr<AudioRegion> ar;
576 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
580 if ((rv = sv->find_view (ar)) == 0) {
584 /* don't reset the selection if its something other than
585 a single other region.
588 if (selection->regions.size() > 1) {
592 begin_reversible_command (_("set selected regions"));
596 commit_reversible_command () ;
602 Editor::track_selection_changed ()
604 switch (selection->tracks.size()){
608 set_selected_mixer_strip (*(selection->tracks.front()));
612 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
613 (*i)->set_selected (false);
614 if (mouse_mode == MouseRange) {
615 (*i)->hide_selection ();
619 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
620 (*i)->set_selected (true);
621 if (mouse_mode == MouseRange) {
622 (*i)->show_selection (selection->time);
628 Editor::time_selection_changed ()
630 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
631 (*i)->hide_selection ();
634 if (selection->tracks.empty()) {
635 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
636 (*i)->show_selection (selection->time);
639 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
640 (*i)->show_selection (selection->time);
644 if (selection->time.empty()) {
645 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
647 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
652 Editor::region_selection_changed ()
654 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
655 (*i)->set_selected_regionviews (selection->regions);
660 Editor::point_selection_changed ()
662 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
663 (*i)->set_selected_points (selection->points);