Center port matrix if IO selector window is too large.
[ardour.git] / gtk2_ardour / group_tabs.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <gtkmm/stock.h>
21 #include "ardour/session.h"
22 #include "ardour/route_group.h"
23 #include "ardour/route.h"
24
25 #include "gui_thread.h"
26 #include "route_group_dialog.h"
27 #include "group_tabs.h"
28 #include "keyboard.h"
29 #include "i18n.h"
30
31 using namespace std;
32 using namespace Gtk;
33 using namespace ARDOUR;
34 using Gtkmm2ext::Keyboard;
35
36 GroupTabs::GroupTabs (Editor* e)
37         : EditorComponent (e),
38           _dragging (0),
39           _dragging_new_tab (0)
40 {
41
42 }
43
44 void
45 GroupTabs::set_session (Session* s)
46 {
47         EditorComponent::set_session (s);
48
49         if (_session) {
50                 _session->RouteGroupChanged.connect (_session_connections, boost::bind (&GroupTabs::set_dirty, this), gui_context());
51         }
52 }
53
54
55 /** Handle a size request.
56  *  @param req GTK requisition
57  */
58 void
59 GroupTabs::on_size_request (Gtk::Requisition *req)
60 {
61         /* Use a dummy, small width and the actual height that we want */
62         req->width = 16;
63         req->height = 16;
64 }
65
66 bool
67 GroupTabs::on_button_press_event (GdkEventButton* ev)
68 {
69         using namespace Menu_Helpers;
70
71         double const p = primary_coordinate (ev->x, ev->y);
72
73         list<Tab>::iterator prev;
74         list<Tab>::iterator next;
75         Tab* t = click_to_tab (p, &prev, &next);
76
77         _drag_min = prev != _tabs.end() ? prev->to : 0;
78         _drag_max = next != _tabs.end() ? next->from : extent ();
79
80         if (ev->button == 1) {
81
82                 if (t == 0) {
83                         Tab n;
84                         n.from = n.to = p;
85                         _dragging_new_tab = true;
86
87                         if (next == _tabs.end()) {
88                                 _tabs.push_back (n);
89                                 t = &_tabs.back ();
90                         } else {
91                                 list<Tab>::iterator j = _tabs.insert (next, n);
92                                 t = &(*j);
93                         }
94                         
95                 } else {
96                         _dragging_new_tab = false;
97                 }
98
99                 _dragging = t;
100                 _drag_moved = false;
101                 _drag_first = p;
102
103                 double const h = (t->from + t->to) / 2;
104                 if (p < h) {
105                         _drag_moving = t->from;
106                         _drag_fixed = t->to;
107                         _drag_offset = p - t->from;
108                 } else {
109                         _drag_moving = t->to;
110                         _drag_fixed = t->from;
111                         _drag_offset = p - t->to;
112                 }
113
114         } else if (ev->button == 3) {
115
116                 RouteGroup* g = t ? t->group : 0;
117                 Menu* m = get_menu (g);
118                 if (m) {
119                         m->popup (ev->button, ev->time);
120                 }
121
122         }
123
124         return true;
125 }
126
127
128 bool
129 GroupTabs::on_motion_notify_event (GdkEventMotion* ev)
130 {
131         if (_dragging == 0) {
132                 return false;
133         }
134
135         double const p = primary_coordinate (ev->x, ev->y);
136
137         if (p != _drag_first) {
138                 _drag_moved = true;
139         }
140
141         _drag_moving = p - _drag_offset;
142
143         _dragging->from = min (_drag_moving, _drag_fixed);
144         _dragging->to = max (_drag_moving, _drag_fixed);
145
146         _dragging->from = max (_dragging->from, _drag_min);
147         _dragging->to = min (_dragging->to, _drag_max);
148
149         set_dirty ();
150         queue_draw ();
151
152         return true;
153 }
154
155
156 bool
157 GroupTabs::on_button_release_event (GdkEventButton* ev)
158 {
159         if (_dragging == 0) {
160                 return false;
161         }
162
163         if (!_drag_moved) {
164
165                 if (_dragging->group) {
166                         
167                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
168                                 
169                                 /* edit */
170                                 RouteGroupDialog d (_dragging->group, Gtk::Stock::APPLY);
171                                 d.do_run ();
172                                 
173                         } else {
174                                 
175                                 /* toggle active state */
176                                 _dragging->group->set_active (!_dragging->group->is_active (), this);
177                                 
178                         }
179                 }
180
181         } else {
182                 /* finish drag */
183                 RouteList routes = routes_for_tab (_dragging);
184
185                 if (!routes.empty()) {
186                         if (_dragging_new_tab) {
187                                 RouteGroup* g = new_route_group ();
188                                 if (g) {
189                                         for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
190                                                 g->add (*i);
191                                         }
192                                 }
193                         } else {
194                                 boost::shared_ptr<RouteList> r = _session->get_routes ();
195                                 for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
196
197                                         if (find (routes.begin(), routes.end(), *i) == routes.end()) {
198                                                 /* this route is not on the list of those that should be in _dragging's group */
199                                                 if ((*i)->route_group() == _dragging->group) {
200                                                         _dragging->group->remove (*i);
201                                                 }
202                                         } else {
203                                                 _dragging->group->add (*i);
204                                         }
205                                 }
206                         }
207                 }
208
209                 set_dirty ();
210                 queue_draw ();
211         }
212
213         _dragging = 0;
214
215         return true;
216 }
217
218 void
219 GroupTabs::render (cairo_t* cr)
220 {
221         if (_dragging == 0) {
222                 _tabs = compute_tabs ();
223         }
224
225         /* background */
226
227         cairo_set_source_rgb (cr, 0, 0, 0);
228         cairo_rectangle (cr, 0, 0, _width, _height);
229         cairo_fill (cr);
230
231         /* tabs */
232
233         for (list<Tab>::const_iterator i = _tabs.begin(); i != _tabs.end(); ++i) {
234                 draw_tab (cr, *i);
235         }
236 }
237
238
239 /** Convert a click position to a tab.
240  *  @param c Click position.
241  *  @param prev Filled in with the previous tab to the click, or 0.
242  *  @param next Filled in with the next tab after the click, or 0.
243  *  @return Tab under the click, or 0.
244  */
245
246 GroupTabs::Tab *
247 GroupTabs::click_to_tab (double c, list<Tab>::iterator* prev, list<Tab>::iterator* next)
248 {
249         *prev = *next = _tabs.end ();
250         Tab* under = 0;
251
252         list<Tab>::iterator i = _tabs.begin ();
253         while (i != _tabs.end()) {
254
255                 if (i->from > c) {
256                         break;
257                 }
258
259                 if (i->to < c) {
260                         *prev = i;
261                         ++i;
262                         continue;
263                 }
264
265                 if (i->from <= c && c < i->to) {
266                         under = &(*i);
267                 }
268
269                 ++i;
270         }
271
272         if (i != _tabs.end()) {
273                 *next = i;
274
275                 if (under) {
276                         *next++;
277                 }
278         }
279
280         return under;
281 }
282