X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fautomation_time_axis.cc;h=d316e74160517a64aced586c995edcf1ed6cd9a5;hb=c862d320ffccf739785e4b9aa59d7842e6f50ace;hp=44ee3edb12ab5ad20417b52fa4e6aefcda4901d4;hpb=b5148d93d5a9e6949f82f8685cab50cb772f2b9d;p=ardour.git diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index 44ee3edb12..d316e74160 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -35,7 +35,6 @@ #include "simplerect.h" #include "selection.h" #include "rgb_macros.h" -#include "automation_selectable.h" #include "point_selection.h" #include "canvas_impl.h" #include "utils.h" @@ -93,7 +92,10 @@ AutomationTimeAxisView::AutomationTimeAxisView (Session* s, boost::shared_ptrproperty_x1() = 0.0; _base_rect->property_y1() = 0.0; - _base_rect->property_x2() = LONG_MAX - 2; + /** gnomecanvas sometimes converts this value to int or adds 2 to it, so it must be + set correctly to avoid overflow. + */ + _base_rect->property_x2() = INT_MAX - 2; _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get(); /* outline ends and bottom */ @@ -194,8 +196,7 @@ AutomationTimeAxisView::AutomationTimeAxisView (Session* s, boost::shared_ptrget_automation_child_xml_node ( - _control->parameter()); + XMLNode* xml_node = get_parent_with_state()->get_automation_child_xml_node (_control->parameter()); if (xml_node) { set_state (*xml_node, Stateful::loading_state_version); @@ -256,22 +257,29 @@ AutomationTimeAxisView::auto_clicked () void AutomationTimeAxisView::set_automation_state (AutoState state) { - if (!ignore_state_request) { - if (_automatable) { - _automatable->set_parameter_automation_state (_control->parameter(), state); - } -#if 0 - if (_route == _automatable) { // This is a time axis for route (not region) automation - _route->set_parameter_automation_state (_control->parameter(), state); - } - - if (_control->list()) - _control->alist()->set_automation_state(state); -#endif + if (ignore_state_request) { + return; } + if (_automatable) { + _automatable->set_parameter_automation_state (_control->parameter(), state); + } +#if 0 + if (_route == _automatable) { // This is a time axis for route (not region) automation + _route->set_parameter_automation_state (_control->parameter(), state); + } + + if (_control->list()) { + _control->alist()->set_automation_state(state); + } +#endif if (_view) { _view->set_automation_state (state); + + /* AutomationStreamViews don't signal when their automation state changes, so handle + our updates `manually'. + */ + automation_state_changed (); } } @@ -282,10 +290,12 @@ AutomationTimeAxisView::automation_state_changed () /* update button label */ - if (!_line) { - state = Off; - } else { + if (_line) { state = _control->alist()->automation_state (); + } else if (_view) { + state = _view->automation_state (); + } else { + state = Off; } switch (state & (Off|Play|Touch|Write)) { @@ -339,13 +349,12 @@ AutomationTimeAxisView::automation_state_changed () } } +/** The interpolation style of our AutomationList has changed, so update */ void -AutomationTimeAxisView::interpolation_changed () +AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s) { - AutomationList::InterpolationStyle style = _control->list()->interpolation(); - if (mode_line_item && mode_discrete_item) { - if (style == AutomationList::Discrete) { + if (s == AutomationList::Discrete) { mode_discrete_item->set_active(true); mode_line_item->set_active(false); } else { @@ -353,38 +362,46 @@ AutomationTimeAxisView::interpolation_changed () mode_discrete_item->set_active(false); } } - - if (_line) { - _line->set_interpolation(style); - } } +/** A menu item has been selected to change our interpolation mode */ void AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style) { - _control->list()->set_interpolation(style); - if (_line) { - _line->set_interpolation(style); + /* Tell our view's list, if we have one, otherwise tell our own. + * Everything else will be signalled back from that. + */ + + if (_view) { + _view->set_interpolation (style); + } else { + _control->list()->set_interpolation (style); } } void AutomationTimeAxisView::clear_clicked () { + assert (_line || _view); + _session->begin_reversible_command (_("clear automation")); + if (_line) { _line->clear (); + } else if (_view) { + _view->clear (); } + _session->commit_reversible_command (); + _session->set_dirty (); } void AutomationTimeAxisView::set_height (uint32_t h) { - bool changed = (height != (uint32_t) h) || first_call_to_set_height; - bool changed_between_small_and_normal = ( - (height < hNormal && h >= hNormal) - || (height >= hNormal || h < hNormal) ); + bool const changed = (height != (uint32_t) h) || first_call_to_set_height; + uint32_t const normal = preset_height (HeightNormal); + bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) ); TimeAxisView* state_parent = get_parent_with_state (); assert(state_parent); @@ -393,8 +410,9 @@ AutomationTimeAxisView::set_height (uint32_t h) TimeAxisView::set_height (h); _base_rect->property_y2() = h; - if (_line) + if (_line) { _line->set_height(h); + } if (_view) { _view->set_height(h); @@ -411,7 +429,7 @@ AutomationTimeAxisView::set_height (uint32_t h) first_call_to_set_height = false; - if (h >= hNormal) { + if (h >= preset_height (HeightNormal)) { controls_table.remove (name_hbox); if (plugname) { @@ -432,7 +450,7 @@ AutomationTimeAxisView::set_height (uint32_t h) auto_button.show(); hide_button.show_all(); - } else if (h >= hSmall) { + } else if (h >= preset_height (HeightSmall)) { controls_table.remove (name_hbox); if (plugname) { if (plugname_packed) { @@ -449,7 +467,7 @@ AutomationTimeAxisView::set_height (uint32_t h) auto_button.hide(); hide_button.hide(); } - } else if (h >= hNormal){ + } else if (h >= preset_height (HeightNormal)) { cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl; } @@ -466,11 +484,13 @@ AutomationTimeAxisView::set_samples_per_unit (double spu) { TimeAxisView::set_samples_per_unit (spu); - if (_line) + if (_line) { _line->reset (); + } - if (_view) + if (_view) { _view->set_samples_per_unit (spu); + } } void @@ -490,10 +510,6 @@ AutomationTimeAxisView::build_display_menu () { using namespace Menu_Helpers; - /* get the size menu ready */ - - build_size_menu (); - /* prepare it */ TimeAxisView::build_display_menu (); @@ -502,8 +518,6 @@ AutomationTimeAxisView::build_display_menu () MenuList& items = display_menu->items(); - items.push_back (MenuElem (_("Height"), *size_menu)); - items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked))); @@ -539,6 +553,9 @@ AutomationTimeAxisView::build_display_menu () /* mode menu */ + /* current interpolation state */ + AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation (); + if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) { Menu* auto_mode_menu = manage (new Menu); @@ -551,17 +568,13 @@ AutomationTimeAxisView::build_display_menu () sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation), AutomationList::Discrete))); mode_discrete_item = dynamic_cast(&am_items.back()); - mode_discrete_item->set_active(_control->list()->interpolation() == AutomationList::Discrete); + mode_discrete_item->set_active (s == AutomationList::Discrete); am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind ( sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation), AutomationList::Linear))); mode_line_item = dynamic_cast(&am_items.back()); - - // Set default interpolation type to linear if this isn't a (usually) discrete controller - if (EventTypeMap::instance().interpolation_of(_control->parameter()) == Evoral::ControlList::Linear) { - mode_line_item->set_active(_control->list()->interpolation() == AutomationList::Linear); - } + mode_line_item->set_active (s == AutomationList::Linear); items.push_back (MenuElem (_("Mode"), *auto_mode_menu)); } @@ -569,7 +582,7 @@ AutomationTimeAxisView::build_display_menu () /* make sure the automation menu state is correct */ automation_state_changed (); - interpolation_changed (); + interpolation_changed (s); } void @@ -601,39 +614,51 @@ AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkE _session->set_dirty (); } -bool +void AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) { - return (_line ? cut_copy_clear_one (*_line, selection, op) : false); + list > lines; + if (_line) { + lines.push_back (_line); + } else if (_view) { + lines = _view->get_lines (); + } + + for (list >::iterator i = lines.begin(); i != lines.end(); ++i) { + cut_copy_clear_one (**i, selection, op); + } } -bool +void AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op) { boost::shared_ptr what_we_got; boost::shared_ptr alist (line.the_list()); - bool ret = false; XMLNode &before = alist->get_state(); + /* convert time selection to automation list model coordinates */ + const Evoral::TimeConverter& tc = line.time_converter (); + double const start = tc.from (selection.time.front().start - tc.origin_b ()); + double const end = tc.from (selection.time.front().end - tc.origin_b ()); + switch (op) { case Cut: - if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) { + + if ((what_we_got = alist->cut (start, end)) != 0) { _editor.get_cut_buffer().add (what_we_got); _session->add_command(new MementoCommand(*alist.get(), &before, &alist->get_state())); - ret = true; } break; case Copy: - if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) { + if ((what_we_got = alist->copy (start, end)) != 0) { _editor.get_cut_buffer().add (what_we_got); } break; case Clear: - if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) { + if ((what_we_got = alist->cut (start, end)) != 0) { _session->add_command(new MementoCommand(*alist.get(), &before, &alist->get_state())); - ret = true; } break; } @@ -647,14 +672,21 @@ AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& sel (*x)->value = val; } } - - return ret; } void AutomationTimeAxisView::reset_objects (PointSelection& selection) { - reset_objects_one (*_line, selection); + list > lines; + if (_line) { + lines.push_back (_line); + } else if (_view) { + lines = _view->get_lines (); + } + + for (list >::iterator i = lines.begin(); i != lines.end(); ++i) { + reset_objects_one (**i, selection); + } } void @@ -674,18 +706,26 @@ AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& } } -bool +void AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op) { - return cut_copy_clear_objects_one (*_line, selection, op); + list > lines; + if (_line) { + lines.push_back (_line); + } else if (_view) { + lines = _view->get_lines (); + } + + for (list >::iterator i = lines.begin(); i != lines.end(); ++i) { + cut_copy_clear_objects_one (**i, selection, op); + } } -bool +void AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op) { boost::shared_ptr what_we_got; boost::shared_ptr alist(line.the_list()); - bool ret = false; XMLNode &before = alist->get_state(); @@ -700,7 +740,6 @@ AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointS if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) { _editor.get_cut_buffer().add (what_we_got); _session->add_command (new MementoCommand(*alist.get(), new XMLNode (before), &alist->get_state())); - ret = true; } break; case Copy: @@ -712,7 +751,6 @@ AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointS case Clear: if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) { _session->add_command (new MementoCommand(*alist.get(), new XMLNode (before), &alist->get_state())); - ret = true; } break; } @@ -729,18 +767,34 @@ AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointS (*x)->value = val; } } - - return ret; } +/** Paste a selection. + * @param pos Position to paste to (session frames). + * @param times Number of times to paste. + * @param selection Selection to paste. + * @param nth Index of the AutomationList within the selection to paste from. + */ bool -AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth) +AutomationTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth) { - return paste_one (*_line, pos, times, selection, nth); + boost::shared_ptr line; + + if (_line) { + line = _line; + } else if (_view) { + line = _view->paste_line (pos); + } + + if (!line) { + return false; + } + + return paste_one (*line, pos, times, selection, nth); } bool -AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth) +AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float times, Selection& selection, size_t nth) { AutomationSelection::iterator p; boost::shared_ptr alist(line.the_list()); @@ -766,25 +820,33 @@ AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float ti (*x)->value = val; } + double const model_pos = line.time_converter().from (pos - line.time_converter().origin_b ()); + XMLNode &before = alist->get_state(); - alist->paste (copy, pos, times); + alist->paste (copy, model_pos, times); _session->add_command (new MementoCommand(*alist.get(), &before, &alist->get_state())); return true; } void -AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list& results) +AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list& results) { - if (_line && touched (top, bot)) { - double topfrac; - double botfrac; + if (!_line && !_view) { + return; + } + + if (touched (top, bot)) { /* remember: this is X Window - coordinate space starts in upper left and moves down. _y_position is the "origin" or "top" of the track. */ - double mybot = _y_position + height; + /* bottom of our track */ + double const mybot = _y_position + height; + + double topfrac; + double botfrac; if (_y_position >= top && mybot <= bot) { @@ -801,18 +863,23 @@ AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double topfrac = 1.0 - ((top - _y_position) / height); botfrac = 1.0 - ((bot - _y_position) / height); + } - if (_line) + if (_line) { _line->get_selectables (start, end, botfrac, topfrac, results); + } else if (_view) { + _view->get_selectables (start, end, botfrac, topfrac, results); + } } } void AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list& result) { - if (_line) + if (_line) { _line->get_inverted_selectables (sel, result); + } } void @@ -820,6 +887,8 @@ AutomationTimeAxisView::set_selected_points (PointSelection& points) { if (_line) { _line->set_selected_points (points); + } else if (_view) { + _view->set_selected_points (points); } } @@ -827,7 +896,7 @@ void AutomationTimeAxisView::clear_lines () { _line.reset(); - automation_connection.disconnect (); + _list_connections.drop_connections (); } void @@ -837,7 +906,13 @@ AutomationTimeAxisView::add_line (boost::shared_ptr line) assert(!_line); assert(line->the_list() == _control->list()); - _control->alist()->automation_state_changed.connect (automation_connection, boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()); + _control->alist()->automation_state_changed.connect ( + _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context() + ); + + _control->alist()->InterpolationChanged.connect ( + _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context() + ); _line = line; //_controller = AutomationController::create(_session, line->the_list(), _control); @@ -877,6 +952,10 @@ AutomationTimeAxisView::set_state (const XMLNode& node, int version) { TimeAxisView::set_state (node, version); + if (version < 3000) { + return set_state_2X (node, version); + } + XMLProperty const * type = node.property ("automation-id"); if (type && type->value () == ARDOUR::EventTypeMap::instance().to_symbol (_control->parameter())) { XMLProperty const * shown = node.property ("shown"); @@ -893,6 +972,25 @@ AutomationTimeAxisView::set_state (const XMLNode& node, int version) return 0; } +int + +AutomationTimeAxisView::set_state_2X (const XMLNode& node, int /*version*/) +{ + if (node.name() == X_("gain") && _control->parameter() == Evoral::Parameter (GainAutomation)) { + XMLProperty const * shown = node.property (X_("shown")); + if (shown && string_is_affirmative (shown->value ())) { + set_marked_for_display (true); + _canvas_display->show (); /* FIXME: necessary? show_at? */ + } + } + + if (!_marked_for_display) { + hide (); + } + + return 0; +} + XMLNode* AutomationTimeAxisView::get_state_node () { @@ -941,3 +1039,24 @@ AutomationTimeAxisView::set_visibility (bool yn) return changed; } + +/** @return true if this view has any automation data to display */ +bool +AutomationTimeAxisView::has_automation () const +{ + return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) ); +} + +list > +AutomationTimeAxisView::lines () const +{ + list > lines; + + if (_line) { + lines.push_back (_line); + } else if (_view) { + lines = _view->get_lines (); + } + + return lines; +}