/* dia-placement-tool.c * Copyright (C) 2001 Arjan Molenaar * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "dia-placement-tool.h" #include "dia-canvas-i18n.h" #include "dia-handle-tool.h" #include /* We need them to determine which handle we should grab: */ #include "dia-canvas-line.h" #include "dia-canvas-element.h" static void dia_placement_tool_class_init (DiaToolClass *class); static void dia_placement_tool_init (DiaPlacementTool *tool); static void dia_placement_tool_dispose (GObject *object); static gboolean dia_placement_tool_button_press (DiaTool *tool, DiaCanvasView *view, GdkEventButton *event); static gboolean dia_placement_tool_button_release (DiaTool *tool, DiaCanvasView *view, GdkEventButton *event); static gboolean dia_placement_tool_motion_notify (DiaTool *tool, DiaCanvasView *view, GdkEventMotion *event); static gboolean dia_placement_tool_key_press (DiaTool *tool, DiaCanvasView *view, GdkEventKey *event); static gboolean dia_placement_tool_key_release (DiaTool *tool, DiaCanvasView *view, GdkEventKey *event); static DiaToolClass *parent_class = NULL; GType dia_placement_tool_get_type (void) { static GtkType object_type = 0; if (!object_type) { static const GTypeInfo object_info = { sizeof (DiaPlacementToolClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) dia_placement_tool_class_init, (GClassFinalizeFunc) NULL, (gconstpointer) NULL, /* class_data */ sizeof (DiaPlacementTool), (guint16) 0, /* n_preallocs */ (GInstanceInitFunc) dia_placement_tool_init, }; object_type = g_type_register_static (DIA_TYPE_TOOL, "DiaPlacementTool", &object_info, 0); } return object_type; } static void dia_placement_tool_class_init (DiaToolClass *klass) { GObjectClass *object_class = (GObjectClass*) klass; parent_class = g_type_class_peek_parent (klass); object_class->dispose = dia_placement_tool_dispose; klass->button_press_event = dia_placement_tool_button_press; klass->button_release_event = dia_placement_tool_button_release; klass->motion_notify_event = dia_placement_tool_motion_notify; klass->key_press_event = dia_placement_tool_key_press; klass->key_release_event = dia_placement_tool_key_release; } static void dia_placement_tool_init (DiaPlacementTool *tool) { tool->object_type = 0; tool->n_params = 0; tool->params = NULL; tool->new_object = NULL; tool->handle_tool = NULL; } static void dia_placement_tool_dispose (GObject *object) { DiaPlacementTool *tool = (DiaPlacementTool*) object; //g_message (G_STRLOC); if (tool->new_object) { g_object_unref (tool->new_object); tool->new_object = NULL; } if (tool->params) { while (tool->n_params--) g_value_unset (&tool->params[tool->n_params].value); g_free (tool->params); tool->n_params = 0; tool->params = NULL; } if (tool->handle_tool) { g_object_unref (tool->handle_tool); tool->handle_tool = NULL; } G_OBJECT_CLASS (parent_class)->dispose (object); } static gboolean dia_placement_tool_button_press (DiaTool *tool, DiaCanvasView *view, GdkEventButton *event) { DiaPlacementTool *ptool = (DiaPlacementTool*) tool; DiaCanvasItem *item; DiaCanvasViewItem *view_item; gdouble affine[6]; DiaPoint pos, wpos; //gboolean old_allow_undo = FALSE; //if (view->canvas) { // old_allow_undo = view->canvas->allow_undo; // g_object_set (G_OBJECT (view->canvas), "allow_undo", FALSE, NULL); //} item = g_object_newv (ptool->object_type, ptool->n_params, ptool->params); ptool->new_object = item; /* If no parent is set, attach the object to the root object: */ if (!item->parent) g_object_set (item, "parent", view->canvas->root, NULL); dia_canvas_item_affine_w2i (item, affine); wpos.x = event->x; wpos.y = event->y; pos = wpos; dia_canvas_item_affine_point_w2i (item, &pos.x, &pos.y); //g_message (G_STRLOC": cursor pos = (%f, %f)", pos.x, pos.y); dia_canvas_item_move (item, pos.x, pos.y); /* Select the new item */ dia_canvas_view_unselect_all (view); view_item = dia_canvas_view_find_view_item (view, item); dia_canvas_view_focus (view, view_item); if (ptool->handle_tool) { g_warning("Adding a new item, while we still have a handle tool..."); g_object_unref (ptool->handle_tool); } ptool->handle_tool = dia_handle_tool_new (); if (g_type_is_a (ptool->object_type, DIA_TYPE_CANVAS_LINE) && item->handles) { DiaHandle *first_handle, *last_handle; first_handle = g_list_first (item->handles)->data; last_handle = g_list_last (item->handles)->data; /* Try to connect the first handle */ if (first_handle->connectable) { gdouble dist; gdouble glue_x, glue_y, glue_distance; DiaCanvasItem *cp_item; dist = dia_canvas_glue_handle (view->canvas, first_handle, wpos.x, wpos.y, &glue_x, &glue_y, &cp_item); // TODO: use the right glue_distance glue_distance = DIA_HANDLE_TOOL (ptool->handle_tool)->glue_distance; if (cp_item && dist <= glue_distance) { dia_canvas_item_connect (cp_item, first_handle); } } /* grab the last handle */ dia_handle_tool_set_grabbed_handle (DIA_HANDLE_TOOL (ptool->handle_tool), last_handle); } else if (g_type_is_a (ptool->object_type, DIA_TYPE_CANVAS_ELEMENT)) { /* grab the lower-right (SE) handle */ DiaHandle *se_handle = g_list_nth (item->handles, DIA_HANDLE_SE)->data; dia_handle_tool_set_grabbed_handle (DIA_HANDLE_TOOL(ptool->handle_tool), se_handle); } //if (view->canvas) // g_object_set (G_OBJECT (view->canvas), // "allow_undo", old_allow_undo, NULL); //g_message (G_STRLOC": item refcnt= %d", G_OBJECT (item)->ref_count); return TRUE; } /** * dia_placement_tool_button_release: * @tool: * @view: * @event: * * On a button release, the handle_tool is released again. * * Return value: **/ static gboolean dia_placement_tool_button_release (DiaTool *tool, DiaCanvasView *view, GdkEventButton *event) { DiaPlacementTool *ptool = DIA_PLACEMENT_TOOL (tool); gboolean retval = FALSE; if (ptool->new_object) { g_object_unref (ptool->new_object); //g_message (G_STRLOC": refcnt= %d", G_OBJECT (ptool)->ref_count); ptool->new_object = NULL; } if (ptool->handle_tool) { retval = dia_tool_button_release (ptool->handle_tool, view, event); g_object_unref (ptool->handle_tool); ptool->handle_tool = NULL; } return retval; } static gboolean dia_placement_tool_motion_notify (DiaTool *tool, DiaCanvasView *view, GdkEventMotion *event) { DiaPlacementTool *ptool = DIA_PLACEMENT_TOOL (tool); gboolean retval = FALSE; if (ptool->handle_tool) retval = dia_tool_motion_notify (ptool->handle_tool, view, event); return retval; } static gboolean dia_placement_tool_key_press (DiaTool *tool, DiaCanvasView *view, GdkEventKey *event) { return FALSE; } static gboolean dia_placement_tool_key_release (DiaTool *tool, DiaCanvasView *view, GdkEventKey *event) { return FALSE; } /** * dia_placement_tool_new: * @object_type: #GType of the item * @first_property_name: the name of the first property * @...: the value of the first property, followed optionally by more * name/value pairs, followed by NULL * * Create a new placement tool for an item of type @object_type. * * Return value: The new placement tool, or NULL if an error occured **/ DiaTool* dia_placement_tool_new (GType object_type, const gchar *first_property_name, ...) { /* This is code lend from g_object_new_valist(). */ DiaPlacementTool *tool; GObjectClass *class; va_list var_args; //GParameter *params; const gchar *name; //guint n_params = 0; guint n_alloced_params = 16; g_return_val_if_fail (g_type_is_a (object_type, DIA_TYPE_CANVAS_ITEM), NULL); tool = g_object_new (DIA_TYPE_PLACEMENT_TOOL, NULL); tool->object_type = object_type; if (!first_property_name) return (DiaTool*) tool; va_start (var_args, first_property_name); class = g_type_class_ref (object_type); tool->params = g_new (GParameter, n_alloced_params); name = first_property_name; while (name) { gchar *error = NULL; GParamSpec *pspec = g_object_class_find_property (class, name); if (!pspec) { g_warning ("%s: object class `%s' has no property named `%s'", G_STRLOC, g_type_name (object_type), name); break; } if (tool->n_params >= n_alloced_params) { n_alloced_params += 16; tool->params = g_renew (GParameter, tool->params, n_alloced_params); } tool->params[tool->n_params].name = name; tool->params[tool->n_params].value.g_type = 0; g_value_init (&tool->params[tool->n_params].value, G_PARAM_SPEC_VALUE_TYPE (pspec)); G_VALUE_COLLECT (&tool->params[tool->n_params].value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); /* we purposely leak the value here, it might not be * in a sane state if an error condition occoured */ break; } tool->n_params++; name = va_arg (var_args, gchar*); } g_type_class_unref (class); va_end (var_args); //g_message (G_STRLOC": refcnt = %d", G_OBJECT (tool)->ref_count); return (DiaTool*) tool; } /** * dia_placement_tool_newv: * @object_type: #GType of the item * @n_params: the length of the parameters array * @params: an array of #GParameter * * Does the same as dia_placement_tool_new(). * * Return value: The new placement tool, or NULL if an error occured **/ DiaTool* dia_placement_tool_newv (GType object_type, guint n_params, GParameter *params) { DiaPlacementTool *tool; GObjectClass *class; int i; g_return_val_if_fail (g_type_is_a (object_type, DIA_TYPE_CANVAS_ITEM), NULL); tool = g_object_new (DIA_TYPE_PLACEMENT_TOOL, NULL); tool->object_type = object_type; tool->n_params = n_params; tool->params = g_new0 (GParameter, n_params); class = g_type_class_ref (object_type); for (i = 0; i < n_params; i++) { GParamSpec *pspec = g_object_class_find_property (class, params[i].name); if (!pspec) { g_warning ("%s: object class `%s' has no property named `%s'", G_STRLOC, g_type_name (object_type), params[i].name); /* FIXME: What to do about tool->params[i].name and tool->params[i].value? */ break; } /* Using pspec->name here because it is guaranteed to be around long enough (?), while params[i].name is not. */ tool->params[i].name = pspec->name; g_value_init (&tool->params[i].value, G_PARAM_SPEC_VALUE_TYPE (pspec)); g_value_copy (¶ms[i].value, &tool->params[i].value); } g_type_class_unref (class); return (DiaTool*) tool; }