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());
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
);
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());
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
;
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
);
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;
153 val
= unzoomed_val
+ lower
;
154 page
= unzoomed_page
- higher
- lower
;
156 page
= max(page
, min_page_size
);
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*/
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;
176 val = unzoomed_val + lower;
177 page = unzoomed_page - higher - lower;
179 page = max(page, min_page_size);
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);*/
192 page
= unzoomed_page
;
195 page
= unzoomed_page
;
198 adj
.set_page_size(page
);
206 Scroomer::on_scroll_event (GdkEventScroll
* ev
)
208 switch (ev
->direction
) {
210 adj
.set_value (adj
.get_value() + adj
.get_page_size() / 10.0);
212 case GDK_SCROLL_DOWN
:
213 adj
.set_value (adj
.get_value() - adj
.get_page_size() / 10.0);
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
) {
235 unzoomed_val
= adj
.get_value();
236 unzoomed_page
= adj
.get_page_size();
237 grab_window
= ev
->window
;
239 if (ev
->button
== 3){
245 DragStarting (); /* EMIT SIGNAL */
252 Scroomer::on_button_release_event (GdkEventButton
* ev
)
254 if (grab_comp
== None
|| grab_comp
== Total
) {
258 if (ev
->window
!= grab_window
) {
260 grab_window
= ev
->window
;
264 if (ev
->button
!= 1 && ev
->button
!= 3) {
286 DragFinishing (); /* EMIT SIGNAL */
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
);
300 /** Assumes that x and width are correct, and they will not be altered.
303 Scroomer::set_comp_rect(GdkRectangle
& r
, Component c
) const
312 r
.height
= position
[Total
];
315 r
.y
= position
[index
];
316 r
.height
= position
[index
+1] - position
[index
];
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
;
334 Scroomer::set_min_page_size(double ps
)
336 double coeff
= ((double)position
[Total
]) / (adj
.get_upper() - adj
.get_lower());
339 handle_size
= (int) floor((ps
* coeff
) / 2);
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
;
363 Scroomer::adjustment_changed()
365 //cerr << floor(adj.get_value()) << " " << floor(adj.get_value() + adj.get_page_size()) << endl;
367 Glib::RefPtr
<Gdk::Window
> win
= get_window();
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);