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.
24 * @short_description: A #GtkWidget specifically designed to contain
25 * an #AdgCanvas entity
28 #include "adg-widget.h"
29 #include "adg-widget-private.h"
45 static void dispose (GObject
*object
);
46 static void get_property (GObject
*object
,
50 static void set_property (GObject
*object
,
54 static void set_canvas (AdgWidget
*widget
,
56 static gboolean
expose_event (GtkWidget
*widget
,
57 GdkEventExpose
*event
);
58 static gboolean
scroll_event (GtkWidget
*widget
,
59 GdkEventScroll
*event
);
60 static gboolean
button_press_event (GtkWidget
*widget
,
61 GdkEventButton
*event
);
62 static gboolean
motion_notify_event (GtkWidget
*widget
,
63 GdkEventMotion
*event
);
64 static gboolean
get_transformation (GtkWidget
*widget
,
67 static void set_transformation (GtkWidget
*widget
,
68 const AdgMatrix
*model
);
70 static guint signals
[LAST_SIGNAL
] = { 0 };
73 G_DEFINE_TYPE(AdgWidget
, adg_widget
, GTK_TYPE_DRAWING_AREA
);
77 adg_widget_class_init(AdgWidgetClass
*klass
)
79 GObjectClass
*gobject_class
;
80 GtkWidgetClass
*widget_class
;
83 gobject_class
= (GObjectClass
*) klass
;
84 widget_class
= (GtkWidgetClass
*) klass
;
86 g_type_class_add_private(klass
, sizeof(AdgWidgetPrivate
));
88 gobject_class
->dispose
= dispose
;
89 gobject_class
->get_property
= get_property
;
90 gobject_class
->set_property
= set_property
;
92 widget_class
->expose_event
= expose_event
;
93 widget_class
->scroll_event
= scroll_event
;
94 widget_class
->button_press_event
= button_press_event
;
95 widget_class
->motion_notify_event
= motion_notify_event
;
97 param
= g_param_spec_object("canvas",
99 P_("The canvas to be shown by this widget"),
101 G_PARAM_CONSTRUCT
|G_PARAM_READWRITE
);
102 g_object_class_install_property(gobject_class
, PROP_CANVAS
, param
);
104 param
= g_param_spec_double("factor",
106 P_("The factor used in zooming in and out"),
107 1., G_MAXDOUBLE
, 1.05,
108 G_PARAM_CONSTRUCT
|G_PARAM_READWRITE
);
109 g_object_class_install_property(gobject_class
, PROP_FACTOR
, param
);
112 * AdgWidget::canvas-changed:
113 * @widget: an #AdgWidget
115 * Emitted when the widget has a new canvas.
117 signals
[CANVAS_CHANGED
] = g_signal_new("canvas-changed", ADG_TYPE_WIDGET
,
118 G_SIGNAL_RUN_LAST
|G_SIGNAL_NO_RECURSE
,
119 G_STRUCT_OFFSET(AdgWidgetClass
, canvas_changed
),
121 g_cclosure_marshal_VOID__VOID
,
126 adg_widget_init(AdgWidget
*widget
)
128 AdgWidgetPrivate
*priv
= G_TYPE_INSTANCE_GET_PRIVATE(widget
,
139 /* Enable GDK events to catch wheel rotation and drag */
140 gtk_widget_add_events((GtkWidget
*) widget
,
141 GDK_BUTTON2_MOTION_MASK
|GDK_SCROLL_MASK
);
145 dispose(GObject
*object
)
147 AdgWidgetPrivate
*priv
= ((AdgWidget
*) object
)->priv
;
149 if (priv
->canvas
!= NULL
) {
150 g_object_unref(priv
->canvas
);
154 G_OBJECT_CLASS(adg_widget_parent_class
)->dispose(object
);
158 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
160 AdgWidget
*widget
= (AdgWidget
*) object
;
165 g_value_set_object(value
, widget
->priv
->canvas
);
169 g_value_set_double(value
, widget
->priv
->factor
);
173 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
179 set_property(GObject
*object
,
180 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
182 AdgWidget
*widget
= (AdgWidget
*) object
;
187 set_canvas(widget
, g_value_get_object(value
));
191 widget
->priv
->factor
= g_value_get_double(value
);
195 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
203 * @path: the #AdgPath to stroke
205 * Creates a new #AdgWidget.
207 * Return value: the newly created widget
210 adg_widget_new(AdgCanvas
*canvas
)
212 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
214 return (GtkWidget
*) g_object_new(ADG_TYPE_WIDGET
, "canvas", canvas
, NULL
);
219 * adg_widget_get_canvas:
220 * @widget: an #AdgWidget
222 * Gets the canvas associated to @widget.
224 * Return value: the requested #AdgCanvas object or %NULL on errors
227 adg_widget_get_canvas(AdgWidget
*widget
)
229 g_return_val_if_fail(ADG_IS_WIDGET(widget
), NULL
);
231 return widget
->priv
->canvas
;
235 * adg_widget_set_canvas:
236 * @widget: an #AdgWidget
237 * @canvas: the new #AdgCanvas
239 * Sets a new canvas on @widget. The old canvas, if presents, is
243 adg_widget_set_canvas(AdgWidget
*widget
, AdgCanvas
*canvas
)
245 g_return_if_fail(ADG_IS_WIDGET(widget
));
247 set_canvas(widget
, canvas
);
249 g_object_notify((GObject
*) widget
, "canvas");
253 * adg_widget_get_factor:
254 * @widget: an #AdgWidget
256 * Gets the zoom factor associated to @widget. The zoom factor is
257 * directly used to zoom in (that is, the default zoom factor of
258 * 1.05 will zoom of 5% every iteration) and it is reversed while
259 * zooming out (that is, the default factor will use 1/1.05).
261 * Return value: the requested zoom factor or 0 on error
264 adg_widget_get_factor(AdgWidget
*widget
)
266 g_return_val_if_fail(ADG_IS_WIDGET(widget
), 0.);
268 return widget
->priv
->factor
;
272 * adg_widget_set_factor:
273 * @widget: an #AdgWidget
274 * @factor: the new zoom factor
276 * Sets a new zoom factor to @widget. If the factor is less than
277 * 1, it will be clamped to 1.
280 adg_widget_set_factor(AdgWidget
*widget
, gdouble factor
)
282 g_return_if_fail(ADG_IS_WIDGET(widget
));
284 widget
->priv
->factor
= CLAMP(factor
, 1., G_MAXDOUBLE
);
286 g_object_notify((GObject
*) widget
, "factor");
291 set_canvas(AdgWidget
*widget
, AdgCanvas
*canvas
)
293 if (widget
->priv
->canvas
!= NULL
)
294 g_object_unref(widget
->priv
->canvas
);
296 widget
->priv
->canvas
= canvas
;
299 g_object_ref(widget
->priv
->canvas
);
301 g_signal_emit(widget
, signals
[CANVAS_CHANGED
], 0);
305 expose_event(GtkWidget
*widget
, GdkEventExpose
*event
)
308 GtkWidgetClass
*parent_class
;
310 canvas
= ((AdgWidget
*) widget
)->priv
->canvas
;
311 parent_class
= (GtkWidgetClass
*) adg_widget_parent_class
;
313 if (canvas
!= NULL
) {
314 cairo_t
*cr
= gdk_cairo_create(widget
->window
);
315 adg_entity_render((AdgEntity
*) canvas
, cr
);
319 if (parent_class
->expose_event
== NULL
)
322 return parent_class
->expose_event(widget
, event
);
326 scroll_event(GtkWidget
*widget
, GdkEventScroll
*event
)
328 GtkWidgetClass
*parent_class
;
329 AdgWidgetPrivate
*priv
;
330 AdgMatrix model
, inverted
;
332 parent_class
= (GtkWidgetClass
*) adg_widget_parent_class
;
333 priv
= ((AdgWidget
*) widget
)->priv
;
335 if ((event
->direction
== GDK_SCROLL_UP
||
336 event
->direction
== GDK_SCROLL_DOWN
) &&
337 get_transformation(widget
, &model
, &inverted
)) {
340 if (event
->direction
== GDK_SCROLL_UP
) {
341 factor
= priv
->factor
;
343 factor
= 1. / priv
->factor
;
349 cairo_matrix_transform_point(&inverted
, &x
, &y
);
351 cairo_matrix_scale(&model
, factor
, factor
);
352 cairo_matrix_translate(&model
, x
/factor
- x
, y
/factor
- y
);
354 set_transformation(widget
, &model
);
356 gtk_widget_queue_draw(widget
);
359 if (parent_class
->scroll_event
== NULL
)
362 return parent_class
->scroll_event(widget
, event
);
366 button_press_event(GtkWidget
*widget
, GdkEventButton
*event
)
368 GtkWidgetClass
*parent_class
;
369 AdgWidgetPrivate
*priv
;
371 parent_class
= (GtkWidgetClass
*) adg_widget_parent_class
;
372 priv
= ((AdgWidget
*) widget
)->priv
;
374 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 2) {
375 priv
->x_event
= event
->x
;
376 priv
->y_event
= event
->y
;
379 if (parent_class
->button_press_event
== NULL
)
382 return parent_class
->button_press_event(widget
, event
);
386 motion_notify_event(GtkWidget
*widget
, GdkEventMotion
*event
)
388 GtkWidgetClass
*parent_class
;
389 AdgWidgetPrivate
*priv
;
390 AdgMatrix model
, inverted
;
392 parent_class
= (GtkWidgetClass
*) adg_widget_parent_class
;
393 priv
= ((AdgWidget
*) widget
)->priv
;
395 if ((event
->state
& GDK_BUTTON2_MASK
) > 0 &&
396 get_transformation(widget
, &model
, &inverted
)) {
399 x
= event
->x
- priv
->x_event
;
400 y
= event
->y
- priv
->y_event
;
402 cairo_matrix_transform_distance(&inverted
, &x
, &y
);
403 cairo_matrix_translate(&model
, x
, y
);
404 priv
->x_event
= event
->x
;
405 priv
->y_event
= event
->y
;
407 set_transformation(widget
, &model
);
409 gtk_widget_queue_draw(widget
);
412 if (parent_class
->motion_notify_event
== NULL
)
415 return parent_class
->motion_notify_event(widget
, event
);
419 get_transformation(GtkWidget
*widget
, AdgMatrix
*model
, AdgMatrix
*inverted
)
422 const AdgMatrix
*src
;
424 canvas
= ((AdgWidget
*) widget
)->priv
->canvas
;
428 src
= adg_container_get_model_transformation((AdgContainer
*) canvas
);
430 adg_matrix_copy(model
, src
);
431 adg_matrix_copy(inverted
, src
);
432 return cairo_matrix_invert(inverted
) == CAIRO_STATUS_SUCCESS
;
436 set_transformation(GtkWidget
*widget
, const AdgMatrix
*model
)
438 AdgCanvas
*canvas
= ((AdgWidget
*) widget
)->priv
->canvas
;
441 adg_container_set_model_transformation((AdgContainer
*) canvas
, model
);