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