Fix DSP load sorting with inactive plugins
[ardour.git] / gtk2_ardour / automation_time_axis.cc
index a0052032b008d76552281bad7aeba55478a30ff4..17e4877f3a8daeb8eb9b48c3c8ba1cc7d3e48557 100644 (file)
@@ -1,27 +1,39 @@
 /*
-    Copyright (C) 2000-2007 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
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
+ * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
+ * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
+ * Copyright (C) 2006-2007 Doug McLain <doug@nostar.net>
+ * Copyright (C) 2006 Hans Fugal <hans@fugal.net>
+ * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2007-2015 David Robillard <d@drobilla.net>
+ * Copyright (C) 2009 Hans Baier <hansfbaier@googlemail.com>
+ * Copyright (C) 2014-2015 Ben Loftis <ben@harrisonconsoles.com>
+ * Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
+ * Copyright (C) 2014-2017 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2015-2017 Tim Mayberry <mojofunk@gmail.com>
+ * Copyright (C) 2016 Julien "_FrnchFrgg_" RIVAUD <frnchfrgg@free.fr>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
 
 #include <utility>
-#include <gtkmm2ext/barcontroller.h>
-#include <gtkmm2ext/utils.h>
+
 #include <boost/algorithm/string.hpp>
 
+#include <gtkmm/separator.h>
+
 #include "pbd/error.h"
 #include "pbd/memento_command.h"
 #include "pbd/stacktrace.h"
 #include "pbd/unwind.h"
 
 #include "ardour/automation_control.h"
-#include "ardour/beats_frames_converter.h"
+#include "ardour/beats_samples_converter.h"
 #include "ardour/event_type_map.h"
 #include "ardour/parameter_types.h"
 #include "ardour/profile.h"
 #include "ardour/route.h"
 #include "ardour/session.h"
 
+#include "gtkmm2ext/utils.h"
+
 #include "canvas/debug.h"
 
 #include "widgets/tooltips.h"
@@ -135,6 +149,7 @@ AutomationTimeAxisView::AutomationTimeAxisView (
 
        auto_off_item = 0;
        auto_touch_item = 0;
+       auto_latch_item = 0;
        auto_write_item = 0;
        auto_play_item = 0;
        mode_discrete_item = 0;
@@ -158,14 +173,17 @@ AutomationTimeAxisView::AutomationTimeAxisView (
 
        using namespace Menu_Helpers;
 
-       auto_dropdown.AddMenuElem (MenuElem (S_("Automation|Manual"), sigc::bind (sigc::mem_fun(*this,
+       auto_dropdown.AddMenuElem (MenuElem (automation_state_off_string(), sigc::bind (sigc::mem_fun(*this,
                                                &AutomationTimeAxisView::set_automation_state), (AutoState) ARDOUR::Off)));
        auto_dropdown.AddMenuElem (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
                                                &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
-       auto_dropdown.AddMenuElem (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
-                                               &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
-       auto_dropdown.AddMenuElem (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
-                                               &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
+
+       if (!(_parameter.type() >= MidiCCAutomation &&
+             _parameter.type() <= MidiChannelPressureAutomation)) {
+               auto_dropdown.AddMenuElem (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
+               auto_dropdown.AddMenuElem (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
+               auto_dropdown.AddMenuElem (MenuElem (_("Latch"), sigc::bind (sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Latch)));
+       }
 
        /* XXX translators: use a string here that will be at least as long
           as the longest automation label (see ::automation_state_changed()
@@ -310,13 +328,17 @@ AutomationTimeAxisView::AutomationTimeAxisView (
 
 AutomationTimeAxisView::~AutomationTimeAxisView ()
 {
-       cleanup_gui_properties ();
+       if (_stripable) {
+               cleanup_gui_properties ();
+       }
        delete _view;
+       CatchDeletion (this);
 }
 
 void
 AutomationTimeAxisView::route_going_away ()
 {
+       cleanup_gui_properties ();
        _stripable.reset ();
 }
 
@@ -332,6 +354,7 @@ AutomationTimeAxisView::set_automation_state (AutoState state)
        }
        else if (_control) {
                _control->set_automation_state (state);
+               _session->set_dirty ();
        }
 
        if (_view) {
@@ -360,50 +383,76 @@ AutomationTimeAxisView::automation_state_changed ()
                state = ARDOUR::Off;
        }
 
-       switch (state & (ARDOUR::Off|Play|Touch|Write)) {
+       switch (state & (ARDOUR::Off|Play|Touch|Write|Latch)) {
        case ARDOUR::Off:
-               auto_dropdown.set_text (S_("Automation|Manual"));
+               auto_dropdown.set_text (automation_state_off_string());
+               ignore_state_request = true;
                if (auto_off_item) {
-                       ignore_state_request = true;
                        auto_off_item->set_active (true);
                        auto_play_item->set_active (false);
+               }
+               if (auto_touch_item) {
                        auto_touch_item->set_active (false);
+                       auto_latch_item->set_active (false);
                        auto_write_item->set_active (false);
-                       ignore_state_request = false;
                }
+               ignore_state_request = false;
                break;
        case Play:
                auto_dropdown.set_text (_("Play"));
-               if (auto_play_item) {
-                       ignore_state_request = true;
+               ignore_state_request = true;
+               if (auto_off_item) {
                        auto_play_item->set_active (true);
                        auto_off_item->set_active (false);
+               }
+               if (auto_touch_item) {
                        auto_touch_item->set_active (false);
+                       auto_latch_item->set_active (false);
                        auto_write_item->set_active (false);
-                       ignore_state_request = false;
                }
+               ignore_state_request = false;
                break;
        case Write:
                auto_dropdown.set_text (_("Write"));
-               if (auto_write_item) {
-                       ignore_state_request = true;
-                       auto_write_item->set_active (true);
+               ignore_state_request = true;
+               if (auto_off_item) {
                        auto_off_item->set_active (false);
                        auto_play_item->set_active (false);
+               }
+               if (auto_touch_item) {
+                       auto_write_item->set_active (true);
                        auto_touch_item->set_active (false);
-                       ignore_state_request = false;
+                       auto_latch_item->set_active (false);
                }
+               ignore_state_request = false;
                break;
        case Touch:
                auto_dropdown.set_text (_("Touch"));
+               ignore_state_request = true;
+               if (auto_off_item) {
+                       auto_off_item->set_active (false);
+                       auto_play_item->set_active (false);
+               }
                if (auto_touch_item) {
-                       ignore_state_request = true;
                        auto_touch_item->set_active (true);
+                       auto_write_item->set_active (false);
+                       auto_latch_item->set_active (false);
+               }
+               ignore_state_request = false;
+               break;
+       case Latch:
+               auto_dropdown.set_text (_("Latch"));
+               ignore_state_request = true;
+               if (auto_off_item) {
                        auto_off_item->set_active (false);
                        auto_play_item->set_active (false);
+               }
+               if (auto_touch_item) {
+                       auto_latch_item->set_active (true);
+                       auto_touch_item->set_active (false);
                        auto_write_item->set_active (false);
-                       ignore_state_request = false;
                }
+               ignore_state_request = false;
                break;
        default:
                auto_dropdown.set_text (_("???"));
@@ -473,7 +522,9 @@ AutomationTimeAxisView::clear_clicked ()
        } else if (_view) {
                _view->clear ();
        }
-       set_automation_state ((AutoState) ARDOUR::Off);
+       if (!EventTypeMap::instance().type_is_midi (_control->parameter().type())) {
+               set_automation_state ((AutoState) ARDOUR::Off);
+       }
        _editor.commit_reversible_command ();
        _session->set_dirty ();
 }
@@ -503,10 +554,7 @@ AutomationTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
                first_call_to_set_height = false;
 
                if (h >= preset_height (HeightNormal)) {
-                       if (!(_parameter.type() >= MidiCCAutomation &&
-                             _parameter.type() <= MidiChannelPressureAutomation)) {
-                               auto_dropdown.show();
-                       }
+                       auto_dropdown.show();
                        name_label.show();
                        hide_button.show();
 
@@ -544,13 +592,23 @@ AutomationTimeAxisView::hide_clicked ()
 {
        hide_button.set_sensitive(false);
        set_marked_for_display (false);
-       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(parent);
-       if (rtv) {
-               rtv->request_redraw ();
+       StripableTimeAxisView* stv = dynamic_cast<StripableTimeAxisView*>(parent);
+       if (stv) {
+               stv->request_redraw ();
        }
        hide_button.set_sensitive(true);
 }
 
+string
+AutomationTimeAxisView::automation_state_off_string () const
+{
+       if (_parameter.type() >= MidiCCAutomation && _parameter.type() <= MidiChannelPressureAutomation) {
+               return S_("Automation|Off");
+       }
+
+       return S_("Automation|Manual");
+}
+
 void
 AutomationTimeAxisView::build_display_menu ()
 {
@@ -575,7 +633,7 @@ AutomationTimeAxisView::build_display_menu ()
        auto_state_menu->set_name ("ArdourContextMenu");
        MenuList& as_items = auto_state_menu->items();
 
-       as_items.push_back (CheckMenuElem (S_("Automation|Manual"), sigc::bind (
+       as_items.push_back (CheckMenuElem (automation_state_off_string(), sigc::bind (
                        sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
                        (AutoState) ARDOUR::Off)));
        auto_off_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
@@ -585,21 +643,26 @@ AutomationTimeAxisView::build_display_menu ()
                        (AutoState) Play)));
        auto_play_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
 
-       as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
-                       sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
-                       (AutoState) Write)));
-       auto_write_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
+       if (!(_parameter.type() >= MidiCCAutomation &&
+             _parameter.type() <= MidiChannelPressureAutomation)) {
+               as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
+                                                          sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
+                                                          (AutoState) Write)));
+               auto_write_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
 
-       as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
-                       sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
+               as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
+                                                          sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
                        (AutoState) Touch)));
-       auto_touch_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
+               auto_touch_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
 
-       if (!(_parameter.type() >= MidiCCAutomation &&
-             _parameter.type() <= MidiChannelPressureAutomation)) {
-               items.push_back (MenuElem (_("State"), *auto_state_menu));
+               as_items.push_back (CheckMenuElem (_("Latch"), sigc::bind (
+                                               sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
+                                               (AutoState) Latch)));
+               auto_latch_item = dynamic_cast<Gtk::CheckMenuItem*>(&as_items.back());
        }
 
+       items.push_back (MenuElem (_("State"), *auto_state_menu));
+
        /* mode menu */
 
        /* current interpolation state */
@@ -626,7 +689,7 @@ AutomationTimeAxisView::build_display_menu ()
                items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
 
        } else {
-#ifdef XXX_NEW_INTERPOLATON__BREAK_SESSION_FORMAT_XXX
+
                Menu* auto_mode_menu = manage (new Menu);
                auto_mode_menu->set_name ("ArdourContextMenu");
                MenuList& am_items = auto_mode_menu->items();
@@ -666,7 +729,6 @@ AutomationTimeAxisView::build_display_menu ()
                        delete auto_mode_menu;
                        auto_mode_menu = 0;
                }
-#endif
        }
 
        /* make sure the automation menu state is correct */
@@ -676,7 +738,7 @@ AutomationTimeAxisView::build_display_menu ()
 }
 
 void
-AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t frame, double y, bool with_guard_points)
+AutomationTimeAxisView::add_automation_event (GdkEvent* event, samplepos_t sample, double y, bool with_guard_points)
 {
        if (!_line) {
                return;
@@ -691,14 +753,14 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t frame,
                return;
        }
 
-       MusicFrame when (frame, 0);
+       MusicSample when (sample, 0);
        _editor.snap_to_with_modifier (when, event);
 
        if (UIConfiguration::instance().get_new_automation_points_on_lane()) {
                if (_control->list()->size () == 0) {
                        y = _control->get_value ();
                } else {
-                       y = _control->list()->eval (when.frame);
+                       y = _control->list()->eval (when.sample);
                }
        } else {
                double x = 0;
@@ -712,12 +774,12 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t frame,
        XMLNode& before = list->get_state();
        std::list<Selectable*> results;
 
-       if (list->editor_add (when.frame, y, with_guard_points)) {
+       if (list->editor_add (when.sample, y, with_guard_points)) {
                XMLNode& after = list->get_state();
                _editor.begin_reversible_command (_("add automation event"));
                _session->add_command (new MementoCommand<ARDOUR::AutomationList> (*list.get (), &before, &after));
 
-               _line->get_selectables (when.frame, when.frame, 0.0, 1.0, results);
+               _line->get_selectables (when.sample, when.sample, 0.0, 1.0, results);
                _editor.get_selection ().set (results);
 
                _editor.commit_reversible_command ();
@@ -726,7 +788,7 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t frame,
 }
 
 bool
-AutomationTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t divisions)
+AutomationTimeAxisView::paste (samplepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t divisions)
 {
        if (_line) {
                return paste_one (pos, ctx.count, ctx.times, selection, ctx.counts, ctx.greedy);
@@ -747,7 +809,7 @@ AutomationTimeAxisView::paste (framepos_t pos, const Selection& selection, Paste
 }
 
 bool
-AutomationTimeAxisView::paste_one (framepos_t pos, unsigned paste_count, float times, const Selection& selection, ItemCounts& counts, bool greedy)
+AutomationTimeAxisView::paste_one (samplepos_t pos, unsigned paste_count, float times, const Selection& selection, ItemCounts& counts, bool greedy)
 {
        boost::shared_ptr<AutomationList> alist(_line->the_list());
 
@@ -774,7 +836,7 @@ AutomationTimeAxisView::paste_one (framepos_t pos, unsigned paste_count, float t
 
        if (parameter_is_midi (src_type)) {
                // convert length to samples (incl tempo-ramps)
-               len = DoubleBeatsFramesConverter (_session->tempo_map(), pos).to (len * paste_count);
+               len = DoubleBeatsSamplesConverter (_session->tempo_map(), pos).to (len * paste_count);
                pos += _editor.get_paste_offset (pos, paste_count > 0 ? 1 : 0, len);
        } else {
                pos += _editor.get_paste_offset (pos, paste_count, len);
@@ -784,14 +846,14 @@ AutomationTimeAxisView::paste_one (framepos_t pos, unsigned paste_count, float t
        double const model_pos = _line->time_converter().from (pos - _line->time_converter().origin_b ());
 
        XMLNode &before = alist->get_state();
-       alist->paste (**p, model_pos, DoubleBeatsFramesConverter (_session->tempo_map(), pos));
+       alist->paste (**p, model_pos, DoubleBeatsSamplesConverter (_session->tempo_map(), pos));
        _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
 
        return true;
 }
 
 void
-AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool /*within*/)
+AutomationTimeAxisView::get_selectables (samplepos_t start, samplepos_t end, double top, double bot, list<Selectable*>& results, bool /*within*/)
 {
        if (!_line && !_view) {
                return;
@@ -885,6 +947,19 @@ AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
        line->add_visibility (AutomationLine::Line);
 }
 
+bool
+AutomationTimeAxisView::propagate_time_selection () const
+{
+       /* MIDI automation is part of the MIDI region. It is always
+        * implicily selected with the parent, regardless of TAV selection
+        */
+       if (_parameter.type() >= MidiCCAutomation &&
+           _parameter.type() <= MidiChannelPressureAutomation) {
+               return true;
+       }
+       return false;
+}
+
 void
 AutomationTimeAxisView::entered()
 {
@@ -934,35 +1009,6 @@ AutomationTimeAxisView::set_state (const XMLNode&, int /*version*/)
        return 0;
 }
 
-void
-AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr<Automatable>& automatable, set<Evoral::Parameter>& visible)
-{
-       /* this keeps "knowledge" of how we store visibility information
-          in XML private to this class.
-       */
-
-       assert (automatable);
-
-       Automatable::Controls& controls (automatable->controls());
-
-       for (Automatable::Controls::iterator i = controls.begin(); i != controls.end(); ++i) {
-
-               boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
-
-               if (ac && ac->alist()) {
-
-                       const XMLNode* gui_node = ac->extra_xml ("GUI");
-
-                       if (gui_node) {
-                               bool shown;
-                               if (gui_node->get_property ("shown", shown) && shown) {
-                                       visible.insert (i->first);
-                               }
-                       }
-               }
-       }
-}
-
 
 /** @return true if this view has any automation data to display */
 bool
@@ -1075,7 +1121,7 @@ AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& sel
        XMLNode &before = alist->get_state();
 
        /* convert time selection to automation list model coordinates */
-       const Evoral::TimeConverter<double, ARDOUR::framepos_t>& tc = line.time_converter ();
+       const Evoral::TimeConverter<double, ARDOUR::samplepos_t>& 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 ());
 
@@ -1134,4 +1180,3 @@ AutomationTimeAxisView::color () const
 {
        return gdk_color_from_rgb (_stripable->presentation_info().color());
 }
-