From e81b2353cc1319c18bf966d18c1602a3c54e6089 Mon Sep 17 00:00:00 2001 From: Ben Loftis Date: Thu, 7 Sep 2023 21:20:05 +0200 Subject: [PATCH] Indicate arrangement section selection on canvas --- gtk2_ardour/editor.cc | 46 ++++++++++++++++++++++++ gtk2_ardour/editor.h | 7 ++++ gtk2_ardour/editor_canvas.cc | 22 ++++++++---- gtk2_ardour/editor_canvas_events.cc | 28 ++++++++++++++- gtk2_ardour/editor_section_box.cc | 54 +++++++++++++++++++++++++++++ gtk2_ardour/editor_section_box.h | 41 ++++++++++++++++++++++ gtk2_ardour/editor_selection.cc | 15 ++++++++ gtk2_ardour/wscript | 1 + 8 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 gtk2_ardour/editor_section_box.cc create mode 100644 gtk2_ardour/editor_section_box.h diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index d82695738d..fb1315afbb 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -122,6 +122,7 @@ #include "editor_regions.h" #include "editor_route_groups.h" #include "editor_routes.h" +#include "editor_section_box.h" #include "editor_sections.h" #include "editor_snapshots.h" #include "editor_sources.h" @@ -410,6 +411,7 @@ Editor::Editor () , _last_region_menu_was_main (false) , _track_selection_change_without_scroll (false) , _editor_track_selection_change_without_scroll (false) + , _section_box (0) , _playhead_cursor (0) , _snapped_cursor (0) , cd_marker_bar_drag_rect (0) @@ -924,6 +926,7 @@ Editor::~Editor() delete selection; delete cut_buffer; delete _cursors; + delete _section_box; LuaInstance::destroy_instance (); @@ -1110,6 +1113,7 @@ Editor::control_scroll (float fraction) /* move visuals, we'll catch up with it later */ _playhead_cursor->set_position (*_control_scroll_target); + update_section_box (); UpdateAllTransportClocks (*_control_scroll_target); if (*_control_scroll_target > (current_page_samples() / 2)) { @@ -1265,6 +1269,8 @@ Editor::map_position_change (samplepos_t sample) if (!_session->locate_initiated()) { _playhead_cursor->set_position (sample); } + + update_section_box (); } void @@ -1584,6 +1590,41 @@ Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::It xfade_out_context_menu.popup (button, time); } +void +Editor::popup_section_box_menu (int button, int32_t time) +{ + using namespace Menu_Helpers; + + section_box_menu.set_name ("ArdourContextMenu"); + MenuList& items (section_box_menu.items()); + items.clear (); + + items.push_back (MenuElem (_("Copy/Paste Range Section to Edit Point"), sigc::bind (sigc::mem_fun (*this, &Editor::cut_copy_section), CopyPasteSection))); + items.push_back (MenuElem (_("Cut/Paste Range Section to Edit Point"), sigc::bind (sigc::mem_fun (*this, &Editor::cut_copy_section), CutPasteSection))); + items.push_back (MenuElem (_("Delete Range Section"), sigc::bind (sigc::mem_fun (*this, &Editor::cut_copy_section), DeleteSection))); +#if 0 + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Delete all markers in Section"), sigc::bind (sigc::mem_fun (*this, &Editor::cut_copy_section), DeleteSection))); +#endif + + timepos_t start, end; + if (get_selection_extents (start, end)) { + /* add some items from build_marker_menu () */ + Location* l = _session->locations ()->mark_at (start); + assert (l); + LocationMarkers* lm = find_location_markers (l); + assert (lm && lm->start); + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Move Playhead to Marker"), sigc::bind (sigc::mem_fun(*_session, &Session::request_locate), start.samples (), false, MustStop, TRS_UI))); + items.push_back (MenuElem (_("Rename..."), sigc::bind (sigc::mem_fun(*this, &Editor::rename_marker), lm->start))); + } + + items.push_back (SeparatorElem()); + add_selection_context_items (items, true); + + section_box_menu.popup (button, time); +} + void Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection) { @@ -2481,6 +2522,7 @@ Editor::set_state (const XMLNode& node, int version) } update_selection_markers (); + update_section_box (); node.get_property ("mixer-width", editor_mixer_strip_width); @@ -5019,6 +5061,7 @@ Editor::on_samples_per_pixel_changed () refresh_location_display(); _summary->set_overlays_dirty (); + update_section_box (); update_marker_labels (); instant_save (); @@ -5703,6 +5746,7 @@ Editor::located () if (_follow_playhead && !_pending_initial_locate) { reset_x_origin_to_follow_playhead (); } + update_section_box (); } _pending_locate_request = false; @@ -6472,6 +6516,8 @@ Editor::super_rapid_screen_update () _playhead_cursor->set_position (sample); } + update_section_box (); + if (_session->requested_return_sample() >= 0) { _last_update_time = 0; return; diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 4e0f131116..78740d0542 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -145,6 +145,7 @@ class QuantizeDialog; class RegionPeakCursor; class RhythmFerret; class RulerDialog; +class SectionBox; class Selection; class SelectionPropertiesBox; class SoundFileOmega; @@ -733,6 +734,7 @@ private: void toggle_marker_lines (); void set_marker_line_visibility (bool); void update_selection_markers (); + void update_section_box (); void jump_forward_to_mark (); void jump_backward_to_mark (); @@ -852,12 +854,14 @@ private: void collect_new_region_view (RegionView*); void collect_and_select_new_region_view (RegionView*); + Gtk::Menu section_box_menu; Gtk::Menu track_context_menu; Gtk::Menu track_region_context_menu; Gtk::Menu track_selection_context_menu; GdkEvent context_click_event; + void popup_section_box_menu (int, int); void popup_track_context_menu (int, int, ItemType, bool); Gtk::Menu* build_track_context_menu (); Gtk::Menu* build_track_bus_context_menu (); @@ -1795,6 +1799,7 @@ private: /* non-public event handlers */ + bool canvas_section_box_event (GdkEvent* event); bool canvas_playhead_cursor_event (GdkEvent* event, ArdourCanvas::Item*); bool track_canvas_scroll (GdkEventScroll* event); @@ -2099,6 +2104,8 @@ private: bool audio_region_selection_covers (samplepos_t where); + SectionBox* _section_box; + /* playhead and edit cursor */ EditorCursor* _playhead_cursor; diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index 2c3c691833..992a453a94 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -53,6 +53,7 @@ #include "editor_drag.h" #include "region_view.h" #include "editor_group_tabs.h" +#include "editor_section_box.h" #include "editor_summary.h" #include "video_timeline.h" #include "keyboard.h" @@ -143,13 +144,6 @@ Editor::initialize_canvas () _time_markers_group = new ArdourCanvas::Container (h_scroll_group); CANVAS_DEBUG_NAME (_time_markers_group, "time bars"); - /* group above rulers, to show selection triangles */ - _selection_marker_group = new ArdourCanvas::Container (h_scroll_group); - CANVAS_DEBUG_NAME (_selection_marker_group, "Canvas Selection Ruler"); - _selection_marker->start = new SelectionMarker (*this, *_selection_marker_group, "play head", ArdourMarker::SelectionStart); - _selection_marker->end = new SelectionMarker (*this, *_selection_marker_group, "play head", ArdourMarker::SelectionEnd); - _selection_marker_group->raise_to_top (); - /* Note that because of ascending-y-axis coordinates, this order is * bottom-to-top. But further note that the actual order is set in * ::update_ruler_visibility() @@ -279,6 +273,17 @@ Editor::initialize_canvas () _canvas_grid_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_grid_zone_event)); _canvas_grid_zone->set_ignore_events (true); + /* and now the timeline-selection rectangle which is controlled by the markers */ + _section_box = new SectionBox (*this, cursor_scroll_group); + _section_box->Event.connect (sigc::mem_fun (*this, &Editor::canvas_section_box_event)); + + /* group above rulers, to show selection triangles */ + _selection_marker_group = new ArdourCanvas::Container (cursor_scroll_group); + CANVAS_DEBUG_NAME (_selection_marker_group, "Canvas Selection Ruler"); + _selection_marker->start = new SelectionMarker (*this, *_selection_marker_group, "selection", ArdourMarker::SelectionStart); + _selection_marker->end = new SelectionMarker (*this, *_selection_marker_group, "selection", ArdourMarker::SelectionEnd); + _selection_marker_group->raise_to_top (); + /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, * they are passed to Editor-level handlers. */ @@ -1063,6 +1068,9 @@ Editor::color_handler() bbt_ruler->set_fill_color (base); bbt_ruler->set_outline_color (text); + _section_box->set_fill_color (UIConfiguration::instance().color_mod ("selection", "selection rect")); + _section_box->set_outline_color (UIConfiguration::instance().color ("selection")); + _playhead_cursor->set_color (UIConfiguration::instance().color ("play head")); meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar")); diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index 7ae9774bdd..aace8b0473 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -1188,7 +1188,7 @@ Editor::section_rect_event (GdkEvent* ev, Location* loc, ArdourCanvas::Rectangle break; } /* and show section context menu */ - //popup_section_box_menu (ev->button.button, ev->button.time); + popup_section_box_menu (ev->button.button, ev->button.time); return true; } break; @@ -1204,6 +1204,32 @@ Editor::canvas_playhead_cursor_event (GdkEvent *event, ArdourCanvas::Item* item) return typed_event (item, event, PlayheadCursorItem); } +bool +Editor::canvas_section_box_event (GdkEvent *event) +{ + switch (event->type) { + case GDK_BUTTON_PRESS: + if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier) + && event->button.button == 1) { + _drags->set (new CursorDrag (this, *_playhead_cursor, false), event); + } + /*fallthrough*/ + case GDK_2BUTTON_PRESS: + /*fallthrough*/ + case GDK_3BUTTON_PRESS: + return !Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier); + case GDK_BUTTON_RELEASE: + if (Keyboard::is_context_menu_event (&event->button)) { + popup_section_box_menu (event->button.button, event->button.time); + return true; + } + return false; + default: + break; + } + return false; +} + bool Editor::canvas_note_event (GdkEvent *event, ArdourCanvas::Item* item) { diff --git a/gtk2_ardour/editor_section_box.cc b/gtk2_ardour/editor_section_box.cc new file mode 100644 index 0000000000..84dab7f99c --- /dev/null +++ b/gtk2_ardour/editor_section_box.cc @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 Robin Gareus + * Copyright (C) 2023 Ben Loftis + * + * 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 + +#include "editor.h" +#include "editor_section_box.h" + +using namespace ARDOUR; + +SectionBox::SectionBox (Editor& ed, ArdourCanvas::Item* parent) + : ArdourCanvas::Rectangle (parent) + , _editor (ed) +{ + set (ArdourCanvas::Rect (0, 0, 0, ArdourCanvas::COORD_MAX)); + set_ignore_events (false); + + set_outline_what (ArdourCanvas::Rectangle::What (Rectangle::LEFT | Rectangle::RIGHT)); + set_outline (true); + set_fill (true); + + hide (); +} + +void +SectionBox::set_position (samplepos_t sample_start, samplepos_t sample_end) +{ + double const new_start = _editor.sample_to_pixel_unrounded (sample_start); + double const new_end = _editor.sample_to_pixel_unrounded (sample_end); + + if (rint (new_start) != rint (x0())) { + set_x0 (new_start + 0.5); // accommodate the 1/2 pixel "line" offset in cairo + } + + if (rint (new_end) != rint (x1())) { + set_x1 (new_end + 0.5); // accommodate the 1/2 pixel "line" offset in cairo + } +} diff --git a/gtk2_ardour/editor_section_box.h b/gtk2_ardour/editor_section_box.h new file mode 100644 index 0000000000..f993e0ab59 --- /dev/null +++ b/gtk2_ardour/editor_section_box.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Robin Gareus + * Copyright (C) 2023 Ben Loftis + * + * 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. + */ + +#ifndef __gtk_ardour_section_box_h__ +#define __gtk_ardour_section_box_h__ + +#include "ardour/types.h" + +#include "canvas/rectangle.h" +#include "canvas/types.h" + +class Editor; + +class SectionBox : public ArdourCanvas::Rectangle +{ +public: + SectionBox (Editor&, ArdourCanvas::Item*); + + void set_position (samplepos_t, samplepos_t); + +private: + Editor& _editor; +}; + +#endif // __gtk_ardour_editor_cursors_h__ diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 7f2f449e9b..c4facf0362 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -39,6 +39,7 @@ #include "editor.h" #include "editor_drag.h" #include "editor_routes.h" +#include "editor_section_box.h" #include "editor_sources.h" #include "actions.h" #include "audio_time_axis.h" @@ -1264,6 +1265,17 @@ Editor::presentation_info_changed (PropertyChange const & what_changed) } } +void +Editor::update_section_box () +{ + if (selection->tracks.size() == 0 && selection->regions.size () == 0 && selection->time.length() != 0) { + _section_box->set_position (selection->time.start_time().samples(), selection->time.end_time().samples()); + _section_box->show (); + } else { + _section_box->hide(); + } +} + void Editor::track_selection_changed () { @@ -1275,6 +1287,7 @@ Editor::track_selection_changed () play_solo_selection(false); update_selection_markers (); + update_section_box (); } void @@ -1294,6 +1307,8 @@ Editor::time_selection_changed () } update_selection_markers (); + update_section_box (); + for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { (*i)->show_selection (selection->time); } diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index cc3cf45594..fe2e723175 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -94,6 +94,7 @@ gtk2_ardour_sources = [ 'editor_cursors.cc', 'editor_drag.cc', 'editor_route_groups.cc', + 'editor_section_box.cc', 'editor_export_audio.cc', 'editor_group_tabs.cc', 'editor_keys.cc',