f459726f274820caa910d180ab55620dbe39c571
[ardour.git] / libs / gtkmm2ext / pixscroller.cc
1 /*
2     Copyright (C) 2005 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     $Id$
19 */
20 #include <iostream>
21 #include <algorithm>
22 #include <cmath>
23
24 #include <gtkmm.h>
25
26 #include <gtkmm2ext/pixscroller.h>
27
28 using namespace std;
29 using namespace Gtk;
30 using namespace Gtkmm2ext;
31
32 PixScroller::PixScroller (Adjustment& a, 
33                           Glib::RefPtr<Gdk::Pixbuf> s,
34                           Glib::RefPtr<Gdk::Pixbuf> r)
35         : adj (a),
36           rail (r),
37           slider (s)
38 {
39         dragging = false;
40         add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK);
41
42         adj.signal_value_changed().connect (mem_fun (*this, &PixScroller::adjustment_changed));
43         default_value = adj.get_value();
44
45         sliderrect.set_width(slider->get_width());
46         sliderrect.set_height(slider->get_height());
47         railrect.set_width(rail->get_width());
48         railrect.set_height(rail->get_height());
49
50         railrect.set_y(sliderrect.get_height() / 2);
51         sliderrect.set_x(0);
52
53         overall_height = railrect.get_height() + sliderrect.get_height();
54
55         sliderrect.set_y((int) rint ((overall_height - sliderrect.get_height()) * (adj.get_upper() - adj.get_value())));
56         railrect.set_x((sliderrect.get_width() / 2) - 2);
57 }
58
59 void
60 PixScroller::on_size_request (GtkRequisition* requisition)
61 {
62         requisition->width = sliderrect.get_width();
63         requisition->height = overall_height;
64 }
65
66 bool
67 PixScroller::on_expose_event (GdkEventExpose* ev)
68 {
69         GdkRectangle intersect;
70         Glib::RefPtr<Gdk::Window> win (get_window());
71
72         win->draw_rectangle (get_style()->get_bg_gc(get_state()), TRUE, 
73                             ev->area.x,
74                             ev->area.y,
75                             ev->area.width,
76                             ev->area.height);
77
78         if (gdk_rectangle_intersect (railrect.gobj(), &ev->area, &intersect)) {
79                 Glib::RefPtr<Gdk::GC> gc(get_style()->get_bg_gc(get_state()));
80                 win->draw_pixbuf (gc, rail, 
81                                   intersect.x - railrect.get_x(),
82                                   intersect.y - railrect.get_y(),
83                                   intersect.x, 
84                                   intersect.y, 
85                                   intersect.width,
86                                   intersect.height,
87                                   Gdk::RGB_DITHER_NONE, 0, 0);
88         }
89         
90         if (gdk_rectangle_intersect (sliderrect.gobj(), &ev->area, &intersect)) {
91                 Glib::RefPtr<Gdk::GC> gc(get_style()->get_fg_gc(get_state()));
92                 // Glib::RefPtr<Gdk::Bitmap> mask (slider_mask);
93
94                 GdkGCValues values;
95                 gdk_gc_get_values(gc->gobj(), &values);
96                 gc->set_clip_origin (sliderrect.get_x(), sliderrect.get_y());
97                 // gc->set_clip_mask (mask);
98                 win->draw_pixbuf (gc, slider, 
99                                   intersect.x - sliderrect.get_x(),
100                                   intersect.y - sliderrect.get_y(),
101                                   intersect.x, 
102                                   intersect.y, 
103                                   intersect.width,
104                                   intersect.height,
105                                   Gdk::RGB_DITHER_NONE, 0, 0);
106                 gc->set_clip_origin (values.clip_x_origin, values.clip_y_origin);
107                 // gdk_gc_set_clip_mask (gc->gobj(), values.clip_mask);
108         }
109
110
111         return true;
112 }
113
114 bool
115 PixScroller::on_button_press_event (GdkEventButton* ev)
116 {
117         switch (ev->button) {
118         case 1:
119                 if (!(ev->state & Gdk::SHIFT_MASK)) {
120                         add_modal_grab();
121                         grab_y = ev->y;
122                         grab_start = ev->y;
123                         grab_window = ev->window;
124                         dragging = true;
125                 }
126                 break;
127         default:
128                 break;
129         } 
130                                
131
132         return false;
133 }
134
135 bool
136 PixScroller::on_button_release_event (GdkEventButton* ev)
137 {
138         double scale;
139         
140         if (ev->state & GDK_CONTROL_MASK) {
141                 if (ev->state & GDK_MOD1_MASK) {
142                         scale = 0.05;
143                 } else {
144                         scale = 0.1;
145                 }
146         } else {
147                 scale = 1.0;
148         }
149
150         switch (ev->button) {
151         case 1:
152                 if (dragging) {
153                         remove_modal_grab();
154                         dragging = false;
155
156                         if (ev->y == grab_start) {
157                                 /* no motion - just a click */
158                                 double fract;
159
160                                 if (ev->y < sliderrect.get_height()/2) {
161                                         /* near the top */
162                                         fract = 1.0;
163                                 } else {
164                                         fract = 1.0 - (ev->y - sliderrect.get_height()/2) / railrect.get_height();
165                                 }
166
167                                 fract = min (1.0, fract);
168                                 fract = max (0.0, fract);
169
170                                 adj.set_value (scale * fract * (adj.get_upper() - adj.get_lower()));
171                         }
172                 } else {
173                         if (ev->state & Gdk::SHIFT_MASK) {
174                                 adj.set_value (default_value);
175                                 cerr << "default value = " << default_value << endl;
176                         }
177                 }
178                 break;
179         default:
180                 break;
181         }
182         return false;
183 }
184
185 bool
186 PixScroller::on_scroll_event (GdkEventScroll* ev)
187 {
188         double scale;
189         
190         if (ev->state & GDK_CONTROL_MASK) {
191                 if (ev->state & GDK_MOD1_MASK) {
192                         scale = 0.05;
193                 } else {
194                         scale = 0.1;
195                 }
196         } else {
197                 scale = 0.5;
198         }
199
200         switch (ev->direction) {
201
202         case GDK_SCROLL_UP:
203                 /* wheel up */
204                 adj.set_value (adj.get_value() + (adj.get_page_increment() * scale));
205                 break;
206         case GDK_SCROLL_DOWN:
207                 /* wheel down */
208                 adj.set_value (adj.get_value() - (adj.get_page_increment() * scale));
209                 break;
210         default:
211                 break;
212         }
213         return false;
214 }
215
216 bool
217 PixScroller::on_motion_notify_event (GdkEventMotion* ev)
218 {
219         if (dragging) {
220                 double fract;
221                 double delta;
222                 double scale;
223
224                 if (ev->window != grab_window) {
225                         grab_y = ev->y;
226                         grab_window = ev->window;
227                         return true;
228                 }
229                 
230                 if (ev->state & GDK_CONTROL_MASK) {
231                         if (ev->state & GDK_MOD1_MASK) {
232                                 scale = 0.05;
233                         } else {
234                                 scale = 0.1;
235                         }
236                 } else {
237                         scale = 1.0;
238                 }
239
240                 delta = ev->y - grab_y;
241                 grab_y = ev->y;
242
243                 fract = (delta / railrect.get_height());
244
245                 fract = min (1.0, fract);
246                 fract = max (-1.0, fract);
247                 
248                 fract = -fract;
249
250                 adj.set_value (adj.get_value() + scale * fract * (adj.get_upper() - adj.get_lower()));
251         }
252
253         return true;
254 }
255
256 void
257 PixScroller::adjustment_changed ()
258 {
259         int y = (int) rint ((overall_height - sliderrect.get_height()) * (adj.get_upper() - adj.get_value()));
260
261         if (y != sliderrect.get_y()) {
262                 sliderrect.set_y(y);
263                 queue_draw ();
264         }
265 }