2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <gtkmm/stock.h>
21 #include "ardour/session.h"
22 #include "ardour/route_group.h"
23 #include "ardour/route.h"
25 #include "gui_thread.h"
26 #include "route_group_dialog.h"
27 #include "group_tabs.h"
33 using namespace ARDOUR
;
34 using Gtkmm2ext::Keyboard
;
36 GroupTabs::GroupTabs ()
39 , _dragging_new_tab (0)
44 GroupTabs::~GroupTabs ()
50 GroupTabs::set_session (Session
* s
)
52 SessionHandlePtr::set_session (s
);
55 _session
->RouteGroupChanged
.connect (_session_connections
, invalidator (*this), boost::bind (&GroupTabs::set_dirty
, this), gui_context());
56 _session
->route_group_removed
.connect (_session_connections
, invalidator (*this), boost::bind (&GroupTabs::set_dirty
, this), gui_context());
61 /** Handle a size request.
62 * @param req GTK requisition
65 GroupTabs::on_size_request (Gtk::Requisition
*req
)
67 /* Use a dummy, small width and the actual height that we want */
73 GroupTabs::on_button_press_event (GdkEventButton
* ev
)
75 using namespace Menu_Helpers
;
77 double const p
= primary_coordinate (ev
->x
, ev
->y
);
79 list
<Tab
>::iterator prev
;
80 list
<Tab
>::iterator next
;
81 Tab
* t
= click_to_tab (p
, &prev
, &next
);
83 _drag_min
= prev
!= _tabs
.end() ? prev
->to
: 0;
84 _drag_max
= next
!= _tabs
.end() ? next
->from
: extent ();
86 if (ev
->button
== 1) {
91 _dragging_new_tab
= true;
93 if (next
== _tabs
.end()) {
97 list
<Tab
>::iterator j
= _tabs
.insert (next
, n
);
102 _dragging_new_tab
= false;
109 double const h
= (t
->from
+ t
->to
) / 2;
111 _drag_moving
= t
->from
;
113 _drag_offset
= p
- t
->from
;
115 _drag_moving
= t
->to
;
116 _drag_fixed
= t
->from
;
117 _drag_offset
= p
- t
->to
;
120 } else if (ev
->button
== 3) {
122 RouteGroup
* g
= t
? t
->group
: 0;
123 Menu
* m
= get_menu (g
);
125 m
->popup (ev
->button
, ev
->time
);
135 GroupTabs::on_motion_notify_event (GdkEventMotion
* ev
)
137 if (_dragging
== 0) {
141 double const p
= primary_coordinate (ev
->x
, ev
->y
);
143 if (p
!= _drag_first
) {
147 _drag_moving
= p
- _drag_offset
;
149 _dragging
->from
= min (_drag_moving
, _drag_fixed
);
150 _dragging
->to
= max (_drag_moving
, _drag_fixed
);
152 _dragging
->from
= max (_dragging
->from
, _drag_min
);
153 _dragging
->to
= min (_dragging
->to
, _drag_max
);
163 GroupTabs::on_button_release_event (GdkEventButton
* ev
)
165 if (_dragging
== 0) {
171 if (_dragging
->group
) {
173 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
176 RouteGroupDialog
d (_dragging
->group
, false);
181 /* toggle active state */
182 _dragging
->group
->set_active (!_dragging
->group
->is_active (), this);
189 RouteList routes
= routes_for_tab (_dragging
);
191 if (!routes
.empty()) {
192 if (_dragging_new_tab
) {
193 RouteGroup
* g
= create_and_add_group ();
195 for (RouteList::iterator i
= routes
.begin(); i
!= routes
.end(); ++i
) {
200 boost::shared_ptr
<RouteList
> r
= _session
->get_routes ();
201 for (RouteList::iterator i
= r
->begin(); i
!= r
->end(); ++i
) {
203 if (find (routes
.begin(), routes
.end(), *i
) == routes
.end()) {
204 /* this route is not on the list of those that should be in _dragging's group */
205 if ((*i
)->route_group() == _dragging
->group
) {
206 _dragging
->group
->remove (*i
);
209 _dragging
->group
->add (*i
);
225 GroupTabs::render (cairo_t
* cr
)
227 if (_dragging
== 0) {
228 _tabs
= compute_tabs ();
233 cairo_set_source_rgb (cr
, 0, 0, 0);
234 cairo_rectangle (cr
, 0, 0, _width
, _height
);
239 for (list
<Tab
>::const_iterator i
= _tabs
.begin(); i
!= _tabs
.end(); ++i
) {
245 /** Convert a click position to a tab.
246 * @param c Click position.
247 * @param prev Filled in with the previous tab to the click, or 0.
248 * @param next Filled in with the next tab after the click, or 0.
249 * @return Tab under the click, or 0.
253 GroupTabs::click_to_tab (double c
, list
<Tab
>::iterator
* prev
, list
<Tab
>::iterator
* next
)
255 *prev
= *next
= _tabs
.end ();
258 list
<Tab
>::iterator i
= _tabs
.begin ();
259 while (i
!= _tabs
.end()) {
271 if (i
->from
<= c
&& c
< i
->to
) {
278 if (i
!= _tabs
.end()) {
290 GroupTabs::get_menu (RouteGroup
* g
)
292 using namespace Menu_Helpers
;
296 Menu
* new_from
= new Menu
;
297 MenuList
& f
= new_from
->items ();
298 f
.push_back (MenuElem (_("Selection..."), sigc::mem_fun (*this, &GroupTabs::new_from_selection
)));
299 f
.push_back (MenuElem (_("Record Enabled..."), sigc::mem_fun (*this, &GroupTabs::new_from_rec_enabled
)));
300 f
.push_back (MenuElem (_("Soloed..."), sigc::mem_fun (*this, &GroupTabs::new_from_soloed
)));
303 _menu
->set_name ("ArdourContextMenu");
304 MenuList
& items
= _menu
->items();
306 items
.push_back (MenuElem (_("New..."), hide_return (sigc::mem_fun(*this, &GroupTabs::create_and_add_group
))));
307 items
.push_back (MenuElem (_("New From"), *new_from
));
310 items
.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &GroupTabs::edit_group
), g
)));
311 items
.push_back (MenuElem (_("Add New Subgroup Bus"), sigc::bind (sigc::mem_fun (*this, &GroupTabs::subgroup
), g
, false, PreFader
)));
312 items
.push_back (MenuElem (_("Add New Aux Bus (pre-fader)"), sigc::bind (sigc::mem_fun (*this, &GroupTabs::subgroup
), g
, true, PreFader
)));
313 items
.push_back (MenuElem (_("Add New Aux Bus (post-fader)"), sigc::bind (sigc::mem_fun (*this, &GroupTabs::subgroup
), g
, true, PostFader
)));
314 items
.push_back (MenuElem (_("Collect"), sigc::bind (sigc::mem_fun (*this, &GroupTabs::collect
), g
)));
315 items
.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun (*this, &GroupTabs::remove_group
), g
)));
318 add_menu_items (_menu
, g
);
320 items
.push_back (SeparatorElem());
321 items
.push_back (MenuElem (_("Activate All"), sigc::mem_fun(*this, &GroupTabs::activate_all
)));
322 items
.push_back (MenuElem (_("Disable All"), sigc::mem_fun(*this, &GroupTabs::disable_all
)));
329 GroupTabs::new_from_selection ()
331 RouteList rl
= selected_routes ();
336 run_new_group_dialog (rl
);
340 GroupTabs::new_from_rec_enabled ()
342 boost::shared_ptr
<RouteList
> rl
= _session
->get_routes ();
344 RouteList rec_enabled
;
346 for (RouteList::iterator i
= rl
->begin(); i
!= rl
->end(); ++i
) {
347 if ((*i
)->record_enabled()) {
348 rec_enabled
.push_back (*i
);
352 if (rec_enabled
.empty()) {
356 run_new_group_dialog (rec_enabled
);
360 GroupTabs::new_from_soloed ()
362 boost::shared_ptr
<RouteList
> rl
= _session
->get_routes ();
366 for (RouteList::iterator i
= rl
->begin(); i
!= rl
->end(); ++i
) {
367 if (!(*i
)->is_master() && (*i
)->soloed()) {
368 soloed
.push_back (*i
);
372 if (soloed
.empty()) {
376 run_new_group_dialog (soloed
);
380 GroupTabs::run_new_group_dialog (RouteList
const & rl
)
382 RouteGroup
* g
= new RouteGroup (*_session
, "");
383 g
->apply_changes (default_properties ());
385 RouteGroupDialog
d (g
, true);
390 _session
->add_route_group (g
);
391 for (RouteList::const_iterator i
= rl
.begin(); i
!= rl
.end(); ++i
) {
398 GroupTabs::create_and_add_group () const
400 RouteGroup
* g
= new RouteGroup (*_session
, "");
402 g
->apply_changes (default_properties ());
404 RouteGroupDialog
d (g
, true);
411 _session
->add_route_group (g
);
416 GroupTabs::edit_group (RouteGroup
* g
)
418 RouteGroupDialog
d (g
, false);
423 GroupTabs::subgroup (RouteGroup
* g
, bool aux
, Placement placement
)
425 g
->make_subgroup (aux
, placement
);
428 struct CollectSorter
{
429 CollectSorter (std::string
const & key
) : _key (key
) {}
431 bool operator () (boost::shared_ptr
<Route
> a
, boost::shared_ptr
<Route
> b
) {
432 return a
->order_key (_key
) < b
->order_key (_key
);
438 /** Collect all members of a RouteGroup so that they are together in the Editor or Mixer.
439 * @param g Group to collect.
442 GroupTabs::collect (RouteGroup
* g
)
444 boost::shared_ptr
<RouteList
> group_routes
= g
->route_list ();
445 group_routes
->sort (CollectSorter (order_key ()));
446 int const N
= group_routes
->size ();
448 RouteList::iterator i
= group_routes
->begin ();
449 boost::shared_ptr
<RouteList
> routes
= _session
->get_routes ();
450 RouteList::const_iterator j
= routes
->begin ();
454 while (i
!= group_routes
->end() && j
!= routes
->end()) {
456 int const k
= (*j
)->order_key (order_key ());
467 (*j
)->set_order_key (order_key (), coll
);
474 (*j
)->set_order_key (order_key (), k
+ diff
);
485 GroupTabs::activate_all ()
487 _session
->foreach_route_group (
488 sigc::bind (sigc::mem_fun (*this, &GroupTabs::set_activation
), true)
493 GroupTabs::disable_all ()
495 _session
->foreach_route_group (
496 sigc::bind (sigc::mem_fun (*this, &GroupTabs::set_activation
), false)
501 GroupTabs::set_activation (RouteGroup
* g
, bool a
)
503 g
->set_active (a
, this);
507 GroupTabs::remove_group (RouteGroup
* g
)
509 _session
->remove_route_group (*g
);