ardour
scroomer.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 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 <iostream>
21 
22 #include "gtkmm2ext/scroomer.h"
23 #include "gtkmm2ext/keyboard.h"
24 
25 using namespace Gtkmm2ext;
26 using namespace Gtk;
27 using namespace Gdk;
28 using namespace std;
29 
30 Scroomer::Scroomer(Gtk::Adjustment& adjustment)
31  : adj(adjustment)
32  , handle_size(0)
33  , grab_comp(None)
34 {
35  position[TopBase] = 0;
36  position[Handle1] = 0;
37  position[Slider] = 0;
38  position[Handle2] = 0;
39  position[BottomBase] = 0;
40  position[Total] = 0;
41 
42  add_events (Gdk::BUTTON_PRESS_MASK |
43  Gdk::BUTTON_RELEASE_MASK |
44  Gdk::POINTER_MOTION_MASK |
45  Gdk::SCROLL_MASK);
46 
47  adjustment.signal_value_changed().connect (mem_fun (*this, &Scroomer::adjustment_changed));
48  //adjustment.signal_changed().connect (mem_fun (*this, &Scroomer::adjustment_changed));
49 }
50 
52 {
53 }
54 
55 bool
57 {
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;
66 
67  if (grab_comp == None || grab_comp == Total) {
68  return true;
69  }
70 
71  if (ev->window != grab_window) {
72  grab_y = ev->y;
73  grab_window = ev->window;
74  return true;
75  }
76 
77  if (ev->y < 0 || ev->y > get_height ()) {
78  return true;
79  }
80 
81  grab_y = ev->y;
82 
83  if (ev->state & Keyboard::PrimaryModifier) {
84  if (ev->state & Keyboard::SecondaryModifier) {
85  scale = 0.05;
86  } else {
87  scale = 0.1;
88  }
89  } else {
90  scale = 1.0;
91  }
92 
93  fract = min (1.0, fract);
94  fract = max (-1.0, fract);
95  fract = -fract;
96 
97  switch (grab_comp) {
98  case TopBase:
99  case BottomBase:
100  unzoomed_val += scale * fract * range;
101  unzoomed_val = min(unzoomed_val, adj.get_upper() - unzoomed_page);
102  unzoomed_val = max(unzoomed_val, adj.get_lower());
103  break;
104  case Slider:
105  unzoomed_val += scale * fract * range;
106  unzoomed_val = min(unzoomed_val, adj.get_upper() - unzoomed_page);
107  unzoomed_val = max(unzoomed_val, adj.get_lower());
108  break;
109  case Handle1:
110 
111  unzoomed_page += scale * fract * range;
112  unzoomed_page = min(unzoomed_page, adj.get_upper() - unzoomed_val);
113  unzoomed_page = max(unzoomed_page, min_page_size);
114 
115  if (pinch){
116  temp = unzoomed_val + unzoomed_page;
117  unzoomed_val -= scale * fract * range * 0.5;
118  unzoomed_val = min(unzoomed_val, temp - min_page_size);
119  unzoomed_val = max(unzoomed_val, adj.get_lower());
120  }
121 
122  break;
123  case Handle2:
124  temp = unzoomed_val + unzoomed_page;
125  unzoomed_val += scale * fract * range;
126  unzoomed_val = min(unzoomed_val, temp - min_page_size);
127  unzoomed_val = max(unzoomed_val, adj.get_lower());
128 
129  unzoomed_page = temp - unzoomed_val;
130 
131  if (pinch){
132 
133  unzoomed_page -= scale * fract * range;
134  }
135 
136  unzoomed_page = min(unzoomed_page, adj.get_upper() - unzoomed_val);
137  unzoomed_page = max(unzoomed_page, min_page_size);
138  break;
139  default:
140  break;
141  }
142 
143  /* Then we handle zoom, which is dragging horizontally. We zoom around the area that is
144  * the current y pointer value, not from the area that was the start of the drag.
145  * We don't start doing zoom until we are at least one scroomer width outside the scroomer's
146  * area.
147  */
148 
149  if (ev->x > (get_width() * 2)) {
150  zoom = ev->x - get_width();
151 
152  double higher = unzoomed_val + unzoomed_page - half_min_page - val_at_pointer;
153  double lower = val_at_pointer - (unzoomed_val + half_min_page);
154 
155  higher *= zoom / 128;
156  lower *= zoom / 128;
157 
158  val = unzoomed_val + lower;
159  page = unzoomed_page - higher - lower;
160 
161  page = max(page, min_page_size);
162 
163  if (lower < 0) {
164  val = max(val, val_at_pointer - half_min_page);
165  } else if (lower > 0) {
166  val = min(val, val_at_pointer - half_min_page);
167  }
168 
169  val = min(val, adj.get_upper() - min_page_size);
170  page = min(page, adj.get_upper() - val);
171  } else if (ev->x < 0) {
172  /* on zoom out increase the page size as well as moving the range towards the mouse pos*/
173  /*zoom = abs(ev->x);
174 
175  double higher = unzoomed_val + unzoomed_page - half_min_page - val_at_pointer;
176  double lower = val_at_pointer - (unzoomed_val + half_min_page);
177 
178  higher *= zoom / 128;
179  lower *= zoom / 128;
180 
181  val = unzoomed_val + lower;
182  page = unzoomed_page - higher - lower;
183 
184  page = max(page, min_page_size);
185 
186  if (lower < 0) {
187  val = max(val, val_at_pointer - half_min_page);
188  }
189  else if (lower > 0) {
190  val = min(val, val_at_pointer - half_min_page);
191  }
192 
193  val = min(val, adj.get_upper() - min_page_size);
194  page = min(page, adj.get_upper() - val);*/
195 
196  val = unzoomed_val;
197  page = unzoomed_page;
198  } else {
199  val = unzoomed_val;
200  page = unzoomed_page;
201  }
202 
203  /* Round these values to stop the scroomer handlers quivering about during drags */
204  adj.set_page_size (rint (page));
205  adj.set_value (rint (val));
206  adj.value_changed();
207 
208  return true;
209 }
210 
211 bool
212 Scroomer::on_scroll_event (GdkEventScroll* ev)
213 {
214  switch (ev->direction) {
215  case GDK_SCROLL_UP:
216  adj.set_value (min (adj.get_value() + adj.get_page_size() / 10.0, adj.get_upper() - adj.get_page_size()));
217  break;
218  case GDK_SCROLL_DOWN:
219  adj.set_value (adj.get_value() - adj.get_page_size() / 10.0);
220  break;
221  default:
222  return false;
223  }
224 
225  return true;
226 }
227 
228 bool
230 {
231  if (ev->button == 1 || ev->button == 3) {
232  Component comp = point_in(ev->y);
233 
234  if (comp == Total || comp == None) {
235  return false;
236  }
237 
238  add_modal_grab();
239  grab_comp = comp;
240  grab_y = ev->y;
241  unzoomed_val = adj.get_value();
242  unzoomed_page = adj.get_page_size();
243  grab_window = ev->window;
244 
245  if (ev->button == 3){
246  pinch = true;
247  } else {
248  pinch = false;
249  }
250 
251  DragStarting (); /* EMIT SIGNAL */
252  }
253 
254  if (ev->type == GDK_2BUTTON_PRESS && ev->button == 1) {
255  DoubleClicked();
256  }
257 
258  return true;
259 }
260 
261 bool
263 {
264  if (grab_comp == None || grab_comp == Total) {
265  return true;
266  }
267 
268  if (ev->window != grab_window) {
269  grab_y = ev->y;
270  grab_window = ev->window;
271  return true;
272  }
273 
274  if (ev->button != 1 && ev->button != 3) {
275  return true;
276  }
277 
278  switch (grab_comp) {
279  case TopBase:
280  break;
281  case Handle1:
282  break;
283  case Slider:
284  break;
285  case Handle2:
286  break;
287  case BottomBase:
288  break;
289  default:
290  break;
291  }
292 
293  grab_comp = None;
294 
295  remove_modal_grab();
296  DragFinishing (); /* EMIT SIGNAL */
297  return true;
298 }
299 
300 void
302 {
303  Gtk::DrawingArea::on_size_allocate(a);
304 
305  position[Total] = a.get_height();
307  update();
308 }
309 
312 void
313 Scroomer::set_comp_rect(GdkRectangle& r, Component c) const
314 {
315  int index = (int) c;
316 
317  switch (c) {
318  case None:
319  return;
320  case Total:
321  r.y = 0;
322  r.height = position[Total];
323  break;
324  default:
325  r.y = position[index];
326  r.height = position[index+1] - position[index];
327  break;
328  }
329 }
330 
332 Scroomer::point_in(double point) const
333 {
334  for (int i = 0; i < Total; ++i) {
335  if (position[i+1] >= point) {
336  return (Component) i;
337  }
338  }
339 
340  return None;
341 }
342 
343 void
345 {
346  double coeff = ((double)position[Total]) / (adj.get_upper() - adj.get_lower());
347 
348  min_page_size = ps;
349  handle_size = (int) floor((ps * coeff) / 2);
350 }
351 
352 void
354 {
355  double range = adj.get_upper() - adj.get_lower();
356  //double value = adj.get_value() - adj.get_lower();
357  int height = position[Total];
358  double coeff = ((double) height) / range;
359 
360  /* save the old positions to calculate update regions later*/
361  for (int i = Handle1; i < Total; ++i) {
362  old_pos[i] = position[i];
363  }
364 
365  position[BottomBase] = (int) floor(height - (adj.get_value() * coeff));
367 
368  position[Handle1] = (int) floor(height - ((adj.get_value() + adj.get_page_size()) * coeff));
370 }
371 
372 void
374 {
375  //cerr << floor(adj.get_value()) << " " << floor(adj.get_value() + adj.get_page_size()) << endl;
376  Gdk::Rectangle rect;
377  Glib::RefPtr<Gdk::Window> win = get_window();
378 
379  update();
380 
381  if (!win) {
382  return;
383  }
384 
385  rect.set_x(0);
386  rect.set_width(get_width());
387 
388  if (position[Handle1] < old_pos[Handle1]) {
389  rect.set_y(position[Handle1]);
390  rect.set_height(old_pos[Slider] - position[Handle1]);
391  win->invalidate_rect(rect, false);
392  } else if (position[Handle1] > old_pos[Handle1]) {
393  rect.set_y(old_pos[Handle1]);
394  rect.set_height(position[Slider] - old_pos[Handle1]);
395  win->invalidate_rect(rect, false);
396  }
397 
398  if (position[Handle2] < old_pos[Handle2]) {
399  rect.set_y(position[Handle2]);
400  rect.set_height(old_pos[BottomBase] - position[Handle2]);
401  win->invalidate_rect(rect, false);
402  } else if (position[Handle2] > old_pos[Handle2]) {
403  rect.set_y(old_pos[Handle2]);
404  rect.set_height(position[BottomBase] - old_pos[Handle2]);
405  win->invalidate_rect(rect, false);
406  }
407 }
408 
Component grab_comp
Definition: scroomer.h:84
void set_min_page_size(double page_size)
Definition: scroomer.cc:344
bool on_motion_notify_event(GdkEventMotion *)
Definition: scroomer.cc:56
Definition: ardour_ui.h:130
void set_comp_rect(GdkRectangle &, Component) const
Definition: scroomer.cc:313
Definition: Beats.hpp:239
virtual void on_size_allocate(Gtk::Allocation &)
Definition: scroomer.cc:301
GdkWindow * grab_window
Definition: scroomer.h:83
Scroomer(Gtk::Adjustment &adjustment)
Definition: scroomer.cc:30
void adjustment_changed()
Definition: scroomer.cc:373
sigc::signal0< void > DragFinishing
Definition: scroomer.h:63
bool on_button_press_event(GdkEventButton *)
Definition: scroomer.cc:229
double unzoomed_val
Definition: scroomer.h:86
bool on_button_release_event(GdkEventButton *)
Definition: scroomer.cc:262
static uint32_t PrimaryModifier
Definition: keyboard.h:55
sigc::signal0< void > DoubleClicked
Definition: scroomer.h:65
sigc::signal0< void > DragStarting
Definition: scroomer.h:62
static uint32_t SecondaryModifier
Definition: keyboard.h:56
Gtk::Adjustment & adj
Definition: scroomer.h:68
double unzoomed_page
Definition: scroomer.h:87
bool on_scroll_event(GdkEventScroll *)
Definition: scroomer.cc:212
static double const handle_size
#define lower
Definition: auto_spin.cc:29
Component point_in(double point) const
Definition: scroomer.cc:332
double min_page_size
Definition: scroomer.h:82