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
;
79 if (ev
->state
& Keyboard::PrimaryModifier
) {
80 if (ev
->state
& Keyboard::SecondaryModifier
) {
89 fract
= min (1.0, fract
);
90 fract
= max (-1.0, fract
);
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());
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());
106 unzoomed_page
+= scale
* fract
* range
;
107 unzoomed_page
= min(unzoomed_page
, adj
.get_upper() - unzoomed_val
);
108 unzoomed_page
= max(unzoomed_page
, min_page_size
);
111 temp
= unzoomed_val
+ unzoomed_page
;
112 unzoomed_val
+= scale
* fract
* range
;
113 unzoomed_val
= min(unzoomed_val
, temp
- min_page_size
);
114 unzoomed_val
= max(unzoomed_val
, adj
.get_lower());
116 unzoomed_page
= temp
- unzoomed_val
;
117 unzoomed_page
= max(unzoomed_page
, min_page_size
);
123 /* Then we handle zoom, which is dragging horizontally. We zoom around the area that is
124 * the current y pointer value, not from the area that was the start of the drag.
125 * the point of zoom must have the same
128 if (ev
->x
> get_width()) {
129 zoom
= ev
->x
- get_width();
131 double higher
= unzoomed_val
+ unzoomed_page
- half_min_page
- val_at_pointer
;
132 double lower
= val_at_pointer
- (unzoomed_val
+ half_min_page
);
134 higher
*= zoom
/ 128;
137 val
= unzoomed_val
+ lower
;
138 page
= unzoomed_page
- higher
- lower
;
140 page
= max(page
, min_page_size
);
143 val
= max(val
, val_at_pointer
- half_min_page
);
144 } else if (lower
> 0) {
145 val
= min(val
, val_at_pointer
- half_min_page
);
148 val
= min(val
, adj
.get_upper() - min_page_size
);
149 page
= min(page
, adj
.get_upper() - val
);
150 } else if (ev
->x
< 0) {
151 /* on zoom out increase the page size as well as moving the range towards the mouse pos*/
154 /*double higher = unzoomed_val + unzoomed_page - half_min_page - val_at_pointer;
155 double lower = val_at_pointer - (unzoomed_val + half_min_page);
157 higher *= zoom / 128;
160 val = unzoomed_val + lower;
161 page = unzoomed_page - higher - lower;
163 page = max(page, min_page_size);
166 val = max(val, val_at_pointer - half_min_page);
168 else if (lower > 0) {
169 val = min(val, val_at_pointer - half_min_page);
172 val = min(val, adj.get_upper() - min_page_size);
173 page = min(page, adj.get_upper() - val);*/
176 page
= unzoomed_page
;
179 page
= unzoomed_page
;
182 adj
.set_page_size(page
);
184 if (val
== adj
.get_value()) {
188 if (val
< adj
.get_lower()) {
190 } else if (val
> adj
.get_upper()) {
200 Scroomer::on_button_press_event (GdkEventButton
* ev
)
202 if (ev
->button
== 1) {
203 Component comp
= point_in(ev
->y
);
205 if (comp
== Total
|| comp
== None
) {
212 unzoomed_val
= adj
.get_value();
213 unzoomed_page
= adj
.get_page_size();
214 grab_window
= ev
->window
;
220 Scroomer::on_button_release_event (GdkEventButton
* ev
)
222 if (grab_comp
== None
|| grab_comp
== Total
) {
226 if (ev
->window
!= grab_window
) {
228 grab_window
= ev
->window
;
232 if (ev
->button
!= 1) {
258 Scroomer::on_scroll_event (GdkEventScroll
*)
264 Scroomer::on_size_allocate (Allocation
& a
)
266 Gtk::DrawingArea::on_size_allocate(a
);
268 position
[Total
] = a
.get_height();
269 set_min_page_size(min_page_size
);
273 /** Assumes that x and width are correct, and they will not be altered.
276 Scroomer::set_comp_rect(GdkRectangle
& r
, Component c
) const
285 r
.height
= position
[Total
];
288 r
.y
= position
[index
];
289 r
.height
= position
[index
+1] - position
[index
];
295 Scroomer::point_in(double point
) const
297 for (int i
= 0; i
< Total
; ++i
) {
298 if (position
[i
+1] >= point
) {
299 return (Component
) i
;
307 Scroomer::set_min_page_size(double ps
)
309 double coeff
= ((double)position
[Total
]) / (adj
.get_upper() - adj
.get_lower());
312 handle_size
= (int) floor((ps
* coeff
) / 2);
318 double range
= adj
.get_upper() - adj
.get_lower();
319 //double value = adj.get_value() - adj.get_lower();
320 int height
= position
[Total
];
321 double coeff
= ((double) height
) / range
;
323 /* save the old positions to calculate update regions later*/
324 for (int i
= Handle1
; i
< Total
; ++i
) {
325 old_pos
[i
] = position
[i
];
328 position
[BottomBase
] = (int) floor(height
- (adj
.get_value() * coeff
));
329 position
[Handle2
] = position
[BottomBase
] - handle_size
;
331 position
[Handle1
] = (int) floor(height
- ((adj
.get_value() + adj
.get_page_size()) * coeff
));
332 position
[Slider
] = position
[Handle1
] + handle_size
;
336 Scroomer::adjustment_changed()
338 //cerr << floor(adj.get_value()) << " " << floor(adj.get_value() + adj.get_page_size()) << endl;
340 Glib::RefPtr
<Gdk::Window
> win
= get_window();
349 rect
.set_width(get_width());
351 if (position
[Handle1
] < old_pos
[Handle1
]) {
352 rect
.set_y(position
[Handle1
]);
353 rect
.set_height(old_pos
[Slider
] - position
[Handle1
]);
354 win
->invalidate_rect(rect
, false);
355 } else if (position
[Handle1
] > old_pos
[Handle1
]) {
356 rect
.set_y(old_pos
[Handle1
]);
357 rect
.set_height(position
[Slider
] - old_pos
[Handle1
]);
358 win
->invalidate_rect(rect
, false);
361 if (position
[Handle2
] < old_pos
[Handle2
]) {
362 rect
.set_y(position
[Handle2
]);
363 rect
.set_height(old_pos
[BottomBase
] - position
[Handle2
]);
364 win
->invalidate_rect(rect
, false);
365 } else if (position
[Handle2
] > old_pos
[Handle2
]) {
366 rect
.set_y(old_pos
[Handle2
]);
367 rect
.set_height(position
[BottomBase
] - old_pos
[Handle2
]);
368 win
->invalidate_rect(rect
, false);
371 win
->process_updates(false);
375 Scroomer::get_comp_name(Component c
)