diff options
Diffstat (limited to 'gtk2_ardour/route_time_axis.cc')
-rw-r--r-- | gtk2_ardour/route_time_axis.cc | 506 |
1 files changed, 505 insertions, 1 deletions
diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index b4f250b5ef..1eba885b44 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000 Paul Davis + Copyright (C) 2006 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,6 +30,8 @@ #include <pbd/stl_delete.h> #include <pbd/whitespace.h> +#include <gtkmm/menu.h> +#include <gtkmm/menuitem.h> #include <gtkmm2ext/gtk_ui.h> #include <gtkmm2ext/selector.h> #include <gtkmm2ext/stop_signal.h> @@ -49,6 +51,9 @@ #include "ardour_ui.h" #include "route_time_axis.h" +#include "automation_time_axis.h" +#include "redirect_automation_time_axis.h" +#include "redirect_automation_line.h" #include "canvas_impl.h" #include "crossfade_view.h" #include "enums.h" @@ -217,6 +222,12 @@ RouteTimeAxisView::~RouteTimeAxisView () { GoingAway (); /* EMIT_SIGNAL */ + vector_delete (&redirect_automation_curves); + + for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) { + delete *i; + } + if (playlist_menu) { delete playlist_menu; playlist_menu = 0; @@ -374,6 +385,97 @@ RouteTimeAxisView::automation_click () } void +RouteTimeAxisView::build_automation_action_menu () +{ + using namespace Menu_Helpers; + + automation_action_menu = manage (new Menu); + MenuList& automation_items = automation_action_menu->items(); + automation_action_menu->set_name ("ArdourContextMenu"); + + automation_items.push_back (MenuElem (_("Show all automation"), + mem_fun(*this, &RouteTimeAxisView::show_all_automation))); + + automation_items.push_back (MenuElem (_("Show existing automation"), + mem_fun(*this, &RouteTimeAxisView::show_existing_automation))); + + automation_items.push_back (MenuElem (_("Hide all automation"), + mem_fun(*this, &RouteTimeAxisView::hide_all_automation))); + + automation_items.push_back (MenuElem (_("Plugins"), subplugin_menu)); +} + +void +RouteTimeAxisView::build_display_menu () +{ + using namespace Menu_Helpers; + + /* get the size menu ready */ + + build_size_menu (); + + /* prepare it */ + + TimeAxisView::build_display_menu (); + + /* now fill it with our stuff */ + + MenuList& items = display_menu->items(); + display_menu->set_name ("ArdourContextMenu"); + + items.push_back (MenuElem (_("Height"), *size_menu)); + items.push_back (MenuElem (_("Color"), mem_fun(*this, &RouteTimeAxisView::select_track_color))); + + items.push_back (SeparatorElem()); + + build_remote_control_menu (); + items.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu)); + + build_automation_action_menu (); + items.push_back (MenuElem (_("Automation"), *automation_action_menu)); + + // Hook for derived classes to add type specific stuff + items.push_back (SeparatorElem()); + append_extra_display_menu_items (); + items.push_back (SeparatorElem()); + + if (is_track()) { + + Menu* alignment_menu = manage (new Menu); + MenuList& alignment_items = alignment_menu->items(); + alignment_menu->set_name ("ArdourContextMenu"); + + RadioMenuItem::Group align_group; + + alignment_items.push_back (RadioMenuElem (align_group, _("Align with existing material"), + bind (mem_fun(*this, &RouteTimeAxisView::set_align_style), ExistingMaterial))); + align_existing_item = dynamic_cast<RadioMenuItem*>(&alignment_items.back()); + if (get_diskstream()->alignment_style() == ExistingMaterial) + align_existing_item->set_active(); + + alignment_items.push_back (RadioMenuElem (align_group, _("Align with capture time"), + bind (mem_fun(*this, &RouteTimeAxisView::set_align_style), CaptureTime))); + align_capture_item = dynamic_cast<RadioMenuItem*>(&alignment_items.back()); + if (get_diskstream()->alignment_style() == CaptureTime) + align_capture_item->set_active(); + + items.push_back (MenuElem (_("Alignment"), *alignment_menu)); + + get_diskstream()->AlignmentStyleChanged.connect ( + mem_fun(*this, &RouteTimeAxisView::align_style_changed)); + } + + items.push_back (SeparatorElem()); + items.push_back (CheckMenuElem (_("Active"), mem_fun(*this, &RouteUI::toggle_route_active))); + route_active_menu_item = dynamic_cast<CheckMenuItem *> (&items.back()); + route_active_menu_item->set_active (_route->active()); + + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route))); +} + + +void RouteTimeAxisView::show_timestretch (jack_nframes_t start, jack_nframes_t end) { double x1; @@ -760,6 +862,12 @@ RouteTimeAxisView::set_selected_points (PointSelection& points) } void +RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions) +{ + _view->set_selected_regionviews (regions); +} + +void RouteTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results) { double speed = 1.0; @@ -1053,3 +1161,399 @@ RouteTimeAxisView::select_me (GdkEventButton* ev) return false; } +void +RouteTimeAxisView::show_all_automation () +{ + no_redraw = true; + + for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) { + for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { + if ((*ii)->view == 0) { + add_redirect_automation_curve ((*i)->redirect, (*ii)->what); + } + + (*ii)->menu_item->set_active (true); + } + } + + no_redraw = false; + + _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ +} + +void +RouteTimeAxisView::show_existing_automation () +{ + no_redraw = true; + + for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) { + for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { + if ((*ii)->view != 0) { + (*ii)->menu_item->set_active (true); + } + } + } + + no_redraw = false; + + _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ +} + +void +RouteTimeAxisView::hide_all_automation () +{ + no_redraw = true; + + for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) { + for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { + (*ii)->menu_item->set_active (false); + } + } + + no_redraw = false; + _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ +} + + +void +RouteTimeAxisView::region_view_added (RegionView* rv) +{ + for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) { + AutomationTimeAxisView* atv; + + if ((atv = dynamic_cast<AutomationTimeAxisView*> (*i)) != 0) { + rv->add_ghost (*atv); + } + } +} + +void +RouteTimeAxisView::add_ghost_to_redirect (RegionView* rv, AutomationTimeAxisView* atv) +{ + rv->add_ghost (*atv); +} + +RouteTimeAxisView::RedirectAutomationInfo::~RedirectAutomationInfo () +{ + for (vector<RedirectAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) { + delete *i; + } +} + + +RouteTimeAxisView::RedirectAutomationNode::~RedirectAutomationNode () +{ + parent.remove_ran (this); + + if (view) { + delete view; + } +} + +void +RouteTimeAxisView::remove_ran (RedirectAutomationNode* ran) +{ + if (ran->view) { + remove_child (ran->view); + } +} + +RouteTimeAxisView::RedirectAutomationNode* +RouteTimeAxisView::find_redirect_automation_node (boost::shared_ptr<Redirect> redirect, uint32_t what) +{ + for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) { + + if ((*i)->redirect == redirect) { + + for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { + if ((*ii)->what == what) { + return *ii; + } + } + } + } + + return 0; +} + +// FIXME: duplicated in midi_time_axis.cc +static string +legalize_for_xml_node (string str) +{ + string::size_type pos; + string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=:"; + string legal; + + legal = str; + pos = 0; + + while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) { + legal.replace (pos, 1, "_"); + pos += 1; + } + + return legal; +} + + +void +RouteTimeAxisView::add_redirect_automation_curve (boost::shared_ptr<Redirect> redirect, uint32_t what) +{ + RedirectAutomationLine* ral; + string name; + RedirectAutomationNode* ran; + + if ((ran = find_redirect_automation_node (redirect, what)) == 0) { + fatal << _("programming error: ") + << string_compose (X_("redirect automation curve for %1:%2 not registered with audio track!"), + redirect->name(), what) + << endmsg; + /*NOTREACHED*/ + return; + } + + if (ran->view) { + return; + } + + name = redirect->describe_parameter (what); + + /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */ + + char state_name[256]; + snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (redirect->name()).c_str(), what); + + ran->view = new RedirectAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, name, what, *redirect, state_name); + + ral = new RedirectAutomationLine (name, + *redirect, what, _session, *ran->view, + *ran->view->canvas_display, redirect->automation_list (what)); + + ral->set_line_color (color_map[cRedirectAutomationLine]); + ral->queue_reset (); + + ran->view->add_line (*ral); + + ran->view->Hiding.connect (bind (mem_fun(*this, &RouteTimeAxisView::redirect_automation_track_hidden), ran, redirect)); + + if (!ran->view->marked_for_display()) { + ran->view->hide (); + } else { + ran->menu_item->set_active (true); + } + + add_child (ran->view); + + _view->foreach_regionview (bind (mem_fun(*this, &RouteTimeAxisView::add_ghost_to_redirect), ran->view)); + + redirect->mark_automation_visible (what, true); +} + +void +RouteTimeAxisView::redirect_automation_track_hidden (RouteTimeAxisView::RedirectAutomationNode* ran, boost::shared_ptr<Redirect> r) +{ + if (!_hidden) { + ran->menu_item->set_active (false); + } + + r->mark_automation_visible (ran->what, false); + + _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ +} + +void +RouteTimeAxisView::add_existing_redirect_automation_curves (boost::shared_ptr<Redirect> redirect) +{ + set<uint32_t> s; + RedirectAutomationLine *ral; + + redirect->what_has_visible_automation (s); + + for (set<uint32_t>::iterator i = s.begin(); i != s.end(); ++i) { + + if ((ral = find_redirect_automation_curve (redirect, *i)) != 0) { + ral->queue_reset (); + } else { + add_redirect_automation_curve (redirect, (*i)); + } + } +} + +void +RouteTimeAxisView::add_redirect_to_subplugin_menu (boost::shared_ptr<Redirect> r) +{ + using namespace Menu_Helpers; + RedirectAutomationInfo *rai; + list<RedirectAutomationInfo*>::iterator x; + + const std::set<uint32_t>& automatable = r->what_can_be_automated (); + std::set<uint32_t> has_visible_automation; + + r->what_has_visible_automation(has_visible_automation); + + if (automatable.empty()) { + return; + } + + for (x = redirect_automation.begin(); x != redirect_automation.end(); ++x) { + if ((*x)->redirect == r) { + break; + } + } + + if (x == redirect_automation.end()) { + + rai = new RedirectAutomationInfo (r); + redirect_automation.push_back (rai); + + } else { + + rai = *x; + + } + + /* any older menu was deleted at the top of redirects_changed() + when we cleared the subplugin menu. + */ + + rai->menu = manage (new Menu); + MenuList& items = rai->menu->items(); + rai->menu->set_name ("ArdourContextMenu"); + + items.clear (); + + for (std::set<uint32_t>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) { + + RedirectAutomationNode* ran; + CheckMenuItem* mitem; + + string name = r->describe_parameter (*i); + + items.push_back (CheckMenuElem (name)); + mitem = dynamic_cast<CheckMenuItem*> (&items.back()); + + if (has_visible_automation.find((*i)) != has_visible_automation.end()) { + mitem->set_active(true); + } + + if ((ran = find_redirect_automation_node (r, *i)) == 0) { + + /* new item */ + + ran = new RedirectAutomationNode (*i, mitem, *this); + + rai->lines.push_back (ran); + + } else { + + ran->menu_item = mitem; + + } + + mitem->signal_toggled().connect (bind (mem_fun(*this, &RouteTimeAxisView::redirect_menu_item_toggled), rai, ran)); + } + + /* add the menu for this redirect, because the subplugin + menu is always cleared at the top of redirects_changed(). + this is the result of some poor design in gtkmm and/or + GTK+. + */ + + subplugin_menu.items().push_back (MenuElem (r->name(), *rai->menu)); + rai->valid = true; +} + +void +RouteTimeAxisView::redirect_menu_item_toggled (RouteTimeAxisView::RedirectAutomationInfo* rai, + RouteTimeAxisView::RedirectAutomationNode* ran) +{ + bool showit = ran->menu_item->get_active(); + bool redraw = false; + + if (ran->view == 0 && showit) { + add_redirect_automation_curve (rai->redirect, ran->what); + redraw = true; + } + + if (showit != ran->view->marked_for_display()) { + + if (showit) { + ran->view->set_marked_for_display (true); + ran->view->canvas_display->show(); + } else { + rai->redirect->mark_automation_visible (ran->what, true); + ran->view->set_marked_for_display (false); + ran->view->hide (); + } + + redraw = true; + + } + + if (redraw && !no_redraw) { + + /* now trigger a redisplay */ + + _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ + + } +} + +void +RouteTimeAxisView::redirects_changed (void *src) +{ + using namespace Menu_Helpers; + + for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) { + (*i)->valid = false; + } + + subplugin_menu.items().clear (); + + _route->foreach_redirect (this, &RouteTimeAxisView::add_redirect_to_subplugin_menu); + _route->foreach_redirect (this, &RouteTimeAxisView::add_existing_redirect_automation_curves); + + for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ) { + + list<RedirectAutomationInfo*>::iterator tmp; + + tmp = i; + ++tmp; + + if (!(*i)->valid) { + + delete *i; + redirect_automation.erase (i); + + } + + i = tmp; + } + + /* change in visibility was possible */ + + _route->gui_changed ("track_height", this); +} + +RedirectAutomationLine * +RouteTimeAxisView::find_redirect_automation_curve (boost::shared_ptr<Redirect> redirect, uint32_t what) +{ + RedirectAutomationNode* ran; + + if ((ran = find_redirect_automation_node (redirect, what)) != 0) { + if (ran->view) { + return dynamic_cast<RedirectAutomationLine*> (ran->view->lines.front()); + } + } + + return 0; +} + +void +RouteTimeAxisView::reset_redirect_automation_curves () +{ + for (vector<RedirectAutomationLine*>::iterator i = redirect_automation_curves.begin(); i != redirect_automation_curves.end(); ++i) { + (*i)->reset(); + } +} + |