2021-07-21 16:44:41 -06:00
/*
* Copyright ( C ) 2021 Paul Davis < paul @ linuxaudiosystems . com >
*
* 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 .
*/
2021-12-13 22:51:16 +01:00
# include <vector>
2025-02-01 15:15:02 +01:00
# include "ytkmm/sizegroup.h"
# include <ytkmm/filechooserdialog.h>
# include <ytkmm/menu.h>
# include <ytkmm/menuitem.h>
# include <ytkmm/stock.h>
2021-08-05 12:11:19 -06:00
2021-07-27 20:39:55 -06:00
# include "pbd/compose.h"
2021-07-27 22:40:35 -06:00
# include "pbd/convert.h"
2021-12-18 00:17:58 +01:00
# include "pbd/unwind.h"
2021-07-27 20:39:55 -06:00
2021-12-29 11:21:44 -06:00
# include "pbd/basename.h"
# include "pbd/file_utils.h"
# include "pbd/pathexpand.h"
# include "pbd/search_path.h"
# include "ardour/directory_names.h"
# include "ardour/filesystem_paths.h"
2021-07-27 22:40:35 -06:00
# include "ardour/region.h"
2022-01-11 01:53:27 +01:00
# include "ardour/region_factory.h"
2021-07-21 17:22:09 -06:00
# include "ardour/triggerbox.h"
2021-07-27 20:39:55 -06:00
# include "canvas/polygon.h"
# include "canvas/text.h"
2021-07-21 17:22:09 -06:00
# include "gtkmm2ext/utils.h"
2021-08-05 15:04:02 -06:00
# include "ardour_ui.h"
2021-08-05 16:20:56 -06:00
# include "gui_thread.h"
2021-12-27 16:48:13 -06:00
# include "keyboard.h"
2021-08-05 15:04:02 -06:00
# include "public_editor.h"
2021-10-24 09:25:49 -06:00
# include "region_view.h"
# include "selection.h"
2022-01-11 13:52:02 +01:00
# include "slot_properties_box.h"
2021-10-03 23:47:47 -06:00
# include "timers.h"
2021-12-17 16:32:52 +01:00
# include "trigger_ui.h"
# include "triggerbox_ui.h"
2021-07-27 22:40:35 -06:00
# include "ui_config.h"
2021-08-05 15:04:02 -06:00
# include "utils.h"
2021-07-21 16:44:41 -06:00
2021-09-27 16:28:10 +02:00
# include "pbd/i18n.h"
2021-07-21 16:44:41 -06:00
using namespace ARDOUR ;
using namespace ArdourCanvas ;
2021-07-21 17:22:09 -06:00
using namespace Gtkmm2ext ;
2021-07-27 22:40:35 -06:00
using namespace PBD ;
2021-07-21 16:44:41 -06:00
2024-01-11 18:00:07 -07:00
TriggerEntry : : TriggerEntry ( Item * item , TriggerStrip & s , TriggerReference tr )
2021-12-05 10:56:42 -06:00
: ArdourCanvas : : Rectangle ( item )
2024-01-11 18:00:07 -07:00
, _strip ( s )
2022-01-08 09:33:20 -06:00
, _grabbed ( false )
2022-01-11 19:39:08 +01:00
, _drag_active ( false )
2024-09-30 18:10:50 -06:00
, rec_blink_on ( false )
2021-07-21 16:44:41 -06:00
{
2021-12-17 16:32:52 +01:00
set_layout_sensitive ( true ) ; // why???
2021-07-27 22:40:35 -06:00
2024-09-17 10:55:52 -06:00
name = string_compose ( " trigger %1 " , tr . slot ( ) ) ;
2021-10-19 14:41:05 -06:00
2021-12-05 10:56:42 -06:00
set_outline ( false ) ;
2021-07-21 16:44:41 -06:00
2021-08-14 01:08:23 +02:00
play_button = new ArdourCanvas : : Rectangle ( this ) ;
2022-01-11 09:48:32 -06:00
play_button - > set_outline ( true ) ;
2021-12-17 16:32:52 +01:00
play_button - > set_fill ( true ) ;
2024-09-28 11:44:16 -06:00
play_button - > name = string_compose ( " playbutton %1 " , tref . slot ( ) ) ;
2021-12-05 10:56:42 -06:00
play_button - > show ( ) ;
2021-07-21 17:22:09 -06:00
2021-12-21 13:31:37 -06:00
follow_button = new ArdourCanvas : : Rectangle ( this ) ;
follow_button - > set_outline ( false ) ;
follow_button - > set_fill ( true ) ;
follow_button - > name = ( " slot_selector_button " ) ;
2022-02-19 12:12:43 -06:00
follow_button - > set_tooltip ( _ ( " Click to select Follow-Actions for this clip " ) ) ;
2021-12-21 13:31:37 -06:00
follow_button - > show ( ) ;
2021-12-05 10:56:42 -06:00
name_button = new ArdourCanvas : : Rectangle ( this ) ;
name_button - > set_outline ( true ) ;
2021-12-17 16:32:52 +01:00
name_button - > set_fill ( true ) ;
2021-12-05 10:56:42 -06:00
name_button - > name = ( " slot_selector_button " ) ;
name_button - > show ( ) ;
name_text = new Text ( name_button ) ;
name_text - > set_ignore_events ( false ) ;
2022-02-19 12:12:43 -06:00
name_text - > set_tooltip ( _ ( " Click to select this clip and edit its properties \n Right-Click for context menu " ) ) ;
2021-12-17 16:32:52 +01:00
name_text - > show ( ) ;
2021-12-30 12:10:50 -06:00
/* this will trigger a call to on_trigger_changed() */
2022-01-11 13:52:02 +01:00
set_trigger ( tr ) ;
2021-12-30 12:10:50 -06:00
2024-10-19 01:51:44 +02:00
trigger ( ) - > ArmChanged . connect ( _rec_enable_connections , MISSING_INVALIDATOR , std : : bind ( & TriggerEntry : : rec_enable_change , this ) , gui_context ( ) ) ;
tref . box ( ) - > RecEnableChanged . connect ( _rec_enable_connections , MISSING_INVALIDATOR , std : : bind ( & TriggerEntry : : rec_enable_change , this ) , gui_context ( ) ) ;
2024-09-28 11:44:16 -06:00
2022-01-11 19:39:08 +01:00
/* DnD Source */
GtkCanvas * gtkcanvas = static_cast < GtkCanvas * > ( canvas ( ) ) ;
assert ( gtkcanvas ) ;
gtkcanvas - > signal_drag_begin ( ) . connect ( sigc : : mem_fun ( * this , & TriggerEntry : : drag_begin ) ) ;
gtkcanvas - > signal_drag_end ( ) . connect ( sigc : : mem_fun ( * this , & TriggerEntry : : drag_end ) ) ;
gtkcanvas - > signal_drag_data_get ( ) . connect ( sigc : : mem_fun ( * this , & TriggerEntry : : drag_data_get ) ) ;
2021-12-30 20:27:26 -06:00
/* event handling */
play_button - > Event . connect ( sigc : : mem_fun ( * this , & TriggerEntry : : play_button_event ) ) ;
name_button - > Event . connect ( sigc : : mem_fun ( * this , & TriggerEntry : : name_button_event ) ) ;
follow_button - > Event . connect ( sigc : : mem_fun ( * this , & TriggerEntry : : follow_button_event ) ) ;
2022-01-11 19:39:08 +01:00
Event . connect ( sigc : : mem_fun ( * this , & TriggerEntry : : event ) ) ;
2021-12-05 10:56:42 -06:00
/* watch for change in theme */
2021-12-17 16:32:52 +01:00
UIConfiguration : : instance ( ) . ParameterChanged . connect ( sigc : : mem_fun ( * this , & TriggerEntry : : ui_parameter_changed ) ) ;
2022-01-11 09:48:32 -06:00
set_widget_colors ( ) ;
2021-08-05 16:20:56 -06:00
2021-12-30 20:27:26 -06:00
/* owner color changes (?) */
2024-10-19 01:51:44 +02:00
dynamic_cast < Stripable * > ( tref . box ( ) - > owner ( ) ) - > presentation_info ( ) . Change . connect ( _owner_prop_connection , MISSING_INVALIDATOR , std : : bind ( & TriggerEntry : : owner_prop_change , this , _1 ) , gui_context ( ) ) ;
2021-08-05 16:20:56 -06:00
2021-12-17 16:32:52 +01:00
selection_change ( ) ;
2024-09-30 18:10:50 -06:00
rec_enable_change ( ) ;
2021-07-27 20:39:55 -06:00
}
2021-07-27 17:08:31 -06:00
2021-07-27 20:39:55 -06:00
TriggerEntry : : ~ TriggerEntry ( )
{
2021-07-21 17:22:09 -06:00
}
2024-09-28 11:44:16 -06:00
void
TriggerEntry : : rec_enable_change ( )
{
2024-09-30 18:10:50 -06:00
switch ( tref . box ( ) - > record_enabled ( ) ) {
case Recording :
break ;
case Enabled :
if ( ! UIConfiguration : : instance ( ) . get_no_strobe ( ) & & trigger ( ) - > armed ( ) ) {
rec_blink_connection = Timers : : blink_connect ( sigc : : mem_fun ( * this , & TriggerEntry : : blink_rec_enable ) ) ;
}
break ;
case Disabled :
rec_blink_connection . disconnect ( ) ;
break ;
}
2024-09-30 14:07:57 -06:00
set_play_button_tooltip ( ) ;
2024-09-28 11:44:16 -06:00
redraw ( ) ;
}
2024-09-30 18:10:50 -06:00
void
TriggerEntry : : blink_rec_enable ( bool onoff )
{
rec_blink_on = onoff ;
redraw ( ) ;
}
2024-09-30 14:07:57 -06:00
void
TriggerEntry : : set_play_button_tooltip ( )
{
2024-09-30 18:10:50 -06:00
switch ( tref . box ( ) - > record_enabled ( ) ) {
case Recording :
case Enabled :
2025-07-14 12:58:14 -06:00
play_button - > set_tooltip ( string_compose ( _ ( " Click to enable recording for the follow length \n %1-click to enable recording until stopped " ) , Keyboard : : primary_modifier_name ( ) ) ) ;
2024-09-30 18:10:50 -06:00
break ;
default :
2024-09-30 14:07:57 -06:00
play_button - > set_tooltip ( _ ( " Stop other clips on this track. \n Right-click to select Launch Options for this clip " ) ) ;
}
}
2021-10-17 17:58:08 -06:00
void
2021-12-17 16:32:52 +01:00
TriggerEntry : : owner_prop_change ( PropertyChange const & pc )
2021-10-17 17:58:08 -06:00
{
if ( pc . contains ( Properties : : color ) ) {
owner_color_changed ( ) ;
}
}
void
TriggerEntry : : owner_color_changed ( )
{
2021-12-17 16:32:52 +01:00
// TODO
2021-10-17 17:58:08 -06:00
}
2021-11-10 17:51:04 -07:00
void
TriggerEntry : : selection_change ( )
{
2022-01-11 09:48:32 -06:00
set_widget_colors ( ) ;
2021-10-03 23:47:47 -06:00
}
2021-08-07 18:55:05 -06:00
void
2021-12-17 16:32:52 +01:00
TriggerEntry : : _size_allocate ( ArdourCanvas : : Rect const & alloc )
2021-10-03 16:46:39 -06:00
{
Rectangle : : _size_allocate ( alloc ) ;
2021-12-17 16:32:52 +01:00
const Distance width = _rect . width ( ) ;
const Distance height = _rect . height ( ) ;
2021-10-03 16:46:39 -06:00
2021-12-05 10:56:42 -06:00
play_button - > set ( ArdourCanvas : : Rect ( 0 , 0 , height , height ) ) ;
2022-01-11 13:52:02 +01:00
name_button - > set ( ArdourCanvas : : Rect ( height , 0 , width - height , height ) ) ;
follow_button - > set ( ArdourCanvas : : Rect ( width - height , 0 , width , height ) ) ;
2021-10-03 16:46:39 -06:00
2021-12-17 16:32:52 +01:00
const double scale = UIConfiguration : : instance ( ) . get_ui_scale ( ) ;
2024-09-28 11:44:16 -06:00
double _poly_margin = 2. * scale ;
double font_margin = 2. * scale ;
2022-01-18 15:52:54 -06:00
name_text - > size_allocate ( ArdourCanvas : : Rect ( 0 , 0 , width , height - font_margin * 2 ) ) ;
2022-01-11 13:52:02 +01:00
float tleft = height ; // make room for the play button
2022-01-18 15:52:54 -06:00
name_text - > set_position ( Duple ( tleft + _poly_margin , font_margin ) ) ; // @paul why do we need tleft here? isn't name_text a child of name_button?
2022-01-11 13:52:02 +01:00
name_text - > clamp_width ( width - height * 2 - _poly_margin * 3 ) ;
2021-10-17 17:58:34 -06:00
2021-12-17 16:32:52 +01:00
/* font scale may have changed. uiconfig 'embeds' the ui-scale in the font */
name_text - > set_font_description ( UIConfiguration : : instance ( ) . get_NormalFont ( ) ) ;
2021-12-05 10:56:42 -06:00
}
2021-12-17 16:32:52 +01:00
void
2022-01-19 15:50:47 -07:00
TriggerEntry : : draw_follow_icon ( Cairo : : RefPtr < Cairo : : Context > context , FollowAction const & icon , float size , float scale ) const
2021-12-17 16:32:52 +01:00
{
2022-01-04 15:59:24 -06:00
uint32_t fg_color = UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ;
2022-01-11 13:52:02 +01:00
/* in the case where there is a random follow-action, just put a "?" */
if ( trigger ( ) - > follow_action_probability ( ) > 0 ) {
2021-12-27 16:49:22 -06:00
Glib : : RefPtr < Pango : : Layout > layout = Pango : : Layout : : create ( context ) ;
layout - > set_font_description ( UIConfiguration : : instance ( ) . get_SmallMonospaceFont ( ) ) ;
layout - > set_text ( " ? " ) ;
int tw , th ;
layout - > get_pixel_size ( tw , th ) ;
context - > move_to ( size / 2 , size / 2 ) ;
context - > rel_move_to ( - tw / 2 , - th / 2 ) ;
layout - > show_in_cairo_context ( context ) ;
return ;
}
2022-01-04 15:59:24 -06:00
set_source_rgba ( context , fg_color ) ;
2021-12-17 16:32:52 +01:00
context - > set_line_width ( 1 * scale ) ;
2021-12-14 20:35:28 -06:00
2022-01-19 15:50:47 -07:00
switch ( icon . type ) {
case FollowAction : : Stop :
2021-12-17 16:32:52 +01:00
context - > rectangle ( 6 * scale , 6 * scale , size - 12 * scale , size - 12 * scale ) ;
context - > stroke ( ) ;
break ;
2022-01-19 15:50:47 -07:00
case FollowAction : : Again :
2021-12-17 16:32:52 +01:00
context - > arc ( size / 2 , size / 2 , size * 0.20 , 60. * ( M_PI / 180.0 ) , 2 * M_PI ) ;
context - > stroke ( ) ;
context - > arc ( size / 2 + size * 0.2 , size / 2 , 1.5 * scale , 0 , 2 * M_PI ) ; // arrow head
context - > fill ( ) ;
2022-01-02 17:10:18 -07:00
break ;
2022-01-19 15:50:47 -07:00
case FollowAction : : ForwardTrigger :
2022-01-02 17:10:18 -07:00
context - > move_to ( size / 2 , 3 * scale ) ;
2022-01-21 14:48:28 -06:00
context - > line_to ( size / 2 , size - 5 * scale ) ;
2022-01-02 17:10:18 -07:00
context - > stroke ( ) ;
2022-01-21 14:48:28 -06:00
context - > arc ( size / 2 , size - 5 * scale , 2 * scale , 0 , 2 * M_PI ) ; // arrow head
2022-01-02 17:10:18 -07:00
context - > fill ( ) ;
break ;
2022-01-19 15:50:47 -07:00
case FollowAction : : ReverseTrigger :
2022-01-21 14:48:28 -06:00
context - > move_to ( size / 2 , 5 * scale ) ;
2022-01-02 17:10:18 -07:00
context - > line_to ( size / 2 , size - 3 * scale ) ;
2021-12-17 16:32:52 +01:00
context - > stroke ( ) ;
2022-01-21 14:48:28 -06:00
context - > arc ( size / 2 , 5 * scale , 2 * scale , 0 , 2 * M_PI ) ; // arrow head
2022-01-04 15:59:24 -06:00
context - > fill ( ) ;
2021-12-17 16:32:52 +01:00
break ;
2022-01-20 11:01:14 -07:00
case FollowAction : : JumpTrigger : {
2022-09-22 08:10:26 -05:00
if ( icon . targets . count ( ) = = 1 ) { //Jump to a specific row; just draw the letter of the row we are jumping to
2022-01-21 14:48:28 -06:00
int cue_idx = - 1 ;
2022-10-04 19:55:15 -06:00
for ( int i = 0 ; i < TriggerBox : : default_triggers_per_box ; i + + ) {
2022-01-21 14:48:28 -06:00
if ( icon . targets . test ( i ) ) {
cue_idx = i ;
break ;
}
}
Glib : : RefPtr < Pango : : Layout > layout = Pango : : Layout : : create ( context ) ;
layout - > set_font_description ( UIConfiguration : : instance ( ) . get_SmallMonospaceFont ( ) ) ;
2022-02-11 11:33:43 -07:00
layout - > set_text ( cue_marker_name ( cue_idx ) ) ;
2022-01-21 14:48:28 -06:00
int tw , th ;
layout - > get_pixel_size ( tw , th ) ;
context - > move_to ( size / 2 , size / 2 ) ;
context - > rel_move_to ( - tw / 2 , - th / 2 ) ;
layout - > show_in_cairo_context ( context ) ;
2022-09-22 08:10:26 -05:00
} else { // Multi-Jump: draw a star-like icon
2022-01-21 14:48:28 -06:00
context - > set_line_width ( 1.5 * scale ) ;
set_source_rgba ( context , HSV ( UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ) . lighter ( 0.25 ) . color ( ) ) ; // needs to be brighter to maintain balance
for ( int i = 0 ; i < 6 ; i + + ) {
Cairo : : Matrix m = context - > get_matrix ( ) ;
context - > translate ( size / 2 , size / 2 ) ;
context - > rotate ( i * M_PI / 3 ) ;
context - > move_to ( 0 , 2 * scale ) ;
context - > line_to ( 0 , ( size / 2 ) - 4 * scale ) ;
context - > stroke ( ) ;
context - > set_matrix ( m ) ;
}
2022-01-20 11:01:14 -07:00
}
2021-12-14 20:35:28 -06:00
} break ;
2022-01-19 15:50:47 -07:00
case FollowAction : : None :
2021-12-14 20:35:28 -06:00
default :
2021-12-17 16:32:52 +01:00
break ;
2021-12-14 20:35:28 -06:00
}
}
2021-12-21 13:31:37 -06:00
void
2021-12-21 14:55:08 -06:00
TriggerEntry : : draw_launch_icon ( Cairo : : RefPtr < Cairo : : Context > context , float sz , float scale ) const
2021-12-21 13:31:37 -06:00
{
context - > set_line_width ( 1 * scale ) ;
2022-01-11 13:52:02 +01:00
float margin = 4 * scale ;
float size = sz - 2 * margin ;
2021-12-21 14:55:08 -06:00
2022-01-11 13:52:02 +01:00
bool active = trigger ( ) - > active ( ) ;
2021-12-21 13:31:37 -06:00
2024-10-02 13:46:59 -06:00
if ( ! trigger ( ) - > playable ( ) ) {
2024-09-28 11:44:16 -06:00
2024-09-30 18:10:50 -06:00
bool solid = false ;
2024-09-28 11:44:16 -06:00
2024-09-30 18:10:50 -06:00
switch ( tref . box ( ) - > record_enabled ( ) ) {
case Enabled :
2025-04-08 15:12:30 -05:00
context - > arc ( margin + ( size * 0.5 ) , margin + ( size * 0.5 ) , ( size * 0.5 ) , 0. , 360.0 * ( M_PI / 180.0 ) ) ;
2024-09-28 11:44:16 -06:00
if ( trigger ( ) - > armed ( ) ) {
2024-09-30 18:10:50 -06:00
solid = rec_blink_on ;
2024-09-28 11:44:16 -06:00
} else {
2024-09-30 18:10:50 -06:00
solid = false ;
2024-09-28 11:44:16 -06:00
}
2024-09-30 18:10:50 -06:00
break ;
2024-09-28 11:44:16 -06:00
2024-09-30 18:10:50 -06:00
case Recording :
2025-04-08 15:12:30 -05:00
context - > arc ( margin + ( size * 0.5 ) , margin + ( size * 0.5 ) , ( size * 0.5 ) , 0. , 360.0 * ( M_PI / 180.0 ) ) ;
2024-09-30 18:10:50 -06:00
if ( trigger ( ) - > armed ( ) ) {
solid = true ;
} else {
solid = false ;
}
break ;
case Disabled :
/* not recording and no content in this slot, it is only a Stop button */
2024-09-28 11:44:16 -06:00
context - > move_to ( margin , margin ) ;
context - > rel_line_to ( size , 0 ) ;
context - > rel_line_to ( 0 , size ) ;
context - > rel_line_to ( - size , 0 ) ;
context - > rel_line_to ( 0 , - size ) ;
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ) ;
context - > stroke ( ) ;
2024-09-30 18:10:50 -06:00
return ;
2024-09-28 11:44:16 -06:00
}
2024-09-30 18:10:50 -06:00
if ( solid ) {
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " record enable button: fill active " ) ) ;
context - > fill ( ) ;
} else {
set_source_rgba ( context , bg_color ( ) ) ;
context - > fill_preserve ( ) ;
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " record enable button: fill active " ) ) ;
context - > stroke ( ) ;
}
2022-01-11 13:52:02 +01:00
return ;
2021-12-21 13:31:37 -06:00
}
2022-01-04 15:59:24 -06:00
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ) ;
2022-01-11 13:52:02 +01:00
switch ( trigger ( ) - > launch_style ( ) ) {
2021-12-21 13:31:37 -06:00
case Trigger : : Toggle :
2022-01-04 15:59:24 -06:00
if ( active ) {
2022-01-11 13:52:02 +01:00
/* special case: now it's a square Stop button */
context - > move_to ( margin , margin ) ;
2025-01-27 15:15:20 -07:00
context - > rel_line_to ( size , 0 ) ;
2022-01-04 15:59:24 -06:00
context - > rel_line_to ( - size , 0 ) ;
context - > line_to ( margin , margin ) ;
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:foreground " ) ) ;
context - > fill ( ) ;
context - > stroke ( ) ;
2025-01-27 15:15:20 -07:00
} else { /* boxy arrow */
context - > rel_line_to ( 0 , size ) ;
2022-01-11 13:52:02 +01:00
context - > move_to ( margin , margin ) ;
2022-01-04 15:59:24 -06:00
context - > rel_line_to ( 0 , size ) ;
2022-01-11 13:52:02 +01:00
context - > rel_line_to ( size * 1 / 3 , 0 ) ;
context - > rel_line_to ( size * 2 / 3 , - size / 2 ) ;
context - > rel_line_to ( - size * 2 / 3 , - size / 2 ) ;
2022-01-04 15:59:24 -06:00
context - > line_to ( margin , margin ) ;
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ) ;
context - > stroke ( ) ;
}
break ;
2021-12-21 13:31:37 -06:00
case Trigger : : OneShot :
2021-12-21 14:55:08 -06:00
context - > move_to ( margin , margin ) ;
context - > rel_line_to ( 0 , size ) ;
2022-01-11 13:52:02 +01:00
context - > rel_line_to ( size , - size / 2 ) ;
2021-12-21 14:55:08 -06:00
context - > line_to ( margin , margin ) ;
2022-01-04 15:59:24 -06:00
if ( active ) {
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:foreground " ) ) ;
context - > fill ( ) ;
context - > stroke ( ) ;
} else {
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ) ;
context - > stroke ( ) ;
}
2021-12-21 13:31:37 -06:00
break ;
2022-01-11 13:52:02 +01:00
case Trigger : : ReTrigger :
/* line + boxy arrow + line */
2022-01-04 15:59:24 -06:00
if ( active ) {
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:foreground " ) ) ;
} else {
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ) ;
}
2022-01-11 13:52:02 +01:00
/* vertical line at left */
2022-01-04 15:59:24 -06:00
context - > set_line_width ( 2 * scale ) ;
2022-01-11 13:52:02 +01:00
context - > move_to ( margin + 1 * scale , margin ) ;
context - > line_to ( margin + 1 * scale , margin + size ) ;
2022-01-03 17:45:13 -07:00
context - > stroke ( ) ;
2022-01-04 15:59:24 -06:00
2022-01-11 13:52:02 +01:00
/* small triangle */
2022-01-04 15:59:24 -06:00
context - > set_line_width ( 1 * scale ) ;
2022-01-11 13:52:02 +01:00
context - > move_to ( margin + scale * 4 , margin + 2 * scale ) ;
context - > line_to ( margin + size , margin + size / 2 ) ;
context - > line_to ( margin + scale * 4 , margin + size - 2 * scale ) ;
context - > line_to ( margin + scale * 4 , margin + 2 * scale ) ;
2022-01-04 15:59:24 -06:00
if ( active ) {
context - > fill ( ) ;
} else {
context - > stroke ( ) ;
}
2022-01-03 17:45:13 -07:00
break ;
2022-01-11 13:52:02 +01:00
case Trigger : : Gate :
/* diamond shape */
context - > move_to ( margin + size / 2 , margin ) ;
context - > rel_line_to ( size / 2 , size / 2 ) ;
context - > rel_line_to ( - size / 2 , size / 2 ) ;
context - > rel_line_to ( - size / 2 , - size / 2 ) ;
context - > rel_line_to ( size / 2 , - size / 2 ) ;
2022-01-04 15:59:24 -06:00
if ( active ) {
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:foreground " ) ) ;
context - > fill ( ) ;
context - > stroke ( ) ;
} else {
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ) ;
context - > stroke ( ) ;
}
2021-12-21 13:31:37 -06:00
break ;
2022-01-11 13:52:02 +01:00
case Trigger : : Repeat :
/* 'stutter' shape */
2021-12-21 13:31:37 -06:00
context - > set_line_width ( 1 * scale ) ;
2022-01-11 13:52:02 +01:00
context - > move_to ( margin , margin ) ;
context - > rel_line_to ( 0 , size ) ;
2021-12-21 13:31:37 -06:00
2022-01-11 13:52:02 +01:00
context - > move_to ( margin + scale * 3 , margin + scale * 2 ) ;
context - > rel_line_to ( 0 , size - scale * 4 ) ;
2021-12-21 13:31:37 -06:00
2022-01-11 13:52:02 +01:00
context - > move_to ( margin + scale * 6 , margin + scale * 3 ) ;
context - > rel_line_to ( 0 , size - scale * 6 ) ;
2021-12-21 13:31:37 -06:00
2022-01-04 15:59:24 -06:00
if ( active ) {
set_source_rgba ( context , UIConfiguration : : instance ( ) . color ( " neutral:foregroundest " ) ) ;
} else {
2022-01-11 13:52:02 +01:00
/* stutter shape needs to be brighter to maintain balance */
set_source_rgba ( context , HSV ( UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ) . lighter ( 0.25 ) . color ( ) ) ;
2022-01-04 15:59:24 -06:00
}
2021-12-21 13:31:37 -06:00
context - > stroke ( ) ;
break ;
default :
2025-01-27 15:15:20 -07:00
std : : cerr < < " other \n " ;
2021-12-21 13:31:37 -06:00
break ;
}
context - > set_line_width ( 1 ) ;
}
2021-12-05 10:56:42 -06:00
void
2021-12-17 16:32:52 +01:00
TriggerEntry : : render ( ArdourCanvas : : Rect const & area , Cairo : : RefPtr < Cairo : : Context > context ) const
2021-12-05 10:56:42 -06:00
{
2021-12-17 16:32:52 +01:00
Rectangle : : render ( area , context ) ;
2021-12-12 11:20:11 -06:00
/* Note that item_to_window() already takes _position into account (as
2022-01-11 13:52:02 +01:00
* part of item_to_canvas ( )
*/
2021-12-17 16:32:52 +01:00
ArdourCanvas : : Rect self ( item_to_window ( _rect ) ) ;
2021-12-15 09:32:58 -07:00
const ArdourCanvas : : Rect draw = self . intersection ( area ) ;
2021-12-12 11:20:11 -06:00
if ( ! draw ) {
return ;
}
2021-12-17 16:32:52 +01:00
float width = _rect . width ( ) ;
float height = _rect . height ( ) ;
2021-12-12 11:20:11 -06:00
2021-12-17 16:32:52 +01:00
const double scale = UIConfiguration : : instance ( ) . get_ui_scale ( ) ;
2021-12-12 11:20:11 -06:00
if ( _fill & & ! _transparent ) {
setup_fill_context ( context ) ;
2021-12-17 16:32:52 +01:00
context - > rectangle ( draw . x0 , draw . y0 , draw . width ( ) , draw . height ( ) ) ;
2021-12-12 11:20:11 -06:00
context - > fill ( ) ;
}
render_children ( area , context ) ;
2022-01-25 18:07:13 -07:00
if ( trigger ( ) - > cue_isolated ( ) ) {
2021-12-18 00:17:58 +01:00
/* left shadow */
2024-12-02 23:07:00 +01:00
context - > save ( ) ;
2021-12-18 00:17:58 +01:00
context - > translate ( self . x0 , self . y0 - 0.5 ) ;
Cairo : : RefPtr < Cairo : : LinearGradient > l_shadow = Cairo : : LinearGradient : : create ( 0 , 0 , scale * 12 , 0 ) ;
l_shadow - > add_color_stop_rgba ( 0.0 , 0.0 , 0.0 , 0.0 , 0.8 ) ;
l_shadow - > add_color_stop_rgba ( 1.0 , 0.0 , 0.0 , 0.0 , 0.0 ) ;
2021-12-17 09:45:34 -06:00
context - > set_source ( l_shadow ) ;
2021-12-18 00:17:58 +01:00
context - > rectangle ( 0 , 0 , scale * 12 , height ) ;
2021-12-17 09:45:34 -06:00
context - > fill ( ) ;
2024-12-02 23:07:00 +01:00
context - > restore ( ) ;
2021-12-17 09:45:34 -06:00
}
2022-01-18 15:52:54 -06:00
if ( false /*tref.slot == 1*/ ) {
2021-12-18 00:17:58 +01:00
/* drop-shadow at top */
Cairo : : RefPtr < Cairo : : LinearGradient > drop_shadow_pattern = Cairo : : LinearGradient : : create ( 0.0 , 0.0 , 0.0 , 6 * scale ) ;
drop_shadow_pattern - > add_color_stop_rgba ( 0 , 0 , 0 , 0 , 0.7 ) ;
drop_shadow_pattern - > add_color_stop_rgba ( 1 , 0 , 0 , 0 , 0.0 ) ;
2021-12-17 08:18:06 -06:00
context - > set_source ( drop_shadow_pattern ) ;
2021-12-17 16:32:52 +01:00
context - > rectangle ( 0 , 0 , width , 6 * scale ) ;
2021-12-12 11:20:11 -06:00
context - > fill ( ) ;
}
2021-12-21 13:31:37 -06:00
/* launch icon */
{
2024-12-02 23:07:00 +01:00
context - > save ( ) ;
2021-12-21 13:31:37 -06:00
context - > translate ( self . x0 , self . y0 - 0.5 ) ;
context - > translate ( 0 , 0 ) ; // left side of the widget
draw_launch_icon ( context , height , scale ) ;
2024-12-02 23:07:00 +01:00
context - > restore ( ) ;
2021-12-21 13:31:37 -06:00
}
2021-12-17 16:32:52 +01:00
/* follow-action icon */
2024-10-02 13:46:59 -06:00
if ( trigger ( ) - > playable ( ) & & trigger ( ) - > will_follow ( ) ) {
2024-12-02 23:07:00 +01:00
context - > save ( ) ;
2021-12-17 16:32:52 +01:00
context - > translate ( self . x0 , self . y0 - 0.5 ) ;
context - > translate ( width - height , 0 ) ; // right side of the widget
2022-01-26 10:12:03 -07:00
draw_follow_icon ( context , trigger ( ) - > follow_action0 ( ) , height , scale ) ;
2024-12-02 23:07:00 +01:00
context - > restore ( ) ;
2021-12-14 20:35:28 -06:00
}
2021-10-03 16:46:39 -06:00
}
2021-08-05 16:20:56 -06:00
void
2021-12-30 12:10:50 -06:00
TriggerEntry : : on_trigger_changed ( PropertyChange const & change )
2021-08-05 16:20:56 -06:00
{
if ( change . contains ( ARDOUR : : Properties : : name ) ) {
2024-10-02 13:46:59 -06:00
if ( trigger ( ) - > playable ( ) ) {
2022-01-11 13:52:02 +01:00
name_text - > set ( short_version ( trigger ( ) - > name ( ) , 16 ) ) ;
2021-08-05 16:20:56 -06:00
} else {
2021-08-07 18:55:05 -06:00
name_text - > set ( " " ) ;
}
2024-09-30 14:07:57 -06:00
set_play_button_tooltip ( ) ;
2021-08-07 18:55:05 -06:00
}
2022-01-11 22:09:17 +01:00
set_widget_colors ( ) ; //depending on the state, this might change a color and queue a redraw
2021-12-27 11:40:39 -06:00
2021-12-18 00:17:58 +01:00
PropertyChange interesting_stuff ;
2021-12-21 13:31:37 -06:00
interesting_stuff . add ( ARDOUR : : Properties : : name ) ;
2021-12-27 11:40:39 -06:00
interesting_stuff . add ( ARDOUR : : Properties : : color ) ;
2021-12-21 13:31:37 -06:00
interesting_stuff . add ( ARDOUR : : Properties : : launch_style ) ;
2021-12-18 00:17:58 +01:00
interesting_stuff . add ( ARDOUR : : Properties : : follow_action0 ) ;
2021-12-27 16:49:22 -06:00
interesting_stuff . add ( ARDOUR : : Properties : : follow_action1 ) ;
interesting_stuff . add ( ARDOUR : : Properties : : follow_action_probability ) ;
interesting_stuff . add ( ARDOUR : : Properties : : follow_count ) ;
2022-01-26 10:12:03 -07:00
interesting_stuff . add ( ARDOUR : : Properties : : cue_isolated ) ;
2021-12-21 13:31:37 -06:00
interesting_stuff . add ( ARDOUR : : Properties : : running ) ;
2021-12-14 20:35:28 -06:00
2021-12-18 00:17:58 +01:00
if ( change . contains ( interesting_stuff ) ) {
redraw ( ) ;
2021-12-17 09:45:34 -06:00
}
2021-08-05 16:20:56 -06:00
}
2024-09-30 14:07:57 -06:00
Color
TriggerEntry : : bg_color ( ) const
2021-12-05 10:56:42 -06:00
{
2024-09-30 14:07:57 -06:00
Color bg_col = UIConfiguration : : instance ( ) . color ( " theme:bg " ) ;
2022-01-11 09:48:32 -06:00
//alternating darker bands
2024-09-17 10:55:52 -06:00
if ( ( tref . slot ( ) / 2 ) % 2 = = 0 ) {
2022-01-18 15:52:54 -06:00
bg_col = HSV ( bg_col ) . darker ( 0.25 ) . color ( ) ;
2021-12-17 16:32:52 +01:00
}
2024-09-30 14:07:57 -06:00
return bg_col ;
}
void
TriggerEntry : : set_widget_colors ( TriggerEntry : : EnteredState es )
{
color_t bg_col = bg_color ( ) ;
2022-01-11 09:48:32 -06:00
set_fill_color ( bg_col ) ;
//child widgets highlight when entered
color_t hilite = HSV ( bg_col ) . lighter ( 0.15 ) . color ( ) ;
2022-01-11 22:09:17 +01:00
play_button - > set_fill_color ( ( es = = PlayEntered ) ? hilite : bg_col ) ;
play_button - > set_outline_color ( ( es = = PlayEntered ) ? hilite : bg_col ) ;
2022-01-11 09:48:32 -06:00
2022-01-11 22:09:17 +01:00
name_button - > set_fill_color ( ( es = = NameEntered ) ? hilite : bg_col ) ;
name_button - > set_outline_color ( ( es = = NameEntered ) ? hilite : bg_col ) ;
2022-01-11 09:48:32 -06:00
2022-01-11 22:09:17 +01:00
follow_button - > set_fill_color ( ( es = = FollowEntered ) ? hilite : bg_col ) ;
2022-01-11 09:48:32 -06:00
2022-01-11 22:09:17 +01:00
name_text - > set_color ( trigger ( ) - > color ( ) ) ;
2021-12-17 16:32:52 +01:00
name_text - > set_fill_color ( UIConfiguration : : instance ( ) . color ( " neutral:midground " ) ) ;
2021-12-05 10:56:42 -06:00
/*preserve selection border*/
2021-12-17 16:32:52 +01:00
if ( PublicEditor : : instance ( ) . get_selection ( ) . selected ( this ) ) {
name_button - > set_outline_color ( UIConfiguration : : instance ( ) . color ( " alert:red " ) ) ;
2021-12-05 10:56:42 -06:00
}
2022-01-11 09:48:32 -06:00
/*draw a box around 'queued' trigger*/
2022-01-11 22:09:17 +01:00
if ( ! trigger ( ) - > active ( ) & & trigger ( ) - > box ( ) . currently_playing ( ) = = trigger ( ) ) {
2022-01-11 09:48:32 -06:00
play_button - > set_outline_color ( UIConfiguration : : instance ( ) . color ( " neutral:foreground " ) ) ;
}
2021-12-05 10:56:42 -06:00
}
void
TriggerEntry : : ui_parameter_changed ( std : : string const & p )
{
if ( p = = " color-file " ) {
2022-01-11 09:48:32 -06:00
set_widget_colors ( ) ;
2021-12-05 10:56:42 -06:00
}
}
2021-08-05 15:04:02 -06:00
bool
2021-12-30 20:27:26 -06:00
TriggerEntry : : name_button_event ( GdkEvent * ev )
2021-08-05 15:04:02 -06:00
{
switch ( ev - > type ) {
2025-04-28 14:26:15 -06:00
case GDK_ENTER_NOTIFY :
if ( ev - > crossing . detail ! = GDK_NOTIFY_INFERIOR ) {
set_widget_colors ( NameEntered ) ;
}
break ;
case GDK_LEAVE_NOTIFY :
if ( ev - > crossing . detail ! = GDK_NOTIFY_INFERIOR ) {
set_widget_colors ( NoneEntered ) ;
}
break ;
case GDK_BUTTON_PRESS :
if ( Gtkmm2ext : : Keyboard : : is_context_menu_event ( & ev - > button ) ) {
PublicEditor : : instance ( ) . get_selection ( ) . set ( this ) ;
context_menu ( ) ;
return true ;
}
break ;
case GDK_2BUTTON_PRESS :
2022-01-31 19:53:33 -06:00
# if SELECTION_PROPERTIES_BOX_TODO
2025-04-28 14:26:15 -06:00
edit_trigger ( ) ;
2022-01-31 19:53:33 -06:00
# endif
2025-04-28 14:26:15 -06:00
return true ;
case GDK_BUTTON_RELEASE :
if ( Gtkmm2ext : : Keyboard : : is_delete_event ( & ev - > button ) ) {
clear_trigger ( ) ;
return true ;
}
switch ( ev - > button . button ) {
case 1 :
PublicEditor : : instance ( ) . get_selection ( ) . set ( this ) ;
2021-08-05 15:04:02 -06:00
return true ;
default :
break ;
2025-04-28 14:26:15 -06:00
}
break ;
default :
break ;
2021-08-05 15:04:02 -06:00
}
return false ;
}
2021-07-27 20:39:55 -06:00
bool
2021-12-30 20:27:26 -06:00
TriggerEntry : : play_button_event ( GdkEvent * ev )
2021-07-27 20:39:55 -06:00
{
2024-10-02 13:46:59 -06:00
if ( ! trigger ( ) - > playable ( ) ) {
2021-12-21 13:31:37 -06:00
/* empty slot; this is just a stop button */
2021-08-06 21:27:38 -06:00
switch ( ev - > type ) {
2021-12-17 16:32:52 +01:00
case GDK_BUTTON_PRESS :
if ( ev - > button . button = = 1 ) {
2024-09-25 18:34:35 -06:00
if ( trigger ( ) - > box ( ) . record_enabled ( ) ) {
/* this is a record button */
if ( trigger ( ) - > armed ( ) ) {
trigger ( ) - > disarm ( ) ;
} else {
2025-05-13 12:16:04 -06:00
if ( Keyboard : : modifier_state_equals ( ev - > button . state , Keyboard : : PrimaryModifier ) ) {
/* Record till done */
trigger ( ) - > arm ( ) ;
} else {
/* use trigger follow length */
trigger ( ) - > arm ( trigger ( ) - > follow_length ( ) ) ;
}
2024-09-25 18:34:35 -06:00
}
return true ;
} else if ( Keyboard : : modifier_state_equals ( ev - > button . state , Keyboard : : PrimaryModifier ) ) {
2022-01-11 13:52:02 +01:00
trigger ( ) - > box ( ) . stop_all_immediately ( ) ;
2021-12-27 16:48:13 -06:00
} else {
2022-01-11 13:52:02 +01:00
trigger ( ) - > box ( ) . stop_all_quantized ( ) ;
2021-12-27 16:48:13 -06:00
}
2021-12-17 16:32:52 +01:00
}
2021-12-18 00:17:58 +01:00
break ;
2021-12-17 16:32:52 +01:00
default :
break ;
2021-08-06 21:27:38 -06:00
}
}
2021-07-27 20:39:55 -06:00
switch ( ev - > type ) {
2022-01-28 15:51:51 -06:00
case GDK_2BUTTON_PRESS : //need to un-grab in a double-click action
2021-12-17 16:32:52 +01:00
case GDK_BUTTON_PRESS :
switch ( ev - > button . button ) {
case 1 :
2022-01-11 13:52:02 +01:00
if ( trigger ( ) - > launch_style ( ) = = Trigger : : Gate | |
trigger ( ) - > launch_style ( ) = = Trigger : : Repeat ) {
trigger ( ) - > bang ( ) ;
2022-01-08 09:33:20 -06:00
_grabbed = true ;
2022-01-11 13:52:02 +01:00
play_button - > grab ( ) ;
2022-01-08 09:33:20 -06:00
} else {
2022-01-11 13:52:02 +01:00
trigger ( ) - > bang ( ) ;
2022-01-08 09:33:20 -06:00
}
2021-12-17 16:32:52 +01:00
default :
break ;
}
2021-08-05 15:04:02 -06:00
break ;
2021-12-17 16:32:52 +01:00
case GDK_BUTTON_RELEASE :
switch ( ev - > button . button ) {
case 1 :
2022-01-08 09:33:20 -06:00
if ( _grabbed ) {
2022-01-11 13:52:02 +01:00
trigger ( ) - > unbang ( ) ;
play_button - > ungrab ( ) ;
2022-01-08 09:33:20 -06:00
_grabbed = false ;
2021-12-17 16:32:52 +01:00
}
break ;
2021-12-21 13:31:37 -06:00
case 3 :
2021-12-30 20:27:26 -06:00
launch_context_menu ( ) ;
2021-12-21 13:31:37 -06:00
return true ;
2021-12-17 16:32:52 +01:00
default :
break ;
}
break ;
case GDK_ENTER_NOTIFY :
if ( ev - > crossing . detail ! = GDK_NOTIFY_INFERIOR ) {
2022-01-11 09:48:32 -06:00
set_widget_colors ( PlayEntered ) ;
2021-12-17 16:32:52 +01:00
}
break ;
case GDK_LEAVE_NOTIFY :
if ( ev - > crossing . detail ! = GDK_NOTIFY_INFERIOR ) {
2022-01-11 09:48:32 -06:00
set_widget_colors ( NoneEntered ) ;
2021-08-05 15:04:02 -06:00
}
break ;
default :
break ;
2021-07-27 20:39:55 -06:00
}
2022-02-03 21:56:35 +01:00
return true ;
2021-07-27 20:39:55 -06:00
}
2021-12-21 13:31:37 -06:00
bool
2021-12-30 20:27:26 -06:00
TriggerEntry : : follow_button_event ( GdkEvent * ev )
2021-12-21 13:31:37 -06:00
{
switch ( ev - > type ) {
2022-01-27 13:06:50 -06:00
case GDK_BUTTON_PRESS :
return true ; //wait for release to show the menu
break ;
2021-12-21 13:31:37 -06:00
case GDK_BUTTON_RELEASE :
switch ( ev - > button . button ) {
2022-01-27 13:06:50 -06:00
case 1 :
2021-12-21 13:31:37 -06:00
case 3 :
2023-10-31 20:25:18 +01:00
follow_context_menu ( & ev - > button ) ;
2021-12-21 13:31:37 -06:00
default :
break ;
}
break ;
case GDK_ENTER_NOTIFY :
if ( ev - > crossing . detail ! = GDK_NOTIFY_INFERIOR ) {
2022-01-11 09:48:32 -06:00
set_widget_colors ( FollowEntered ) ;
2021-12-21 13:31:37 -06:00
}
break ;
case GDK_LEAVE_NOTIFY :
if ( ev - > crossing . detail ! = GDK_NOTIFY_INFERIOR ) {
2022-01-11 09:48:32 -06:00
set_widget_colors ( NoneEntered ) ;
2021-12-21 13:31:37 -06:00
}
break ;
default :
break ;
}
2022-02-03 21:56:35 +01:00
return true ;
2021-12-21 13:31:37 -06:00
}
2022-01-11 19:39:08 +01:00
bool
TriggerEntry : : event ( GdkEvent * ev )
{
2024-10-02 13:46:59 -06:00
if ( ! trigger ( ) - > playable ( ) ) {
2022-01-11 19:39:08 +01:00
return false ;
}
switch ( ev - > type ) {
2022-01-28 15:51:51 -06:00
case GDK_2BUTTON_PRESS : //need to un-grab in a double-click action
2022-01-11 19:39:08 +01:00
case GDK_BUTTON_RELEASE :
2022-01-27 12:44:02 -06:00
if ( _grabbed ) {
ungrab ( ) ;
_grabbed = false ;
2022-02-03 23:29:01 +01:00
if ( ev - > type = = GDK_BUTTON_RELEASE ) {
/* Pass event down to child item, as if this item was not grabbed.
* This is needed to select item on release .
*/
name_button - > Event ( ev ) ;
return true ;
}
2022-02-03 21:56:35 +01:00
}
break ;
2022-01-11 19:39:08 +01:00
case GDK_BUTTON_PRESS :
if ( ! _drag_active ) {
GdkEventButton * bev = ( GdkEventButton * ) ev ;
if ( bev - > button = = 1 ) {
_drag_start_x = bev - > x ;
_drag_start_y = bev - > y ;
2022-01-27 12:44:02 -06:00
_grabbed = true ;
grab ( ) ;
2022-01-11 19:39:08 +01:00
return true ;
} else {
_drag_start_x = - 1 ;
_drag_start_y = - 1 ;
}
}
break ;
case GDK_MOTION_NOTIFY :
if ( ! _drag_active ) {
int x , y ;
Gdk : : ModifierType mask ;
GtkCanvas * gtkcanvas = static_cast < GtkCanvas * > ( canvas ( ) ) ;
gtkcanvas - > get_window ( ) - > get_pointer ( x , y , mask ) ;
if ( mask & GDK_BUTTON1_MASK ) {
if ( gtkcanvas - > drag_check_threshold ( _drag_start_x , _drag_start_y , x , y ) ) {
_drag_active = true ;
gtkcanvas - > drag_begin ( TriggerBoxUI : : dnd_src ( ) , Gdk : : ACTION_COPY , 1 , ev ) ;
// -> save a reference to the dragged slot, for use in ::drag_begin ::drag_data_get()
return true ;
}
}
}
2022-02-03 21:56:35 +01:00
break ;
2022-01-11 19:39:08 +01:00
default :
break ;
}
return false ;
}
void
TriggerEntry : : drag_begin ( Glib : : RefPtr < Gdk : : DragContext > const & context )
{
2022-01-11 21:53:56 +01:00
if ( ! _drag_active ) {
/* Since the canvas is shared, all TriggerEntry inside
* the TriggerBox canvas receive this signal
*/
return ;
}
//const ArdourCanvas::Rect rect = allocation ();
int width = _rect . width ( ) ;
int height = _rect . height ( ) ;
GtkCanvas * gtkcanvas = static_cast < GtkCanvas * > ( canvas ( ) ) ;
Glib : : RefPtr < Gdk : : Pixmap > pixmap = Gdk : : Pixmap : : create ( gtkcanvas - > get_root_window ( ) , width , height ) ;
{
cairo_t * cr = gdk_cairo_create ( Glib : : unwrap ( pixmap ) ) ;
Cairo : : RefPtr < Cairo : : Context > ctx = Cairo : : RefPtr < Cairo : : Context > ( new Cairo : : Context ( cr , true /* has_reference */ ) ) ;
/* inverse offset, because ::render() translates coordinates itself */
ArdourCanvas : : Rect self ( item_to_window ( _rect ) ) ;
ctx - > translate ( - self . x0 , - self . y0 ) ;
ctx - > save ( ) ;
render ( self , ctx ) ;
ctx - > restore ( ) ;
/* draw an outline around the drag object, replace red selection border */
ctx - > set_identity_matrix ( ) ;
ctx - > rectangle ( 0 , 0 , width , height ) ;
set_source_rgba ( ctx , UIConfiguration : : instance ( ) . color ( " neutral:foreground " ) ) ;
ctx - > set_line_width ( 1.5 ) ;
ctx - > stroke ( ) ;
/* ctx leaves scope, cr is destroyed, and pixmap surface is flush()ed */
}
2024-10-02 13:46:59 -06:00
std : : shared_ptr < Region > region = trigger ( ) - > the_region ( ) ;
2022-01-19 05:05:57 +01:00
if ( region ) {
PublicEditor : : instance ( ) . pbdid_dragged_dt = region - > data_type ( ) ;
} else {
PublicEditor : : instance ( ) . pbdid_dragged_dt = DataType : : NIL ;
}
2022-01-11 21:53:56 +01:00
context - > set_icon ( pixmap - > get_colormap ( ) , pixmap , Glib : : RefPtr < Gdk : : Bitmap > ( NULL ) , width / 2 , height / 2 ) ;
2022-01-11 19:39:08 +01:00
}
void
TriggerEntry : : drag_end ( Glib : : RefPtr < Gdk : : DragContext > const & )
{
2022-01-19 05:05:57 +01:00
if ( _drag_active ) {
PublicEditor : : instance ( ) . pbdid_dragged_dt = DataType : : NIL ;
}
2022-01-11 19:39:08 +01:00
_drag_active = false ;
}
void
TriggerEntry : : drag_data_get ( Glib : : RefPtr < Gdk : : DragContext > const & , Gtk : : SelectionData & data , guint , guint )
{
if ( ! _drag_active ) {
/* Since the canvas is shared, all TriggerEntry instances
* inside a TriggerBox canvas receive this signal .
*/
return ;
}
2022-01-27 22:15:46 +01:00
if ( data . get_target ( ) = = " x-ardour/region.pbdid " ) {
2024-10-02 13:46:59 -06:00
std : : shared_ptr < Region > region = trigger ( ) - > the_region ( ) ;
2022-01-27 22:15:46 +01:00
if ( region ) {
data . set ( data . get_target ( ) , region - > id ( ) . to_s ( ) ) ;
}
}
if ( data . get_target ( ) = = " x-ardour/trigger.pbdid " ) {
data . set ( data . get_target ( ) , trigger ( ) - > id ( ) . to_s ( ) ) ;
2022-01-11 19:39:08 +01:00
}
}
2021-12-30 20:27:26 -06:00
/* ***************************************************** */
2022-01-11 19:39:08 +01:00
Glib : : RefPtr < Gtk : : TargetList > TriggerBoxUI : : _dnd_src ;
2024-01-11 18:00:07 -07:00
TriggerBoxUI : : TriggerBoxUI ( ArdourCanvas : : Item * parent , TriggerStrip & s , TriggerBox & tb )
2021-12-30 20:27:26 -06:00
: Rectangle ( parent )
, _triggerbox ( tb )
2024-01-11 18:00:07 -07:00
, _strip ( s )
2021-12-30 20:27:26 -06:00
{
set_layout_sensitive ( true ) ; // why???
set_fill_color ( UIConfiguration : : instance ( ) . color ( X_ ( " theme:bg " ) ) ) ;
set_fill ( true ) ;
build ( ) ;
2021-08-05 12:11:19 -06:00
2021-12-30 20:27:26 -06:00
_selection_connection = PublicEditor : : instance ( ) . get_selection ( ) . TriggersChanged . connect ( sigc : : mem_fun ( * this , & TriggerBoxUI : : selection_changed ) ) ;
2022-01-11 19:39:08 +01:00
/* DnD */
if ( ! _dnd_src ) {
std : : vector < Gtk : : TargetEntry > source_table ;
2022-01-27 13:10:54 -06:00
source_table . push_back ( Gtk : : TargetEntry ( " x-ardour/trigger.pbdid " , Gtk : : TARGET_SAME_APP ) ) ;
2022-01-27 22:15:46 +01:00
source_table . push_back ( Gtk : : TargetEntry ( " x-ardour/region.pbdid " , Gtk : : TARGET_SAME_APP ) ) ;
2022-01-11 19:39:08 +01:00
_dnd_src = Gtk : : TargetList : : create ( source_table ) ;
}
2021-12-30 20:27:26 -06:00
std : : vector < Gtk : : TargetEntry > target_table ;
2022-01-27 13:10:54 -06:00
target_table . push_back ( Gtk : : TargetEntry ( " x-ardour/trigger.pbdid " , Gtk : : TARGET_SAME_APP ) ) ;
2022-01-11 01:53:27 +01:00
target_table . push_back ( Gtk : : TargetEntry ( " x-ardour/region.pbdid " , Gtk : : TARGET_SAME_APP ) ) ;
2021-12-30 20:27:26 -06: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 " ) ) ;
GtkCanvas * gtkcanvas = static_cast < GtkCanvas * > ( canvas ( ) ) ;
assert ( gtkcanvas ) ;
gtkcanvas - > drag_dest_set ( target_table ) ;
gtkcanvas - > signal_drag_motion ( ) . connect ( sigc : : mem_fun ( * this , & TriggerBoxUI : : drag_motion ) ) ;
gtkcanvas - > signal_drag_leave ( ) . connect ( sigc : : mem_fun ( * this , & TriggerBoxUI : : drag_leave ) ) ;
gtkcanvas - > signal_drag_data_received ( ) . connect ( sigc : : mem_fun ( * this , & TriggerBoxUI : : drag_data_received ) ) ;
}
2021-08-05 12:11:19 -06:00
2021-12-30 20:27:26 -06:00
TriggerBoxUI : : ~ TriggerBoxUI ( )
{
/* sigc connection's are not scoped (i.e. they do not disconnect the
functor from the signal when they are destroyed ) .
*/
_selection_connection . disconnect ( ) ;
2021-08-05 12:11:19 -06:00
}
void
2021-12-30 20:27:26 -06:00
TriggerBoxUI : : selection_changed ( )
2021-08-05 12:11:19 -06:00
{
2021-12-30 20:27:26 -06:00
for ( auto & slot : _slots ) {
slot - > selection_change ( ) ;
}
}
2021-08-05 12:11:19 -06:00
2021-12-30 20:27:26 -06:00
void
TriggerBoxUI : : build ( )
{
TriggerPtr t ;
2022-01-11 13:52:02 +01:00
uint64_t n = 0 ;
2021-12-30 20:27:26 -06:00
// clear_items (true);
_slots . clear ( ) ;
while ( true ) {
t = _triggerbox . trigger ( n ) ;
if ( ! t ) {
2021-12-17 16:32:52 +01:00
break ;
2021-12-30 20:27:26 -06:00
}
2024-09-17 10:55:52 -06:00
TriggerEntry * te = new TriggerEntry ( this , _strip , TriggerReference ( _triggerbox . shared_from_this ( ) , n ) ) ;
2021-12-30 20:27:26 -06:00
_slots . push_back ( te ) ;
2021-08-05 12:11:19 -06:00
2021-10-11 17:58:51 -06:00
+ + n ;
}
2021-08-05 12:11:19 -06:00
}
2021-12-30 20:27:26 -06:00
void
TriggerBoxUI : : _size_allocate ( ArdourCanvas : : Rect const & alloc )
{
Rectangle : : _size_allocate ( alloc ) ;
const float width = alloc . width ( ) ;
const float height = alloc . height ( ) ;
2022-10-04 19:55:15 -06:00
const float slot_h = height / TriggerBox : : default_triggers_per_box ; // TODO
2021-12-30 20:27:26 -06:00
float ypos = 0 ;
for ( auto & slot : _slots ) {
slot - > size_allocate ( ArdourCanvas : : Rect ( 0 , 0 , width , slot_h ) ) ;
slot - > set_position ( Duple ( 0 , ypos ) ) ;
ypos + = slot_h ;
slot - > show ( ) ;
}
}
2021-12-14 00:45:16 +01:00
uint64_t
TriggerBoxUI : : slot_at_y ( int y ) const
2021-12-13 22:51:16 +01:00
{
2021-12-14 00:45:16 +01:00
uint64_t n = 0 ;
2021-12-13 23:05:03 +01:00
for ( auto & slot : _slots ) {
if ( slot - > height ( ) < y ) {
+ + n ;
y - = slot - > height ( ) ;
}
}
2021-12-14 00:45:16 +01:00
return n ;
}
bool
2021-12-17 16:32:52 +01:00
TriggerBoxUI : : drag_motion ( Glib : : RefPtr < Gdk : : DragContext > const & context , int , int y , guint time )
2021-12-14 00:45:16 +01:00
{
2022-01-21 02:14:05 +01:00
bool can_drop = true ;
GtkCanvas * gtkcanvas = static_cast < GtkCanvas * > ( canvas ( ) ) ;
std : : string target = gtkcanvas - > drag_dest_find_target ( context , gtkcanvas - > drag_dest_get_target_list ( ) ) ;
2022-01-27 13:10:54 -06:00
if ( ( target = = " x-ardour/region.pbdid " ) | | ( target = = " x-ardour/trigger.pbdid " ) ) {
2022-01-27 22:15:46 +01:00
can_drop = PublicEditor : : instance ( ) . pbdid_dragged_dt = = _triggerbox . data_type ( ) ;
2022-01-21 02:14:05 +01:00
}
2022-01-19 05:05:57 +01:00
uint64_t n = slot_at_y ( y ) ;
2021-12-14 00:45:16 +01:00
if ( n > = _slots . size ( ) ) {
assert ( 0 ) ;
can_drop = false ;
}
if ( can_drop ) {
2021-12-17 16:32:52 +01:00
context - > drag_status ( Gdk : : ACTION_COPY , time ) ;
2021-12-14 00:45:16 +01:00
/* prelight */
GdkEventCrossing ev ;
ev . detail = GDK_NOTIFY_ANCESTOR ;
for ( size_t i = 0 ; i < _slots . size ( ) ; + + i ) {
ev . type = ( i = = n ) ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY ;
2021-12-30 20:27:26 -06:00
_slots [ i ] - > name_button_event ( ( GdkEvent * ) & ev ) ;
2021-12-14 00:45:16 +01:00
}
return true ;
} else {
context - > drag_status ( Gdk : : DragAction ( 0 ) , time ) ;
return false ;
}
}
void
2021-12-17 16:32:52 +01:00
TriggerBoxUI : : drag_leave ( Glib : : RefPtr < Gdk : : DragContext > const & , guint )
2021-12-14 00:45:16 +01:00
{
GdkEventCrossing ev ;
2021-12-17 16:32:52 +01:00
ev . type = GDK_LEAVE_NOTIFY ;
2021-12-14 00:45:16 +01:00
ev . detail = GDK_NOTIFY_ANCESTOR ;
for ( size_t i = 0 ; i < _slots . size ( ) ; + + i ) {
2021-12-30 20:27:26 -06:00
_slots [ i ] - > name_button_event ( ( GdkEvent * ) & ev ) ;
2021-12-14 00:45:16 +01:00
}
}
2021-12-13 23:05:03 +01:00
2021-12-14 00:45:16 +01:00
void
TriggerBoxUI : : drag_data_received ( Glib : : RefPtr < Gdk : : DragContext > const & context , int /*x*/ , int y , Gtk : : SelectionData const & data , guint /*info*/ , guint time )
{
uint64_t n = slot_at_y ( y ) ;
if ( n > = _slots . size ( ) ) {
context - > drag_finish ( false , false , time ) ;
return ;
}
2021-12-13 22:51:16 +01:00
2022-01-11 01:53:27 +01:00
if ( data . get_target ( ) = = " x-ardour/region.pbdid " ) {
2022-01-11 13:52:02 +01:00
PBD : : ID rid ( data . get_data_as_string ( ) ) ;
2023-02-16 16:33:28 -07:00
std : : shared_ptr < Region > region = RegionFactory : : region_by_id ( rid ) ;
2022-01-11 01:53:27 +01:00
if ( region ) {
_triggerbox . set_from_selection ( n , region ) ;
context - > drag_finish ( true , false , time ) ;
} else {
context - > drag_finish ( false , false , time ) ;
}
return ;
}
2022-01-27 13:13:37 -06:00
if ( data . get_target ( ) = = " x-ardour/trigger.pbdid " ) {
PBD : : ID tid ( data . get_data_as_string ( ) ) ;
2023-02-16 16:33:28 -07:00
std : : shared_ptr < Trigger > source = _triggerbox . session ( ) . trigger_by_id ( tid ) ;
2022-01-27 13:13:37 -06:00
if ( source ) {
2022-02-11 07:32:30 -06:00
Trigger : : UIState * state = new Trigger : : UIState ( ) ;
source - > get_ui_state ( * state ) ;
2023-02-16 16:33:28 -07:00
std : : shared_ptr < Trigger : : UIState > state_p ( state ) ;
2024-10-02 13:46:59 -06:00
_triggerbox . enqueue_trigger_state_for_region ( source - > the_region ( ) , state_p ) ;
_triggerbox . set_from_selection ( n , source - > the_region ( ) ) ;
2022-01-27 13:13:37 -06:00
context - > drag_finish ( true , false , time ) ;
} else {
context - > drag_finish ( false , false , time ) ;
}
return ;
}
2021-12-13 22:51:16 +01:00
std : : vector < std : : string > paths ;
if ( ARDOUR_UI_UTILS : : convert_drop_to_paths ( paths , data ) ) {
for ( std : : vector < std : : string > : : iterator s = paths . begin ( ) ; s ! = paths . end ( ) ; + + s ) {
/* this will do nothing if n is too large */
_triggerbox . set_from_path ( n , * s ) ;
2022-02-10 02:21:53 +01:00
# if 1 /* assume drop from sidebar -- TODO use a special data.get_target() ? */
ARDOUR_UI_UTILS : : copy_patch_changes ( _triggerbox . session ( ) . the_auditioner ( ) , _triggerbox . trigger ( n ) ) ;
# endif
2021-12-13 22:51:16 +01:00
+ + n ;
}
}
context - > drag_finish ( true , false , time ) ;
}
2021-12-30 12:10:50 -06:00
/* ********************************************** */
2024-01-11 18:00:07 -07:00
TriggerBoxWidget : : TriggerBoxWidget ( TriggerStrip & s , float w , float h )
2021-12-17 16:32:52 +01:00
: FittedCanvasWidget ( w , h )
2024-01-11 18:00:07 -07:00
, ui ( nullptr )
, _strip ( s )
2021-07-21 17:22:09 -06:00
{
2024-12-06 23:42:53 +01:00
use_intermediate_surface ( false ) ;
2021-12-17 16:32:52 +01:00
set_background_color ( UIConfiguration : : instance ( ) . color ( X_ ( " theme:bg " ) ) ) ;
2021-07-21 17:22:09 -06:00
}
2021-07-22 10:33:47 -06:00
2021-10-03 23:47:47 -06:00
void
2021-12-16 15:15:14 -07:00
TriggerBoxWidget : : set_triggerbox ( TriggerBox * tb )
2021-10-03 23:47:47 -06:00
{
2021-12-16 15:15:14 -07:00
if ( ui ) {
2021-12-17 16:32:52 +01:00
root ( ) - > remove ( ui ) ;
2021-12-16 15:15:14 -07:00
delete ui ;
2024-01-11 18:00:07 -07:00
ui = nullptr ;
2021-12-16 15:15:14 -07:00
}
2021-10-03 23:47:47 -06:00
2021-12-16 15:15:14 -07:00
if ( ! tb ) {
return ;
}
2024-01-11 18:00:07 -07:00
ui = new TriggerBoxUI ( root ( ) , _strip , * tb ) ;
2021-12-16 18:55:05 -07:00
repeat_size_allocation ( ) ;
2021-08-05 15:04:02 -06:00
}