X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_route_list.cc;h=00b4a3656d400327a2f6f1b03e8a8701bfa2f9f8;hb=73a6b8ef25bb90ad6c66b25b17da5962a38291d2;hp=99031f168ced2e37b4c4b3f7cf02f72b952704c8;hpb=355c078023fb10078e24ac171e99125c02e08fad;p=ardour.git diff --git a/gtk2_ardour/editor_route_list.cc b/gtk2_ardour/editor_route_list.cc index 99031f168c..00b4a3656d 100644 --- a/gtk2_ardour/editor_route_list.cc +++ b/gtk2_ardour/editor_route_list.cc @@ -17,46 +17,65 @@ */ +#include +#include #include #include #include +#include #include "editor.h" #include "keyboard.h" #include "ardour_ui.h" #include "audio_time_axis.h" +#include "midi_time_axis.h" #include "mixer_strip.h" #include "gui_thread.h" +#include "actions.h" -#include -#include +#include "pbd/unknown_type.h" + +#include "ardour/route.h" #include "i18n.h" +using namespace std; using namespace sigc; using namespace ARDOUR; using namespace PBD; using namespace Gtk; +using namespace Glib; + +const char* _order_key = N_("editor"); void -Editor::handle_new_route (Session::RouteList& routes) +Editor::handle_new_route (RouteList& routes) { ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_new_route), routes)); TimeAxisView *tv; - AudioTimeAxisView *atv; + RouteTimeAxisView *rtv; TreeModel::Row parent; TreeModel::Row row; - for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) { + route_redisplay_does_not_sync_order_keys = true; + no_route_list_redisplay = true; + + for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) { boost::shared_ptr route = (*x); - if (route->hidden()) { + if (route->is_hidden()) { continue; } - tv = new AudioTimeAxisView (*this, *session, route, track_canvas); - + if (route->default_type() == ARDOUR::DataType::AUDIO) + tv = new AudioTimeAxisView (*this, *session, route, *track_canvas); + else if (route->default_type() == ARDOUR::DataType::MIDI) + tv = new MidiTimeAxisView (*this, *session, route, *track_canvas); + else + throw unknown_type(); + + //cerr << "Editor::handle_new_route() called on " << route->name() << endl;//DEBUG #if 0 if (route_display_model->children().size() == 0) { @@ -86,77 +105,119 @@ Editor::handle_new_route (Session::RouteList& routes) #else row = *(route_display_model->append ()); #endif + + // cerr << route->name() << " marked for display ? " << tv->marked_for_display() << endl; row[route_display_columns.text] = route->name(); row[route_display_columns.visible] = tv->marked_for_display(); row[route_display_columns.tv] = tv; - + row[route_display_columns.route] = route; + track_views.push_back (tv); ignore_route_list_reorder = true; - if ((atv = dynamic_cast (tv)) != 0) { + if ((rtv = dynamic_cast (tv)) != 0) { /* added a new fresh one at the end */ - if (atv->route()->order_key(N_("editor")) == -1) { - atv->route()->set_order_key (N_("editor"), route_display_model->children().size()-1); + if (rtv->route()->order_key(_order_key) == -1) { + rtv->route()->set_order_key (_order_key, route_display_model->children().size()-1); } + rtv->effective_gain_display (); } ignore_route_list_reorder = false; - + route->gui_changed.connect (mem_fun(*this, &Editor::handle_gui_changes)); tv->GoingAway.connect (bind (mem_fun(*this, &Editor::remove_route), tv)); } + no_route_list_redisplay = false; + + redisplay_route_list (); + if (show_editor_mixer_when_tracks_arrive) { show_editor_mixer (true); } - editor_mixer_button.set_sensitive(true); + editor_list_button.set_sensitive(true); + route_redisplay_does_not_sync_order_keys = false; } void Editor::handle_gui_changes (const string & what, void *src) { ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_gui_changes), what, src)); - + if (what == "track_height") { + /* Optional :make tracks change height while it happens, instead + of on first-idle + */ + //track_canvas->update_now (); redisplay_route_list (); } -} + if (what == "visible_tracks") { + redisplay_route_list (); + } +} void Editor::remove_route (TimeAxisView *tv) { ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::remove_route), tv)); - TrackViewList::iterator i; TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator ri; + boost::shared_ptr route; + TimeAxisView* next_tv; - if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) { - track_views.erase (i); + if (tv == entered_track) { + entered_track = 0; } + /* the core model has changed, there is no need to sync + view orders. + */ + + route_redisplay_does_not_sync_order_keys = true; + for (ri = rows.begin(); ri != rows.end(); ++ri) { if ((*ri)[route_display_columns.tv] == tv) { + route = (*ri)[route_display_columns.route]; route_display_model->erase (ri); break; } } - /* since the editor mixer goes away when you remove a route, set the - * button to inacttive - */ - editor_mixer_button.set_active(false); - /* and disable if all tracks and/or routes are gone */ + route_redisplay_does_not_sync_order_keys = false; - if (track_views.size() == 0) { - editor_mixer_button.set_sensitive(false); + if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) { + + i = track_views.erase (i); + + if (track_views.empty()) { + next_tv = 0; + } else if (i == track_views.end()) { + next_tv = track_views.front(); + } else { + next_tv = (*i); + } } + + if (current_mixer_strip && current_mixer_strip->route() == route) { + + if (next_tv) { + set_selected_mixer_strip (*next_tv); + } else { + /* make the editor mixer strip go away setting the + * button to inactive (which also unticks the menu option) + */ + + ActionManager::uncheck_toggleaction ("/Editor/show-editor-mixer"); + } + } } void @@ -173,11 +234,28 @@ Editor::route_name_changed (TimeAxisView *tv) break; } } +} +void +Editor::update_route_visibility () +{ + TreeModel::Children rows = route_display_model->children(); + TreeModel::Children::iterator i; + + no_route_list_redisplay = true; + + for (i = rows.begin(); i != rows.end(); ++i) { + TimeAxisView *tv = (*i)[route_display_columns.tv]; + (*i)[route_display_columns.visible] = tv->marked_for_display (); + cerr << "marked " << tv->name() << " for display = " << tv->marked_for_display() << endl; + } + + no_route_list_redisplay = false; + redisplay_route_list (); } void -Editor::hide_track_in_display (TimeAxisView& tv) +Editor::hide_track_in_display (TimeAxisView& tv, bool temponly) { TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; @@ -189,9 +267,9 @@ Editor::hide_track_in_display (TimeAxisView& tv) } } - AudioTimeAxisView* atv = dynamic_cast (&tv); + RouteTimeAxisView* rtv = dynamic_cast (&tv); - if (atv && current_mixer_strip && (atv->route() == current_mixer_strip->route())) { + if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) { // this will hide the mixer strip set_selected_mixer_strip (tv); } @@ -206,7 +284,6 @@ Editor::show_track_in_display (TimeAxisView& tv) for (i = rows.begin(); i != rows.end(); ++i) { if ((*i)[route_display_columns.tv] == &tv) { (*i)[route_display_columns.visible] = true; - tv.set_marked_for_display (true); break; } } @@ -218,61 +295,127 @@ Editor::route_list_reordered (const TreeModel::Path& path,const TreeModel::itera redisplay_route_list (); } + +void +Editor::sync_order_keys (const char *src) +{ + vector neworder; + TreeModel::Children rows = route_display_model->children(); + TreeModel::Children::iterator ri; + + if ((strcmp (src, _order_key) == 0) || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) { + return; + } + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + neworder.push_back (0); + } + + bool changed = false; + int order; + + for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) { + boost::shared_ptr route = (*ri)[route_display_columns.route]; + + int old_key = order; + int new_key = route->order_key (_order_key); + + neworder[new_key] = old_key; + + if (new_key != old_key) { + changed = true; + } + } + + if (changed) { + route_redisplay_does_not_reset_order_keys = true; + route_display_model->reorder (neworder); + route_redisplay_does_not_reset_order_keys = false; + } +} + void Editor::redisplay_route_list () { TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; uint32_t position; - uint32_t order; int n; - + if (no_route_list_redisplay) { return; } - for (n = 0, order = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) { + for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) { TimeAxisView *tv = (*i)[route_display_columns.tv]; - AudioTimeAxisView* at; + boost::shared_ptr route = (*i)[route_display_columns.route]; if (tv == 0) { // just a "title" row continue; } - if (!ignore_route_list_reorder) { + if (!route_redisplay_does_not_reset_order_keys) { /* this reorder is caused by user action, so reassign sort order keys to tracks. */ - if ((at = dynamic_cast (tv)) != 0) { - at->route()->set_order_key (N_("editor"), order); - ++order; - } + route->set_order_key (_order_key, n); } bool visible = (*i)[route_display_columns.visible]; + /* show or hide the TimeAxisView */ if (visible) { tv->set_marked_for_display (true); position += tv->show_at (position, n, &edit_controls_vbox); - position += track_spacing; + tv->clip_to_viewport (); } else { + tv->set_marked_for_display (false); tv->hide (); } - + n++; } - full_canvas_height = position; + /* whenever we go idle, update the track view list to reflect the new order. + we can't do this here, because we could mess up something that is traversing + the track order and has caused a redisplay of the list. + */ + + Glib::signal_idle().connect (mem_fun (*this, &Editor::sync_track_view_list_and_route_list)); + + full_canvas_height = position + canvas_timebars_vsize; + vertical_adjustment.set_upper (full_canvas_height); + if ((vertical_adjustment.get_value() + canvas_height) > vertical_adjustment.get_upper()) { + /* + We're increasing the size of the canvas while the bottom is visible. + We scroll down to keep in step with the controls layout. + */ + vertical_adjustment.set_value (full_canvas_height - canvas_height); + } + + if (!route_redisplay_does_not_reset_order_keys && !route_redisplay_does_not_sync_order_keys) { + session->sync_order_keys (_order_key); + } +} + +bool +Editor::sync_track_view_list_and_route_list () +{ + TreeModel::Children rows = route_display_model->children(); + TreeModel::Children::iterator i; - /* make sure the cursors stay on top of every newly added track */ + track_views.clear (); - cursor_group->raise_to_top (); + for (i = rows.begin(); i != rows.end(); ++i) { + TimeAxisView *tv = (*i)[route_display_columns.tv]; + track_views.push_back (tv); + } - reset_scrolling_region (); + return false; // do not call again (until needed) } void @@ -302,13 +445,13 @@ Editor::hide_all_tracks (bool with_select) otherwise. */ - reset_scrolling_region (); + //reset_scrolling_region (); } void Editor::build_route_list_menu () { - using namespace Menu_Helpers; + using namespace Menu_Helpers; using namespace Gtk; route_list_menu = new Menu; @@ -328,7 +471,7 @@ Editor::build_route_list_menu () void Editor::set_all_tracks_visibility (bool yn) { - TreeModel::Children rows = route_display_model->children(); + TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; no_route_list_redisplay = true; @@ -352,7 +495,7 @@ Editor::set_all_tracks_visibility (bool yn) void Editor::set_all_audio_visibility (int tracks, bool yn) { - TreeModel::Children rows = route_display_model->children(); + TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; no_route_list_redisplay = true; @@ -484,15 +627,15 @@ Editor::route_list_selection_filter (const Glib::RefPtr& model, const struct EditorOrderRouteSorter { bool operator() (boost::shared_ptr a, boost::shared_ptr b) { /* use of ">" forces the correct sort order */ - return a->order_key ("editor") < b->order_key ("editor"); + return a->order_key (_order_key) < b->order_key (_order_key); } }; void Editor::initial_route_list_display () { - boost::shared_ptr routes = session->get_routes(); - Session::RouteList r (*routes); + boost::shared_ptr routes = session->get_routes(); + RouteList r (*routes); EditorOrderRouteSorter sorter; r.sort (sorter); @@ -508,16 +651,180 @@ Editor::initial_route_list_display () redisplay_route_list (); } +void +Editor::track_list_reorder (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter, int* new_order) +{ + route_redisplay_does_not_sync_order_keys = true; + session->set_remote_control_ids(); + redisplay_route_list (); + route_redisplay_does_not_sync_order_keys = false; +} + void Editor::route_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter) { + /* never reset order keys because of a property change */ + route_redisplay_does_not_reset_order_keys = true; session->set_remote_control_ids(); redisplay_route_list (); + route_redisplay_does_not_reset_order_keys = false; } void Editor::route_list_delete (const Gtk::TreeModel::Path& path) { + /* this could require an order reset & sync */ session->set_remote_control_ids(); + ignore_route_list_reorder = true; redisplay_route_list (); + ignore_route_list_reorder = false; +} + +void +Editor::route_list_display_drag_data_received (const RefPtr& context, + int x, int y, + const SelectionData& data, + guint info, guint time) +{ + if (data.get_target() == "GTK_TREE_MODEL_ROW") { + route_list_display.on_drag_data_received (context, x, y, data, info, time); + return; + } + context->drag_finish (true, false, time); +} + +RouteTimeAxisView* +Editor::get_route_view_by_id (PBD::ID& id) +{ + RouteTimeAxisView* v; + + for(TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + if((v = dynamic_cast(*i)) != 0) { + if(v->route()->id() == id) { + return v; + } + } + } + + return 0; +} + +void +Editor::foreach_time_axis_view (sigc::slot theslot) +{ + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + theslot (**i); + } +} + +void +Editor::move_selected_tracks (bool up) +{ + if (selection->tracks.empty()) { + return; + } + + typedef std::pair > ViewRoute; + std::list view_routes; + std::vector neworder; + TreeModel::Children rows = route_display_model->children(); + TreeModel::Children::iterator ri; + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + TimeAxisView* tv = (*ri)[route_display_columns.tv]; + boost::shared_ptr route = (*ri)[route_display_columns.route]; + + view_routes.push_back (ViewRoute (tv, route)); + } + + list::iterator trailing; + list::iterator leading; + + if (up) { + + trailing = view_routes.begin(); + leading = view_routes.begin(); + + ++leading; + + while (leading != view_routes.end()) { + if (selection->selected (leading->first)) { + view_routes.insert (trailing, ViewRoute (leading->first, leading->second)); + leading = view_routes.erase (leading); + } else { + ++leading; + ++trailing; + } + } + + } else { + + /* if we could use reverse_iterator in list::insert, this code + would be a beautiful reflection of the code above. but we can't + and so it looks like a bit of a mess. + */ + + trailing = view_routes.end(); + leading = view_routes.end(); + + --leading; if (leading == view_routes.begin()) { return; } + --leading; + --trailing; + + while (1) { + + if (selection->selected (leading->first)) { + list::iterator tmp; + + /* need to insert *after* trailing, not *before* it, + which is what insert (iter, val) normally does. + */ + + tmp = trailing; + tmp++; + + view_routes.insert (tmp, ViewRoute (leading->first, leading->second)); + + /* can't use iter = cont.erase (iter); form here, because + we need iter to move backwards. + */ + + tmp = leading; + --tmp; + + bool done = false; + + if (leading == view_routes.begin()) { + /* the one we've just inserted somewhere else + was the first in the list. erase this copy, + and then break, because we're done. + */ + done = true; + } + + view_routes.erase (leading); + + if (done) { + break; + } + + leading = tmp; + + } else { + if (leading == view_routes.begin()) { + break; + } + --leading; + --trailing; + } + }; + } + + for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) { + neworder.push_back (leading->second->order_key (_order_key)); + } + + route_display_model->reorder (neworder); + + session->sync_order_keys (_order_key); }