first vaguely working version using PresentationInfo
[ardour.git] / gtk2_ardour / editor.cc
index 0afc797dc1e7df317a313359e516bb0d0959ad2b..3dbdd8a9925913cbfd3f09022ae688db71829bdc 100644 (file)
@@ -65,6 +65,7 @@
 #include "gtkmm2ext/choice.h"
 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
 
+#include "ardour/analysis_graph.h"
 #include "ardour/audio_track.h"
 #include "ardour/audioengine.h"
 #include "ardour/audioregion.h"
 #include "editor_routes.h"
 #include "editor_snapshots.h"
 #include "editor_summary.h"
+#include "export_report.h"
 #include "global_port_matrix.h"
 #include "gui_object.h"
 #include "gui_thread.h"
 #include "keyboard.h"
+#include "keyeditor.h"
 #include "luainstance.h"
 #include "marker.h"
 #include "midi_region_view.h"
 #include "rgb_macros.h"
 #include "rhythm_ferret.h"
 #include "selection.h"
+#include "simple_progress_dialog.h"
 #include "sfdb_ui.h"
 #include "tempo_lines.h"
 #include "time_axis_view.h"
@@ -208,8 +212,8 @@ static const gchar *_zoom_focus_strings[] = {
        N_("Right"),
        N_("Center"),
        N_("Playhead"),
-       N_("Mouse"),
-       N_("Edit point"),
+       N_("Mouse"),
+       N_("Edit point"),
        0
 };
 
@@ -387,7 +391,8 @@ Editor::Editor ()
        , tempo_lines (0)
        , global_rect_group (0)
        , time_line_group (0)
-       , tempo_or_meter_marker_menu (0)
+       , tempo_marker_menu (0)
+       , meter_marker_menu (0)
        , marker_menu (0)
        , range_marker_menu (0)
        , transport_marker_menu (0)
@@ -432,6 +437,7 @@ Editor::Editor ()
        , show_gain_after_trim (false)
        , selection_op_cmd_depth (0)
        , selection_op_history_it (0)
+       , no_save_instant (false)
        , current_timefx (0)
        , current_mixer_strip (0)
        , show_editor_mixer_when_tracks_arrive (false)
@@ -503,6 +509,7 @@ Editor::Editor ()
 
        TimeAxisView::setup_sizes ();
        ArdourMarker::setup_sizes (timebar_height);
+       TempoCurve::setup_sizes (timebar_height);
 
        bbt_label.set_name ("EditorRulerLabel");
        bbt_label.set_size_request (-1, (int)timebar_height);
@@ -945,7 +952,7 @@ Editor::set_entered_track (TimeAxisView* tav)
 void
 Editor::instant_save ()
 {
-       if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
+       if (!constructed || !ARDOUR_UI::instance()->session_loaded || no_save_instant) {
                return;
        }
 
@@ -993,7 +1000,7 @@ Editor::control_unselect ()
 }
 
 void
-Editor::control_select (uint32_t rid, Selection::Operation op)
+Editor::control_select (uint16_t rid, Selection::Operation op)
 {
        /* handles the (static) signal from the ControlProtocol class that
         * requests setting the selected track to a given RID
@@ -1003,7 +1010,7 @@ Editor::control_select (uint32_t rid, Selection::Operation op)
                return;
        }
 
-       boost::shared_ptr<Route> r = _session->route_by_remote_id (rid);
+       boost::shared_ptr<Route> r = _session->get_remote_nth_route (rid);
 
        if (!r) {
                return;
@@ -1175,7 +1182,7 @@ Editor::generic_event_handler (GdkEvent* ev)
                        /* leaving window, so reset focus, thus ending any and
                           all text entry operations.
                        */
-                       reset_focus();
+                       reset_focus (&contents());
                        break;
                }
                break;
@@ -1360,6 +1367,7 @@ Editor::set_session (Session *t)
        _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context());
        _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
        _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context());
+       _session->tempo_map().MetricPositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::marker_position_changed, this), gui_context());
        _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
        _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
        _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context());
@@ -1665,7 +1673,7 @@ Editor::build_track_context_menu ()
 {
        using namespace Menu_Helpers;
 
-       MenuList& edit_items = track_context_menu.items();
+       MenuList& edit_items = track_context_menu.items();
        edit_items.clear();
 
        add_dstream_context_items (edit_items);
@@ -1677,7 +1685,7 @@ Editor::build_track_bus_context_menu ()
 {
        using namespace Menu_Helpers;
 
-       MenuList& edit_items = track_context_menu.items();
+       MenuList& edit_items = track_context_menu.items();
        edit_items.clear();
 
        add_bus_context_items (edit_items);
@@ -1714,7 +1722,103 @@ Editor::build_track_region_context_menu ()
 }
 
 void
-Editor::analyze_region_selection ()
+Editor::loudness_analyze_region_selection ()
+{
+       if (!_session) {
+               return;
+       }
+       Selection& s (PublicEditor::instance ().get_selection ());
+       RegionSelection ars = s.regions;
+       ARDOUR::AnalysisGraph ag (_session);
+       framecnt_t total_work = 0;
+
+       for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
+               AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
+               if (!arv) {
+                       continue;
+               }
+               if (!boost::dynamic_pointer_cast<AudioRegion> (arv->region ())) {
+                       continue;
+               }
+               assert (dynamic_cast<RouteTimeAxisView *> (&arv->get_time_axis_view ()));
+               total_work += arv->region ()->length ();
+       }
+
+       SimpleProgressDialog spd (_("Region Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
+       ScopedConnection c;
+       ag.set_total_frames (total_work);
+       ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
+       spd.show();
+
+       for (RegionSelection::iterator j = ars.begin (); j != ars.end (); ++j) {
+               AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*j);
+               if (!arv) {
+                       continue;
+               }
+               boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (arv->region ());
+               if (!ar) {
+                       continue;
+               }
+               ag.analyze_region (ar);
+       }
+       spd.hide();
+       if (!ag.canceled ()) {
+               ExportReport er (_("Audio Report/Analysis"), ag.results ());
+               er.run();
+       }
+}
+
+void
+Editor::loudness_analyze_range_selection ()
+{
+       if (!_session) {
+               return;
+       }
+       Selection& s (PublicEditor::instance ().get_selection ());
+       TimeSelection ts = s.time;
+       ARDOUR::AnalysisGraph ag (_session);
+       framecnt_t total_work = 0;
+
+       for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
+               boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
+               if (!pl) {
+                       continue;
+               }
+               RouteUI *rui = dynamic_cast<RouteUI *> (*i);
+               if (!pl || !rui) {
+                       continue;
+               }
+               for (std::list<AudioRange>::iterator j = ts.begin (); j != ts.end (); ++j) {
+                       total_work += j->length ();
+               }
+       }
+
+       SimpleProgressDialog spd (_("Range Loudness Analysis"), sigc::mem_fun (ag, &AnalysisGraph::cancel));
+       ScopedConnection c;
+       ag.set_total_frames (total_work);
+       ag.Progress.connect_same_thread (c, boost::bind (&SimpleProgressDialog::update_progress, &spd, _1, _2));
+       spd.show();
+
+       for (TrackSelection::iterator i = s.tracks.begin (); i != s.tracks.end (); ++i) {
+               boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> ((*i)->playlist ());
+               if (!pl) {
+                       continue;
+               }
+               RouteUI *rui = dynamic_cast<RouteUI *> (*i);
+               if (!pl || !rui) {
+                       continue;
+               }
+               ag.analyze_range (rui->route (), pl, ts);
+       }
+       spd.hide();
+       if (!ag.canceled ()) {
+               ExportReport er (_("Audio Report/Analysis"), ag.results ());
+               er.run();
+       }
+}
+
+void
+Editor::spectral_analyze_region_selection ()
 {
        if (analysis_window == 0) {
                analysis_window = new AnalysisWindow();
@@ -1732,7 +1836,7 @@ Editor::analyze_region_selection ()
 }
 
 void
-Editor::analyze_range_selection()
+Editor::spectral_analyze_range_selection()
 {
        if (analysis_window == 0) {
                analysis_window = new AnalysisWindow();
@@ -1826,7 +1930,8 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
        edit_items.push_back (MenuElem (_("Zoom to Range"), sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_selection), false)));
 
        edit_items.push_back (SeparatorElem());
-       edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
+       edit_items.push_back (MenuElem (_("Loudness Analysis"), sigc::mem_fun(*this, &Editor::loudness_analyze_range_selection)));
+       edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::spectral_analyze_range_selection)));
 
        edit_items.push_back (SeparatorElem());
 
@@ -2033,6 +2138,37 @@ Editor::snap_type() const
        return _snap_type;
 }
 
+bool
+Editor::snap_musical() const
+{
+       switch (_snap_type) {
+       case SnapToBeatDiv128:
+       case SnapToBeatDiv64:
+       case SnapToBeatDiv32:
+       case SnapToBeatDiv28:
+       case SnapToBeatDiv24:
+       case SnapToBeatDiv20:
+       case SnapToBeatDiv16:
+       case SnapToBeatDiv14:
+       case SnapToBeatDiv12:
+       case SnapToBeatDiv10:
+       case SnapToBeatDiv8:
+       case SnapToBeatDiv7:
+       case SnapToBeatDiv6:
+       case SnapToBeatDiv5:
+       case SnapToBeatDiv4:
+       case SnapToBeatDiv3:
+       case SnapToBeatDiv2:
+       case SnapToBeat:
+       case SnapToBar:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
 SnapMode
 Editor::snap_mode() const
 {
@@ -2083,14 +2219,10 @@ Editor::set_snap_to (SnapType st)
        case SnapToBeatDiv4:
        case SnapToBeatDiv3:
        case SnapToBeatDiv2: {
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin;
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end;
-
-               compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(),
-                                           current_bbt_points_begin, current_bbt_points_end);
-               compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples(),
-                                        current_bbt_points_begin, current_bbt_points_end);
-               update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end);
+               std::vector<TempoMap::BBTPoint> grid;
+               compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples());
+               compute_bbt_ruler_scale (grid, leftmost_frame, leftmost_frame + current_page_samples());
+               update_tempo_based_rulers (grid);
                break;
        }
 
@@ -2186,8 +2318,9 @@ Editor::set_edit_point_preference (EditPoint ep, bool force)
 int
 Editor::set_state (const XMLNode& node, int version)
 {
-       const XMLProperty* prop;
+       XMLProperty const * prop;
        set_id (node);
+       PBD::Unwinder<bool> nsi (no_save_instant, true);
 
        Tabbable::set_state (node, version);
 
@@ -2226,10 +2359,16 @@ Editor::set_state (const XMLNode& node, int version)
 
        if ((prop = node.property ("snap-to"))) {
                snap_type_selection_done ((SnapType) string_2_enum (prop->value(), _snap_type));
+               set_snap_to ((SnapType) string_2_enum (prop->value(), _snap_type));
        }
 
        if ((prop = node.property ("snap-mode"))) {
                snap_mode_selection_done((SnapMode) string_2_enum (prop->value(), _snap_mode));
+               /* set text of Dropdown. in case _snap_mode == SnapOff (default)
+                * snap_mode_selection_done() will only mark an already active item as active
+                * which does not trigger set_text().
+                */
+               set_snap_mode ((SnapMode) string_2_enum (prop->value(), _snap_mode));
        }
 
        if ((prop = node.property ("internal-snap-to"))) {
@@ -2422,10 +2561,10 @@ Editor::get_state ()
 
        node->add_child_nocopy (Tabbable::get_state());
 
-       snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
+       snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (edit_pane, false));
        node->add_property("edit-horizontal-pane-pos", string(buf));
        node->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
-       snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
+       snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (editor_summary_pane, true));
        node->add_property("edit-vertical-pane-pos", string(buf));
 
        maybe_add_mixer_strip_width (*node);
@@ -3704,7 +3843,7 @@ bool
 Editor::edit_controls_button_release (GdkEventButton* ev)
 {
        if (Keyboard::is_context_menu_event (ev)) {
-               ARDOUR_UI::instance()->add_route (current_toplevel());
+               ARDOUR_UI::instance()->add_route ();
        } else if (ev->button == 1) {
                selection->clear_tracks ();
        }
@@ -3769,12 +3908,13 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
 {
        /* recover or initialize pane positions. do this here rather than earlier because
           we don't want the positions to change the child allocations, which they seem to do.
+
+          See comments in mixer_ui.cc about how this works.
         */
 
-       int pos;
+       float pos;
        XMLProperty* prop;
-       char buf[32];
-       XMLNode* node = ARDOUR_UI::instance()->editor_settings();
+       XMLNode* geometry = ARDOUR_UI::instance()->editor_settings();
 
        enum Pane {
                Horizontal = 0x1,
@@ -3783,8 +3923,6 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
 
        static Pane done;
 
-       XMLNode* geometry = find_named_node (*node, "geometry");
-
        if (which == static_cast<Paned*> (&edit_pane)) {
 
                if (done & Horizontal) {
@@ -3798,17 +3936,23 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
                if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
                        /* initial allocation is 90% to canvas, 10% to notebook */
                        pos = (int) floor (alloc.get_width() * 0.90f);
-                       snprintf (buf, sizeof(buf), "%d", pos);
                } else {
-                       pos = atoi (prop->value());
+                       pos = atof (prop->value());
                }
 
-               if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
-                       edit_pane.set_position (pos);
+               if (pos > 1.0f) {
+                       /* older versions of Ardour stored absolute position */
+                       if (alloc.get_width() > pos) {
+                               edit_pane.set_position (pos);
+                               done = (Pane) (done | Horizontal);
+                       }
+               } else {
+                       if (alloc.get_width() > 1.0/pos) {
+                               paned_set_position_as_fraction (edit_pane, pos, false);
+                               done = (Pane) (done | Horizontal);
+                       }
                }
 
-               done = (Pane) (done | Horizontal);
-
        } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
 
                if (done & Vertical) {
@@ -3818,17 +3962,23 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
                if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
                        /* initial allocation is 90% to canvas, 10% to summary */
                        pos = (int) floor (alloc.get_height() * 0.90f);
-                       snprintf (buf, sizeof(buf), "%d", pos);
                } else {
 
-                       pos = atoi (prop->value());
+                       pos = atof (prop->value());
                }
 
-               if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
-                       editor_summary_pane.set_position (pos);
+               if (pos > 1.0f) {
+                       /* older versions of Ardour stored absolute position */
+                       if (alloc.get_height() > pos) {
+                               editor_summary_pane.set_position (pos);
+                               done = (Pane) (done | Vertical);
+                       }
+               } else {
+                       if (alloc.get_height() > 1.0/pos) {
+                               paned_set_position_as_fraction (editor_summary_pane, pos, true);
+                               done = (Pane) (done | Vertical);
+                       }
                }
-
-               done = (Pane) (done | Vertical);
        }
 }
 
@@ -3843,11 +3993,9 @@ Editor::set_show_measures (bool yn)
                                tempo_lines->show();
                        }
 
-                       ARDOUR::TempoMap::BBTPointList::const_iterator begin;
-                       ARDOUR::TempoMap::BBTPointList::const_iterator end;
-
-                       compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(), begin, end);
-                       draw_measures (begin, end);
+                       std::vector<TempoMap::BBTPoint> grid;
+                       compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples());
+                       draw_measures (grid);
                }
 
                instant_save ();
@@ -3968,7 +4116,7 @@ Editor::get_grid_type_as_beats (bool& success, framepos_t position)
                return Evoral::Beats(1.0);
        case SnapToBar:
                if (_session) {
-                       return Evoral::Beats(_session->tempo_map().meter_at (position).divisions_per_bar());
+                       return Evoral::Beats(_session->tempo_map().meter_at_frame (position).divisions_per_bar());
                }
                break;
        default:
@@ -4473,14 +4621,10 @@ Editor::visual_changer (const VisualChange& vc)
 
                compute_fixed_ruler_scale ();
 
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin;
-               ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end;
-
-               compute_current_bbt_points (vc.time_origin, pending_visual_change.time_origin + current_page_samples(),
-                                           current_bbt_points_begin, current_bbt_points_end);
-               compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples(),
-                                        current_bbt_points_begin, current_bbt_points_end);
-               update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end);
+               std::vector<TempoMap::BBTPoint> grid;
+               compute_current_bbt_points (grid, vc.time_origin, pending_visual_change.time_origin + current_page_samples());
+               compute_bbt_ruler_scale (grid, vc.time_origin, pending_visual_change.time_origin + current_page_samples());
+               update_tempo_based_rulers (grid);
 
                update_video_timeline();
        }
@@ -4798,7 +4942,7 @@ Editor::get_regions_from_selection_and_mouse (framepos_t pos)
  */
 
 RegionSelection
-Editor::get_regions_from_selection_and_entered ()
+Editor::get_regions_from_selection_and_entered () const
 {
        RegionSelection regions = selection->regions;
 
@@ -5625,12 +5769,6 @@ Editor::session_going_away ()
        SessionHandlePtr::session_going_away ();
 }
 
-void
-Editor::manage_action_scripts ()
-{
-       ARDOUR_UI::instance()->lua_script_manager();
-}
-
 void
 Editor::trigger_script (int i)
 {
@@ -5645,9 +5783,14 @@ Editor::set_script_action_name (int i, const std::string& n)
        assert (act);
        if (n.empty ()) {
                act->set_label (string_compose (_("Unset #%1"), i + 1));
+               act->set_tooltip (_("no action bound"));
+               act->set_sensitive (false);
        } else {
                act->set_label (n);
+               act->set_tooltip (n);
+               act->set_sensitive (true);
        }
+       KeyEditor::UpdateBindings ();
 }
 
 void