2009-06-21 15:59:56 -04:00
/*
2019-08-02 17:26:43 -04:00
* Copyright ( C ) 2009 - 2011 Carl Hetherington < carl @ carlh . net >
* Copyright ( C ) 2009 - 2012 David Robillard < d @ drobilla . net >
* Copyright ( C ) 2009 - 2017 Paul Davis < paul @ linuxaudiosystems . com >
* Copyright ( C ) 2013 - 2019 Robin Gareus < robin @ gareus . org >
* Copyright ( C ) 2015 Tim Mayberry < mojofunk @ gmail . 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 .
*/
2009-06-21 15:59:56 -04:00
# include <gtkmm/stock.h>
2016-05-19 10:45:55 -04:00
2009-06-21 15:59:56 -04:00
# include "ardour/session.h"
# include "ardour/route_group.h"
2009-12-01 10:32:42 -05:00
# include "ardour/route.h"
2016-05-19 10:45:55 -04:00
# include "ardour/vca_manager.h"
# include "ardour/vca.h"
2009-12-21 13:23:07 -05:00
2016-06-11 15:49:18 -04:00
# include "gtkmm2ext/doi.h"
2009-12-21 13:23:07 -05:00
# include "gui_thread.h"
2009-06-21 15:59:56 -04:00
# include "route_group_dialog.h"
# include "group_tabs.h"
2009-06-22 20:27:52 -04:00
# include "keyboard.h"
2016-07-14 14:44:52 -04:00
# include "pbd/i18n.h"
2011-08-30 05:48:53 -04:00
# include "ardour_ui.h"
2014-06-09 23:28:32 -04:00
# include "rgb_macros.h"
2015-01-04 10:58:46 -05:00
# include "ui_config.h"
2011-08-30 05:48:53 -04:00
# include "utils.h"
2009-06-21 15:59:56 -04:00
2009-06-21 18:17:17 -04:00
using namespace std ;
2009-06-21 15:59:56 -04:00
using namespace Gtk ;
using namespace ARDOUR ;
2014-06-25 15:27:37 -04:00
using namespace ARDOUR_UI_UTILS ;
2009-12-04 17:51:32 -05:00
using Gtkmm2ext : : Keyboard ;
2009-06-21 15:59:56 -04:00
2011-08-30 05:48:53 -04:00
list < Gdk : : Color > GroupTabs : : _used_colors ;
2010-07-19 17:47:07 -04:00
GroupTabs : : GroupTabs ( )
: _menu ( 0 )
, _dragging ( 0 )
, _dragging_new_tab ( 0 )
2009-06-21 15:59:56 -04:00
{
2013-07-16 20:35:02 -04:00
add_events ( Gdk : : BUTTON_PRESS_MASK | Gdk : : BUTTON_RELEASE_MASK | Gdk : : POINTER_MOTION_MASK ) ;
2015-01-02 09:44:54 -05:00
UIConfiguration : : instance ( ) . ColorsChanged . connect ( sigc : : mem_fun ( * this , & GroupTabs : : queue_draw ) ) ;
2009-06-21 15:59:56 -04:00
}
2010-07-19 17:47:07 -04:00
GroupTabs : : ~ GroupTabs ( )
{
delete _menu ;
}
2009-06-21 15:59:56 -04:00
void
2009-12-17 13:24:23 -05:00
GroupTabs : : set_session ( Session * s )
2009-06-21 15:59:56 -04:00
{
2010-07-19 17:47:07 -04:00
SessionHandlePtr : : set_session ( s ) ;
2009-07-03 14:37:15 -04:00
2009-12-17 13:24:23 -05:00
if ( _session ) {
2011-09-07 07:56:23 -04:00
_session - > RouteGroupPropertyChanged . connect (
_session_connections , invalidator ( * this ) , boost : : bind ( & GroupTabs : : route_group_property_changed , this , _1 ) , gui_context ( )
) ;
_session - > RouteAddedToRouteGroup . connect (
_session_connections , invalidator ( * this ) , boost : : bind ( & GroupTabs : : route_added_to_route_group , this , _1 , _2 ) , gui_context ( )
) ;
_session - > RouteRemovedFromRouteGroup . connect (
_session_connections , invalidator ( * this ) , boost : : bind ( & GroupTabs : : route_removed_from_route_group , this , _1 , _2 ) , gui_context ( )
) ;
2015-10-05 10:17:49 -04:00
2016-02-12 16:58:33 -05:00
_session - > route_group_removed . connect ( _session_connections , invalidator ( * this ) , boost : : bind ( & GroupTabs : : set_dirty , this , ( cairo_rectangle_t * ) 0 ) , gui_context ( ) ) ;
2009-12-17 13:24:23 -05:00
}
2009-06-21 15:59:56 -04:00
}
/** Handle a size request.
* @ param req GTK requisition
*/
void
GroupTabs : : on_size_request ( Gtk : : Requisition * req )
{
2017-05-04 13:15:18 -04:00
req - > width = std : : max ( 16.f , rintf ( 16.f * UIConfiguration : : instance ( ) . get_ui_scale ( ) ) ) ;
req - > height = std : : max ( 16.f , rintf ( 16.f * UIConfiguration : : instance ( ) . get_ui_scale ( ) ) ) ;
2009-06-21 15:59:56 -04:00
}
bool
GroupTabs : : on_button_press_event ( GdkEventButton * ev )
{
using namespace Menu_Helpers ;
2009-06-21 18:17:17 -04:00
2021-03-28 11:43:17 -04:00
if ( ! get_sensitive ( ) ) {
return true ;
}
2009-06-21 18:17:17 -04:00
double const p = primary_coordinate ( ev - > x , ev - > y ) ;
2009-12-01 10:32:42 -05:00
list < Tab > : : iterator prev ;
list < Tab > : : iterator next ;
2009-06-21 18:17:17 -04:00
Tab * t = click_to_tab ( p , & prev , & next ) ;
2009-12-01 10:32:42 -05:00
_drag_min = prev ! = _tabs . end ( ) ? prev - > to : 0 ;
_drag_max = next ! = _tabs . end ( ) ? next - > from : extent ( ) ;
if ( ev - > button = = 1 ) {
if ( t = = 0 ) {
Tab n ;
n . from = n . to = p ;
_dragging_new_tab = true ;
if ( next = = _tabs . end ( ) ) {
_tabs . push_back ( n ) ;
t = & _tabs . back ( ) ;
} else {
list < Tab > : : iterator j = _tabs . insert ( next , n ) ;
t = & ( * j ) ;
}
2011-06-01 13:00:29 -04:00
2009-12-01 10:32:42 -05:00
} else {
_dragging_new_tab = false ;
2011-08-31 16:46:16 -04:00
_initial_dragging_routes = routes_for_tab ( t ) ;
2009-12-01 10:32:42 -05:00
}
2009-06-21 18:17:17 -04:00
_dragging = t ;
_drag_moved = false ;
2009-12-01 10:32:42 -05:00
_drag_first = p ;
2009-06-21 18:17:17 -04:00
double const h = ( t - > from + t - > to ) / 2 ;
2009-12-01 10:32:42 -05:00
if ( p < h ) {
_drag_moving = t - > from ;
_drag_fixed = t - > to ;
_drag_offset = p - t - > from ;
2009-06-21 18:17:17 -04:00
} else {
2009-12-01 10:32:42 -05:00
_drag_moving = t - > to ;
_drag_fixed = t - > from ;
_drag_offset = p - t - > to ;
2009-06-21 18:17:17 -04:00
}
} else if ( ev - > button = = 3 ) {
2009-06-21 15:59:56 -04:00
2009-06-22 19:17:46 -04:00
RouteGroup * g = t ? t - > group : 0 ;
2015-10-05 10:17:49 -04:00
2017-02-20 18:11:38 -05:00
if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : TertiaryModifier ) & & g ) {
remove_group ( g ) ;
} else if ( Keyboard : : modifier_state_equals ( ev - > state , Keyboard : : PrimaryModifier ) & & g ) {
edit_group ( g ) ;
2011-11-15 19:38:12 -05:00
} else {
2015-09-21 17:49:40 -04:00
Menu * m = get_menu ( g , true ) ;
2011-11-15 19:38:12 -05:00
if ( m ) {
m - > popup ( ev - > button , ev - > time ) ;
}
2009-06-23 19:05:14 -04:00
}
2009-06-21 15:59:56 -04:00
}
return true ;
}
2009-06-21 18:17:17 -04:00
bool
GroupTabs : : on_motion_notify_event ( GdkEventMotion * ev )
{
if ( _dragging = = 0 ) {
return false ;
}
double const p = primary_coordinate ( ev - > x , ev - > y ) ;
2009-10-14 12:10:01 -04:00
2009-12-01 10:32:42 -05:00
if ( p ! = _drag_first ) {
2009-06-21 18:17:17 -04:00
_drag_moved = true ;
}
2009-12-01 10:32:42 -05:00
_drag_moving = p - _drag_offset ;
2009-06-22 11:47:48 -04:00
2009-12-01 10:32:42 -05:00
_dragging - > from = min ( _drag_moving , _drag_fixed ) ;
_dragging - > to = max ( _drag_moving , _drag_fixed ) ;
2009-10-14 12:10:01 -04:00
2009-12-01 10:32:42 -05:00
_dragging - > from = max ( _dragging - > from , _drag_min ) ;
_dragging - > to = min ( _dragging - > to , _drag_max ) ;
2009-06-21 18:17:17 -04:00
set_dirty ( ) ;
queue_draw ( ) ;
2013-07-16 20:35:02 -04:00
gdk_event_request_motions ( ev ) ;
2009-06-21 18:17:17 -04:00
return true ;
}
bool
2012-05-02 16:29:46 -04:00
GroupTabs : : on_button_release_event ( GdkEventButton * )
2009-06-21 18:17:17 -04:00
{
if ( _dragging = = 0 ) {
return false ;
}
2015-10-05 10:17:49 -04:00
2009-06-21 18:17:17 -04:00
if ( ! _drag_moved ) {
2015-10-05 10:17:49 -04:00
2009-12-01 10:32:42 -05:00
if ( _dragging - > group ) {
2011-11-15 19:38:12 -05:00
/* toggle active state */
_dragging - > group - > set_active ( ! _dragging - > group - > is_active ( ) , this ) ;
2009-06-22 20:27:52 -04:00
}
2015-10-05 10:17:49 -04:00
2009-06-21 18:17:17 -04:00
} else {
2009-06-22 11:47:48 -04:00
/* finish drag */
2009-12-01 10:32:42 -05:00
RouteList routes = routes_for_tab ( _dragging ) ;
2015-10-05 10:17:49 -04:00
2009-12-01 10:32:42 -05:00
if ( ! routes . empty ( ) ) {
if ( _dragging_new_tab ) {
2016-06-11 15:49:18 -04:00
run_new_group_dialog ( & routes , false ) ;
2009-12-01 10:32:42 -05:00
} else {
2023-04-07 17:33:13 -04:00
std : : shared_ptr < RouteList const > r = _session - > get_routes ( ) ;
2018-03-20 13:07:55 -04:00
/* First add new ones, then remove old ones.
* We cannot allow the group to become temporarily empty , because
* Session : : route_removed_from_route_group ( ) will delete empty groups .
*/
for ( RouteList : : const_iterator i = routes . begin ( ) ; i ! = routes . end ( ) ; + + i ) {
/* RouteGroup::add () ignores routes already present in the set */
_dragging - > group - > add ( * i ) ;
}
2023-04-07 17:33:13 -04:00
for ( auto const & i : * r ) {
2015-10-05 10:17:49 -04:00
2011-08-31 16:46:16 -04:00
bool const was_in_tab = find (
2023-04-07 17:33:13 -04:00
_initial_dragging_routes . begin ( ) , _initial_dragging_routes . end ( ) , i ) ! = _initial_dragging_routes . end ( ) ;
2015-10-05 10:17:49 -04:00
2023-04-07 17:33:13 -04:00
bool const now_in_tab = find ( routes . begin ( ) , routes . end ( ) , i ) ! = routes . end ( ) ;
2015-10-05 10:17:49 -04:00
2011-08-31 16:46:16 -04:00
if ( was_in_tab & & ! now_in_tab ) {
2023-04-07 17:33:13 -04:00
_dragging - > group - > remove ( i ) ;
2009-12-01 10:32:42 -05:00
}
}
2018-03-20 13:07:55 -04:00
2009-12-01 10:32:42 -05:00
}
}
2015-10-05 10:17:49 -04:00
2009-06-21 18:17:17 -04:00
set_dirty ( ) ;
queue_draw ( ) ;
}
2015-10-05 10:17:49 -04:00
2009-12-01 10:32:42 -05:00
_dragging = 0 ;
2011-08-31 16:46:16 -04:00
_initial_dragging_routes . clear ( ) ;
2009-12-01 10:32:42 -05:00
2009-06-21 18:17:17 -04:00
return true ;
}
void
2017-03-20 12:11:56 -04:00
GroupTabs : : render ( Cairo : : RefPtr < Cairo : : Context > const & ctx , cairo_rectangle_t * )
2009-06-21 18:17:17 -04:00
{
2017-03-20 12:11:56 -04:00
cairo_t * cr = ctx - > cobj ( ) ;
2021-03-28 11:43:17 -04:00
Gdk : : Color c ;
if ( ! get_sensitive ( ) ) {
c = get_style ( ) - > get_base ( Gtk : : STATE_INSENSITIVE ) ;
} else {
c = get_style ( ) - > get_base ( Gtk : : STATE_NORMAL ) ;
2009-06-21 18:17:17 -04:00
}
/* background */
2009-10-14 12:10:01 -04:00
2012-03-17 13:14:15 -04:00
cairo_set_source_rgb ( cr , c . get_red_p ( ) , c . get_green_p ( ) , c . get_blue_p ( ) ) ;
2011-11-18 10:35:58 -05:00
cairo_rectangle ( cr , 0 , 0 , get_width ( ) , get_height ( ) ) ;
2009-06-21 18:17:17 -04:00
cairo_fill ( cr ) ;
2015-10-05 10:17:49 -04:00
2021-03-28 11:43:17 -04:00
if ( ! get_sensitive ( ) ) {
return ;
}
if ( _dragging = = 0 ) {
_tabs = compute_tabs ( ) ;
}
2009-06-21 18:17:17 -04:00
/* tabs */
for ( list < Tab > : : const_iterator i = _tabs . begin ( ) ; i ! = _tabs . end ( ) ; + + i ) {
draw_tab ( cr , * i ) ;
2009-10-14 12:10:01 -04:00
}
2009-06-21 18:17:17 -04:00
}
2009-06-22 11:47:48 -04:00
/** Convert a click position to a tab.
* @ param c Click position .
2011-08-15 10:08:01 -04:00
* @ param prev Filled in with the previous tab to the click , or _tabs . end ( ) .
* @ param next Filled in with the next tab after the click , or _tabs . end ( ) .
2009-06-22 11:47:48 -04:00
* @ return Tab under the click , or 0.
*/
2009-10-14 12:10:01 -04:00
2009-06-21 18:17:17 -04:00
GroupTabs : : Tab *
2009-12-01 10:32:42 -05:00
GroupTabs : : click_to_tab ( double c , list < Tab > : : iterator * prev , list < Tab > : : iterator * next )
2009-06-21 18:17:17 -04:00
{
2009-12-01 10:32:42 -05:00
* prev = * next = _tabs . end ( ) ;
Tab * under = 0 ;
2009-10-14 12:10:01 -04:00
2009-06-21 18:17:17 -04:00
list < Tab > : : iterator i = _tabs . begin ( ) ;
2009-12-01 10:32:42 -05:00
while ( i ! = _tabs . end ( ) ) {
if ( i - > from > c ) {
2011-08-15 10:08:01 -04:00
* next = i ;
2009-12-01 10:32:42 -05:00
break ;
}
if ( i - > to < c ) {
* prev = i ;
+ + i ;
continue ;
}
if ( i - > from < = c & & c < i - > to ) {
under = & ( * i ) ;
}
2009-06-21 18:17:17 -04:00
+ + i ;
}
2009-12-01 10:32:42 -05:00
return under ;
2009-06-21 18:17:17 -04:00
}
2009-06-21 21:01:43 -04:00
2016-05-19 13:11:10 -04:00
void
GroupTabs : : add_new_from_items ( Menu_Helpers : : MenuList & items )
2010-07-19 17:47:07 -04:00
{
using namespace Menu_Helpers ;
2016-05-19 13:11:10 -04:00
Menu * new_from ;
2015-09-21 17:49:40 -04:00
2017-03-15 19:48:36 -04:00
new_from = manage ( new Menu ) ;
2016-05-18 23:44:09 -04:00
{
MenuList & f = new_from - > items ( ) ;
2016-05-19 10:45:55 -04:00
f . push_back ( MenuElem ( _ ( " Selection... " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : new_from_selection ) , false ) ) ) ;
f . push_back ( MenuElem ( _ ( " Record Enabled... " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : new_from_rec_enabled ) , false ) ) ) ;
f . push_back ( MenuElem ( _ ( " Soloed... " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : new_from_soloed ) , false ) ) ) ;
2016-05-18 23:44:09 -04:00
}
2016-05-19 10:45:55 -04:00
items . push_back ( MenuElem ( _ ( " Create New Group From... " ) , * new_from ) ) ;
2011-06-01 13:00:29 -04:00
2017-03-15 19:48:36 -04:00
new_from = manage ( new Menu ) ;
2016-05-18 23:44:09 -04:00
{
MenuList & f = new_from - > items ( ) ;
2016-05-19 10:45:55 -04:00
f . push_back ( MenuElem ( _ ( " Selection... " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : new_from_selection ) , true ) ) ) ;
f . push_back ( MenuElem ( _ ( " Record Enabled... " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : new_from_rec_enabled ) , true ) ) ) ;
f . push_back ( MenuElem ( _ ( " Soloed... " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : new_from_soloed ) , true ) ) ) ;
2016-05-18 23:44:09 -04:00
}
2016-05-19 10:45:55 -04:00
items . push_back ( MenuElem ( _ ( " Create New Group with Master From... " ) , * new_from ) ) ;
2016-05-19 13:11:10 -04:00
}
Gtk : : Menu *
GroupTabs : : get_menu ( RouteGroup * g , bool in_tab_area )
{
using namespace Menu_Helpers ;
delete _menu ;
_menu = new Menu ;
_menu - > set_name ( " ArdourContextMenu " ) ;
2016-05-18 23:44:09 -04:00
2016-05-19 13:11:10 -04:00
MenuList & items = _menu - > items ( ) ;
2016-05-19 10:45:55 -04:00
Menu * vca_menu ;
2016-05-19 13:11:10 -04:00
2016-05-19 10:45:55 -04:00
const VCAList vcas = _session - > vca_manager ( ) . vcas ( ) ;
2016-05-19 13:11:10 -04:00
if ( ! in_tab_area ) {
2024-05-30 12:13:10 -04:00
/* context menu is not for a group tab (e.g. mixer sidebar).
* Show the " create new from " items here .
2016-05-19 13:11:10 -04:00
*/
add_new_from_items ( items ) ;
}
2024-05-30 12:13:10 -04:00
if ( ! in_tab_area & & g ) {
2016-05-19 13:11:10 -04:00
items . push_back ( SeparatorElem ( ) ) ;
2024-05-30 12:13:10 -04:00
}
if ( g ) {
2016-05-19 13:11:10 -04:00
items . push_back ( MenuElem ( _ ( " Edit Group... " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : edit_group ) , g ) ) ) ;
items . push_back ( MenuElem ( _ ( " Collect Group " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : collect ) , g ) ) ) ;
items . push_back ( MenuElem ( _ ( " Remove Group " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : remove_group ) , g ) ) ) ;
items . push_back ( SeparatorElem ( ) ) ;
2017-03-26 09:01:00 -04:00
if ( g - > has_control_master ( ) ) {
items . push_back ( MenuElem ( _ ( " Drop Group from VCA... " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : unassign_group_to_master ) , g - > group_master_number ( ) , g ) ) ) ;
} else {
vca_menu = manage ( new Menu ) ;
MenuList & f ( vca_menu - > items ( ) ) ;
f . push_back ( MenuElem ( " New " , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : assign_group_to_master ) , 0 , g , true ) ) ) ;
2016-05-19 13:11:10 -04:00
2017-03-26 09:01:00 -04:00
for ( VCAList : : const_iterator v = vcas . begin ( ) ; v ! = vcas . end ( ) ; + + v ) {
f . push_back ( MenuElem ( ( * v ) - > name ( ) . empty ( ) ? string_compose ( " VCA %1 " , ( * v ) - > number ( ) ) : ( * v ) - > name ( ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : assign_group_to_master ) , ( * v ) - > number ( ) , g , true ) ) ) ;
}
items . push_back ( MenuElem ( _ ( " Assign Group to VCA... " ) , * vca_menu ) ) ;
2016-05-19 13:11:10 -04:00
}
items . push_back ( SeparatorElem ( ) ) ;
2019-04-10 20:08:36 -04:00
if ( g - > has_subgroup ( ) ) {
2016-05-19 13:11:10 -04:00
items . push_back ( MenuElem ( _ ( " Remove Subgroup Bus " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : un_subgroup ) , g ) ) ) ;
2023-04-20 18:41:30 -04:00
} else {
2016-05-19 13:11:10 -04:00
items . push_back ( MenuElem ( _ ( " Add New Subgroup Bus " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : subgroup ) , g , false , PreFader ) ) ) ;
2023-04-20 18:41:30 -04:00
items . back ( ) . set_sensitive ( g - > can_subgroup ( false , PostFader ) ) ;
2019-04-10 20:08:36 -04:00
items . push_back ( MenuElem ( _ ( " Add New Aux Bus (pre-fader) " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : subgroup ) , g , true , PreFader ) ) ) ;
2023-04-20 18:41:30 -04:00
items . back ( ) . set_sensitive ( g - > can_subgroup ( true , PreFader ) ) ;
2019-04-10 20:08:36 -04:00
items . push_back ( MenuElem ( _ ( " Add New Aux Bus (post-fader) " ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : subgroup ) , g , true , PostFader ) ) ) ;
2023-04-20 18:41:30 -04:00
items . back ( ) . set_sensitive ( g - > can_subgroup ( true , PostFader ) ) ;
2019-04-10 20:08:36 -04:00
}
2023-04-20 18:41:30 -04:00
items . push_back ( SeparatorElem ( ) ) ;
2016-05-19 13:11:10 -04:00
}
add_menu_items ( _menu , g ) ;
if ( in_tab_area ) {
/* context menu is for a group tab, show the "create new
from " items here
*/
add_new_from_items ( items ) ;
}
items . push_back ( SeparatorElem ( ) ) ;
2017-03-15 19:48:36 -04:00
vca_menu = manage ( new Menu ) ;
2016-05-18 23:44:09 -04:00
{
2016-05-19 10:45:55 -04:00
MenuList & f ( vca_menu - > items ( ) ) ;
f . push_back ( MenuElem ( " New " , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : assign_selection_to_master ) , 0 ) ) ) ;
for ( VCAList : : const_iterator v = vcas . begin ( ) ; v ! = vcas . end ( ) ; + + v ) {
2017-01-28 07:03:43 -05:00
f . push_back ( MenuElem ( ( * v ) - > name ( ) . empty ( ) ? string_compose ( " VCA %1 " , ( * v ) - > number ( ) ) : ( * v ) - > name ( ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : assign_selection_to_master ) , ( * v ) - > number ( ) ) ) ) ;
2016-05-19 10:45:55 -04:00
}
2016-05-18 23:44:09 -04:00
}
2017-02-20 09:40:24 -05:00
items . push_back ( MenuElem ( _ ( " Assign Selection to VCA... " ) , * vca_menu ) ) ;
2016-05-19 10:45:55 -04:00
2017-03-15 19:48:36 -04:00
vca_menu = manage ( new Menu ) ;
2016-05-19 10:45:55 -04:00
{
MenuList & f ( vca_menu - > items ( ) ) ;
2017-01-26 03:09:05 -05:00
f . push_back ( MenuElem ( " New " , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : assign_recenabled_to_master ) , 0 ) ) ) ;
2016-05-19 10:45:55 -04:00
for ( VCAList : : const_iterator v = vcas . begin ( ) ; v ! = vcas . end ( ) ; + + v ) {
2017-01-28 07:03:43 -05:00
f . push_back ( MenuElem ( ( * v ) - > name ( ) . empty ( ) ? string_compose ( " VCA %1 " , ( * v ) - > number ( ) ) : ( * v ) - > name ( ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : assign_recenabled_to_master ) , ( * v ) - > number ( ) ) ) ) ;
2016-05-19 10:45:55 -04:00
}
}
2017-02-20 09:40:24 -05:00
items . push_back ( MenuElem ( _ ( " Assign Record Enabled to VCA... " ) , * vca_menu ) ) ;
2016-05-19 10:45:55 -04:00
2017-03-15 19:48:36 -04:00
vca_menu = manage ( new Menu ) ;
2016-05-19 10:45:55 -04:00
{
MenuList & f ( vca_menu - > items ( ) ) ;
2017-01-26 03:09:05 -05:00
f . push_back ( MenuElem ( " New " , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : assign_soloed_to_master ) , 0 ) ) ) ;
2016-05-19 10:45:55 -04:00
for ( VCAList : : const_iterator v = vcas . begin ( ) ; v ! = vcas . end ( ) ; + + v ) {
2017-01-28 07:03:43 -05:00
f . push_back ( MenuElem ( ( * v ) - > name ( ) . empty ( ) ? string_compose ( " VCA %1 " , ( * v ) - > number ( ) ) : ( * v ) - > name ( ) , sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : assign_soloed_to_master ) , ( * v ) - > number ( ) ) ) ) ;
2016-05-19 10:45:55 -04:00
}
}
2017-02-20 09:40:24 -05:00
items . push_back ( MenuElem ( _ ( " Assign Soloed to VCA... " ) , * vca_menu ) ) ;
2016-05-18 23:44:09 -04:00
2010-07-19 17:47:07 -04:00
items . push_back ( SeparatorElem ( ) ) ;
2011-12-06 22:28:19 -05:00
items . push_back ( MenuElem ( _ ( " Enable All Groups " ) , sigc : : mem_fun ( * this , & GroupTabs : : activate_all ) ) ) ;
items . push_back ( MenuElem ( _ ( " Disable All Groups " ) , sigc : : mem_fun ( * this , & GroupTabs : : disable_all ) ) ) ;
2016-05-18 23:44:09 -04:00
2016-05-19 10:45:55 -04:00
return _menu ;
}
2016-05-18 23:44:09 -04:00
2016-05-19 10:45:55 -04:00
void
2016-06-11 15:49:18 -04:00
GroupTabs : : assign_group_to_master ( uint32_t which , RouteGroup * group , bool rename_master ) const
2016-05-19 10:45:55 -04:00
{
if ( ! _session | | ! group ) {
return ;
2016-05-18 23:44:09 -04:00
}
2023-02-16 18:33:28 -05:00
std : : shared_ptr < VCA > master ;
2016-05-18 23:44:09 -04:00
2016-05-19 10:45:55 -04:00
if ( which = = 0 ) {
2018-03-15 15:25:14 -04:00
if ( _session - > vca_manager ( ) . create_vca ( 1 ) . empty ( ) ) {
2016-05-19 10:45:55 -04:00
/* error */
return ;
}
2016-05-18 23:44:09 -04:00
2017-03-18 17:50:35 -04:00
/* Get most recently created VCA... */
which = _session - > vca_manager ( ) . vcas ( ) . back ( ) - > number ( ) ;
2016-05-18 23:44:09 -04:00
}
2016-05-19 10:45:55 -04:00
master = _session - > vca_manager ( ) . vca_by_number ( which ) ;
2016-05-18 23:44:09 -04:00
2016-05-19 10:45:55 -04:00
if ( ! master ) {
/* should never happen; if it does, basically something deeply
odd happened , no reason to tell user because there ' s no
sensible explanation .
*/
return ;
2016-05-18 23:44:09 -04:00
}
2011-06-01 13:00:29 -04:00
2016-05-19 10:45:55 -04:00
group - > assign_master ( master ) ;
2016-05-21 07:49:51 -04:00
if ( rename_master ) {
master - > set_name ( group - > name ( ) ) ;
}
2010-07-19 17:47:07 -04:00
}
2017-03-26 09:01:00 -04:00
void
GroupTabs : : unassign_group_to_master ( uint32_t which , RouteGroup * group ) const
{
if ( ! _session | | ! group ) {
return ;
}
2023-02-16 18:33:28 -05:00
std : : shared_ptr < VCA > master = _session - > vca_manager ( ) . vca_by_number ( which ) ;
2017-03-26 09:01:00 -04:00
if ( ! master ) {
/* should never happen; if it does, basically something deeply
odd happened , no reason to tell user because there ' s no
sensible explanation .
*/
return ;
}
group - > unassign_master ( master ) ;
}
2010-07-19 17:47:07 -04:00
void
2017-08-07 10:46:35 -04:00
GroupTabs : : assign_some_to_master ( uint32_t which , RouteList rl , std : : string vcaname )
2016-05-18 23:44:09 -04:00
{
2016-05-19 10:45:55 -04:00
if ( ! _session ) {
return ;
}
2016-05-18 23:44:09 -04:00
2023-02-16 18:33:28 -05:00
std : : shared_ptr < VCA > master ;
2017-08-07 10:46:35 -04:00
bool set_name = false ;
2016-05-19 10:45:55 -04:00
if ( which = = 0 ) {
2018-03-15 15:25:14 -04:00
if ( _session - > vca_manager ( ) . create_vca ( 1 ) . empty ( ) ) {
2016-05-19 10:45:55 -04:00
/* error */
return ;
}
2017-08-07 10:46:35 -04:00
set_name = true ;
2016-05-19 10:45:55 -04:00
2017-03-18 17:50:35 -04:00
/* Get most recently created VCA... */
which = _session - > vca_manager ( ) . vcas ( ) . back ( ) - > number ( ) ;
2016-05-19 10:45:55 -04:00
}
master = _session - > vca_manager ( ) . vca_by_number ( which ) ;
if ( ! master ) {
/* should never happen; if it does, basically something deeply
odd happened , no reason to tell user because there ' s no
sensible explanation .
*/
return ;
}
2016-05-18 23:44:09 -04:00
2010-07-19 17:47:07 -04:00
if ( rl . empty ( ) ) {
return ;
}
2016-05-19 10:45:55 -04:00
for ( RouteList : : iterator r = rl . begin ( ) ; r ! = rl . end ( ) ; + + r ) {
2017-06-22 15:06:24 -04:00
( * r ) - > assign ( master ) ;
2016-05-19 10:45:55 -04:00
}
2017-08-07 10:46:35 -04:00
if ( set_name & & ! vcaname . empty ( ) ) {
master - > set_name ( vcaname ) ;
}
2010-07-19 17:47:07 -04:00
}
2016-05-19 10:45:55 -04:00
RouteList
GroupTabs : : get_rec_enabled ( )
2010-07-19 17:47:07 -04:00
{
RouteList rec_enabled ;
2016-05-19 10:45:55 -04:00
if ( ! _session ) {
return rec_enabled ;
}
2023-04-07 17:33:13 -04:00
std : : shared_ptr < RouteList const > rl = _session - > get_routes ( ) ;
2016-05-19 10:45:55 -04:00
2023-04-07 17:33:13 -04:00
for ( auto const & i : * rl ) {
std : : shared_ptr < Track > trk ( std : : dynamic_pointer_cast < Track > ( i ) ) ;
2016-04-08 16:49:47 -04:00
if ( trk & & trk - > rec_enable_control ( ) - > get_value ( ) ) {
2023-04-07 17:33:13 -04:00
rec_enabled . push_back ( i ) ;
2010-07-19 17:47:07 -04:00
}
}
2016-05-19 10:45:55 -04:00
return rec_enabled ;
2010-07-19 17:47:07 -04:00
}
2016-05-19 10:45:55 -04:00
RouteList
GroupTabs : : get_soloed ( )
2010-07-19 17:47:07 -04:00
{
2018-11-07 16:05:05 -05:00
RouteList rl = _session - > get_routelist ( ) ;
2010-07-19 17:47:07 -04:00
RouteList soloed ;
2018-11-07 16:05:05 -05:00
for ( RouteList : : iterator i = rl . begin ( ) ; i ! = rl . end ( ) ; + + i ) {
2010-07-19 17:47:07 -04:00
if ( ! ( * i ) - > is_master ( ) & & ( * i ) - > soloed ( ) ) {
soloed . push_back ( * i ) ;
}
}
2016-05-19 10:45:55 -04:00
return soloed ;
}
void
GroupTabs : : assign_selection_to_master ( uint32_t which )
{
2017-08-07 10:46:35 -04:00
assign_some_to_master ( which , selected_routes ( ) , _ ( " Selection " ) ) ;
2016-05-19 10:45:55 -04:00
}
void
GroupTabs : : assign_recenabled_to_master ( uint32_t which )
{
assign_some_to_master ( which , get_rec_enabled ( ) ) ;
}
2010-07-19 17:47:07 -04:00
2016-05-19 10:45:55 -04:00
void
GroupTabs : : assign_soloed_to_master ( uint32_t which )
{
assign_some_to_master ( which , get_soloed ( ) ) ;
}
void
GroupTabs : : new_from_selection ( bool with_master )
{
2016-06-11 15:49:18 -04:00
RouteList rl ( selected_routes ( ) ) ;
run_new_group_dialog ( & rl , with_master ) ;
2016-05-19 10:45:55 -04:00
}
void
GroupTabs : : new_from_rec_enabled ( bool with_master )
{
2016-06-11 15:49:18 -04:00
RouteList rl ( get_rec_enabled ( ) ) ;
run_new_group_dialog ( & rl , with_master ) ;
2016-05-19 10:45:55 -04:00
}
void
GroupTabs : : new_from_soloed ( bool with_master )
{
2016-06-11 15:49:18 -04:00
RouteList rl ( get_soloed ( ) ) ;
run_new_group_dialog ( & rl , with_master ) ;
2010-07-19 17:47:07 -04:00
}
void
2016-06-11 15:49:18 -04:00
GroupTabs : : run_new_group_dialog ( RouteList const * rl , bool with_master )
2010-07-19 17:47:07 -04:00
{
2016-06-11 15:49:18 -04:00
if ( rl & & rl - > empty ( ) ) {
2016-05-19 10:45:55 -04:00
return ;
}
2010-07-19 17:47:07 -04:00
RouteGroup * g = new RouteGroup ( * _session , " " ) ;
2016-06-11 15:49:18 -04:00
RouteGroupDialog * d = new RouteGroupDialog ( g , true ) ;
2016-05-21 07:49:51 -04:00
2016-06-11 15:49:18 -04:00
d - > signal_response ( ) . connect ( sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : new_group_dialog_finished ) , d , rl ? new RouteList ( * rl ) : 0 , with_master ) ) ;
d - > present ( ) ;
2010-07-19 17:47:07 -04:00
}
2016-06-11 15:49:18 -04:00
void
GroupTabs : : new_group_dialog_finished ( int r , RouteGroupDialog * d , RouteList const * rl , bool with_master ) const
2016-05-18 23:44:09 -04:00
{
2016-06-11 15:49:18 -04:00
if ( r = = RESPONSE_OK ) {
2016-05-18 23:44:09 -04:00
2016-06-12 14:33:18 -04:00
if ( ! d - > name_check ( ) ) {
return ;
}
2016-06-11 15:49:18 -04:00
_session - > add_route_group ( d - > group ( ) ) ;
2016-05-18 23:44:09 -04:00
2016-06-11 15:49:18 -04:00
if ( rl ) {
for ( RouteList : : const_iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
d - > group ( ) - > add ( * i ) ;
}
2010-07-19 17:47:07 -04:00
2016-06-11 15:49:18 -04:00
if ( with_master ) {
assign_group_to_master ( 0 , d - > group ( ) , true ) ; /* zero => new master */
}
}
} else {
delete d - > group ( ) ;
2010-07-19 17:47:07 -04:00
}
2011-06-01 13:00:29 -04:00
2016-06-11 15:49:18 -04:00
delete rl ;
delete_when_idle ( d ) ;
2010-07-19 17:47:07 -04:00
}
void
GroupTabs : : edit_group ( RouteGroup * g )
{
2016-06-11 15:49:18 -04:00
RouteGroupDialog * d = new RouteGroupDialog ( g , false ) ;
d - > signal_response ( ) . connect ( sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : edit_group_dialog_finished ) , d ) ) ;
d - > present ( ) ;
}
void
GroupTabs : : edit_group_dialog_finished ( int r , RouteGroupDialog * d ) const
{
delete_when_idle ( d ) ;
2010-07-19 17:47:07 -04:00
}
void
2011-01-03 21:36:29 -05:00
GroupTabs : : subgroup ( RouteGroup * g , bool aux , Placement placement )
2010-07-19 17:47:07 -04:00
{
2011-01-03 21:36:29 -05:00
g - > make_subgroup ( aux , placement ) ;
2010-07-19 17:47:07 -04:00
}
2013-01-03 14:34:07 -05:00
void
GroupTabs : : un_subgroup ( RouteGroup * g )
{
g - > destroy_subgroup ( ) ;
}
2010-07-19 17:47:07 -04:00
/** Collect all members of a RouteGroup so that they are together in the Editor or Mixer.
* @ param g Group to collect .
*/
void
GroupTabs : : collect ( RouteGroup * g )
{
2023-02-16 18:33:28 -05:00
std : : shared_ptr < RouteList > group_routes = g - > route_list ( ) ;
2017-06-16 17:45:16 -04:00
group_routes - > sort ( Stripable : : Sorter ( ) ) ;
2010-07-19 17:47:07 -04:00
int const N = group_routes - > size ( ) ;
RouteList : : iterator i = group_routes - > begin ( ) ;
2018-11-07 16:05:05 -05:00
RouteList routes = _session - > get_routelist ( ) ;
routes . sort ( Stripable : : Sorter ( ) ) ;
RouteList : : const_iterator j = routes . begin ( ) ;
2010-07-19 17:47:07 -04:00
int diff = 0 ;
int coll = - 1 ;
2017-01-26 13:20:40 -05:00
PresentationInfo : : ChangeSuspender cs ;
2018-11-07 16:05:05 -05:00
while ( i ! = group_routes - > end ( ) & & j ! = routes . end ( ) ) {
2010-07-19 17:47:07 -04:00
2016-06-03 15:15:30 -04:00
PresentationInfo : : order_t const k = ( * j ) - > presentation_info ( ) . order ( ) ;
2010-07-19 17:47:07 -04:00
if ( * i = = * j ) {
if ( coll = = - 1 ) {
coll = k ;
diff = N - 1 ;
} else {
- - diff ;
}
2017-01-26 13:20:40 -05:00
( * j ) - > set_presentation_order ( coll ) ;
2010-07-19 17:47:07 -04:00
+ + coll ;
+ + i ;
} else {
2011-06-01 13:00:29 -04:00
2017-01-26 13:20:40 -05:00
( * j ) - > set_presentation_order ( k + diff ) ;
2011-06-01 13:00:29 -04:00
2010-07-19 17:47:07 -04:00
}
+ + j ;
}
}
void
GroupTabs : : activate_all ( )
{
_session - > foreach_route_group (
sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : set_activation ) , true )
) ;
}
void
GroupTabs : : disable_all ( )
{
_session - > foreach_route_group (
sigc : : bind ( sigc : : mem_fun ( * this , & GroupTabs : : set_activation ) , false )
) ;
}
void
GroupTabs : : set_activation ( RouteGroup * g , bool a )
{
g - > set_active ( a , this ) ;
}
2011-06-01 13:00:29 -04:00
2010-07-19 19:26:40 -04:00
void
GroupTabs : : remove_group ( RouteGroup * g )
{
2023-02-16 18:33:28 -05:00
std : : shared_ptr < RouteList > rl ( g - > route_list ( ) ) ;
2010-07-19 19:26:40 -04:00
_session - > remove_route_group ( * g ) ;
2016-12-01 09:51:00 -05:00
2017-05-14 05:59:15 -04:00
emit_gui_changed_for_members ( rl ) ;
2010-07-19 19:26:40 -04:00
}
2011-08-30 05:48:53 -04:00
/** Set the color of the tab of a route group */
void
2014-06-09 23:28:32 -04:00
GroupTabs : : set_group_color ( RouteGroup * group , uint32_t color )
2011-08-30 05:48:53 -04:00
{
assert ( group ) ;
2017-05-03 11:46:58 -04:00
PresentationInfo : : ChangeSuspender cs ;
group - > set_rgba ( color ) ;
2011-08-30 05:48:53 -04:00
}
/** @return the ID string to use for the GUI state of a route group */
string
GroupTabs : : group_gui_id ( RouteGroup * group )
{
assert ( group ) ;
char buf [ 64 ] ;
snprintf ( buf , sizeof ( buf ) , " route_group %s " , group - > id ( ) . to_s ( ) . c_str ( ) ) ;
return buf ;
}
/** @return the color to use for a route group tab */
2014-06-09 23:28:32 -04:00
uint32_t
2011-08-30 05:48:53 -04:00
GroupTabs : : group_color ( RouteGroup * group )
{
assert ( group ) ;
2015-10-05 10:17:49 -04:00
2017-05-03 11:46:58 -04:00
/* prefer libardour color, if set */
uint32_t rgba = group - > rgba ( ) ;
if ( rgba ! = 0 ) {
return rgba ;
}
/* backwards compatibility, load old color */
2011-08-30 05:48:53 -04:00
GUIObjectState & gui_state = * ARDOUR_UI : : instance ( ) - > gui_object_state ;
string const gui_id = group_gui_id ( group ) ;
bool empty ;
string const color = gui_state . get_string ( gui_id , " color " , & empty ) ;
2014-06-09 23:28:32 -04:00
2011-08-30 05:48:53 -04:00
if ( empty ) {
/* no color has yet been set, so use a random one */
2022-08-10 23:11:52 -04:00
uint32_t c = Gtkmm2ext : : gdk_color_to_rgba ( unique_random_color ( _used_colors ) ) ;
2014-06-09 23:28:32 -04:00
set_group_color ( group , c ) ;
return c ;
2011-08-30 05:48:53 -04:00
}
int r , g , b ;
2017-05-03 11:46:58 -04:00
/* for historical reasons, colors are stored as 16 bit values. */
2014-06-09 23:28:32 -04:00
2011-08-30 05:48:53 -04:00
sscanf ( color . c_str ( ) , " %d:%d:%d " , & r , & g , & b ) ;
2014-06-09 23:28:32 -04:00
r / = 256 ;
g / = 256 ;
b / = 256 ;
2017-05-03 11:46:58 -04:00
group - > migrate_rgba ( RGBA_TO_UINT ( r , g , b , 255 ) ) ;
gui_state . remove_node ( gui_id ) ;
2014-06-09 23:28:32 -04:00
return RGBA_TO_UINT ( r , g , b , 255 ) ;
2011-08-30 05:48:53 -04:00
}
2011-09-07 07:56:23 -04:00
void
GroupTabs : : route_group_property_changed ( RouteGroup * rg )
{
/* This is a bit of a hack, but this might change
our route ' s effective color , so emit gui_changed
for our routes .
*/
2017-05-14 05:59:15 -04:00
emit_gui_changed_for_members ( rg - > route_list ( ) ) ;
2015-10-05 10:17:49 -04:00
2011-09-07 07:56:23 -04:00
set_dirty ( ) ;
}
void
2023-02-16 18:33:28 -05:00
GroupTabs : : route_added_to_route_group ( RouteGroup * , std : : weak_ptr < Route > w )
2011-09-07 07:56:23 -04:00
{
/* Similarly-spirited hack as in route_group_property_changed */
2011-11-03 13:18:51 -04:00
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Route > r = w . lock ( ) ;
2011-09-07 07:56:23 -04:00
if ( ! r ) {
return ;
}
2016-06-05 17:16:20 -04:00
r - > presentation_info ( ) . PropertyChanged ( Properties : : color ) ;
2011-11-03 13:18:51 -04:00
set_dirty ( ) ;
2011-09-07 07:56:23 -04:00
}
void
2023-02-16 18:33:28 -05:00
GroupTabs : : route_removed_from_route_group ( RouteGroup * , std : : weak_ptr < Route > w )
2011-09-07 07:56:23 -04:00
{
/* Similarly-spirited hack as in route_group_property_changed */
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Route > r = w . lock ( ) ;
2011-09-07 07:56:23 -04:00
if ( ! r ) {
return ;
}
2016-06-05 17:16:20 -04:00
r - > presentation_info ( ) . PropertyChanged ( Properties : : color ) ;
2011-11-03 13:18:51 -04:00
set_dirty ( ) ;
2011-09-07 07:56:23 -04:00
}
void
2023-02-16 18:33:28 -05:00
GroupTabs : : emit_gui_changed_for_members ( std : : shared_ptr < RouteList > rl )
2011-09-07 07:56:23 -04:00
{
2017-01-26 13:20:40 -05:00
PresentationInfo : : ChangeSuspender cs ;
2017-05-14 05:59:15 -04:00
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
2016-12-01 09:51:00 -05:00
( * i ) - > presentation_info ( ) . PropertyChanged ( Properties : : color ) ;
2011-09-07 07:56:23 -04:00
}
}