1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18 #include <config.h>
19
20 #include "create_object.h"
21 #include "connectionpoint_ops.h"
22 #include "handle_ops.h"
23 #include "object_ops.h"
24 #include "preferences.h"
25 #include "undo.h"
26 #include "cursor.h"
27 #include "highlight.h"
28 #include "textedit.h"
29 #include "parent.h"
30 #include "message.h"
31
32 static void create_object_button_press(CreateObjectTool *tool, GdkEventButton *event,
33 DDisplay *ddisp);
34 static void create_object_button_release(CreateObjectTool *tool, GdkEventButton *event,
35 DDisplay *ddisp);
36 static void create_object_motion(CreateObjectTool *tool, GdkEventMotion *event,
37 DDisplay *ddisp);
38 static void create_object_double_click(CreateObjectTool *tool, GdkEventMotion *event,
39 DDisplay *ddisp);
40
41
42 static void
create_object_button_press(CreateObjectTool * tool,GdkEventButton * event,DDisplay * ddisp)43 create_object_button_press(CreateObjectTool *tool, GdkEventButton *event,
44 DDisplay *ddisp)
45 {
46 Point clickedpoint, origpoint;
47 Handle *handle1;
48 Handle *handle2;
49 DiaObject *obj;
50 real click_distance;
51
52 ddisplay_untransform_coords(ddisp,
53 (int)event->x, (int)event->y,
54 &clickedpoint.x, &clickedpoint.y);
55
56 origpoint = clickedpoint;
57
58 snap_to_grid(ddisp, &clickedpoint.x, &clickedpoint.y);
59
60 click_distance = ddisplay_untransform_length(ddisp, 3.0);
61
62 obj = dia_object_default_create (tool->objtype, &clickedpoint,
63 tool->user_data,
64 &handle1, &handle2);
65
66 tool->obj = obj; /* ensure that tool->obj is initialised in case we
67 return early. */
68 if (!obj) {
69 tool->moving = FALSE;
70 tool->handle = NULL;
71 message_error(_("'%s' creation failed"), tool->objtype ? tool->objtype->name : "NULL");
72 return;
73 }
74
75 diagram_add_object(ddisp->diagram, obj);
76
77 /* Try a connect */
78 if (handle1 != NULL &&
79 handle1->connect_type != HANDLE_NONCONNECTABLE) {
80 ConnectionPoint *connectionpoint;
81 connectionpoint =
82 object_find_connectpoint_display(ddisp, &origpoint, obj, TRUE);
83 if (connectionpoint != NULL) {
84 (obj->ops->move)(obj, &origpoint);
85 }
86 }
87
88 if (!(event->state & GDK_SHIFT_MASK)) {
89 /* Not Multi-select => remove current selection */
90 diagram_remove_all_selected(ddisp->diagram, TRUE);
91 }
92 diagram_select(ddisp->diagram, obj);
93
94 /* Connect first handle if possible: */
95 if ((handle1!= NULL) &&
96 (handle1->connect_type != HANDLE_NONCONNECTABLE)) {
97 object_connect_display(ddisp, obj, handle1, TRUE);
98 }
99
100 object_add_updates(obj, ddisp->diagram);
101 ddisplay_do_update_menu_sensitivity(ddisp);
102 diagram_flush(ddisp->diagram);
103
104 if (handle2 != NULL) {
105 tool->handle = handle2;
106 tool->moving = TRUE;
107 tool->last_to = handle2->pos;
108
109 gdk_pointer_grab (ddisp->canvas->window, FALSE,
110 GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
111 NULL, NULL, event->time);
112 ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL));
113 } else {
114 diagram_update_extents(ddisp->diagram);
115 tool->moving = FALSE;
116 }
117
118 }
119
120 static void
create_object_double_click(CreateObjectTool * tool,GdkEventMotion * event,DDisplay * ddisp)121 create_object_double_click(CreateObjectTool *tool, GdkEventMotion *event,
122 DDisplay *ddisp)
123 {
124 }
125
126 static void
create_object_button_release(CreateObjectTool * tool,GdkEventButton * event,DDisplay * ddisp)127 create_object_button_release(CreateObjectTool *tool, GdkEventButton *event,
128 DDisplay *ddisp)
129 {
130 GList *list = NULL;
131 DiaObject *obj = tool->obj;
132 gboolean reset;
133
134 GList *parent_candidates;
135
136 g_return_if_fail (obj != NULL);
137 if (!obj) /* not sure if this isn't enough */
138 return; /* could be a legal invariant */
139
140 if (tool->moving) {
141 gdk_pointer_ungrab (event->time);
142
143 object_add_updates(tool->obj, ddisp->diagram);
144 tool->obj->ops->move_handle(tool->obj, tool->handle, &tool->last_to,
145 NULL, HANDLE_MOVE_CREATE_FINAL, 0);
146 object_add_updates(tool->obj, ddisp->diagram);
147
148 }
149
150 parent_candidates =
151 layer_find_objects_containing_rectangle(obj->parent_layer,
152 &obj->bounding_box);
153
154 /* whole object must be within another object to parent it */
155 for (; parent_candidates != NULL; parent_candidates = g_list_next(parent_candidates)) {
156 DiaObject *parent_obj = (DiaObject *) parent_candidates->data;
157 if (obj != parent_obj
158 && object_within_parent(obj, parent_obj)) {
159 Change *change = undo_parenting(ddisp->diagram, parent_obj, obj, TRUE);
160 (change->apply)(change, ddisp->diagram);
161 break;
162 /*
163 obj->parent = parent_obj;
164 parent_obj->children = g_list_append(parent_obj->children, obj);
165 */
166 }
167 }
168 g_list_free(parent_candidates);
169
170 list = g_list_prepend(list, tool->obj);
171
172 undo_insert_objects(ddisp->diagram, list, 1);
173
174 if (tool->moving) {
175 if (tool->handle->connect_type != HANDLE_NONCONNECTABLE) {
176 object_connect_display(ddisp, tool->obj, tool->handle, TRUE);
177 diagram_update_connections_selection(ddisp->diagram);
178 diagram_flush(ddisp->diagram);
179 }
180 tool->moving = FALSE;
181 tool->handle = NULL;
182 tool->obj = NULL;
183 }
184
185 {
186 /* remove position from status bar */
187 GtkStatusbar *statusbar = GTK_STATUSBAR (ddisp->modified_status);
188 guint context_id = gtk_statusbar_get_context_id (statusbar, "ObjectPos");
189 gtk_statusbar_pop (statusbar, context_id);
190 }
191
192 highlight_reset_all(ddisp->diagram);
193 reset = prefs.reset_tools_after_create != tool->invert_persistence;
194 /* kind of backward: first starting editing to see if it is possible at all, than GUI reflection */
195 if (textedit_activate_object(ddisp, obj, NULL) && reset) {
196 gtk_action_activate (menus_get_action ("ToolsTextedit"));
197 reset = FALSE; /* don't switch off textedit below */
198 }
199 diagram_update_extents(ddisp->diagram);
200 diagram_modified(ddisp->diagram);
201
202 undo_set_transactionpoint(ddisp->diagram->undo);
203
204 if (reset)
205 tool_reset();
206 ddisplay_set_all_cursor(default_cursor);
207 ddisplay_do_update_menu_sensitivity(ddisp);
208 }
209
210 static void
create_object_motion(CreateObjectTool * tool,GdkEventMotion * event,DDisplay * ddisp)211 create_object_motion(CreateObjectTool *tool, GdkEventMotion *event,
212 DDisplay *ddisp)
213 {
214 Point to;
215 ConnectionPoint *connectionpoint = NULL;
216 gchar *postext;
217 GtkStatusbar *statusbar;
218 guint context_id;
219
220 if (!tool->moving)
221 return;
222
223 ddisplay_untransform_coords(ddisp, event->x, event->y, &to.x, &to.y);
224
225 /* make sure the new object is restricted to its parent */
226 parent_handle_move_out_check(tool->obj, &to);
227
228 /* Move to ConnectionPoint if near: */
229 if (tool->handle != NULL &&
230 tool->handle->connect_type != HANDLE_NONCONNECTABLE) {
231 connectionpoint =
232 object_find_connectpoint_display(ddisp, &to, tool->obj, TRUE);
233
234 if (connectionpoint != NULL) {
235 to = connectionpoint->pos;
236 highlight_object(connectionpoint->object, NULL, ddisp->diagram);
237 ddisplay_set_all_cursor(get_cursor(CURSOR_CONNECT));
238 }
239 }
240
241 if (connectionpoint == NULL) {
242 /* No connectionopoint near, then snap to grid (if enabled) */
243 snap_to_grid(ddisp, &to.x, &to.y);
244 highlight_reset_all(ddisp->diagram);
245 ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL));
246 }
247
248 object_add_updates(tool->obj, ddisp->diagram);
249 tool->obj->ops->move_handle(tool->obj, tool->handle, &to, connectionpoint,
250 HANDLE_MOVE_CREATE, 0);
251 object_add_updates(tool->obj, ddisp->diagram);
252
253 /* Put current mouse position in status bar */
254 statusbar = GTK_STATUSBAR (ddisp->modified_status);
255 context_id = gtk_statusbar_get_context_id (statusbar, "ObjectPos");
256
257 postext = g_strdup_printf("%.3f, %.3f - %.3f, %.3f",
258 tool->obj->bounding_box.left,
259 tool->obj->bounding_box.top,
260 tool->obj->bounding_box.right,
261 tool->obj->bounding_box.bottom);
262
263 gtk_statusbar_pop (statusbar, context_id);
264 gtk_statusbar_push (statusbar, context_id, postext);
265
266 g_free(postext);
267
268 diagram_flush(ddisp->diagram);
269
270 tool->last_to = to;
271
272 return;
273 }
274
275
276
277 Tool *
create_create_object_tool(DiaObjectType * objtype,void * user_data,int invert_persistence)278 create_create_object_tool(DiaObjectType *objtype, void *user_data,
279 int invert_persistence)
280 {
281 CreateObjectTool *tool;
282
283 tool = g_new0(CreateObjectTool, 1);
284 tool->tool.type = CREATE_OBJECT_TOOL;
285 tool->tool.button_press_func = (ButtonPressFunc) &create_object_button_press;
286 tool->tool.button_release_func = (ButtonReleaseFunc) &create_object_button_release;
287 tool->tool.motion_func = (MotionFunc) &create_object_motion;
288 tool->tool.double_click_func = (DoubleClickFunc) &create_object_double_click;
289
290 tool->objtype = objtype;
291 tool->user_data = user_data;
292 tool->moving = FALSE;
293 tool->invert_persistence = invert_persistence;
294
295 return (Tool *) tool;
296 }
297
free_create_object_tool(Tool * tool)298 void free_create_object_tool(Tool *tool)
299 {
300 CreateObjectTool *real_tool = (CreateObjectTool *)tool;
301
302 if (real_tool->moving) { /* should not get here, but see bug #619246 */
303 gdk_pointer_ungrab (GDK_CURRENT_TIME);
304 ddisplay_set_all_cursor(default_cursor);
305 }
306 g_free(tool);
307 }
308