2 Copyright (C) 2000 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.
26 #include "ardour_ui.h"
27 #include "audio_time_axis.h"
28 #include "mixer_strip.h"
29 #include "gui_thread.h"
32 #include <ardour/route.h>
33 #include <ardour/audio_track.h>
34 #include <ardour/route_group.h>
39 using namespace ARDOUR
;
44 const char* _order_key
= N_("editor");
47 Editor::handle_new_route (Session::RouteList
& routes
)
49 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_new_route
), routes
));
52 AudioTimeAxisView
*atv
;
53 TreeModel::Row parent
;
56 route_redisplay_does_not_sync_order_keys
= true;
57 no_route_list_redisplay
= true;
59 for (Session::RouteList::iterator x
= routes
.begin(); x
!= routes
.end(); ++x
) {
60 boost::shared_ptr
<Route
> route
= (*x
);
62 if (route
->hidden()) {
66 tv
= new AudioTimeAxisView (*this, *session
, route
, *track_canvas
);
67 //cerr << "Editor::handle_new_route() called on " << route->name() << endl;//DEBUG
68 row
= *(route_display_model
->append ());
70 row
[route_display_columns
.route
] = route
;
71 row
[route_display_columns
.text
] = route
->name();
72 row
[route_display_columns
.visible
] = tv
->marked_for_display();
73 row
[route_display_columns
.tv
] = tv
;
75 RouteGroup
*group
= route
->edit_group();
77 if (tv
->marked_for_display()) {
78 group
->set_hidden(false, this);
79 group_flags_changed(this, group
);
83 track_views
.push_back (tv
);
85 if ((atv
= dynamic_cast<AudioTimeAxisView
*> (tv
)) != 0) {
86 /* added a new fresh one at the end */
87 if (atv
->route()->order_key(_order_key
) == -1) {
88 atv
->route()->set_order_key (_order_key
, route_display_model
->children().size()-1);
90 atv
->effective_gain_display ();
93 route
->gui_changed
.connect (mem_fun(*this, &Editor::handle_gui_changes
));
94 tv
->GoingAway
.connect (bind (mem_fun(*this, &Editor::remove_route
), tv
));
97 no_route_list_redisplay
= false;
99 redisplay_route_list ();
101 if (show_editor_mixer_when_tracks_arrive
) {
102 show_editor_mixer (true);
105 route_redisplay_does_not_sync_order_keys
= false;
109 Editor::handle_gui_changes (const string
& what
, void *src
)
111 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_gui_changes
), what
, src
));
113 if (what
== "track_height") {
114 /* Optional :make tracks change height while it happens, instead
117 //track_canvas->update_now ();
118 redisplay_route_list ();
121 if (what
== "visible_tracks") {
122 redisplay_route_list ();
127 Editor::remove_route (TimeAxisView
*tv
)
129 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::remove_route
), tv
));
131 TrackViewList::iterator i
;
132 TreeModel::Children rows
= route_display_model
->children();
133 TreeModel::Children::iterator ri
;
134 boost::shared_ptr
<Route
> route
;
135 TimeAxisView
* next_tv
= 0;
137 if (tv
== entered_track
) {
141 /* the core model has changed, there is no need to sync
145 route_redisplay_does_not_sync_order_keys
= true;
147 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
148 if ((*ri
)[route_display_columns
.tv
] == tv
) {
149 route
= (*ri
)[route_display_columns
.route
];
150 route_display_model
->erase (ri
);
155 route_redisplay_does_not_sync_order_keys
= false;
157 if ((i
= find (track_views
.begin(), track_views
.end(), tv
)) != track_views
.end()) {
158 i
= track_views
.erase (i
);
160 if (track_views
.empty()) {
162 } else if (i
== track_views
.end()) {
163 next_tv
= track_views
.front();
169 if (current_mixer_strip
&& (current_mixer_strip
->route() == route
)) {
172 set_selected_mixer_strip (*next_tv
);
174 /* make the editor mixer strip go away by setting the
175 * button to inactive (which also unticks the menu option)
178 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
184 Editor::route_name_changed (TimeAxisView
*tv
)
186 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::route_name_changed
), tv
));
188 TreeModel::Children rows
= route_display_model
->children();
189 TreeModel::Children::iterator i
;
191 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
192 if ((*i
)[route_display_columns
.tv
] == tv
) {
193 (*i
)[route_display_columns
.text
] = tv
->name();
200 Editor::update_route_visibility ()
202 TreeModel::Children rows
= route_display_model
->children();
203 TreeModel::Children::iterator i
;
205 no_route_list_redisplay
= true;
207 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
208 TimeAxisView
*tv
= (*i
)[route_display_columns
.tv
];
209 (*i
)[route_display_columns
.visible
] = tv
->marked_for_display ();
212 no_route_list_redisplay
= false;
213 redisplay_route_list ();
217 Editor::hide_track_in_display (TimeAxisView
& tv
, bool temponly
)
219 TreeModel::Children rows
= route_display_model
->children();
220 TreeModel::Children::iterator i
;
222 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
223 if ((*i
)[route_display_columns
.tv
] == &tv
) {
224 (*i
)[route_display_columns
.visible
] = false;
229 AudioTimeAxisView
* atv
= dynamic_cast<AudioTimeAxisView
*> (&tv
);
231 if (atv
&& current_mixer_strip
&& (atv
->route() == current_mixer_strip
->route())) {
232 // this will hide the mixer strip
233 set_selected_mixer_strip (tv
);
238 Editor::show_track_in_display (TimeAxisView
& tv
)
240 TreeModel::Children rows
= route_display_model
->children();
241 TreeModel::Children::iterator i
;
243 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
244 if ((*i
)[route_display_columns
.tv
] == &tv
) {
245 (*i
)[route_display_columns
.visible
] = true;
252 Editor::sync_order_keys (const char *src
)
254 vector
<int> neworder
;
255 TreeModel::Children rows
= route_display_model
->children();
256 TreeModel::Children::iterator ri
;
258 if ((strcmp (src
, _order_key
) == 0) || !session
|| (session
->state_of_the_state() & Session::Loading
) || rows
.empty()) {
262 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
263 neworder
.push_back (0);
266 bool changed
= false;
269 for (order
= 0, ri
= rows
.begin(); ri
!= rows
.end(); ++ri
, ++order
) {
270 boost::shared_ptr
<Route
> route
= (*ri
)[route_display_columns
.route
];
273 int new_key
= route
->order_key (_order_key
);
275 neworder
[new_key
] = old_key
;
277 if (new_key
!= old_key
) {
283 route_redisplay_does_not_reset_order_keys
= true;
284 route_display_model
->reorder (neworder
);
285 route_redisplay_does_not_reset_order_keys
= false;
290 Editor::redisplay_route_list ()
292 TreeModel::Children rows
= route_display_model
->children();
293 TreeModel::Children::iterator i
;
298 if (no_route_list_redisplay
) {
302 if (session
&& (rows
.size() > session
->nroutes())) {
303 /* temporary condition during a drag-n-drop */
307 for (n
= 0, order
= 0, position
= 0, i
= rows
.begin(); i
!= rows
.end(); ++i
) {
308 TimeAxisView
*tv
= (*i
)[route_display_columns
.tv
];
309 boost::shared_ptr
<Route
> route
= (*i
)[route_display_columns
.route
];
312 // just a "title" row
316 if (!route_redisplay_does_not_reset_order_keys
) {
318 /* this reorder is caused by user action, so reassign sort order keys
322 route
->set_order_key (_order_key
, order
);
325 bool visible
= (*i
)[route_display_columns
.visible
];
328 tv
->set_marked_for_display (true);
329 position
+= tv
->show_at (position
, n
, &edit_controls_vbox
);
330 tv
->clip_to_viewport ();
332 tv
->set_marked_for_display (false);
340 /* whenever we go idle, update the track view list to reflect the new order.
341 we can't do this here, because we could mess up something that is traversing
342 the track order and has caused a redisplay of the list.
345 Glib::signal_idle().connect (mem_fun (*this, &Editor::sync_track_view_list_and_route_list
));
347 full_canvas_height
= position
+ canvas_timebars_vsize
;
348 vertical_adjustment
.set_upper (full_canvas_height
);
349 if ((vertical_adjustment
.get_value() + canvas_height
) > vertical_adjustment
.get_upper()) {
351 We're increasing the size of the canvas while the bottom is visible.
352 We scroll down to keep in step with the controls layout.
354 vertical_adjustment
.set_value (full_canvas_height
- canvas_height
);
357 if (!route_redisplay_does_not_reset_order_keys
&& !route_redisplay_does_not_sync_order_keys
) {
358 session
->sync_order_keys (_order_key
);
363 Editor::sync_track_view_list_and_route_list ()
365 TreeModel::Children rows
= route_display_model
->children();
366 TreeModel::Children::iterator i
;
368 track_views
.clear ();
370 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
371 TimeAxisView
*tv
= (*i
)[route_display_columns
.tv
];
372 track_views
.push_back (tv
);
375 return false; // do not call again (until needed)
379 Editor::hide_all_tracks (bool with_select
)
381 TreeModel::Children rows
= route_display_model
->children();
382 TreeModel::Children::iterator i
;
384 no_route_list_redisplay
= true;
386 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
388 TreeModel::Row row
= (*i
);
389 TimeAxisView
*tv
= row
[route_display_columns
.tv
];
395 row
[route_display_columns
.visible
] = false;
398 no_route_list_redisplay
= false;
399 redisplay_route_list ();
401 /* XXX this seems like a hack and half, but its not clear where to put this
405 //reset_scrolling_region ();
409 Editor::build_route_list_menu ()
411 using namespace Menu_Helpers
;
414 route_list_menu
= new Menu
;
416 MenuList
& items
= route_list_menu
->items();
417 route_list_menu
->set_name ("ArdourContextMenu");
419 items
.push_back (MenuElem (_("Show All"), mem_fun(*this, &Editor::show_all_routes
)));
420 items
.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Editor::hide_all_routes
)));
421 items
.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Editor::show_all_audiotracks
)));
422 items
.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Editor::hide_all_audiotracks
)));
423 items
.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Editor::show_all_audiobus
)));
424 items
.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Editor::hide_all_audiobus
)));
429 Editor::set_all_tracks_visibility (bool yn
)
431 TreeModel::Children rows
= route_display_model
->children();
432 TreeModel::Children::iterator i
;
434 no_route_list_redisplay
= true;
436 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
438 TreeModel::Row row
= (*i
);
439 TimeAxisView
* tv
= row
[route_display_columns
.tv
];
445 (*i
)[route_display_columns
.visible
] = yn
;
448 no_route_list_redisplay
= false;
449 redisplay_route_list ();
453 Editor::set_all_audio_visibility (int tracks
, bool yn
)
455 TreeModel::Children rows
= route_display_model
->children();
456 TreeModel::Children::iterator i
;
458 no_route_list_redisplay
= true;
460 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
461 TreeModel::Row row
= (*i
);
462 TimeAxisView
* tv
= row
[route_display_columns
.tv
];
463 AudioTimeAxisView
* atv
;
469 if ((atv
= dynamic_cast<AudioTimeAxisView
*>(tv
)) != 0) {
472 (*i
)[route_display_columns
.visible
] = yn
;
476 if (atv
->is_audio_track()) {
477 (*i
)[route_display_columns
.visible
] = yn
;
482 if (!atv
->is_audio_track()) {
483 (*i
)[route_display_columns
.visible
] = yn
;
490 no_route_list_redisplay
= false;
491 redisplay_route_list ();
495 Editor::hide_all_routes ()
497 set_all_tracks_visibility (false);
501 Editor::show_all_routes ()
503 set_all_tracks_visibility (true);
507 Editor::show_all_audiobus ()
509 set_all_audio_visibility (2, true);
512 Editor::hide_all_audiobus ()
514 set_all_audio_visibility (2, false);
518 Editor::show_all_audiotracks()
520 set_all_audio_visibility (1, true);
523 Editor::hide_all_audiotracks ()
525 set_all_audio_visibility (1, false);
529 Editor::route_list_display_button_press (GdkEventButton
* ev
)
531 if (Keyboard::is_context_menu_event (ev
)) {
532 show_route_list_menu ();
537 TreeModel::Path path
;
538 TreeViewColumn
* column
;
542 if (!route_list_display
.get_path_at_pos ((int)ev
->x
, (int)ev
->y
, path
, column
, cellx
, celly
)) {
546 switch (GPOINTER_TO_UINT (column
->get_data (X_("colnum")))) {
548 if ((iter
= route_display_model
->get_iter (path
))) {
549 TimeAxisView
* tv
= (*iter
)[route_display_columns
.tv
];
551 bool visible
= (*iter
)[route_display_columns
.visible
];
552 (*iter
)[route_display_columns
.visible
] = !visible
;
558 /* allow normal processing to occur */
569 Editor::show_route_list_menu()
571 if (route_list_menu
== 0) {
572 build_route_list_menu ();
575 route_list_menu
->popup (1, gtk_get_current_event_time());
579 Editor::route_list_selection_filter (const Glib::RefPtr
<TreeModel
>& model
, const TreeModel::Path
& path
, bool yn
)
584 struct EditorOrderRouteSorter
{
585 bool operator() (boost::shared_ptr
<Route
> a
, boost::shared_ptr
<Route
> b
) {
586 /* use of ">" forces the correct sort order */
587 return a
->order_key (_order_key
) < b
->order_key (_order_key
);
592 Editor::initial_route_list_display ()
594 boost::shared_ptr
<Session::RouteList
> routes
= session
->get_routes();
595 Session::RouteList
r (*routes
);
596 EditorOrderRouteSorter sorter
;
600 no_route_list_redisplay
= true;
602 route_display_model
->clear ();
604 handle_new_route (r
);
606 no_route_list_redisplay
= false;
608 redisplay_route_list ();
612 Editor::track_list_reorder (const Gtk::TreeModel::Path
& path
,const Gtk::TreeModel::iterator
& iter
, int* new_order
)
614 route_redisplay_does_not_sync_order_keys
= true;
615 session
->set_remote_control_ids();
616 redisplay_route_list ();
617 route_redisplay_does_not_sync_order_keys
= false;
621 Editor::route_list_change (const Gtk::TreeModel::Path
& path
,const Gtk::TreeModel::iterator
& iter
)
623 /* never reset order keys because of a property change */
624 route_redisplay_does_not_reset_order_keys
= true;
625 session
->set_remote_control_ids();
626 redisplay_route_list ();
627 route_redisplay_does_not_reset_order_keys
= false;
631 Editor::route_list_delete (const Gtk::TreeModel::Path
& path
)
633 /* this could require an order reset & sync */
634 session
->set_remote_control_ids();
635 redisplay_route_list ();
639 Editor::route_list_display_drag_data_received (const RefPtr
<Gdk::DragContext
>& context
,
641 const SelectionData
& data
,
642 guint info
, guint time
)
644 if (data
.get_target() == "GTK_TREE_MODEL_ROW") {
645 route_list_display
.on_drag_data_received (context
, x
, y
, data
, info
, time
);
648 context
->drag_finish (true, false, time
);
652 Editor::foreach_time_axis_view (sigc::slot
<void,TimeAxisView
&> theslot
)
654 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
660 Editor::move_selected_tracks (bool up
)
662 if (selection
->tracks
.empty()) {
666 typedef pair
<TimeAxisView
*,boost::shared_ptr
<Route
> > ViewRoute
;
667 list
<ViewRoute
> view_routes
;
668 vector
<int> neworder
;
669 TreeModel::Children rows
= route_display_model
->children();
670 TreeModel::Children::iterator ri
;
672 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
673 TimeAxisView
* tv
= (*ri
)[route_display_columns
.tv
];
674 boost::shared_ptr
<Route
> route
= (*ri
)[route_display_columns
.route
];
676 view_routes
.push_back (ViewRoute (tv
, route
));
679 list
<ViewRoute
>::iterator trailing
;
680 list
<ViewRoute
>::iterator leading
;
684 trailing
= view_routes
.begin();
685 leading
= view_routes
.begin();
689 while (leading
!= view_routes
.end()) {
690 if (selection
->selected (leading
->first
)) {
691 view_routes
.insert (trailing
, ViewRoute (leading
->first
, leading
->second
));
692 leading
= view_routes
.erase (leading
);
701 /* if we could use reverse_iterator in list::insert, this code
702 would be a beautiful reflection of the code above. but we can't
703 and so it looks like a bit of a mess.
706 trailing
= view_routes
.end();
707 leading
= view_routes
.end();
709 --leading
; if (leading
== view_routes
.begin()) { return; }
715 if (selection
->selected (leading
->first
)) {
716 list
<ViewRoute
>::iterator tmp
;
718 /* need to insert *after* trailing, not *before* it,
719 which is what insert (iter, val) normally does.
725 view_routes
.insert (tmp
, ViewRoute (leading
->first
, leading
->second
));
727 /* can't use iter = cont.erase (iter); form here, because
728 we need iter to move backwards.
736 if (leading
== view_routes
.begin()) {
737 /* the one we've just inserted somewhere else
738 was the first in the list. erase this copy,
739 and then break, because we're done.
744 view_routes
.erase (leading
);
753 if (leading
== view_routes
.begin()) {
762 for (leading
= view_routes
.begin(); leading
!= view_routes
.end(); ++leading
) {
763 neworder
.push_back (leading
->second
->order_key (_order_key
));
766 route_display_model
->reorder (neworder
);
768 session
->sync_order_keys (_order_key
);