diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 78af5dad9c..a253b180b4 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -2251,6 +2251,9 @@ ARDOUR_UI::save_state_canfail (string name, bool switch_to_it) if ((ret = _session->save_state (name, false, switch_to_it)) != 0) { return ret; } + + std::string rus_path = Glib::build_filename (_session->session_directory().root_path(), "rus.xml"); + region_ui_settings_manager.save (rus_path); } save_ardour_state (); /* XXX cannot fail? yeah, right ... */ diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index b36827d49d..362b666426 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -89,6 +89,7 @@ #include "ardour_window.h" #include "editing.h" #include "enums.h" +#include "region_ui_settings.h" #include "shuttle_control.h" #include "startup_fsm.h" #include "transport_control.h" @@ -419,6 +420,8 @@ public: void gui_idle_handler (); + RegionUISettingsManager region_ui_settings_manager; + protected: friend class PublicEditor; diff --git a/gtk2_ardour/ardour_ui_dependents.cc b/gtk2_ardour/ardour_ui_dependents.cc index 7479d4414d..979d4c7110 100644 --- a/gtk2_ardour/ardour_ui_dependents.cc +++ b/gtk2_ardour/ardour_ui_dependents.cc @@ -88,7 +88,6 @@ ARDOUR_UI::we_have_dependents () mixer->monitor_section().use_others_actions (); StepEntry::setup_actions_and_bindings (); - ClipEditorBox::init (); RegionEditor::setup_actions_and_bindings (); setup_action_tooltips (); diff --git a/gtk2_ardour/ardour_ui_engine.cc b/gtk2_ardour/ardour_ui_engine.cc index 40d8b35ba9..cf653f815d 100644 --- a/gtk2_ardour/ardour_ui_engine.cc +++ b/gtk2_ardour/ardour_ui_engine.cc @@ -80,6 +80,7 @@ ARDOUR_UI::audioengine_became_silent () pay_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://ardour.org/download"))); subscribe_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://ardour.org/subscribe"))); + msg.get_vbox()->pack_start (pay_label); msg.get_vbox()->pack_start (pay_button_box); msg.get_vbox()->pack_start (subscribe_label); diff --git a/gtk2_ardour/ardour_ui_session.cc b/gtk2_ardour/ardour_ui_session.cc index 81d3d97e74..0092ebd22d 100644 --- a/gtk2_ardour/ardour_ui_session.cc +++ b/gtk2_ardour/ardour_ui_session.cc @@ -599,6 +599,15 @@ ARDOUR_UI::load_session_stage_two (const std::string& path, const std::string& s msg.hide (); } + { + if (new_session) { + std::string rus_path = Glib::build_filename (new_session->session_directory().root_path(), "rus.xml"); + region_ui_settings_manager.load (rus_path); + } else { + region_ui_settings_manager.clear (); + } + } + /* Now the session been created, add the transport controls */ new_session->add_controllable(roll_controllable); @@ -611,6 +620,7 @@ ARDOUR_UI::load_session_stage_two (const std::string& path, const std::string& s set_session (new_session); + if (_session) { _session->set_clean (); } @@ -620,6 +630,7 @@ ARDOUR_UI::load_session_stage_two (const std::string& path, const std::string& s flush_pending (10); } + retval = 0; if (!mix_template.empty ()) { diff --git a/gtk2_ardour/audio_clip_editor.cc b/gtk2_ardour/audio_clip_editor.cc index ce6c4217db..a9ade693b9 100644 --- a/gtk2_ardour/audio_clip_editor.cc +++ b/gtk2_ardour/audio_clip_editor.cc @@ -41,12 +41,16 @@ #include "widgets/ardour_button.h" #include "widgets/ardour_icon.h" +#include "ardour_ui.h" #include "audio_clip_editor.h" #include "audio_clock.h" #include "editor_automation_line.h" +#include "editor_cursors.h" +#include "editor_drag.h" #include "control_point.h" #include "editor.h" #include "region_view.h" +#include "verbose_cursor.h" #include "ui_config.h" #include "pbd/i18n.h" @@ -60,33 +64,11 @@ using namespace ArdourWidgets; using std::max; using std::min; -Glib::RefPtr ClipEditorBox::clip_editor_actions; - -void -ClipEditorBox::init () -{ - Bindings* bindings = Bindings::get_bindings (X_("Clip Editing")); - - register_clip_editor_actions (bindings); -} - -void -ClipEditorBox::register_clip_editor_actions (Bindings* clip_editor_bindings) -{ - clip_editor_actions = ActionManager::create_action_group (clip_editor_bindings, X_("ClipEditing")); - - /* two versions to allow same action for Delete and Backspace */ - - // ActionManager::register_action (clip_editor_actions, X_("zoom-in"), _("Zoom In"), sigc::mem_fun (*this, &ClipEditorBox::zoom_in)); - // ActionManager::register_action (clip_editor_actions, X_("zoom-in"), _("Zoom In"), sigc::mem_fun (*this, &ClipEditorBox::zoom_out)); -} - void AudioClipEditor::ClipBBTMetric::get_marks (std::vector& marks, int64_t lower, int64_t upper, int maxchars) const { TriggerPtr trigger (tref.trigger()); if (!trigger) { - std::cerr << "No trigger\n"; return; } @@ -121,46 +103,153 @@ AudioClipEditor::ClipBBTMetric::get_marks (std::vectorname = "audio clip editor frame"; - frame->set_fill (false); - frame->Event.connect (sigc::mem_fun (*this, &AudioClipEditor::event_handler)); + load_bindings (); + register_actions (); - /* Scroll bar does not scroll and it outside the frame */ + build_grid_type_menu (); +} - scroll_bar_trough = new ArdourCanvas::Rectangle (root ()); - scroll_bar_handle = new ArdourCanvas::Rectangle (scroll_bar_trough); - scroll_bar_handle->set_outline (false); - scroll_bar_handle->set_corner_radius (5.); - scroll_bar_handle->Event.connect (sigc::mem_fun (*this, &AudioClipEditor::scroll_event_handler)); - scroll_bar_handle->disable_scroll_translation (); +void +AudioClipEditor::load_shared_bindings () +{ + /* Full shared binding loading must have preceded this in some other EditingContext */ + assert (!need_shared_actions); - /* A scrolling container for our waves and lines etc. */ + Bindings* b = Bindings::get_bindings (X_("Editing")); - waves_container = new ArdourCanvas::ScrollGroup (frame, ScrollGroup::ScrollsHorizontally); - add_scroller (*waves_container); + /* Copy each shared bindings but give them a new name, which will make them refer to actions + * named after this EditingContext (ie. unique to this EC) + */ - ruler_container = new ArdourCanvas::Container (waves_container); - ruler = new ArdourCanvas::Ruler (ruler_container, 0); - ruler->name = "Clip Editor"; - ruler->set_font_description (UIConfiguration::instance ().get_SmallerFont ()); - ruler->set_fill_color (UIConfiguration::instance().color (X_("theme:bg1"))); - ruler->set_outline_color (UIConfiguration::instance().color (X_("theme:contrasting less"))); + Bindings* shared_bindings = new Bindings (editor_name(), *b); + register_common_actions (shared_bindings, editor_name()); + shared_bindings->associate (); + + /* Attach bindings to the canvas for this editing context */ + + bindings.push_back (shared_bindings); +} + +void +AudioClipEditor::pack_inner (Gtk::Box& box) +{ + box.pack_start (snap_box, false, false); + box.pack_start (grid_box, false, false); +} + +void +AudioClipEditor::pack_outer (Gtk::Box& box) +{ + if (with_transport_controls) { + box.pack_start (play_box, false, false); + } + std::cerr << "pack up rec box\n"; + box.pack_start (rec_box, false, false); + box.pack_start (follow_playhead_button, false, false); +} + +void +AudioClipEditor::build_lower_toolbar () +{ + _toolbox.pack_start (*_canvas_hscrollbar, false, false); +} + +void +AudioClipEditor::build_canvas () +{ + _canvas.set_background_color (UIConfiguration::instance().color ("arrange base")); + _canvas.signal_event().connect (sigc::mem_fun (*this, &CueEditor::canvas_pre_event), false); + _canvas.use_nsglview (UIConfiguration::instance().get_nsgl_view_mode () == NSGLHiRes); + + _canvas.PreRender.connect (sigc::mem_fun(*this, &EditingContext::pre_render)); + + /* scroll group for items that should not automatically scroll + * (e.g verbose cursor). It shares the canvas coordinate space. + */ + no_scroll_group = new ArdourCanvas::Container (_canvas.root()); + + h_scroll_group = new ArdourCanvas::ScrollGroup (_canvas.root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally); + CANVAS_DEBUG_NAME (h_scroll_group, "audioclip h scroll"); + _canvas.add_scroller (*h_scroll_group); + + + v_scroll_group = new ArdourCanvas::ScrollGroup (_canvas.root(), ArdourCanvas::ScrollGroup::ScrollsVertically); + CANVAS_DEBUG_NAME (v_scroll_group, "audioclip v scroll"); + _canvas.add_scroller (*v_scroll_group); + + hv_scroll_group = new ArdourCanvas::ScrollGroup (_canvas.root(), + ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically| + ArdourCanvas::ScrollGroup::ScrollsHorizontally)); + CANVAS_DEBUG_NAME (hv_scroll_group, "audioclip hv scroll"); + _canvas.add_scroller (*hv_scroll_group); + + cursor_scroll_group = new ArdourCanvas::ScrollGroup (_canvas.root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally); + CANVAS_DEBUG_NAME (cursor_scroll_group, "audioclip cursor scroll"); + _canvas.add_scroller (*cursor_scroll_group); + + /*a group to hold global rects like punch/loop indicators */ + global_rect_group = new ArdourCanvas::Container (hv_scroll_group); + CANVAS_DEBUG_NAME (global_rect_group, "audioclip global rect group"); + + transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX)); + CANVAS_DEBUG_NAME (transport_loop_range_rect, "audioclip loop rect"); + transport_loop_range_rect->hide(); + + /*a group to hold time (measure) lines */ + time_line_group = new ArdourCanvas::Container (h_scroll_group); + CANVAS_DEBUG_NAME (time_line_group, "audioclip time line group"); + + n_timebars = 0; + minsec_ruler = new ArdourCanvas::Ruler (time_line_group, clip_metric, ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height)); + // minsec_ruler->set_name ("audio clip editor ruler"); + minsec_ruler->set_font_description (UIConfiguration::instance ().get_SmallerFont ()); + minsec_ruler->set_fill_color (UIConfiguration::instance().color (X_("theme:bg1"))); + minsec_ruler->set_outline_color (UIConfiguration::instance().color (X_("theme:contrasting less"))); + n_timebars++; + + minsec_ruler->Event.connect (sigc::mem_fun (*this, &CueEditor::ruler_event)); + + data_group = new ArdourCanvas::Container (hv_scroll_group); + CANVAS_DEBUG_NAME (data_group, "cue data group"); + + data_group->set_position (ArdourCanvas::Duple (_timeline_origin, timebar_height * n_timebars)); + no_scroll_group->set_position (ArdourCanvas::Duple (_timeline_origin, timebar_height * n_timebars)); + cursor_scroll_group->set_position (ArdourCanvas::Duple (_timeline_origin, timebar_height * n_timebars)); + h_scroll_group->set_position (Duple (_timeline_origin, 0.)); + + _verbose_cursor = new VerboseCursor (*this); + + // _playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event, X_("playhead")); + _playhead_cursor = new EditorCursor (*this, X_("playhead")); + _playhead_cursor->set_sensitive (UIConfiguration::instance().get_sensitize_playhead()); + _playhead_cursor->set_color (UIConfiguration::instance().color ("play head")); + _playhead_cursor->canvas_item().raise_to_top(); + h_scroll_group->raise_to_top (); + + _canvas.set_name ("AudioClipCanvas"); + _canvas.add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK); + _canvas.set_can_focus (); + _canvas_viewport.signal_size_allocate().connect (sigc::mem_fun(*this, &AudioClipEditor::canvas_allocate), false); + + _toolbox.pack_start (_canvas_viewport, true, true); /* the lines */ - line_container = new ArdourCanvas::Container (waves_container); + line_container = new ArdourCanvas::Container (data_group); + CANVAS_DEBUG_NAME (line_container, "audio clip line container"); const double line_width = 3.; + double scale = UIConfiguration::instance().get_ui_scale(); start_line = new Line (line_container); start_line->set_outline_width (line_width * scale); @@ -222,41 +311,6 @@ AudioClipEditor::line_event_handler (GdkEvent* ev, ArdourCanvas::Line* l) return false; } -bool -AudioClipEditor::scroll_event_handler (GdkEvent* ev) -{ - switch (ev->type) { - case GDK_BUTTON_PRESS: - current_scroll_drag = new ScrollDrag (*this); - current_scroll_drag->begin (&ev->button); - return true; - - case GDK_BUTTON_RELEASE: - if (current_scroll_drag) { - current_scroll_drag->end (&ev->button); - delete current_scroll_drag; - current_scroll_drag = 0; - return true; - } - break; - - case GDK_MOTION_NOTIFY: - if (current_scroll_drag) { - current_scroll_drag->motion (&ev->motion); - return true; - } - break; - - case GDK_KEY_PRESS: - return key_press (&ev->key); - - default: - break; - } - - return false; -} - bool AudioClipEditor::key_press (GdkEventKey* ev) { @@ -266,27 +320,15 @@ AudioClipEditor::key_press (GdkEventKey* ev) void AudioClipEditor::position_lines () { - if (!audio_region) { + if (!_region) { return; } - start_line->set_x0 (sample_to_pixel (audio_region->start ().samples ())); - start_line->set_x1 (sample_to_pixel (audio_region->start ().samples ())); + start_line->set_x0 (sample_to_pixel (_region->start ().samples ())); + start_line->set_x1 (sample_to_pixel (_region->start ().samples ())); - end_line->set_x0 (sample_to_pixel (audio_region->end ().samples ())); - end_line->set_x1 (sample_to_pixel (audio_region->end ().samples ())); -} - -double -AudioClipEditor::sample_to_pixel (samplepos_t s) -{ - return round (s / _spp); -} - -samplepos_t -AudioClipEditor::pixel_to_sample (double p) -{ - return round (p * _spp); + end_line->set_x0 (sample_to_pixel (_region->end ().samples ())); + end_line->set_x1 (sample_to_pixel (_region->end ().samples ())); } AudioClipEditor::LineDrag::LineDrag (AudioClipEditor& ed, ArdourCanvas::Line& l) @@ -317,78 +359,15 @@ AudioClipEditor::LineDrag::motion (GdkEventMotion* ev) void AudioClipEditor::set_colors () { - set_background_color (UIConfiguration::instance ().color (X_("theme:bg"))); - - frame->set_outline_color (UIConfiguration::instance ().color (X_("neutral:midground"))); + _canvas.set_background_color (UIConfiguration::instance ().color (X_("theme:bg"))); start_line->set_outline_color (UIConfiguration::instance ().color (X_("theme:contrasting clock"))); end_line->set_outline_color (UIConfiguration::instance ().color (X_("theme:contrasting alt"))); loop_line->set_outline_color (UIConfiguration::instance ().color (X_("theme:contrasting selection"))); - scroll_bar_trough->set_fill_color (UIConfiguration::instance ().color (X_("theme:bg"))); - scroll_bar_trough->set_outline_color (UIConfiguration::instance ().color (X_("theme:contrasting less"))); - scroll_bar_handle->set_fill_color (UIConfiguration::instance ().color (X_("theme:contrasting clock"))); - set_waveform_colors (); } -void -AudioClipEditor::scroll_changed () -{ - if (!audio_region) { - return; - } - - const double right_edge = scroll_bar_handle->get ().x0; - const double avail_width = scroll_bar_trough->get ().width () - scroll_bar_handle->get ().width (); - scroll_fraction = right_edge / avail_width; - scroll_fraction = std::min (1., std::max (0., scroll_fraction)); - const samplepos_t s = llrintf (audio_region->source (0)->length ().samples () * scroll_fraction); - - ruler->set_range (s, s + pixel_to_sample (frame->get().width() - 2.)); - - scroll_to (sample_to_pixel (s), 0); - - queue_draw (); -} - -AudioClipEditor::ScrollDrag::ScrollDrag (AudioClipEditor& e) - : editor (e) -{ - e.scroll_bar_handle->grab (); -} - -void -AudioClipEditor::ScrollDrag::begin (GdkEventButton* ev) -{ - last_x = ev->x; -} - -void -AudioClipEditor::ScrollDrag::end (GdkEventButton* ev) -{ - editor.scroll_bar_handle->ungrab (); - editor.scroll_changed (); -} - -void -AudioClipEditor::ScrollDrag::motion (GdkEventMotion* ev) -{ - ArdourCanvas::Rectangle& r (*editor.scroll_bar_handle); - const double xdelta = ev->x - last_x; - ArdourCanvas::Rect n (r.get ()); - const double handle_width = n.width (); - const double avail_width = editor.scroll_bar_trough->get ().width () - handle_width; - - n.x0 = std::max (0., std::min (avail_width, n.x0 + xdelta)); - n.x1 = n.x0 + handle_width; - - r.set (n); - last_x = ev->x; - - editor.scroll_changed (); -} - void AudioClipEditor::drop_waves () { @@ -400,11 +379,39 @@ AudioClipEditor::drop_waves () } void -AudioClipEditor::set_region (std::shared_ptr r, TriggerReference tr) +AudioClipEditor::set_trigger (TriggerReference& tr) { + if (tr == ref) { + return; + } + + CueEditor::set_trigger (tr); + rec_box.show (); + + minsec_ruler->show (); + minsec_ruler->set_range (0, pixel_to_sample (_visible_canvas_width - 2.)); +} + +void +AudioClipEditor::set_region (std::shared_ptr region) +{ + CueEditor::set_region (region); + + if (_visible_pending_region) { + return; + } + drop_waves (); - audio_region = r; + if (!region) { + return; + } + + std::shared_ptr r (std::dynamic_pointer_cast (region)); + + if (!r) { + return; + } /* Ruler has to reflect tempo of the region, so we have to recreate it * every time. Note that we retain ownership of the metric, and that @@ -414,8 +421,8 @@ AudioClipEditor::set_region (std::shared_ptr r, TriggerReference tr */ delete clip_metric; - clip_metric = new ClipBBTMetric (tr); - ruler->set_metric (clip_metric); + clip_metric = new ClipBBTMetric (ref); + minsec_ruler->set_metric (clip_metric); uint32_t n_chans = r->n_channels (); samplecnt_t len; @@ -424,6 +431,7 @@ AudioClipEditor::set_region (std::shared_ptr r, TriggerReference tr for (uint32_t n = 0; n < n_chans; ++n) { std::shared_ptr wr = RegionFactory::get_whole_region_for_source (r->source (n)); + if (!wr) { continue; } @@ -433,8 +441,8 @@ AudioClipEditor::set_region (std::shared_ptr r, TriggerReference tr continue; } - WaveView* wv = new WaveView (waves_container, war); - wv->set_channel (n); + WaveView* wv = new WaveView (data_group, war); + wv->set_channel (0); wv->set_show_zero_line (false); wv->set_clip_level (1.0); wv->lower_to_bottom (); @@ -442,75 +450,56 @@ AudioClipEditor::set_region (std::shared_ptr r, TriggerReference tr waves.push_back (wv); } - TriggerPtr trigger (tr.trigger()); - std::shared_ptr at = std::dynamic_pointer_cast (trigger); - if (at) { - if (at->segment_tempo() == 0.) { - /* tempo unknown, hide ruler */ - ruler->hide (); - } else { - ruler->show (); - ruler->set_range (0, pixel_to_sample (frame->get().width() - 2.)); - } - } else { - ruler->hide (); - } set_spp_from_length (len); set_wave_heights (); set_waveform_colors (); line_container->show (); line_container->raise_to_top (); + + set_session (&r->session ()); + state_connection.disconnect (); + + PBD::PropertyChange interesting_stuff; + region_changed (interesting_stuff); + + region->PropertyChanged.connect (state_connection, invalidator (*this), std::bind (&AudioClipEditor::region_changed, this, _1), gui_context ()); + + maybe_set_from_rsu (); } void -AudioClipEditor::on_size_allocate (Gtk::Allocation& alloc) +AudioClipEditor::canvas_allocate (Gtk::Allocation& alloc) { - GtkCanvas::on_size_allocate (alloc); + _canvas.size_allocate (alloc); - ArdourCanvas::Rect r (1, 1, alloc.get_width () - 2, alloc.get_height () - 2); - frame->set (r); + _visible_canvas_width = alloc.get_width(); + _visible_canvas_height = alloc.get_height(); - const double ruler_height = 25.; - ruler->set (ArdourCanvas::Rect (2, 2, alloc.get_width() - 4, ruler_height)); + /* no track header here, "track width" is the whole canvas */ + _track_canvas_width = _visible_canvas_width; - const double scroll_bar_height = 10.; - const double scroll_bar_width = alloc.get_width () - 2; - const double scroll_bar_handle_left = scroll_bar_width * scroll_fraction; - - scroll_bar_trough->set (ArdourCanvas::Rect (1, alloc.get_height () - scroll_bar_height, scroll_bar_width, alloc.get_height ())); - scroll_bar_handle->set (ArdourCanvas::Rect (scroll_bar_handle_left, scroll_bar_trough->get ().y0 + 1, scroll_bar_handle_left + 30., scroll_bar_trough->get ().y1 - 1)); + minsec_ruler->set (ArdourCanvas::Rect (2, 2, alloc.get_width() - 4, timebar_height)); position_lines (); - start_line->set_y1 (frame->get ().height () - 2. - scroll_bar_height); - end_line->set_y1 (frame->get ().height () - 2. - scroll_bar_height); - loop_line->set_y1 (frame->get ().height () - 2. - scroll_bar_height); + start_line->set_y1 (_visible_canvas_height - 2.); + end_line->set_y1 (_visible_canvas_height - 2.); + loop_line->set_y1 (_visible_canvas_height - 2.); set_wave_heights (); -} -void -AudioClipEditor::set_spp (double samples_per_pixel) -{ - _spp = samples_per_pixel; + catch_pending_show_region (); - clip_metric->units_per_pixel = _spp; - - position_lines (); - - for (auto& wave : waves) { - wave->set_samples_per_pixel (_spp); - } + update_grid (); } void AudioClipEditor::set_spp_from_length (samplecnt_t len) { - double available_width = frame->get ().width (); - double s = floor (len / available_width); - - set_spp (s); + if (_visible_canvas_width) { + set_samples_per_pixel (floor (len / _visible_canvas_width)); + } } void @@ -521,12 +510,12 @@ AudioClipEditor::set_wave_heights () } uint32_t n = 0; - const Distance w = frame->get ().height () - scroll_bar_trough->get ().height () - 2. - ruler->get().height(); + const Distance w = _visible_canvas_height - (n_timebars * timebar_height); Distance ht = w / waves.size (); for (auto& wave : waves) { wave->set_height (ht); - wave->set_y_position (ruler->get ().height () + (n * ht)); + wave->set_y_position ((n_timebars * timebar_height) + (n * ht)); ++n; } } @@ -547,86 +536,146 @@ AudioClipEditor::set_waveform_colors () } } +Gtk::Widget& +AudioClipEditor::contents () +{ + return _contents; +} + +void +AudioClipEditor::region_changed (const PBD::PropertyChange& what_changed) +{ +} + +void +AudioClipEditor::set_samples_per_pixel (samplecnt_t spp) +{ + CueEditor::set_samples_per_pixel (spp); + + clip_metric->units_per_pixel = samples_per_pixel; + + position_lines (); + + for (auto& wave : waves) { + wave->set_samples_per_pixel (samples_per_pixel); + } + + horizontal_adjustment.set_upper (max_zoom_extent().second.samples() / samples_per_pixel); + horizontal_adjustment.set_page_size (current_page_samples()/ samples_per_pixel / 10); + horizontal_adjustment.set_page_increment (current_page_samples()/ samples_per_pixel / 20); + horizontal_adjustment.set_step_increment (current_page_samples() / samples_per_pixel / 100); +} + +samplecnt_t +AudioClipEditor::current_page_samples() const +{ + return (samplecnt_t) _track_canvas_width * samples_per_pixel; +} + bool -AudioClipEditor::event_handler (GdkEvent* ev) +AudioClipEditor::canvas_enter_leave (GdkEventCrossing* ev) { switch (ev->type) { - case GDK_BUTTON_PRESS: - break; - case GDK_ENTER_NOTIFY: - break; - case GDK_LEAVE_NOTIFY: - break; - default: - break; + case GDK_ENTER_NOTIFY: + if (ev->detail != GDK_NOTIFY_INFERIOR) { + _canvas.grab_focus (); + // ActionManager::set_sensitive (_midi_actions, true); + within_track_canvas = true; + } + break; + case GDK_LEAVE_NOTIFY: + if (ev->detail != GDK_NOTIFY_INFERIOR) { + // ActionManager::set_sensitive (_midi_actions, false); + within_track_canvas = false; + ARDOUR_UI::instance()->reset_focus (&_canvas_viewport); + gdk_window_set_cursor (_canvas_viewport.get_window()->gobj(), nullptr); + } + default: + break; } return false; } -AudioClipEditorBox::AudioClipEditorBox () +void +AudioClipEditor::begin_write () { - _header_label.set_text (_("AUDIO Region Trimmer:")); - _header_label.set_alignment (0.0, 0.5); - - zoom_in_button.set_icon (ArdourIcon::ZoomIn); - zoom_out_button.set_icon (ArdourIcon::ZoomOut); - - zoom_in_button.signal_clicked.connect (sigc::mem_fun (*this, &AudioClipEditorBox::zoom_in_click)); - zoom_out_button.signal_clicked.connect (sigc::mem_fun (*this, &AudioClipEditorBox::zoom_out_click)); - - header_box.pack_start (_header_label, false, false); - header_box.pack_start (zoom_in_button, false, false); - header_box.pack_start (zoom_out_button, false, false); - - pack_start (header_box, false, false, 6); - - editor = manage (new AudioClipEditor); - editor->set_size_request (600, 120); - - pack_start (*editor, true, true); - editor->show (); -} - -AudioClipEditorBox::~AudioClipEditorBox () -{ - delete editor; } void -AudioClipEditorBox::zoom_in_click () +AudioClipEditor::end_write () { - editor->set_spp (editor->spp () / 2.); } void -AudioClipEditorBox::zoom_out_click () +AudioClipEditor::show_count_in (std::string const &) { - editor->set_spp (editor->spp () * 2.); } void -AudioClipEditorBox::set_region (std::shared_ptr r, TriggerReference tref) +AudioClipEditor::hide_count_in () { - std::shared_ptr ar = std::dynamic_pointer_cast (r); +} - if (!ar) { - return; +void +AudioClipEditor::maybe_update () +{ + ARDOUR::TriggerPtr playing_trigger; + + if (ref.trigger()) { + + /* Trigger editor */ + + playing_trigger = ref.box()->currently_playing (); + + if (!playing_trigger) { + + if (_drags->active() || !_region || !_track || !_track->triggerbox()) { + return; + } + + if (_track->triggerbox()->record_enabled() == Recording) { + + _playhead_cursor->set_position (data_capture_duration); + } + + } else { + if (playing_trigger->active ()) { + if (playing_trigger->the_region()) { + _playhead_cursor->set_position (playing_trigger->current_pos().samples() + playing_trigger->the_region()->start().samples()); + } + } else { + _playhead_cursor->set_position (0); + } + } +#if 0 + } else if (view->midi_region()) { + + /* Timeline region editor */ + + if (!_session) { + return; + } + + samplepos_t pos = _session->transport_sample(); + samplepos_t spos = view->midi_region()->source_position().samples(); + if (pos < spos) { + _playhead_cursor->set_position (0); + } else { + _playhead_cursor->set_position (pos - spos); + } +#endif + } else { + _playhead_cursor->set_position (0); } - set_session (&r->session ()); - - state_connection.disconnect (); - - _region = r; - editor->set_region (ar, tref); - - PBD::PropertyChange interesting_stuff; - region_changed (interesting_stuff); - - _region->PropertyChanged.connect (state_connection, invalidator (*this), std::bind (&AudioClipEditorBox::region_changed, this, _1), gui_context ()); + if (follow_playhead()) { + reset_x_origin_to_follow_playhead (); + } } void -AudioClipEditorBox::region_changed (const PBD::PropertyChange& what_changed) +AudioClipEditor::unset (bool trigger_too) { + drop_waves (); + CueEditor::unset (trigger_too); } diff --git a/gtk2_ardour/audio_clip_editor.h b/gtk2_ardour/audio_clip_editor.h index 52b126a404..81060fec57 100644 --- a/gtk2_ardour/audio_clip_editor.h +++ b/gtk2_ardour/audio_clip_editor.h @@ -25,6 +25,8 @@ #include #include +#include "pbd/history_owner.h" + #include "ardour/ardour.h" #include "ardour/session_handle.h" #include "ardour/triggerbox.h" @@ -36,7 +38,6 @@ #include "widgets/ardour_button.h" -#include "canvas/canvas.h" #include "canvas/container.h" #include "canvas/line.h" #include "canvas/rectangle.h" @@ -44,6 +45,7 @@ #include "canvas/scroll_group.h" #include "audio_clock.h" +#include "cue_editor.h" namespace ARDOUR { @@ -63,59 +65,76 @@ namespace ArdourWaveView class WaveView; } -class ClipEditorBox : public Gtk::VBox, public ARDOUR::SessionHandlePtr +class AudioClipEditor : public CueEditor { public: - ClipEditorBox () {} - ~ClipEditorBox () {} - - virtual void set_region (std::shared_ptr, ARDOUR::TriggerReference) = 0; - - static void init (); - static void register_clip_editor_actions (Gtkmm2ext::Bindings*); - static Glib::RefPtr clip_editor_actions; -}; - -class ClipEditor -{ -public: - virtual ~ClipEditor () {} - - virtual void zoom_in () = 0; - virtual void zoom_out () = 0; -}; - -class AudioClipEditor : public ArdourCanvas::GtkCanvas -{ -public: - AudioClipEditor (); + AudioClipEditor (std::string const &, bool with_transport = false); ~AudioClipEditor (); - void set_region (std::shared_ptr, ARDOUR::TriggerReference); - void on_size_allocate (Gtk::Allocation&); + void canvas_allocate (Gtk::Allocation&); - double sample_to_pixel (ARDOUR::samplepos_t); - samplepos_t pixel_to_sample (double); + Gtk::Widget& contents (); - void set_spp (double); - double spp () const - { - return _spp; - } + void set_trigger (ARDOUR::TriggerReference&); + void set_region (std::shared_ptr r); + void region_changed (const PBD::PropertyChange& what_changed); bool key_press (GdkEventKey*); -private: - ArdourCanvas::Rectangle* frame; - ArdourCanvas::ScrollGroup* waves_container; - ArdourCanvas::Container* line_container; - ArdourCanvas::Line* start_line; - ArdourCanvas::Line* end_line; - ArdourCanvas::Line* loop_line; - ArdourCanvas::Rectangle* scroll_bar_trough; - ArdourCanvas::Rectangle* scroll_bar_handle; - ArdourCanvas::Container* ruler_container; - ArdourCanvas::Ruler* ruler; + /* EditingContext API. As of July 2025, we do not implement most of + * these + */ + + bool button_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; } + bool button_press_handler_1 (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; } + bool button_press_handler_2 (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; } + bool button_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; } + bool button_press_dispatch (GdkEventButton*) { return true; } + bool button_release_dispatch (GdkEventButton*) { return true; } + bool motion_handler (ArdourCanvas::Item*, GdkEvent*, bool from_autoscroll = false) { return true; } + bool enter_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; } + bool leave_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; } + bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; } + bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) { return true; } + + bool canvas_note_event (GdkEvent* event, ArdourCanvas::Item*) { return true; } + bool canvas_velocity_base_event (GdkEvent* event, ArdourCanvas::Item*) { return true; } + bool canvas_velocity_event (GdkEvent* event, ArdourCanvas::Item*) { return true; } + bool canvas_control_point_event (GdkEvent* event, ArdourCanvas::Item*, ControlPoint*) { return true; } + bool canvas_bg_event (GdkEvent* event, ArdourCanvas::Item*) { return true; } + + samplecnt_t current_page_samples() const; + void set_samples_per_pixel (samplecnt_t); + + Gdk::Cursor* which_track_cursor () const { return nullptr; } + Gdk::Cursor* which_mode_cursor () const { return nullptr; } + Gdk::Cursor* which_trim_cursor (bool left_side) const { return nullptr; } + Gdk::Cursor* which_canvas_cursor (ItemType type) const { return nullptr; } + + Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start, Temporal::RoundMode direction, ARDOUR::SnapPref gpref) const { return start; } + void snap_to_internal (Temporal::timepos_t& first, Temporal::RoundMode direction = Temporal::RoundNearest, ARDOUR::SnapPref gpref = ARDOUR::SnapToAny_Visual, bool ensure_snap = false) const {} + + void select_all_within (Temporal::timepos_t const &, Temporal::timepos_t const &, double, double, std::list const &, ARDOUR::SelectionOperation, bool) {} + void get_per_region_note_selection (std::list > > > >&) const {} + void get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const {} + + void point_selection_changed () {} + void delete_ () {} + void paste (float times, bool from_context_menu) {} + void keyboard_paste () {} + void cut_copy (Editing::CutCopyOp) {} + + void maybe_update (); + + bool idle_data_captured () { return false; } + + private: + ArdourCanvas::Container* line_container; + ArdourCanvas::Line* start_line; + ArdourCanvas::Line* end_line; + ArdourCanvas::Line* loop_line; + ArdourCanvas::Container* ruler_container; + ArdourCanvas::Ruler* minsec_ruler; class ClipBBTMetric : public ArdourCanvas::Ruler::Metric { @@ -135,9 +154,7 @@ private: std::vector waves; double non_wave_height; samplepos_t left_origin; - double _spp; double scroll_fraction; - std::shared_ptr audio_region; void scroll_left (); void scrol_right (); @@ -150,7 +167,6 @@ private: bool event_handler (GdkEvent* ev); bool line_event_handler (GdkEvent* ev, ArdourCanvas::Line*); - bool scroll_event_handler (GdkEvent* ev); void drop_waves (); void set_wave_heights (); void set_spp_from_length (ARDOUR::samplecnt_t); @@ -176,47 +192,21 @@ private: friend class LineDrag; LineDrag* current_line_drag; - class ScrollDrag - { - public: - ScrollDrag (AudioClipEditor&); - - void begin (GdkEventButton*); - void end (GdkEventButton*); - void motion (GdkEventMotion*); - - private: - AudioClipEditor& editor; - double last_x; - }; - - friend class ScrollDrag; - ScrollDrag* current_scroll_drag; -}; - -class AudioClipEditorBox : public ClipEditorBox -{ -public: - AudioClipEditorBox (); - ~AudioClipEditorBox (); - - void set_region (std::shared_ptr, ARDOUR::TriggerReference); - void region_changed (const PBD::PropertyChange& what_changed); - -private: - Gtk::HBox header_box; - ArdourWidgets::ArdourButton zoom_in_button; - ArdourWidgets::ArdourButton zoom_out_button; - Gtk::Label _header_label; - Gtk::Table table; - - AudioClipEditor* editor; - PBD::ScopedConnection state_connection; - std::shared_ptr _region; + void build_canvas (); + void build_lower_toolbar (); + void pack_inner (Gtk::Box&); + void pack_outer (Gtk::Box&); - void zoom_in_click (); - void zoom_out_click (); + bool canvas_enter_leave (GdkEventCrossing* ev); + + void begin_write (); + void end_write (); + + void show_count_in (std::string const &); + void hide_count_in (); + + void unset (bool trigger_too); + void load_shared_bindings (); }; - diff --git a/gtk2_ardour/cue_editor.cc b/gtk2_ardour/cue_editor.cc index ee96e4b5ac..381ce2d6ec 100644 --- a/gtk2_ardour/cue_editor.cc +++ b/gtk2_ardour/cue_editor.cc @@ -1,20 +1,72 @@ +/* + * Copyright (C) 2023 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "ytkmm/scrollbar.h" + +#include "widgets/ardour_icon.h" +#include "widgets/tooltips.h" + +#include "pbd/controllable.h" + +#include "ardour/midi_region.h" +#include "ardour/smf_source.h" +#include "ardour/types.h" + +#include "canvas/canvas.h" + +#include "gtkmm2ext/bindings.h" + +#include "ardour_ui.h" #include "cue_editor.h" #include "editor_drag.h" #include "gui_thread.h" +#include "timers.h" +#include "ui_config.h" #include "pbd/i18n.h" -CueEditor::CueEditor (std::string const & name) - : EditingContext (name) - , HistoryOwner (X_("cue-editor")) -{ - _history.Changed.connect (history_connection, invalidator (*this), std::bind (&CueEditor::history_changed, this), gui_context()); +using namespace ARDOUR; +using namespace ArdourWidgets; +using namespace Temporal; +CueEditor::CueEditor (std::string const & name, bool with_transport) + : EditingContext (name) + , HistoryOwner (name) + , _canvas_viewport (horizontal_adjustment, vertical_adjustment) + , _canvas (*_canvas_viewport.canvas ()) + , with_transport_controls (with_transport) + , length_label (X_("Record:")) + , solo_button (S_("Solo|S")) + , zoom_in_allocate (false) + , timebar_height (15.) + , n_timebars (0) +{ + _canvas_hscrollbar = manage (new Gtk::HScrollbar (horizontal_adjustment)); + _canvas_hscrollbar->show (); + horizontal_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &CueEditor::scrolled)); + + _history.Changed.connect (history_connection, invalidator (*this), std::bind (&CueEditor::history_changed, this), gui_context()); set_zoom_focus (Editing::ZoomFocusLeft); } CueEditor::~CueEditor () { + delete own_bindings; } void @@ -71,22 +123,6 @@ CueEditor::find_marker_for_meter (Temporal::MeterPoint const &) return nullptr; } -void -CueEditor::maybe_autoscroll (bool, bool, bool from_headers) -{ -} - -void -CueEditor::stop_canvas_autoscroll () -{ -} - -bool -CueEditor::autoscroll_active() const -{ - return false; -} - void CueEditor::redisplay_grid (bool immediate_redraw) { @@ -102,6 +138,22 @@ CueEditor::get_nudge_distance (Temporal::timepos_t const & pos, Temporal::timecn void CueEditor::instant_save() { + if (!_region) { + return; + } + + /* derived classes should set other fields first, then call parent */ + + region_ui_settings.follow_playhead = follow_playhead(); + region_ui_settings.samples_per_pixel = samples_per_pixel; + region_ui_settings.grid_type = grid_type (); + + std::pair res (ARDOUR_UI::instance()->region_ui_settings_manager.insert (std::make_pair (_region->id(), region_ui_settings))); + + if (!res.second) { + /* region (ID) already present, set contents */ + res.first->second = region_ui_settings; + } } void @@ -204,13 +256,6 @@ CueEditor::get_canvas_cursor () const return nullptr; } -Editing::MouseMode -CueEditor::current_mouse_mode () const -{ - return Editing::MouseContent; -} - - std::shared_ptr CueEditor::start_local_tempo_map (std::shared_ptr map) { @@ -263,3 +308,1409 @@ CueEditor::_get_preferred_edit_position (Editing::EditIgnoreOption ignore, bool return Temporal::timepos_t (where); } + +void +CueEditor::build_upper_toolbar () +{ + using namespace Gtk::Menu_Helpers; + + Gtk::HBox* mode_box = manage(new Gtk::HBox); + mode_box->set_border_width (2); + mode_box->set_spacing(2); + + Gtk::HBox* mouse_mode_box = manage (new Gtk::HBox); + Gtk::HBox* mouse_mode_hbox = manage (new Gtk::HBox); + Gtk::VBox* mouse_mode_vbox = manage (new Gtk::VBox); + Gtk::Alignment* mouse_mode_align = manage (new Gtk::Alignment); + + Glib::RefPtr mouse_mode_size_group = Gtk::SizeGroup::create (Gtk::SIZE_GROUP_VERTICAL); + mouse_mode_size_group->add_widget (mouse_draw_button); + mouse_mode_size_group->add_widget (mouse_content_button); + + mouse_mode_size_group->add_widget (grid_type_selector); + mouse_mode_size_group->add_widget (draw_length_selector); + mouse_mode_size_group->add_widget (draw_velocity_selector); + mouse_mode_size_group->add_widget (draw_channel_selector); + mouse_mode_size_group->add_widget (snap_mode_button); + + mouse_mode_hbox->set_spacing (2); + mouse_mode_hbox->pack_start (mouse_draw_button, false, false); + mouse_mode_hbox->pack_start (mouse_content_button, false, false); + + mouse_mode_vbox->pack_start (*mouse_mode_hbox); + + mouse_mode_align->add (*mouse_mode_vbox); + mouse_mode_align->set (0.5, 1.0, 0.0, 0.0); + + mouse_mode_box->pack_start (*mouse_mode_align, false, false); + + pack_snap_box (); + pack_draw_box (false); + + Gtk::HBox* _toolbar_inner = manage (new Gtk::HBox); + Gtk::HBox* _toolbar_outer = manage (new Gtk::HBox); + Gtk::HBox* _toolbar_left = manage (new Gtk::HBox); + + _toolbar_inner->pack_start (*mouse_mode_box, false, false); + pack_inner (*_toolbar_inner); + + set_tooltip (full_zoom_button, _("Zoom to full clip")); + set_tooltip (note_mode_button, _("Toggle between drum and regular note drawing")); + + play_button.set_icon (ArdourIcon::TransportPlay); + play_button.set_name ("transport button"); + play_button.show(); + + if (with_transport_controls) { + loop_button.set_icon (ArdourIcon::TransportLoop); + loop_button.set_name ("transport button"); + + solo_button.set_name ("solo button"); + + play_box.set_spacing (8); + play_box.pack_start (play_button, false, false); + play_box.pack_start (loop_button, false, false); + play_box.pack_start (solo_button, false, false); + loop_button.show(); + solo_button.show(); + play_box.set_no_show_all (true); + play_box.show (); + + play_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::play_button_press), false); + solo_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::solo_button_press), false); + loop_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::loop_button_press), false); + } else { + rec_box.pack_start (play_button, false, false); + play_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::bang_button_press), false); + } + + rec_enable_button.set_icon (ArdourIcon::RecButton); + rec_enable_button.set_sensitive (false); + rec_enable_button.signal_button_release_event().connect (sigc::mem_fun (*this, &CueEditor::rec_button_press), false); + rec_enable_button.set_name ("record enable button"); + + length_selector.add_menu_elem (MenuElem (_("Until Stopped"), sigc::bind (sigc::mem_fun (*this, &CueEditor::set_recording_length), Temporal::BBT_Offset ()))); + length_selector.add_menu_elem (MenuElem (_("1 Bar"), sigc::bind (sigc::mem_fun (*this, &CueEditor::set_recording_length), Temporal::BBT_Offset (1, 0, 0)))); + std::vector b ({ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 20, 24, 32 }); + for (auto & n : b) { + length_selector.add_menu_elem (MenuElem (string_compose (_("%1 Bars"), n), sigc::bind (sigc::mem_fun (*this, &CueEditor::set_recording_length), Temporal::BBT_Offset (n, 0, 0)))); + } + length_selector.set_active (_("Until Stopped")); + + rec_box.set_spacing (12); + rec_box.pack_start (rec_enable_button, false, false); + rec_box.pack_start (length_label, false, false); + rec_box.pack_start (length_selector, false, false); + rec_enable_button.show(); + length_label.show (); + length_selector.show (); + rec_box.set_no_show_all (true); + /* rec box not shown */ + + _toolbar_outer->set_border_width (6); + _toolbar_outer->set_spacing (12); + + pack_outer (*_toolbar_outer); + + _toolbar_outer->pack_start (*_toolbar_inner, true, false); + + build_zoom_focus_menu (); + zoom_focus_selector.set_text (zoom_focus_strings[(int)_zoom_focus]); + + _toolbar_left->pack_start (zoom_in_button, false, false); + _toolbar_left->pack_start (zoom_out_button, false, false); + _toolbar_left->pack_start (full_zoom_button, false, false); + _toolbar_left->pack_start (zoom_focus_selector, false, false); + + _toolbar_outer->pack_start (*_toolbar_left, true, false); + _toolbox.pack_start (*_toolbar_outer, false, false); + + _contents.add (_toolbox); + _contents.signal_unmap().connect ([this]() { get_canvas_viewport()->unmap (); }, false); + _contents.signal_map().connect ([this]() { get_canvas_viewport()->map (); }, false); +} + +void +CueEditor::build_zoom_focus_menu () +{ + using namespace Gtk::Menu_Helpers; + using namespace Editing; + + zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft))); + zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight))); + zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter))); + zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse))); + zoom_focus_selector.set_sizing_texts (zoom_focus_strings); +} + +bool +CueEditor::bang_button_press (GdkEventButton* ev) +{ + if (!ref.trigger()) { + return true; + } + + ref.trigger()->bang (); + + return true; +} + +bool +CueEditor::play_button_press (GdkEventButton* ev) +{ + if (_session && _region) { + _session->request_locate (_region->position().samples()); + _session->request_roll (); + } + + return true; +} + +bool +CueEditor::loop_button_press (GdkEventButton* ev) +{ + if (!_region) { + return true; + } + + if (_session->get_play_loop()) { + _session->request_play_loop (false); + } else { + set_loop_range (_region->position(), _region->end(), _("loop region")); + _session->request_play_loop (true); + } + + return true; +} + +bool +CueEditor::solo_button_press (GdkEventButton* ev) +{ + if (!_track) { + return true; + } + + _track->solo_control()->set_value (!_track->solo_control()->get_value(), PBD::Controllable::NoGroup); + + return true; +} + +bool +CueEditor::rec_button_press (GdkEventButton* ev) +{ + if (ev->button != 1) { + return false; + } + + TriggerPtr trigger (ref.trigger()); + + if (!trigger) { + return true; + } + + if (trigger->armed()) { + trigger->disarm (); + } else { + trigger->arm (rec_length); + } + + return true; +} + +void +CueEditor::blink_rec_enable (bool onoff) +{ + if (onoff) { + rec_enable_button.set_active_state (Gtkmm2ext::ExplicitActive); + } else { + rec_enable_button.set_active_state (Gtkmm2ext::Off); + } +} + +void +CueEditor::trigger_arm_change () +{ + if (!ref.trigger()) { + return; + } + + if (!ref.trigger()->armed()) { + end_write (); + } else { + maybe_set_count_in (); + } + + rec_enable_change (); +} + +void +CueEditor::rec_enable_change () +{ + if (!ref.box()) { + return; + } + + rec_blink_connection.disconnect (); + count_in_connection.disconnect (); + + std::cerr << "REC, state " << ref.box()->record_enabled() << std::endl; + + switch (ref.box()->record_enabled()) { + case Recording: + rec_enable_button.set_active_state (Gtkmm2ext::ExplicitActive); + rec_blink_connection.disconnect (); + begin_write (); + break; + case Enabled: + if (!UIConfiguration::instance().get_no_strobe() && ref.trigger()->armed()) { + rec_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &CueEditor::blink_rec_enable)); + } else { + rec_enable_button.set_active_state (Gtkmm2ext::Off); + } + maybe_set_count_in (); + break; + case Disabled: + rec_enable_button.set_active_state (Gtkmm2ext::Off); + break; + } +} + +void +CueEditor::set_recording_length (Temporal::BBT_Offset dur) +{ + rec_length = dur; +} + +void +CueEditor::scrolled () +{ + pending_visual_change.add (VisualChange::TimeOrigin); + pending_visual_change.time_origin = horizontal_adjustment.get_value() * samples_per_pixel; + ensure_visual_change_idle_handler (); +} + +bool +CueEditor::canvas_pre_event (GdkEvent* ev) +{ + switch (ev->type) { + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + if (canvas_enter_leave (&ev->crossing)) { + return true; + } + break; + default: + break; + } + + return false; +} + +bool +CueEditor::autoscroll_active () const +{ + return autoscroll_connection.connected (); +} + +/** @param allow_horiz true to allow horizontal autoscroll, otherwise false. + * + * @param allow_vert true to allow vertical autoscroll, otherwise false. + * + */ +void +CueEditor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) +{ + if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) { + return; + } + + /* define a rectangular boundary for scrolling. If the mouse moves + * outside of this area and/or continue to be outside of this area, + * then we will continuously auto-scroll the canvas in the appropriate + * direction(s) + * + * the boundary is defined in coordinates relative to canvas' own + * window since that is what we're going to call ::get_pointer() on + * during autoscrolling to determine if we're still outside the + * boundary or not. + */ + + ArdourCanvas::Rect scrolling_boundary; + Gtk::Allocation alloc; + + alloc = get_canvas()->get_allocation (); + + alloc.set_x (0); + alloc.set_y (0); + + if (allow_vert) { + /* reduce height by the height of the timebars, which happens + to correspond to the position of the data_group. + */ + + alloc.set_height (alloc.get_height() - data_group->position().y); + alloc.set_y (alloc.get_y() + data_group->position().y); + + /* now reduce it again so that we start autoscrolling before we + * move off the top or bottom of the canvas + */ + + alloc.set_height (alloc.get_height() - 20); + alloc.set_y (alloc.get_y() + 10); + } + + if (allow_horiz && (alloc.get_width() > 20)) { + + manage_possible_header (alloc); + + /* the effective width of the autoscroll boundary so + that we start scrolling before we hit the edge. + + this helps when the window is slammed up against the + right edge of the screen, making it hard to scroll + effectively. + */ + + alloc.set_width (alloc.get_width() - 20); + alloc.set_x (alloc.get_x() + 10); + } + + scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height()); + + int x, y; + Gdk::ModifierType mask; + + get_canvas()->get_window()->get_pointer (x, y, mask); + + if ((allow_horiz && ((x < scrolling_boundary.x0 && _leftmost_sample > 0) || x >= scrolling_boundary.x1)) || + (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) { + start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary); + } +} + +bool +CueEditor::autoscroll_canvas () +{ + using std::max; + using std::min; + int x, y; + Gdk::ModifierType mask; + sampleoffset_t dx = 0; + bool no_stop = false; + Gtk::Window* toplevel = dynamic_cast (_canvas_viewport.get_toplevel()); + + if (!toplevel) { + return false; + } + + get_canvas()->get_window()->get_pointer (x, y, mask); + + VisualChange vc; + bool vertical_motion = false; + + if (autoscroll_horizontal_allowed) { + + samplepos_t new_sample = _leftmost_sample; + + /* horizontal */ + + if (x > autoscroll_boundary.x1) { + + /* bring it back into view */ + dx = x - autoscroll_boundary.x1; + dx += 10 + (2 * (autoscroll_cnt/2)); + + dx = pixel_to_sample (dx); + + dx *= UIConfiguration::instance().get_draggable_playhead_speed(); + + if (_leftmost_sample < max_samplepos - dx) { + new_sample = _leftmost_sample + dx; + } else { + new_sample = max_samplepos; + } + + no_stop = true; + + } else if (x < autoscroll_boundary.x0) { + + dx = autoscroll_boundary.x0 - x; + dx += 10 + (2 * (autoscroll_cnt/2)); + + dx = pixel_to_sample (dx); + + dx *= UIConfiguration::instance().get_draggable_playhead_speed(); + + if (_leftmost_sample >= dx) { + new_sample = _leftmost_sample - dx; + } else { + new_sample = 0; + } + + no_stop = true; + } + + if (new_sample != _leftmost_sample) { + vc.time_origin = new_sample; + vc.add (VisualChange::TimeOrigin); + } + } + + if (autoscroll_vertical_allowed) { + + // const double vertical_pos = vertical_adjustment.get_value(); + const int speed_factor = 10; + + /* vertical */ + + if (y < autoscroll_boundary.y0) { + + /* scroll to make higher tracks visible */ + + if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) { + // XXX SCROLL UP + vertical_motion = true; + } + no_stop = true; + + } else if (y > autoscroll_boundary.y1) { + + if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) { + // XXX SCROLL DOWN + vertical_motion = true; + } + no_stop = true; + } + + } + + if (vc.pending || vertical_motion) { + + /* change horizontal first */ + + if (vc.pending) { + visual_changer (vc); + } + + /* now send a motion event to notify anyone who cares + that we have moved to a new location (because we scrolled) + */ + + GdkEventMotion ev; + + ev.type = GDK_MOTION_NOTIFY; + ev.state = Gdk::BUTTON1_MASK; + + /* the motion handler expects events in canvas coordinate space */ + + /* we asked for the mouse position above (::get_pointer()) via + * our own top level window (we being the Editor). Convert into + * coordinates within the canvas window. + */ + + int cx; + int cy; + + //toplevel->translate_coordinates (*get_canvas(), x, y, cx, + //cy); + cx = x; + cy = y; + + /* clamp x and y to remain within the autoscroll boundary, + * which is defined in window coordinates + */ + + x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1); + y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1); + + /* now convert from Editor window coordinates to canvas + * window coordinates + */ + + ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy)); + ev.x = d.x; + ev.y = d.y; + ev.state = mask; + + motion_handler (0, (GdkEvent*) &ev, true); + + } else if (no_stop) { + + /* not changing visual state but pointer is outside the scrolling boundary + * so we still need to deliver a fake motion event + */ + + GdkEventMotion ev; + + ev.type = GDK_MOTION_NOTIFY; + ev.state = Gdk::BUTTON1_MASK; + + /* the motion handler expects events in canvas coordinate space */ + + /* first convert from Editor window coordinates to canvas + * window coordinates + */ + + int cx; + int cy; + + /* clamp x and y to remain within the visible area. except + * .. if horizontal scrolling is allowed, always allow us to + * move back to zero + */ + + if (autoscroll_horizontal_allowed) { + x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1); + } else { + x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1); + } + y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1); + + // toplevel->translate_coordinates (*get_canvas_viewport(), x, + // y, cx, cy); + cx = x; + cy = y; + + ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy)); + ev.x = d.x; + ev.y = d.y; + ev.state = mask; + + motion_handler (0, (GdkEvent*) &ev, true); + + } else { + stop_canvas_autoscroll (); + return false; + } + + autoscroll_cnt++; + + return true; /* call me again */ +} + +void +CueEditor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary) +{ + if (!_session) { + return; + } + + stop_canvas_autoscroll (); + + autoscroll_horizontal_allowed = allow_horiz; + autoscroll_vertical_allowed = allow_vert; + autoscroll_boundary = boundary; + + /* do the first scroll right now + */ + + autoscroll_canvas (); + + /* scroll again at very very roughly 30FPS */ + + autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &CueEditor::autoscroll_canvas), 30); +} + +void +CueEditor::stop_canvas_autoscroll () +{ + autoscroll_connection.disconnect (); + autoscroll_cnt = 0; +} + +void +CueEditor::visual_changer (const VisualChange& vc) +{ + /** + * Changed first so the correct horizontal canvas position is calculated in + * EditingContext::set_horizontal_position + */ + if (vc.pending & VisualChange::ZoomLevel) { + set_samples_per_pixel (vc.samples_per_pixel); + } + + if (vc.pending & VisualChange::TimeOrigin) { + double new_time_origin = sample_to_pixel_unrounded (vc.time_origin); + set_horizontal_position (new_time_origin); + update_rulers (); + } + + if (vc.pending & VisualChange::YOrigin) { + vertical_adjustment.set_value (vc.y_origin); + } + + if (vc.pending & VisualChange::ZoomLevel) { + if (!(vc.pending & VisualChange::TimeOrigin)) { + update_rulers (); + } + } else { + /* If the canvas is not being zoomed then the canvas items will not change + * and cause Item::prepare_for_render to be called so do it here manually. + * Not ideal, but I can't think of a better solution atm. + */ + get_canvas()->prepare_for_render(); + } + + /* If we are only scrolling vertically there is no need to update these */ + if (vc.pending != VisualChange::YOrigin) { + // XXX update_fixed_rulers (); + redisplay_grid (true); + } +} + +void +CueEditor::catch_pending_show_region () +{ + if (_visible_pending_region) { + std::shared_ptr r (_visible_pending_region); + _visible_pending_region.reset (); + set_region (r); + } +} + +Editing::MouseMode +CueEditor::current_mouse_mode () const +{ + return mouse_mode; +} + +RegionSelection +CueEditor::region_selection() +{ + RegionSelection rs; + /* there is never any region-level selection in a pianoroll */ + return rs; +} + +void +CueEditor::mouse_mode_toggled (Editing::MouseMode m) +{ + Glib::RefPtr act = get_mouse_mode_action (m); + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic (act); + + if (!tact->get_active()) { + /* this was just the notification that the old mode has been + * left. we'll get called again with the new mode active in a + * jiffy. + */ + return; + } + + mouse_mode = m; + + /* this should generate a new enter event which will + trigger the appropriate cursor. + */ + + if (get_canvas()) { + get_canvas()->re_enter (); + } +} + +std::pair +CueEditor::max_zoom_extent() const +{ + if (_region) { + + Temporal::Beats len; + + if (show_source) { + len = _region->source()->length().beats(); + } else { + len = _region->length().beats(); + } + + if (len != Temporal::Beats()) { + return std::make_pair (Temporal::timepos_t (Temporal::Beats()), Temporal::timepos_t (len)); + } + } + + /* this needs to match the default empty region length used in ::make_a_region() */ + return std::make_pair (Temporal::timepos_t (Temporal::Beats()), Temporal::timepos_t (Temporal::Beats (32, 0))); +} + +void +CueEditor::zoom_to_show (Temporal::timecnt_t const & duration) +{ + if (!_track_canvas_width) { + zoom_in_allocate = true; + return; + } + + reset_zoom ((samplecnt_t) floor (duration.samples() / _track_canvas_width)); +} + +void +CueEditor::full_zoom_clicked() +{ + /* XXXX NEED LOCAL TEMPO MAP */ + + std::pair dur (max_zoom_extent()); + samplecnt_t s = dur.second.samples() - dur.first.samples(); + reposition_and_zoom (0, (s / (double) _visible_canvas_width)); +} + +void +CueEditor::set_show_source (bool yn) +{ + show_source = yn; +} + +void +CueEditor::update_solo_display () +{ + if (_track->solo_control()->get_value()) { + solo_button.set_active_state (Gtkmm2ext::ExplicitActive); + } else { + solo_button.set_active_state (Gtkmm2ext::Off); + } +} + +void +CueEditor::set_track (std::shared_ptr t) +{ + _track = t; + _track->solo_control()->Changed.connect (object_connections, invalidator (*this), std::bind (&CueEditor::update_solo_display, this), gui_context()); + update_solo_display (); +} + +void +CueEditor::set_region (std::shared_ptr r) +{ + if (r == _region) { + return; + } + + unset (false); + + _region = r; + + if (!get_canvas()->is_visible()) { + _visible_pending_region = r; + } else { + _visible_pending_region.reset (); + } +} + +void +CueEditor::maybe_set_from_rsu () +{ + RegionUISettingsManager::iterator rsu = ARDOUR_UI::instance()->region_ui_settings_manager.find (_region->id()); + if (rsu != ARDOUR_UI::instance()->region_ui_settings_manager.end()) { + set_from_rsu (rsu->second); + } +} + +void +CueEditor::set_from_rsu (RegionUISettings& rsu) +{ + follow_playhead_action->set_active (rsu.follow_playhead); + + /* XXXX play selection */ + + set_grid_type (rsu.grid_type); + set_recording_length (rsu.recording_length); + set_snap_mode (rsu.snap_mode); + set_zoom_focus (rsu.zoom_focus); + reposition_and_zoom (rsu.x_origin.samples(), rsu.samples_per_pixel); + set_recording_length (rsu.recording_length); + set_draw_length (rsu.draw_length); + set_draw_velocity (rsu.draw_velocity); + set_draw_channel (rsu.channel); +} + +void +CueEditor::set_trigger (TriggerReference& tref) +{ + if (tref == ref) { + return; + } + + _update_connection.disconnect (); + object_connections.drop_connections (); + + ref = tref; + + Stripable* st = dynamic_cast (ref.box()->owner()); + assert (st); + + set_track (std::dynamic_pointer_cast (st->shared_from_this())); + set_region (ref.trigger()->the_region()); + + _update_connection = Timers::rapid_connect (sigc::mem_fun (*this, &CueEditor::maybe_update)); +} + +void +CueEditor::ruler_locate (GdkEventButton* ev) +{ + if (!_session) { + return; + } + + if (ref.box()) { + /* we don't locate when working with triggers */ + return; + } + + if (!_region) { + return; + } + + samplepos_t sample = pixel_to_sample_from_event (ev->x); + sample += _region->source_position().samples(); + _session->request_locate (sample); +} + +void +CueEditor::maybe_set_count_in () +{ + if (!ref.box()) { + std::cerr << "msci no box\n"; + return; + } + + if (ref.box()->record_enabled() == Disabled) { + std::cerr << "msci RE\n"; + return; + } + + count_in_connection.disconnect (); + + Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use()); + bool valid; + count_in_to = ref.box()->start_time (valid); + + if (!valid) { + std::cerr << "no start time\n"; + return; + } + + samplepos_t audible (_session->audible_sample()); + Temporal::Beats const & a_q (tmap->quarters_at_sample (audible)); + + if ((count_in_to - a_q).get_beats() == 0) { + std::cerr << "not enough time\n"; + return; + } + + count_in_connection = ARDOUR_UI::Clock.connect (sigc::bind (sigc::mem_fun (*this, &CueEditor::count_in), ARDOUR_UI::clock_signal_interval())); + std::cerr << "count in started" << std::endl; +} + +void +CueEditor::count_in (Temporal::timepos_t audible, unsigned int clock_interval_msecs) +{ + if (!_session) { + return; + } + + if (!_session->transport_rolling()) { + return; + } + + TempoMapPoints grid_points; + TempoMap::SharedPtr tmap (TempoMap::use()); + Temporal::Beats audible_beats = tmap->quarters_at_sample (audible.samples()); + samplepos_t audible_samples = audible.samples (); + + if (audible_beats >= count_in_to) { + /* passed the count_in_to time */ + hide_count_in (); + count_in_connection.disconnect (); + return; + } + + tmap->get_grid (grid_points, samples_to_superclock (audible_samples, _session->sample_rate()), samples_to_superclock ((audible_samples + ((_session->sample_rate() / 1000) * clock_interval_msecs)), _session->sample_rate())); + + if (!grid_points.empty()) { + + /* At least one click in the time between now and the next + * Clock signal + */ + + Temporal::Beats current_delta = count_in_to - audible_beats; + + if (current_delta.get_beats() < 1) { + hide_count_in (); + count_in_connection.disconnect (); + return; + } + + std::string str (string_compose ("%1", current_delta.get_beats())); + std::cerr << str << std::endl; + show_count_in (str); + } +} + +bool +CueEditor::ruler_event (GdkEvent* ev) +{ + switch (ev->type) { + case GDK_BUTTON_RELEASE: + if (ev->button.button == 1) { + ruler_locate (&ev->button); + } + return true; + default: + break; + } + + return false; +} + +void +CueEditor::data_captured (samplecnt_t total_duration) +{ + data_capture_duration = total_duration; + + if (!idle_update_queued.exchange (1)) { + Glib::signal_idle().connect (sigc::mem_fun (*this, &CueEditor::idle_data_captured)); + } +} + +bool +CueEditor::idle_data_captured () +{ + if (!ref.box()) { + return false; + } + + switch (ref.box()->record_enabled()) { + case Recording: + break; + default: + return false; + } + + double where = sample_to_pixel_unrounded (data_capture_duration); + + if (where > _visible_canvas_width * 0.80) { + set_samples_per_pixel (samples_per_pixel * 1.5); + } + + idle_update_queued.store (0); + return false; +} + +void +CueEditor::unset (bool trigger_too) +{ + _history.clear (); + history_connection.disconnect(); + _update_connection.disconnect(); + object_connections.drop_connections (); + rec_blink_connection.disconnect (); + count_in_connection.disconnect (); + capture_connections.drop_connections (); + + _track.reset (); + _region.reset (); + + if (trigger_too) { + ref = TriggerReference (); + } +} + +void +CueEditor::session_going_away () +{ + EditingContext::session_going_away (); + unset (true); +} + +void +CueEditor::load_bindings () +{ + load_shared_bindings (); + for (auto & b : bindings) { + b->associate (); + } + set_widget_bindings (*get_canvas(), bindings, Gtkmm2ext::ARDOUR_BINDING_KEY); +} + +void +CueEditor::register_actions () +{ + editor_actions = ActionManager::create_action_group (own_bindings, editor_name()); + bind_mouse_mode_buttons (); +} + +ArdourCanvas::GtkCanvasViewport* +CueEditor::get_canvas_viewport() const +{ + return const_cast(&_canvas_viewport); +} + +ArdourCanvas::GtkCanvas* +CueEditor::get_canvas() const +{ + return &_canvas; +} + + +int +CueEditor::set_state (XMLNode const & node, int version) +{ + set_common_editing_state (node); + return 0; +} + +XMLNode& +CueEditor::get_state () const +{ + XMLNode* node (new XMLNode (editor_name())); + get_common_editing_state (*node); + return *node; +} + +static void +edit_last_mark_label (std::vector& marks, const std::string& newlabel) +{ + ArdourCanvas::Ruler::Mark copy = marks.back(); + copy.label = newlabel; + marks.pop_back (); + marks.push_back (copy); +} + +void +CueEditor::metric_get_bbt (std::vector& marks, samplepos_t leftmost, samplepos_t rightmost, gint /*maxchars*/) +{ + if (!_session) { + return; + } + + bool provided = false; + std::shared_ptr tmap; + std::shared_ptr mr = std::dynamic_pointer_cast (_region); + + if (mr) { + std::shared_ptr smf (std::dynamic_pointer_cast (mr->midi_source())); + + if (smf) { + tmap = smf->tempo_map (provided); + } + } + + if (!provided) { + tmap.reset (new Temporal::TempoMap (Temporal::Tempo (120, 4), Temporal::Meter (4, 4))); + } + + EditingContext::TempoMapScope tms (*this, tmap); + Temporal::TempoMapPoints::const_iterator i; + + char buf[64]; + Temporal::BBT_Time next_beat; + double bbt_position_of_helper; + bool helper_active = false; + ArdourCanvas::Ruler::Mark mark; + const samplecnt_t sr (_session->sample_rate()); + + Temporal::TempoMapPoints grid; + grid.reserve (4096); + + /* prevent negative values of leftmost from creeping into tempomap + */ + + const Beats left = tmap->quarters_at_sample (leftmost).round_down_to_beat(); + const Beats lower_beat = (left < Beats() ? Beats() : left); + + using std::max; + + switch (bbt_ruler_scale) { + + case bbt_show_quarters: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 1); + break; + case bbt_show_eighths: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 2); + break; + case bbt_show_sixteenths: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 4); + break; + case bbt_show_thirtyseconds: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 8); + break; + case bbt_show_sixtyfourths: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 16); + break; + case bbt_show_onetwentyeighths: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 32); + break; + + case bbt_show_1: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 1); + break; + + case bbt_show_4: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 4); + break; + + case bbt_show_16: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 16); + break; + + case bbt_show_64: + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 64); + break; + + default: + /* bbt_show_many */ + tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 128); + break; + } + +#if 0 // DEBUG GRID + for (auto const& g : grid) { + std::cout << "Grid " << g.time() << " Beats: " << g.beats() << " BBT: " << g.bbt() << " sample: " << g.sample(_session->nominal_sample_rate ()) << "\n"; + } +#endif + + if (distance (grid.begin(), grid.end()) == 0) { + return; + } + + /* we can accent certain lines depending on the user's Grid choice */ + /* for example, even in a 4/4 meter we can draw a grid with triplet-feel */ + /* and in this case you will want the accents on '3s' not '2s' */ + uint32_t bbt_divisor = 2; + + using namespace Editing; + + switch (grid_type()) { + case GridTypeBeatDiv3: + bbt_divisor = 3; + break; + case GridTypeBeatDiv5: + bbt_divisor = 5; + break; + case GridTypeBeatDiv6: + bbt_divisor = 3; + break; + case GridTypeBeatDiv7: + bbt_divisor = 7; + break; + case GridTypeBeatDiv10: + bbt_divisor = 5; + break; + case GridTypeBeatDiv12: + bbt_divisor = 3; + break; + case GridTypeBeatDiv14: + bbt_divisor = 7; + break; + case GridTypeBeatDiv16: + break; + case GridTypeBeatDiv20: + bbt_divisor = 5; + break; + case GridTypeBeatDiv24: + bbt_divisor = 6; + break; + case GridTypeBeatDiv28: + bbt_divisor = 7; + break; + case GridTypeBeatDiv32: + break; + default: + bbt_divisor = 2; + break; + } + + uint32_t bbt_beat_subdivision = 1; + switch (bbt_ruler_scale) { + case bbt_show_quarters: + bbt_beat_subdivision = 1; + break; + case bbt_show_eighths: + bbt_beat_subdivision = 1; + break; + case bbt_show_sixteenths: + bbt_beat_subdivision = 2; + break; + case bbt_show_thirtyseconds: + bbt_beat_subdivision = 4; + break; + case bbt_show_sixtyfourths: + bbt_beat_subdivision = 8; + break; + case bbt_show_onetwentyeighths: + bbt_beat_subdivision = 16; + break; + default: + bbt_beat_subdivision = 1; + break; + } + + bbt_beat_subdivision *= bbt_divisor; + + switch (bbt_ruler_scale) { + + case bbt_show_many: + snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars); + mark.style = ArdourCanvas::Ruler::Mark::Major; + mark.label = buf; + mark.position = leftmost; + marks.push_back (mark); + break; + + case bbt_show_64: + for (i = grid.begin(); i != grid.end(); i++) { + BBT_Time bbt ((*i).bbt()); + if (bbt.is_bar()) { + if (bbt.bars % 64 == 1) { + if (bbt.bars % 256 == 1) { + snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); + mark.style = ArdourCanvas::Ruler::Mark::Major; + } else { + buf[0] = '\0'; + if (bbt.bars % 256 == 129) { + mark.style = ArdourCanvas::Ruler::Mark::Minor; + } else { + mark.style = ArdourCanvas::Ruler::Mark::Micro; + } + } + mark.label = buf; + mark.position = (*i).sample (sr); + marks.push_back (mark); + } + } + } + break; + + case bbt_show_16: + for (i = grid.begin(); i != grid.end(); i++) { + BBT_Time bbt ((*i).bbt()); + if (bbt.is_bar()) { + if (bbt.bars % 16 == 1) { + if (bbt.bars % 64 == 1) { + snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); + mark.style = ArdourCanvas::Ruler::Mark::Major; + } else { + buf[0] = '\0'; + if (bbt.bars % 64 == 33) { + mark.style = ArdourCanvas::Ruler::Mark::Minor; + } else { + mark.style = ArdourCanvas::Ruler::Mark::Micro; + } + } + mark.label = buf; + mark.position = (*i).sample(sr); + marks.push_back (mark); + } + } + } + break; + + case bbt_show_4: + for (i = grid.begin(); i != grid.end(); ++i) { + BBT_Time bbt ((*i).bbt()); + if (bbt.is_bar()) { + if (bbt.bars % 4 == 1) { + if (bbt.bars % 16 == 1) { + snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); + mark.style = ArdourCanvas::Ruler::Mark::Major; + } else { + buf[0] = '\0'; + mark.style = ArdourCanvas::Ruler::Mark::Minor; + } + mark.label = buf; + mark.position = (*i).sample (sr); + marks.push_back (mark); + } + } + } + break; + + case bbt_show_1: + for (i = grid.begin(); i != grid.end(); ++i) { + BBT_Time bbt ((*i).bbt()); + if (bbt.is_bar()) { + snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); + mark.style = ArdourCanvas::Ruler::Mark::Major; + mark.label = buf; + mark.position = (*i).sample (sr); + marks.push_back (mark); + } + } + break; + + case bbt_show_quarters: + + mark.label = ""; + mark.position = leftmost; + mark.style = ArdourCanvas::Ruler::Mark::Micro; + marks.push_back (mark); + + for (i = grid.begin(); i != grid.end(); ++i) { + + BBT_Time bbt ((*i).bbt()); + + if ((*i).sample (sr) < leftmost && (bbt_bar_helper_on)) { + snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats); + edit_last_mark_label (marks, buf); + } else { + + if (bbt.is_bar()) { + mark.style = ArdourCanvas::Ruler::Mark::Major; + snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); + } else if ((bbt.beats % 2) == 1) { + mark.style = ArdourCanvas::Ruler::Mark::Minor; + buf[0] = '\0'; + } else { + mark.style = ArdourCanvas::Ruler::Mark::Micro; + buf[0] = '\0'; + } + mark.label = buf; + mark.position = (*i).sample (sr); + marks.push_back (mark); + } + } + break; + + case bbt_show_eighths: + case bbt_show_sixteenths: + case bbt_show_thirtyseconds: + case bbt_show_sixtyfourths: + case bbt_show_onetwentyeighths: + + bbt_position_of_helper = leftmost + (3 * get_current_zoom ()); + + mark.label = ""; + mark.position = leftmost; + mark.style = ArdourCanvas::Ruler::Mark::Micro; + marks.push_back (mark); + + for (i = grid.begin(); i != grid.end(); ++i) { + + BBT_Time bbt ((*i).bbt()); + + if ((*i).sample (sr) < leftmost && (bbt_bar_helper_on)) { + snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats); + edit_last_mark_label (marks, buf); + helper_active = true; + } else { + + if (bbt.is_bar()) { + mark.style = ArdourCanvas::Ruler::Mark::Major; + snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); + } else if (bbt.ticks == 0) { + mark.style = ArdourCanvas::Ruler::Mark::Minor; + snprintf (buf, sizeof(buf), "%" PRIu32, bbt.beats); + } else { + mark.style = ArdourCanvas::Ruler::Mark::Micro; + buf[0] = '\0'; + } + + if (((*i).sample(sr) < bbt_position_of_helper) && helper_active) { + buf[0] = '\0'; + } + mark.label = buf; + mark.position = (*i).sample (sr); + marks.push_back (mark); + } + } + + break; + } +} + diff --git a/gtk2_ardour/cue_editor.h b/gtk2_ardour/cue_editor.h index dbf6eae95b..e6ccba6f70 100644 --- a/gtk2_ardour/cue_editor.h +++ b/gtk2_ardour/cue_editor.h @@ -20,15 +20,34 @@ #include "pbd/history_owner.h" +#include "canvas/canvas.h" + +#include "widgets/ardour_button.h" +#include "widgets/eventboxext.h" + #include "editing.h" #include "editing_context.h" +#include "region_ui_settings.h" + +namespace Gtk { + class HScrollbar; +} class CueEditor : public EditingContext, public PBD::HistoryOwner { public: - CueEditor (std::string const & name); + CueEditor (std::string const & name, bool with_transport_controls); ~CueEditor (); + virtual Gtk::Widget& contents () = 0; + + void session_going_away (); + + ArdourCanvas::Container* get_trackview_group () const { return data_group; } + ArdourCanvas::Container* get_noscroll_group() const { return no_scroll_group; } + ArdourCanvas::ScrollGroup* get_hscroll_group () const { return h_scroll_group; } + ArdourCanvas::ScrollGroup* get_cursor_scroll_group () const { return cursor_scroll_group; } + void get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const; StripableTimeAxisView* get_stripable_time_axis_by_id (const PBD::ID& id) const; TrackViewList axis_views_from_routes (std::shared_ptr) const; @@ -46,6 +65,7 @@ class CueEditor : public EditingContext, public PBD::HistoryOwner void redisplay_grid (bool immediate_redraw); Temporal::timecnt_t get_nudge_distance (Temporal::timepos_t const & pos, Temporal::timecnt_t& next) const; + std::list selectable_owners() { return std::list(); } void instant_save(); @@ -56,6 +76,8 @@ class CueEditor : public EditingContext, public PBD::HistoryOwner void undo_selection_op (); void redo_selection_op (); + RegionSelection region_selection(); + PBD::HistoryOwner& history() { return *this; } void history_changed (); PBD::ScopedConnection history_connection; @@ -85,6 +107,7 @@ class CueEditor : public EditingContext, public PBD::HistoryOwner Editing::MouseMode current_mouse_mode () const; /** cue editors are *always* used for internal editing */ bool internal_editing() const { return true; } + void mouse_mode_toggled (Editing::MouseMode); Gdk::Cursor* get_canvas_cursor () const; MouseCursors const* cursors () const { @@ -97,10 +120,150 @@ class CueEditor : public EditingContext, public PBD::HistoryOwner std::shared_ptr start_local_tempo_map (std::shared_ptr); void end_local_tempo_map (std::shared_ptr); + void scrolled (); + bool canvas_pre_event (GdkEvent*); + void catch_pending_show_region (); + + std::pair max_zoom_extent() const; + + void full_zoom_clicked(); + void zoom_to_show (Temporal::timecnt_t const &); + + bool ruler_event (GdkEvent*); + + virtual void set_show_source (bool); + virtual void set_region (std::shared_ptr); + virtual void set_track (std::shared_ptr); + virtual void set_trigger (ARDOUR::TriggerReference&); + + virtual void maybe_update () = 0; + + ArdourCanvas::GtkCanvasViewport* get_canvas_viewport() const; + ArdourCanvas::GtkCanvas* get_canvas() const; + + int set_state (const XMLNode&, int version); + XMLNode& get_state () const; + protected: + ArdourCanvas::GtkCanvasViewport _canvas_viewport; + ArdourCanvas::GtkCanvas& _canvas; + ARDOUR::TriggerReference ref; + std::shared_ptr _region; + std::shared_ptr _track; + bool with_transport_controls; + bool show_source; + ArdourWidgets::EventBoxExt _contents; + Gtk::VBox _toolbox; + Gtk::HBox button_bar; + Gtk::HScrollbar* _canvas_hscrollbar; + + void load_bindings (); + void register_actions (); + + /* The group containing all other groups that are scrolled vertically + and horizontally. + */ + ArdourCanvas::ScrollGroup* hv_scroll_group; + + /* The group containing all other groups that are scrolled horizontally ONLY + */ + ArdourCanvas::ScrollGroup* h_scroll_group; + ArdourCanvas::ScrollGroup* v_scroll_group; + + /* Scroll group for cursors, scrolled horizontally, above everything else + */ + ArdourCanvas::ScrollGroup* cursor_scroll_group; + + ArdourCanvas::Container* global_rect_group; + ArdourCanvas::Container* no_scroll_group; + ArdourCanvas::Container* data_group; + + Gtk::Label length_label; + Gtk::HBox rec_box; + Gtk::HBox play_box; + + virtual void pack_inner (Gtk::Box&) = 0; + virtual void pack_outer (Gtk::Box&) = 0; + void build_zoom_focus_menu (); + + virtual void update_rulers() {} + virtual bool canvas_enter_leave (GdkEventCrossing* ev) = 0; + + void build_upper_toolbar (); void do_undo (uint32_t n); void do_redo (uint32_t n); Temporal::timepos_t _get_preferred_edit_position (Editing::EditIgnoreOption, bool use_context_click, bool from_outside_canvas); + + ArdourWidgets::ArdourButton rec_enable_button; + ArdourWidgets::ArdourButton play_button; + ArdourWidgets::ArdourButton solo_button; + ArdourWidgets::ArdourButton loop_button; + + ArdourCanvas::Rectangle* transport_loop_range_rect; + + bool play_button_press (GdkEventButton*); + bool solo_button_press (GdkEventButton*); + bool bang_button_press (GdkEventButton*); + bool loop_button_press (GdkEventButton*); + + ArdourWidgets::ArdourDropdown length_selector; + Temporal::BBT_Offset rec_length; + + bool zoom_in_allocate; + + void set_recording_length (Temporal::BBT_Offset bars); + + bool rec_button_press (GdkEventButton*); + void rec_enable_change (); + void blink_rec_enable (bool); + sigc::connection rec_blink_connection; + + sigc::connection _update_connection; + PBD::ScopedConnectionList object_connections; + + void trigger_arm_change (); + + double timebar_height; + size_t n_timebars; + + /* autoscrolling */ + + bool autoscroll_canvas (); + void start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary); + void visual_changer (const VisualChange&); + + void update_solo_display (); + + std::shared_ptr _visible_pending_region; + + void ruler_locate (GdkEventButton*); + + virtual void begin_write () = 0; + virtual void end_write () = 0; + + virtual void manage_possible_header (Gtk::Allocation&) {} + + sigc::connection count_in_connection; + Temporal::Beats count_in_to; + + void count_in (Temporal::timepos_t, unsigned int); + void maybe_set_count_in (); + virtual void show_count_in (std::string const &) = 0; + virtual void hide_count_in () = 0; + + void data_captured (samplecnt_t); + virtual bool idle_data_captured () = 0; + std::atomic idle_update_queued; + PBD::ScopedConnectionList capture_connections; + samplecnt_t data_capture_duration; + + virtual void unset (bool trigger_too); + + RegionUISettings region_ui_settings; + void maybe_set_from_rsu (); + virtual void set_from_rsu (RegionUISettings&); + + void metric_get_bbt (std::vector&, samplepos_t, samplepos_t, gint); }; diff --git a/gtk2_ardour/editing_context.cc b/gtk2_ardour/editing_context.cc index 629e459430..5401552106 100644 --- a/gtk2_ardour/editing_context.cc +++ b/gtk2_ardour/editing_context.cc @@ -117,11 +117,6 @@ EditingContext::EditingContext (std::string const & name) , pre_internal_snap_mode (SnapOff) , internal_grid_type (GridTypeBeat) , internal_snap_mode (SnapOff) - , _grid_type (GridTypeBeat) - , _snap_mode (SnapOff) - , _draw_length (GridTypeNone) - , _draw_velocity (DRAW_VEL_AUTO) - , _draw_channel (DRAW_CHAN_AUTO) , _timeline_origin (0.) , play_note_selection_button (ArdourButton::default_elements) , follow_playhead_button (_("Follow Playhead"), ArdourButton::Element (ArdourButton::Edge | ArdourButton::Body | ArdourButton::VectorIcon), true) @@ -131,7 +126,6 @@ EditingContext::EditingContext (std::string const & name) , _leftmost_sample (0) , _playhead_cursor (nullptr) , _snapped_cursor (nullptr) - , _follow_playhead (false) , selection (new Selection (this, true)) , cut_buffer (new Selection (this, false)) , _selection_memento (new SelectionMemento()) @@ -194,10 +188,6 @@ EditingContext::EditingContext (std::string const & name) std::cerr << "Set cursor set to " << UIConfiguration::instance().get_icon_set() << std::endl; } - DrawLengthChanged.connect (sigc::mem_fun (*this, &EditingContext::draw_length_changed)); - DrawVelocityChanged.connect (sigc::mem_fun (*this, &EditingContext::draw_velocity_changed)); - DrawChannelChanged.connect (sigc::mem_fun (*this, &EditingContext::draw_channel_changed)); - set_tooltip (draw_length_selector, _("Note Length to Draw (AUTO uses the current Grid setting)")); set_tooltip (draw_velocity_selector, _("Note Velocity to Draw (AUTO uses the nearest note's velocity)")); set_tooltip (draw_channel_selector, _("Note Channel to Draw (AUTO uses the nearest note's channel)")); @@ -231,6 +221,11 @@ EditingContext::EditingContext (std::string const & name) follow_playhead_button.set_name ("transport option button"); follow_edits_button.set_name ("transport option button"); + note_mode_button.set_icon (ArdourIcon::Drum); +#define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale())) + note_mode_button.set_size_request (PX_SCALE(50), -1); + note_mode_button.set_active_color (UIConfiguration::instance().color ("alert:yellow")); + selection->PointsChanged.connect (sigc::mem_fun(*this, &EditingContext::point_selection_changed)); for (int i = 0; i < 16; i++) { @@ -321,13 +316,17 @@ EditingContext::register_automation_actions (Bindings* automation_bindings, std: void EditingContext::enable_automation_bindings () { - ActionManager::set_sensitive (_automation_actions, true); + if (_automation_actions) { + ActionManager::set_sensitive (_automation_actions, true); + } } void EditingContext::disable_automation_bindings () { - ActionManager::set_sensitive (_automation_actions, false); + if (_automation_actions) { + ActionManager::set_sensitive (_automation_actions, false); + } } void @@ -338,7 +337,7 @@ EditingContext::register_common_actions (Bindings* common_bindings, std::string reg_sens (_common_actions, "temporal-zoom-out", _("Zoom Out"), sigc::bind (sigc::mem_fun (*this, &EditingContext::temporal_zoom_step), true)); reg_sens (_common_actions, "temporal-zoom-in", _("Zoom In"), sigc::bind (sigc::mem_fun (*this, &EditingContext::temporal_zoom_step), false)); - toggle_reg_sens (_common_actions, "toggle-follow-playhead", _("Follow Playhead"), (sigc::mem_fun(*this, &EditingContext::toggle_follow_playhead))); + follow_playhead_action = toggle_reg_sens (_common_actions, "toggle-follow-playhead", _("Follow Playhead"), (sigc::mem_fun(*this, &EditingContext::toggle_follow_playhead))); undo_action = reg_sens (_common_actions, "undo", S_("Command|Undo"), sigc::bind (sigc::mem_fun (*this, &EditingContext::undo), 1U)); redo_action = reg_sens (_common_actions, "redo", _("Redo"), sigc::bind (sigc::mem_fun (*this, &EditingContext::redo), 1U)); @@ -379,9 +378,9 @@ EditingContext::register_common_actions (Bindings* common_bindings, std::string ActionManager::register_action (_common_actions, X_("GridChoice"), _("Snap & Grid")); RadioAction::Group snap_mode_group; - /* deprecated */ ActionManager::register_radio_action (_common_actions, snap_mode_group, X_("snap-off"), _("No Grid"), (sigc::bind (sigc::mem_fun(*this, &EditingContext::snap_mode_chosen), Editing::SnapOff))); - /* deprecated */ ActionManager::register_radio_action (_common_actions, snap_mode_group, X_("snap-normal"), _("Grid"), (sigc::bind (sigc::mem_fun(*this, &EditingContext::snap_mode_chosen), Editing::SnapNormal))); //deprecated - /* deprecated */ ActionManager::register_radio_action (_common_actions, snap_mode_group, X_("snap-magnetic"), _("Magnetic"), (sigc::bind (sigc::mem_fun(*this, &EditingContext::snap_mode_chosen), Editing::SnapMagnetic))); + snap_mode_actions[Editing::SnapOff] = ActionManager::register_radio_action (_common_actions, snap_mode_group, X_("snap-off"), _("No Grid"), (sigc::bind (sigc::mem_fun(*this, &EditingContext::snap_mode_chosen), Editing::SnapOff))); + snap_mode_actions[Editing::SnapNormal] = ActionManager::register_radio_action (_common_actions, snap_mode_group, X_("snap-normal"), _("Grid"), (sigc::bind (sigc::mem_fun(*this, &EditingContext::snap_mode_chosen), Editing::SnapNormal))); //deprecated + snap_mode_actions[Editing::SnapMagnetic] = ActionManager::register_radio_action (_common_actions, snap_mode_group, X_("snap-magnetic"), _("Magnetic"), (sigc::bind (sigc::mem_fun(*this, &EditingContext::snap_mode_chosen), Editing::SnapMagnetic))); ActionManager::register_action (_common_actions, X_("cycle-snap-mode"), _("Toggle Snap"), sigc::mem_fun (*this, &EditingContext::cycle_snap_mode)); ActionManager::register_action (_common_actions, X_("next-grid-choice"), _("Next Quantize Grid Choice"), sigc::mem_fun (*this, &EditingContext::next_grid_choice)); @@ -390,30 +389,30 @@ EditingContext::register_common_actions (Bindings* common_bindings, std::string snap_actions = ActionManager::create_action_group (common_bindings, prefix + X_("Snap")); RadioAction::Group grid_choice_group; - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-thirtyseconds"), grid_type_strings[(int)GridTypeBeatDiv32].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv32))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-twentyeighths"), grid_type_strings[(int)GridTypeBeatDiv28].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv28))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-twentyfourths"), grid_type_strings[(int)GridTypeBeatDiv24].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv24))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-twentieths"), grid_type_strings[(int)GridTypeBeatDiv20].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv20))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-asixteenthbeat"), grid_type_strings[(int)GridTypeBeatDiv16].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv16))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-fourteenths"), grid_type_strings[(int)GridTypeBeatDiv14].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv14))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-twelfths"), grid_type_strings[(int)GridTypeBeatDiv12].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv12))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-tenths"), grid_type_strings[(int)GridTypeBeatDiv10].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv10))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-eighths"), grid_type_strings[(int)GridTypeBeatDiv8].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv8))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-sevenths"), grid_type_strings[(int)GridTypeBeatDiv7].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv7))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-sixths"), grid_type_strings[(int)GridTypeBeatDiv6].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv6))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-fifths"), grid_type_strings[(int)GridTypeBeatDiv5].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv5))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-quarters"), grid_type_strings[(int)GridTypeBeatDiv4].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv4))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-thirds"), grid_type_strings[(int)GridTypeBeatDiv3].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv3))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-halves"), grid_type_strings[(int)GridTypeBeatDiv2].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv2))); + grid_actions[Editing::GridTypeBeatDiv32] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-thirtyseconds"), grid_type_strings[(int)GridTypeBeatDiv32].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv32))); + grid_actions[Editing::GridTypeBeatDiv28] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-twentyeighths"), grid_type_strings[(int)GridTypeBeatDiv28].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv28))); + grid_actions[Editing::GridTypeBeatDiv24] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-twentyfourths"), grid_type_strings[(int)GridTypeBeatDiv24].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv24))); + grid_actions[Editing::GridTypeBeatDiv20] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-twentieths"), grid_type_strings[(int)GridTypeBeatDiv20].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv20))); + grid_actions[Editing::GridTypeBeatDiv16] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-asixteenthbeat"), grid_type_strings[(int)GridTypeBeatDiv16].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv16))); + grid_actions[Editing::GridTypeBeatDiv14] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-fourteenths"), grid_type_strings[(int)GridTypeBeatDiv14].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv14))); + grid_actions[Editing::GridTypeBeatDiv12] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-twelfths"), grid_type_strings[(int)GridTypeBeatDiv12].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv12))); + grid_actions[Editing::GridTypeBeatDiv10] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-tenths"), grid_type_strings[(int)GridTypeBeatDiv10].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv10))); + grid_actions[Editing::GridTypeBeatDiv8] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-eighths"), grid_type_strings[(int)GridTypeBeatDiv8].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv8))); + grid_actions[Editing::GridTypeBeatDiv7] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-sevenths"), grid_type_strings[(int)GridTypeBeatDiv7].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv7))); + grid_actions[Editing::GridTypeBeatDiv6] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-sixths"), grid_type_strings[(int)GridTypeBeatDiv6].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv6))); + grid_actions[Editing::GridTypeBeatDiv5] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-fifths"), grid_type_strings[(int)GridTypeBeatDiv5].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv5))); + grid_actions[Editing::GridTypeBeatDiv4] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-quarters"), grid_type_strings[(int)GridTypeBeatDiv4].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv4))); + grid_actions[Editing::GridTypeBeatDiv3] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-thirds"), grid_type_strings[(int)GridTypeBeatDiv3].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv3))); + grid_actions[Editing::GridTypeBeatDiv2] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-halves"), grid_type_strings[(int)GridTypeBeatDiv2].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeatDiv2))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-timecode"), grid_type_strings[(int)GridTypeTimecode].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeTimecode))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-minsec"), grid_type_strings[(int)GridTypeMinSec].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeMinSec))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-cdframe"), grid_type_strings[(int)GridTypeCDFrame].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeCDFrame))); + grid_actions[Editing::GridTypeTimecode] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-timecode"), grid_type_strings[(int)GridTypeTimecode].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeTimecode))); + grid_actions[Editing::GridTypeMinSec] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-minsec"), grid_type_strings[(int)GridTypeMinSec].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeMinSec))); + grid_actions[Editing::GridTypeCDFrame] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-cdframe"), grid_type_strings[(int)GridTypeCDFrame].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeCDFrame))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-beat"), grid_type_strings[(int)GridTypeBeat].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeat))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-bar"), grid_type_strings[(int)GridTypeBar].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBar))); + grid_actions[Editing::GridTypeBeat] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-beat"), grid_type_strings[(int)GridTypeBeat].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBeat))); + grid_actions[Editing::GridTypeBar] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-bar"), grid_type_strings[(int)GridTypeBar].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeBar))); - ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-none"), grid_type_strings[(int)GridTypeNone].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeNone))); + grid_actions[Editing::GridTypeNone] = ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-none"), grid_type_strings[(int)GridTypeNone].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeNone))); } void @@ -500,47 +499,47 @@ EditingContext::register_midi_actions (Bindings* midi_bindings, std::string cons length_actions = ActionManager::create_action_group (midi_bindings, prefix + X_("DrawLength")); RadioAction::Group draw_length_group; - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-thirtyseconds"), grid_type_strings[(int)GridTypeBeatDiv32].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv32)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-twentyeighths"), grid_type_strings[(int)GridTypeBeatDiv28].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv28)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-twentyfourths"), grid_type_strings[(int)GridTypeBeatDiv24].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv24)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-twentieths"), grid_type_strings[(int)GridTypeBeatDiv20].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv20)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-asixteenthbeat"), grid_type_strings[(int)GridTypeBeatDiv16].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv16)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-fourteenths"), grid_type_strings[(int)GridTypeBeatDiv14].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv14)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-twelfths"), grid_type_strings[(int)GridTypeBeatDiv12].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv12)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-tenths"), grid_type_strings[(int)GridTypeBeatDiv10].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv10)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-eighths"), grid_type_strings[(int)GridTypeBeatDiv8].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv8)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-sevenths"), grid_type_strings[(int)GridTypeBeatDiv7].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv7)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-sixths"), grid_type_strings[(int)GridTypeBeatDiv6].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv6)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-fifths"), grid_type_strings[(int)GridTypeBeatDiv5].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv5)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-quarters"), grid_type_strings[(int)GridTypeBeatDiv4].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv4)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-thirds"), grid_type_strings[(int)GridTypeBeatDiv3].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv3)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-halves"), grid_type_strings[(int)GridTypeBeatDiv2].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv2)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-beat"), grid_type_strings[(int)GridTypeBeat].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeat)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-bar"), grid_type_strings[(int)GridTypeBar].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBar)); - ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-auto"), _("Auto"), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), DRAW_LEN_AUTO)); + draw_length_actions[Editing::GridTypeBeatDiv32] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-thirtyseconds"), grid_type_strings[(int)GridTypeBeatDiv32].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv32)); + draw_length_actions[Editing::GridTypeBeatDiv28] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-twentyeighths"), grid_type_strings[(int)GridTypeBeatDiv28].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv28)); + draw_length_actions[Editing::GridTypeBeatDiv24] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-twentyfourths"), grid_type_strings[(int)GridTypeBeatDiv24].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv24)); + draw_length_actions[Editing::GridTypeBeatDiv20] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-twentieths"), grid_type_strings[(int)GridTypeBeatDiv20].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv20)); + draw_length_actions[Editing::GridTypeBeatDiv16] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-asixteenthbeat"), grid_type_strings[(int)GridTypeBeatDiv16].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv16)); + draw_length_actions[Editing::GridTypeBeatDiv14] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-fourteenths"), grid_type_strings[(int)GridTypeBeatDiv14].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv14)); + draw_length_actions[Editing::GridTypeBeatDiv12] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-twelfths"), grid_type_strings[(int)GridTypeBeatDiv12].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv12)); + draw_length_actions[Editing::GridTypeBeatDiv10] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-tenths"), grid_type_strings[(int)GridTypeBeatDiv10].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv10)); + draw_length_actions[Editing::GridTypeBeatDiv8] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-eighths"), grid_type_strings[(int)GridTypeBeatDiv8].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv8)); + draw_length_actions[Editing::GridTypeBeatDiv7] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-sevenths"), grid_type_strings[(int)GridTypeBeatDiv7].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv7)); + draw_length_actions[Editing::GridTypeBeatDiv6] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-sixths"), grid_type_strings[(int)GridTypeBeatDiv6].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv6)); + draw_length_actions[Editing::GridTypeBeatDiv5] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-fifths"), grid_type_strings[(int)GridTypeBeatDiv5].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv5)); + draw_length_actions[Editing::GridTypeBeatDiv4] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-quarters"), grid_type_strings[(int)GridTypeBeatDiv4].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv4)); + draw_length_actions[Editing::GridTypeBeatDiv3] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-thirds"), grid_type_strings[(int)GridTypeBeatDiv3].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv3)); + draw_length_actions[Editing::GridTypeBeatDiv2] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-halves"), grid_type_strings[(int)GridTypeBeatDiv2].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeatDiv2)); + draw_length_actions[Editing::GridTypeBeat] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-beat"), grid_type_strings[(int)GridTypeBeat].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBeat)); + draw_length_actions[Editing::GridTypeBar] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-bar"), grid_type_strings[(int)GridTypeBar].c_str(), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), Editing::GridTypeBar)); + draw_length_actions[DRAW_LEN_AUTO] = ActionManager::register_radio_action (length_actions, draw_length_group, X_("draw-length-auto"), _("Auto"), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_length_chosen), DRAW_LEN_AUTO)); velocity_actions = ActionManager::create_action_group (midi_bindings, prefix + X_("DrawVelocity")); RadioAction::Group draw_velocity_group; - ActionManager::register_radio_action (velocity_actions, draw_velocity_group, X_("draw-velocity-auto"), _("Auto"), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_velocity_chosen), DRAW_VEL_AUTO)); + draw_velocity_actions[DRAW_VEL_AUTO] = ActionManager::register_radio_action (velocity_actions, draw_velocity_group, X_("draw-velocity-auto"), _("Auto"), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_velocity_chosen), DRAW_VEL_AUTO)); for (int i = 1; i <= 127; i++) { char buf[64]; snprintf(buf, sizeof (buf), X_("draw-velocity-%d"), i); char vel[64]; sprintf(vel, _("Velocity %d"), i); - Glib::RefPtr act = ActionManager::register_radio_action (velocity_actions, draw_velocity_group, buf, vel, sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_velocity_chosen), i)); + draw_velocity_actions[i] = ActionManager::register_radio_action (velocity_actions, draw_velocity_group, buf, vel, sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_velocity_chosen), i)); snprintf (buf,sizeof (buf), "%d", i); - act->set_short_label (buf); + draw_velocity_actions[i]->set_short_label (buf); } channel_actions = ActionManager::create_action_group (midi_bindings, prefix + X_("DrawChannel")); RadioAction::Group draw_channel_group; - ActionManager::register_radio_action (channel_actions, draw_channel_group, X_("draw-channel-auto"), _("Auto"), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_channel_chosen), DRAW_CHAN_AUTO)); + draw_channel_actions[DRAW_CHAN_AUTO] = ActionManager::register_radio_action (channel_actions, draw_channel_group, X_("draw-channel-auto"), _("Auto"), sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_channel_chosen), DRAW_CHAN_AUTO)); for (int i = 0; i <= 15; i++) { char buf[64]; snprintf(buf, sizeof (buf), X_("draw-channel-%d"), i+1); char ch[64]; sprintf(ch, X_("Channel %d"), i+1); - ActionManager::register_radio_action (channel_actions, draw_channel_group, buf, ch, sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_channel_chosen), i)); + draw_channel_actions[i] = ActionManager::register_radio_action (channel_actions, draw_channel_group, buf, ch, sigc::bind (sigc::mem_fun (*this, &EditingContext::draw_channel_chosen), i)); } ActionManager::set_sensitive (_midi_actions, false); @@ -573,142 +572,33 @@ EditingContext::midi_action (void (MidiView::*method)()) } } -void -EditingContext::grid_type_selection_done (GridType gridtype) -{ - RefPtr ract = grid_type_action (gridtype); - if (ract && ract->get_active()) { /*radio-action is already set*/ - set_grid_to(gridtype); /*so we must set internal state here*/ - } else { - ract->set_active (); - } -} - -void -EditingContext::snap_mode_selection_done (SnapMode mode) -{ - RefPtr ract = snap_mode_action (mode); - - if (ract) { - ract->set_active (true); - } -} - -RefPtr -EditingContext::grid_type_action (GridType type) -{ - const char* action = 0; - RefPtr act; - - switch (type) { - case Editing::GridTypeBeatDiv32: - action = "grid-type-thirtyseconds"; - break; - case Editing::GridTypeBeatDiv28: - action = "grid-type-twentyeighths"; - break; - case Editing::GridTypeBeatDiv24: - action = "grid-type-twentyfourths"; - break; - case Editing::GridTypeBeatDiv20: - action = "grid-type-twentieths"; - break; - case Editing::GridTypeBeatDiv16: - action = "grid-type-asixteenthbeat"; - break; - case Editing::GridTypeBeatDiv14: - action = "grid-type-fourteenths"; - break; - case Editing::GridTypeBeatDiv12: - action = "grid-type-twelfths"; - break; - case Editing::GridTypeBeatDiv10: - action = "grid-type-tenths"; - break; - case Editing::GridTypeBeatDiv8: - action = "grid-type-eighths"; - break; - case Editing::GridTypeBeatDiv7: - action = "grid-type-sevenths"; - break; - case Editing::GridTypeBeatDiv6: - action = "grid-type-sixths"; - break; - case Editing::GridTypeBeatDiv5: - action = "grid-type-fifths"; - break; - case Editing::GridTypeBeatDiv4: - action = "grid-type-quarters"; - break; - case Editing::GridTypeBeatDiv3: - action = "grid-type-thirds"; - break; - case Editing::GridTypeBeatDiv2: - action = "grid-type-halves"; - break; - case Editing::GridTypeBeat: - action = "grid-type-beat"; - break; - case Editing::GridTypeBar: - action = "grid-type-bar"; - break; - case Editing::GridTypeNone: - action = "grid-type-none"; - break; - case Editing::GridTypeTimecode: - action = "grid-type-timecode"; - break; - case Editing::GridTypeCDFrame: - action = "grid-type-cdframe"; - break; - case Editing::GridTypeMinSec: - action = "grid-type-minsec"; - break; - default: - fatal << string_compose (_("programming error: %1: %2"), "Editor: impossible snap-to type", (int) type) << endmsg; - abort(); /*NOTREACHED*/ - } - - std::string action_name = editor_name() + X_("Snap"); - act = ActionManager::get_action (action_name.c_str(), action); - - if (act) { - RefPtr ract = RefPtr::cast_dynamic(act); - return ract; - - } else { - error << string_compose (_("programming error: %1"), "EditingContext::grid_type_chosen could not find action to match type.") << endmsg; - return RefPtr(); - } -} - void EditingContext::next_grid_choice () { - switch (_grid_type) { + switch (grid_type()) { case Editing::GridTypeBeatDiv32: - set_grid_to (Editing::GridTypeNone); + set_grid_type (Editing::GridTypeNone); break; case Editing::GridTypeBeatDiv16: - set_grid_to (Editing::GridTypeBeatDiv32); + set_grid_type (Editing::GridTypeBeatDiv32); break; case Editing::GridTypeBeatDiv8: - set_grid_to (Editing::GridTypeBeatDiv16); + set_grid_type (Editing::GridTypeBeatDiv16); break; case Editing::GridTypeBeatDiv4: - set_grid_to (Editing::GridTypeBeatDiv8); + set_grid_type (Editing::GridTypeBeatDiv8); break; case Editing::GridTypeBeatDiv2: - set_grid_to (Editing::GridTypeBeatDiv4); + set_grid_type (Editing::GridTypeBeatDiv4); break; case Editing::GridTypeBeat: - set_grid_to (Editing::GridTypeBeatDiv2); + set_grid_type (Editing::GridTypeBeatDiv2); break; case Editing::GridTypeBar: - set_grid_to (Editing::GridTypeBeat); + set_grid_type (Editing::GridTypeBeat); break; case Editing::GridTypeNone: - set_grid_to (Editing::GridTypeBar); + set_grid_type (Editing::GridTypeBar); break; case Editing::GridTypeBeatDiv3: case Editing::GridTypeBeatDiv6: @@ -730,30 +620,30 @@ EditingContext::next_grid_choice () void EditingContext::prev_grid_choice () { - switch (_grid_type) { + switch (grid_type()) { case Editing::GridTypeBeatDiv32: - set_grid_to (Editing::GridTypeBeatDiv16); + set_grid_type (Editing::GridTypeBeatDiv16); break; case Editing::GridTypeBeatDiv16: - set_grid_to (Editing::GridTypeBeatDiv8); + set_grid_type (Editing::GridTypeBeatDiv8); break; case Editing::GridTypeBeatDiv8: - set_grid_to (Editing::GridTypeBeatDiv4); + set_grid_type (Editing::GridTypeBeatDiv4); break; case Editing::GridTypeBeatDiv4: - set_grid_to (Editing::GridTypeBeatDiv2); + set_grid_type (Editing::GridTypeBeatDiv2); break; case Editing::GridTypeBeatDiv2: - set_grid_to (Editing::GridTypeBeat); + set_grid_type (Editing::GridTypeBeat); break; case Editing::GridTypeBeat: - set_grid_to (Editing::GridTypeBar); + set_grid_type (Editing::GridTypeBar); break; case Editing::GridTypeBar: - set_grid_to (Editing::GridTypeNone); + set_grid_type (Editing::GridTypeNone); break; case Editing::GridTypeNone: - set_grid_to (Editing::GridTypeBeatDiv32); + set_grid_type (Editing::GridTypeBeatDiv32); break; case Editing::GridTypeBeatDiv3: case Editing::GridTypeBeatDiv6: @@ -773,18 +663,55 @@ EditingContext::prev_grid_choice () } void -EditingContext::grid_type_chosen (GridType type) +EditingContext::grid_type_chosen (GridType gt) { /* this is driven by a toggle on a radio group, and so is invoked twice, once for the item that became inactive and once for the one that became active. */ - RefPtr ract = grid_type_action (type); + auto ti = grid_actions.find (gt); + assert (ti != grid_actions.end()); - if (ract && ract->get_active()) { - set_grid_to (type); + std::cerr << "gt chosen, type " << enum_2_string (gt) << " active ? " << ti->second->get_active () << std::endl; + + if (!ti->second->get_active()) { + return; } + + unsigned int grid_ind = (unsigned int) gt; + + if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) { + internal_grid_type = gt; + } else { + pre_internal_grid_type = gt; + } + + grid_type_selector.set_active (grid_type_strings[grid_ind]); + + if (UIConfiguration::instance().get_show_grids_ruler()) { + show_rulers_for_grid (); + } + + instant_save (); + + const bool grid_is_musical = grid_musical (); + + if (grid_is_musical) { + compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples()); + update_tempo_based_rulers (); + } else if (current_mouse_mode () == Editing::MouseGrid) { + Glib::RefPtr ract = Glib::RefPtr::cast_dynamic (get_mouse_mode_action (Editing::MouseObject)); + ract->set_active (true); + } + + get_mouse_mode_action (Editing::MouseGrid)->set_sensitive (grid_is_musical); + + mark_region_boundary_cache_dirty (); + + redisplay_grid (false); + + SnapChanged (); /* EMIT SIGNAL */ } void @@ -795,12 +722,24 @@ EditingContext::draw_length_chosen (GridType type) active. */ - RefPtr ract = draw_length_action (type); + RefPtr ract = draw_length_actions[type]; - if (ract && ract->get_active()) { - /* It doesn't really matter which EditingContext executes this */ - set_draw_length_to (type); + if (!ract->get_active()) { + return; } + + if (!grid_type_is_musical (type) ) { // is this is sensible sanity check ? + set_draw_length (DRAW_LEN_AUTO); + return; + } + + if (DRAW_LEN_AUTO == (unsigned int) type) { + draw_length_selector.set_active (_("Auto")); + } else { + draw_length_selector.set_active (grid_type_strings[(unsigned int) type]); + } + + instant_save (); } void @@ -811,12 +750,27 @@ EditingContext::draw_velocity_chosen (int v) active. */ - RefPtr ract = draw_velocity_action (v); + RefPtr ract; - if (ract && ract->get_active()) { - /* It doesn't really matter which EditingContext executes this */ - set_draw_velocity_to (v); + if (v == DRAW_VEL_AUTO) { + ract = draw_velocity_actions[DRAW_VEL_AUTO]; + } else { + ract = draw_velocity_actions[std::max (std::min (v, 127), 0)]; } + + if (!ract->get_active()) { + return; + } + + if (DRAW_VEL_AUTO == v) { + draw_velocity_selector.set_active (_("Auto")); + } else { + char buf[64]; + snprintf (buf, sizeof (buf), "%d", v); + draw_velocity_selector.set_text (buf); + } + + instant_save (); } void @@ -827,51 +781,33 @@ EditingContext::draw_channel_chosen (int c) active. */ - RefPtr ract = draw_channel_action (c); + RefPtr ract; - if (ract && ract->get_active()) { - /* It doesn't really matter which EditingContext executes this */ - set_draw_channel_to (c); - } -} - -RefPtr -EditingContext::snap_mode_action (SnapMode mode) -{ - const char* action = 0; - RefPtr act; - - switch (mode) { - case Editing::SnapOff: - action = X_("snap-off"); - break; - case Editing::SnapNormal: - action = X_("snap-normal"); - break; - case Editing::SnapMagnetic: - action = X_("snap-magnetic"); - break; - default: - fatal << string_compose (_("programming error: %1: %2"), "Editor: impossible snap mode type", (int) mode) << endmsg; - abort(); /*NOTREACHED*/ + if (c == DRAW_CHAN_AUTO) { + ract = draw_channel_actions[DRAW_CHAN_AUTO]; + } else { + ract = draw_channel_actions[std::max (std::min (c, 5), 0)]; } - act = ActionManager::get_action ((_name + X_("Editing")).c_str(), action); - - if (act) { - RefPtr ract = RefPtr::cast_dynamic(act); - return ract; - - } else { - error << string_compose (_("programming error: %1: %2"), "EditingContext::snap_mode_chosen could not find action to match mode.", action) << endmsg; - return RefPtr (); + if (!ract->get_active()) { + return; } + + if (DRAW_CHAN_AUTO == c) { + draw_channel_selector.set_active (_("Auto")); + } else { + char buf[64]; + snprintf (buf, sizeof (buf), "%d", c+1 ); + draw_channel_selector.set_active (buf); + } + + instant_save (); } void EditingContext::cycle_snap_mode () { - switch (_snap_mode) { + switch (snap_mode()) { case SnapOff: case SnapNormal: set_snap_mode (SnapMagnetic); @@ -894,41 +830,80 @@ EditingContext::snap_mode_chosen (SnapMode mode) mode = SnapMagnetic; } - RefPtr ract = snap_mode_action (mode); + auto si = snap_mode_actions.find (mode); + assert (si != snap_mode_actions.end()); - if (ract && ract->get_active()) { - set_snap_mode (mode); + if (!si->second->get_active()) { + return; } + + if (internal_editing()) { + internal_snap_mode = mode; + } else { + pre_internal_snap_mode = mode; + } + + if (mode == SnapOff) { + snap_mode_button.set_active_state (Gtkmm2ext::Off); + } else { + snap_mode_button.set_active_state (Gtkmm2ext::ExplicitActive); + } + + instant_save (); } GridType EditingContext::grid_type() const { - return _grid_type; + for (auto const & [grid_type,action] : grid_actions) { + if (action->get_active()) { + return grid_type; + } + } + + return Editing::GridTypeNone; } GridType EditingContext::draw_length() const { - return _draw_length; + for (auto const & [len,action] : draw_length_actions) { + if (action->get_active()) { + return len; + } + } + + return GridTypeBeat; } int EditingContext::draw_velocity() const { - return _draw_velocity; + for (auto const & [vel,action] : draw_velocity_actions) { + if (action->get_active()) { + return vel; + } + } + + return 64; } int EditingContext::draw_channel() const { - return _draw_channel; + for (auto const & [chn,action] : draw_channel_actions) { + if (action->get_active()) { + return chn; + } + } + + return 0; } bool EditingContext::grid_musical() const { - return grid_type_is_musical (_grid_type); + return grid_type_is_musical (grid_type()); } bool @@ -965,286 +940,51 @@ EditingContext::grid_type_is_musical(GridType gt) const SnapMode EditingContext::snap_mode() const { - return _snap_mode; -} - -void -EditingContext::set_draw_length_to (GridType gt) -{ - if ( !grid_type_is_musical(gt) ) { //range-check - gt = DRAW_LEN_AUTO; + for (auto const & [mode,action] : snap_mode_actions) { + if (action->get_active()) { + return mode; + } } - _draw_length = gt; - DrawLengthChanged (); /* EMIT SIGNAL */ + return Editing::SnapOff; } void -EditingContext::draw_length_changed () +EditingContext::set_draw_length (GridType gt) { - if (DRAW_LEN_AUTO == _draw_length) { - draw_length_selector.set_text (_("Auto")); - return; - } - - unsigned int grid_index = (unsigned int) _draw_length; - std::string str = grid_type_strings[grid_index]; - draw_length_selector.set_text (str); - instant_save (); + draw_length_actions[gt]->set_active (true); } void -EditingContext::set_draw_velocity_to (int v) +EditingContext::set_draw_velocity (int v) { - if (v < 0 || v > 127) { - v = DRAW_VEL_AUTO; - } - - _draw_velocity = v; - DrawVelocityChanged (); /* EMIT SIGNAL */ -} - -void -EditingContext::draw_velocity_changed () -{ - if (DRAW_VEL_AUTO == _draw_velocity) { - draw_velocity_selector.set_text (_("Auto")); - return; - } - - char buf[64]; - snprintf (buf, sizeof (buf), "%d", _draw_velocity); - draw_velocity_selector.set_text (buf); - instant_save (); -} - -void -EditingContext::set_draw_channel_to (int c) -{ - if (c < 0 || c > 15) { //range-check midi channel - c = DRAW_CHAN_AUTO; - } - - _draw_channel = c; - DrawChannelChanged (); /* EMIT SIGNAL */ -} - -void -EditingContext::draw_channel_changed () -{ - if (DRAW_CHAN_AUTO == _draw_channel) { - draw_channel_selector.set_text (_("Auto")); - return; - } - - char buf[64]; - snprintf (buf, sizeof (buf), "%d", _draw_channel+1 ); - draw_channel_selector.set_text (buf); - instant_save (); -} - -void -EditingContext::set_grid_to (GridType gt) -{ - unsigned int grid_ind = (unsigned int)gt; - - if (internal_editing() && UIConfiguration::instance().get_grid_follows_internal()) { - internal_grid_type = gt; + if (v == DRAW_VEL_AUTO) { + draw_velocity_actions[v]->set_active (true); } else { - pre_internal_grid_type = gt; + draw_velocity_actions[std::max (std::min (v, 127), 0)]->set_active (true); } +} - bool grid_type_changed = true; - if ( grid_type_is_musical(_grid_type) && grid_type_is_musical(gt)) { - grid_type_changed = false; +void +EditingContext::set_draw_channel (int c) +{ + if (c == DRAW_CHAN_AUTO) { + draw_channel_actions[c]->set_active (true); + } else { + draw_channel_actions[std::max (std::min (c, 15), 0)]->set_active (true); } +} - _grid_type = gt; - - if (grid_ind > grid_type_strings.size() - 1) { - grid_ind = 0; - _grid_type = (GridType)grid_ind; - } - - std::string str = grid_type_strings[grid_ind]; - - if (str != grid_type_selector.get_text()) { - grid_type_selector.set_text (str); - } - - if (grid_type_changed && UIConfiguration::instance().get_show_grids_ruler()) { - show_rulers_for_grid (); - } - - instant_save (); - - const bool grid_is_musical = grid_musical (); - - if (grid_is_musical) { - compute_bbt_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples()); - update_tempo_based_rulers (); - } else if (current_mouse_mode () == Editing::MouseGrid) { - Glib::RefPtr ract = Glib::RefPtr::cast_dynamic (get_mouse_mode_action (Editing::MouseObject)); - ract->set_active (true); - } - - get_mouse_mode_action (Editing::MouseGrid)->set_sensitive (grid_is_musical); - - mark_region_boundary_cache_dirty (); - - redisplay_grid (false); - - SnapChanged (); /* EMIT SIGNAL */ +void +EditingContext::set_grid_type (GridType gt) +{ + grid_actions[gt]->set_active (true); } void EditingContext::set_snap_mode (SnapMode mode) { - if (internal_editing()) { - internal_snap_mode = mode; - } else { - pre_internal_snap_mode = mode; - } - - _snap_mode = mode; - - if (_snap_mode == SnapOff) { - snap_mode_button.set_active_state (Gtkmm2ext::Off); - } else { - snap_mode_button.set_active_state (Gtkmm2ext::ExplicitActive); - } - - instant_save (); -} - - -RefPtr -EditingContext::draw_velocity_action (int v) -{ - char buf[64]; - const char* action = 0; - RefPtr act; - - if (DRAW_VEL_AUTO == v) { - action = "draw-velocity-auto"; - } else if (v >= 1 && v <= 127) { - snprintf (buf, sizeof (buf), X_("draw-velocity-%d"), v); //we don't allow drawing a velocity 0; some synths use that as note-off - action = buf; - } - - act = ActionManager::get_action ((_name + X_("DrawVelocity")).c_str(), action); - if (act) { - RefPtr ract = RefPtr::cast_dynamic(act); - return ract; - } else { - error << string_compose (_("programming error: %1"), "EditingContext::draw_velocity_action could not find action to match velocity.") << endmsg; - return RefPtr(); - } -} - -RefPtr -EditingContext::draw_channel_action (int c) -{ - char buf[64]; - const char* action = 0; - RefPtr act; - - if (c==DRAW_CHAN_AUTO) { - action = "draw-channel-auto"; - } else if (c>=0 && c<=15) { - snprintf (buf, sizeof (buf), X_("draw-channel-%d"), c+1); - action = buf; - } - - act = ActionManager::get_action ((_name + X_("DrawChannel")).c_str(), action); - if (act) { - RefPtr ract = RefPtr::cast_dynamic(act); - return ract; - } else { - error << string_compose (_("programming error: %1"), "EditingContext::draw_channel_action could not find action to match channel.") << endmsg; - return RefPtr(); - } -} - -RefPtr -EditingContext::draw_length_action (GridType type) -{ - const char* action = 0; - RefPtr act; - - switch (type) { - case Editing::GridTypeBeatDiv32: - action = "draw-length-thirtyseconds"; - break; - case Editing::GridTypeBeatDiv28: - action = "draw-length-twentyeighths"; - break; - case Editing::GridTypeBeatDiv24: - action = "draw-length-twentyfourths"; - break; - case Editing::GridTypeBeatDiv20: - action = "draw-length-twentieths"; - break; - case Editing::GridTypeBeatDiv16: - action = "draw-length-asixteenthbeat"; - break; - case Editing::GridTypeBeatDiv14: - action = "draw-length-fourteenths"; - break; - case Editing::GridTypeBeatDiv12: - action = "draw-length-twelfths"; - break; - case Editing::GridTypeBeatDiv10: - action = "draw-length-tenths"; - break; - case Editing::GridTypeBeatDiv8: - action = "draw-length-eighths"; - break; - case Editing::GridTypeBeatDiv7: - action = "draw-length-sevenths"; - break; - case Editing::GridTypeBeatDiv6: - action = "draw-length-sixths"; - break; - case Editing::GridTypeBeatDiv5: - action = "draw-length-fifths"; - break; - case Editing::GridTypeBeatDiv4: - action = "draw-length-quarters"; - break; - case Editing::GridTypeBeatDiv3: - action = "draw-length-thirds"; - break; - case Editing::GridTypeBeatDiv2: - action = "draw-length-halves"; - break; - case Editing::GridTypeBeat: - action = "draw-length-beat"; - break; - case Editing::GridTypeBar: - action = "draw-length-bar"; - break; - case Editing::GridTypeNone: - action = "draw-length-auto"; - break; - case Editing::GridTypeTimecode: - case Editing::GridTypeCDFrame: - case Editing::GridTypeMinSec: - default: - fatal << string_compose (_("programming error: %1: %2"), "Editor: impossible grid length type", (int) type) << endmsg; - abort(); /*NOTREACHED*/ - } - - act = ActionManager::get_action ((_name + X_("DrawLength")).c_str(), action); - - if (act) { - RefPtr ract = RefPtr::cast_dynamic(act); - return ract; - - } else { - error << string_compose (_("programming error: %1"), "EditingContext::draw_length_chosen could not find action to match type.") << endmsg; - return RefPtr(); - } + snap_mode_actions[mode]->set_active (true);; } void @@ -1253,54 +993,51 @@ EditingContext::build_grid_type_menu () using namespace Menu_Helpers; /* there's no Grid, but if Snap is engaged, the Snap preferences will be applied */ - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeNone], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeNone))); - grid_type_selector.add_menu_elem(SeparatorElem()); + grid_type_selector.append (grid_actions[GridTypeNone]); + grid_type_selector.add_separator (); /* musical grid: bars, quarter-notes, etc */ - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeBar], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBar))); - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeBeat], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeat))); - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv2], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv2))); - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv4], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv4))); - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv8], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv8))); - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv16], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv16))); - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeBeatDiv32], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv32))); + grid_type_selector.append (grid_actions[GridTypeBar]); + grid_type_selector.append (grid_actions[GridTypeBeat]); + grid_type_selector.append (grid_actions[GridTypeBeatDiv4]); + grid_type_selector.append (grid_actions[GridTypeBeatDiv8]); + grid_type_selector.append (grid_actions[GridTypeBeatDiv16]); + grid_type_selector.append (grid_actions[GridTypeBeatDiv32]); /* triplet grid */ - grid_type_selector.add_menu_elem(SeparatorElem()); - Gtk::Menu *_triplet_menu = manage (new Menu); - MenuList& triplet_items (_triplet_menu->items()); + grid_type_selector.add_separator ();; + Gtk::Menu *triplet_menu = manage (new Menu); { - triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv3], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv3))); - triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv6], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv6))); - triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv12], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv12))); - triplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv24], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv24))); + grid_type_selector.append (*triplet_menu, grid_actions[GridTypeBeatDiv3]); + grid_type_selector.append (*triplet_menu, grid_actions[GridTypeBeatDiv6]); + grid_type_selector.append (*triplet_menu, grid_actions[GridTypeBeatDiv12]); + grid_type_selector.append (*triplet_menu, grid_actions[GridTypeBeatDiv24]); } - grid_type_selector.add_menu_elem (Menu_Helpers::MenuElem (_("Triplets"), *_triplet_menu)); + grid_type_selector.add_menu_elem (Menu_Helpers::MenuElem (_("Triplets"), *triplet_menu)); /* quintuplet grid */ - Gtk::Menu *_quintuplet_menu = manage (new Menu); - MenuList& quintuplet_items (_quintuplet_menu->items()); + Gtk::Menu *quintuplet_menu = manage (new Menu); { - quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv5], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv5))); - quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv10], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv10))); - quintuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv20], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv20))); + grid_type_selector.append (*quintuplet_menu, grid_actions[GridTypeBeatDiv5]); + grid_type_selector.append (*quintuplet_menu, grid_actions[GridTypeBeatDiv10]); + grid_type_selector.append (*quintuplet_menu, grid_actions[GridTypeBeatDiv20]); } - grid_type_selector.add_menu_elem (Menu_Helpers::MenuElem (_("Quintuplets"), *_quintuplet_menu)); + + grid_type_selector.add_menu_elem (Menu_Helpers::MenuElem (_("Quintuplets"), *quintuplet_menu)); /* septuplet grid */ - Gtk::Menu *_septuplet_menu = manage (new Menu); - MenuList& septuplet_items (_septuplet_menu->items()); + Gtk::Menu *septuplet_menu = manage (new Menu); { - septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv7], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv7))); - septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv14], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv14))); - septuplet_items.push_back (MenuElem (grid_type_strings[(int)GridTypeBeatDiv28], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeBeatDiv28))); + grid_type_selector.append (*septuplet_menu, grid_actions[GridTypeBeatDiv7]); + grid_type_selector.append (*septuplet_menu, grid_actions[GridTypeBeatDiv14]); + grid_type_selector.append (*septuplet_menu, grid_actions[GridTypeBeatDiv28]); } - grid_type_selector.add_menu_elem (Menu_Helpers::MenuElem (_("Septuplets"), *_septuplet_menu)); + grid_type_selector.add_menu_elem (Menu_Helpers::MenuElem (_("Septuplets"), *septuplet_menu)); - grid_type_selector.add_menu_elem(SeparatorElem()); - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeTimecode], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeTimecode))); - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeMinSec], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeMinSec))); - grid_type_selector.add_menu_elem (MenuElem (grid_type_strings[(int)GridTypeCDFrame], sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_selection_done), (GridType) GridTypeCDFrame))); + grid_type_selector.add_separator (); + grid_type_selector.append (grid_actions[GridTypeTimecode]); + grid_type_selector.append (grid_actions[GridTypeMinSec]); + grid_type_selector.append (grid_actions[GridTypeCDFrame]); grid_type_selector.set_sizing_texts (grid_type_strings); } @@ -1312,7 +1049,8 @@ EditingContext::build_draw_midi_menus () /* Note-Length when drawing */ - std::vector grids ({GridTypeBeat, + std::vector grids ({ + GridTypeBeat, GridTypeBeatDiv2, GridTypeBeatDiv4, GridTypeBeatDiv8, @@ -1321,26 +1059,22 @@ EditingContext::build_draw_midi_menus () GridTypeNone}); std::vector draw_grid_type_strings; - draw_length_action (GridTypeNone)->set_active(); /* default */ - for (auto & g : grids) { - Glib::RefPtr ract = draw_length_action (g); + Glib::RefPtr ract = draw_length_actions[g]; draw_length_selector.append (ract); draw_grid_type_strings.push_back (ract->get_short_label()); } - grid_type_selector.set_sizing_texts (draw_grid_type_strings); + draw_length_selector.set_sizing_texts (draw_grid_type_strings); /* Note-Velocity when drawing */ std::vector preselected_velocities ({8,32,64,82,100,127, DRAW_VEL_AUTO}); std::vector draw_velocity_strings; - draw_velocity_action (DRAW_VEL_AUTO)->set_active (); /* default */ - for (auto & v : preselected_velocities) { - Glib::RefPtr ract = draw_velocity_action (v); + Glib::RefPtr ract = draw_velocity_actions[v]; assert (ract); draw_velocity_selector.append (ract); draw_velocity_strings.push_back (ract->get_short_label()); @@ -1352,13 +1086,17 @@ EditingContext::build_draw_midi_menus () std::vector draw_channel_strings; for (auto & c : possible_channels) { - Glib::RefPtr ract = draw_channel_action (c); + Glib::RefPtr ract = draw_channel_actions[c]; assert (ract); draw_channel_selector.append (ract); draw_channel_strings.push_back (ract->get_short_label()); } draw_channel_selector.set_sizing_texts (draw_channel_strings); + + draw_channel_chosen (draw_channel()); + draw_length_chosen (draw_length()); + draw_velocity_chosen (draw_velocity()); } bool @@ -1382,11 +1120,11 @@ EditingContext::time_domain () const /* Probably never reached */ - if (_snap_mode == SnapOff) { + if (snap_mode() == SnapOff) { return Temporal::AudioTime; } - switch (_grid_type) { + switch (grid_type()) { case GridTypeNone: /* fallthrough */ case GridTypeMinSec: @@ -1416,11 +1154,14 @@ EditingContext::toggle_follow_playhead () void EditingContext::set_follow_playhead (bool yn, bool catch_up) { - if (_follow_playhead != yn) { - if ((_follow_playhead = yn) == true && catch_up) { + assert (follow_playhead_action); + if (follow_playhead() != yn) { + follow_playhead_action->set_active (yn); + if (yn && catch_up) { /* catch up */ reset_x_origin_to_follow_playhead (); } + std::cerr << editor_name() << " SFP instant save\n"; instant_save (); } } @@ -1468,12 +1209,12 @@ EditingContext::snap_to_with_modifier (timepos_t& start, GdkEvent const * event, } if (ArdourKeyboard::indicates_snap (event->button.state)) { - if (_snap_mode == SnapOff) { + if (snap_mode() == SnapOff) { snap_to_internal (start, direction, pref, ensure_snap); } } else { - if (_snap_mode != SnapOff) { + if (snap_mode() != SnapOff) { snap_to_internal (start, direction, pref); } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) { /* SnapOff, but we pressed the snap_delta modifier */ @@ -1485,7 +1226,7 @@ EditingContext::snap_to_with_modifier (timepos_t& start, GdkEvent const * event, void EditingContext::snap_to (timepos_t& start, Temporal::RoundMode direction, SnapPref pref, bool ensure_snap) const { - if (!_session || (_snap_mode == SnapOff && !ensure_snap)) { + if (!_session || (snap_mode() == SnapOff && !ensure_snap)) { return; } @@ -1495,7 +1236,7 @@ EditingContext::snap_to (timepos_t& start, Temporal::RoundMode direction, SnapPr timepos_t EditingContext::snap_to_bbt (timepos_t const & presnap, Temporal::RoundMode direction, SnapPref gpref) const { - return snap_to_bbt_via_grid (presnap, direction, gpref, _grid_type); + return snap_to_bbt_via_grid (presnap, direction, gpref, grid_type()); } timepos_t @@ -1522,7 +1263,7 @@ EditingContext::snap_to_bbt_via_grid (timepos_t const & presnap, Temporal::Round */ int divisor; - switch (_grid_type) { + switch (grid_type) { case GridTypeBeatDiv3: case GridTypeBeatDiv6: case GridTypeBeatDiv12: @@ -1738,10 +1479,11 @@ EditingContext::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper) /* Now that we know how fine a grid (Ruler) is allowable on this screen, limit it to the coarseness selected by the user */ /* note: GridType and RulerScale are not the same enums, so it's not a simple mathematical operation */ int suggested_scale = (int) bbt_ruler_scale; - int divs = get_grid_music_divisions(_grid_type, 0); - if (_grid_type == GridTypeBar) { + GridType gt (grid_type()); + int divs = get_grid_music_divisions(gt, 0); + if (gt == GridTypeBar) { suggested_scale = std::min(suggested_scale, (int) bbt_show_1); - } else if (_grid_type == GridTypeBeat) { + } else if (gt == GridTypeBeat) { suggested_scale = std::min(suggested_scale, (int) bbt_show_quarters); } else if ( divs < 4 ) { suggested_scale = std::min(suggested_scale, (int) bbt_show_eighths); @@ -2338,7 +2080,7 @@ EditingContext::on_velocity_scroll_event (GdkEventScroll* ev) default: return false; } - set_draw_velocity_to(v); + set_draw_velocity (v); return true; } @@ -2355,39 +2097,15 @@ EditingContext::set_common_editing_state (XMLNode const & node) GridType grid_type; if (!node.get_property ("grid-type", grid_type)) { - grid_type = _grid_type; + grid_type = GridTypeNone; } - grid_type_selection_done (grid_type); - - GridType draw_length; - if (!node.get_property ("draw-length", draw_length)) { - draw_length = _draw_length; - } - draw_length_chosen (draw_length); - - int draw_vel; - if (!node.get_property ("draw-velocity", draw_vel)) { - draw_vel = _draw_velocity; - } - draw_velocity_chosen (draw_vel); - - int draw_chan; - if (!node.get_property ("draw-channel", draw_chan)) { - draw_chan = DRAW_CHAN_AUTO; - } - draw_channel_chosen (draw_chan); + set_grid_type (grid_type); SnapMode sm; - if (node.get_property ("snap-mode", sm)) { - snap_mode_selection_done(sm); - /* 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 (sm); - } else { - set_snap_mode (_snap_mode); + if (!node.get_property ("snap-mode", sm)) { + sm = SnapOff; } + set_snap_mode (sm); node.get_property ("internal-grid-type", internal_grid_type); node.get_property ("internal-snap-mode", internal_snap_mode); @@ -2415,15 +2133,12 @@ void EditingContext::get_common_editing_state (XMLNode& node) const { node.set_property ("zoom", samples_per_pixel); - node.set_property ("grid-type", _grid_type); - node.set_property ("snap-mode", _snap_mode); + node.set_property ("grid-type", grid_type()); + node.set_property ("snap-mode", snap_mode()); node.set_property ("internal-grid-type", internal_grid_type); node.set_property ("internal-snap-mode", internal_snap_mode); node.set_property ("pre-internal-grid-type", pre_internal_grid_type); node.set_property ("pre-internal-snap-mode", pre_internal_snap_mode); - node.set_property ("draw-length", _draw_length); - node.set_property ("draw-velocity", _draw_velocity); - node.set_property ("draw-channel", _draw_channel); node.set_property ("left-frame", _leftmost_sample); } @@ -2538,10 +2253,6 @@ EditingContext::reset_zoom (samplecnt_t spp) pending_visual_change.add (VisualChange::ZoomLevel); pending_visual_change.samples_per_pixel = spp; - if (spp == 0.0) { - std::cerr << "spp set to zero\n"; - PBD::stacktrace (std::cerr, 12); - } ensure_visual_change_idle_handler (); } @@ -2565,18 +2276,20 @@ EditingContext::reg_sens (RefPtr group, char const * name, char con return act; } -void +Glib::RefPtr EditingContext::toggle_reg_sens (RefPtr group, char const * name, char const * label, sigc::slot slot) { - RefPtr act = ActionManager::register_toggle_action (group, name, label, slot); + RefPtr act = ActionManager::register_toggle_action (group, name, label, slot); ActionManager::session_sensitive_actions.push_back (act); + return act; } -void +Glib::RefPtr EditingContext::radio_reg_sens (RefPtr action_group, RadioAction::Group& radio_group, char const * name, char const * label, sigc::slot slot) { - RefPtr act = ActionManager::register_radio_action (action_group, radio_group, name, label, slot); + RefPtr act = ActionManager::register_radio_action (action_group, radio_group, name, label, slot); ActionManager::session_sensitive_actions.push_back (act); + return act; } void @@ -2655,7 +2368,7 @@ EditingContext::get_grid_type_as_beats (bool& success, timepos_t const & positio { success = true; - int32_t const divisions = get_grid_beat_divisions (_grid_type); + int32_t const divisions = get_grid_beat_divisions (grid_type()); /* Beat (+1), and Bar (-1) are handled below */ if (divisions > 1) { /* grid divisions are divisions of a 1/4 note */ @@ -2664,7 +2377,7 @@ EditingContext::get_grid_type_as_beats (bool& success, timepos_t const & positio TempoMap::SharedPtr tmap (TempoMap::use()); - switch (_grid_type) { + switch (grid_type()) { case GridTypeBar: if (_session) { const Meter& m = tmap->meter_at (position); @@ -3358,18 +3071,20 @@ EditingContext::maybe_draw_grid_lines (ArdourCanvas::Container* group) if (!grid_lines) { grid_lines = new GridLines (*this, group, ArdourCanvas::LineSet::Vertical); + } grid_marks.clear(); samplepos_t rightmost_sample = _leftmost_sample + current_page_samples(); + GridType gt (grid_type()); if (grid_musical()) { - metric_get_bbt (grid_marks, _leftmost_sample, rightmost_sample, 12); - } else if (_grid_type== GridTypeTimecode) { - metric_get_timecode (grid_marks, _leftmost_sample, rightmost_sample, 12); - } else if (_grid_type == GridTypeCDFrame) { + metric_get_bbt (grid_marks, _leftmost_sample, rightmost_sample, 12); + } else if (gt == GridTypeTimecode) { + metric_get_timecode (grid_marks, _leftmost_sample, rightmost_sample, 12); + } else if (gt == GridTypeCDFrame) { metric_get_minsec (grid_marks, _leftmost_sample, rightmost_sample, 12); - } else if (_grid_type == GridTypeMinSec) { + } else if (gt == GridTypeMinSec) { metric_get_minsec (grid_marks, _leftmost_sample, rightmost_sample, 12); } @@ -3385,15 +3100,8 @@ EditingContext::update_grid () return; } - if (_grid_type == GridTypeNone) { + if (grid_type() == GridTypeNone) { hide_grid_lines (); - } else if (grid_musical()) { -// Temporal::TempoMapPoints grid; -// grid.reserve (4096); -// if (bbt_ruler_scale != bbt_show_many) { -// compute_current_bbt_points (grid, _leftmost_sample, _leftmost_sample + current_page_samples()); -// } - maybe_draw_grid_lines (time_line_group); } else { maybe_draw_grid_lines (time_line_group); } @@ -3525,3 +3233,13 @@ EditingContext::center_screen_internal (samplepos_t sample, float page) reset_x_origin (sample); } + +bool +EditingContext::follow_playhead() const +{ + if (!follow_playhead_action) { + return false; + } + + return follow_playhead_action->get_active (); +} diff --git a/gtk2_ardour/editing_context.h b/gtk2_ardour/editing_context.h index 5ded2331b4..209b11012f 100644 --- a/gtk2_ardour/editing_context.h +++ b/gtk2_ardour/editing_context.h @@ -147,7 +147,7 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, void toggle_follow_playhead (); /** @return true if the editor is following the playhead */ - bool follow_playhead () const { return _follow_playhead; } + bool follow_playhead () const; Temporal::timepos_t get_preferred_edit_position (Editing::EditIgnoreOption eio = Editing::EDIT_IGNORE_NONE, bool use_context_click = false, @@ -258,6 +258,9 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, double timeline_to_canvas (double p) const { return p + _timeline_origin; } double canvas_to_timeline (double p) const { return p - _timeline_origin; } + double visible_canvas_width () const { return _visible_canvas_width; } + double visible_canvas_height () const { return _visible_canvas_height; } + /** computes the timeline position for an event whose coordinates * are in canvas units (pixels, scroll offset included). The time * domain used by the return value will match ::default_time_domain() @@ -291,12 +294,12 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, void cycle_snap_mode (); void next_grid_choice (); void prev_grid_choice (); - void set_grid_to (Editing::GridType); + void set_grid_type (Editing::GridType); void set_snap_mode (Editing::SnapMode); - void set_draw_length_to (Editing::GridType); - void set_draw_velocity_to (int); - void set_draw_channel_to (int); + void set_draw_length (Editing::GridType); + void set_draw_velocity (int); + void set_draw_channel (int); Editing::GridType draw_length () const; int draw_velocity () const; @@ -516,7 +519,7 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, Glib::RefPtr velocity_actions; Glib::RefPtr zoom_actions; - void load_shared_bindings (); + virtual void load_shared_bindings (); Editing::GridType pre_internal_grid_type; Editing::SnapMode pre_internal_snap_mode; @@ -525,19 +528,11 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, static std::vector grid_type_strings; - Glib::RefPtr grid_type_action (Editing::GridType); - Glib::RefPtr snap_mode_action (Editing::SnapMode); - - Glib::RefPtr draw_length_action (Editing::GridType); - Glib::RefPtr draw_velocity_action (int); - Glib::RefPtr draw_channel_action (int); - - Editing::GridType _grid_type; - Editing::SnapMode _snap_mode; - - Editing::GridType _draw_length; - int _draw_velocity; - int _draw_channel; + std::map > grid_actions; + std::map > snap_mode_actions; + std::map > draw_length_actions; + std::map > draw_velocity_actions; + std::map > draw_channel_actions; void draw_channel_chosen (int); void draw_velocity_chosen (int); @@ -561,8 +556,6 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, ArdourWidgets::ArdourDropdown draw_channel_selector; void build_draw_midi_menus (); - void grid_type_selection_done (Editing::GridType); - void snap_mode_selection_done (Editing::SnapMode); void snap_mode_chosen (Editing::SnapMode); void grid_type_chosen (Editing::GridType); @@ -600,7 +593,7 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, EditorCursor* _playhead_cursor; EditorCursor* _snapped_cursor; - bool _follow_playhead; + Glib::RefPtr follow_playhead_action; /* selection process */ @@ -633,7 +626,6 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, bool ensure_snap = false) const = 0; void check_best_snap (Temporal::timepos_t const & presnap, Temporal::timepos_t &test, Temporal::timepos_t &dist, Temporal::timepos_t &best) const; - virtual double visible_canvas_width() const = 0; enum BBTRulerScale { bbt_show_many, @@ -819,8 +811,8 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider, /* protected helper functions to help with registering actions */ static Glib::RefPtr reg_sens (Glib::RefPtr group, char const* name, char const* label, sigc::slot slot); - static void toggle_reg_sens (Glib::RefPtr group, char const* name, char const* label, sigc::slot slot); - static void radio_reg_sens (Glib::RefPtr action_group, Gtk::RadioAction::Group& radio_group, char const* name, char const* label, sigc::slot slot); + static Glib::RefPtr toggle_reg_sens (Glib::RefPtr group, char const* name, char const* label, sigc::slot slot); + static Glib::RefPtr radio_reg_sens (Glib::RefPtr action_group, Gtk::RadioAction::Group& radio_group, char const* name, char const* label, sigc::slot slot); void center_screen_internal (samplepos_t, float); diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 9ee8b6fe01..fb77bf7253 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -379,7 +379,6 @@ Editor::Editor () , _last_cut_copy_source_track (nullptr) , _region_selection_change_updates_region_list (true) , _following_mixer_selection (false) - , _show_touched_automation (false) , _control_point_toggled_on_press (false) , _stepping_axis_view (nullptr) , _main_menu_disabler (nullptr) @@ -1151,7 +1150,7 @@ Editor::map_position_change (samplepos_t sample) return; } - if (_follow_playhead) { + if (follow_playhead()) { center_screen (sample); } @@ -1229,7 +1228,7 @@ Editor::set_session (Session *t) if (!_pianoroll) { // XXX this should really not happen here _pianoroll = new Pianoroll ("editor pianoroll", true); - _pianoroll->viewport().set_size_request (-1, 120); + _pianoroll->get_canvas_viewport()->set_size_request (-1, 120); } _pianoroll->set_session (_session); @@ -2068,6 +2067,8 @@ Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items) void Editor::show_rulers_for_grid () { + GridType gt (grid_type()); + /* show appropriate rulers for this grid setting. */ if (grid_musical()) { ruler_tempo_action->set_active(true); @@ -2079,7 +2080,7 @@ Editor::show_rulers_for_grid () ruler_minsec_action->set_active(false); ruler_samples_action->set_active(false); } - } else if (_grid_type == GridTypeTimecode) { + } else if (gt == GridTypeTimecode) { ruler_timecode_action->set_active(true); if (UIConfiguration::instance().get_rulers_follow_grid()) { @@ -2089,7 +2090,7 @@ Editor::show_rulers_for_grid () ruler_minsec_action->set_active(false); ruler_samples_action->set_active(false); } - } else if (_grid_type == GridTypeMinSec) { + } else if (gt == GridTypeMinSec) { ruler_minsec_action->set_active(true); if (UIConfiguration::instance().get_rulers_follow_grid()) { @@ -2099,7 +2100,7 @@ Editor::show_rulers_for_grid () ruler_timecode_action->set_active(false); ruler_samples_action->set_active(false); } - } else if (_grid_type == GridTypeCDFrame) { + } else if (gt == GridTypeCDFrame) { ruler_minsec_action->set_active(true); if (UIConfiguration::instance().get_rulers_follow_grid()) { @@ -2334,7 +2335,7 @@ Editor::set_state (const XMLNode& node, int version) RefPtr tact; tact = ActionManager::get_toggle_action ((editor_name () + X_("Editing")).c_str(), X_("toggle-follow-playhead")); - yn = _follow_playhead; + yn = follow_playhead(); if (tact->get_active() != yn) { tact->set_active (yn); } @@ -2374,19 +2375,14 @@ Editor::get_state () const node->set_property ("y-origin", vertical_adjustment.get_value ()); node->set_property ("maximised", _maximised); - node->set_property ("follow-playhead", _follow_playhead); + node->set_property ("follow-playhead", follow_playhead()); node->set_property ("stationary-playhead", _stationary_playhead); node->set_property ("mouse-mode", mouse_mode); node->set_property ("join-object-range", smart_mode_action->get_active ()); - Glib::RefPtr tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-mixer")); - node->set_property (X_("show-editor-mixer"), tact->get_active()); - - tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-list")); - node->set_property (X_("show-editor-list"), tact->get_active()); - - tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-editor-props")); - node->set_property (X_("show-editor-props"), tact->get_active()); + node->set_property (X_("show-editor-mixer"), show_editor_mixer_action->get_active()); + node->set_property (X_("show-editor-list"), show_editor_list_action->get_active()); + node->set_property (X_("show-editor-props"), show_editor_props_action->get_active()); node->set_property (X_("editor-list-page"), _the_notebook.get_current_page ()); node->set_property (X_("editor-list-btn1"), _notebook_tab1.index ()); @@ -2399,7 +2395,7 @@ Editor::get_state () const } node->set_property (X_("show-marker-lines"), _show_marker_lines); - node->set_property (X_("show-touched-automation"), _show_touched_automation); + node->set_property (X_("show-touched-automation"), show_touched_automation()); node->add_child_nocopy (selection->get_state ()); @@ -2615,7 +2611,7 @@ Editor::snap_to_grid (timepos_t const & presnap, Temporal::RoundMode direction, ret = snap_to_bbt (presnap, direction, gpref); } - switch (_grid_type) { + switch (grid_type()) { case GridTypeTimecode: ret = snap_to_timecode(presnap, direction, gpref); break; @@ -3458,28 +3454,29 @@ Editor::set_stationary_playhead (bool yn) } bool -Editor::show_touched_automation () const +Editor::show_touched_automation() const { if (!contents().get_mapped()) { return false; } - return _show_touched_automation; + + if (!show_touched_automation_action) { + return false; + } + + return show_touched_automation_action->get_active (); } void Editor::toggle_show_touched_automation () { - RefPtr tact = ActionManager::get_toggle_action (X_("Editor"), X_("show-touched-automation")); - set_show_touched_automation (tact->get_active()); + set_show_touched_automation (show_touched_automation_action->get_active()); } void Editor::set_show_touched_automation (bool yn) { - if (_show_touched_automation == yn) { - return; - } - _show_touched_automation = yn; + show_touched_automation_action->set_active (yn); if (!yn) { RouteTimeAxisView::signal_ctrl_touched (true); } @@ -4546,7 +4543,7 @@ Editor::located () if (_session) { _playhead_cursor->set_position (_session->audible_sample ()); - if (_follow_playhead && !_pending_initial_locate) { + if (follow_playhead() && !_pending_initial_locate) { reset_x_origin_to_follow_playhead (); } update_section_box (); @@ -5330,7 +5327,7 @@ Editor::super_rapid_screen_update () return; } - if (!_follow_playhead || pending_visual_change.being_handled) { + if (!follow_playhead() || pending_visual_change.being_handled) { /* We only do this if we aren't already * handling a visual change (ie if * pending_visual_change.being_handled is @@ -5721,7 +5718,7 @@ Editor::snap_to_internal (timepos_t& start, Temporal::RoundMode direction, SnapP timepos_t best = timepos_t::max (start.time_domain()); // this records the best snap-result we've found so far /* check Grid */ - if ( (_grid_type != GridTypeNone) && (uic.get_snap_target () != SnapTargetOther) ) { + if ( (grid_type() != GridTypeNone) && (uic.get_snap_target () != SnapTargetOther) ) { timepos_t pre (presnap); timepos_t post (snap_to_grid (pre, direction, pref)); check_best_snap (presnap, post, dist, best); diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 485374512f..79d3c1bf55 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -2065,6 +2065,11 @@ private: Glib::RefPtr selection_undo_action; Glib::RefPtr selection_redo_action; + Glib::RefPtr show_editor_mixer_action; + Glib::RefPtr show_editor_list_action; + Glib::RefPtr show_editor_props_action; + Glib::RefPtr show_touched_automation_action; + void history_changed (); Editing::EditPoint _edit_point; @@ -2200,7 +2205,6 @@ private: /* RTAV Automation display option */ void toggle_show_touched_automation (); void set_show_touched_automation (bool); - bool _show_touched_automation; int time_fx (ARDOUR::RegionList&, Temporal::ratio_t ratio, bool pitching, bool fixed_end); void toggle_sound_midi_notes (); diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index b020b27bee..24093e3636 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -187,17 +187,17 @@ Editor::register_actions () /* attachments visibility (editor-mixer-strip, bottom properties, sidebar list) */ - act = ActionManager::register_toggle_action (editor_actions, "show-editor-list", _("Show Editor List"), sigc::mem_fun (*this, &Tabbable::att_right_button_toggled)); - ActionManager::session_sensitive_actions.push_back (act); - right_attachment_button.set_related_action (act); + show_editor_list_action = ActionManager::register_toggle_action (editor_actions, "show-editor-list", _("Show Editor List"), sigc::mem_fun (*this, &Tabbable::att_right_button_toggled)); + ActionManager::session_sensitive_actions.push_back (show_editor_list_action); + right_attachment_button.set_related_action (show_editor_list_action); - act = ActionManager::register_toggle_action (editor_actions, "show-editor-mixer", _("Show Editor Mixer"), sigc::mem_fun (*this, &Tabbable::att_left_button_toggled)); - ActionManager::session_sensitive_actions.push_back (act); - left_attachment_button.set_related_action (act); + show_editor_mixer_action = ActionManager::register_toggle_action (editor_actions, "show-editor-mixer", _("Show Editor Mixer"), sigc::mem_fun (*this, &Tabbable::att_left_button_toggled)); + ActionManager::session_sensitive_actions.push_back (show_editor_mixer_action); + left_attachment_button.set_related_action (show_editor_mixer_action); - act = ActionManager::register_toggle_action (editor_actions, "show-editor-props", _("Show Editor Properties Box"), sigc::mem_fun (*this, &Tabbable::att_bottom_button_toggled)); - ActionManager::session_sensitive_actions.push_back (act); - bottom_attachment_button.set_related_action (act); + show_editor_props_action = ActionManager::register_toggle_action (editor_actions, "show-editor-props", _("Show Editor Properties Box"), sigc::mem_fun (*this, &Tabbable::att_bottom_button_toggled)); + ActionManager::session_sensitive_actions.push_back (show_editor_props_action); + bottom_attachment_button.set_related_action (show_editor_props_action); reg_sens (editor_actions, "playhead-to-next-region-boundary", _("Playhead to Next Region Boundary"), sigc::bind (sigc::mem_fun(*this, &Editor::cursor_to_next_region_boundary), true)); reg_sens (editor_actions, "playhead-to-next-region-boundary-noselection", _("Playhead to Next Region Boundary (No Track Selection)"), sigc::bind (sigc::mem_fun(*this, &Editor::cursor_to_next_region_boundary), false)); @@ -480,7 +480,7 @@ Editor::register_actions () ActionManager::register_toggle_action (editor_actions, "toggle-stationary-playhead", _("Stationary Playhead"), (mem_fun(*this, &Editor::toggle_stationary_playhead))); - ActionManager::register_toggle_action (editor_actions, "show-touched-automation", _("Show Automation Lane on Touch"), (mem_fun(*this, &Editor::toggle_show_touched_automation))); + show_touched_automation_action = ActionManager::register_toggle_action (editor_actions, "show-touched-automation", _("Show Automation Lane on Touch"), (mem_fun(*this, &Editor::toggle_show_touched_automation))); act = reg_sens (editor_actions, "insert-time", _("Insert Time"), (sigc::mem_fun(*this, &Editor::do_insert_time))); ActionManager::track_selection_sensitive_actions.push_back (act); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index d5f014e680..a586e51fe1 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -3106,7 +3106,7 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move) /* only snap to bars. */ - editing_context.set_grid_to (GridTypeBar); + editing_context.set_grid_type (GridTypeBar); editing_context.set_snap_mode (SnapMagnetic); } @@ -3143,7 +3143,7 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred) } /* reinstate old snap setting */ - editing_context.set_grid_to (_old_grid_type); + editing_context.set_grid_type (_old_grid_type); editing_context.set_snap_mode (_old_snap_mode); _editor.commit_tempo_map_edit (map); @@ -3167,7 +3167,7 @@ MeterMarkerDrag::aborted (bool moved) if (moved) { /* reinstate old snap setting */ - editing_context.set_grid_to (_old_grid_type); + editing_context.set_grid_type (_old_grid_type); editing_context.set_snap_mode (_old_snap_mode); // delete the dummy marker we used for visual representation while moving. diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index ff776b8bd5..f3f56aa7b6 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -150,14 +150,14 @@ Editor::mouse_mode_toggled (MouseMode m) this must toggle the actions and not call set_snap_*() directly, otherwise things get out of sync and the combo box stops working. */ if (!UIConfiguration::instance().get_grid_follows_internal()) { - grid_type_action(pre_internal_grid_type)->set_active(true); - snap_mode_action(pre_internal_snap_mode)->set_active(true); + grid_actions[pre_internal_grid_type]->set_active(true); + snap_mode_actions[pre_internal_snap_mode]->set_active(true); } else if (!was_internal && internal_editing()) { - grid_type_action(internal_grid_type)->set_active(true); - snap_mode_action(internal_snap_mode)->set_active(true); + grid_actions[internal_grid_type]->set_active(true); + snap_mode_actions[internal_snap_mode]->set_active(true); } else if (was_internal && !internal_editing()) { - grid_type_action(pre_internal_grid_type)->set_active(true); - snap_mode_action(pre_internal_snap_mode)->set_active(true); + grid_actions[pre_internal_grid_type]->set_active(true); + snap_mode_actions[pre_internal_snap_mode]->set_active(true); } instant_save (); @@ -2108,7 +2108,7 @@ void Editor::mouse_brush_insert_region (RegionView* rv, timepos_t const & pos) { /* no brushing without a useful quantize setting */ - if (_grid_type == GridTypeNone) + if (grid_type() == GridTypeNone) return; /* don't brush a copy over the original */ diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index c8dc731b5f..e7bdd9456b 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -2620,7 +2620,7 @@ Editor::maybe_locate_with_edit_preroll (samplepos_t location) } //if follow_playhead is on, keep the playhead on the screen - if (_follow_playhead) + if (follow_playhead()) if (location < _leftmost_sample) location = _leftmost_sample; @@ -7801,7 +7801,7 @@ Editor::playhead_forward_to_grid () timepos_t pos (_playhead_cursor->current_sample ()); - if (_grid_type == GridTypeNone) { + if (grid_type() == GridTypeNone) { timepos_t const decipage (samplepos_t(floor (current_page_samples() * 0.1))); if (pos < timepos_t::max (pos.time_domain()).earlier (decipage)) { pos += timepos_t (decipage); @@ -7830,7 +7830,7 @@ Editor::playhead_backward_to_grid () timepos_t pos (_playhead_cursor->current_sample ()); - if (_grid_type == GridTypeNone) { + if (grid_type() == GridTypeNone) { samplepos_t const decipage (floor (current_page_samples() * 0.1)); if (pos.samples() > decipage) { pos.shift_earlier (timepos_t (decipage)); diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc index 432396e9a5..71ba7bd911 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -363,7 +363,7 @@ Editor::compute_current_bbt_points (Temporal::TempoMapPoints& grid, samplepos_t const samplecnt_t sr (_session->sample_rate()); float divisor; - switch (_grid_type) { + switch (grid_type()) { case GridTypeBeatDiv3: case GridTypeBeatDiv6: case GridTypeBeatDiv12: diff --git a/gtk2_ardour/midi_view.cc b/gtk2_ardour/midi_view.cc index 6a832deea3..4ea88a833d 100644 --- a/gtk2_ardour/midi_view.cc +++ b/gtk2_ardour/midi_view.cc @@ -202,7 +202,6 @@ MidiView::init (std::shared_ptr mt) void MidiView::note_mode_changed () { - std::cerr << "NM change\n"; clear_events (); model_changed (); } diff --git a/gtk2_ardour/pianoroll.cc b/gtk2_ardour/pianoroll.cc index d011936f8d..9c6e90bcb1 100644 --- a/gtk2_ardour/pianoroll.cc +++ b/gtk2_ardour/pianoroll.cc @@ -16,6 +16,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "ytkmm/scrollbar.h" + #include "pbd/stateful_diff_command.h" #include "pbd/unwind.h" @@ -32,8 +34,6 @@ #include "canvas/rectangle.h" #include "canvas/widget.h" -#include "ytkmm/scrollbar.h" - #include "gtkmm2ext/actions.h" #include "widgets/ardour_button.h" @@ -42,6 +42,7 @@ #include "widgets/tooltips.h" #include "ardour_ui.h" +#include "editing_convert.h" #include "editor_cursors.h" #include "editor_drag.h" #include "gui_thread.h" @@ -66,20 +67,13 @@ using namespace Gtkmm2ext; using namespace Temporal; Pianoroll::Pianoroll (std::string const & name, bool with_transport) - : CueEditor (name) - , timebar_height (15.) - , n_timebars (0) + : CueEditor (name, with_transport) , prh (nullptr) , bg (nullptr) , view (nullptr) , bbt_metric (*this) , _note_mode (Sustained) - , zoom_in_allocate (false) - , solo_button (S_("Solo|S")) - , length_label (X_("Record:")) , ignore_channel_changes (false) - , with_transport_controls (with_transport) - , show_source (false) { mouse_mode = Editing::MouseContent; autoscroll_vertical_allowed = false; @@ -90,7 +84,6 @@ Pianoroll::Pianoroll (std::string const & name, bool with_transport) load_bindings (); register_actions (); - bind_mouse_mode_buttons (); build_grid_type_menu (); build_draw_midi_menus(); @@ -100,71 +93,21 @@ Pianoroll::Pianoroll (std::string const & name, bool with_transport) Pianoroll::~Pianoroll () { - delete own_bindings; - drop_grid (); // unparent gridlines before deleting _canvas_viewport delete view; delete bg; - delete _canvas_viewport; } void Pianoroll::set_show_source (bool yn) { - show_source = yn; + CueEditor::set_show_source (yn); if (view) { view->set_show_source (yn); } } -void -Pianoroll::load_bindings () -{ - load_shared_bindings (); - for (auto & b : bindings) { - b->associate (); - } - set_widget_bindings (*get_canvas(), bindings, ARDOUR_BINDING_KEY); -} - -void -Pianoroll::register_actions () -{ - editor_actions = ActionManager::create_action_group (own_bindings, editor_name()); - - bind_mouse_mode_buttons (); -} - -ArdourCanvas::GtkCanvasViewport* -Pianoroll::get_canvas_viewport() const -{ - return _canvas_viewport; -} - -ArdourCanvas::GtkCanvas* -Pianoroll::get_canvas() const -{ - return _canvas; -} - -bool -Pianoroll::canvas_pre_event (GdkEvent* ev) -{ - switch (ev->type) { - case GDK_ENTER_NOTIFY: - case GDK_LEAVE_NOTIFY: - if (canvas_enter_leave (&ev->crossing)) { - return true; - } - break; - default: - break; - } - - return false; -} - void Pianoroll::rebuild_parameter_button_map() { @@ -274,15 +217,6 @@ Pianoroll::add_multi_controller_item (Gtk::Menu_Helpers::MenuList&, mb->add_item (name, menu_text, *chn_menu, [](){}); } - -void -Pianoroll::scrolled () -{ - pending_visual_change.add (VisualChange::TimeOrigin); - pending_visual_change.time_origin = horizontal_adjustment.get_value() * samples_per_pixel; - ensure_visual_change_idle_handler (); -} - void Pianoroll::build_lower_toolbar () { @@ -290,8 +224,6 @@ Pianoroll::build_lower_toolbar () ArdourButton::Element elements = ArdourButton::Element (ArdourButton::Text|ArdourButton::Indicator|ArdourButton::Edge|ArdourButton::Body); - _canvas_hscrollbar = manage (new Gtk::HScrollbar (horizontal_adjustment)); - velocity_button = new ArdourButton (_("Velocity"), elements); bender_button = new ArdourButton (_("Bender"), elements); pressure_button = new ArdourButton (_("Pressure"), elements); @@ -355,145 +287,25 @@ Pianoroll::build_lower_toolbar () } void -Pianoroll::build_upper_toolbar () +Pianoroll::pack_inner (Gtk::Box& box) { - using namespace Gtk::Menu_Helpers; - - Gtk::HBox* mode_box = manage(new Gtk::HBox); - mode_box->set_border_width (2); - mode_box->set_spacing(2); - - Gtk::HBox* mouse_mode_box = manage (new Gtk::HBox); - Gtk::HBox* mouse_mode_hbox = manage (new Gtk::HBox); - Gtk::VBox* mouse_mode_vbox = manage (new Gtk::VBox); - Gtk::Alignment* mouse_mode_align = manage (new Gtk::Alignment); - - Glib::RefPtr mouse_mode_size_group = Gtk::SizeGroup::create (Gtk::SIZE_GROUP_VERTICAL); - mouse_mode_size_group->add_widget (mouse_draw_button); - mouse_mode_size_group->add_widget (mouse_content_button); - - mouse_mode_size_group->add_widget (grid_type_selector); - mouse_mode_size_group->add_widget (draw_length_selector); - mouse_mode_size_group->add_widget (draw_velocity_selector); - mouse_mode_size_group->add_widget (draw_channel_selector); - mouse_mode_size_group->add_widget (snap_mode_button); - - mouse_mode_hbox->set_spacing (2); - mouse_mode_hbox->pack_start (mouse_draw_button, false, false); - mouse_mode_hbox->pack_start (mouse_content_button, false, false); - - mouse_mode_vbox->pack_start (*mouse_mode_hbox); - - mouse_mode_align->add (*mouse_mode_vbox); - mouse_mode_align->set (0.5, 1.0, 0.0, 0.0); - - mouse_mode_box->pack_start (*mouse_mode_align, false, false); - - pack_snap_box (); - pack_draw_box (false); - - Gtk::HBox* _toolbar_inner = manage (new Gtk::HBox); - Gtk::HBox* _toolbar_outer = manage (new Gtk::HBox); - Gtk::HBox* _toolbar_left = manage (new Gtk::HBox); - - _toolbar_inner->pack_start (*mouse_mode_box, false, false); - _toolbar_inner->pack_start (snap_box, false, false); - _toolbar_inner->pack_start (grid_box, false, false); - _toolbar_inner->pack_start (draw_box, false, false); - - set_tooltip (full_zoom_button, _("Zoom to full clip")); - set_tooltip (note_mode_button, _("Toggle between drum and regular note drawing")); - note_mode_button.set_icon (ArdourIcon::Drum); - - play_note_selection_button.set_icon (ArdourIcon::ToolAudition); - -#define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale())) - note_mode_button.set_size_request (PX_SCALE(50), -1); - note_mode_button.set_active_color (UIConfiguration::instance().color ("alert:yellow")); - - play_button.set_icon (ArdourIcon::TransportPlay); - play_button.set_name ("transport button"); - play_button.show(); - - if (with_transport_controls) { - loop_button.set_icon (ArdourIcon::TransportLoop); - loop_button.set_name ("transport button"); - - solo_button.set_name ("solo button"); - - play_box.set_spacing (8); - play_box.pack_start (play_button, false, false); - play_box.pack_start (loop_button, false, false); - play_box.pack_start (solo_button, false, false); - loop_button.show(); - solo_button.show(); - play_box.set_no_show_all (true); - play_box.show (); - - play_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::play_button_press), false); - solo_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::solo_button_press), false); - loop_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::loop_button_press), false); - } else { - rec_box.pack_start (play_button, false, false); - play_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::bang_button_press), false); - } - - rec_enable_button.set_icon (ArdourIcon::RecButton); - rec_enable_button.set_sensitive (false); - rec_enable_button.signal_button_release_event().connect (sigc::mem_fun (*this, &Pianoroll::rec_button_press), false); - rec_enable_button.set_name ("record enable button"); - - length_selector.add_menu_elem (MenuElem (_("Until Stopped"), sigc::bind (sigc::mem_fun (*this, &Pianoroll::set_recording_length), Temporal::BBT_Offset ()))); - length_selector.add_menu_elem (MenuElem (_("1 Bar"), sigc::bind (sigc::mem_fun (*this, &Pianoroll::set_recording_length), Temporal::BBT_Offset (1, 0, 0)))); - std::vector b ({ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 20, 24, 32 }); - for (auto & n : b) { - length_selector.add_menu_elem (MenuElem (string_compose (_("%1 Bars"), n), sigc::bind (sigc::mem_fun (*this, &Pianoroll::set_recording_length), Temporal::BBT_Offset (n, 0, 0)))); - } - length_selector.set_active (_("Until Stopped")); - - rec_box.set_spacing (12); - rec_box.pack_start (rec_enable_button, false, false); - rec_box.pack_start (length_label, false, false); - rec_box.pack_start (length_selector, false, false); - rec_enable_button.show(); - length_label.show (); - length_selector.show (); - rec_box.set_no_show_all (true); - /* rec box not shown */ - - _toolbar_outer->set_border_width (6); - _toolbar_outer->set_spacing (12); - if (with_transport_controls) { - _toolbar_outer->pack_start (play_box, false, false); - } - _toolbar_outer->pack_start (rec_box, false, false); - _toolbar_outer->pack_start (visible_channel_label, false, false); - _toolbar_outer->pack_start (visible_channel_selector, false, false); - _toolbar_outer->pack_start (play_note_selection_button, false, false); - _toolbar_outer->pack_start (note_mode_button, false, false); - _toolbar_outer->pack_start (follow_playhead_button, false, false); - _toolbar_outer->pack_start (*_toolbar_inner, true, false); - - build_zoom_focus_menu (); - zoom_focus_selector.set_text (zoom_focus_strings[(int)_zoom_focus]); - - _toolbar_left->pack_start (zoom_in_button, false, false); - _toolbar_left->pack_start (zoom_out_button, false, false); - _toolbar_left->pack_start (full_zoom_button, false, false); - _toolbar_left->pack_start (zoom_focus_selector, false, false); - - _toolbar_outer->pack_start (*_toolbar_left, true, false); - _toolbox.pack_start (*_toolbar_outer, false, false); - - _contents.add (_toolbox); - _contents.signal_unmap().connect ([this]() {_canvas_viewport->unmap ();}, false); - _contents.signal_map().connect ([this]() {_canvas_viewport->map ();}, false); + box.pack_start (snap_box, false, false); + box.pack_start (grid_box, false, false); + box.pack_start (draw_box, false, false); } void -Pianoroll::set_recording_length (Temporal::BBT_Offset dur) +Pianoroll::pack_outer (Gtk::Box& box) { - rec_length = dur; + if (with_transport_controls) { + box.pack_start (play_box, false, false); + } + + box.pack_start (rec_box, false, false); + box.pack_start (visible_channel_label, false, false); + box.pack_start (visible_channel_selector, false, false); + box.pack_start (note_mode_button, false, false); + box.pack_start (follow_playhead_button, false, false); } void @@ -517,38 +329,35 @@ Pianoroll::set_visible_channel (int n) void Pianoroll::build_canvas () { - _canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment); + _canvas.set_background_color (UIConfiguration::instance().color ("arrange base")); + _canvas.signal_event().connect (sigc::mem_fun (*this, &Pianoroll::canvas_pre_event), false); + dynamic_cast(&_canvas)->use_nsglview (UIConfiguration::instance().get_nsgl_view_mode () == NSGLHiRes); - _canvas = _canvas_viewport->canvas (); - _canvas->set_background_color (UIConfiguration::instance().color ("midi track base")); - _canvas->signal_event().connect (sigc::mem_fun (*this, &Pianoroll::canvas_pre_event), false); - dynamic_cast(_canvas)->use_nsglview (UIConfiguration::instance().get_nsgl_view_mode () == NSGLHiRes); - - _canvas->PreRender.connect (sigc::mem_fun(*this, &EditingContext::pre_render)); + _canvas.PreRender.connect (sigc::mem_fun(*this, &EditingContext::pre_render)); /* scroll group for items that should not automatically scroll * (e.g verbose cursor). It shares the canvas coordinate space. */ - no_scroll_group = new ArdourCanvas::Container (_canvas->root()); + no_scroll_group = new ArdourCanvas::Container (_canvas.root()); - h_scroll_group = new ArdourCanvas::ScrollGroup (_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally); + h_scroll_group = new ArdourCanvas::ScrollGroup (_canvas.root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally); CANVAS_DEBUG_NAME (h_scroll_group, "pianoroll h scroll"); - _canvas->add_scroller (*h_scroll_group); + _canvas.add_scroller (*h_scroll_group); - v_scroll_group = new ArdourCanvas::ScrollGroup (_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically); + v_scroll_group = new ArdourCanvas::ScrollGroup (_canvas.root(), ArdourCanvas::ScrollGroup::ScrollsVertically); CANVAS_DEBUG_NAME (v_scroll_group, "pianoroll v scroll"); - _canvas->add_scroller (*v_scroll_group); + _canvas.add_scroller (*v_scroll_group); - hv_scroll_group = new ArdourCanvas::ScrollGroup (_canvas->root(), + hv_scroll_group = new ArdourCanvas::ScrollGroup (_canvas.root(), ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically| ArdourCanvas::ScrollGroup::ScrollsHorizontally)); CANVAS_DEBUG_NAME (hv_scroll_group, "pianoroll hv scroll"); - _canvas->add_scroller (*hv_scroll_group); + _canvas.add_scroller (*hv_scroll_group); - cursor_scroll_group = new ArdourCanvas::ScrollGroup (_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally); + cursor_scroll_group = new ArdourCanvas::ScrollGroup (_canvas.root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally); CANVAS_DEBUG_NAME (cursor_scroll_group, "pianoroll cursor scroll"); - _canvas->add_scroller (*cursor_scroll_group); + _canvas.add_scroller (*cursor_scroll_group); /*a group to hold global rects like punch/loop indicators */ global_rect_group = new ArdourCanvas::Container (hv_scroll_group); @@ -599,13 +408,13 @@ Pianoroll::build_canvas () n_timebars++; - bbt_ruler->Event.connect (sigc::mem_fun (*this, &Pianoroll::bbt_ruler_event)); + bbt_ruler->Event.connect (sigc::mem_fun (*this, &CueEditor::ruler_event)); data_group = new ArdourCanvas::Container (hv_scroll_group); CANVAS_DEBUG_NAME (data_group, "cue data group"); bg = new PianorollMidiBackground (data_group, *this); - _canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Pianoroll::canvas_allocate), false); + _canvas_viewport.signal_size_allocate().connect (sigc::mem_fun(*this, &Pianoroll::canvas_allocate), false); // used as rubberband rect rubberband_rect = new ArdourCanvas::Rectangle (data_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0)); @@ -650,57 +459,12 @@ Pianoroll::build_canvas () _playhead_cursor->canvas_item().raise_to_top(); h_scroll_group->raise_to_top (); - _canvas->set_name ("MidiCueCanvas"); - _canvas->add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK); - _canvas->set_can_focus (); - _canvas->signal_show().connect (sigc::mem_fun (*this, &Pianoroll::catch_pending_show_region)); - _toolbox.pack_start (*_canvas_viewport, true, true); -} + _canvas.set_name ("MidiCueCanvas"); + _canvas.add_events (Gdk::POINTER_MOTION_HINT_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK); + _canvas.set_can_focus (); + _canvas.signal_show().connect (sigc::mem_fun (*this, &CueEditor::catch_pending_show_region)); -void -Pianoroll::catch_pending_show_region () -{ - if (_visible_pending_region) { - set_region (_visible_pending_region); - _visible_pending_region.reset (); - } -} - -bool -Pianoroll::bbt_ruler_event (GdkEvent* ev) -{ - switch (ev->type) { - case GDK_BUTTON_RELEASE: - if (ev->button.button == 1) { - ruler_locate (&ev->button); - } - return true; - default: - break; - } - - return false; -} - -void -Pianoroll::ruler_locate (GdkEventButton* ev) -{ - if (!_session) { - return; - } - - if (ref.box()) { - /* we don't locate when working with triggers */ - return; - } - - if (!view->midi_region()) { - return; - } - - samplepos_t sample = pixel_to_sample_from_event (ev->x); - sample += view->midi_region()->source_position().samples(); - _session->request_locate (sample); + _toolbox.pack_start (_canvas_viewport, true, true); } void @@ -780,7 +544,7 @@ Pianoroll::maybe_update () _playhead_cursor->set_position (0); } - if (_follow_playhead) { + if (follow_playhead()) { reset_x_origin_to_follow_playhead (); } } @@ -791,7 +555,7 @@ Pianoroll::canvas_enter_leave (GdkEventCrossing* ev) switch (ev->type) { case GDK_ENTER_NOTIFY: if (ev->detail != GDK_NOTIFY_INFERIOR) { - _canvas_viewport->canvas()->grab_focus (); + _canvas.grab_focus (); ActionManager::set_sensitive (_midi_actions, true); within_track_canvas = true; } @@ -800,8 +564,8 @@ Pianoroll::canvas_enter_leave (GdkEventCrossing* ev) if (ev->detail != GDK_NOTIFY_INFERIOR) { ActionManager::set_sensitive (_midi_actions, false); within_track_canvas = false; - ARDOUR_UI::instance()->reset_focus (_canvas_viewport); - gdk_window_set_cursor (_canvas_viewport->get_window()->gobj(), nullptr); + ARDOUR_UI::instance()->reset_focus (&_canvas_viewport); + gdk_window_set_cursor (_canvas_viewport.get_window()->gobj(), nullptr); } default: break; @@ -962,28 +726,12 @@ Pianoroll::set_trigger_end (Temporal::timepos_t const & p) } } -Gtk::Widget& -Pianoroll::viewport() -{ - return *_canvas_viewport; -} - Gtk::Widget& Pianoroll::contents () { return _contents; } -void -Pianoroll::data_captured (samplecnt_t total_duration) -{ - data_capture_duration = total_duration; - - if (!idle_update_queued.exchange (1)) { - Glib::signal_idle().connect (sigc::mem_fun (*this, &Pianoroll::idle_data_captured)); - } -} - bool Pianoroll::idle_data_captured () { @@ -991,23 +739,12 @@ Pianoroll::idle_data_captured () return false; } - switch (ref.box()->record_enabled()) { - case Recording: - break; - default: - return false; - } - - double where = sample_to_pixel_unrounded (data_capture_duration); - - if (where > _visible_canvas_width * 0.80) { - set_samples_per_pixel (samples_per_pixel * 1.5); - } + CueEditor::idle_data_captured (); if (view) { view->clip_data_recorded (data_capture_duration); } - idle_update_queued.store (0); + return false; } @@ -1284,781 +1021,6 @@ Pianoroll::set_mouse_mode (Editing::MouseMode m, bool force) EditingContext::set_mouse_mode (m, force); } -void -Pianoroll::step_mouse_mode (bool next) -{ -} - -Editing::MouseMode -Pianoroll::current_mouse_mode () const -{ - return mouse_mode; -} - -bool -Pianoroll::internal_editing() const -{ - return true; -} - -RegionSelection -Pianoroll::region_selection() -{ - RegionSelection rs; - /* there is never any region-level selection in a pianoroll */ - return rs; -} - -static void -edit_last_mark_label (std::vector& marks, const std::string& newlabel) -{ - ArdourCanvas::Ruler::Mark copy = marks.back(); - copy.label = newlabel; - marks.pop_back (); - marks.push_back (copy); -} - -void -Pianoroll::metric_get_bbt (std::vector& marks, samplepos_t leftmost, samplepos_t rightmost, gint /*maxchars*/) -{ - if (!_session) { - return; - } - - bool provided = false; - std::shared_ptr tmap; - - if (view && view->midi_region()) { - std::shared_ptr smf (std::dynamic_pointer_cast (view->midi_region()->midi_source())); - - if (smf) { - tmap = smf->tempo_map (provided); - } - } - - if (!provided) { - tmap.reset (new Temporal::TempoMap (Temporal::Tempo (120, 4), Temporal::Meter (4, 4))); - } - - EditingContext::TempoMapScope tms (*this, tmap); - Temporal::TempoMapPoints::const_iterator i; - - char buf[64]; - Temporal::BBT_Time next_beat; - double bbt_position_of_helper; - bool helper_active = false; - ArdourCanvas::Ruler::Mark mark; - const samplecnt_t sr (_session->sample_rate()); - - Temporal::TempoMapPoints grid; - grid.reserve (4096); - - - /* prevent negative values of leftmost from creeping into tempomap - */ - - const Beats left = tmap->quarters_at_sample (leftmost).round_down_to_beat(); - const Beats lower_beat = (left < Beats() ? Beats() : left); - - using std::max; - - switch (bbt_ruler_scale) { - - case bbt_show_quarters: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 1); - break; - case bbt_show_eighths: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 2); - break; - case bbt_show_sixteenths: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 4); - break; - case bbt_show_thirtyseconds: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 8); - break; - case bbt_show_sixtyfourths: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 16); - break; - case bbt_show_onetwentyeighths: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 0, 32); - break; - - case bbt_show_1: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 1); - break; - - case bbt_show_4: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 4); - break; - - case bbt_show_16: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 16); - break; - - case bbt_show_64: - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 64); - break; - - default: - /* bbt_show_many */ - tmap->get_grid (grid, max (tmap->superclock_at (lower_beat), (superclock_t) 0), samples_to_superclock (rightmost, sr), 128); - break; - } - -#if 0 // DEBUG GRID - for (auto const& g : grid) { - std::cout << "Grid " << g.time() << " Beats: " << g.beats() << " BBT: " << g.bbt() << " sample: " << g.sample(_session->nominal_sample_rate ()) << "\n"; - } -#endif - - if (distance (grid.begin(), grid.end()) == 0) { - return; - } - - /* we can accent certain lines depending on the user's Grid choice */ - /* for example, even in a 4/4 meter we can draw a grid with triplet-feel */ - /* and in this case you will want the accents on '3s' not '2s' */ - uint32_t bbt_divisor = 2; - - using namespace Editing; - - switch (_grid_type) { - case GridTypeBeatDiv3: - bbt_divisor = 3; - break; - case GridTypeBeatDiv5: - bbt_divisor = 5; - break; - case GridTypeBeatDiv6: - bbt_divisor = 3; - break; - case GridTypeBeatDiv7: - bbt_divisor = 7; - break; - case GridTypeBeatDiv10: - bbt_divisor = 5; - break; - case GridTypeBeatDiv12: - bbt_divisor = 3; - break; - case GridTypeBeatDiv14: - bbt_divisor = 7; - break; - case GridTypeBeatDiv16: - break; - case GridTypeBeatDiv20: - bbt_divisor = 5; - break; - case GridTypeBeatDiv24: - bbt_divisor = 6; - break; - case GridTypeBeatDiv28: - bbt_divisor = 7; - break; - case GridTypeBeatDiv32: - break; - default: - bbt_divisor = 2; - break; - } - - uint32_t bbt_beat_subdivision = 1; - switch (bbt_ruler_scale) { - case bbt_show_quarters: - bbt_beat_subdivision = 1; - break; - case bbt_show_eighths: - bbt_beat_subdivision = 1; - break; - case bbt_show_sixteenths: - bbt_beat_subdivision = 2; - break; - case bbt_show_thirtyseconds: - bbt_beat_subdivision = 4; - break; - case bbt_show_sixtyfourths: - bbt_beat_subdivision = 8; - break; - case bbt_show_onetwentyeighths: - bbt_beat_subdivision = 16; - break; - default: - bbt_beat_subdivision = 1; - break; - } - - bbt_beat_subdivision *= bbt_divisor; - - switch (bbt_ruler_scale) { - - case bbt_show_many: - snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars); - mark.style = ArdourCanvas::Ruler::Mark::Major; - mark.label = buf; - mark.position = leftmost; - marks.push_back (mark); - break; - - case bbt_show_64: - for (i = grid.begin(); i != grid.end(); i++) { - BBT_Time bbt ((*i).bbt()); - if (bbt.is_bar()) { - if (bbt.bars % 64 == 1) { - if (bbt.bars % 256 == 1) { - snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); - mark.style = ArdourCanvas::Ruler::Mark::Major; - } else { - buf[0] = '\0'; - if (bbt.bars % 256 == 129) { - mark.style = ArdourCanvas::Ruler::Mark::Minor; - } else { - mark.style = ArdourCanvas::Ruler::Mark::Micro; - } - } - mark.label = buf; - mark.position = (*i).sample (sr); - marks.push_back (mark); - } - } - } - break; - - case bbt_show_16: - for (i = grid.begin(); i != grid.end(); i++) { - BBT_Time bbt ((*i).bbt()); - if (bbt.is_bar()) { - if (bbt.bars % 16 == 1) { - if (bbt.bars % 64 == 1) { - snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); - mark.style = ArdourCanvas::Ruler::Mark::Major; - } else { - buf[0] = '\0'; - if (bbt.bars % 64 == 33) { - mark.style = ArdourCanvas::Ruler::Mark::Minor; - } else { - mark.style = ArdourCanvas::Ruler::Mark::Micro; - } - } - mark.label = buf; - mark.position = (*i).sample(sr); - marks.push_back (mark); - } - } - } - break; - - case bbt_show_4: - for (i = grid.begin(); i != grid.end(); ++i) { - BBT_Time bbt ((*i).bbt()); - if (bbt.is_bar()) { - if (bbt.bars % 4 == 1) { - if (bbt.bars % 16 == 1) { - snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); - mark.style = ArdourCanvas::Ruler::Mark::Major; - } else { - buf[0] = '\0'; - mark.style = ArdourCanvas::Ruler::Mark::Minor; - } - mark.label = buf; - mark.position = (*i).sample (sr); - marks.push_back (mark); - } - } - } - break; - - case bbt_show_1: - for (i = grid.begin(); i != grid.end(); ++i) { - BBT_Time bbt ((*i).bbt()); - if (bbt.is_bar()) { - snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); - mark.style = ArdourCanvas::Ruler::Mark::Major; - mark.label = buf; - mark.position = (*i).sample (sr); - marks.push_back (mark); - } - } - break; - - case bbt_show_quarters: - - mark.label = ""; - mark.position = leftmost; - mark.style = ArdourCanvas::Ruler::Mark::Micro; - marks.push_back (mark); - - for (i = grid.begin(); i != grid.end(); ++i) { - - BBT_Time bbt ((*i).bbt()); - - if ((*i).sample (sr) < leftmost && (bbt_bar_helper_on)) { - snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats); - edit_last_mark_label (marks, buf); - } else { - - if (bbt.is_bar()) { - mark.style = ArdourCanvas::Ruler::Mark::Major; - snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); - } else if ((bbt.beats % 2) == 1) { - mark.style = ArdourCanvas::Ruler::Mark::Minor; - buf[0] = '\0'; - } else { - mark.style = ArdourCanvas::Ruler::Mark::Micro; - buf[0] = '\0'; - } - mark.label = buf; - mark.position = (*i).sample (sr); - marks.push_back (mark); - } - } - break; - - case bbt_show_eighths: - case bbt_show_sixteenths: - case bbt_show_thirtyseconds: - case bbt_show_sixtyfourths: - case bbt_show_onetwentyeighths: - - bbt_position_of_helper = leftmost + (3 * get_current_zoom ()); - - mark.label = ""; - mark.position = leftmost; - mark.style = ArdourCanvas::Ruler::Mark::Micro; - marks.push_back (mark); - - for (i = grid.begin(); i != grid.end(); ++i) { - - BBT_Time bbt ((*i).bbt()); - - if ((*i).sample (sr) < leftmost && (bbt_bar_helper_on)) { - snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats); - edit_last_mark_label (marks, buf); - helper_active = true; - } else { - - if (bbt.is_bar()) { - mark.style = ArdourCanvas::Ruler::Mark::Major; - snprintf (buf, sizeof(buf), "%" PRIu32, bbt.bars); - } else if (bbt.ticks == 0) { - mark.style = ArdourCanvas::Ruler::Mark::Minor; - snprintf (buf, sizeof(buf), "%" PRIu32, bbt.beats); - } else { - mark.style = ArdourCanvas::Ruler::Mark::Micro; - buf[0] = '\0'; - } - - if (((*i).sample(sr) < bbt_position_of_helper) && helper_active) { - buf[0] = '\0'; - } - mark.label = buf; - mark.position = (*i).sample (sr); - marks.push_back (mark); - } - } - - break; - } -} - - -void -Pianoroll::mouse_mode_toggled (Editing::MouseMode m) -{ - Glib::RefPtr act = get_mouse_mode_action (m); - Glib::RefPtr tact = Glib::RefPtr::cast_dynamic (act); - - if (!tact->get_active()) { - /* this was just the notification that the old mode has been - * left. we'll get called again with the new mode active in a - * jiffy. - */ - return; - } - - mouse_mode = m; - - /* this should generate a new enter event which will - trigger the appropriate cursor. - */ - - if (_canvas) { - _canvas->re_enter (); - } -} - -int -Pianoroll::set_state (XMLNode const & node, int version) -{ - set_common_editing_state (node); - return 0; -} - -XMLNode& -Pianoroll::get_state () const -{ - XMLNode* node (new XMLNode (editor_name())); - get_common_editing_state (*node); - return *node; -} - -/** @param allow_horiz true to allow horizontal autoscroll, otherwise false. - * - * @param allow_vert true to allow vertical autoscroll, otherwise false. - * - */ -void -Pianoroll::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) -{ - if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) { - return; - } - - /* define a rectangular boundary for scrolling. If the mouse moves - * outside of this area and/or continue to be outside of this area, - * then we will continuously auto-scroll the canvas in the appropriate - * direction(s) - * - * the boundary is defined in coordinates relative to canvas' own - * window since that is what we're going to call ::get_pointer() on - * during autoscrolling to determine if we're still outside the - * boundary or not. - */ - - ArdourCanvas::Rect scrolling_boundary; - Gtk::Allocation alloc; - - alloc = get_canvas()->get_allocation (); - - alloc.set_x (0); - alloc.set_y (0); - - if (allow_vert) { - /* reduce height by the height of the timebars, which happens - to correspond to the position of the data_group. - */ - - alloc.set_height (alloc.get_height() - data_group->position().y); - alloc.set_y (alloc.get_y() + data_group->position().y); - - /* now reduce it again so that we start autoscrolling before we - * move off the top or bottom of the canvas - */ - - alloc.set_height (alloc.get_height() - 20); - alloc.set_y (alloc.get_y() + 10); - } - - if (allow_horiz && (alloc.get_width() > 20)) { - - if (prh) { - double w, h; - prh->size_request (w, h); - - alloc.set_width (alloc.get_width() - w); - alloc.set_x (alloc.get_x() + w); - } - - /* the effective width of the autoscroll boundary so - that we start scrolling before we hit the edge. - - this helps when the window is slammed up against the - right edge of the screen, making it hard to scroll - effectively. - */ - - alloc.set_width (alloc.get_width() - 20); - alloc.set_x (alloc.get_x() + 10); - } - - scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height()); - - int x, y; - Gdk::ModifierType mask; - - get_canvas()->get_window()->get_pointer (x, y, mask); - - if ((allow_horiz && ((x < scrolling_boundary.x0 && _leftmost_sample > 0) || x >= scrolling_boundary.x1)) || - (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) { - start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary); - } -} - -bool -Pianoroll::autoscroll_active () const -{ - return autoscroll_connection.connected (); -} - -bool -Pianoroll::autoscroll_canvas () -{ - using std::max; - using std::min; - int x, y; - Gdk::ModifierType mask; - sampleoffset_t dx = 0; - bool no_stop = false; - Gtk::Window* toplevel = dynamic_cast(_canvas_viewport->get_toplevel()); - - if (!toplevel) { - return false; - } - - get_canvas()->get_window()->get_pointer (x, y, mask); - - VisualChange vc; - bool vertical_motion = false; - - if (autoscroll_horizontal_allowed) { - - samplepos_t new_sample = _leftmost_sample; - - /* horizontal */ - - if (x > autoscroll_boundary.x1) { - - /* bring it back into view */ - dx = x - autoscroll_boundary.x1; - dx += 10 + (2 * (autoscroll_cnt/2)); - - dx = pixel_to_sample (dx); - - dx *= UIConfiguration::instance().get_draggable_playhead_speed(); - - if (_leftmost_sample < max_samplepos - dx) { - new_sample = _leftmost_sample + dx; - } else { - new_sample = max_samplepos; - } - - no_stop = true; - - } else if (x < autoscroll_boundary.x0) { - - dx = autoscroll_boundary.x0 - x; - dx += 10 + (2 * (autoscroll_cnt/2)); - - dx = pixel_to_sample (dx); - - dx *= UIConfiguration::instance().get_draggable_playhead_speed(); - - if (_leftmost_sample >= dx) { - new_sample = _leftmost_sample - dx; - } else { - new_sample = 0; - } - - no_stop = true; - } - - if (new_sample != _leftmost_sample) { - vc.time_origin = new_sample; - vc.add (VisualChange::TimeOrigin); - } - } - - if (autoscroll_vertical_allowed) { - - // const double vertical_pos = vertical_adjustment.get_value(); - const int speed_factor = 10; - - /* vertical */ - - if (y < autoscroll_boundary.y0) { - - /* scroll to make higher tracks visible */ - - if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) { - // XXX SCROLL UP - vertical_motion = true; - } - no_stop = true; - - } else if (y > autoscroll_boundary.y1) { - - if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) { - // XXX SCROLL DOWN - vertical_motion = true; - } - no_stop = true; - } - - } - - if (vc.pending || vertical_motion) { - - /* change horizontal first */ - - if (vc.pending) { - visual_changer (vc); - } - - /* now send a motion event to notify anyone who cares - that we have moved to a new location (because we scrolled) - */ - - GdkEventMotion ev; - - ev.type = GDK_MOTION_NOTIFY; - ev.state = Gdk::BUTTON1_MASK; - - /* the motion handler expects events in canvas coordinate space */ - - /* we asked for the mouse position above (::get_pointer()) via - * our own top level window (we being the Editor). Convert into - * coordinates within the canvas window. - */ - - int cx; - int cy; - - //toplevel->translate_coordinates (*get_canvas(), x, y, cx, - //cy); - cx = x; - cy = y; - - /* clamp x and y to remain within the autoscroll boundary, - * which is defined in window coordinates - */ - - x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1); - y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1); - - /* now convert from Editor window coordinates to canvas - * window coordinates - */ - - ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy)); - ev.x = d.x; - ev.y = d.y; - ev.state = mask; - - motion_handler (0, (GdkEvent*) &ev, true); - - } else if (no_stop) { - - /* not changing visual state but pointer is outside the scrolling boundary - * so we still need to deliver a fake motion event - */ - - GdkEventMotion ev; - - ev.type = GDK_MOTION_NOTIFY; - ev.state = Gdk::BUTTON1_MASK; - - /* the motion handler expects events in canvas coordinate space */ - - /* first convert from Editor window coordinates to canvas - * window coordinates - */ - - int cx; - int cy; - - /* clamp x and y to remain within the visible area. except - * .. if horizontal scrolling is allowed, always allow us to - * move back to zero - */ - - if (autoscroll_horizontal_allowed) { - x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1); - } else { - x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1); - } - y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1); - - // toplevel->translate_coordinates (*get_canvas_viewport(), x, - // y, cx, cy); - cx = x; - cy = y; - - ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy)); - ev.x = d.x; - ev.y = d.y; - ev.state = mask; - - motion_handler (0, (GdkEvent*) &ev, true); - - } else { - stop_canvas_autoscroll (); - return false; - } - - autoscroll_cnt++; - - return true; /* call me again */ -} - -void -Pianoroll::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary) -{ - if (!_session) { - return; - } - - stop_canvas_autoscroll (); - - autoscroll_horizontal_allowed = allow_horiz; - autoscroll_vertical_allowed = allow_vert; - autoscroll_boundary = boundary; - - /* do the first scroll right now - */ - - autoscroll_canvas (); - - /* scroll again at very very roughly 30FPS */ - - autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Pianoroll::autoscroll_canvas), 30); -} - -void -Pianoroll::stop_canvas_autoscroll () -{ - autoscroll_connection.disconnect (); - autoscroll_cnt = 0; -} - -void -Pianoroll::visual_changer (const VisualChange& vc) -{ - /** - * Changed first so the correct horizontal canvas position is calculated in - * EditingContext::set_horizontal_position - */ - if (vc.pending & VisualChange::ZoomLevel) { - set_samples_per_pixel (vc.samples_per_pixel); - } - - if (vc.pending & VisualChange::TimeOrigin) { - double new_time_origin = sample_to_pixel_unrounded (vc.time_origin); - set_horizontal_position (new_time_origin); - update_tempo_based_rulers (); - } - - if (vc.pending & VisualChange::YOrigin) { - vertical_adjustment.set_value (vc.y_origin); - } - - if (vc.pending & VisualChange::ZoomLevel) { - if (!(vc.pending & VisualChange::TimeOrigin)) { - update_tempo_based_rulers (); - } - } else { - /* If the canvas is not being zoomed then the canvas items will not change - * and cause Item::prepare_for_render to be called so do it here manually. - * Not ideal, but I can't think of a better solution atm. - */ - get_canvas()->prepare_for_render(); - } - - /* If we are only scrolling vertically there is no need to update these */ - if (vc.pending != VisualChange::YOrigin) { - // XXX update_fixed_rulers (); - redisplay_grid (true); - } -} - void Pianoroll::midi_action (void (MidiView::*method)()) { @@ -2295,223 +1257,16 @@ Pianoroll::region_prop_change (PBD::PropertyChange const & what_changed) } } -void -Pianoroll::blink_rec_enable (bool onoff) -{ - if (onoff) { - rec_enable_button.set_active_state (Gtkmm2ext::ExplicitActive); - } else { - rec_enable_button.set_active_state (Gtkmm2ext::Off); - } -} - -void -Pianoroll::trigger_arm_change () -{ - if (!ref.trigger()) { - return; - } - - if (!ref.trigger()->armed()) { - view->end_write (); - } - - rec_enable_change (); -} - -void -Pianoroll::rec_enable_change () -{ - if (!ref.box()) { - return; - } - - rec_blink_connection.disconnect (); - count_in_connection.disconnect (); - - switch (ref.box()->record_enabled()) { - case Recording: - rec_enable_button.set_active_state (Gtkmm2ext::ExplicitActive); - rec_blink_connection.disconnect (); - if (view) { - view->begin_write (); - } - break; - case Enabled: - if (!UIConfiguration::instance().get_no_strobe() && ref.trigger()->armed()) { - rec_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &Pianoroll::blink_rec_enable)); - } else { - rec_enable_button.set_active_state (Gtkmm2ext::Off); - } - maybe_set_count_in (); - break; - case Disabled: - rec_enable_button.set_active_state (Gtkmm2ext::Off); - break; - } -} - -void -Pianoroll::maybe_set_count_in () -{ - if (!ref.box()) { - return; - } - - count_in_connection.disconnect (); - - Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use()); - bool valid; - count_in_to = ref.box()->start_time (valid); - - if (!valid) { - return; - } - - samplepos_t audible (_session->audible_sample()); - Temporal::Beats const & a_q (tmap->quarters_at_sample (audible)); - - if ((count_in_to - a_q).get_beats() == 0) { - return; - } - - count_in_connection = ARDOUR_UI::Clock.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::count_in), ARDOUR_UI::clock_signal_interval())); -} - -void -Pianoroll::count_in (Temporal::timepos_t audible, unsigned int clock_interval_msecs) -{ - if (!_session) { - return; - } - - if (!_session->transport_rolling()) { - return; - } - - TempoMapPoints grid_points; - TempoMap::SharedPtr tmap (TempoMap::use()); - Temporal::Beats audible_beats = tmap->quarters_at_sample (audible.samples()); - samplepos_t audible_samples = audible.samples (); - - if (audible_beats >= count_in_to) { - /* passed the count_in_to time */ - view->hide_overlay_text (); - count_in_connection.disconnect (); - return; - } - - tmap->get_grid (grid_points, samples_to_superclock (audible_samples, _session->sample_rate()), samples_to_superclock ((audible_samples + ((_session->sample_rate() / 1000) * clock_interval_msecs)), _session->sample_rate())); - - if (!grid_points.empty()) { - - /* At least one click in the time between now and the next - * Clock signal - */ - - Temporal::Beats current_delta = count_in_to - audible_beats; - - if (current_delta.get_beats() < 1) { - view->hide_overlay_text (); - count_in_connection.disconnect (); - return; - } - - std::string str (string_compose ("%1", current_delta.get_beats())); - view->set_overlay_text (str); - } -} - -bool -Pianoroll::play_button_press (GdkEventButton* ev) -{ - _session->request_locate (view->midi_region()->position().samples()); - _session->request_roll (); - return true; -} - - -bool -Pianoroll::bang_button_press (GdkEventButton* ev) -{ - if (!ref.trigger()) { - return true; - } - - ref.trigger()->bang (); - - return true; -} - -bool -Pianoroll::loop_button_press (GdkEventButton* ev) -{ - if (!view) { - return true; - } - if (!view->midi_region()) { - return true; - } - - if (_session->get_play_loop()) { - _session->request_play_loop (false); - } else { - set_loop_range (view->midi_region()->position(), view->midi_region()->end(), _("loop region")); - _session->request_play_loop (true); - } - - return true; -} - -bool -Pianoroll::solo_button_press (GdkEventButton* ev) -{ - if (!view) { - return true; - } - - if (!view->midi_track()) { - return true; - } - - view->midi_track()->solo_control()->set_value (!view->midi_track()->solo_control()->get_value(), Controllable::NoGroup); - - return true; -} - -bool -Pianoroll::rec_button_press (GdkEventButton* ev) -{ - if (ev->button != 1) { - return false; - } - - TriggerPtr trigger (ref.trigger()); - - if (!trigger) { - return true; - } - - if (trigger->armed()) { - trigger->disarm (); - } else { - trigger->arm (rec_length); - } - - return true; -} - void Pianoroll::set_trigger (TriggerReference & tref) { - if (tref.trigger() == ref.trigger()) { + if (ref == tref) { return; } - _update_connection.disconnect (); - object_connections.drop_connections (); + TriggerPtr trigger (tref.trigger()); - ref = tref; + CueEditor::set_trigger (tref); rec_box.show (); rec_enable_button.set_sensitive (true); @@ -2519,31 +1274,22 @@ Pianoroll::set_trigger (TriggerReference & tref) idle_update_queued.store (0); ref.box()->Captured.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::data_captured, this, _1), gui_context()); - /* Don't bind a shared_ptr within the lambda */ - TriggerBox* tb (ref.box().get()); - tb->RecEnableChanged.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::rec_enable_change, this), gui_context()); + ref.box()->RecEnableChanged.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::rec_enable_change, this), gui_context()); + maybe_set_count_in (); - Stripable* st = dynamic_cast (ref.box()->owner()); - assert (st); - _track = std::dynamic_pointer_cast (st->shared_from_this()); - assert (_track); - - set_track (_track); - - _track->DropReferences.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::unset, this, true), gui_context()); - ref.trigger()->PropertyChanged.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::trigger_prop_change, this, _1), gui_context()); - ref.trigger()->ArmChanged.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::trigger_arm_change, this), gui_context()); - - std::shared_ptr mr; - - if (ref.trigger()->the_region()) { - std::shared_ptr mr = std::dynamic_pointer_cast (ref.trigger()->the_region()); - if (mr) { - set_region (mr); - } + if (_track) { + _track->DropReferences.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::unset, this, true), gui_context()); } - _update_connection = Timers::rapid_connect (sigc::mem_fun (*this, &Pianoroll::maybe_update)); + trigger->PropertyChanged.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::trigger_prop_change, this, _1), gui_context()); + trigger->ArmChanged.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::trigger_arm_change, this), gui_context()); + _update_connection = Timers::rapid_connect (sigc::mem_fun (*this, &CueEditor::maybe_update)); + + if (trigger) { + set_region (trigger->the_region()); + } else { + set_region (nullptr); + } } void @@ -2574,21 +1320,17 @@ Pianoroll::make_a_region () void Pianoroll::unset (bool trigger_too) { - _history.clear (); - _update_connection.disconnect(); - object_connections.drop_connections (); - _track.reset (); + CueEditor::unset (trigger_too); view->set_region (nullptr); - if (trigger_too) { - ref = TriggerReference (); - } } void -Pianoroll::set_track (std::shared_ptr track) +Pianoroll::set_track (std::shared_ptr track) { + CueEditor::set_track (track); + if (view) { - view->set_track (track); + view->set_track (std::dynamic_pointer_cast (track)); } cc_dropdown1->menu().items().clear (); @@ -2605,36 +1347,23 @@ Pianoroll::set_track (std::shared_ptr track) sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_single_controller_item), cc_dropdown3), sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_multi_controller_item), cc_dropdown3), 12); - track->solo_control()->Changed.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::update_solo_display, this), gui_context()); - update_solo_display (); - // reset_user_cc_choice (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE1), cc_dropdown1); // reset_user_cc_choice (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE2), cc_dropdown2); // reset_user_cc_choice (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE3), cc_dropdown3); } void -Pianoroll::update_solo_display () +Pianoroll::set_region (std::shared_ptr region) { - if (view->midi_track()->solo_control()->get_value()) { - solo_button.set_active_state (Gtkmm2ext::ExplicitActive); - } else { - solo_button.set_active_state (Gtkmm2ext::Off); - } -} + CueEditor::set_region (region); -void -Pianoroll::set_region (std::shared_ptr r) -{ - if (!get_canvas()->is_visible()) { - _visible_pending_region = r; + if (_visible_pending_region) { return; } - unset (false); + std::shared_ptr r (std::dynamic_pointer_cast (region)); - if (!r) { - view->set_region (nullptr); + if (!r || !region) { return; } @@ -2687,17 +1416,8 @@ Pianoroll::set_region (std::shared_ptr r) bg->display_region (*view); _update_connection = Timers::rapid_connect (sigc::mem_fun (*this, &Pianoroll::maybe_update)); -} -void -Pianoroll::zoom_to_show (Temporal::timecnt_t const & duration) -{ - if (!_track_canvas_width) { - zoom_in_allocate = true; - return; - } - - reset_zoom ((samplecnt_t) floor (duration.samples() / _track_canvas_width)); + maybe_set_from_rsu (); } bool @@ -2819,59 +1539,13 @@ Pianoroll::set_note_mode (NoteMode nm) if (nm != bg->note_mode()) { bg->set_note_mode (nm); if (bg->note_mode() == Percussive) { - note_mode_button.set_active (true); + note_mode_button.set_active_state (Gtkmm2ext::ExplicitActive); } else { - note_mode_button.set_active (false); + note_mode_button.set_active_state (Gtkmm2ext::Off); } } } -void -Pianoroll::build_zoom_focus_menu () -{ - using namespace Gtk::Menu_Helpers; - using namespace Editing; - - zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusLeft], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusLeft))); - zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusRight], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusRight))); - zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusCenter], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusCenter))); - zoom_focus_selector.add_menu_elem (MenuElem (zoom_focus_strings[(int)ZoomFocusMouse], sigc::bind (sigc::mem_fun(*this, &EditingContext::zoom_focus_selection_done), (ZoomFocus) ZoomFocusMouse))); - zoom_focus_selector.set_sizing_texts (zoom_focus_strings); -} - - -std::pair -Pianoroll::max_zoom_extent() const -{ - if (view && view->midi_region()) { - - Temporal::Beats len; - - if (show_source) { - len = view->midi_region()->midi_source()->length().beats(); - } else { - len = view->midi_region()->length().beats(); - } - - if (len != Temporal::Beats()) { - return std::make_pair (Temporal::timepos_t (Temporal::Beats()), Temporal::timepos_t (len)); - } - } - - /* this needs to match the default empty region length used in ::make_a_region() */ - return std::make_pair (Temporal::timepos_t (Temporal::Beats()), Temporal::timepos_t (Temporal::Beats (32, 0))); -} - -void -Pianoroll::full_zoom_clicked() -{ - /* XXXX NEED LOCAL TEMPO MAP */ - - std::pair dur (max_zoom_extent()); - samplecnt_t s = dur.second.samples() - dur.first.samples(); - reposition_and_zoom (0, (s / (double) _visible_canvas_width)); -} - void Pianoroll::point_selection_changed () { @@ -3045,13 +1719,6 @@ Pianoroll::select_all_within (Temporal::timepos_t const & start, Temporal::timep commit_reversible_selection_op (); } -void -Pianoroll::session_going_away () -{ - unset (true); - CueEditor::session_going_away (); -} - void Pianoroll::set_session (ARDOUR::Session* s) { @@ -3204,3 +1871,60 @@ Pianoroll::toggle_note_selection (uint8_t note) view->toggle_matching_notes (note, chn_mask); commit_reversible_selection_op(); } + +void +Pianoroll::begin_write () +{ + if (view) { + view->begin_write (); + } +} + +void +Pianoroll::end_write () +{ + if (view) { + view->end_write (); + } +} + +void +Pianoroll::manage_possible_header (Gtk::Allocation& alloc) +{ + if (prh) { + double w, h; + prh->size_request (w, h); + + alloc.set_width (alloc.get_width() - w); + alloc.set_x (alloc.get_x() + w); + } +} + +void +Pianoroll::show_count_in (std::string const & str) +{ + if (view) { + view->set_overlay_text (str); + } +} + +void +Pianoroll::hide_count_in () +{ + if (view) { + view->hide_overlay_text (); + } +} + +void +Pianoroll::instant_save () +{ + region_ui_settings.draw_length = draw_length(); + region_ui_settings.draw_velocity = draw_velocity(); + region_ui_settings.channel = draw_channel(); + region_ui_settings.note_min = bg->lowest_note (); + region_ui_settings.note_max = bg->highest_note(); + + CueEditor::instant_save (); +} + diff --git a/gtk2_ardour/pianoroll.h b/gtk2_ardour/pianoroll.h index 46b8604797..5aae4885c3 100644 --- a/gtk2_ardour/pianoroll.h +++ b/gtk2_ardour/pianoroll.h @@ -58,12 +58,8 @@ class Pianoroll : public CueEditor Pianoroll (std::string const & name, bool with_transport_controls = false); ~Pianoroll (); - ArdourCanvas::Container* get_trackview_group () const { return data_group; } - ArdourCanvas::Container* get_noscroll_group() const { return no_scroll_group; } - Gtk::Widget& viewport(); Gtk::Widget& contents (); - double visible_canvas_width() const { return _visible_canvas_width; } samplecnt_t current_page_samples() const; void get_per_region_note_selection (std::list > > > >&) const {} @@ -83,33 +79,13 @@ class Pianoroll : public CueEditor int32_t get_grid_music_divisions (Editing::GridType gt, uint32_t event_state) const { return 1; } void set_trigger (ARDOUR::TriggerReference&); - void set_region (std::shared_ptr); - void set_track (std::shared_ptr); - - ArdourCanvas::ScrollGroup* get_hscroll_group () const { return h_scroll_group; } - ArdourCanvas::ScrollGroup* get_cursor_scroll_group () const { return cursor_scroll_group; } + void set_region (std::shared_ptr); + void set_track (std::shared_ptr); double max_extents_scale() const { return 1.2; } void set_samples_per_pixel (samplecnt_t); void set_mouse_mode (Editing::MouseMode, bool force = false); - void step_mouse_mode (bool next); - Editing::MouseMode current_mouse_mode () const; - bool internal_editing() const; - - void trigger_arm_change (); - - double timebar_height; - size_t n_timebars; - - ArdourCanvas::GtkCanvasViewport* get_canvas_viewport() const; - ArdourCanvas::GtkCanvas* get_canvas() const; - - int set_state (const XMLNode&, int version); - XMLNode& get_state () const; - - void maybe_autoscroll (bool, bool, bool); - bool autoscroll_active() const; void midi_action (void (MidiView::*method)()); @@ -133,9 +109,6 @@ class Pianoroll : public CueEditor void set_trigger_length (Temporal::timecnt_t const &); void set_trigger_bounds (Temporal::timepos_t const &, Temporal::timepos_t const &); - void full_zoom_clicked(); - void zoom_to_show (Temporal::timecnt_t const &); - void delete_ (); void paste (float times, bool from_context_menu); void keyboard_paste (); @@ -143,7 +116,6 @@ class Pianoroll : public CueEditor PianorollMidiView* midi_view() const { return view; } void set_session (ARDOUR::Session*); - void session_going_away (); bool allow_trim_cursors () const; void shift_midi (Temporal::timepos_t const &, bool model); @@ -154,9 +126,6 @@ class Pianoroll : public CueEditor void set_show_source (bool); protected: - void load_bindings (); - void register_actions (); - Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start, Temporal::RoundMode direction, ARDOUR::SnapPref gpref) const; @@ -178,46 +147,14 @@ class Pianoroll : public CueEditor bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType); - void mouse_mode_toggled (Editing::MouseMode); - void escape (); private: - ARDOUR::TriggerReference ref; - std::shared_ptr _track; - ArdourCanvas::GtkCanvasViewport* _canvas_viewport; - ArdourCanvas::GtkCanvas* _canvas; - - Gtk::HScrollbar* _canvas_hscrollbar; - - /* The group containing all other groups that are scrolled vertically - and horizontally. - */ - ArdourCanvas::ScrollGroup* hv_scroll_group; - - /* The group containing all other groups that are scrolled horizontally ONLY - */ - ArdourCanvas::ScrollGroup* h_scroll_group; - ArdourCanvas::ScrollGroup* v_scroll_group; - - /* Scroll group for cursors, scrolled horizontally, above everything else - */ - ArdourCanvas::ScrollGroup* cursor_scroll_group; - - ArdourCanvas::Container* global_rect_group; - ArdourCanvas::Container* no_scroll_group; - ArdourCanvas::Container* data_group; ArdourCanvas::Ruler* bbt_ruler; ArdourCanvas::Rectangle* tempo_bar; ArdourCanvas::Rectangle* meter_bar; ArdourCanvas::PianoRollHeader* prh; - ArdourCanvas::Rectangle* transport_loop_range_rect; - - ArdourWidgets::EventBoxExt _contents; - Gtk::VBox _toolbox; - - Gtk::HBox button_bar; ArdourWidgets::ArdourButton* velocity_button; ArdourWidgets::ArdourButton* bender_button; ArdourWidgets::ArdourButton* pressure_button; @@ -236,15 +173,10 @@ class Pianoroll : public CueEditor void build_canvas (); void canvas_allocate (Gtk::Allocation); - void build_upper_toolbar (); void build_lower_toolbar (); - RegionSelection region_selection(); - bool canvas_enter_leave (GdkEventCrossing* ev); - void metric_get_bbt (std::vector&, samplepos_t, samplepos_t, gint); - class BBTMetric : public ArdourCanvas::Ruler::Metric { public: @@ -260,16 +192,6 @@ class Pianoroll : public CueEditor BBTMetric bbt_metric; - bool canvas_pre_event (GdkEvent*); - - /* autoscrolling */ - - bool autoscroll_canvas (); - void start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary); - void stop_canvas_autoscroll (); - - sigc::connection _update_connection; - PBD::ScopedConnectionList object_connections; PBD::ScopedConnectionList view_connections; void maybe_update (); void trigger_prop_change (PBD::PropertyChange const &); @@ -277,14 +199,9 @@ class Pianoroll : public CueEditor void unset (bool trigger_too); - void visual_changer (const VisualChange&); void bindings_changed (); - void data_captured (samplecnt_t); bool idle_data_captured (); - std::atomic idle_update_queued; - PBD::ScopedConnectionList capture_connections; - samplecnt_t data_capture_duration; bool user_automation_button_event (GdkEventButton* ev, ArdourWidgets::MetaButton* mb); bool automation_button_event (GdkEventButton*, Evoral::ParameterType type, int id); @@ -299,37 +216,8 @@ class Pianoroll : public CueEditor void automation_state_changed (); - void build_zoom_focus_menu (); - - std::pair max_zoom_extent() const; - void point_selection_changed (); - bool zoom_in_allocate; - - ArdourWidgets::ArdourButton rec_enable_button; - ArdourWidgets::ArdourButton play_button; - ArdourWidgets::ArdourButton solo_button; - ArdourWidgets::ArdourButton loop_button; - - bool play_button_press (GdkEventButton*); - bool bang_button_press (GdkEventButton*); - bool solo_button_press (GdkEventButton*); - bool loop_button_press (GdkEventButton*); - - ArdourWidgets::ArdourDropdown length_selector; - Temporal::BBT_Offset rec_length; - Gtk::Label length_label; - Gtk::HBox rec_box; - Gtk::HBox play_box; - - void set_recording_length (Temporal::BBT_Offset bars); - - bool rec_button_press (GdkEventButton*); - void rec_enable_change (); - void blink_rec_enable (bool); - sigc::connection rec_blink_connection; - void add_single_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, int ctl, const std::string& name, ArdourWidgets::MetaButton*); void add_multi_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, uint16_t channels, int ctl, const std::string& name, ArdourWidgets::MetaButton*); void reset_user_cc_choice (std::string, Evoral::Parameter param, ArdourWidgets::MetaButton*); @@ -337,30 +225,29 @@ class Pianoroll : public CueEditor bool ignore_channel_changes; void visible_channel_changed (); - bool with_transport_controls; - void update_solo_display (); void map_transport_state (); - sigc::connection count_in_connection; - Temporal::Beats count_in_to; - - void count_in (Temporal::timepos_t, unsigned int); - void maybe_set_count_in (); - - bool bbt_ruler_event (GdkEvent*); - void ruler_locate (GdkEventButton*); - void scrolled (); void update_tempo_based_rulers (); + void update_rulers() { update_tempo_based_rulers (); } Gtk::Menu _region_context_menu; void popup_region_context_menu (ArdourCanvas::Item* item, GdkEvent* event); - std::shared_ptr _visible_pending_region; - void catch_pending_show_region (); - - bool show_source; void set_note_selection (uint8_t note); void add_note_selection (uint8_t note); void extend_note_selection (uint8_t note); void toggle_note_selection (uint8_t note); + + void pack_inner (Gtk::Box&); + void pack_outer (Gtk::Box&); + + void begin_write (); + void end_write (); + + void manage_possible_header (Gtk::Allocation& alloc); + + void show_count_in (std::string const &); + void hide_count_in (); + + void instant_save (); }; diff --git a/gtk2_ardour/pianoroll_window.cc b/gtk2_ardour/pianoroll_window.cc index 824ffa8b6f..826d40e6e9 100644 --- a/gtk2_ardour/pianoroll_window.cc +++ b/gtk2_ardour/pianoroll_window.cc @@ -19,6 +19,7 @@ #include "pbd/compose.h" #include "ardour/midi_region.h" +#include "ardour/midi_track.h" #include "gtkmm2ext/doi.h" @@ -35,7 +36,7 @@ PianorollWindow::PianorollWindow (std::string const & name, Session& s) , region_editor (nullptr) { pianoroll->set_session (&s); - pianoroll->viewport().set_size_request (600, 120); + pianoroll->get_canvas_viewport()->set_size_request (600, 120); add (hpacker); hpacker.show (); diff --git a/gtk2_ardour/trigger_page.cc b/gtk2_ardour/trigger_page.cc index a6ac3d179f..e0c05fe018 100644 --- a/gtk2_ardour/trigger_page.cc +++ b/gtk2_ardour/trigger_page.cc @@ -144,24 +144,21 @@ TriggerPage::TriggerPage () _sidebar_pager2.set_index (3); _midi_editor = new Pianoroll (X_("MIDICueEditor")); + _audio_editor = new AudioClipEditor (X_("AudioClipEditor")); + + _audio_editor->get_canvas_viewport()->show (); + _midi_editor->get_canvas_viewport()->show (); /* Bottom -- Properties of selected Slot/Region */ - table.set_homogeneous (false); - table.set_spacings (8); //match to slot_properties_box::set_spacings - table.set_border_width (8); + hpacker.set_homogeneous (false); + hpacker.set_spacing (8); //match to slot_properties_box::set_spacings + hpacker.set_border_width (8); - int col = 0; - table.attach (_properties_box, col, col + 1, 0, 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK | Gtk::FILL); - ++col; - table.attach (_audio_trig_box, col, col + 1, 0, 1, Gtk::FILL, Gtk::SHRINK | Gtk::FILL); - clip_editor_column = ++col; - ++col; - - table.set_no_show_all (); - - _parameter_box.pack_start (table); - _parameter_box.show (); + hpacker.pack_start (_properties_box, false, false); + hpacker.pack_start (_midi_trig_box, false, false); + hpacker.pack_start (_audio_trig_box, false, false); + hpacker.set_no_show_all (); _sidebar_notebook.signal_switch_page().connect ([this](GtkNotebookPage*, guint page) { std::string label (_sidebar_notebook.get_tab_label_text (*_sidebar_notebook.get_nth_page (page))); @@ -184,7 +181,7 @@ TriggerPage::TriggerPage () /* Top-level Layout */ content_app_bar.add (_application_bar); content_main.add (_strip_group_box); - content_att_bottom.add (_parameter_box); + content_att_bottom.add (hpacker); content_att_right.add (_sidebar_vbox); /* Show all */ @@ -263,6 +260,7 @@ TriggerPage::get_state () const node->set_property (X_("triggerpage-sidebar-btn2"), _sidebar_pager2.index ()); node->add_child_nocopy (_midi_editor->get_state()); + node->add_child_nocopy (_audio_editor->get_state()); Glib::RefPtr act = ActionManager::get_toggle_action ("Cues", "ToggleTriggerList"); node->set_property ("show-trigger-list", act->get_active ()); @@ -306,6 +304,11 @@ TriggerPage::set_state (const XMLNode& node, int version) _midi_editor->set_state (*mn, version); } + XMLNode* an = node.child (_audio_editor->editor_name().c_str()); + if (an) { + _audio_editor->set_state (*an, version); + } + bool yn = true; node.get_property ("show-trigger-list", yn); { @@ -368,10 +371,10 @@ TriggerPage::set_session (Session* s) initial_track_display (); _properties_box.set_session (s); - _audio_trig_box.set_session (s); - + _midi_trig_box.set_session (s); _midi_editor->set_session (s); + _audio_editor->set_session (s); update_title (); start_updating (); @@ -475,9 +478,7 @@ TriggerPage::trigger_arm_changed (Trigger const * trigger) /* hide everything */ - _audio_trig_box.hide (); - _midi_trig_box.hide (); - _midi_editor->viewport().hide (); + hide_all (); Tabbable::showhide_att_bottom (false); @@ -485,10 +486,13 @@ TriggerPage::trigger_arm_changed (Trigger const * trigger) TriggerReference ref (trigger->boxptr(), trigger->index()); if (box.data_type () == DataType::AUDIO) { - if (trigger->the_region()) { - _audio_trig_box.set_trigger (ref); - _audio_trig_box.show (); - } + + _audio_trig_box.set_trigger (ref); + _audio_trig_box.show (); + + _audio_editor->set_trigger (ref); + + hpacker.pack_start (_audio_editor->contents(), true, true); } else { @@ -496,7 +500,7 @@ TriggerPage::trigger_arm_changed (Trigger const * trigger) _midi_trig_box.show (); _midi_editor->set_trigger (ref); - _midi_editor->viewport().show (); + hpacker.pack_start (_midi_editor->contents(), true, true); } if (_show_bottom_pane) { @@ -505,38 +509,46 @@ TriggerPage::trigger_arm_changed (Trigger const * trigger) } void -TriggerPage::selection_changed () +TriggerPage::hide_all () { - Selection& selection (Editor::instance ().get_selection ()); - - /* hide everything */ - - _audio_trig_box.hide (); - _midi_trig_box.hide (); - _midi_editor->contents().hide(); // although we de-parent this, it requires this explicit HIDE to prevent bleeding into other pages on macOS + if (_audio_editor->contents().get_parent()) { + _audio_editor->contents().get_parent()->remove (_audio_editor->contents()); + } if (_midi_editor->contents().get_parent()) { _midi_editor->contents().get_parent()->remove (_midi_editor->contents()); } + _audio_trig_box.hide (); + _midi_trig_box.hide (); +} + +void +TriggerPage::selection_changed () +{ + Selection& selection (Editor::instance ().get_selection ()); + + hide_all (); + Tabbable::showhide_att_bottom (false); if (selection.triggers.empty ()) { return; } - TriggerSelection ts = selection.triggers; - TriggerEntry* entry = *ts.begin (); - TriggerReference ref = entry->trigger_reference (); - TriggerPtr trigger = entry->trigger (); + TriggerReference ref = selection.triggers.front()->trigger_reference (); std::shared_ptr box = ref.box(); if (box->data_type () == DataType::AUDIO) { - if (trigger->the_region()) { - _audio_trig_box.set_trigger (ref); - _audio_trig_box.show (); - } + _audio_trig_box.set_trigger (ref); + _audio_trig_box.show (); + + _audio_editor->set_trigger (ref); + _audio_editor->get_canvas_viewport()->show (); + + hpacker.pack_start (_audio_editor->contents(), true, true); + _audio_editor->contents().show_all (); } else { @@ -544,12 +556,13 @@ TriggerPage::selection_changed () _midi_trig_box.show (); _midi_editor->set_trigger (ref); + _midi_editor->get_canvas_viewport()->show (); - table.attach (_midi_editor->contents(), clip_editor_column, clip_editor_column + 1, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL); + hpacker.pack_start (_midi_editor->contents(), true, true); _midi_editor->contents().show_all (); } - table.show (); + hpacker.show (); if (_show_bottom_pane) { Tabbable::showhide_att_bottom (true); diff --git a/gtk2_ardour/trigger_page.h b/gtk2_ardour/trigger_page.h index 2d963983f9..22d0b043b2 100644 --- a/gtk2_ardour/trigger_page.h +++ b/gtk2_ardour/trigger_page.h @@ -30,6 +30,7 @@ #include "widgets/tabbable.h" #include "application_bar.h" +#include "audio_clip_editor.h" #include "audio_region_operations_box.h" #include "audio_trigger_properties_box.h" #include "axis_provider.h" @@ -76,7 +77,7 @@ private: void remove_route (TriggerStrip*); void clear_selected_slot (); - + void hide_all (); void redisplay_track_list (); void pi_property_changed (PBD::PropertyChange const&); void stripable_property_changed (PBD::PropertyChange const&, std::weak_ptr); @@ -117,7 +118,6 @@ private: Gtk::EventBox _no_strips; Gtk::Alignment _cue_area_frame; Gtk::VBox _cue_area_box; - Gtk::HBox _parameter_box; Gtk::VBox _sidebar_vbox; ArdourWidgets::MetaButton _sidebar_pager1; ArdourWidgets::MetaButton _sidebar_pager2; @@ -126,7 +126,7 @@ private: TriggerSourceList _trigger_source_list; TriggerRegionList _trigger_region_list; TriggerRouteList _trigger_route_list; - Gtk::Table table; + Gtk::HBox hpacker; CueBoxWidget _cue_box; FittedCanvasWidget _master_widget; @@ -140,10 +140,10 @@ private: #if REGION_PROPERTIES_BOX_TODO AudioRegionOperationsBox _audio_ops_box; - AudioClipEditorBox _audio_trim_box; #endif Pianoroll* _midi_editor; + AudioClipEditor* _audio_editor; RouteProcessorSelection _selection; std::list _strips;