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