2022-01-25 17:19:20 -05:00
/*
* Copyright ( C ) 2009 - 2011 Carl Hetherington < carl @ carlh . net >
* Copyright ( C ) 2009 - 2014 David Robillard < d @ drobilla . net >
* Copyright ( C ) 2009 - 2017 Paul Davis < paul @ linuxaudiosystems . com >
* Copyright ( C ) 2013 - 2015 Nick Mainsbridge < mainsbridge @ gmail . com >
* Copyright ( C ) 2014 - 2019 Robin Gareus < robin @ gareus . org >
* Copyright ( C ) 2016 - 2018 Len Ovens < len @ ovenwerks . net >
*
* 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 <algorithm>
# include <cassert>
# include <cmath>
# include <cstdlib>
# include <list>
# include <vector>
# include "pbd/unwind.h"
# include "ardour/audio_track.h"
# include "ardour/debug.h"
# include "ardour/midi_track.h"
# include "ardour/route.h"
# include "ardour/selection.h"
# include "ardour/session.h"
# include "ardour/solo_isolate_control.h"
# include "ardour/utils.h"
# include "ardour/vca.h"
# include "ardour/vca_manager.h"
# include "gtkmm2ext/treeutils.h"
# include "widgets/tooltips.h"
# include "actions.h"
# include "ardour_ui.h"
# include "route_list_base.h"
# include "gui_thread.h"
# include "keyboard.h"
# include "public_editor.h"
# include "route_sorter.h"
# include "utils.h"
# include "pbd/i18n.h"
using namespace std ;
using namespace ARDOUR ;
using namespace ArdourWidgets ;
using namespace ARDOUR_UI_UTILS ;
using namespace PBD ;
using namespace Gtk ;
using namespace Gtkmm2ext ;
using namespace Glib ;
using Gtkmm2ext : : Keyboard ;
RouteListBase : : RouteListBase ( )
: _menu ( 0 )
, old_focus ( 0 )
, name_editable ( 0 )
, _ignore_reorder ( false )
, _ignore_visibility_change ( false )
, _ignore_selection_change ( false )
, _column_does_not_select ( false )
, _adding_routes ( false )
, _route_deletion_in_progress ( false )
{
add_name_column ( ) ;
#if 0
setup_col ( append_toggle ( _columns . visible , _columns . noop_true , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_visible_changed ) ) , S_ ( " Visible|V " ) , _ ( " Track/Bus visible ? " ) ) ;
2022-01-31 20:54:55 -05:00
setup_col ( append_toggle ( _columns . trigger , _columns . is_track , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_trigger_changed ) ) , S_ ( " Cues|C " ) , _ ( " Visible on Cues window ? " ) ) ;
2022-01-25 17:19:20 -05:00
setup_col ( append_toggle ( _columns . active , _columns . activatable , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_active_changed ) ) , S_ ( " Active|A " ) , _ ( " Track/Bus active ? " ) ) ;
append_col_input_active ( ) ;
append_col_rec_enable ( ) ;
append_col_rec_safe ( ) ;
append_col_mute ( ) ;
append_col_solo ( ) ;
# endif
_display . set_headers_visible ( true ) ;
_display . get_selection ( ) - > set_mode ( SELECTION_MULTIPLE ) ;
_display . get_selection ( ) - > signal_changed ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : selection_changed ) ) ;
_display . set_reorderable ( true ) ;
_display . set_name ( X_ ( " EditGroupList " ) ) ;
_display . set_rules_hint ( true ) ;
_display . set_size_request ( 100 , - 1 ) ;
_scroller . add ( _display ) ;
_scroller . set_policy ( POLICY_NEVER , POLICY_AUTOMATIC ) ;
_model = ListStore : : create ( _columns ) ;
_display . set_model ( _model ) ;
_display . get_selection ( ) - > set_select_function ( sigc : : mem_fun ( * this , & RouteListBase : : select_function ) ) ;
_model - > signal_row_deleted ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : row_deleted ) ) ;
_model - > signal_rows_reordered ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : reordered ) ) ;
_display . signal_button_press_event ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : button_press ) , false ) ;
_display . signal_button_release_event ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : button_release ) , false ) ;
_scroller . signal_key_press_event ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : key_press ) , false ) ;
_scroller . signal_focus_in_event ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : focus_in ) , false ) ;
_scroller . signal_focus_out_event ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : focus_out ) ) ;
_display . signal_enter_notify_event ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : enter_notify ) , false ) ;
_display . signal_leave_notify_event ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : leave_notify ) , false ) ;
_display . set_enable_search ( false ) ;
}
RouteListBase : : ~ RouteListBase ( )
{
delete _menu ;
}
void
2022-12-15 12:04:55 -05:00
RouteListBase : : setup_col ( Gtk : : TreeViewColumn * tvc , const char * label , const char * tooltip , bool require_mod_to_edit )
2022-01-25 17:19:20 -05:00
{
Gtk : : Label * l = manage ( new Label ( label ) ) ;
set_tooltip ( * l , tooltip ) ;
tvc - > set_widget ( * l ) ;
l - > show ( ) ;
2022-12-15 12:04:55 -05:00
if ( require_mod_to_edit ) {
/* Try to prevent single mouse presses from initiating edits.
* This relies on a hack in gtktreeview . c : gtk_treeview_button_press ( ) */
tvc - > set_data ( " mouse-edits-require-mod1 " , ( gpointer ) 0x1 ) ;
}
2022-01-25 17:19:20 -05:00
}
void
RouteListBase : : add_name_column ( )
{
Gtk : : TreeViewColumn * tvc = manage ( new Gtk : : TreeViewColumn ( " " , _columns . text ) ) ;
2022-12-15 12:04:55 -05:00
setup_col ( tvc , _ ( " Name " ) , ( " Track/Bus name " ) , true ) ;
2022-01-25 17:19:20 -05:00
2022-01-26 17:01:44 -05:00
CellRendererText * cell = dynamic_cast < CellRendererText * > ( tvc - > get_first_cell ( ) ) ;
2022-01-25 17:19:20 -05:00
cell - > signal_editing_started ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : name_edit_started ) ) ;
tvc - > set_sizing ( TREE_VIEW_COLUMN_FIXED ) ;
tvc - > set_expand ( true ) ;
tvc - > set_min_width ( 50 ) ;
cell - > property_editable ( ) = true ;
cell - > signal_editing_started ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : name_edit_started ) ) ;
cell - > signal_edited ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : name_edit ) ) ;
_display . append_column ( * tvc ) ;
}
void
RouteListBase : : append_col_rec_enable ( )
{
CellRendererPixbufMulti * cell ;
cell = append_cell ( S_ ( " Rec|R " ) , _ ( " Record enabled " ) , _columns . rec_state , _columns . is_track , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_rec_enable_changed ) ) ;
cell - > set_pixbuf ( 0 , : : get_icon ( " record-normal-disabled " ) ) ;
cell - > set_pixbuf ( 1 , : : get_icon ( " record-normal-in-progress " ) ) ;
cell - > set_pixbuf ( 2 , : : get_icon ( " record-normal-enabled " ) ) ;
cell - > set_pixbuf ( 3 , : : get_icon ( " record-step " ) ) ;
}
void
RouteListBase : : append_col_rec_safe ( )
{
CellRendererPixbufMulti * cell ;
2022-09-12 22:50:22 -04:00
cell = append_cell ( S_ ( " Rec|RS " ) , _ ( " Record Safe " ) , _columns . rec_safe , _columns . is_track , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_rec_safe_toggled ) ) ;
2022-01-25 17:19:20 -05:00
cell - > set_pixbuf ( 0 , : : get_icon ( " rec-safe-disabled " ) ) ;
cell - > set_pixbuf ( 1 , : : get_icon ( " rec-safe-enabled " ) ) ;
}
void
RouteListBase : : append_col_input_active ( )
{
CellRendererPixbufMulti * cell ;
cell = append_cell ( S_ ( " MidiInput|I " ) , _ ( " MIDI input enabled " ) , _columns . is_input_active , _columns . is_midi , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_input_active_changed ) ) ;
cell - > set_pixbuf ( 0 , : : get_icon ( " midi-input-inactive " ) ) ;
cell - > set_pixbuf ( 1 , : : get_icon ( " midi-input-active " ) ) ;
}
void
RouteListBase : : append_col_mute ( )
{
CellRendererPixbufMulti * cell ;
cell = append_cell ( S_ ( " Mute|M " ) , _ ( " Muted " ) , _columns . mute_state , _columns . noop_true , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_mute_enable_toggled ) ) ;
cell - > set_pixbuf ( Gtkmm2ext : : Off , : : get_icon ( " mute-disabled " ) ) ;
cell - > set_pixbuf ( Gtkmm2ext : : ImplicitActive , : : get_icon ( " muted-by-others " ) ) ;
cell - > set_pixbuf ( Gtkmm2ext : : ExplicitActive , : : get_icon ( " mute-enabled " ) ) ;
}
void
RouteListBase : : append_col_solo ( )
{
CellRendererPixbufMulti * cell ;
cell = append_cell ( S_ ( " Solo|S " ) , _ ( " Soloed " ) , _columns . solo_state , _columns . solo_visible , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_solo_enable_toggled ) ) ;
cell - > set_pixbuf ( Gtkmm2ext : : Off , : : get_icon ( " solo-disabled " ) ) ;
cell - > set_pixbuf ( Gtkmm2ext : : ExplicitActive , : : get_icon ( " solo-enabled " ) ) ;
cell - > set_pixbuf ( Gtkmm2ext : : ImplicitActive , : : get_icon ( " soloed-by-others " ) ) ;
cell = append_cell ( S_ ( " SoloIso|SI " ) , _ ( " Solo Isolated " ) , _columns . solo_isolate_state , _columns . solo_lock_iso_visible , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_solo_isolate_toggled ) ) ;
cell - > set_pixbuf ( 0 , : : get_icon ( " solo-isolate-disabled " ) ) ;
cell - > set_pixbuf ( 1 , : : get_icon ( " solo-isolate-enabled " ) ) ;
cell = append_cell ( S_ ( " SoloLock|SS " ) , _ ( " Solo Safe (Locked) " ) , _columns . solo_safe_state , _columns . solo_lock_iso_visible , sigc : : mem_fun ( * this , & RouteListBase : : on_tv_solo_safe_toggled ) ) ;
cell - > set_pixbuf ( 0 , : : get_icon ( " solo-safe-disabled " ) ) ;
cell - > set_pixbuf ( 1 , : : get_icon ( " solo-safe-enabled " ) ) ;
}
bool
RouteListBase : : focus_in ( GdkEventFocus * )
{
Window * win = dynamic_cast < Window * > ( _scroller . get_toplevel ( ) ) ;
if ( win ) {
old_focus = win - > get_focus ( ) ;
} else {
old_focus = 0 ;
}
name_editable = 0 ;
/* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
return true ;
}
bool
RouteListBase : : focus_out ( GdkEventFocus * )
{
if ( old_focus ) {
old_focus - > grab_focus ( ) ;
old_focus = 0 ;
}
return false ;
}
bool
RouteListBase : : enter_notify ( GdkEventCrossing * )
{
if ( name_editable ) {
return true ;
}
Keyboard : : magic_widget_grab_focus ( ) ;
return false ;
}
bool
RouteListBase : : leave_notify ( GdkEventCrossing * )
{
if ( old_focus ) {
old_focus - > grab_focus ( ) ;
old_focus = 0 ;
}
Keyboard : : magic_widget_drop_focus ( ) ;
return false ;
}
void
RouteListBase : : set_session ( Session * s )
{
SessionHandlePtr : : set_session ( s ) ;
initial_display ( ) ;
if ( _session ) {
2024-10-18 19:51:44 -04:00
_session - > vca_manager ( ) . VCAAdded . connect ( _session_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : add_masters , this , _1 ) , gui_context ( ) ) ;
_session - > RouteAdded . connect ( _session_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : add_routes , this , _1 ) , gui_context ( ) ) ;
_session - > SoloChanged . connect ( _session_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
_session - > RecordStateChanged . connect ( _session_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
PresentationInfo : : Change . connect ( _session_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : presentation_info_changed , this , _1 ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
}
}
void
RouteListBase : : on_tv_input_active_changed ( std : : string const & path_string )
{
Gtk : : TreeModel : : Row row = * _model - > get_iter ( Gtk : : TreeModel : : Path ( path_string ) ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = row [ _columns . stripable ] ;
std : : shared_ptr < MidiTrack > mt = std : : dynamic_pointer_cast < MidiTrack > ( stripable ) ;
2022-01-25 17:19:20 -05:00
if ( mt ) {
mt - > set_input_active ( ! mt - > input_active ( ) ) ;
}
}
void
RouteListBase : : on_tv_rec_enable_changed ( std : : string const & path_string )
{
Gtk : : TreeModel : : Row row = * _model - > get_iter ( Gtk : : TreeModel : : Path ( path_string ) ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = row [ _columns . stripable ] ;
std : : shared_ptr < AutomationControl > ac = stripable - > rec_enable_control ( ) ;
2022-01-25 17:19:20 -05:00
if ( ac ) {
ac - > set_value ( ! ac - > get_value ( ) , Controllable : : UseGroup ) ;
}
}
void
RouteListBase : : on_tv_rec_safe_toggled ( std : : string const & path_string )
{
Gtk : : TreeModel : : Row row = * _model - > get_iter ( Gtk : : TreeModel : : Path ( path_string ) ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = row [ _columns . stripable ] ;
std : : shared_ptr < AutomationControl > ac ( stripable - > rec_safe_control ( ) ) ;
2022-01-25 17:19:20 -05:00
if ( ac ) {
ac - > set_value ( ! ac - > get_value ( ) , Controllable : : UseGroup ) ;
}
}
void
RouteListBase : : on_tv_mute_enable_toggled ( std : : string const & path_string )
{
// Get the model row that has been toggled.
Gtk : : TreeModel : : Row row = * _model - > get_iter ( Gtk : : TreeModel : : Path ( path_string ) ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = row [ _columns . stripable ] ;
std : : shared_ptr < AutomationControl > ac ( stripable - > mute_control ( ) ) ;
2022-01-25 17:19:20 -05:00
if ( ac ) {
ac - > set_value ( ! ac - > get_value ( ) , Controllable : : UseGroup ) ;
}
}
void
RouteListBase : : on_tv_solo_enable_toggled ( std : : string const & path_string )
{
// Get the model row that has been toggled.
Gtk : : TreeModel : : Row row = * _model - > get_iter ( Gtk : : TreeModel : : Path ( path_string ) ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = row [ _columns . stripable ] ;
std : : shared_ptr < AutomationControl > ac ( stripable - > solo_control ( ) ) ;
2022-01-25 17:19:20 -05:00
if ( ac ) {
ac - > set_value ( ! ac - > get_value ( ) , Controllable : : UseGroup ) ;
}
}
void
RouteListBase : : on_tv_solo_isolate_toggled ( std : : string const & path_string )
{
// Get the model row that has been toggled.
Gtk : : TreeModel : : Row row = * _model - > get_iter ( Gtk : : TreeModel : : Path ( path_string ) ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = row [ _columns . stripable ] ;
std : : shared_ptr < AutomationControl > ac ( stripable - > solo_isolate_control ( ) ) ;
2022-01-25 17:19:20 -05:00
if ( ac ) {
ac - > set_value ( ! ac - > get_value ( ) , Controllable : : UseGroup ) ;
}
}
void
RouteListBase : : on_tv_solo_safe_toggled ( std : : string const & path_string )
{
// Get the model row that has been toggled.
Gtk : : TreeModel : : Row row = * _model - > get_iter ( Gtk : : TreeModel : : Path ( path_string ) ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = row [ _columns . stripable ] ;
std : : shared_ptr < AutomationControl > ac ( stripable - > solo_safe_control ( ) ) ;
2022-01-25 17:19:20 -05:00
if ( ac ) {
ac - > set_value ( ! ac - > get_value ( ) , Controllable : : UseGroup ) ;
}
}
void
RouteListBase : : build_menu ( )
{
using namespace Menu_Helpers ;
using namespace Gtk ;
_menu = new Menu ;
MenuList & items = _menu - > items ( ) ;
_menu - > set_name ( " ArdourContextMenu " ) ;
items . push_back ( MenuElem ( _ ( " Show All " ) , sigc : : bind ( sigc : : mem_fun ( * this , & RouteListBase : : set_all_audio_midi_visibility ) , 0 , true ) ) ) ;
items . push_back ( MenuElem ( _ ( " Hide All " ) , sigc : : bind ( sigc : : mem_fun ( * this , & RouteListBase : : set_all_audio_midi_visibility ) , 0 , false ) ) ) ;
items . push_back ( MenuElem ( _ ( " Show All Audio Tracks " ) , sigc : : bind ( sigc : : mem_fun ( * this , & RouteListBase : : set_all_audio_midi_visibility ) , 1 , true ) ) ) ;
items . push_back ( MenuElem ( _ ( " Hide All Audio Tracks " ) , sigc : : bind ( sigc : : mem_fun ( * this , & RouteListBase : : set_all_audio_midi_visibility ) , 1 , false ) ) ) ;
items . push_back ( MenuElem ( _ ( " Show All Midi Tracks " ) , sigc : : bind ( sigc : : mem_fun ( * this , & RouteListBase : : set_all_audio_midi_visibility ) , 3 , true ) ) ) ;
items . push_back ( MenuElem ( _ ( " Hide All Midi Tracks " ) , sigc : : bind ( sigc : : mem_fun ( * this , & RouteListBase : : set_all_audio_midi_visibility ) , 3 , false ) ) ) ;
items . push_back ( MenuElem ( _ ( " Show All Busses " ) , sigc : : bind ( sigc : : mem_fun ( * this , & RouteListBase : : set_all_audio_midi_visibility ) , 2 , true ) ) ) ;
items . push_back ( MenuElem ( _ ( " Hide All Busses " ) , sigc : : bind ( sigc : : mem_fun ( * this , & RouteListBase : : set_all_audio_midi_visibility ) , 2 , false ) ) ) ;
items . push_back ( MenuElem ( _ ( " Only Show Tracks with Regions Under Playhead " ) , sigc : : mem_fun ( * this , & RouteListBase : : show_tracks_with_regions_at_playhead ) ) ) ;
}
void
RouteListBase : : row_deleted ( Gtk : : TreeModel : : Path const & )
{
if ( ! _session | | _session - > deletion_in_progress ( ) ) {
return ;
}
/* this happens as the second step of a DnD within the treeview, and
* when a route is actually removed . we don ' t differentiate between
* the two cases .
*
* note that the sync_presentation_info_from_treeview ( ) step may not
* actually change any presentation info ( e . g . the last track may be
* removed , so all other tracks keep the same presentation info ) , which
* means that no redisplay would happen . so we have to force a
* redisplay .
*/
DEBUG_TRACE ( DEBUG : : OrderKeys , " editor routes treeview row deleted \n " ) ;
if ( ! _route_deletion_in_progress ) {
sync_presentation_info_from_treeview ( ) ;
}
}
void
RouteListBase : : reordered ( TreeModel : : Path const & , TreeModel : : iterator const & , int * /*what*/ )
{
/* reordering implies that RID's will change, so
* sync_presentation_info_from_treeview ( ) will cause a redisplay .
*/
DEBUG_TRACE ( DEBUG : : OrderKeys , " editor routes treeview reordered \n " ) ;
sync_presentation_info_from_treeview ( ) ;
}
void
RouteListBase : : on_tv_visible_changed ( std : : string const & path )
{
if ( ! _session | | _session - > deletion_in_progress ( ) ) {
return ;
}
if ( _ignore_visibility_change ) {
return ;
}
DisplaySuspender ds ;
TreeIter iter ;
if ( ( iter = _model - > get_iter ( path ) ) ) {
bool hidden = ( * iter ) [ _columns . visible ] ; // toggle -> invert flag
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * iter ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
if ( hidden ! = stripable - > presentation_info ( ) . hidden ( ) ) {
stripable - > presentation_info ( ) . set_hidden ( hidden ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Route > route = std : : dynamic_pointer_cast < Route > ( stripable ) ;
2022-01-25 17:19:20 -05:00
RouteGroup * rg = route ? route - > route_group ( ) : 0 ;
if ( rg & & rg - > is_active ( ) & & rg - > is_hidden ( ) ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < RouteList > rl ( rg - > route_list ( ) ) ;
2022-01-25 17:19:20 -05:00
for ( RouteList : : const_iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
( * i ) - > presentation_info ( ) . set_hidden ( hidden ) ;
}
}
}
}
}
void
RouteListBase : : on_tv_trigger_changed ( std : : string const & path )
{
if ( ! _session | | _session - > deletion_in_progress ( ) ) {
return ;
}
Gtk : : TreeModel : : Row row = * _model - > get_iter ( path ) ;
assert ( row [ _columns . is_track ] ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = row [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
bool const tt = row [ _columns . trigger ] ;
stripable - > presentation_info ( ) . set_trigger_track ( ! tt ) ;
}
void
RouteListBase : : on_tv_active_changed ( std : : string const & path )
{
if ( ! _session | | _session - > deletion_in_progress ( ) ) {
return ;
}
Gtk : : TreeModel : : Row row = * _model - > get_iter ( path ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = row [ _columns . stripable ] ;
std : : shared_ptr < Route > route = std : : dynamic_pointer_cast < Route > ( stripable ) ;
2022-01-25 17:19:20 -05:00
if ( route ) {
bool const active = row [ _columns . active ] ;
route - > set_active ( ! active , this ) ;
}
}
void
RouteListBase : : initial_display ( )
{
if ( ! _session ) {
clear ( ) ;
return ;
}
_model - > clear ( ) ;
StripableList sl ;
_session - > get_stripables ( sl ) ;
add_stripables ( sl ) ;
sync_treeview_from_presentation_info ( Properties : : order ) ;
}
void
RouteListBase : : add_masters ( VCAList & vlist )
{
StripableList sl ;
for ( VCAList : : iterator v = vlist . begin ( ) ; v ! = vlist . end ( ) ; + + v ) {
2023-02-16 18:33:28 -05:00
sl . push_back ( std : : dynamic_pointer_cast < Stripable > ( * v ) ) ;
2022-01-25 17:19:20 -05:00
}
add_stripables ( sl ) ;
}
void
RouteListBase : : add_routes ( RouteList & rlist )
{
StripableList sl ;
for ( RouteList : : iterator r = rlist . begin ( ) ; r ! = rlist . end ( ) ; + + r ) {
sl . push_back ( * r ) ;
}
add_stripables ( sl ) ;
}
void
RouteListBase : : add_stripables ( StripableList & slist )
{
PBD : : Unwinder < bool > at ( _adding_routes , true ) ;
Gtk : : TreeModel : : Children : : iterator insert_iter = _model - > children ( ) . end ( ) ;
slist . sort ( Stripable : : Sorter ( ) ) ;
for ( Gtk : : TreeModel : : Children : : iterator it = _model - > children ( ) . begin ( ) ; it ! = _model - > children ( ) . end ( ) ; + + it ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > r = ( * it ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
if ( r - > presentation_info ( ) . order ( ) = = ( slist . front ( ) - > presentation_info ( ) . order ( ) + slist . size ( ) ) ) {
insert_iter = it ;
break ;
}
}
{
PBD : : Unwinder < bool > uw ( _ignore_selection_change , true ) ;
_display . set_model ( Glib : : RefPtr < ListStore > ( ) ) ;
}
for ( StripableList : : iterator s = slist . begin ( ) ; s ! = slist . end ( ) ; + + s ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = * s ;
std : : shared_ptr < MidiTrack > midi_trk ;
std : : shared_ptr < Route > route ;
2022-01-25 17:19:20 -05:00
TreeModel : : Row row ;
2023-02-16 18:33:28 -05:00
if ( std : : dynamic_pointer_cast < VCA > ( stripable ) ) {
2022-01-25 17:19:20 -05:00
row = * ( _model - > insert ( insert_iter ) ) ;
row [ _columns . is_track ] = false ;
row [ _columns . is_input_active ] = false ;
row [ _columns . is_midi ] = false ;
row [ _columns . activatable ] = true ;
2023-02-16 18:33:28 -05:00
} else if ( ( route = std : : dynamic_pointer_cast < Route > ( * s ) ) ) {
2022-01-25 17:19:20 -05:00
if ( route - > is_auditioner ( ) ) {
continue ;
}
if ( route - > is_monitor ( ) ) {
continue ;
}
2024-01-02 08:26:56 -05:00
if ( route - > is_surround_master ( ) ) {
continue ;
}
2022-01-25 17:19:20 -05:00
row = * ( _model - > insert ( insert_iter ) ) ;
2023-02-16 18:33:28 -05:00
midi_trk = std : : dynamic_pointer_cast < MidiTrack > ( stripable ) ;
2022-01-25 17:19:20 -05:00
2023-02-16 18:33:28 -05:00
row [ _columns . is_track ] = ( std : : dynamic_pointer_cast < Track > ( stripable ) ! = 0 ) ;
2023-11-26 22:26:46 -05:00
row [ _columns . activatable ] = ! stripable - > is_singleton ( ) ;
2022-01-25 17:19:20 -05:00
if ( midi_trk ) {
row [ _columns . is_input_active ] = midi_trk - > input_active ( ) ;
row [ _columns . is_midi ] = true ;
} else {
row [ _columns . is_input_active ] = false ;
row [ _columns . is_midi ] = false ;
}
}
row [ _columns . noop_true ] = true ;
row [ _columns . text ] = stripable - > name ( ) ;
row [ _columns . visible ] = ! stripable - > presentation_info ( ) . hidden ( ) ;
row [ _columns . trigger ] = stripable - > presentation_info ( ) . trigger_track ( ) & & row [ _columns . is_track ] ;
row [ _columns . active ] = true ;
row [ _columns . stripable ] = stripable ;
row [ _columns . mute_state ] = RouteUI : : mute_active_state ( _session , stripable ) ;
row [ _columns . solo_state ] = RouteUI : : solo_active_state ( stripable ) ;
row [ _columns . solo_visible ] = ! stripable - > is_master ( ) ;
row [ _columns . solo_lock_iso_visible ] = row [ _columns . solo_visible ] & & row [ _columns . activatable ] ;
row [ _columns . solo_isolate_state ] = RouteUI : : solo_isolate_active_state ( stripable ) ;
row [ _columns . solo_safe_state ] = RouteUI : : solo_safe_active_state ( stripable ) ;
row [ _columns . name_editable ] = true ;
2023-02-16 18:33:28 -05:00
std : : weak_ptr < Stripable > ws ( stripable ) ;
2022-01-25 17:19:20 -05:00
/* for now, we need both of these. PropertyChanged covers on
* pre - defined , " global " things of interest to a
* UI . gui_changed covers arbitrary , un - enumerated , un - typed
* changes that may only be of interest to a particular
* UI ( e . g . track - height is not of any relevant to OSC )
*/
2024-10-18 19:51:44 -04:00
stripable - > PropertyChanged . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : route_property_changed , this , _1 , ws ) , gui_context ( ) ) ;
stripable - > presentation_info ( ) . PropertyChanged . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : route_property_changed , this , _1 , ws ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
2023-02-16 18:33:28 -05:00
if ( std : : dynamic_pointer_cast < Track > ( stripable ) ) {
std : : shared_ptr < Track > t = std : : dynamic_pointer_cast < Track > ( stripable ) ;
2024-10-18 19:51:44 -04:00
t - > rec_enable_control ( ) - > Changed . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
t - > rec_safe_control ( ) - > Changed . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
}
if ( midi_trk ) {
2024-10-18 19:51:44 -04:00
midi_trk - > StepEditStatusChange . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
midi_trk - > InputActiveChanged . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : update_input_active_display , this ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
}
2023-02-16 18:33:28 -05:00
std : : shared_ptr < AutomationControl > ac ;
2022-01-25 17:19:20 -05:00
if ( ( ac = stripable - > mute_control ( ) ) ! = 0 ) {
2024-10-18 19:51:44 -04:00
ac - > Changed . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
}
if ( ( ac = stripable - > solo_control ( ) ) ! = 0 ) {
2024-10-18 19:51:44 -04:00
ac - > Changed . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
}
if ( ( ac = stripable - > solo_isolate_control ( ) ) ! = 0 ) {
2024-10-18 19:51:44 -04:00
ac - > Changed . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
}
if ( ( ac = stripable - > solo_safe_control ( ) ) ! = 0 ) {
2024-10-18 19:51:44 -04:00
ac - > Changed . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
}
if ( route ) {
2024-10-18 19:51:44 -04:00
route - > active_changed . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : queue_idle_update , this ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
}
2024-10-18 19:51:44 -04:00
stripable - > DropReferences . connect ( _stripable_connections , invalidator ( _scroller ) , std : : bind ( & RouteListBase : : remove_strip , this , ws ) , gui_context ( ) ) ;
2022-01-25 17:19:20 -05:00
}
queue_idle_update ( ) ;
update_input_active_display ( ) ;
{
PBD : : Unwinder < bool > uw ( _ignore_selection_change , true ) ;
_display . set_model ( _model ) ;
/* set the treeview model selection state */
TreeModel : : Children rows = _model - > children ( ) ;
for ( TreeModel : : Children : : iterator ri = rows . begin ( ) ; ri ! = rows . end ( ) ; + + ri ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * ri ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
if ( stripable & & stripable - > is_selected ( ) ) {
_display . get_selection ( ) - > select ( * ri ) ;
} else {
_display . get_selection ( ) - > unselect ( * ri ) ;
}
}
}
}
void
2023-02-16 18:33:28 -05:00
RouteListBase : : remove_strip ( std : : weak_ptr < ARDOUR : : Stripable > ws )
2022-01-25 17:19:20 -05:00
{
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable ( ws . lock ( ) ) ;
2022-01-25 17:19:20 -05:00
if ( ! stripable ) {
return ;
}
TreeModel : : Children rows = _model - > children ( ) ;
TreeModel : : Children : : iterator ri ;
PBD : : Unwinder < bool > uw ( _ignore_selection_change , true ) ;
for ( ri = rows . begin ( ) ; ri ! = rows . end ( ) ; + + ri ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > s = ( * ri ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
if ( s = = stripable ) {
PBD : : Unwinder < bool > uw ( _route_deletion_in_progress , true ) ;
_model - > erase ( ri ) ;
break ;
}
}
}
void
2023-02-16 18:33:28 -05:00
RouteListBase : : route_property_changed ( const PropertyChange & what_changed , std : : weak_ptr < Stripable > s )
2022-01-25 17:19:20 -05:00
{
if ( _adding_routes ) {
return ;
}
PropertyChange interests ;
interests . add ( ARDOUR : : Properties : : name ) ;
interests . add ( ARDOUR : : Properties : : hidden ) ;
interests . add ( ARDOUR : : Properties : : trigger_track ) ;
if ( ! what_changed . contains ( interests ) ) {
return ;
}
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = s . lock ( ) ;
2022-01-25 17:19:20 -05:00
if ( ! stripable ) {
return ;
}
TreeModel : : Children rows = _model - > children ( ) ;
for ( TreeModel : : Children : : iterator i = rows . begin ( ) ; i ! = rows . end ( ) ; + + i ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > ss = ( * i ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
if ( ss ! = stripable ) {
continue ;
}
if ( what_changed . contains ( ARDOUR : : Properties : : name ) ) {
( * i ) [ _columns . text ] = stripable - > name ( ) ;
}
if ( what_changed . contains ( ARDOUR : : Properties : : hidden ) ) {
( * i ) [ _columns . visible ] = ! stripable - > presentation_info ( ) . hidden ( ) ;
}
if ( what_changed . contains ( ARDOUR : : Properties : : trigger_track ) ) {
( * i ) [ _columns . trigger ] = stripable - > presentation_info ( ) . trigger_track ( ) & & ( * i ) [ _columns . is_track ] ;
}
break ;
}
}
void
RouteListBase : : presentation_info_changed ( PropertyChange const & what_changed )
{
PropertyChange soh ;
soh . add ( Properties : : order ) ;
soh . add ( Properties : : selected ) ;
if ( what_changed . contains ( soh ) ) {
sync_treeview_from_presentation_info ( what_changed ) ;
}
}
void
RouteListBase : : sync_presentation_info_from_treeview ( )
{
if ( _ignore_reorder | | ! _session | | _session - > deletion_in_progress ( ) ) {
return ;
}
TreeModel : : Children rows = _model - > children ( ) ;
if ( rows . empty ( ) ) {
return ;
}
DEBUG_TRACE ( DEBUG : : OrderKeys , " editor sync presentation info from treeview \n " ) ;
PresentationInfo : : order_t order = 0 ;
PresentationInfo : : ChangeSuspender cs ;
for ( TreeModel : : Children : : iterator ri = rows . begin ( ) ; ri ! = rows . end ( ) ; + + ri ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * ri ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
bool visible = ( * ri ) [ _columns . visible ] ;
stripable - > presentation_info ( ) . set_hidden ( ! visible ) ;
stripable - > set_presentation_order ( order ) ;
+ + order ;
}
}
void
RouteListBase : : sync_treeview_from_presentation_info ( PropertyChange const & what_changed )
{
/* Some route order key(s) have been changed, make sure that
* we update out tree / list model and GUI to reflect the change .
*/
if ( _ignore_reorder | | ! _session | | _session - > deletion_in_progress ( ) ) {
return ;
}
TreeModel : : Children rows = _model - > children ( ) ;
if ( rows . empty ( ) ) {
return ;
}
DEBUG_TRACE ( DEBUG : : OrderKeys , " editor sync model from presentation info. \n " ) ;
bool changed = false ;
if ( what_changed . contains ( Properties : : order ) ) {
vector < int > neworder ;
uint32_t old_order = 0 ;
TreeOrderKeys sorted ;
for ( TreeModel : : Children : : iterator ri = rows . begin ( ) ; ri ! = rows . end ( ) ; + + ri , + + old_order ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * ri ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
/* use global order */
sorted . push_back ( TreeOrderKey ( old_order , stripable ) ) ;
}
TreeOrderKeySorter cmp ;
sort ( sorted . begin ( ) , sorted . end ( ) , cmp ) ;
neworder . assign ( sorted . size ( ) , 0 ) ;
uint32_t n = 0 ;
for ( TreeOrderKeys : : iterator sr = sorted . begin ( ) ; sr ! = sorted . end ( ) ; + + sr , + + n ) {
neworder [ n ] = sr - > old_display_order ;
if ( sr - > old_display_order ! = n ) {
changed = true ;
}
}
if ( changed ) {
Unwinder < bool > uw ( _ignore_reorder , true ) ;
/* prevent traverse_cells: assertion 'row_path != NULL'
* in case of DnD re - order : row - removed + row - inserted .
*
* The rows ( stripables ) are not actually removed from the model ,
* but only from the display in the DnDTreeView .
* - > reorder ( ) will fail to find the row_path .
* ( re - order drag - > remove row - > sync PI from TV - > notify - > sync TV from PI - > crash )
*/
Unwinder < bool > uw2 ( _ignore_selection_change , true ) ;
_display . unset_model ( ) ;
_model - > reorder ( neworder ) ;
_display . set_model ( _model ) ;
}
}
if ( changed | | what_changed . contains ( Properties : : selected ) ) {
/* by the time this is invoked, the GUI Selection model has
* already updated itself .
*/
PBD : : Unwinder < bool > uw ( _ignore_selection_change , true ) ;
/* set the treeview model selection state */
for ( TreeModel : : Children : : iterator ri = rows . begin ( ) ; ri ! = rows . end ( ) ; + + ri ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * ri ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
if ( stripable & & stripable - > is_selected ( ) ) {
_display . get_selection ( ) - > select ( * ri ) ;
} else {
_display . get_selection ( ) - > unselect ( * ri ) ;
}
}
}
}
void
RouteListBase : : set_all_audio_midi_visibility ( int which , bool yn )
{
TreeModel : : Children rows = _model - > children ( ) ;
TreeModel : : Children : : iterator i ;
DisplaySuspender ds ;
PBD : : Unwinder < bool > uw ( _ignore_visibility_change , true ) ;
for ( i = rows . begin ( ) ; i ! = rows . end ( ) ; + + i ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * i ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
/*
* which = 0 : any ( incl . VCA )
* which = 1 : audio - tracks
* which = 2 : busses
* which = 3 : midi - tracks
*/
2023-02-16 18:33:28 -05:00
bool is_audio = std : : dynamic_pointer_cast < AudioTrack > ( stripable ) ! = 0 ;
bool is_midi = std : : dynamic_pointer_cast < MidiTrack > ( stripable ) ! = 0 ;
bool is_bus = ! is_audio & & ! is_midi & & std : : dynamic_pointer_cast < Route > ( stripable ) ! = 0 ;
2022-01-25 17:19:20 -05:00
switch ( which ) {
case 0 :
( * i ) [ _columns . visible ] = yn ;
break ;
case 1 :
if ( is_audio ) {
( * i ) [ _columns . visible ] = yn ;
}
break ;
case 2 :
if ( is_bus ) {
( * i ) [ _columns . visible ] = yn ;
}
break ;
case 3 :
if ( is_midi ) {
( * i ) [ _columns . visible ] = yn ;
}
break ;
default :
assert ( 0 ) ;
}
}
sync_presentation_info_from_treeview ( ) ;
}
bool
RouteListBase : : key_press ( GdkEventKey * ev )
{
TreeViewColumn * col ;
TreePath path ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < RouteList > rl ( new RouteList ) ;
2022-01-25 17:19:20 -05:00
switch ( ev - > keyval ) {
case GDK_Tab :
case GDK_ISO_Left_Tab :
/* If we appear to be editing something, leave that cleanly and appropriately. */
if ( name_editable ) {
name_editable - > editing_done ( ) ;
name_editable = 0 ;
}
col = _display . get_column ( 0 ) ; /* track-name col */
if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : TertiaryModifier ) ) {
treeview_select_previous ( _display , _model , col ) ;
} else {
treeview_select_next ( _display , _model , col ) ;
}
return true ;
break ;
case ' m ' :
if ( get_relevant_routes ( rl ) ) {
_session - > set_controls ( route_list_to_control_list ( rl , & Stripable : : mute_control ) , rl - > front ( ) - > muted ( ) ? 0.0 : 1.0 , Controllable : : NoGroup ) ;
}
return true ;
break ;
case ' s ' :
if ( get_relevant_routes ( rl ) ) {
_session - > set_controls ( route_list_to_control_list ( rl , & Stripable : : solo_control ) , rl - > front ( ) - > self_soloed ( ) ? 0.0 : 1.0 , Controllable : : NoGroup ) ;
}
return true ;
break ;
case ' r ' :
if ( get_relevant_routes ( rl ) ) {
for ( RouteList : : const_iterator r = rl - > begin ( ) ; r ! = rl - > end ( ) ; + + r ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Track > t = std : : dynamic_pointer_cast < Track > ( * r ) ;
2022-01-25 17:19:20 -05:00
if ( t ) {
_session - > set_controls ( route_list_to_control_list ( rl , & Stripable : : rec_enable_control ) , ! t - > rec_enable_control ( ) - > get_value ( ) , Controllable : : NoGroup ) ;
break ;
}
}
}
break ;
default :
break ;
}
return false ;
}
bool
2023-02-16 18:33:28 -05:00
RouteListBase : : get_relevant_routes ( std : : shared_ptr < RouteList > rl )
2022-01-25 17:19:20 -05:00
{
RefPtr < TreeSelection > selection = _display . get_selection ( ) ;
TreePath path ;
TreeIter iter ;
if ( selection - > count_selected_rows ( ) ! = 0 ) {
/* use selection */
RefPtr < TreeModel > tm = RefPtr < TreeModel > : : cast_dynamic ( _model ) ;
iter = selection - > get_selected ( tm ) ;
} else {
/* use mouse pointer */
int x , y ;
int bx , by ;
_display . get_pointer ( x , y ) ;
_display . convert_widget_to_bin_window_coords ( x , y , bx , by ) ;
if ( _display . get_path_at_pos ( bx , by , path ) ) {
iter = _model - > get_iter ( path ) ;
}
}
if ( iter ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * iter ) [ _columns . stripable ] ;
std : : shared_ptr < Route > route = std : : dynamic_pointer_cast < Route > ( stripable ) ;
2022-01-25 17:19:20 -05:00
if ( route ) {
rl - > push_back ( route ) ;
}
}
return ! rl - > empty ( ) ;
}
bool
RouteListBase : : select_function ( const Glib : : RefPtr < Gtk : : TreeModel > & , const Gtk : : TreeModel : : Path & , bool )
{
return ! _column_does_not_select ;
}
bool
RouteListBase : : button_release ( GdkEventButton * )
{
_column_does_not_select = false ;
return false ;
}
bool
RouteListBase : : button_press ( GdkEventButton * ev )
{
if ( Keyboard : : is_context_menu_event ( ev ) ) {
if ( _menu = = 0 ) {
build_menu ( ) ;
}
_menu - > popup ( ev - > button , ev - > time ) ;
return true ;
}
TreeModel : : Path path ;
TreeViewColumn * tvc ;
int cell_x ;
int cell_y ;
if ( ! _display . get_path_at_pos ( ( int ) ev - > x , ( int ) ev - > y , path , tvc , cell_x , cell_y ) ) {
/* cancel selection */
_display . get_selection ( ) - > unselect_all ( ) ;
/* end any editing by grabbing focus */
_display . grab_focus ( ) ;
return true ;
}
if ( no_select_columns . find ( tvc ) ! = no_select_columns . end ( ) ) {
_column_does_not_select = true ;
}
//Scroll editor canvas to selected track
if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : PrimaryModifier ) ) {
PublicEditor & e ( PublicEditor : : instance ( ) ) ;
Gtk : : TreeModel : : Row row = * _model - > get_iter ( path ) ;
TimeAxisView * tv = e . time_axis_view_from_stripable ( row [ _columns . stripable ] ) ;
if ( tv ) {
e . ensure_time_axis_view_is_visible ( * tv , true ) ;
}
}
return false ;
}
void
RouteListBase : : selection_changed ( )
{
if ( _ignore_selection_change | | _column_does_not_select ) {
return ;
}
PublicEditor & e ( PublicEditor : : instance ( ) ) ;
TrackViewList selected ;
if ( _display . get_selection ( ) - > count_selected_rows ( ) > 0 ) {
TreeView : : Selection : : ListHandle_Path rows = _display . get_selection ( ) - > get_selected_rows ( ) ;
for ( TreeView : : Selection : : ListHandle_Path : : iterator i = rows . begin ( ) ; i ! = rows . end ( ) ; + + i ) {
TreeIter iter ;
if ( ( iter = _model - > get_iter ( * i ) ) ) {
TimeAxisView * tv = e . time_axis_view_from_stripable ( ( * iter ) [ _columns . stripable ] ) ;
if ( tv ) {
selected . push_back ( tv ) ;
}
}
}
}
e . begin_reversible_selection_op ( X_ ( " Select Track from Route List " ) ) ;
Selection & s ( e . get_selection ( ) ) ;
if ( selected . empty ( ) ) {
s . clear_tracks ( ) ;
} else {
s . set ( selected ) ;
e . ensure_time_axis_view_is_visible ( * ( selected . front ( ) ) , true ) ;
}
e . commit_reversible_selection_op ( ) ;
}
void
RouteListBase : : update_input_active_display ( )
{
TreeModel : : Children rows = _model - > children ( ) ;
TreeModel : : Children : : iterator i ;
for ( i = rows . begin ( ) ; i ! = rows . end ( ) ; + + i ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * i ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
2023-02-16 18:33:28 -05:00
if ( std : : dynamic_pointer_cast < Track > ( stripable ) ) {
std : : shared_ptr < MidiTrack > mt = std : : dynamic_pointer_cast < MidiTrack > ( stripable ) ;
2022-01-25 17:19:20 -05:00
if ( mt ) {
( * i ) [ _columns . is_input_active ] = mt - > input_active ( ) ;
}
}
}
}
void
RouteListBase : : queue_idle_update ( )
{
if ( ! _idle_update_connection . connected ( ) ) {
_idle_update_connection = Glib : : signal_idle ( ) . connect ( sigc : : mem_fun ( * this , & RouteListBase : : idle_update_mute_rec_solo_etc ) ) ;
}
}
bool
RouteListBase : : idle_update_mute_rec_solo_etc ( )
{
TreeModel : : Children rows = _model - > children ( ) ;
TreeModel : : Children : : iterator i ;
for ( i = rows . begin ( ) ; i ! = rows . end ( ) ; + + i ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * i ) [ _columns . stripable ] ;
std : : shared_ptr < Route > route = std : : dynamic_pointer_cast < Route > ( stripable ) ;
2022-01-25 17:19:20 -05:00
( * i ) [ _columns . mute_state ] = RouteUI : : mute_active_state ( _session , stripable ) ;
( * i ) [ _columns . solo_state ] = RouteUI : : solo_active_state ( stripable ) ;
( * i ) [ _columns . solo_isolate_state ] = RouteUI : : solo_isolate_active_state ( stripable ) ? 1 : 0 ;
( * i ) [ _columns . solo_safe_state ] = RouteUI : : solo_safe_active_state ( stripable ) ? 1 : 0 ;
if ( route ) {
( * i ) [ _columns . active ] = route - > active ( ) ;
} else {
( * i ) [ _columns . active ] = true ;
}
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Track > trk ( std : : dynamic_pointer_cast < Track > ( route ) ) ;
2022-01-25 17:19:20 -05:00
if ( trk ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < MidiTrack > mt = std : : dynamic_pointer_cast < MidiTrack > ( route ) ;
2022-01-25 17:19:20 -05:00
if ( trk - > rec_enable_control ( ) - > get_value ( ) ) {
2024-09-30 18:04:19 -04:00
if ( _session - > record_status ( ) = = Recording ) {
2022-01-25 17:19:20 -05:00
( * i ) [ _columns . rec_state ] = 1 ;
} else {
( * i ) [ _columns . rec_state ] = 2 ;
}
} else if ( mt & & mt - > step_editing ( ) ) {
( * i ) [ _columns . rec_state ] = 3 ;
} else {
( * i ) [ _columns . rec_state ] = 0 ;
}
( * i ) [ _columns . rec_safe ] = trk - > rec_safe_control ( ) - > get_value ( ) ;
( * i ) [ _columns . name_editable ] = ! trk - > rec_enable_control ( ) - > get_value ( ) ;
}
}
return false ; // do not call again (until needed)
}
void
RouteListBase : : clear ( )
{
PBD : : Unwinder < bool > uw ( _ignore_selection_change , true ) ;
_stripable_connections . drop_connections ( ) ;
_display . set_model ( Glib : : RefPtr < Gtk : : TreeStore > ( 0 ) ) ;
_model - > clear ( ) ;
_display . set_model ( _model ) ;
}
void
RouteListBase : : name_edit_started ( CellEditable * ce , const Glib : : ustring & )
{
name_editable = ce ;
/* give it a special name */
Gtk : : Entry * e = dynamic_cast < Gtk : : Entry * > ( ce ) ;
if ( e ) {
e - > set_name ( X_ ( " RouteNameEditorEntry " ) ) ;
}
}
void
RouteListBase : : name_edit ( std : : string const & path , std : : string const & new_text )
{
name_editable = 0 ;
TreeIter iter = _model - > get_iter ( path ) ;
if ( ! iter ) {
return ;
}
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * iter ) [ _columns . stripable ] ;
2022-01-25 17:19:20 -05:00
if ( stripable & & stripable - > name ( ) ! = new_text ) {
stripable - > set_name ( new_text ) ;
}
}
void
RouteListBase : : show_tracks_with_regions_at_playhead ( )
{
2023-02-16 18:33:28 -05:00
std : : shared_ptr < RouteList > const r = _session - > get_routes_with_regions_at ( timepos_t ( _session - > transport_sample ( ) ) ) ;
2022-01-25 17:19:20 -05:00
DisplaySuspender ds ;
TreeModel : : Children rows = _model - > children ( ) ;
for ( TreeModel : : Children : : iterator i = rows . begin ( ) ; i ! = rows . end ( ) ; + + i ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > stripable = ( * i ) [ _columns . stripable ] ;
std : : shared_ptr < Route > route = std : : dynamic_pointer_cast < Route > ( stripable ) ;
2022-01-25 17:19:20 -05:00
bool to_show = std : : find ( r - > begin ( ) , r - > end ( ) , route ) ! = r - > end ( ) ;
stripable - > presentation_info ( ) . set_hidden ( ! to_show ) ;
}
}