81f856c2a48e6ee48f16de39612b6fc51c3292ad
[ardour.git] / libs / gtkmm2ext / motionfeedback.cc
1 /*
2     Copyright (C) 1998-99 Paul Barton-Davis
3     This program is free software; you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation; either version 2 of the License, or
6     (at your option) any later version.
7
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12
13     You should have received a copy of the GNU General Public License
14     along with this program; if not, write to the Free Software
15     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
17     $Id: motionfeedback.cc,v 1.5 2004/03/01 03:44:19 pauld Exp $
18 */
19
20 #include <iostream>
21 #include <cmath>
22 #include <unistd.h>
23 #include <stdio.h> /* for snprintf, grrr */
24
25 #include <gdk/gdkkeysyms.h>
26 #include <gtkmm.h>
27
28 #include "gtkmm2ext/motionfeedback.h"
29 #include "gtkmm2ext/keyboard.h"
30 #include "gtkmm2ext/prolooks-helpers.h"
31
32 using namespace Gtk;
33 using namespace Gtkmm2ext;
34 using namespace sigc;
35
36 MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix,
37                                 Type t,
38                                 const char *widget_name, 
39                                 Adjustment *adj,
40                                 bool with_numeric_display, int subw, int subh) 
41         : type (t)
42         , value_packer (0)
43         , value (0)
44         , pixbuf (pix)
45         , subwidth (subw)
46         , subheight (subh)
47 {
48         char value_name[1024];
49
50         if (adj == NULL) {
51             i_own_my_adjustment = true;
52             set_adjustment (new Adjustment (0, 0, 10000, 1, 10, 0));
53         } else {
54             i_own_my_adjustment = false;
55             set_adjustment (adj);
56         }
57
58         default_value = adjustment->get_value();
59
60         HBox* hpacker = manage (new HBox);
61         hpacker->pack_start (pixwin, true, false);
62         hpacker->show ();
63         pack_start (*hpacker, false, false);
64         pixwin.show ();
65
66         if (with_numeric_display) {
67
68                 value_packer = new HBox;
69                 value = new SpinButton (*adjustment);
70                 value_packer->pack_start (*value, false, false);
71
72                 if (step_inc < 1) {
73                         value->set_digits (abs ((int) ceil (log10 (step_inc))));
74                 }
75                 
76                 pack_start (*value_packer, false, false);
77
78                 if (widget_name) {
79                         snprintf (value_name, sizeof(value_name), "%sValue", widget_name);
80                         value->set_name (value_name);
81                 }
82
83                 value->show ();
84         }
85
86         adjustment->signal_value_changed().connect (mem_fun (*this, &MotionFeedback::adjustment_changed));
87
88         pixwin.set_events (Gdk::BUTTON_PRESS_MASK|
89                            Gdk::BUTTON_RELEASE_MASK|
90                            Gdk::POINTER_MOTION_MASK|
91                            Gdk::ENTER_NOTIFY_MASK|
92                            Gdk::LEAVE_NOTIFY_MASK|
93                            Gdk::SCROLL_MASK|
94                            Gdk::KEY_PRESS_MASK|
95                            Gdk::KEY_RELEASE_MASK);
96
97         pixwin.set_flags (CAN_FOCUS);
98
99         /* Proxy all important events on the pixwin to ourselves */
100
101         pixwin.signal_button_press_event().connect(mem_fun (*this,&MotionFeedback::pixwin_button_press_event));
102         pixwin.signal_button_release_event().connect(mem_fun (*this,&MotionFeedback::pixwin_button_release_event));
103         pixwin.signal_motion_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_motion_notify_event));
104         pixwin.signal_enter_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_enter_notify_event));
105         pixwin.signal_leave_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_leave_notify_event));
106         pixwin.signal_key_press_event().connect(mem_fun (*this,&MotionFeedback::pixwin_key_press_event));
107         pixwin.signal_scroll_event().connect(mem_fun (*this,&MotionFeedback::pixwin_scroll_event));
108         pixwin.signal_expose_event().connect(mem_fun (*this,&MotionFeedback::pixwin_expose_event), true);
109         pixwin.signal_size_request().connect(mem_fun (*this,&MotionFeedback::pixwin_size_request));
110         pixwin.signal_realize().connect(mem_fun (*this,&MotionFeedback::pixwin_realized));
111 }
112
113 MotionFeedback::~MotionFeedback()
114
115 {
116         if (i_own_my_adjustment) {
117                 delete adjustment;
118         }
119
120         delete value;
121         delete value_packer;
122 }
123
124 void
125 MotionFeedback::set_adjustment (Adjustment *adj)
126 {
127         adjustment = adj;
128
129         if (value) {
130                 value->set_adjustment (*adj);
131         }
132
133         _lower = adj->get_lower();
134         _upper = adj->get_upper();
135         _range = _upper - _lower;
136         step_inc = adj->get_step_increment();
137         page_inc = adj->get_page_increment();
138 }
139
140 bool
141 MotionFeedback::pixwin_button_press_event (GdkEventButton *ev) 
142
143         if (binding_proxy.button_press_handler (ev)) {
144                 return true;
145         }
146
147         switch (ev->button) {
148         case 2:
149                 return FALSE;  /* XXX why ? */
150
151         case 1:
152                 grab_is_fine = false;
153                 break;
154         case 3:
155                 grab_is_fine = true;
156                 break;
157         }
158
159         gtk_grab_add(GTK_WIDGET(pixwin.gobj()));
160         grabbed_y = ev->y_root;
161         grabbed_x = ev->x_root;
162
163         /* XXX should we return TRUE ? */
164
165         return FALSE;
166 }
167
168 bool
169 MotionFeedback::pixwin_button_release_event (GdkEventButton *ev) 
170
171         switch (ev->button) {
172         case 1:
173                 if (pixwin.has_grab()) {
174                         if (!grab_is_fine) {
175                                 gtk_grab_remove
176                                         (GTK_WIDGET(pixwin.gobj()));
177                         }
178                 }
179                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
180                         /* shift click back to the default */
181                         adjustment->set_value (default_value);
182                         return true;
183                 }
184                 break;
185                 
186         case 3:
187                 if (pixwin.has_grab()) {
188                         if (grab_is_fine) {
189                                 gtk_grab_remove
190                                         (GTK_WIDGET(pixwin.gobj()));
191                         }
192                 }
193                 break;
194         }
195
196         return VBox::on_button_release_event (ev); 
197 }
198
199 bool
200 MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev) 
201
202         gfloat multiplier;
203         gfloat x_delta;
204         gfloat y_delta;
205
206         if (!pixwin.has_grab()) {
207                 return VBox::on_motion_notify_event (ev); 
208         }
209
210         multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) *
211                 ((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) * 
212                 ((ev->state & Keyboard::PrimaryModifier) ? 2 : 1);
213
214
215         if (ev->state & Gdk::BUTTON1_MASK) {
216
217                 y_delta = grabbed_y - ev->y_root;
218                 grabbed_y = ev->y_root;
219                 
220                 x_delta = ev->x_root - grabbed_x;
221                 
222                 if (y_delta == 0) return TRUE;
223                 
224                 y_delta *= 1 + (x_delta/100);
225                 y_delta *= multiplier;
226                 y_delta /= 10;
227                 
228                 adjustment->set_value (adjustment->get_value() + 
229                                        ((grab_is_fine ? step_inc : page_inc) * y_delta));
230                 
231         } else if (ev->state & Gdk::BUTTON3_MASK) {
232
233                 double range = adjustment->get_upper() - adjustment->get_lower();
234                 double x = ev->x - subwidth/2;
235                 double y = - ev->y + subwidth/2;
236                 double angle = std::atan2 (y, x) / M_PI;
237                 
238                 if (angle < -0.5) {
239                         angle += 2.0;
240                 }
241                 
242                 angle = -(2.0/3.0) * (angle - 1.25);
243                 angle *= range;
244                 angle *= multiplier;
245                 angle += adjustment->get_lower();
246                 
247                 adjustment->set_value (angle);
248         }
249
250
251         return true;
252 }
253
254 bool
255 MotionFeedback::pixwin_enter_notify_event (GdkEventCrossing *ev) 
256 {
257         pixwin.grab_focus();
258         return false;
259 }
260
261 bool
262 MotionFeedback::pixwin_leave_notify_event (GdkEventCrossing *ev) 
263 {
264         pixwin.unset_flags (HAS_FOCUS);
265         return false;
266 }
267
268 bool
269 MotionFeedback::pixwin_key_press_event (GdkEventKey *ev) 
270 {
271         bool retval = false;
272         gfloat curval;
273         gfloat multiplier;
274
275         multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) *
276                 ((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) * 
277                 ((ev->state & Keyboard::PrimaryModifier) ? 2 : 1);
278
279         switch (ev->keyval) {
280         case GDK_Page_Up:
281                 retval = true;
282                 curval = adjustment->get_value();
283                 adjustment->set_value (curval + (multiplier * page_inc));
284                 break;
285
286         case GDK_Page_Down:
287                 retval = true;
288                 curval = adjustment->get_value();
289                 adjustment->set_value (curval - (multiplier * page_inc));
290                 break;
291
292         case GDK_Up:
293                 retval = true;
294                 curval = adjustment->get_value();
295                 adjustment->set_value (curval + (multiplier * step_inc));
296                 break;
297
298         case GDK_Down:
299                 retval = true;
300                 curval = adjustment->get_value();
301                 adjustment->set_value (curval - (multiplier * step_inc));
302                 break;
303
304         case GDK_Home:
305                 retval = true;
306                 adjustment->set_value (_lower);
307                 break;
308
309         case GDK_End:
310                 retval = true;
311                 adjustment->set_value (_upper);
312                 break;
313         }
314         
315         return retval;
316 }
317
318 void
319 MotionFeedback::adjustment_changed ()
320 {
321         pixwin.queue_draw ();
322 }
323
324 void
325 MotionFeedback::core_draw (cairo_t* cr, int phase, double radius, double x, double y)
326 {
327         double width;
328         double height;
329         double xc;
330         double yc;
331         double start_angle;
332         double end_angle;
333         double value_angle;
334         double value;
335         double value_x;
336         double value_y;
337         double start_angle_x;
338         double start_angle_y;
339         double end_angle_x;
340         double end_angle_y;
341         double progress_width;
342         double progress_radius;
343         double progress_radius_inner;
344         double progress_radius_outer;
345         double knob_disc_radius;
346         cairo_pattern_t* pattern;
347         double progress_rim_width;
348         cairo_pattern_t* progress_shine;
349         double degrees;
350         cairo_pattern_t* knob_ripples;
351
352         g_return_if_fail (cr != NULL);
353
354         cairo_set_source_rgba (cr, 0.75, 0.75, 0.75, (double) 0);
355         cairo_rectangle (cr, (double) 0, (double) 0, subwidth, subheight);
356         cairo_fill (cr);
357
358         width = 105.0;
359         height = 105.0;
360         xc = width / 2.0;
361         yc = height / 2.0;
362         start_angle = 0.0;
363         end_angle = 0.0;
364         value_angle = 0.0;
365         value = (phase * 1.0) / (65 - 1);
366
367         start_angle = ((180 - 65) * G_PI) / 180;
368         end_angle = ((360 + 65) * G_PI) / 180;
369
370         value_angle = start_angle + (value * (end_angle - start_angle));
371         value_x = cos (value_angle);
372         value_y = sin (value_angle);
373         start_angle_x = cos (start_angle);
374         start_angle_y = sin (start_angle);
375         end_angle_x = cos (end_angle);
376         end_angle_y = sin (end_angle);
377         cairo_save (cr);
378         //cairo_translate (cr, x, (double) 0);
379         cairo_scale (cr, (2.0 * radius) / width, (2.0 * radius) / height);
380         //cairo_translate (cr, -xc, (double) 0);
381
382         pattern = prolooks_create_gradient_str ((double) 32, (double) 16, (double) 75, (double) 16, "#d4c8b9", "#ae977b", 1.0, 1.0);
383         cairo_set_source (cr, pattern);
384         cairo_pattern_destroy (pattern);
385         cairo_set_line_width (cr, 2.0);
386         cairo_arc (cr, xc, yc, 31.5, 0.0, 2 * G_PI);
387         cairo_stroke (cr);
388
389         progress_width = 10.0;
390         progress_radius = 40.0;
391         progress_radius_inner = progress_radius - (progress_width / 2.0);
392         progress_radius_outer = progress_radius + (progress_width / 2.0);
393         knob_disc_radius = progress_radius_inner - 5.0;
394
395         pattern = prolooks_create_gradient_str ((double) 20, (double) 20, (double) 89, (double) 87, "#2f2f4c", "#090a0d", 1.0, 1.0);
396         cairo_set_source (cr, pattern);
397         cairo_pattern_destroy (pattern);
398         cairo_set_line_width (cr, progress_width);
399         cairo_arc (cr, xc, yc, progress_radius, start_angle, end_angle);
400         cairo_stroke (cr);
401
402         pattern = prolooks_create_gradient ((double) 20, (double) 20, (double) 89, (double) 87, &lamp_bright, &lamp_dark, 1.0, 1.0);
403         cairo_set_source (cr, pattern);
404         cairo_pattern_destroy (pattern);
405         cairo_set_line_width (cr, progress_width);
406         cairo_arc (cr, xc, yc, progress_radius, start_angle, value_angle);
407         cairo_stroke (cr);
408
409         cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
410         progress_rim_width = 2.0;
411         cairo_set_line_width (cr, progress_rim_width);
412         pattern = prolooks_create_gradient_str ((double) 18, (double) 79, (double) 35, (double) 79, "#dfd5c9", "#dfd5c9", 1.0, 0.0);
413         cairo_set_source (cr, pattern);
414         cairo_pattern_destroy (pattern);
415         cairo_move_to (cr, xc + (progress_radius_outer * start_angle_x), yc + (progress_radius_outer * start_angle_y));
416         cairo_line_to (cr, xc + (progress_radius_inner * start_angle_x), yc + (progress_radius_inner * start_angle_y));
417         cairo_stroke (cr);
418
419         prolooks_set_source_color_string (cr, "#000000", 1.0);
420         cairo_move_to (cr, xc + (progress_radius_outer * end_angle_x), yc + (progress_radius_outer * end_angle_y));
421         cairo_line_to (cr, xc + (progress_radius_inner * end_angle_x), yc + (progress_radius_inner * end_angle_y));
422         cairo_stroke (cr);
423
424         // pattern = prolooks_create_gradient_str ((double) 95, (double) 6, (double) 5, (double) 44, "#dfd5c9", "#b0a090", 1.0, 1.0);
425         pattern = prolooks_create_gradient_str ((double) 95, (double) 6, (double) 5, (double) 44, "#000000", "#000000", 1.0, 1.0);
426         cairo_set_source (cr, pattern);
427         cairo_pattern_destroy (pattern);
428         cairo_arc (cr, xc, yc, progress_radius_outer, start_angle, end_angle);
429         cairo_stroke (cr);
430
431         cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
432         pattern = prolooks_create_gradient ((double) 20, (double) 20, (double) 89, (double) 87, &lamp_bright, &lamp_dark, 0.25, 0.25);
433         cairo_set_source (cr, pattern);
434         cairo_pattern_destroy (pattern);
435         cairo_set_line_width (cr, progress_width);
436         cairo_arc (cr, xc, yc, progress_radius, start_angle, value_angle + (G_PI / 180.0));
437         cairo_stroke (cr);
438
439         progress_shine = prolooks_create_gradient_str ((double) 89, (double) 73, (double) 34, (double) 16, "#ffffff", "#ffffff", 0.3, 0.04);
440         cairo_pattern_add_color_stop_rgba (progress_shine, 0.5, 1.0, 1.0, 1.0, 0.0);
441         if (subwidth > 50) {
442                 cairo_pattern_add_color_stop_rgba (progress_shine, 0.75, 1.0, 1.0, 1.0, 0.3);
443         } else {
444                 cairo_pattern_add_color_stop_rgba (progress_shine, 0.75, 1.0, 1.0, 1.0, 0.2);
445         }
446         cairo_set_source (cr, progress_shine);
447         cairo_set_line_width (cr, progress_width);
448         cairo_arc (cr, xc, yc, progress_radius, start_angle, end_angle);
449         cairo_stroke (cr);
450         cairo_pattern_destroy (progress_shine);
451
452         cairo_set_line_width (cr, 1.0);
453         cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
454         cairo_arc (cr, xc, yc, progress_radius_inner, (double) 0, 2 * G_PI);
455         pattern = prolooks_create_gradient_str ((double) 35, (double) 31, (double) 75, (double) 72, "#68625c", "#44494b", 1.0, 1.0);
456         cairo_set_source (cr, pattern);
457         cairo_pattern_destroy (pattern);
458         cairo_fill (cr);
459         cairo_set_source_rgb (cr, (double) 0, (double) 0, (double) 0);
460         cairo_arc (cr, xc, yc, progress_radius_inner, (double) 0, 2 * G_PI);
461         cairo_stroke (cr);
462
463         pattern = prolooks_create_gradient_str ((double) 42, (double) 34, (double) 68, (double) 70, "#e7ecef", "#9cafb8", 1.0, 1.0);
464         cairo_set_source (cr, pattern);
465         cairo_pattern_destroy (pattern);
466         cairo_arc (cr, xc, yc, knob_disc_radius, (double) 0, 2 * G_PI);
467         cairo_fill (cr);
468
469         cairo_set_line_width (cr, 2.0);
470         degrees = G_PI / 180.0;
471         pattern = prolooks_create_gradient_str ((double) 38, (double) 34, (double) 70, (double) 68, "#ffffff", "#caddf2", 0.2, 0.2);
472         cairo_set_source (cr, pattern);
473         cairo_pattern_destroy (pattern);
474         cairo_move_to (cr, xc, yc);
475         cairo_arc (cr, xc, yc, knob_disc_radius - 1, (-154) * degrees, (-120) * degrees);
476         cairo_move_to (cr, xc, yc);
477         cairo_arc (cr, xc, yc, knob_disc_radius - 1, (G_PI / 2.0) - (60 * degrees), (G_PI / 2.0) - (29 * degrees));
478         cairo_fill (cr);
479
480         pattern = prolooks_create_gradient_str ((double) 50, (double) 40, (double) 62, (double) 60, "#a1adb6", "#47535c", 0.07, 0.15);
481         cairo_set_source (cr, pattern);
482         cairo_pattern_destroy (pattern);
483         cairo_move_to (cr, xc, yc);
484         cairo_arc (cr, xc, yc, knob_disc_radius - 1, (-67) * degrees, (-27) * degrees);
485         cairo_move_to (cr, xc, yc);
486         cairo_arc (cr, xc, yc, knob_disc_radius - 1, G_PI - (67 * degrees), G_PI - (27 * degrees));
487         cairo_fill (cr);
488
489         knob_ripples = cairo_pattern_create_radial (xc, yc, (double) 0, xc, yc, (double) 4);
490         prolooks_add_color_stop_str (knob_ripples, 0.0, "#e7ecef", 0.05);
491         prolooks_add_color_stop_str (knob_ripples, 0.5, "#58717d", 0.05);
492         prolooks_add_color_stop_str (knob_ripples, 0.75, "#d1d9de", 0.05);
493         prolooks_add_color_stop_str (knob_ripples, 1.0, "#5d7682", 0.05);
494         cairo_pattern_set_extend (knob_ripples, CAIRO_EXTEND_REPEAT);
495         cairo_set_line_width (cr, 0.0);
496         cairo_set_source (cr, knob_ripples);
497         cairo_arc (cr, xc, yc, knob_disc_radius, (double) 0, 2 * G_PI);
498         cairo_fill (cr);
499
500         cairo_save (cr);
501         cairo_translate (cr, xc + (knob_disc_radius * value_x), yc + (knob_disc_radius * value_y));
502         cairo_rotate (cr, value_angle - G_PI);
503         cairo_set_source (cr, pattern = prolooks_create_gradient_str ((double) 16, (double) (-2), (double) 9, (double) 13, "#e7ecef", "#9cafb8", 0.8, 0.8));
504         cairo_pattern_destroy (pattern);
505         cairo_move_to (cr, (double) 0, (double) 4);
506         cairo_line_to (cr, (double) 17, (double) 4);
507         cairo_curve_to (cr, (double) 19, (double) 4, (double) 21, (double) 2, (double) 21, (double) 0);
508         cairo_curve_to (cr, (double) 21, (double) (-2), (double) 19, (double) (-4), (double) 17, (double) (-4));
509         cairo_line_to (cr, (double) 0, (double) (-4));
510         cairo_close_path (cr);
511         cairo_fill (cr);
512
513         pattern = prolooks_create_gradient_str ((double) 9, (double) (-2), (double) 9, (double) 2, "#68625c", "#44494b", 1.0, 1.0);
514         cairo_set_source (cr, pattern);
515         cairo_pattern_destroy (pattern);
516         cairo_move_to (cr, (double) 0, (double) 2);
517         cairo_line_to (cr, (double) 16, (double) 2);
518         cairo_curve_to (cr, (double) 17, (double) 2, (double) 18, (double) 1, (double) 18, (double) 0);
519         cairo_curve_to (cr, (double) 18, (double) (-1), (double) 17, (double) (-2), (double) 16, (double) (-2));
520         cairo_line_to (cr, (double) 0, (double) (-2));
521         cairo_close_path (cr);
522         cairo_fill (cr);
523
524         cairo_restore (cr);
525         cairo_set_line_width (cr, 2.0);
526         pattern = prolooks_create_gradient_str ((double) 38, (double) 32, (double) 70, (double) 67, "#3d3d3d", "#000000", 1.0, 1.0);
527         cairo_set_source (cr, pattern);
528         cairo_pattern_destroy (pattern);
529         cairo_arc (cr, xc, yc, knob_disc_radius, (double) 0, 2 * G_PI);
530         cairo_stroke (cr);
531         cairo_restore (cr);
532
533         cairo_pattern_destroy (knob_ripples);
534 }
535
536 bool
537 MotionFeedback::pixwin_expose_event (GdkEventExpose* ev)
538 {
539         // GtkWidget* widget = GTK_WIDGET(pixwin.gobj());
540         GdkWindow *window = pixwin.get_window()->gobj();
541         GtkAdjustment* adj = adjustment->gobj();
542
543         int phase = (int)((adj->value - adj->lower) * 64 / 
544                           (adj->upper - adj->lower));
545
546         // skip middle phase except for true middle value
547
548         if (type == Rotary && phase == 32) {
549                 double pt = (adj->value - adj->lower) * 2.0 / 
550                         (adj->upper - adj->lower) - 1.0;
551                 if (pt < 0)
552                         phase = 31;
553                 if (pt > 0)
554                         phase = 33;
555         }
556
557         // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
558
559         if (type == Endless && !(phase % 16)) {
560                 if (phase == 64) {
561                         phase = 0;
562                 }
563
564                 double nom = adj->lower + phase * (adj->upper - adj->lower) / 64.0;
565                 double diff = (adj->value - nom) / (adj->upper - adj->lower);
566
567                 if (diff > 0.0001)
568                         phase = (phase + 1) % 64;
569                 if (diff < -0.0001)
570                         phase = (phase + 63) % 64;
571         }
572
573 #if 1
574         cairo_t* cr = gdk_cairo_create (GDK_DRAWABLE (window));
575
576         gdk_cairo_rectangle (cr, &ev->area);
577         cairo_clip (cr);
578
579         core_draw (cr, phase, subheight/2, subwidth/2, subheight/2);
580         cairo_destroy (cr);
581
582 #else
583         
584         gdk_draw_pixbuf (GDK_DRAWABLE(window), widget->style->fg_gc[0], 
585                          pixbuf->gobj(), 
586                          phase * subwidth, type * subheight, 
587                          0, 0, subwidth, subheight, GDK_RGB_DITHER_NORMAL, 0, 0);
588 #endif  
589
590         return true;
591 }
592
593 bool
594 MotionFeedback::pixwin_scroll_event (GdkEventScroll* ev)
595 {
596         double scale;
597
598         if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
599                 scale = 0.01;
600         } else if (ev->state & Keyboard::PrimaryModifier) {
601                 scale = 0.1;
602         } else {
603                 scale = 1.0;
604         }
605
606         switch (ev->direction) {
607         case GDK_SCROLL_UP:
608         case GDK_SCROLL_RIGHT:
609                 adjustment->set_value (adjustment->get_value() + (scale * adjustment->get_step_increment()));
610                 break;
611
612         case GDK_SCROLL_DOWN:
613         case GDK_SCROLL_LEFT:
614                 adjustment->set_value (adjustment->get_value() - (scale * adjustment->get_step_increment()));
615                 break;
616         }
617
618         return true;
619 }
620
621 void
622 MotionFeedback::pixwin_size_request (GtkRequisition* req)
623 {
624         req->width = subwidth;
625         req->height = subheight;
626 }
627
628 void
629 MotionFeedback::pixwin_realized ()
630 {
631         set_lamp_color (Gdk::Color ("#b9feff"));
632 }
633
634 void
635 MotionFeedback::set_lamp_color (const Gdk::Color& c)
636 {
637         GdkColor col2 = {0,0,0,0};
638         GdkColor col3 = {0,0,0,0};
639
640         _lamp_color = c;
641         lamp_hsv = prolooks_hsv_new_for_gdk_color (_lamp_color.gobj());
642         lamp_bright = (prolooks_hsv_to_gdk_color (lamp_hsv, &col2), col2);
643         prolooks_hsv_set_saturation (lamp_hsv, 0.66);
644         prolooks_hsv_set_value (lamp_hsv, 0.67);
645         lamp_dark = (prolooks_hsv_to_gdk_color (lamp_hsv, &col3), col3);
646 }