1 /*
2 * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <stdio.h>
20 #include <math.h>
21 #include <unistd.h>
22 #include <string.h>
23
24 #include "GxRegler.h"
25 #include "GxControlParameter.h"
26 #include <gdk/gdkkeysyms.h>
27
28 #include "regler_marshal.cc"
29
30
31 #define P_(s) (s) // FIXME -> gettext
32 #define I_(s) (s) // FIXME -> gettext
33
34 /****************************************************************
35 ** GxRegler
36 */
37
38 struct _GxReglerPrivate
39 {
40 GtkRequisition value_req;
41 gdouble last_step;
42 int round_digits;
43 GtkAdjustment *adjustment;
44 gchar *var_id;
45 GtkLabel *label;
46 gboolean show_value:1;
47 GtkPositionType value_position:2;
48 gdouble value_xalign;
49 PangoLayout *value_layout;
50 };
51
52 enum {
53 VALUE_ENTRY,
54 FORMAT_VALUE,
55 INPUT_VALUE,
56 LAST_SIGNAL
57 };
58
59 enum {
60 PROP_VAR_ID = 1,
61 PROP_SHOW_VALUE,
62 PROP_VALUE_POSITION,
63 PROP_VALUE_XALIGN,
64 PROP_LABEL_REF,
65 PROP_DIGITS,
66 };
67
68 static guint signals[LAST_SIGNAL];
69
70 static void gx_regler_class_init(GxReglerClass *klass);
71 static void gx_regler_init(GxRegler *regler);
72 static void gx_control_parameter_interface_init (GxControlParameterIface *iface);
73 static void gx_regler_finalize(GObject*);
74 static void gx_regler_destroy(GtkWidget *object);
75 static void gx_regler_set_property(
76 GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
77 static void gx_regler_get_property(
78 GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
79 static gboolean gx_regler_button_release (GtkWidget *widget, GdkEventButton *event);
80 static gboolean gx_regler_scroll (GtkWidget *widget, GdkEventScroll *event);
81 static gboolean gx_regler_change_value(GtkRange *range, GtkScrollType scroll, gdouble value);
82 static void gx_regler_value_changed(GtkRange *range);
83 static gboolean gx_regler_value_entry(GxRegler *regler, GdkRectangle *rect, GdkEventButton *event);
84 static void gx_regler_change_adjustment(GxRegler *regler, GtkAdjustment *adjustment);
85 static void gx_regler_adjustment_notified(GObject *gobject, GParamSpec *pspec);
86 static void gx_regler_move_slider(GtkRange *range, GtkScrollType scroll);
87
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(GxRegler,gx_regler,GTK_TYPE_RANGE,G_ADD_PRIVATE (GxRegler)G_IMPLEMENT_INTERFACE (GX_TYPE_CONTROL_PARAMETER,gx_control_parameter_interface_init))88 G_DEFINE_ABSTRACT_TYPE_WITH_CODE(GxRegler, gx_regler, GTK_TYPE_RANGE,
89 G_ADD_PRIVATE(GxRegler)
90 G_IMPLEMENT_INTERFACE(GX_TYPE_CONTROL_PARAMETER,
91 gx_control_parameter_interface_init))
92
93 static void gx_regler_value_changed(GtkRange *range)
94 {
95 gtk_widget_queue_draw(GTK_WIDGET(range));
96 }
97
98
99 static void
gx_regler_cp_configure(GxControlParameter * self,const gchar * group,const gchar * name,gdouble lower,gdouble upper,gdouble step)100 gx_regler_cp_configure(GxControlParameter *self, const gchar* group, const gchar *name, gdouble lower, gdouble upper, gdouble step)
101 {
102 g_return_if_fail(GX_IS_REGLER(self));
103 GxRegler *regler = GX_REGLER(self);
104 if (regler->priv->label) {
105 gtk_label_set_text(regler->priv->label, name);
106 }
107 GtkRange *range = GTK_RANGE(self);
108 gtk_range_set_range(range, lower, upper);
109 gtk_range_set_increments(range, step, 10*step);
110 }
111
112 static gdouble
gx_regler_cp_get_value(GxControlParameter * self)113 gx_regler_cp_get_value(GxControlParameter *self)
114 {
115 return gtk_range_get_value(GTK_RANGE(self));
116 }
117
118 static void
gx_regler_cp_set_value(GxControlParameter * self,gdouble value)119 gx_regler_cp_set_value(GxControlParameter *self, gdouble value)
120 {
121 gtk_range_set_value(GTK_RANGE(self), value);
122 }
123
124 static void
gx_control_parameter_interface_init(GxControlParameterIface * iface)125 gx_control_parameter_interface_init(GxControlParameterIface *iface)
126 {
127 iface->cp_configure = gx_regler_cp_configure;
128 iface->cp_set_value = gx_regler_cp_set_value;
129 iface->cp_get_value = gx_regler_cp_get_value;
130 }
131
gx_boolean_handled_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer dummy)132 static gboolean gx_boolean_handled_accumulator(
133 GSignalInvocationHint *ihint, GValue *return_accu,
134 const GValue *handler_return, gpointer dummy)
135 {
136 gboolean continue_emission;
137 gboolean signal_handled;
138
139 signal_handled = g_value_get_boolean (handler_return);
140 g_value_set_boolean (return_accu, signal_handled);
141 continue_emission = !signal_handled;
142
143 return continue_emission;
144 }
145
single_string_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer dummy)146 static gboolean single_string_accumulator(
147 GSignalInvocationHint *ihint, GValue *return_accu,
148 const GValue *handler_return, gpointer dummy)
149 {
150 gboolean continue_emission;
151 const gchar *str;
152
153 str = g_value_get_string(handler_return);
154 g_value_set_string(return_accu, str);
155 continue_emission = str == NULL;
156 return continue_emission;
157 }
158
159 #define add_slider_binding(binding_set, keyval, mask, scroll) \
160 gtk_binding_entry_add_signal (binding_set, keyval, mask, \
161 I_("move-slider"), 1, \
162 GTK_TYPE_SCROLL_TYPE, scroll)
163
gx_regler_class_init(GxReglerClass * klass)164 static void gx_regler_class_init(GxReglerClass *klass)
165 {
166 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
167 GtkRangeClass *range_class = (GtkRangeClass*)klass;
168 GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
169 GtkBindingSet *binding_set;
170
171 gobject_class->finalize = gx_regler_finalize;
172 gobject_class->set_property = gx_regler_set_property;
173 gobject_class->get_property = gx_regler_get_property;
174
175 widget_class->destroy = gx_regler_destroy;
176 widget_class->button_release_event = gx_regler_button_release;
177 widget_class->scroll_event = gx_regler_scroll;
178
179 range_class->value_changed = gx_regler_value_changed;
180 range_class->change_value = gx_regler_change_value;
181 range_class->move_slider = gx_regler_move_slider;
182
183 klass->value_entry = gx_regler_value_entry;
184
185 gtk_widget_class_set_css_name(widget_class, "gx-regler");
186
187 signals[VALUE_ENTRY] =
188 g_signal_new (I_("value-entry"),
189 G_OBJECT_CLASS_TYPE (klass),
190 G_SIGNAL_RUN_LAST,
191 G_STRUCT_OFFSET (GxReglerClass, value_entry),
192 gx_boolean_handled_accumulator, NULL,
193 marshal_BOOLEAN__BOXED_BOXED,
194 G_TYPE_BOOLEAN, 2, GDK_TYPE_RECTANGLE,
195 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
196
197 signals[FORMAT_VALUE] =
198 g_signal_new (I_("format-value"),
199 G_TYPE_FROM_CLASS (gobject_class),
200 G_SIGNAL_RUN_LAST,
201 G_STRUCT_OFFSET (GxReglerClass, format_value),
202 single_string_accumulator, NULL,
203 marshal_STRING__DOUBLE,
204 G_TYPE_STRING, 1,
205 G_TYPE_DOUBLE);
206
207 signals[INPUT_VALUE] =
208 g_signal_new (I_("input-value"),
209 G_TYPE_FROM_CLASS (gobject_class),
210 G_SIGNAL_RUN_LAST,
211 G_STRUCT_OFFSET (GxReglerClass, input_value),
212 NULL, NULL,
213 NULL,
214 G_TYPE_INT, 2,
215 G_TYPE_POINTER, //FIXME: GtkEntry*, gdouble*
216 G_TYPE_POINTER);
217
218 gtk_widget_class_install_style_property(
219 widget_class,
220 g_param_spec_int("border-radius",
221 P_("Border Radius"),
222 P_("The radius of the corners in pixels"),
223 0, 100, 0,
224 GParamFlags(G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS)));
225
226 gtk_widget_class_install_style_property(
227 widget_class,
228 g_param_spec_float("bevel",
229 P_("Bevel"),
230 P_("The bevel effect"),
231 -1.0, 1.0, 0.0,
232 GParamFlags(G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS)));
233
234 gtk_widget_class_install_style_property(
235 widget_class,
236 g_param_spec_boolean("show-value",
237 P_("show value"),
238 P_("display the value"),
239 TRUE,
240 GParamFlags(G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)));
241 gtk_widget_class_install_style_property(
242 widget_class,
243 g_param_spec_int("value-spacing",P_("Value spacing"),
244 P_("Distance of value display"),
245 0, 100, 5, GParamFlags(G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)));
246 gtk_widget_class_install_style_property(
247 widget_class,
248 g_param_spec_boxed("value-border",
249 P_("Value Spacing"),
250 P_("Extra space for value display"),
251 GTK_TYPE_BORDER,
252 GParamFlags(G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)));
253
254 g_object_class_install_property(
255 gobject_class, PROP_SHOW_VALUE,
256 g_param_spec_boolean("show-value",
257 P_("show value"),
258 P_("display the value"),
259 TRUE,
260 GParamFlags(G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS)));
261 g_object_class_install_property(
262 gobject_class, PROP_LABEL_REF,
263 g_param_spec_object("label-ref",
264 P_("Label ref"),
265 P_("GtkLabel for caption"),
266 GTK_TYPE_LABEL,
267 GParamFlags(G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS)));
268 g_object_class_install_property(
269 gobject_class, PROP_VALUE_POSITION,
270 g_param_spec_enum("value-position",
271 P_("Value Position"),
272 P_("The position of the value display"),
273 GTK_TYPE_POSITION_TYPE,
274 GTK_POS_BOTTOM,
275 GParamFlags(G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS)));
276 g_object_class_install_property(
277 gobject_class, PROP_VALUE_XALIGN,
278 g_param_spec_double("value-xalign",
279 P_("Value Alignment"),
280 P_("The horizontal position of the value (0..1)"),
281 0, 1, 0.5,
282 GParamFlags(G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS)));
283 g_object_class_install_property(
284 gobject_class, PROP_DIGITS,
285 g_param_spec_int("digits",
286 P_("Digits"),
287 P_("Number of digits for display"),
288 0, 10, 1,
289 GParamFlags(G_PARAM_READABLE|G_PARAM_STATIC_STRINGS)));
290 g_object_class_override_property(gobject_class, PROP_VAR_ID, "var-id");
291
292 binding_set = gtk_binding_set_by_class(klass);
293
294 add_slider_binding (binding_set, GDK_KEY_Left, (GdkModifierType)0,
295 GTK_SCROLL_STEP_LEFT);
296
297 add_slider_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
298 GTK_SCROLL_PAGE_LEFT);
299
300 add_slider_binding (binding_set, GDK_KEY_KP_Left, (GdkModifierType)0,
301 GTK_SCROLL_STEP_LEFT);
302
303 add_slider_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
304 GTK_SCROLL_PAGE_LEFT);
305
306 add_slider_binding (binding_set, GDK_KEY_Right, (GdkModifierType)0,
307 GTK_SCROLL_STEP_RIGHT);
308
309 add_slider_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
310 GTK_SCROLL_PAGE_RIGHT);
311
312 add_slider_binding (binding_set, GDK_KEY_KP_Right, (GdkModifierType)0,
313 GTK_SCROLL_STEP_RIGHT);
314
315 add_slider_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
316 GTK_SCROLL_PAGE_RIGHT);
317
318 add_slider_binding (binding_set, GDK_KEY_Up, (GdkModifierType)0,
319 GTK_SCROLL_STEP_UP);
320
321 add_slider_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK,
322 GTK_SCROLL_PAGE_UP);
323
324 add_slider_binding (binding_set, GDK_KEY_KP_Up, (GdkModifierType)0,
325 GTK_SCROLL_STEP_UP);
326
327 add_slider_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
328 GTK_SCROLL_PAGE_UP);
329
330 add_slider_binding (binding_set, GDK_KEY_Down, (GdkModifierType)0,
331 GTK_SCROLL_STEP_DOWN);
332
333 add_slider_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
334 GTK_SCROLL_PAGE_DOWN);
335
336 add_slider_binding (binding_set, GDK_KEY_KP_Down, (GdkModifierType)0,
337 GTK_SCROLL_STEP_DOWN);
338
339 add_slider_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
340 GTK_SCROLL_PAGE_DOWN);
341
342 add_slider_binding (binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK,
343 GTK_SCROLL_PAGE_LEFT);
344
345 add_slider_binding (binding_set, GDK_KEY_KP_Page_Up, GDK_CONTROL_MASK,
346 GTK_SCROLL_PAGE_LEFT);
347
348 add_slider_binding (binding_set, GDK_KEY_Page_Up, (GdkModifierType)0,
349 GTK_SCROLL_PAGE_UP);
350
351 add_slider_binding (binding_set, GDK_KEY_KP_Page_Up, (GdkModifierType)0,
352 GTK_SCROLL_PAGE_UP);
353
354 add_slider_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK,
355 GTK_SCROLL_PAGE_RIGHT);
356
357 add_slider_binding (binding_set, GDK_KEY_KP_Page_Down, GDK_CONTROL_MASK,
358 GTK_SCROLL_PAGE_RIGHT);
359
360 add_slider_binding (binding_set, GDK_KEY_Page_Down, (GdkModifierType)0,
361 GTK_SCROLL_PAGE_DOWN);
362
363 add_slider_binding (binding_set, GDK_KEY_KP_Page_Down, (GdkModifierType)0,
364 GTK_SCROLL_PAGE_DOWN);
365
366 /* Logical bindings (vs. visual bindings above) */
367
368 add_slider_binding (binding_set, GDK_KEY_plus, (GdkModifierType)0,
369 GTK_SCROLL_STEP_FORWARD);
370
371 add_slider_binding (binding_set, GDK_KEY_minus, (GdkModifierType)0,
372 GTK_SCROLL_STEP_BACKWARD);
373
374 add_slider_binding (binding_set, GDK_KEY_plus, GDK_CONTROL_MASK,
375 GTK_SCROLL_PAGE_FORWARD);
376
377 add_slider_binding (binding_set, GDK_KEY_minus, GDK_CONTROL_MASK,
378 GTK_SCROLL_PAGE_BACKWARD);
379
380
381 add_slider_binding (binding_set, GDK_KEY_KP_Add, (GdkModifierType)0,
382 GTK_SCROLL_STEP_FORWARD);
383
384 add_slider_binding (binding_set, GDK_KEY_KP_Subtract, (GdkModifierType)0,
385 GTK_SCROLL_STEP_BACKWARD);
386
387 add_slider_binding (binding_set, GDK_KEY_KP_Add, GDK_CONTROL_MASK,
388 GTK_SCROLL_PAGE_FORWARD);
389
390 add_slider_binding (binding_set, GDK_KEY_KP_Subtract, GDK_CONTROL_MASK,
391 GTK_SCROLL_PAGE_BACKWARD);
392
393
394 add_slider_binding (binding_set, GDK_KEY_Home, (GdkModifierType)0,
395 GTK_SCROLL_START);
396
397 add_slider_binding (binding_set, GDK_KEY_KP_Home, (GdkModifierType)0,
398 GTK_SCROLL_START);
399
400 add_slider_binding (binding_set, GDK_KEY_End, (GdkModifierType)0,
401 GTK_SCROLL_END);
402
403 add_slider_binding (binding_set, GDK_KEY_KP_End, (GdkModifierType)0,
404 GTK_SCROLL_END);
405 }
406
407
gx_regler_destroy(GtkWidget * object)408 static void gx_regler_destroy(GtkWidget *object)
409 {
410 GxRegler *regler = GX_REGLER(object);
411 if (regler->priv->label) {
412 g_object_unref(regler->priv->label);
413 regler->priv->label = NULL;
414 }
415 gx_regler_change_adjustment(regler, NULL);
416 g_signal_handlers_disconnect_by_func(
417 regler, (gpointer)gx_regler_adjustment_notified, NULL);
418 GTK_WIDGET_CLASS(gx_regler_parent_class)->destroy(object);
419 }
420
gx_regler_finalize(GObject * object)421 static void gx_regler_finalize(GObject *object)
422 {
423 GxRegler *regler = GX_REGLER(object);
424 g_free(regler->priv->var_id);
425 if (regler->priv->value_layout) {
426 g_object_unref(regler->priv->value_layout);
427 }
428 G_OBJECT_CLASS(gx_regler_parent_class)->finalize(object);
429 }
430
step_back(GtkRange * range)431 static void step_back(GtkRange *range)
432 {
433 gdouble newval;
434 gboolean handled;
435
436 GtkAdjustment *adjustment = gtk_range_get_adjustment(range);
437 newval = gtk_adjustment_get_value(adjustment) - gtk_adjustment_get_step_increment(adjustment);
438 g_signal_emit_by_name(range, "change-value", GTK_SCROLL_STEP_BACKWARD, newval, &handled);
439 }
440
step_forward(GtkRange * range)441 static void step_forward(GtkRange *range)
442 {
443 gdouble newval;
444 gboolean handled;
445
446 GtkAdjustment *adjustment = gtk_range_get_adjustment(range);
447 newval = gtk_adjustment_get_value(adjustment) + gtk_adjustment_get_step_increment(adjustment);
448 g_signal_emit_by_name(range, "change-value", GTK_SCROLL_STEP_FORWARD, newval, &handled);
449 }
450
451
page_back(GtkRange * range)452 static void page_back(GtkRange *range)
453 {
454 gdouble newval;
455 gboolean handled;
456
457 GtkAdjustment *adjustment = gtk_range_get_adjustment(range);
458 newval = gtk_adjustment_get_value(adjustment) - gtk_adjustment_get_page_increment(adjustment);
459 g_signal_emit_by_name(range, "change-value", GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
460 }
461
page_forward(GtkRange * range)462 static void page_forward(GtkRange *range)
463 {
464 gdouble newval;
465 gboolean handled;
466
467 GtkAdjustment *adjustment = gtk_range_get_adjustment(range);
468 newval = gtk_adjustment_get_value(adjustment) + gtk_adjustment_get_page_increment(adjustment);
469 g_signal_emit_by_name(range, "change-value", GTK_SCROLL_PAGE_FORWARD, newval, &handled);
470 }
471
scroll_begin(GtkRange * range)472 static void scroll_begin(GtkRange *range)
473 {
474 gboolean handled;
475 g_signal_emit_by_name(range, "change-value", GTK_SCROLL_START,
476 gtk_adjustment_get_lower(gtk_range_get_adjustment(range)),
477 &handled);
478 }
479
scroll_end(GtkRange * range)480 static void scroll_end(GtkRange *range)
481 {
482 gdouble newval;
483 gboolean handled;
484
485 GtkAdjustment *adjustment = gtk_range_get_adjustment(range);
486 newval = gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment);
487 g_signal_emit_by_name(range, "change-value", GTK_SCROLL_END, newval, &handled);
488 }
489
should_invert(GtkRange * range)490 static gboolean should_invert(GtkRange *range)
491 {
492 gboolean inverted = gtk_range_get_inverted(range);
493 if (gtk_orientable_get_orientation(GTK_ORIENTABLE(range)) == GTK_ORIENTATION_HORIZONTAL) {
494 gboolean flippable = gtk_range_get_flippable(range);
495 return
496 (inverted && !flippable) ||
497 (inverted && flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
498 (!inverted && flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
499 } else {
500 return inverted;
501 }
502 }
503
gx_regler_scroll(GtkRange * range,GtkScrollType scroll)504 static gboolean gx_regler_scroll(GtkRange *range, GtkScrollType scroll)
505 {
506 gdouble old_value = gtk_range_get_value(range);
507
508 switch (scroll) {
509 case GTK_SCROLL_STEP_DOWN:
510 case GTK_SCROLL_STEP_LEFT:
511 if (should_invert (range))
512 step_forward (range);
513 else
514 step_back (range);
515 break;
516
517 case GTK_SCROLL_STEP_UP:
518 case GTK_SCROLL_STEP_RIGHT:
519 if (should_invert (range))
520 step_back (range);
521 else
522 step_forward (range);
523 break;
524
525
526 case GTK_SCROLL_STEP_BACKWARD:
527 step_back (range);
528 break;
529
530 case GTK_SCROLL_STEP_FORWARD:
531 step_forward (range);
532 break;
533
534 case GTK_SCROLL_PAGE_DOWN:
535 case GTK_SCROLL_PAGE_LEFT:
536 if (should_invert (range))
537 page_forward (range);
538 else
539 page_back (range);
540 break;
541
542 case GTK_SCROLL_PAGE_UP:
543 case GTK_SCROLL_PAGE_RIGHT:
544 if (should_invert (range))
545 page_back (range);
546 else
547 page_forward (range);
548 break;
549
550 case GTK_SCROLL_PAGE_BACKWARD:
551 page_back (range);
552 break;
553
554 case GTK_SCROLL_PAGE_FORWARD:
555 page_forward (range);
556 break;
557
558 case GTK_SCROLL_START:
559 scroll_begin (range);
560 break;
561
562 case GTK_SCROLL_END:
563 scroll_end (range);
564 break;
565
566 case GTK_SCROLL_JUMP:
567 case GTK_SCROLL_NONE:
568 break;
569 }
570
571 return gtk_range_get_value(range) != old_value;
572 }
573
gx_regler_move_slider(GtkRange * range,GtkScrollType scroll)574 static void gx_regler_move_slider(GtkRange *range, GtkScrollType scroll)
575 {
576 gboolean cursor_only;
577 g_object_get(gtk_widget_get_settings(GTK_WIDGET(range)),
578 "gtk-keynav-cursor-only", &cursor_only,
579 NULL);
580 if (cursor_only) {
581 GtkWidget *toplevel = gtk_widget_get_toplevel(GTK_WIDGET(range));
582 if (gtk_orientable_get_orientation(GTK_ORIENTABLE(range)) == GTK_ORIENTATION_HORIZONTAL) {
583 if (scroll == GTK_SCROLL_STEP_UP || scroll == GTK_SCROLL_STEP_DOWN) {
584 if (toplevel) {
585 gtk_widget_child_focus(toplevel,
586 scroll == GTK_SCROLL_STEP_UP ?
587 GTK_DIR_UP : GTK_DIR_DOWN);
588 }
589 return;
590 }
591 } else {
592 if (scroll == GTK_SCROLL_STEP_LEFT || scroll == GTK_SCROLL_STEP_RIGHT) {
593 if (toplevel) {
594 gtk_widget_child_focus(toplevel,
595 scroll == GTK_SCROLL_STEP_LEFT ?
596 GTK_DIR_LEFT : GTK_DIR_RIGHT);
597 }
598 return;
599 }
600 }
601 }
602 if (!gx_regler_scroll (range, scroll)) {
603 gtk_widget_error_bell(GTK_WIDGET(range));
604 }
605 }
606
ensure_digits(GxRegler * regler)607 static void ensure_digits(GxRegler *regler)
608 {
609 GxReglerPrivate *priv = regler->priv;
610 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(regler));
611 if (!adj) {
612 return;
613 }
614 gdouble v = gtk_adjustment_get_step_increment(adj);
615 if (v == priv->last_step) {
616 return;
617 }
618 priv->last_step = v;
619 gint n = 0;
620 if (v > 0.0) {
621 while (v < 1.0 - 1e-3) {
622 v *= 10;
623 n++;
624 }
625 }
626 priv->round_digits = n;
627 }
628
gx_regler_change_value(GtkRange * range,GtkScrollType scroll,gdouble value)629 static gboolean gx_regler_change_value(GtkRange *range, GtkScrollType scroll, gdouble value)
630 {
631 g_assert(GX_IS_REGLER(range));
632 return GTK_RANGE_CLASS(gx_regler_parent_class)->change_value(range, scroll, value);
633 }
634
635 /****************************************************************
636 ** calculate the knop pointer with dead zone
637 */
638
639 /*
640 ** when this function gets inlined (e.g. -O3) in GxKnob.c:_gx_knob_pointer_event
641 ** bad things will happen, at least with g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 (funny
642 ** lockup in malloc semaphore after returning from first signal handler...)
643 ** I'm don't know how to generate assembler output commented with original C code,
644 ** without its rather unreadable, and anyhow I don't really want to debug the
645 ** compiler. Inlining for this function can also be prevented with
646 ** __attribute__((noinline)) for the function definition in the header file. /ad
647 */
_approx_in_rectangle(gdouble x,gdouble y,GdkRectangle * rect)648 gboolean _approx_in_rectangle(gdouble x, gdouble y, GdkRectangle *rect)
649 {
650 const int off = 5;
651 if (rect->width == 0 || rect->height == 0) {
652 return FALSE;
653 }
654 else if (x >= rect->x-off && x < rect->x + rect->width + off &&
655 y >= rect->y-off && y < rect->y + rect->height + off) {
656 return TRUE;
657 }
658 return FALSE;
659 }
660
gx_regler_ensure_layout(GxRegler * regler)661 static void gx_regler_ensure_layout(GxRegler *regler)
662 {
663 if (regler->priv->show_value && !regler->priv->value_layout) {
664 regler->priv->value_layout = gtk_widget_create_pango_layout(GTK_WIDGET(regler), NULL);
665 }
666 }
667
668 #ifndef min
669 #define min(x, y) ((x) < (y) ? (x) : (y))
670 #endif
671 #ifndef max
672 #define max(x, y) ((x) < (y) ? (y) : (x))
673 #endif
674
_gx_regler_get_step_pos(GxRegler * regler,gint step)675 gdouble _gx_regler_get_step_pos(GxRegler *regler, gint step)
676 {
677 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(regler));
678 gdouble lower = gtk_adjustment_get_lower(adj);
679 double df = gtk_adjustment_get_upper(adj) - lower;
680 if (df == 0.0) {
681 return 0.0;
682 } else {
683 return (gtk_adjustment_get_value(adj) - lower) * step / df;
684 }
685 }
686
_gx_regler_get_positions(GxRegler * regler,GdkRectangle * image_rect,GdkRectangle * value_rect,gboolean hfill)687 void _gx_regler_get_positions(GxRegler *regler, GdkRectangle *image_rect,
688 GdkRectangle *value_rect, gboolean hfill)
689 {
690 GtkWidget *widget = GTK_WIDGET(regler);
691 GtkStyleContext *style = gtk_widget_get_style_context(widget);
692 GtkAllocation allocation;
693 gtk_widget_get_allocation(widget, &allocation);
694 gint x = 0;
695 gint y = 0;
696 gint width = 0;
697 gint height = 0;
698 if (image_rect) {
699 width = image_rect->width;
700 height = image_rect->height;
701 }
702 gboolean show_value;
703 gtk_style_context_get_style(style, "show-value", &show_value, NULL);
704 if (regler->priv->show_value && show_value) {
705 GxReglerPrivate *priv = regler->priv;
706 gint text_width = priv->value_req.width;
707 gint text_height = priv->value_req.height;
708 gint text_x = 0, text_y = 0;
709 gint value_spacing;
710 gtk_style_context_get_style(style, "value-spacing", &value_spacing, NULL);
711 switch (priv->value_position) {
712 case GTK_POS_LEFT:
713 text_x = x + (allocation.width - width - text_width - value_spacing) / 2;
714 text_y = y + (allocation.height - text_height) / 2;
715 if (image_rect) {
716 image_rect->x = x + (allocation.width - width + text_width + value_spacing) / 2;
717 image_rect->y = y + (allocation.height - height) / 2;
718 }
719 break;
720 case GTK_POS_RIGHT:
721 text_x = x + value_spacing + (allocation.width + width - text_width - value_spacing) / 2;
722 text_y = y + (allocation.height - text_height) / 2;
723 if (image_rect) {
724 image_rect->x = x + (allocation.width - width - text_width - value_spacing) / 2;
725 image_rect->y = y + (allocation.height - height) / 2;
726 }
727 break;
728 case GTK_POS_TOP:
729 text_x = x + (allocation.width - text_width) / 2;
730 text_y = y + (allocation.height - height - text_height - value_spacing) / 2;
731 if (image_rect) {
732 image_rect->x = x + (allocation.width - width) / 2;
733 image_rect->y = y + (allocation.height - height + text_height + value_spacing) / 2;
734 }
735 break;
736 case GTK_POS_BOTTOM:
737 text_x = x + (allocation.width - text_width) / 2;
738 text_y = y + value_spacing + (allocation.height + height - text_height - value_spacing) / 2;
739 if (image_rect) {
740 image_rect->x = x + (allocation.width - width) / 2;
741 image_rect->y = y + (allocation.height - height - text_height - value_spacing) / 2;
742 }
743 break;
744 }
745 if (value_rect) {
746 if (hfill) {
747 value_rect->x = 0;
748 value_rect->width = allocation.width;
749 } else {
750 value_rect->x = text_x;
751 value_rect->width = text_width;
752 }
753 value_rect->y = text_y;
754 value_rect->height = text_height;
755 }
756 } else {
757 if (image_rect) {
758 image_rect->x = x + (allocation.width - width) / 2;
759 image_rect->y = y + (allocation.height - height) / 2;
760 }
761 if (value_rect) {
762 value_rect->x = value_rect->y = value_rect->width = value_rect->height = 0;
763 }
764 }
765 }
766
_gx_regler_check_display_popup(GxRegler * regler,GdkRectangle * image_rect,GdkRectangle * value_rect,GdkEventButton * event)767 gboolean _gx_regler_check_display_popup(GxRegler *regler, GdkRectangle *image_rect,
768 GdkRectangle *value_rect, GdkEventButton *event)
769 {
770 // check if value entry popup requested
771 gdouble x = event->x;
772 gdouble y = event->y;
773 GdkRectangle *rect = NULL;
774 if (image_rect && _approx_in_rectangle(x, y, image_rect)) {
775 if (event->button == 3) {
776 rect = image_rect;
777 }
778 } else if (value_rect && _approx_in_rectangle(x, y, value_rect)) {
779 if (event->button == 1 || event->button == 3) {
780 rect = value_rect;
781 } else {
782 return TRUE;
783 }
784 } else {
785 return TRUE;
786 }
787 if (rect) {
788 gboolean ret;
789 g_signal_emit_by_name(regler, "value-entry", rect, event, &ret);
790 return TRUE;
791 }
792 return FALSE;
793 }
794
_gx_regler_format_value(GxRegler * regler,gdouble value)795 static gchar* _gx_regler_format_value(GxRegler *regler, gdouble value)
796 {
797 gchar *fmt = NULL;
798 g_signal_emit(regler, signals[FORMAT_VALUE], 0, value, &fmt);
799 if (fmt) {
800 return fmt;
801 } else {
802 int rd = regler->priv->round_digits;
803 if (rd < 0) {
804 rd = 0;
805 }
806 return g_strdup_printf ("%0.*f", rd, value);
807 }
808 }
809
810
811 static const GtkBorder default_value_border = { 6, 6, 3, 3 };
812
get_value_border(GtkStyleContext * style,GtkBorder * value_border)813 static void get_value_border(GtkStyleContext *style, GtkBorder *value_border)
814 {
815 GtkBorder *tmp_border;
816 gtk_style_context_get_style(style, "value-border", &tmp_border, NULL);
817 if (tmp_border) {
818 *value_border = *tmp_border;
819 gtk_border_free(tmp_border);
820 } else {
821 *value_border = default_value_border;
822 }
823 }
824
_gx_regler_simple_display_value(GxRegler * regler,cairo_t * cr,GdkRectangle * rect)825 void _gx_regler_simple_display_value(GxRegler *regler, cairo_t *cr, GdkRectangle *rect)
826 {
827 if (!regler->priv->show_value) {
828 return;
829 }
830 GtkStyleContext *style = gtk_widget_get_style_context(GTK_WIDGET(regler));
831 gboolean show_value;
832 gtk_style_context_get_style(style, "show-value", &show_value, NULL);
833 if (!show_value) {
834 return;
835 }
836 PangoLayout *l = regler->priv->value_layout;
837 PangoRectangle logical_rect;
838 gchar *txt;
839 ensure_digits(regler);
840 txt = _gx_regler_format_value(regler, gtk_range_get_value(GTK_RANGE(regler)));
841 pango_layout_set_text(l, txt, -1);
842 g_free (txt);
843 pango_layout_get_pixel_extents(l, NULL, &logical_rect);
844 GtkBorder border;
845 get_value_border(style, &border);
846 gtk_render_layout(style, cr,
847 rect->x + (rect->width - logical_rect.width + border.left - border.right) / 2,
848 rect->y + border.top, regler->priv->value_layout);
849 }
850
851 #define DISPLAY_BORDER_SIZE 2
852
_gx_regler_display_value(GxRegler * regler,cairo_t * cr,GdkRectangle * rect)853 void _gx_regler_display_value(GxRegler *regler, cairo_t *cr, GdkRectangle *rect)
854 {
855 if (!regler->priv->show_value) {
856 return;
857 }
858 gboolean show_value;
859 GtkWidget * widget = GTK_WIDGET(regler);
860 GtkStyleContext *style = gtk_widget_get_style_context(widget);
861 GtkStateFlags state_flags = gtk_widget_get_state_flags(widget);
862 gtk_style_context_get_style(style, "show-value", &show_value, NULL);
863 if (!show_value) {
864 return;
865 }
866 double x0 = rect->x + DISPLAY_BORDER_SIZE;
867 double y0 = rect->y + DISPLAY_BORDER_SIZE;
868 double w = rect->width - 2 * DISPLAY_BORDER_SIZE;
869 double h = rect->height - 2 * DISPLAY_BORDER_SIZE;
870 gint rad;
871 GtkStyleContext *sc = gtk_widget_get_style_context(widget);
872 gtk_widget_style_get(widget, "border-radius", &rad, NULL);
873
874 gx_draw_inset(cr, x0, y0, w, h, rad, DISPLAY_BORDER_SIZE);
875 gtk_render_background(sc, cr, x0, y0, w, h);
876 gx_draw_glass(cr, x0, y0, w, h, rad);
877
878 gchar *txt;
879 ensure_digits(regler);
880 txt = _gx_regler_format_value(regler, gtk_range_get_value(GTK_RANGE(regler)));
881 GdkRGBA color;
882 gtk_style_context_get_color(style, state_flags, &color);
883 cairo_set_source_rgba(cr, color.red, color.green, color.blue, color.alpha);
884
885 PangoLayout *l = regler->priv->value_layout;
886 pango_layout_set_text(l, txt, -1);
887 g_free(txt);
888 PangoRectangle logical_rect;
889 pango_layout_get_pixel_extents(l, NULL, &logical_rect);
890 GtkBorder border;
891 get_value_border(style, &border);
892 gdouble off = border.left + regler->priv->value_xalign *
893 (w - logical_rect.width - (border.left + border.right));
894 cairo_move_to(cr, x0+off, y0+border.top);
895 pango_cairo_show_layout(cr, l);
896 }
897
898 /*
899 * _gx_regler_calc_size_request()
900 * calculate size of value display and add it to *out_width and *out_height
901 *
902 * setting of with_border when using
903 * _gx_regler_simple_display_value: false
904 * _gx_regler_display_value(): true
905 *
906 * value-spacing shifts the displayed value (padding between regler and value display)
907 * to make acts as padding around the displayed value between border
908 *
909 * value-border is the padding around the displayed value (and inside the border when with_border is set)
910 */
_gx_regler_calc_size_request(GxRegler * regler,gint * out_width,gint * out_height,gboolean with_border)911 void _gx_regler_calc_size_request(GxRegler *regler, gint *out_width, gint *out_height, gboolean with_border)
912 {
913 if (!regler->priv->show_value) {
914 return;
915 }
916 GtkStyleContext *style = gtk_widget_get_style_context(GTK_WIDGET(regler));
917 gboolean show_value;
918 gtk_style_context_get_style(style, "show-value", &show_value, NULL);
919 if (!show_value) {
920 return;
921 }
922 gx_regler_ensure_layout(regler);
923 PangoRectangle logical_rect1, logical_rect2;
924 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(regler));
925 gchar *txt;
926 ensure_digits(regler);
927 txt = _gx_regler_format_value(regler, gtk_adjustment_get_lower(adj));
928 pango_layout_set_text(regler->priv->value_layout, txt, -1);
929 g_free(txt);
930 pango_layout_get_pixel_extents(regler->priv->value_layout, NULL, &logical_rect1);
931 txt = _gx_regler_format_value(regler, gtk_adjustment_get_upper(adj));
932 pango_layout_set_text(regler->priv->value_layout, txt, -1);
933 g_free(txt);
934 pango_layout_get_pixel_extents(regler->priv->value_layout, NULL, &logical_rect2);
935 GtkBorder border;
936 get_value_border(style, &border);
937 gint height = max(logical_rect1.height,logical_rect2.height)
938 + border.top + border.bottom + 2 * DISPLAY_BORDER_SIZE;
939 gint width = max(logical_rect1.width,logical_rect2.width)
940 + border.left + border.right + 2 * DISPLAY_BORDER_SIZE;
941 GxReglerPrivate *priv = regler->priv;
942 priv->value_req.width = width;
943 priv->value_req.height = height;
944 gint value_spacing;
945 gtk_style_context_get_style(style, "value-spacing", &value_spacing, NULL);
946 switch (regler->priv->value_position) {
947 case GTK_POS_LEFT:
948 case GTK_POS_RIGHT:
949 *out_width += width + value_spacing;
950 if (height > *out_height) {
951 *out_height = height;
952 }
953 break;
954 case GTK_POS_TOP:
955 case GTK_POS_BOTTOM:
956 *out_height += height + value_spacing;
957 if (width > *out_width) {
958 *out_width = width;
959 }
960 break;
961 }
962 }
963
964 /****************************************************************
965 ** set value from key bindings
966 */
967
gx_regler_set_value(GtkWidget * widget,GdkScrollDirection dir_down)968 static void gx_regler_set_value (GtkWidget *widget, GdkScrollDirection dir_down)
969 {
970 g_assert(GX_IS_REGLER(widget));
971
972 if (dir_down != GDK_SCROLL_DOWN && dir_down != GDK_SCROLL_UP) {
973 return;
974 }
975 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
976 gdouble adj_value = gtk_adjustment_get_value(adj);
977 gdouble lower = gtk_adjustment_get_lower(adj);
978 gdouble upper = gtk_adjustment_get_upper(adj);
979 gdouble step_increment = gtk_adjustment_get_step_increment(adj);
980
981 int oldstep = (int)(0.5f + (adj_value - lower) / step_increment);
982 int step;
983 int nsteps = (int)(0.5f + (upper - lower) / step_increment);
984 if (dir_down == GDK_SCROLL_DOWN)
985 step = oldstep - 1;
986 else
987 step = oldstep + 1;
988 gdouble value = lower + step * (upper - lower) / nsteps;
989 gtk_widget_grab_focus(widget);
990 gtk_range_set_value(GTK_RANGE(widget), value);
991 }
992
993 /****************************************************************
994 ** mouse button pressed set value
995 */
996
gx_regler_relay_value(GtkAdjustment * src,GtkAdjustment * dst)997 static void gx_regler_relay_value(GtkAdjustment *src, GtkAdjustment *dst) {
998 gtk_adjustment_set_value(dst, gtk_adjustment_get_value(src));
999 }
1000
gx_regler_spinner_input(GtkSpinButton * spin_button,gdouble * new_val,GxRegler * regler)1001 static gint gx_regler_spinner_input(GtkSpinButton *spin_button, gdouble *new_val, GxRegler *regler)
1002 {
1003 gint ret;
1004 g_signal_emit(regler, signals[INPUT_VALUE], 0, spin_button, new_val, &ret);
1005 return ret;
1006 }
1007
gx_regler_spinner_output(GtkSpinButton * spinner,GxRegler * regler)1008 static gboolean gx_regler_spinner_output(GtkSpinButton *spinner, GxRegler *regler) {
1009 gchar *fmt;
1010 gdouble value = gtk_adjustment_get_value(gtk_spin_button_get_adjustment(spinner));
1011 fmt = _gx_regler_format_value(regler, value);
1012 gtk_entry_set_text (GTK_ENTRY(spinner), fmt);
1013 g_free(fmt);
1014 return TRUE;
1015 }
1016
gx_regler_get_nchars(GxRegler * regler,gdouble value)1017 static int gx_regler_get_nchars(GxRegler *regler, gdouble value)
1018 {
1019 gchar *txt = _gx_regler_format_value(regler, value);
1020 int nchars = strlen(txt);
1021 g_free(txt);
1022 return nchars;
1023 }
1024
gx_regler_value_entry(GxRegler * regler,GdkRectangle * rect,GdkEventButton * event)1025 static gboolean gx_regler_value_entry(GxRegler *regler, GdkRectangle *rect, GdkEventButton *event)
1026 {
1027 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) {
1028 return FALSE;
1029 }
1030 g_assert(GX_IS_REGLER(regler));
1031 GtkAdjustment *dst_adj = gtk_range_get_adjustment(GTK_RANGE(regler));
1032 GtkWidget *popover = gtk_popover_new(GTK_WIDGET(regler));
1033 gtk_popover_set_pointing_to(GTK_POPOVER(popover), rect);
1034 ensure_digits(regler);
1035 gdouble lower = gtk_adjustment_get_lower(dst_adj);
1036 gdouble upper = gtk_adjustment_get_upper(dst_adj);
1037 GtkAdjustment *src_adj = GTK_ADJUSTMENT(
1038 gtk_adjustment_new(
1039 gtk_adjustment_get_value(dst_adj), lower, upper,
1040 gtk_adjustment_get_step_increment(dst_adj),
1041 gtk_adjustment_get_page_increment(dst_adj),
1042 gtk_adjustment_get_page_size(dst_adj)));
1043 GtkWidget *spinner = gtk_spin_button_new(
1044 src_adj, gtk_adjustment_get_step_increment(src_adj),
1045 regler->priv->round_digits);
1046 gtk_entry_set_width_chars(GTK_ENTRY(spinner), max(gx_regler_get_nchars(regler, lower),
1047 gx_regler_get_nchars(regler, upper)));
1048 g_signal_connect(spinner, "output", G_CALLBACK(gx_regler_spinner_output), regler);
1049 g_signal_connect(spinner, "input", G_CALLBACK(gx_regler_spinner_input), regler);
1050 g_signal_connect(src_adj, "value-changed", G_CALLBACK(gx_regler_relay_value), dst_adj);
1051 gtk_container_add(GTK_CONTAINER(popover), spinner);
1052
1053 g_signal_connect_object(spinner, "activate", G_CALLBACK(gtk_widget_destroy),
1054 popover, (GConnectFlags)(G_CONNECT_AFTER|G_CONNECT_SWAPPED));
1055 g_signal_connect(popover, "hide", G_CALLBACK(gtk_widget_destroy), popover);
1056
1057 gtk_widget_show(spinner);
1058
1059 gtk_popover_popup(GTK_POPOVER(popover));
1060 return FALSE;
1061 }
1062
1063 /****************************************************************
1064 ** mouse button release
1065 */
1066
gx_regler_button_release(GtkWidget * widget,GdkEventButton * event)1067 static gboolean gx_regler_button_release (GtkWidget *widget, GdkEventButton *event)
1068 {
1069 g_assert(GX_IS_REGLER(widget));
1070 if (gtk_widget_has_grab(widget))
1071 gtk_grab_remove(widget);
1072 return FALSE;
1073 }
1074
1075 /****************************************************************
1076 ** set value from mouseweel
1077 */
1078
gx_regler_scroll(GtkWidget * widget,GdkEventScroll * event)1079 static gboolean gx_regler_scroll (GtkWidget *widget, GdkEventScroll *event)
1080 {
1081 usleep(5000);
1082
1083 GdkScrollDirection direction;
1084 gdouble delta_x, delta_y;
1085 if (event->direction != GDK_SCROLL_SMOOTH) {
1086 gx_regler_set_value(widget, event->direction);
1087 } else if (gdk_event_get_scroll_direction((GdkEvent*)event, &direction)) {
1088 gx_regler_set_value(widget, direction);
1089 } else if (gdk_event_get_scroll_deltas((GdkEvent*)event, &delta_x, &delta_y)) {
1090 GdkScrollDirection vdir = delta_y > 0.0 ? GDK_SCROLL_UP : GDK_SCROLL_DOWN;
1091 if (abs(delta_y) > abs(delta_x)) {
1092 gx_regler_set_value(widget, vdir);
1093 }
1094 }
1095
1096 return TRUE;
1097 }
1098
1099 /****************************************************************
1100 ** init the Regler type/size
1101 */
1102
gx_regler_adjustment_changed(GtkAdjustment * adjustment,gpointer data)1103 static void gx_regler_adjustment_changed(GtkAdjustment *adjustment, gpointer data)
1104 {
1105 gtk_widget_queue_resize(GTK_WIDGET(data));
1106 }
1107
gx_regler_change_adjustment(GxRegler * regler,GtkAdjustment * adjustment)1108 static void gx_regler_change_adjustment(GxRegler *regler, GtkAdjustment *adjustment)
1109 {
1110 GxReglerPrivate *priv = regler->priv;
1111 if (adjustment == priv->adjustment) {
1112 return;
1113 }
1114 if (priv->adjustment) {
1115 g_signal_handlers_disconnect_by_func(
1116 priv->adjustment, (gpointer)gx_regler_adjustment_changed, regler);
1117 g_object_unref(priv->adjustment);
1118 priv->adjustment = NULL;
1119 }
1120 if (!adjustment) {
1121 return;
1122 }
1123 priv->adjustment = adjustment;
1124 g_object_ref_sink(adjustment);
1125 g_signal_connect(adjustment, "changed", G_CALLBACK(gx_regler_adjustment_changed), regler);
1126 gtk_widget_queue_resize(GTK_WIDGET(regler));
1127 }
1128
gx_regler_adjustment_notified(GObject * gobject,GParamSpec * pspec)1129 static void gx_regler_adjustment_notified(GObject *gobject, GParamSpec *pspec)
1130 {
1131 GxRegler *regler = GX_REGLER(gobject);
1132 gx_regler_change_adjustment(regler, gtk_range_get_adjustment(®ler->parent));
1133 }
1134
gx_regler_init(GxRegler * regler)1135 static void gx_regler_init(GxRegler *regler)
1136 {
1137 regler->priv = (GxReglerPrivate*)gx_regler_get_instance_private(regler);
1138 regler->priv->last_step = -1;
1139 regler->priv->round_digits = 0;
1140 regler->priv->value_position = GTK_POS_BOTTOM;
1141 regler->priv->show_value = TRUE;
1142 regler->priv->value_xalign = 0.5;
1143 gtk_widget_set_can_focus(GTK_WIDGET(regler), TRUE);
1144 gtk_widget_set_receives_default(GTK_WIDGET(regler), TRUE);
1145 gtk_widget_set_has_window(GTK_WIDGET(regler), FALSE);
1146 g_signal_connect(regler, "notify::adjustment", G_CALLBACK(gx_regler_adjustment_notified), NULL);
1147 }
1148
1149 /****************************************************************
1150 ** Properties
1151 */
1152
gx_regler_set_var_id(GxRegler * regler,const gchar * str)1153 static void gx_regler_set_var_id(GxRegler *regler, const gchar *str)
1154 {
1155 g_free(regler->priv->var_id);
1156 regler->priv->var_id = g_strdup(str ? str : "");
1157 g_object_notify(G_OBJECT(regler), "var-id");
1158 }
1159
gx_regler_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1160 static void gx_regler_set_property (
1161 GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1162 {
1163 GxRegler *regler;
1164
1165 regler = GX_REGLER (object);
1166
1167 switch(prop_id) {
1168 case PROP_VAR_ID:
1169 gx_regler_set_var_id (regler, g_value_get_string (value));
1170 break;
1171 case PROP_SHOW_VALUE:
1172 gx_regler_set_show_value(regler, g_value_get_boolean(value));
1173 break;
1174 case PROP_VALUE_POSITION:
1175 gx_regler_set_value_position(regler, GtkPositionType(g_value_get_enum(value)));
1176 break;
1177 case PROP_VALUE_XALIGN:
1178 regler->priv->value_xalign = g_value_get_double(value);
1179 gtk_widget_queue_draw(GTK_WIDGET(object));
1180 g_object_notify(object, "value-xalign");
1181 break;
1182 case PROP_LABEL_REF:
1183 gx_regler_set_label_ref(regler, GTK_LABEL(g_value_get_object(value)));
1184 break;
1185 default:
1186 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1187 break;
1188 }
1189 }
1190
gx_regler_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1191 static void gx_regler_get_property(
1192 GObject *object, guint prop_id, GValue *value, GParamSpec*pspec)
1193 {
1194 GxRegler *regler;
1195
1196 regler = GX_REGLER(object);
1197
1198 switch(prop_id) {
1199 case PROP_VAR_ID:
1200 g_value_set_string (value, regler->priv->var_id);
1201 break;
1202 case PROP_SHOW_VALUE:
1203 g_value_set_boolean(value, regler->priv->show_value);
1204 break;
1205 case PROP_VALUE_POSITION:
1206 g_value_set_enum(value, regler->priv->value_position);
1207 break;
1208 case PROP_VALUE_XALIGN:
1209 g_value_set_double(value, regler->priv->value_xalign);
1210 break;
1211 case PROP_LABEL_REF:
1212 g_value_set_object(value, regler->priv->label);
1213 break;
1214 case PROP_DIGITS:
1215 g_value_set_int(value, regler->priv->round_digits);
1216 break;
1217 default:
1218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1219 break;
1220 }
1221 }
1222
1223 /*
1224 ** getter / setter
1225 */
1226
gx_regler_set_show_value(GxRegler * regler,gboolean value)1227 void gx_regler_set_show_value(GxRegler *regler, gboolean value)
1228 {
1229 g_return_if_fail(GX_IS_REGLER(regler));
1230 regler->priv->show_value = value;
1231 gtk_widget_queue_resize(GTK_WIDGET(regler));
1232 g_object_notify(G_OBJECT(regler), "show-value");
1233 }
1234
gx_regler_get_show_value(GxRegler * regler)1235 gboolean gx_regler_get_show_value(GxRegler *regler)
1236 {
1237 g_return_val_if_fail(GX_IS_REGLER(regler), 0);
1238 return regler->priv->show_value;
1239 }
1240
gx_regler_set_value_position(GxRegler * regler,GtkPositionType value)1241 void gx_regler_set_value_position(GxRegler *regler, GtkPositionType value)
1242 {
1243 g_return_if_fail(GX_IS_REGLER(regler));
1244 regler->priv->value_position = value;
1245 gtk_widget_queue_resize(GTK_WIDGET(regler));
1246 g_object_notify(G_OBJECT(regler), "value-position");
1247 }
1248
gx_regler_get_value_position(GxRegler * regler)1249 GtkPositionType gx_regler_get_value_position(GxRegler *regler)
1250 {
1251 g_return_val_if_fail(GX_IS_REGLER(regler), GTK_POS_BOTTOM);
1252 return regler->priv->value_position;
1253 }
1254
gx_regler_set_label_ref(GxRegler * regler,GtkLabel * label)1255 void gx_regler_set_label_ref(GxRegler *regler, GtkLabel *label)
1256 {
1257 g_return_if_fail(GX_IS_REGLER(regler));
1258 if (regler->priv->label) {
1259 g_object_unref(regler->priv->label);
1260 regler->priv->label = NULL;
1261 }
1262 if (label) {
1263 g_return_if_fail(GTK_IS_LABEL(label));
1264 regler->priv->label = label;
1265 g_object_ref(label);
1266 }
1267 g_object_notify(G_OBJECT(regler), "label-ref");
1268 }
1269
1270
gx_regler_get_label_ref(GxRegler * regler)1271 GtkLabel *gx_regler_get_label_ref(GxRegler *regler)
1272 {
1273 g_return_val_if_fail(GX_IS_REGLER(regler), 0);
1274 return regler->priv->label;
1275 }
1276