[AdgWidget] Code cleanup
[adg.git] / adg / adg-widget.c
blob1093baa147444ba814c53718a150d9953668ec75
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library 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 GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 /**
22 * SECTION:adg-widget
23 * @short_description: A #GtkWidget specifically designed to contain
24 * an #AdgCanvas entity
26 * This is a #GtkDrawingArea derived object that provides an easy way
27 * to show an ADG based canvas. Its default implementation reacts to
28 * some mouse events: if you drag the mouse by keepeng the wheel pressed
29 * the canvas is translated by translating its local map; if the mouse
30 * wheel is rotated, the local map of the canvas is scaled up or down
31 * (accordling to the wheel direction) by a factor specified in the
32 * #AdgWidget:factor property.
33 **/
35 /**
36 * AdgWidget:
38 * All fields are private and should not be used directly.
39 * Use its public methods instead.
40 **/
43 #include "adg-widget.h"
44 #include "adg-widget-private.h"
45 #include "adg-marshal.h"
46 #include "adg-intl.h"
48 #define PARENT_WIDGET_CLASS ((GtkWidgetClass *) adg_widget_parent_class)
51 enum {
52 PROP_0,
53 PROP_CANVAS,
54 PROP_FACTOR
57 enum {
58 CANVAS_CHANGED,
59 LAST_SIGNAL
63 static void dispose (GObject *object);
64 static void get_property (GObject *object,
65 guint prop_id,
66 GValue *value,
67 GParamSpec *pspec);
68 static void set_property (GObject *object,
69 guint prop_id,
70 const GValue *value,
71 GParamSpec *pspec);
72 static void set_canvas (AdgWidget *widget,
73 AdgCanvas *canvas);
74 static gboolean expose_event (GtkWidget *widget,
75 GdkEventExpose *event);
76 static gboolean scroll_event (GtkWidget *widget,
77 GdkEventScroll *event);
78 static gboolean button_press_event (GtkWidget *widget,
79 GdkEventButton *event);
80 static gboolean motion_notify_event (GtkWidget *widget,
81 GdkEventMotion *event);
82 static gboolean get_local_map (GtkWidget *widget,
83 AdgMatrix *map,
84 AdgMatrix *inverted);
85 static void set_local_map (GtkWidget *widget,
86 const AdgMatrix *map);
88 static guint signals[LAST_SIGNAL] = { 0 };
91 G_DEFINE_TYPE(AdgWidget, adg_widget, GTK_TYPE_DRAWING_AREA);
94 static void
95 adg_widget_class_init(AdgWidgetClass *klass)
97 GObjectClass *gobject_class;
98 GtkWidgetClass *widget_class;
99 GParamSpec *param;
101 gobject_class = (GObjectClass *) klass;
102 widget_class = (GtkWidgetClass *) klass;
104 g_type_class_add_private(klass, sizeof(AdgWidgetPrivate));
106 gobject_class->dispose = dispose;
107 gobject_class->get_property = get_property;
108 gobject_class->set_property = set_property;
110 widget_class->expose_event = expose_event;
111 widget_class->scroll_event = scroll_event;
112 widget_class->button_press_event = button_press_event;
113 widget_class->motion_notify_event = motion_notify_event;
115 param = g_param_spec_object("canvas",
116 P_("Canvas"),
117 P_("The canvas to be shown by this widget"),
118 ADG_TYPE_CANVAS,
119 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
120 g_object_class_install_property(gobject_class, PROP_CANVAS, param);
122 param = g_param_spec_double("factor",
123 P_("Factor"),
124 P_("The factor used in zooming in and out"),
125 1., G_MAXDOUBLE, 1.05,
126 G_PARAM_READWRITE);
127 g_object_class_install_property(gobject_class, PROP_FACTOR, param);
130 * AdgWidget::canvas-changed:
131 * @widget: an #AdgWidget
133 * Emitted when the widget has a new canvas.
135 signals[CANVAS_CHANGED] = g_signal_new("canvas-changed", ADG_TYPE_WIDGET,
136 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
137 G_STRUCT_OFFSET(AdgWidgetClass, canvas_changed),
138 NULL, NULL,
139 adg_marshal_VOID__VOID,
140 G_TYPE_NONE, 0);
143 static void
144 adg_widget_init(AdgWidget *widget)
146 AdgWidgetPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(widget,
147 ADG_TYPE_WIDGET,
148 AdgWidgetPrivate);
150 data->canvas = NULL;
151 data->factor = 1.05;
152 data->x_event = 0;
153 data->y_event = 0;
155 widget->data = data;
157 /* Enable GDK events to catch wheel rotation and drag */
158 gtk_widget_add_events((GtkWidget *) widget,
159 GDK_BUTTON_PRESS_MASK|
160 GDK_BUTTON2_MOTION_MASK|
161 GDK_SCROLL_MASK);
164 static void
165 dispose(GObject *object)
167 AdgWidgetPrivate *data = ((AdgWidget *) object)->data;
169 if (data->canvas != NULL) {
170 g_object_unref(data->canvas);
171 data->canvas = NULL;
174 G_OBJECT_CLASS(adg_widget_parent_class)->dispose(object);
177 static void
178 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
180 AdgWidgetPrivate *data = ((AdgWidget *) object)->data;
182 switch (prop_id) {
184 case PROP_CANVAS:
185 g_value_set_object(value, data->canvas);
186 break;
188 case PROP_FACTOR:
189 g_value_set_double(value, data->factor);
190 break;
192 default:
193 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
194 break;
198 static void
199 set_property(GObject *object,
200 guint prop_id, const GValue *value, GParamSpec *pspec)
202 AdgWidget *widget;
203 AdgWidgetPrivate *data;
205 widget = (AdgWidget *) object;
206 data = widget->data;
208 switch (prop_id) {
210 case PROP_CANVAS:
211 set_canvas(widget, g_value_get_object(value));
212 break;
214 case PROP_FACTOR:
215 data->factor = g_value_get_double(value);
216 break;
218 default:
219 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
220 break;
226 * adg_widget_new:
227 * @path: the #AdgPath to stroke
229 * Creates a new #AdgWidget.
231 * Returns: the newly created widget
233 GtkWidget *
234 adg_widget_new(AdgCanvas *canvas)
236 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
238 return g_object_new(ADG_TYPE_WIDGET, "canvas", canvas, NULL);
243 * adg_widget_get_canvas:
244 * @widget: an #AdgWidget
246 * Gets the canvas associated to @widget.
248 * Returns: the requested #AdgCanvas object or %NULL on errors
250 AdgCanvas *
251 adg_widget_get_canvas(AdgWidget *widget)
253 AdgWidgetPrivate *data;
255 g_return_val_if_fail(ADG_IS_WIDGET(widget), NULL);
257 data = widget->data;
259 return data->canvas;
263 * adg_widget_set_canvas:
264 * @widget: an #AdgWidget
265 * @canvas: the new #AdgCanvas
267 * Sets a new canvas on @widget. The old canvas, if presents, is
268 * unreferenced.
270 void
271 adg_widget_set_canvas(AdgWidget *widget, AdgCanvas *canvas)
273 g_return_if_fail(ADG_IS_WIDGET(widget));
275 set_canvas(widget, canvas);
277 g_object_notify((GObject *) widget, "canvas");
281 * adg_widget_get_factor:
282 * @widget: an #AdgWidget
284 * Gets the zoom factor associated to @widget. The zoom factor is
285 * directly used to zoom in (that is, the default zoom factor of
286 * 1.05 will zoom of 5% every iteration) and it is reversed while
287 * zooming out (that is, the default factor will use 1/1.05).
289 * Returns: the requested zoom factor or 0 on error
291 gdouble
292 adg_widget_get_factor(AdgWidget *widget)
294 AdgWidgetPrivate *data;
296 g_return_val_if_fail(ADG_IS_WIDGET(widget), 0.);
298 data = widget->data;
300 return data->factor;
304 * adg_widget_set_factor:
305 * @widget: an #AdgWidget
306 * @factor: the new zoom factor
308 * Sets a new zoom factor to @widget. If the factor is less than
309 * 1, it will be clamped to 1.
311 void
312 adg_widget_set_factor(AdgWidget *widget, gdouble factor)
314 AdgWidgetPrivate *data;
316 g_return_if_fail(ADG_IS_WIDGET(widget));
318 data = widget->data;
319 data->factor = CLAMP(factor, 1., G_MAXDOUBLE);
321 g_object_notify((GObject *) widget, "factor");
325 static void
326 set_canvas(AdgWidget *widget, AdgCanvas *canvas)
328 AdgWidgetPrivate *data = widget->data;
330 if (data->canvas != NULL)
331 g_object_unref(data->canvas);
333 data->canvas = canvas;
335 if (canvas != NULL)
336 g_object_ref(data->canvas);
338 g_signal_emit(widget, signals[CANVAS_CHANGED], 0);
341 static gboolean
342 expose_event(GtkWidget *widget, GdkEventExpose *event)
344 AdgWidgetPrivate *data;
345 AdgCanvas *canvas;
347 data = ((AdgWidget *) widget)->data;
348 canvas = data->canvas;
350 if (canvas != NULL) {
351 cairo_t *cr = gdk_cairo_create(widget->window);
352 adg_entity_render((AdgEntity *) canvas, cr);
353 cairo_destroy(cr);
356 if (PARENT_WIDGET_CLASS->expose_event == NULL)
357 return FALSE;
359 return PARENT_WIDGET_CLASS->expose_event(widget, event);
362 static gboolean
363 scroll_event(GtkWidget *widget, GdkEventScroll *event)
365 AdgWidgetPrivate *data;
366 AdgMatrix map, inverted;
368 data = ((AdgWidget *) widget)->data;
370 if ((event->direction == GDK_SCROLL_UP ||
371 event->direction == GDK_SCROLL_DOWN) &&
372 get_local_map(widget, &map, &inverted)) {
373 double factor, x, y;
375 if (event->direction == GDK_SCROLL_UP) {
376 factor = data->factor;
377 } else {
378 factor = 1. / data->factor;
381 x = event->x;
382 y = event->y;
384 cairo_matrix_transform_point(&inverted, &x, &y);
386 cairo_matrix_scale(&map, factor, factor);
387 cairo_matrix_translate(&map, x/factor - x, y/factor - y);
389 set_local_map(widget, &map);
391 gtk_widget_queue_draw(widget);
394 if (PARENT_WIDGET_CLASS->scroll_event == NULL)
395 return FALSE;
397 return PARENT_WIDGET_CLASS->scroll_event(widget, event);
400 static gboolean
401 button_press_event(GtkWidget *widget, GdkEventButton *event)
403 AdgWidgetPrivate *data = ((AdgWidget *) widget)->data;
405 if (event->type == GDK_BUTTON_PRESS && event->button == 2) {
406 data->x_event = event->x;
407 data->y_event = event->y;
410 if (PARENT_WIDGET_CLASS->button_press_event == NULL)
411 return FALSE;
413 return PARENT_WIDGET_CLASS->button_press_event(widget, event);
416 static gboolean
417 motion_notify_event(GtkWidget *widget, GdkEventMotion *event)
419 AdgWidgetPrivate *data;
420 AdgMatrix map, inverted;
422 data = ((AdgWidget *) widget)->data;
424 if ((event->state & GDK_BUTTON2_MASK) > 0 &&
425 get_local_map(widget, &map, &inverted)) {
426 double x, y;
428 x = event->x - data->x_event;
429 y = event->y - data->y_event;
431 cairo_matrix_transform_distance(&inverted, &x, &y);
432 cairo_matrix_translate(&map, x, y);
433 data->x_event = event->x;
434 data->y_event = event->y;
436 set_local_map(widget, &map);
438 gtk_widget_queue_draw(widget);
441 if (PARENT_WIDGET_CLASS->motion_notify_event == NULL)
442 return FALSE;
444 return PARENT_WIDGET_CLASS->motion_notify_event(widget, event);
447 static gboolean
448 get_local_map(GtkWidget *widget, AdgMatrix *map, AdgMatrix *inverted)
450 AdgWidgetPrivate *data;
451 AdgCanvas *canvas;
453 data = ((AdgWidget *) widget)->data;
454 canvas = data->canvas;
455 if (canvas == NULL)
456 return FALSE;
458 adg_entity_get_local_map((AdgEntity *) canvas, map);
459 adg_matrix_copy(inverted, map);
461 return cairo_matrix_invert(inverted) == CAIRO_STATUS_SUCCESS;
464 static void
465 set_local_map(GtkWidget *widget, const AdgMatrix *map)
467 AdgWidgetPrivate *data;
468 AdgCanvas *canvas;
470 data = ((AdgWidget *) widget)->data;
471 canvas = data->canvas;
473 if (canvas != NULL)
474 adg_entity_set_local_map((AdgEntity *) canvas, map);