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.
22 #include "gtkmm2ext/scroomer.h"
23 #include "gtkmm2ext/keyboard.h"
25 using namespace Gtkmm2ext;
30 Scroomer::Scroomer(Gtk::Adjustment& adjustment)
35 position[TopBase] = 0;
36 position[Handle1] = 0;
38 position[Handle2] = 0;
39 position[BottomBase] = 0;
42 add_events (Gdk::BUTTON_PRESS_MASK |
43 Gdk::BUTTON_RELEASE_MASK |
44 Gdk::POINTER_MOTION_MASK |
47 adjustment.signal_value_changed().connect (mem_fun (*this, &Scroomer::adjustment_changed));
48 //adjustment.signal_changed().connect (mem_fun (*this, &Scroomer::adjustment_changed));
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;
67 if (grab_comp == None || grab_comp == Total) {
71 if (ev->window != grab_window) {
73 grab_window = ev->window;
77 if (ev->y < 0 || ev->y > get_height ()) {
83 if (ev->state & Keyboard::PrimaryModifier) {
84 if (ev->state & Keyboard::SecondaryModifier) {
93 fract = min (1.0, fract);
94 fract = max (-1.0, fract);
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());
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());
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);
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());
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());
129 unzoomed_page = temp - unzoomed_val;
133 unzoomed_page -= scale * fract * range;
136 unzoomed_page = min(unzoomed_page, adj.get_upper() - unzoomed_val);
137 unzoomed_page = max(unzoomed_page, min_page_size);
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
149 if (ev->x > (get_width() * 2)) {
150 zoom = ev->x - get_width();
152 double higher = unzoomed_val + unzoomed_page - half_min_page - val_at_pointer;
153 double lower = val_at_pointer - (unzoomed_val + half_min_page);
155 higher *= zoom / 128;
158 val = unzoomed_val + lower;
159 page = unzoomed_page - higher - lower;
161 page = max(page, min_page_size);
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);
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*/
175 /*double higher = unzoomed_val + unzoomed_page - half_min_page - val_at_pointer;
176 double lower = val_at_pointer - (unzoomed_val + half_min_page);
178 higher *= zoom / 128;
181 val = unzoomed_val + lower;
182 page = unzoomed_page - higher - lower;
184 page = max(page, min_page_size);
187 val = max(val, val_at_pointer - half_min_page);
189 else if (lower > 0) {
190 val = min(val, val_at_pointer - half_min_page);
193 val = min(val, adj.get_upper() - min_page_size);
194 page = min(page, adj.get_upper() - val);*/
197 page = unzoomed_page;
200 page = unzoomed_page;
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));
212 Scroomer::on_scroll_event (GdkEventScroll* ev)
214 switch (ev->direction) {
216 adj.set_value (adj.get_value() + adj.get_page_size() / 10.0);
218 case GDK_SCROLL_DOWN:
219 adj.set_value (adj.get_value() - adj.get_page_size() / 10.0);
229 Scroomer::on_button_press_event (GdkEventButton* ev)
231 if (ev->button == 1 || ev->button == 3) {
232 Component comp = point_in(ev->y);
234 if (comp == Total || comp == None) {
241 unzoomed_val = adj.get_value();
242 unzoomed_page = adj.get_page_size();
243 grab_window = ev->window;
245 if (ev->button == 3){
251 DragStarting (); /* EMIT SIGNAL */
254 if (ev->type == GDK_2BUTTON_PRESS && ev->button == 1) {
262 Scroomer::on_button_release_event (GdkEventButton* ev)
264 if (grab_comp == None || grab_comp == Total) {
268 if (ev->window != grab_window) {
270 grab_window = ev->window;
274 if (ev->button != 1 && ev->button != 3) {
296 DragFinishing (); /* EMIT SIGNAL */
301 Scroomer::on_size_allocate (Allocation& a)
303 Gtk::DrawingArea::on_size_allocate(a);
305 position[Total] = a.get_height();
306 set_min_page_size(min_page_size);
310 /** Assumes that x and width are correct, and they will not be altered.
313 Scroomer::set_comp_rect(GdkRectangle& r, Component c) const
322 r.height = position[Total];
325 r.y = position[index];
326 r.height = position[index+1] - position[index];
332 Scroomer::point_in(double point) const
334 for (int i = 0; i < Total; ++i) {
335 if (position[i+1] >= point) {
336 return (Component) i;
344 Scroomer::set_min_page_size(double ps)
346 double coeff = ((double)position[Total]) / (adj.get_upper() - adj.get_lower());
349 handle_size = (int) floor((ps * coeff) / 2);
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;
360 /* save the old positions to calculate update regions later*/
361 for (int i = Handle1; i < Total; ++i) {
362 old_pos[i] = position[i];
365 position[BottomBase] = (int) floor(height - (adj.get_value() * coeff));
366 position[Handle2] = position[BottomBase] - handle_size;
368 position[Handle1] = (int) floor(height - ((adj.get_value() + adj.get_page_size()) * coeff));
369 position[Slider] = position[Handle1] + handle_size;
373 Scroomer::adjustment_changed()
375 //cerr << floor(adj.get_value()) << " " << floor(adj.get_value() + adj.get_page_size()) << endl;
377 Glib::RefPtr<Gdk::Window> win = get_window();
386 rect.set_width(get_width());
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);
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);