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