2021-11-08 20:27:38 -05:00
/*
* Copyright ( C ) 2021 Robin Gareus < robin @ gareus . org >
*
* 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 .
*/
# ifdef WAF_BUILD
# include "gtk2ardour-config.h"
# endif
2022-01-03 20:20:54 -05:00
# include <list>
2021-11-08 20:27:38 -05:00
# include <gtkmm/label.h>
2021-11-10 19:15:00 -05:00
# include "pbd/properties.h"
2021-11-08 20:27:38 -05:00
# include "gtkmm2ext/gtk_ui.h"
# include "gtkmm2ext/keyboard.h"
# include "gtkmm2ext/window_title.h"
2021-12-07 16:47:42 -05:00
# include "widgets/ardour_spacer.h"
2022-01-03 20:20:54 -05:00
# include "ardour/audio_track.h"
# include "ardour/audioregion.h"
# include "ardour/midi_region.h"
# include "ardour/midi_track.h"
# include "ardour/region_factory.h"
# include "ardour/profile.h"
2022-01-03 17:57:50 -05:00
# include "ardour/smf_source.h"
2021-11-08 20:27:38 -05:00
# include "actions.h"
# include "ardour_ui.h"
2021-12-02 00:52:03 -05:00
# include "editor.h"
2021-11-08 20:27:38 -05:00
# include "gui_thread.h"
# include "public_editor.h"
2021-11-10 19:15:00 -05:00
# include "timers.h"
2021-12-02 00:52:03 -05:00
2021-12-12 07:43:40 -05:00
# include "trigger_page.h"
# include "trigger_strip.h"
2022-01-10 22:06:52 -05:00
# include "triggerbox_ui.h"
2021-11-08 20:27:38 -05:00
# include "ui_config.h"
# include "utils.h"
# include "pbd/i18n.h"
2021-11-10 19:15:00 -05:00
# define PX_SCALE(px) std::max ((float)px, rintf ((float)px* UIConfiguration::instance ().get_ui_scale ()))
2021-11-08 20:27:38 -05:00
using namespace ARDOUR ;
2021-12-07 16:47:42 -05:00
using namespace ArdourWidgets ;
2021-11-08 20:27:38 -05:00
using namespace Gtkmm2ext ;
using namespace Gtk ;
using namespace std ;
TriggerPage : : TriggerPage ( )
2022-01-31 21:14:33 -05:00
: Tabbable ( _content , _ ( " Cues " ) , X_ ( " trigger " ) )
2021-12-17 08:49:19 -05:00
, _cue_area_frame ( 0.5 , 0 , 1.0 , 0 )
2022-10-04 21:55:15 -04:00
, _cue_box ( 16 , 16 * TriggerBox : : default_triggers_per_box )
2022-01-21 18:17:53 -05:00
, _master_widget ( 16 , 16 )
2021-12-12 08:32:26 -05:00
, _master ( _master_widget . root ( ) )
2022-02-02 23:39:48 -05:00
, _selection ( * this , * this )
2021-11-08 20:27:38 -05:00
{
load_bindings ( ) ;
register_actions ( ) ;
2021-12-17 08:49:19 -05:00
/* Match TriggerStrip::_name_button height */
ArdourButton * spacer = manage ( new ArdourButton ( ArdourButton : : Text ) ) ;
spacer - > set_name ( " mixer strip button " ) ;
spacer - > set_sensitive ( false ) ;
spacer - > set_text ( " " ) ;
/* left-side, fixed-size cue-box */
_cue_area_box . set_spacing ( 2 ) ;
_cue_area_box . pack_start ( * spacer , Gtk : : PACK_SHRINK ) ;
_cue_area_box . pack_start ( _cue_box , Gtk : : PACK_SHRINK ) ;
_cue_area_box . pack_start ( _master_widget , Gtk : : PACK_SHRINK ) ;
/* left-side frame, same layout as TriggerStrip.
* use Alignment instead of Frame with SHADOW_IN ( 2 px )
* + 1 px padding for _strip_scroller frame - > 3 px top padding
*/
_cue_area_frame . set_padding ( 3 , 1 , 1 , 1 ) ;
_cue_area_frame . add ( _cue_area_box ) ;
2021-11-10 19:15:00 -05:00
_strip_scroller . add ( _strip_packer ) ;
_strip_scroller . set_policy ( Gtk : : POLICY_ALWAYS , Gtk : : POLICY_AUTOMATIC ) ;
2021-12-17 08:49:19 -05:00
/* Last item of strip packer, "+" background */
2021-11-10 19:15:00 -05:00
_strip_packer . pack_end ( _no_strips , true , true ) ;
2022-01-26 16:29:36 -05:00
_no_strips . set_can_focus ( ) ;
2021-12-17 09:30:14 -05:00
_no_strips . add_events ( Gdk : : BUTTON_PRESS_MASK | Gdk : : BUTTON_RELEASE_MASK ) ;
2021-12-14 15:03:48 -05:00
_no_strips . set_size_request ( PX_SCALE ( 20 ) , - 1 ) ;
2022-01-14 21:36:50 -05:00
_no_strips . signal_expose_event ( ) . connect ( sigc : : bind ( sigc : : ptr_fun ( & ArdourWidgets : : ArdourIcon : : expose_with_text ) , & _no_strips , ArdourWidgets : : ArdourIcon : : ShadedPlusSign ,
2022-01-16 18:15:46 -05:00
_ ( " Drop a clip here \n to create a new Track " ) ) ) ;
2021-12-17 09:30:14 -05:00
_no_strips . signal_button_press_event ( ) . connect ( sigc : : mem_fun ( * this , & TriggerPage : : no_strip_button_event ) ) ;
_no_strips . signal_button_release_event ( ) . connect ( sigc : : mem_fun ( * this , & TriggerPage : : no_strip_button_event ) ) ;
2022-01-03 17:57:50 -05:00
_no_strips . signal_drag_motion ( ) . connect ( sigc : : mem_fun ( * this , & TriggerPage : : no_strip_drag_motion ) ) ;
_no_strips . signal_drag_data_received ( ) . connect ( sigc : : mem_fun ( * this , & TriggerPage : : no_strip_drag_data_received ) ) ;
std : : vector < Gtk : : TargetEntry > target_table ;
2022-01-18 00:52:57 -05:00
target_table . push_back ( Gtk : : TargetEntry ( " x-ardour/region.pbdid " , Gtk : : TARGET_SAME_APP ) ) ;
2022-01-03 17:57:50 -05:00
target_table . push_back ( Gtk : : TargetEntry ( " text/uri-list " ) ) ;
target_table . push_back ( Gtk : : TargetEntry ( " text/plain " ) ) ;
target_table . push_back ( Gtk : : TargetEntry ( " application/x-rootwin-drop " ) ) ;
2022-01-28 20:57:25 -05:00
_no_strips . drag_dest_set ( target_table , DEST_DEFAULT_ALL , Gdk : : ACTION_COPY ) ;
2021-11-10 19:15:00 -05:00
2021-12-17 08:49:19 -05:00
_strip_group_box . pack_start ( _cue_area_frame , false , false ) ;
2021-11-10 19:15:00 -05:00
_strip_group_box . pack_start ( _strip_scroller , true , true ) ;
2022-01-17 16:06:48 -05:00
/* sidebar */
_sidebar_notebook . set_show_tabs ( true ) ;
_sidebar_notebook . set_scrollable ( true ) ;
_sidebar_notebook . popup_disable ( ) ;
_sidebar_notebook . set_tab_pos ( Gtk : : POS_RIGHT ) ;
_sidebar_vbox . pack_start ( _sidebar_notebook ) ;
add_sidebar_page ( _ ( " Clips " ) , _trigger_clip_picker ) ;
2022-01-26 11:20:16 -05:00
add_sidebar_page ( _ ( " Tracks " ) , _trigger_route_list . widget ( ) ) ;
2022-01-18 20:42:09 -05:00
add_sidebar_page ( _ ( " Sources " ) , _trigger_source_list . widget ( ) ) ;
2022-01-17 21:39:49 -05:00
add_sidebar_page ( _ ( " Regions " ) , _trigger_region_list . widget ( ) ) ;
2022-01-17 16:06:48 -05:00
2021-12-17 08:49:19 -05:00
/* Upper pane ([slot | strips] | file browser) */
2021-11-10 19:15:00 -05:00
_pane_upper . add ( _strip_group_box ) ;
2022-01-17 16:06:48 -05:00
_pane_upper . add ( _sidebar_vbox ) ;
2021-11-10 19:15:00 -05:00
2021-12-17 08:49:19 -05:00
/* Bottom -- Properties of selected Slot/Region */
Gtk : : Table * table = manage ( new Gtk : : Table ) ;
table - > set_homogeneous ( false ) ;
2022-01-17 14:48:41 -05:00
table - > set_spacings ( 8 ) ; //match to slot_properties_box::set_spacings
2021-12-17 08:49:19 -05:00
table - > set_border_width ( 8 ) ;
int col = 0 ;
2022-01-24 13:28:58 -05:00
table - > attach ( _slot_prop_box , col , col + 1 , 0 , 1 , Gtk : : FILL , Gtk : : SHRINK | Gtk : : FILL ) ;
2021-12-17 08:49:19 -05:00
2022-01-17 14:11:14 -05:00
col = 1 ;
2022-01-24 13:28:58 -05:00
table - > attach ( _audio_trig_box , col , col + 1 , 0 , 1 , Gtk : : FILL , Gtk : : SHRINK | Gtk : : FILL ) ;
2021-12-17 08:49:19 -05:00
+ + col ;
2022-01-24 12:53:58 -05:00
# ifdef MIDI_PROPERTIES_BOX_IMPLEMENTED
2022-01-17 14:11:14 -05:00
col = 2 ;
2022-01-17 14:48:41 -05:00
table - > attach ( _midi_trig_box , col , col + 1 , 0 , 1 , Gtk : : FILL , Gtk : : SHRINK ) ;
2021-12-17 08:49:19 -05:00
+ + col ;
2022-01-18 11:14:08 -05:00
# endif
2021-12-17 08:49:19 -05:00
_parameter_box . pack_start ( * table ) ;
2021-11-10 19:15:00 -05:00
/* Top-level Layout */
2022-01-26 16:25:57 -05:00
_content . pack_start ( _pane_upper , true , true ) ;
_content . pack_start ( _parameter_box , false , false ) ;
2021-11-08 20:27:38 -05:00
_content . show ( ) ;
2021-11-10 19:15:00 -05:00
/* Show all */
_pane_upper . show ( ) ;
_strip_group_box . show ( ) ;
_strip_scroller . show ( ) ;
_strip_packer . show ( ) ;
2021-12-17 08:49:19 -05:00
_cue_area_frame . show_all ( ) ;
2021-12-12 08:25:49 -05:00
_trigger_clip_picker . show ( ) ;
2021-12-14 15:03:48 -05:00
_no_strips . show ( ) ;
2022-01-17 16:06:48 -05:00
_sidebar_notebook . show_all ( ) ;
2021-11-10 19:15:00 -05:00
2021-11-08 20:27:38 -05:00
/* setup keybidings */
_content . set_data ( " ardour-bindings " , bindings ) ;
/* subscribe to signals */
Config - > ParameterChanged . connect ( * this , invalidator ( * this ) , boost : : bind ( & TriggerPage : : parameter_changed , this , _1 ) , gui_context ( ) ) ;
2021-11-10 19:15:00 -05:00
PresentationInfo : : Change . connect ( * this , invalidator ( * this ) , boost : : bind ( & TriggerPage : : pi_property_changed , this , _1 ) , gui_context ( ) ) ;
2021-11-08 20:27:38 -05:00
/* init */
update_title ( ) ;
2021-11-10 19:15:00 -05:00
/* Restore pane state */
float fract ;
XMLNode const * settings = ARDOUR_UI : : instance ( ) - > trigger_page_settings ( ) ;
if ( ! settings | | ! settings - > get_property ( " triggerpage-hpane-pos " , fract ) | | fract > 1.0 ) {
fract = 0.75f ;
}
_pane_upper . set_divider ( 0 , fract ) ;
2021-11-08 20:27:38 -05:00
}
TriggerPage : : ~ TriggerPage ( )
{
}
Gtk : : Window *
TriggerPage : : use_own_window ( bool and_fill_it )
{
bool new_window = ! own_window ( ) ;
Gtk : : Window * win = Tabbable : : use_own_window ( and_fill_it ) ;
if ( win & & new_window ) {
win - > set_name ( " TriggerWindow " ) ;
2022-01-31 21:14:33 -05:00
ARDOUR_UI : : instance ( ) - > setup_toplevel_window ( * win , _ ( " Cues " ) , this ) ;
2021-11-08 20:27:38 -05:00
win - > signal_event ( ) . connect ( sigc : : bind ( sigc : : ptr_fun ( & Keyboard : : catch_user_event_for_pre_dialog_focus ) , win ) ) ;
win - > set_data ( " ardour-bindings " , bindings ) ;
update_title ( ) ;
#if 0 // TODO
if ( ! win - > get_focus ( ) ) {
win - > set_focus ( scroller ) ;
}
# endif
}
contents ( ) . show ( ) ;
return win ;
}
XMLNode &
2022-04-06 23:56:45 -04:00
TriggerPage : : get_state ( ) const
2021-11-08 20:27:38 -05:00
{
XMLNode * node = new XMLNode ( X_ ( " TriggerPage " ) ) ;
node - > add_child_nocopy ( Tabbable : : get_state ( ) ) ;
2021-11-10 19:15:00 -05:00
node - > set_property ( X_ ( " triggerpage-hpane-pos " ) , _pane_upper . get_divider ( ) ) ;
2022-01-17 16:06:48 -05:00
node - > set_property ( X_ ( " triggerpage-sidebar-page " ) , _sidebar_notebook . get_current_page ( ) ) ;
2021-11-08 20:27:38 -05:00
return * node ;
}
int
TriggerPage : : set_state ( const XMLNode & node , int version )
{
2022-01-17 16:06:48 -05:00
int32_t sidebar_page ;
if ( node . get_property ( X_ ( " triggerpage-sidebar-page " ) , sidebar_page ) ) {
_sidebar_notebook . set_current_page ( sidebar_page ) ;
}
2021-11-08 20:27:38 -05:00
return Tabbable : : set_state ( node , version ) ;
}
void
TriggerPage : : load_bindings ( )
{
2022-01-31 20:26:39 -05:00
bindings = Bindings : : get_bindings ( X_ ( " Cues " ) ) ;
2021-11-08 20:27:38 -05:00
}
void
TriggerPage : : set_session ( Session * s )
{
SessionHandlePtr : : set_session ( s ) ;
2021-12-23 23:14:48 -05:00
_cue_box . set_session ( s ) ;
2021-12-16 18:36:06 -05:00
_trigger_clip_picker . set_session ( s ) ;
2021-12-17 09:30:14 -05:00
_master . set_session ( s ) ;
2022-01-18 20:42:09 -05:00
_trigger_source_list . set_session ( s ) ;
2022-01-17 21:39:49 -05:00
_trigger_region_list . set_session ( s ) ;
2022-01-26 11:20:16 -05:00
_trigger_route_list . set_session ( s ) ;
2021-12-16 18:40:38 -05:00
2021-11-08 20:27:38 -05:00
if ( ! _session ) {
2022-02-02 23:39:48 -05:00
_selection . clear ( ) ;
2021-11-08 20:27:38 -05:00
return ;
}
2021-11-10 19:15:00 -05:00
XMLNode * node = ARDOUR_UI : : instance ( ) - > trigger_page_settings ( ) ;
2021-11-08 20:27:38 -05:00
set_state ( * node , Stateful : : loading_state_version ) ;
_session - > DirtyChanged . connect ( _session_connections , invalidator ( * this ) , boost : : bind ( & TriggerPage : : update_title , this ) , gui_context ( ) ) ;
_session - > StateSaved . connect ( _session_connections , invalidator ( * this ) , boost : : bind ( & TriggerPage : : update_title , this ) , gui_context ( ) ) ;
2021-11-10 19:15:00 -05:00
_session - > RouteAdded . connect ( _session_connections , invalidator ( * this ) , boost : : bind ( & TriggerPage : : add_routes , this , _1 ) , gui_context ( ) ) ;
TriggerStrip : : CatchDeletion . connect ( * this , invalidator ( * this ) , boost : : bind ( & TriggerPage : : remove_route , this , _1 ) , gui_context ( ) ) ;
2021-11-08 20:27:38 -05:00
_session - > config . ParameterChanged . connect ( _session_connections , invalidator ( * this ) , boost : : bind ( & TriggerPage : : parameter_changed , this , _1 ) , gui_context ( ) ) ;
2021-12-12 07:43:40 -05:00
Editor : : instance ( ) . get_selection ( ) . TriggersChanged . connect ( sigc : : mem_fun ( * this , & TriggerPage : : selection_changed ) ) ;
2021-12-02 00:52:03 -05:00
2021-11-10 19:15:00 -05:00
initial_track_display ( ) ;
2021-12-12 08:32:26 -05:00
_slot_prop_box . set_session ( s ) ;
2021-12-02 11:50:19 -05:00
2021-12-15 19:08:15 -05:00
_audio_trig_box . set_session ( s ) ;
2021-12-02 11:50:19 -05:00
2021-12-15 19:08:15 -05:00
_midi_trig_box . set_session ( s ) ;
2021-12-02 00:52:03 -05:00
2021-11-08 20:27:38 -05:00
update_title ( ) ;
2021-11-10 19:15:00 -05:00
start_updating ( ) ;
2021-12-12 07:43:40 -05:00
selection_changed ( ) ;
2022-02-02 23:39:48 -05:00
PBD : : PropertyChange sc ;
sc . add ( Properties : : selected ) ;
_selection . presentation_info_changed ( sc ) ;
2021-11-08 20:27:38 -05:00
}
void
TriggerPage : : session_going_away ( )
{
ENSURE_GUI_THREAD ( * this , & TriggerPage : : session_going_away ) ;
2021-11-10 19:15:00 -05:00
stop_updating ( ) ;
#if 0
/* DropReferneces calls RouteUI::self_delete -> CatchDeletion .. */
for ( list < TriggerStrip * > : : iterator i = _strips . begin ( ) ; i ! = _strips . end ( ) ; + + i ) {
delete ( * i ) ;
}
# endif
2022-02-02 23:39:48 -05:00
_selection . clear ( ) ;
2021-11-10 19:15:00 -05:00
_strips . clear ( ) ;
2021-11-08 20:27:38 -05:00
SessionHandlePtr : : session_going_away ( ) ;
update_title ( ) ;
}
void
TriggerPage : : update_title ( )
{
if ( ! own_window ( ) ) {
return ;
}
if ( _session ) {
string n ;
if ( _session - > snap_name ( ) ! = _session - > name ( ) ) {
n = _session - > snap_name ( ) ;
} else {
n = _session - > name ( ) ;
}
if ( _session - > dirty ( ) ) {
n = " * " + n ;
}
WindowTitle title ( n ) ;
2022-01-31 21:14:33 -05:00
title + = S_ ( " Window|Cues " ) ;
2021-11-08 20:27:38 -05:00
title + = Glib : : get_application_name ( ) ;
own_window ( ) - > set_title ( title . get_string ( ) ) ;
} else {
2022-01-31 21:14:33 -05:00
WindowTitle title ( S_ ( " Window|Cues " ) ) ;
2021-11-08 20:27:38 -05:00
title + = Glib : : get_application_name ( ) ;
own_window ( ) - > set_title ( title . get_string ( ) ) ;
}
}
2022-01-17 16:06:48 -05:00
void
TriggerPage : : add_sidebar_page ( string const & name , Gtk : : Widget & widget )
{
EventBox * b = manage ( new EventBox ) ;
Label * l = manage ( new Label ( name ) ) ;
l - > set_angle ( - 90 ) ;
b - > add ( * l ) ;
b - > show_all ( ) ;
_sidebar_notebook . append_page ( widget , * b ) ;
}
2021-11-10 19:15:00 -05:00
void
TriggerPage : : initial_track_display ( )
{
2023-02-16 18:33:28 -05:00
std : : shared_ptr < RouteList > r = _session - > get_tracks ( ) ;
2021-11-10 19:15:00 -05:00
RouteList rl ( * r ) ;
_strips . clear ( ) ;
add_routes ( rl ) ;
}
2022-12-02 13:19:20 -05:00
void
TriggerPage : : clear_selected_slot ( )
{
Selection & selection ( Editor : : instance ( ) . get_selection ( ) ) ;
TriggerSelection ts = selection . triggers ;
if ( ts . empty ( ) ) {
return ;
}
TriggerPtr trigger = ts . front ( ) - > trigger ( ) ;
2023-02-16 18:33:28 -05:00
trigger - > set_region ( std : : shared_ptr < Region > ( ) ) ;
2022-12-02 13:19:20 -05:00
}
2021-12-02 00:52:03 -05:00
void
TriggerPage : : selection_changed ( )
{
2021-12-12 07:43:40 -05:00
Selection & selection ( Editor : : instance ( ) . get_selection ( ) ) ;
2021-12-02 00:52:03 -05:00
2021-12-12 08:32:26 -05:00
_slot_prop_box . hide ( ) ;
2021-12-02 11:50:19 -05:00
2021-12-15 19:08:15 -05:00
_audio_trig_box . hide ( ) ;
2021-12-02 11:50:19 -05:00
2021-12-15 19:08:15 -05:00
_midi_trig_box . hide ( ) ;
2021-12-02 00:52:03 -05:00
2021-12-02 11:50:19 -05:00
_parameter_box . hide ( ) ;
2021-12-12 07:43:40 -05:00
if ( ! selection . triggers . empty ( ) ) {
2021-12-24 16:13:36 -05:00
TriggerSelection ts = selection . triggers ;
TriggerEntry * entry = * ts . begin ( ) ;
TriggerReference ref = entry - > trigger_reference ( ) ;
TriggerPtr trigger = entry - > trigger ( ) ;
2021-12-12 07:43:40 -05:00
2021-12-24 16:13:36 -05:00
_slot_prop_box . set_slot ( ref ) ;
2021-12-12 08:32:26 -05:00
_slot_prop_box . show ( ) ;
2021-12-24 16:13:36 -05:00
if ( trigger - > region ( ) ) {
if ( trigger - > region ( ) - > data_type ( ) = = DataType : : AUDIO ) {
_audio_trig_box . set_trigger ( ref ) ;
2021-12-15 19:08:15 -05:00
_audio_trig_box . show ( ) ;
2021-12-02 11:50:19 -05:00
} else {
2021-12-24 16:13:36 -05:00
_midi_trig_box . set_trigger ( ref ) ;
2021-12-15 19:08:15 -05:00
_midi_trig_box . show ( ) ;
2021-12-02 11:50:19 -05:00
}
2021-12-02 00:52:03 -05:00
}
2021-12-02 11:50:19 -05:00
_parameter_box . show ( ) ;
2021-12-02 00:52:03 -05:00
}
}
2021-11-10 19:15:00 -05:00
void
TriggerPage : : add_routes ( RouteList & rl )
{
rl . sort ( Stripable : : Sorter ( ) ) ;
for ( RouteList : : iterator r = rl . begin ( ) ; r ! = rl . end ( ) ; + + r ) {
/* we're only interested in Tracks */
2023-02-16 18:33:28 -05:00
if ( ! std : : dynamic_pointer_cast < Track > ( * r ) ) {
2021-11-10 19:15:00 -05:00
continue ;
}
#if 0
Fix typos in gtk2_ardour/ directory
Found via `codespell -q 3 -S *.po,./share/patchfiles,./libs -L ba,buss,busses,doubleclick,hsi,ontop,ro,seh,siz,sur,te,trough,ue`
2022-01-26 12:35:38 -05:00
/* TODO, only subscribe to PropertyChanged, create (and destroy) TriggerStrip as needed.
2021-11-10 19:15:00 -05:00
* For now we just hide non trigger strips .
*/
if ( ! ( * r ) - > presentation_info ( ) . trigger_track ( ) ) {
continue ;
}
# endif
2021-12-12 07:43:40 -05:00
if ( ! ( * r ) - > triggerbox ( ) ) {
2021-11-10 19:15:00 -05:00
/* This Route has no TriggerBox -- and can never have one */
continue ;
}
TriggerStrip * ts = new TriggerStrip ( _session , * r ) ;
_strips . push_back ( ts ) ;
2023-02-16 18:33:28 -05:00
( * r ) - > presentation_info ( ) . PropertyChanged . connect ( * this , invalidator ( * this ) , boost : : bind ( & TriggerPage : : stripable_property_changed , this , _1 , std : : weak_ptr < Stripable > ( * r ) ) , gui_context ( ) ) ;
( * r ) - > PropertyChanged . connect ( * this , invalidator ( * this ) , boost : : bind ( & TriggerPage : : stripable_property_changed , this , _1 , std : : weak_ptr < Stripable > ( * r ) ) , gui_context ( ) ) ;
2022-02-02 23:39:48 -05:00
ts - > signal_button_release_event ( ) . connect ( sigc : : bind ( sigc : : mem_fun ( * this , & TriggerPage : : strip_button_release_event ) , ts ) ) ;
2021-11-10 19:15:00 -05:00
}
redisplay_track_list ( ) ;
}
void
TriggerPage : : remove_route ( TriggerStrip * ra )
{
if ( ! _session | | _session - > deletion_in_progress ( ) ) {
_strips . clear ( ) ;
return ;
}
list < TriggerStrip * > : : iterator i = find ( _strips . begin ( ) , _strips . end ( ) , ra ) ;
if ( i ! = _strips . end ( ) ) {
_strip_packer . remove ( * * i ) ;
_strips . erase ( i ) ;
}
redisplay_track_list ( ) ;
}
2022-01-26 22:06:13 -05:00
struct TriggerStripSorter {
bool operator ( ) ( const TriggerStrip * ts_a , const TriggerStrip * ts_b )
{
2023-02-16 18:33:28 -05:00
std : : shared_ptr < ARDOUR : : Stripable > const & a = ts_a - > stripable ( ) ;
std : : shared_ptr < ARDOUR : : Stripable > const & b = ts_b - > stripable ( ) ;
2022-01-26 22:06:13 -05:00
return ARDOUR : : Stripable : : Sorter ( ) ( a , b ) ;
}
} ;
2021-11-10 19:15:00 -05:00
void
TriggerPage : : redisplay_track_list ( )
{
2022-01-26 22:06:13 -05:00
_strips . sort ( TriggerStripSorter ( ) ) ;
2022-02-02 23:39:48 -05:00
PresentationInfo : : ChangeSuspender cs ;
2022-01-26 22:06:13 -05:00
2021-11-10 19:15:00 -05:00
for ( list < TriggerStrip * > : : iterator i = _strips . begin ( ) ; i ! = _strips . end ( ) ; + + i ) {
2021-12-12 07:43:40 -05:00
TriggerStrip * strip = * i ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > s = strip - > stripable ( ) ;
std : : shared_ptr < Route > route = std : : dynamic_pointer_cast < Route > ( s ) ;
2021-11-10 19:15:00 -05:00
bool hidden = s - > presentation_info ( ) . hidden ( ) ;
2022-02-02 23:39:48 -05:00
if ( s - > is_selected ( ) ) {
_selection . add ( * i ) ;
} else {
_selection . remove ( * i ) ;
}
2021-11-10 19:15:00 -05:00
if ( ! ( s ) - > presentation_info ( ) . trigger_track ( ) ) {
hidden = true ;
}
2021-12-12 07:43:40 -05:00
assert ( route & & route - > triggerbox ( ) ) ;
if ( ! route | | ! route - > triggerbox ( ) ) {
2021-11-10 19:15:00 -05:00
hidden = true ;
}
if ( hidden & & strip - > get_parent ( ) ) {
/* if packed, remove it */
_strip_packer . remove ( * strip ) ;
} else if ( ! hidden & & strip - > get_parent ( ) ) {
/* already packed, put it at the end */
_strip_packer . reorder_child ( * strip , - 1 ) ; /* put at end */
} else if ( ! hidden ) {
_strip_packer . pack_start ( * strip , false , false ) ;
}
}
}
2022-02-02 23:39:48 -05:00
AxisView *
2023-02-16 18:33:28 -05:00
TriggerPage : : axis_view_by_stripable ( std : : shared_ptr < Stripable > s ) const
2022-02-02 23:39:48 -05:00
{
for ( list < TriggerStrip * > : : const_iterator i = _strips . begin ( ) ; i ! = _strips . end ( ) ; + + i ) {
TriggerStrip * strip = * i ;
if ( s = = strip - > stripable ( ) ) {
return strip ;
}
}
return 0 ;
}
AxisView *
2023-02-16 18:33:28 -05:00
TriggerPage : : axis_view_by_control ( std : : shared_ptr < AutomationControl > c ) const
2022-02-02 23:39:48 -05:00
{
return 0 ;
}
2021-11-08 20:27:38 -05:00
void
TriggerPage : : parameter_changed ( string const & p )
{
}
2021-11-10 19:15:00 -05:00
void
TriggerPage : : pi_property_changed ( PBD : : PropertyChange const & what_changed )
{
2022-02-02 23:39:48 -05:00
if ( what_changed . contains ( Properties : : selected ) ) {
_selection . presentation_info_changed ( what_changed ) ;
}
2022-01-26 22:06:13 -05:00
if ( what_changed . contains ( ARDOUR : : Properties : : order ) ) {
redisplay_track_list ( ) ;
}
2021-11-10 19:15:00 -05:00
}
void
2023-02-16 18:33:28 -05:00
TriggerPage : : stripable_property_changed ( PBD : : PropertyChange const & what_changed , std : : weak_ptr < Stripable > ws )
2021-11-10 19:15:00 -05:00
{
if ( what_changed . contains ( ARDOUR : : Properties : : trigger_track ) ) {
#if 0
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Stripable > s = ws . lock ( ) ;
2021-11-10 19:15:00 -05:00
/* TODO: find trigger-strip for given stripable, delete *it; */
# else
/* For now we just hide it */
redisplay_track_list ( ) ;
return ;
# endif
}
if ( what_changed . contains ( ARDOUR : : Properties : : hidden ) ) {
redisplay_track_list ( ) ;
}
}
2022-02-02 23:39:48 -05:00
bool
TriggerPage : : strip_button_release_event ( GdkEventButton * ev , TriggerStrip * strip )
{
if ( ev - > button ! = 1 ) {
return false ;
}
if ( _selection . selected ( strip ) ) {
/* primary-click: toggle selection state of strip */
if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : PrimaryModifier ) ) {
_selection . remove ( strip , true ) ;
} else if ( _selection . axes . size ( ) > 1 ) {
/* de-select others */
_selection . set ( strip ) ;
}
PublicEditor & pe = PublicEditor : : instance ( ) ;
TimeAxisView * tav = pe . time_axis_view_from_stripable ( strip - > stripable ( ) ) ;
if ( tav ) {
pe . set_selected_mixer_strip ( * tav ) ;
}
} else {
if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : PrimaryModifier ) ) {
_selection . add ( strip , true ) ;
} else if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : RangeSelectModifier ) ) {
/* extend selection */
vector < TriggerStrip * > tmp ;
bool accumulate = false ;
bool found_another = false ;
_strips . sort ( TriggerStripSorter ( ) ) ;
for ( list < TriggerStrip * > : : iterator i = _strips . begin ( ) ; i ! = _strips . end ( ) ; + + i ) {
TriggerStrip * ts = * i ;
if ( ts = = strip ) {
/* hit clicked strip, start accumulating till we hit the first
selected strip
*/
if ( accumulate ) {
/* done */
break ;
} else {
accumulate = true ;
}
} else if ( _selection . selected ( ts ) ) {
/* hit selected strip. if currently accumulating others,
we ' re done . if not accumulating others , start doing so .
*/
found_another = true ;
if ( accumulate ) {
/* done */
break ;
} else {
accumulate = true ;
}
} else {
if ( accumulate ) {
tmp . push_back ( ts ) ;
}
}
}
tmp . push_back ( strip ) ;
if ( found_another ) {
PresentationInfo : : ChangeSuspender cs ;
for ( vector < TriggerStrip * > : : iterator i = tmp . begin ( ) ; i ! = tmp . end ( ) ; + + i ) {
_selection . add ( * i , true ) ;
}
} else {
_selection . set ( strip ) ; //user wants to start a range selection, but there aren't any others selected yet
}
} else {
_selection . set ( strip ) ;
}
}
return true ;
}
2021-12-14 15:03:48 -05:00
bool
TriggerPage : : no_strip_button_event ( GdkEventButton * ev )
{
if ( ( ev - > type = = GDK_2BUTTON_PRESS & & ev - > button = = 1 ) | | ( ev - > type = = GDK_BUTTON_RELEASE & & Keyboard : : is_context_menu_event ( ev ) ) ) {
2021-12-17 09:30:14 -05:00
ARDOUR_UI : : instance ( ) - > add_route ( ) ;
2021-12-14 15:03:48 -05:00
return true ;
}
return false ;
}
2022-01-03 17:57:50 -05:00
bool
TriggerPage : : no_strip_drag_motion ( Glib : : RefPtr < Gdk : : DragContext > const & context , int , int y , guint time )
{
context - > drag_status ( Gdk : : ACTION_COPY , time ) ;
return true ;
}
void
TriggerPage : : no_strip_drag_data_received ( Glib : : RefPtr < Gdk : : DragContext > const & context , int /*x*/ , int y , Gtk : : SelectionData const & data , guint /*info*/ , guint time )
{
2022-01-18 00:52:57 -05:00
if ( data . get_target ( ) = = " x-ardour/region.pbdid " ) {
PBD : : ID rid ( data . get_data_as_string ( ) ) ;
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Region > region = RegionFactory : : region_by_id ( rid ) ;
std : : shared_ptr < TriggerBox > triggerbox ;
2022-01-03 20:20:54 -05:00
2023-02-16 18:33:28 -05:00
if ( std : : dynamic_pointer_cast < AudioRegion > ( region ) ) {
2022-01-03 20:20:54 -05:00
uint32_t output_chan = region - > sources ( ) . size ( ) ;
if ( ( Config - > get_output_auto_connect ( ) & AutoConnectMaster ) & & session ( ) - > master_out ( ) ) {
output_chan = session ( ) - > master_out ( ) - > n_inputs ( ) . n_audio ( ) ;
}
2023-02-16 18:33:28 -05:00
std : : list < std : : shared_ptr < AudioTrack > > audio_tracks ;
2022-01-18 10:01:13 -05:00
audio_tracks = session ( ) - > new_audio_track ( region - > sources ( ) . size ( ) , output_chan , 0 , 1 , region - > name ( ) , PresentationInfo : : max_order , Normal , true , true ) ;
2022-01-03 20:20:54 -05:00
if ( ! audio_tracks . empty ( ) ) {
triggerbox = audio_tracks . front ( ) - > triggerbox ( ) ;
}
2023-02-16 18:33:28 -05:00
} else if ( std : : dynamic_pointer_cast < MidiRegion > ( region ) ) {
2022-01-03 20:20:54 -05:00
ChanCount one_midi_port ( DataType : : MIDI , 1 ) ;
2023-02-16 18:33:28 -05:00
list < std : : shared_ptr < MidiTrack > > midi_tracks ;
2022-01-03 20:20:54 -05:00
midi_tracks = session ( ) - > new_midi_track ( one_midi_port , one_midi_port ,
Config - > get_strict_io ( ) | | Profile - > get_mixbus ( ) ,
2023-02-16 18:33:28 -05:00
std : : shared_ptr < ARDOUR : : PluginInfo > ( ) ,
2022-01-03 20:20:54 -05:00
( ARDOUR : : Plugin : : PresetRecord * ) 0 ,
2022-01-18 10:01:13 -05:00
( ARDOUR : : RouteGroup * ) 0 , 1 , region - > name ( ) , PresentationInfo : : max_order , Normal , true , true ) ;
2022-01-03 20:20:54 -05:00
if ( ! midi_tracks . empty ( ) ) {
triggerbox = midi_tracks . front ( ) - > triggerbox ( ) ;
}
}
if ( ! triggerbox ) {
2022-01-03 17:57:50 -05:00
context - > drag_finish ( false , false , time ) ;
2022-01-03 20:20:54 -05:00
return ;
2022-01-03 17:57:50 -05:00
}
2022-01-03 20:20:54 -05:00
// XXX: check does the region need to be copied?
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Region > region_copy = RegionFactory : : create ( region , true ) ;
2022-01-03 20:20:54 -05:00
triggerbox - > set_from_selection ( 0 , region_copy ) ;
context - > drag_finish ( true , false , time ) ;
2022-01-03 17:57:50 -05:00
return ;
}
std : : vector < std : : string > paths ;
if ( ARDOUR_UI_UTILS : : convert_drop_to_paths ( paths , data ) ) {
# ifdef __APPLE__
/* We are not allowed to call recursive main event loops from within
* the main event loop with GTK / Quartz . Since import / embed wants
* to push up a progress dialog , defer all this till we go idle .
*/
Glib : : signal_idle ( ) . connect ( sigc : : bind ( sigc : : mem_fun ( * this , & TriggerPage : : idle_drop_paths ) , paths ) ) ;
# else
drop_paths_part_two ( paths ) ;
# endif
}
context - > drag_finish ( true , false , time ) ;
}
void
TriggerPage : : drop_paths_part_two ( std : : vector < std : : string > paths )
{
/* compare to Editor::drop_paths_part_two */
std : : vector < string > midi_paths ;
std : : vector < string > audio_paths ;
for ( std : : vector < std : : string > : : iterator s = paths . begin ( ) ; s ! = paths . end ( ) ; + + s ) {
if ( SMFSource : : safe_midi_file_extension ( * s ) ) {
midi_paths . push_back ( * s ) ;
} else {
audio_paths . push_back ( * s ) ;
}
}
2022-01-03 20:25:08 -05:00
timepos_t pos ( 0 ) ;
Editing : : ImportDisposition disposition = Editing : : ImportSerializeFiles ; // or Editing::ImportDistinctFiles // TODO use drop modifier? config?
2022-09-30 15:28:01 -04:00
PublicEditor : : instance ( ) . do_import ( midi_paths , disposition , Editing : : ImportAsTrigger , SrcBest , SMFTrackNumber , SMFTempoIgnore , pos , _trigger_clip_picker . instrument_plugin ( ) ) ;
2022-01-19 13:22:51 -05:00
PublicEditor : : instance ( ) . do_import ( audio_paths , disposition , Editing : : ImportAsTrigger , SrcBest , SMFTrackNumber , SMFTempoIgnore , pos ) ;
2022-01-03 17:57:50 -05:00
}
bool
TriggerPage : : idle_drop_paths ( std : : vector < std : : string > paths )
{
2022-01-17 16:07:35 -05:00
drop_paths_part_two ( paths ) ;
return false ;
2022-01-03 17:57:50 -05:00
}
2021-11-10 19:15:00 -05:00
gint
TriggerPage : : start_updating ( )
{
_fast_screen_update_connection = Timers : : super_rapid_connect ( sigc : : mem_fun ( * this , & TriggerPage : : fast_update_strips ) ) ;
return 0 ;
}
gint
TriggerPage : : stop_updating ( )
{
_fast_screen_update_connection . disconnect ( ) ;
return 0 ;
}
void
TriggerPage : : fast_update_strips ( )
{
2022-01-26 15:17:53 -05:00
if ( _content . get_mapped ( ) & & _session ) {
2021-11-10 19:15:00 -05:00
for ( list < TriggerStrip * > : : iterator i = _strips . begin ( ) ; i ! = _strips . end ( ) ; + + i ) {
( * i ) - > fast_update ( ) ;
}
}
}
2022-11-16 16:15:13 -05:00
void
TriggerPage : : register_actions ( )
{
Glib : : RefPtr < ActionGroup > trigger_actions = ActionManager : : create_action_group ( bindings , X_ ( " Cues " ) ) ;
2022-12-02 13:19:20 -05:00
ActionManager : : register_action ( trigger_actions , " clear-trigger-slot " , _ ( " Clear Selected Slot " ) , sigc : : mem_fun ( * this , & TriggerPage : : clear_selected_slot ) ) ;
2022-12-02 13:50:39 -05:00
ActionManager : : register_action ( trigger_actions , " clear-trigger-slot-alt " , _ ( " Clear Selected Slot " ) , sigc : : mem_fun ( * this , & TriggerPage : : clear_selected_slot ) ) ;
2022-12-02 13:19:20 -05:00
2022-11-16 16:15:13 -05:00
for ( int32_t n = 0 ; n < TriggerBox : : default_triggers_per_box ; + + n ) {
const std : : string action_name = string_compose ( " trigger-cue-%1 " , n ) ;
const std : : string display_name = string_compose ( _ ( " Trigger Cue %1 " ) , cue_marker_name ( n ) ) ;
ActionManager : : register_action ( trigger_actions , action_name . c_str ( ) , display_name . c_str ( ) , sigc : : bind ( sigc : : mem_fun ( ARDOUR_UI : : instance ( ) , & ARDOUR_UI : : trigger_cue_row ) , n ) ) ;
}
for ( int32_t c = 0 ; c < 16 ; + + c ) {
for ( int32_t n = 0 ; n < TriggerBox : : default_triggers_per_box ; + + n ) {
const std : : string action_name = string_compose ( " trigger-slot-%1-%2 " , c , n ) ;
const std : : string display_name = string_compose ( _ ( " Trigger Slot %1/%2 " ) , c , cue_marker_name ( n ) ) ;
ActionManager : : register_action ( trigger_actions , action_name . c_str ( ) , display_name . c_str ( ) , sigc : : bind ( sigc : : mem_fun ( ARDOUR_UI : : instance ( ) , & ARDOUR_UI : : trigger_slot ) , c , n ) ) ;
}
2022-12-10 12:33:39 -05:00
ActionManager : : register_action ( trigger_actions , string_compose ( " stop-cues-%1-now " , c ) . c_str ( ) , string_compose ( _ ( " Stop Cues %1 " ) , c ) . c_str ( ) , sigc : : bind ( sigc : : mem_fun ( ARDOUR_UI : : instance ( ) , & ARDOUR_UI : : stop_cues ) , c , true ) ) ;
ActionManager : : register_action ( trigger_actions , string_compose ( " stop-cues-%1-soon " , c ) . c_str ( ) , string_compose ( _ ( " Stop Cues %1 " ) , c ) . c_str ( ) , sigc : : bind ( sigc : : mem_fun ( ARDOUR_UI : : instance ( ) , & ARDOUR_UI : : stop_cues ) , c , false ) ) ;
2022-11-16 16:15:13 -05:00
}
2022-12-10 12:33:39 -05:00
ActionManager : : register_action ( trigger_actions , X_ ( " stop-all-cues-now " ) , _ ( " Stop all cues now " ) , sigc : : bind ( sigc : : mem_fun ( ARDOUR_UI : : instance ( ) , & ARDOUR_UI : : stop_all_cues ) , true ) ) ;
ActionManager : : register_action ( trigger_actions , X_ ( " stop-all-cues-soon " ) , _ ( " Stop all cues soon " ) , sigc : : bind ( sigc : : mem_fun ( ARDOUR_UI : : instance ( ) , & ARDOUR_UI : : stop_all_cues ) , false ) ) ;
2022-11-16 16:15:13 -05:00
}