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