60b32520fec88d05571384cc784e188b0a8e337f
[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 "route_group_dialog.h"
24 #include "group_tabs.h"
25 #include "keyboard.h"
26 #include "i18n.h"
27
28 using namespace std;
29 using namespace Gtk;
30 using namespace ARDOUR;
31
32 GroupTabs::GroupTabs (Editor* e)
33         : EditorComponent (e),
34           _dragging (0)
35 {
36
37 }
38
39 void
40 GroupTabs::connect_to_session (Session* s)
41 {
42         EditorComponent::connect_to_session (s);
43
44         _session_connections.push_back (_session->RouteGroupChanged.connect (mem_fun (*this, &GroupTabs::set_dirty)));
45 }
46
47
48 /** Handle a size request.
49  *  @param req GTK requisition
50  */
51 void
52 GroupTabs::on_size_request (Gtk::Requisition *req)
53 {
54         /* Use a dummy, small width and the actual height that we want */
55         req->width = 16;
56         req->height = 16;
57 }
58
59 bool
60 GroupTabs::on_button_press_event (GdkEventButton* ev)
61 {
62         using namespace Menu_Helpers;
63
64         double const p = primary_coordinate (ev->x, ev->y);
65
66         Tab* prev;
67         Tab* next;
68         Tab* t = click_to_tab (p, &prev, &next);
69
70         if (ev->button == 1 && t) {
71
72                 _dragging = t;
73                 _drag_moved = false;
74                 _drag_last = p;
75
76                 double const h = (t->from + t->to) / 2;
77                 _drag_from = p < h;
78
79                 if (_drag_from) {
80                         /* limit is the end of the previous tab */
81                         _drag_limit = prev ? prev->to : 0;
82                 } else {
83                         /* limit is the start of the next tab */
84                         _drag_limit = next ? next->from : extent ();
85                 }
86
87         } else if (ev->button == 3) {
88
89                 RouteGroup* g = t ? t->group : 0;
90                 Menu* m = get_menu (g);
91                 if (m) {
92                         m->popup (ev->button, ev->time);
93                 }
94
95         }
96
97         return true;
98 }
99
100
101 bool
102 GroupTabs::on_motion_notify_event (GdkEventMotion* ev)
103 {
104         if (_dragging == 0) {
105                 return false;
106         }
107
108         double const p = primary_coordinate (ev->x, ev->y);
109
110         if (p != _drag_last) {
111                 _drag_moved = true;
112         }
113
114         if (_drag_from) {
115
116                 double f = _dragging->from + p - _drag_last;
117
118                 if (f < _drag_limit) {
119                         /* limit drag in the `too big' direction */
120                         f = _drag_limit;
121                 }
122
123                 double const t = _dragging->to - _dragging->last_ui_size;
124                 if (f > t) {
125                         /* limit drag in the `too small' direction */
126                         f = t;
127                 }
128
129                 _dragging->from = f;
130
131         } else {
132
133                 double t = _dragging->to + p - _drag_last;
134
135                 if (t > _drag_limit) {
136                         /* limit drag in the `too big' direction */
137                         t = _drag_limit;
138                 }
139
140                 double const f = _dragging->from + _dragging->first_ui_size;
141                 if (t < f) {
142                         /* limit drag in the `too small' direction */
143                         t = f;
144                 }
145
146                 _dragging->to = t;
147         }
148
149         set_dirty ();
150         queue_draw ();
151
152         _drag_last = p;
153
154         return true;
155 }
156
157
158 bool
159 GroupTabs::on_button_release_event (GdkEventButton* ev)
160 {
161         if (_dragging == 0) {
162                 return false;
163         }
164
165         if (!_drag_moved) {
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                         _dragging = 0;
178
179                 }
180
181         } else {
182                 /* finish drag */
183                 _dragging = 0;
184                 reflect_tabs (_tabs);
185                 set_dirty ();
186                 queue_draw ();
187         }
188
189         return true;
190 }
191
192 void
193 GroupTabs::render (cairo_t* cr)
194 {
195         if (_dragging == 0) {
196                 _tabs = compute_tabs ();
197         }
198
199         /* background */
200
201         cairo_set_source_rgb (cr, 0, 0, 0);
202         cairo_rectangle (cr, 0, 0, _width, _height);
203         cairo_fill (cr);
204
205         /* tabs */
206
207         for (list<Tab>::const_iterator i = _tabs.begin(); i != _tabs.end(); ++i) {
208                 draw_tab (cr, *i);
209         }
210 }
211
212
213 /** Convert a click position to a tab.
214  *  @param c Click position.
215  *  @param prev Filled in with the previous tab to the click, or 0.
216  *  @param next Filled in with the next tab after the click, or 0.
217  *  @return Tab under the click, or 0.
218  */
219
220 GroupTabs::Tab *
221 GroupTabs::click_to_tab (double c, Tab** prev, Tab** next)
222 {
223         *prev = 0;
224
225         list<Tab>::iterator i = _tabs.begin ();
226         while (i != _tabs.end() && (c < i->from || c > i->to)) {
227                 *prev = &(*i);
228                 ++i;
229         }
230
231         if (i == _tabs.end()) {
232                 *next = 0;
233                 return 0;
234         }
235
236         list<Tab>::iterator j = i;
237         ++j;
238         if (j == _tabs.end()) {
239                 *next = 0;
240         } else {
241                 *next = &(*j);
242         }
243
244         return &(*i);
245 }
246