Copy local state in AudioRegionView copy constructor. Fixes #4047.
[ardour2.git] / libs / gtkmm2ext / scroomer.cc
blob85092379f495b79f728bffb22c98a65f6135fd70
1 /*
2 Copyright (C) 2008 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 <iostream>
22 #include "gtkmm2ext/scroomer.h"
23 #include "gtkmm2ext/keyboard.h"
25 using namespace Gtkmm2ext;
26 using namespace Gtk;
27 using namespace Gdk;
28 using namespace std;
30 Scroomer::Scroomer(Gtk::Adjustment& adjustment)
31 : adj(adjustment)
32 , handle_size(0)
33 , grab_comp(None)
35 position[TopBase] = 0;
36 position[Handle1] = 0;
37 position[Slider] = 0;
38 position[Handle2] = 0;
39 position[BottomBase] = 0;
40 position[Total] = 0;
42 add_events (Gdk::BUTTON_PRESS_MASK |
43 Gdk::BUTTON_RELEASE_MASK |
44 Gdk::POINTER_MOTION_MASK |
45 Gdk::SCROLL_MASK);
47 adjustment.signal_value_changed().connect (mem_fun (*this, &Scroomer::adjustment_changed));
48 //adjustment.signal_changed().connect (mem_fun (*this, &Scroomer::adjustment_changed));
51 Scroomer::~Scroomer()
55 bool
56 Scroomer::on_motion_notify_event (GdkEventMotion* ev)
58 double range = adj.get_upper() - adj.get_lower();
59 double pixel2val = range / get_height();
60 double val_at_pointer = ((get_height() - ev->y) * pixel2val) + adj.get_lower();
61 double delta_y = ev->y - grab_y;
62 double half_min_page = min_page_size / 2;
63 double fract = delta_y / position[Total];
64 double scale, temp, zoom;
65 double val, page;
67 if (grab_comp == None || grab_comp == Total) {
68 return true;
71 if (ev->window != grab_window) {
72 grab_y = ev->y;
73 grab_window = ev->window;
74 return true;
77 grab_y = ev->y;
79 if (ev->state & Keyboard::PrimaryModifier) {
80 if (ev->state & Keyboard::SecondaryModifier) {
81 scale = 0.05;
82 } else {
83 scale = 0.1;
85 } else {
86 scale = 1.0;
89 fract = min (1.0, fract);
90 fract = max (-1.0, fract);
91 fract = -fract;
93 switch (grab_comp) {
94 case TopBase:
95 case BottomBase:
96 unzoomed_val += scale * fract * range;
97 unzoomed_val = min(unzoomed_val, adj.get_upper() - unzoomed_page);
98 unzoomed_val = max(unzoomed_val, adj.get_lower());
99 break;
100 case Slider:
101 unzoomed_val += scale * fract * range;
102 unzoomed_val = min(unzoomed_val, adj.get_upper() - unzoomed_page);
103 unzoomed_val = max(unzoomed_val, adj.get_lower());
104 break;
105 case Handle1:
107 unzoomed_page += scale * fract * range;
108 unzoomed_page = min(unzoomed_page, adj.get_upper() - unzoomed_val);
109 unzoomed_page = max(unzoomed_page, min_page_size);
111 if (pinch){
112 temp = unzoomed_val + unzoomed_page;
113 unzoomed_val -= scale * fract * range * 0.5;
114 unzoomed_val = min(unzoomed_val, temp - min_page_size);
115 unzoomed_val = max(unzoomed_val, adj.get_lower());
118 break;
119 case Handle2:
120 temp = unzoomed_val + unzoomed_page;
121 unzoomed_val += scale * fract * range;
122 unzoomed_val = min(unzoomed_val, temp - min_page_size);
123 unzoomed_val = max(unzoomed_val, adj.get_lower());
125 unzoomed_page = temp - unzoomed_val;
127 if (pinch){
129 unzoomed_page -= scale * fract * range;
132 unzoomed_page = min(unzoomed_page, adj.get_upper() - unzoomed_val);
133 unzoomed_page = max(unzoomed_page, min_page_size);
134 break;
135 default:
136 break;
139 /* Then we handle zoom, which is dragging horizontally. We zoom around the area that is
140 * the current y pointer value, not from the area that was the start of the drag.
141 * the point of zoom must have the same
144 if (ev->x > get_width()) {
145 zoom = ev->x - get_width();
147 double higher = unzoomed_val + unzoomed_page - half_min_page - val_at_pointer;
148 double lower = val_at_pointer - (unzoomed_val + half_min_page);
150 higher *= zoom / 128;
151 lower *= zoom / 128;
153 val = unzoomed_val + lower;
154 page = unzoomed_page - higher - lower;
156 page = max(page, min_page_size);
158 if (lower < 0) {
159 val = max(val, val_at_pointer - half_min_page);
160 } else if (lower > 0) {
161 val = min(val, val_at_pointer - half_min_page);
164 val = min(val, adj.get_upper() - min_page_size);
165 page = min(page, adj.get_upper() - val);
166 } else if (ev->x < 0) {
167 /* on zoom out increase the page size as well as moving the range towards the mouse pos*/
168 zoom = abs(ev->x);
170 /*double higher = unzoomed_val + unzoomed_page - half_min_page - val_at_pointer;
171 double lower = val_at_pointer - (unzoomed_val + half_min_page);
173 higher *= zoom / 128;
174 lower *= zoom / 128;
176 val = unzoomed_val + lower;
177 page = unzoomed_page - higher - lower;
179 page = max(page, min_page_size);
181 if (lower < 0) {
182 val = max(val, val_at_pointer - half_min_page);
184 else if (lower > 0) {
185 val = min(val, val_at_pointer - half_min_page);
188 val = min(val, adj.get_upper() - min_page_size);
189 page = min(page, adj.get_upper() - val);*/
191 val = unzoomed_val;
192 page = unzoomed_page;
193 } else {
194 val = unzoomed_val;
195 page = unzoomed_page;
198 adj.set_page_size(page);
199 adj.set_value(val);
200 adj.value_changed();
202 return true;
205 bool
206 Scroomer::on_scroll_event (GdkEventScroll* ev)
208 switch (ev->direction) {
209 case GDK_SCROLL_UP:
210 adj.set_value (adj.get_value() + adj.get_page_size() / 10.0);
211 break;
212 case GDK_SCROLL_DOWN:
213 adj.set_value (adj.get_value() - adj.get_page_size() / 10.0);
214 break;
215 default:
216 return false;
219 return true;
222 bool
223 Scroomer::on_button_press_event (GdkEventButton* ev)
225 if (ev->button == 1 || ev->button == 3) {
226 Component comp = point_in(ev->y);
228 if (comp == Total || comp == None) {
229 return false;
232 add_modal_grab();
233 grab_comp = comp;
234 grab_y = ev->y;
235 unzoomed_val = adj.get_value();
236 unzoomed_page = adj.get_page_size();
237 grab_window = ev->window;
239 if (ev->button == 3){
240 pinch = true;
241 } else {
242 pinch = false;
245 DragStarting (); /* EMIT SIGNAL */
248 return false;
251 bool
252 Scroomer::on_button_release_event (GdkEventButton* ev)
254 if (grab_comp == None || grab_comp == Total) {
255 return true;
258 if (ev->window != grab_window) {
259 grab_y = ev->y;
260 grab_window = ev->window;
261 return true;
264 if (ev->button != 1 && ev->button != 3) {
265 return true;
268 switch (grab_comp) {
269 case TopBase:
270 break;
271 case Handle1:
272 break;
273 case Slider:
274 break;
275 case Handle2:
276 break;
277 case BottomBase:
278 break;
279 default:
280 break;
283 grab_comp = None;
285 remove_modal_grab();
286 DragFinishing (); /* EMIT SIGNAL */
287 return true;
290 void
291 Scroomer::on_size_allocate (Allocation& a)
293 Gtk::DrawingArea::on_size_allocate(a);
295 position[Total] = a.get_height();
296 set_min_page_size(min_page_size);
297 update();
300 /** Assumes that x and width are correct, and they will not be altered.
302 void
303 Scroomer::set_comp_rect(GdkRectangle& r, Component c) const
305 int index = (int) c;
307 switch (c) {
308 case None:
309 return;
310 case Total:
311 r.y = 0;
312 r.height = position[Total];
313 break;
314 default:
315 r.y = position[index];
316 r.height = position[index+1] - position[index];
317 break;
321 Scroomer::Component
322 Scroomer::point_in(double point) const
324 for (int i = 0; i < Total; ++i) {
325 if (position[i+1] >= point) {
326 return (Component) i;
330 return None;
333 void
334 Scroomer::set_min_page_size(double ps)
336 double coeff = ((double)position[Total]) / (adj.get_upper() - adj.get_lower());
338 min_page_size = ps;
339 handle_size = (int) floor((ps * coeff) / 2);
342 void
343 Scroomer::update()
345 double range = adj.get_upper() - adj.get_lower();
346 //double value = adj.get_value() - adj.get_lower();
347 int height = position[Total];
348 double coeff = ((double) height) / range;
350 /* save the old positions to calculate update regions later*/
351 for (int i = Handle1; i < Total; ++i) {
352 old_pos[i] = position[i];
355 position[BottomBase] = (int) floor(height - (adj.get_value() * coeff));
356 position[Handle2] = position[BottomBase] - handle_size;
358 position[Handle1] = (int) floor(height - ((adj.get_value() + adj.get_page_size()) * coeff));
359 position[Slider] = position[Handle1] + handle_size;
362 void
363 Scroomer::adjustment_changed()
365 //cerr << floor(adj.get_value()) << " " << floor(adj.get_value() + adj.get_page_size()) << endl;
366 Gdk::Rectangle rect;
367 Glib::RefPtr<Gdk::Window> win = get_window();
369 update();
371 if (!win) {
372 return;
375 rect.set_x(0);
376 rect.set_width(get_width());
378 if (position[Handle1] < old_pos[Handle1]) {
379 rect.set_y(position[Handle1]);
380 rect.set_height(old_pos[Slider] - position[Handle1]);
381 win->invalidate_rect(rect, false);
382 } else if (position[Handle1] > old_pos[Handle1]) {
383 rect.set_y(old_pos[Handle1]);
384 rect.set_height(position[Slider] - old_pos[Handle1]);
385 win->invalidate_rect(rect, false);
388 if (position[Handle2] < old_pos[Handle2]) {
389 rect.set_y(position[Handle2]);
390 rect.set_height(old_pos[BottomBase] - position[Handle2]);
391 win->invalidate_rect(rect, false);
392 } else if (position[Handle2] > old_pos[Handle2]) {
393 rect.set_y(old_pos[Handle2]);
394 rect.set_height(position[BottomBase] - old_pos[Handle2]);
395 win->invalidate_rect(rect, false);