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"
44 static void dispose (GObject
*object
);
45 static void get_property (GObject
*object
,
49 static void set_property (GObject
*object
,
53 static void set_canvas (AdgWidget
*widget
,
55 static gboolean
expose_event (GtkWidget
*widget
,
56 GdkEventExpose
*event
);
57 static gboolean
scroll_event (GtkWidget
*widget
,
58 GdkEventScroll
*event
);
59 static gboolean
button_press_event (GtkWidget
*widget
,
60 GdkEventButton
*event
);
61 static gboolean
motion_notify_event (GtkWidget
*widget
,
62 GdkEventMotion
*event
);
63 static gboolean
get_transformation (GtkWidget
*widget
,
66 static void set_transformation (GtkWidget
*widget
,
67 const AdgMatrix
*model
);
69 static guint signals
[LAST_SIGNAL
] = { 0 };
72 G_DEFINE_TYPE(AdgWidget
, adg_widget
, GTK_TYPE_DRAWING_AREA
);
76 adg_widget_class_init(AdgWidgetClass
*klass
)
78 GObjectClass
*gobject_class
;
79 GtkWidgetClass
*widget_class
;
82 gobject_class
= (GObjectClass
*) klass
;
83 widget_class
= (GtkWidgetClass
*) klass
;
85 g_type_class_add_private(klass
, sizeof(AdgWidgetPrivate
));
87 gobject_class
->dispose
= dispose
;
88 gobject_class
->get_property
= get_property
;
89 gobject_class
->set_property
= set_property
;
91 widget_class
->expose_event
= expose_event
;
92 widget_class
->scroll_event
= scroll_event
;
93 widget_class
->button_press_event
= button_press_event
;
94 widget_class
->motion_notify_event
= motion_notify_event
;
96 param
= g_param_spec_object("canvas",
98 P_("The canvas to be shown by this widget"),
100 G_PARAM_CONSTRUCT
|G_PARAM_READWRITE
);
101 g_object_class_install_property(gobject_class
, PROP_CANVAS
, param
);
104 * AdgWidget::canvas-changed:
105 * @widget: an #AdgWidget
107 * Emitted when the widget has a new canvas.
109 signals
[CANVAS_CHANGED
] = g_signal_new("canvas-changed", ADG_TYPE_WIDGET
,
110 G_SIGNAL_RUN_LAST
|G_SIGNAL_NO_RECURSE
,
111 G_STRUCT_OFFSET(AdgWidgetClass
, canvas_changed
),
113 g_cclosure_marshal_VOID__VOID
,
118 adg_widget_init(AdgWidget
*widget
)
120 AdgWidgetPrivate
*priv
= G_TYPE_INSTANCE_GET_PRIVATE(widget
,
130 /* Enable GDK events to catch wheel rotation and drag */
131 gtk_widget_add_events((GtkWidget
*) widget
,
132 GDK_BUTTON2_MOTION_MASK
|GDK_SCROLL_MASK
);
136 dispose(GObject
*object
)
138 AdgWidgetPrivate
*priv
= ((AdgWidget
*) object
)->priv
;
140 if (priv
->canvas
!= NULL
) {
141 g_object_unref(priv
->canvas
);
145 G_OBJECT_CLASS(adg_widget_parent_class
)->dispose(object
);
149 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
151 AdgWidget
*widget
= (AdgWidget
*) object
;
156 g_value_set_object(value
, widget
->priv
->canvas
);
160 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
166 set_property(GObject
*object
,
167 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
169 AdgWidget
*widget
= (AdgWidget
*) object
;
174 set_canvas(widget
, g_value_get_object(value
));
178 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
186 * @path: the #AdgPath to stroke
188 * Creates a new #AdgWidget.
190 * Return value: the newly created widget
193 adg_widget_new(AdgCanvas
*canvas
)
195 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
197 return (GtkWidget
*) g_object_new(ADG_TYPE_WIDGET
, "canvas", canvas
, NULL
);
202 * adg_widget_get_canvas:
203 * @widget: an #AdgWidget
205 * Gets the canvas associated to @widget.
207 * Return value: the requested #AdgCanvas object or %NULL on errors
210 adg_widget_get_canvas(AdgWidget
*widget
)
212 g_return_val_if_fail(ADG_IS_WIDGET(widget
), NULL
);
214 return widget
->priv
->canvas
;
218 * adg_widget_set_canvas:
219 * @widget: an #AdgWidget
220 * @canvas: the new #AdgCanvas
222 * Sets a new canvas on @widget. The old canvas, if presents, is
226 adg_widget_set_canvas(AdgWidget
*widget
, AdgCanvas
*canvas
)
228 g_return_if_fail(ADG_IS_WIDGET(widget
));
230 set_canvas(widget
, canvas
);
232 g_object_notify((GObject
*) widget
, "canvas");
237 set_canvas(AdgWidget
*widget
, AdgCanvas
*canvas
)
239 if (widget
->priv
->canvas
!= NULL
)
240 g_object_unref(widget
->priv
->canvas
);
242 widget
->priv
->canvas
= canvas
;
245 g_object_ref(widget
->priv
->canvas
);
247 g_signal_emit(widget
, signals
[CANVAS_CHANGED
], 0);
251 expose_event(GtkWidget
*widget
, GdkEventExpose
*event
)
254 GtkWidgetClass
*parent_class
;
256 canvas
= ((AdgWidget
*) widget
)->priv
->canvas
;
257 parent_class
= (GtkWidgetClass
*) adg_widget_parent_class
;
259 if (canvas
!= NULL
) {
260 cairo_t
*cr
= gdk_cairo_create(widget
->window
);
261 adg_entity_render((AdgEntity
*) canvas
, cr
);
265 if (parent_class
->expose_event
== NULL
)
268 return parent_class
->expose_event(widget
, event
);
272 scroll_event(GtkWidget
*widget
, GdkEventScroll
*event
)
274 GtkWidgetClass
*parent_class
;
275 AdgWidgetPrivate
*priv
;
276 AdgMatrix model
, inverted
;
278 parent_class
= (GtkWidgetClass
*) adg_widget_parent_class
;
279 priv
= ((AdgWidget
*) widget
)->priv
;
281 if ((event
->direction
== GDK_SCROLL_UP
||
282 event
->direction
== GDK_SCROLL_DOWN
) &&
283 get_transformation(widget
, &model
, &inverted
)) {
286 if (event
->direction
== GDK_SCROLL_UP
) {
295 cairo_matrix_transform_point(&inverted
, &x
, &y
);
297 cairo_matrix_scale(&model
, factor
, factor
);
298 cairo_matrix_translate(&model
, x
/factor
- x
, y
/factor
- y
);
300 set_transformation(widget
, &model
);
302 gtk_widget_queue_draw(widget
);
305 if (parent_class
->scroll_event
== NULL
)
308 return parent_class
->scroll_event(widget
, event
);
312 button_press_event(GtkWidget
*widget
, GdkEventButton
*event
)
314 GtkWidgetClass
*parent_class
;
315 AdgWidgetPrivate
*priv
;
317 parent_class
= (GtkWidgetClass
*) adg_widget_parent_class
;
318 priv
= ((AdgWidget
*) widget
)->priv
;
320 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 2) {
321 priv
->x_event
= event
->x
;
322 priv
->y_event
= event
->y
;
325 if (parent_class
->button_press_event
== NULL
)
328 return parent_class
->button_press_event(widget
, event
);
332 motion_notify_event(GtkWidget
*widget
, GdkEventMotion
*event
)
334 GtkWidgetClass
*parent_class
;
335 AdgWidgetPrivate
*priv
;
336 AdgMatrix model
, inverted
;
338 parent_class
= (GtkWidgetClass
*) adg_widget_parent_class
;
339 priv
= ((AdgWidget
*) widget
)->priv
;
341 if ((event
->state
& GDK_BUTTON2_MASK
) > 0 &&
342 get_transformation(widget
, &model
, &inverted
)) {
345 x
= event
->x
- priv
->x_event
;
346 y
= event
->y
- priv
->y_event
;
348 cairo_matrix_transform_distance(&inverted
, &x
, &y
);
349 cairo_matrix_translate(&model
, x
, y
);
350 priv
->x_event
= event
->x
;
351 priv
->y_event
= event
->y
;
353 set_transformation(widget
, &model
);
355 gtk_widget_queue_draw(widget
);
358 if (parent_class
->motion_notify_event
== NULL
)
361 return parent_class
->motion_notify_event(widget
, event
);
365 get_transformation(GtkWidget
*widget
, AdgMatrix
*model
, AdgMatrix
*inverted
)
368 const AdgMatrix
*src
;
370 canvas
= ((AdgWidget
*) widget
)->priv
->canvas
;
374 src
= adg_container_get_model_transformation((AdgContainer
*) canvas
);
376 adg_matrix_copy(model
, src
);
377 adg_matrix_copy(inverted
, src
);
378 return cairo_matrix_invert(inverted
) == CAIRO_STATUS_SUCCESS
;
382 set_transformation(GtkWidget
*widget
, const AdgMatrix
*model
)
384 AdgCanvas
*canvas
= ((AdgWidget
*) widget
)->priv
->canvas
;
387 adg_container_set_model_transformation((AdgContainer
*) canvas
, model
);