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.
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.
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.
17 $Id: motionfeedback.cc,v 1.5 2004/03/01 03:44:19 pauld Exp $
23 #include <stdio.h> /* for snprintf, grrr */
25 #include <gdk/gdkkeysyms.h>
28 #include "gtkmm2ext/motionfeedback.h"
29 #include "gtkmm2ext/keyboard.h"
32 using namespace Gtkmm2ext;
35 MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix,
37 const char *widget_name,
39 bool with_numeric_display, int subw, int subh)
47 char value_name[1024];
50 i_own_my_adjustment = true;
51 set_adjustment (new Adjustment (0, 0, 10000, 1, 10, 0));
53 i_own_my_adjustment = false;
57 default_value = adjustment->get_value();
59 HBox* hpacker = manage (new HBox);
60 hpacker->pack_start (pixwin, true, false);
62 pack_start (*hpacker, false, false);
65 if (with_numeric_display) {
67 value_packer = new HBox;
68 value = new SpinButton (*adjustment);
69 value_packer->pack_start (*value, false, false);
72 value->set_digits (abs ((int) ceil (log10 (step_inc))));
75 pack_start (*value_packer, false, false);
78 snprintf (value_name, sizeof(value_name), "%sValue", widget_name);
79 value->set_name (value_name);
85 adjustment->signal_value_changed().connect (mem_fun (*this, &MotionFeedback::adjustment_changed));
87 pixwin.set_events (Gdk::BUTTON_PRESS_MASK|
88 Gdk::BUTTON_RELEASE_MASK|
89 Gdk::POINTER_MOTION_MASK|
90 Gdk::ENTER_NOTIFY_MASK|
91 Gdk::LEAVE_NOTIFY_MASK|
94 Gdk::KEY_RELEASE_MASK);
96 pixwin.set_flags (CAN_FOCUS);
98 /* Proxy all important events on the pixwin to ourselves */
100 pixwin.signal_button_press_event().connect(mem_fun (*this,&MotionFeedback::pixwin_button_press_event));
101 pixwin.signal_button_release_event().connect(mem_fun (*this,&MotionFeedback::pixwin_button_release_event));
102 pixwin.signal_motion_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_motion_notify_event));
103 pixwin.signal_enter_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_enter_notify_event));
104 pixwin.signal_leave_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_leave_notify_event));
105 pixwin.signal_key_press_event().connect(mem_fun (*this,&MotionFeedback::pixwin_key_press_event));
106 pixwin.signal_scroll_event().connect(mem_fun (*this,&MotionFeedback::pixwin_scroll_event));
107 pixwin.signal_expose_event().connect(mem_fun (*this,&MotionFeedback::pixwin_expose_event), true);
108 pixwin.signal_size_request().connect(mem_fun (*this,&MotionFeedback::pixwin_size_request));
111 MotionFeedback::~MotionFeedback()
114 if (i_own_my_adjustment) {
123 MotionFeedback::set_adjustment (Adjustment *adj)
128 value->set_adjustment (*adj);
131 _lower = adj->get_lower();
132 _upper = adj->get_upper();
133 _range = _upper - _lower;
134 step_inc = adj->get_step_increment();
135 page_inc = adj->get_page_increment();
139 MotionFeedback::pixwin_button_press_event (GdkEventButton *ev)
141 switch (ev->button) {
143 return FALSE; /* XXX why ? */
146 grab_is_fine = false;
153 gtk_grab_add(GTK_WIDGET(pixwin.gobj()));
154 grabbed_y = ev->y_root;
155 grabbed_x = ev->x_root;
157 /* XXX should we return TRUE ? */
163 MotionFeedback::pixwin_button_release_event (GdkEventButton *ev)
165 switch (ev->button) {
167 if (pixwin.has_grab()) {
170 (GTK_WIDGET(pixwin.gobj()));
173 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
174 /* shift click back to the default */
175 adjustment->set_value (default_value);
181 if (pixwin.has_grab()) {
184 (GTK_WIDGET(pixwin.gobj()));
190 return VBox::on_button_release_event (ev);
194 MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev)
200 if(!pixwin.has_grab()) {
201 return VBox::on_motion_notify_event (ev);
204 multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) *
205 ((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) *
206 ((ev->state & Keyboard::PrimaryModifier) ? 2 : 1);
208 y_delta = grabbed_y - ev->y_root;
209 grabbed_y = ev->y_root;
211 x_delta = ev->x_root - grabbed_x;
213 if (y_delta == 0) return TRUE;
215 y_delta *= 1 + (x_delta/100);
216 y_delta *= multiplier;
219 adjustment->set_value (adjustment->get_value() +
220 ((grab_is_fine ? step_inc : page_inc) * y_delta));
226 MotionFeedback::pixwin_enter_notify_event (GdkEventCrossing *ev)
233 MotionFeedback::pixwin_leave_notify_event (GdkEventCrossing *ev)
235 pixwin.unset_flags (HAS_FOCUS);
240 MotionFeedback::pixwin_key_press_event (GdkEventKey *ev)
246 multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) *
247 ((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) *
248 ((ev->state & Keyboard::PrimaryModifier) ? 2 : 1);
250 switch (ev->keyval) {
253 curval = adjustment->get_value();
254 adjustment->set_value (curval + (multiplier * page_inc));
259 curval = adjustment->get_value();
260 adjustment->set_value (curval - (multiplier * page_inc));
265 curval = adjustment->get_value();
266 adjustment->set_value (curval + (multiplier * step_inc));
271 curval = adjustment->get_value();
272 adjustment->set_value (curval - (multiplier * step_inc));
277 adjustment->set_value (_lower);
282 adjustment->set_value (_upper);
290 MotionFeedback::adjustment_changed ()
292 pixwin.queue_draw ();
296 MotionFeedback::pixwin_expose_event (GdkEventExpose* ev)
298 GtkWidget* widget = GTK_WIDGET(pixwin.gobj());
299 GdkWindow *window = pixwin.get_window()->gobj();
300 GtkAdjustment* adj = adjustment->gobj();
302 int phase = (int)((adj->value - adj->lower) * 64 /
303 (adj->upper - adj->lower));
305 // skip middle phase except for true middle value
307 if (type == Rotary && phase == 32) {
308 double pt = (adj->value - adj->lower) * 2.0 /
309 (adj->upper - adj->lower) - 1.0;
316 // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
318 if (type == Endless && !(phase % 16)) {
323 double nom = adj->lower + phase * (adj->upper - adj->lower) / 64.0;
324 double diff = (adj->value - nom) / (adj->upper - adj->lower);
327 phase = (phase + 1) % 64;
329 phase = (phase + 63) % 64;
332 gdk_draw_pixbuf (GDK_DRAWABLE(window), widget->style->fg_gc[0],
334 phase * subwidth, type * subheight,
335 0, 0, subwidth, subheight, GDK_RGB_DITHER_NORMAL, 0, 0);
341 MotionFeedback::pixwin_scroll_event (GdkEventScroll* ev)
345 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
347 } else if (ev->state & Keyboard::PrimaryModifier) {
353 switch (ev->direction) {
355 case GDK_SCROLL_RIGHT:
356 adjustment->set_value (adjustment->get_value() + (scale * adjustment->get_step_increment()));
359 case GDK_SCROLL_DOWN:
360 case GDK_SCROLL_LEFT:
361 adjustment->set_value (adjustment->get_value() - (scale * adjustment->get_step_increment()));
369 MotionFeedback::pixwin_size_request (GtkRequisition* req)
371 req->width = subwidth;
372 req->height = subheight;