1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
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.
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.
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.
27 #include <gdk/gdkkeysyms.h>
35 #include "interface.h"
37 #include "object_ops.h"
38 #include "connectionpoint_ops.h"
44 #include "preferences.h"
45 #include "scroll_tool.h"
47 #include "highlight.h"
50 /* This contains the point that was clicked to get this menu */
51 static Point object_menu_clicked_point
;
54 object_menu_proxy(GtkWidget
*widget
, gpointer data
)
56 DiaMenuItem
*dia_menu_item
;
57 ObjectChange
*obj_change
;
59 DDisplay
*ddisp
= ddisplay_active();
63 obj
= (DiaObject
*)ddisp
->diagram
->data
->selected
->data
;
64 dia_menu_item
= (DiaMenuItem
*) data
;
67 object_add_updates(obj
, ddisp
->diagram
);
68 obj_change
= (dia_menu_item
->callback
)(obj
, &object_menu_clicked_point
,
69 dia_menu_item
->callback_data
);
70 object_add_updates(obj
, ddisp
->diagram
);
71 diagram_update_connections_object(ddisp
->diagram
, obj
, TRUE
);
73 if (obj_change
!= NULL
) {
74 undo_object_change(ddisp
->diagram
, obj
, obj_change
);
76 diagram_modified(ddisp
->diagram
);
78 diagram_update_extents(ddisp
->diagram
);
80 if (obj_change
!= NULL
) {
81 undo_set_transactionpoint(ddisp
->diagram
->undo
);
83 message_warning(_("This object doesn't support Undo/Redo.\n"
84 "Undo information erased."));
85 undo_clear(ddisp
->diagram
->undo
);
88 diagram_flush(ddisp
->diagram
);
92 dia_menu_free(DiaMenu
*dia_menu
)
94 if (dia_menu
->app_data
)
95 gtk_object_destroy((GtkObject
*)dia_menu
->app_data
);
96 dia_menu
->app_data
= NULL
;
97 dia_menu
->app_data_free
= NULL
;
101 This add a Properties... menu item to the GtkMenu passed, at the
102 end and set the callback to raise de properties dialog
104 pass TRUE in separator if you want to insert a separator before the poperty
108 add_properties_menu_item (GtkMenu
*menu
, gboolean separator
)
110 GtkWidget
*menu_item
= NULL
;
113 menu_item
= gtk_menu_item_new();
114 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
115 gtk_widget_show(menu_item
);
118 menu_item
= gtk_menu_item_new_with_label(_("Properties..."));
119 g_signal_connect(GTK_OBJECT(menu_item
), "activate", G_CALLBACK(dialogs_properties_callback
), NULL
);
120 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
121 gtk_widget_show(menu_item
);
125 create_object_menu(DiaMenu
*dia_menu
)
129 GtkWidget
*menu_item
;
131 menu
= gtk_menu_new();
133 if ( dia_menu
->title
) {
134 menu_item
= gtk_menu_item_new_with_label(gettext(dia_menu
->title
));
135 gtk_widget_set_sensitive(menu_item
, FALSE
);
136 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
137 gtk_widget_show(menu_item
);
140 menu_item
= gtk_menu_item_new();
141 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
142 gtk_widget_show(menu_item
);
144 for (i
=0;i
<dia_menu
->num_items
;i
++) {
145 DiaMenuItem
*item
= &dia_menu
->items
[i
];
147 if (item
->active
& DIAMENU_TOGGLE
) {
149 menu_item
= gtk_check_menu_item_new_with_label(gettext(item
->text
));
151 menu_item
= gtk_check_menu_item_new();
152 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item
),
153 item
->active
& DIAMENU_TOGGLE_ON
);
154 gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(menu_item
),
158 menu_item
= gtk_menu_item_new_with_label(gettext(item
->text
));
160 menu_item
= gtk_menu_item_new();
162 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
163 gtk_widget_show(menu_item
);
164 item
->app_data
= menu_item
;
165 if ( dia_menu
->items
[i
].callback
) {
166 /* only connect signal handler if there is actually a callback */
167 gtk_signal_connect(GTK_OBJECT(menu_item
), "activate",
168 (GtkSignalFunc
)object_menu_proxy
, &dia_menu
->items
[i
]);
170 if ( item
->callback_data
) {
171 /* This menu item is a submenu if it has no callback, but does
172 * Have callback_data. In this case the callback_data is a
173 * DiaMenu pointer for the submenu. */
174 if ( ((DiaMenu
*)item
->callback_data
)->app_data
== NULL
) {
175 /* Create the popup menu items for the submenu. */
176 create_object_menu((DiaMenu
*)(item
->callback_data
) ) ;
177 gtk_menu_item_set_submenu( GTK_MENU_ITEM (menu_item
),
178 GTK_WIDGET(((DiaMenu
*)(item
->callback_data
))->app_data
));
184 /* Finally add a Properties... menu item for objects*/
185 add_properties_menu_item(GTK_MENU (menu
), i
> 0);
187 dia_menu
->app_data
= menu
;
188 dia_menu
->app_data_free
= dia_menu_free
;
191 static DiaMenuItem empty_menu_items
[] = { {0, } };
192 static DiaMenu empty_menu
= {
194 sizeof(empty_menu_items
)/sizeof(DiaMenuItem
),
200 popup_object_menu(DDisplay
*ddisp
, GdkEventButton
*bevent
)
204 GtkMenu
*menu
= NULL
;
205 DiaMenu
*dia_menu
= NULL
;
206 GList
*selected_list
;
210 diagram
= ddisp
->diagram
;
211 if (g_list_length (diagram
->data
->selected
) != 1)
214 selected_list
= diagram
->data
->selected
;
216 /* Have to have exactly one selected object */
217 if (selected_list
== NULL
|| g_list_next(selected_list
) != NULL
) {
218 message_error("Selected list is %s while selected_count is %d\n",
219 (selected_list
?"long":"empty"), g_list_length (diagram
->data
->selected
));
223 obj
= (DiaObject
*)g_list_first(selected_list
)->data
;
225 /* Possibly react differently at a handle? */
227 /* Get its menu, and remember the # of object-generated items */
228 if (obj
->ops
->get_object_menu
== NULL
) {
229 dia_menu
= &empty_menu
;
230 if (dia_menu
->title
&&
231 (0 != strcmp(dia_menu
->title
,obj
->type
->name
))) {
232 dia_menu
->app_data_free(dia_menu
);
234 dia_menu
->title
= obj
->type
->name
;
237 dia_menu
= (obj
->ops
->get_object_menu
)(obj
, &object_menu_clicked_point
);
238 num_items
= dia_menu
->num_items
;
241 if (dia_menu
->app_data
== NULL
) {
242 create_object_menu(dia_menu
);
244 /* Update active/nonactive menuitems */
245 for (i
=0;i
<num_items
;i
++) {
246 DiaMenuItem
*item
= &dia_menu
->items
[i
];
247 gtk_widget_set_sensitive(GTK_WIDGET(item
->app_data
),
248 item
->active
& DIAMENU_ACTIVE
);
249 if (item
->active
& DIAMENU_TOGGLE
) {
250 g_signal_handlers_block_by_func(GTK_CHECK_MENU_ITEM(item
->app_data
),
251 (GtkSignalFunc
)object_menu_proxy
, item
);
252 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
->app_data
),
253 item
->active
& DIAMENU_TOGGLE_ON
);
254 g_signal_handlers_unblock_by_func(GTK_CHECK_MENU_ITEM(item
->app_data
),
255 (GtkSignalFunc
)object_menu_proxy
, item
);
259 menu
= GTK_MENU(dia_menu
->app_data
);
260 /* add the properties menu item to raise the properties from the contextual menu */
262 popup_shell
= ddisp
->shell
;
263 gtk_menu_popup(menu
, NULL
, NULL
, NULL
, NULL
, bevent
->button
, bevent
->time
);
267 ddisplay_focus_in_event(GtkWidget
*widget
, GdkEventFocus
*event
, gpointer data
)
271 g_return_val_if_fail (widget
!= NULL
, FALSE
);
272 g_return_val_if_fail (event
!= NULL
, FALSE
);
273 g_return_val_if_fail (data
!= NULL
, FALSE
);
275 ddisp
= (DDisplay
*)data
;
277 GTK_WIDGET_SET_FLAGS(widget
, GTK_HAS_FOCUS
);
279 gtk_im_context_focus_in(GTK_IM_CONTEXT(ddisp
->im_context
));
285 ddisplay_focus_out_event(GtkWidget
*widget
, GdkEventFocus
*event
,gpointer data
)
290 g_return_val_if_fail (widget
!= NULL
, FALSE
);
291 g_return_val_if_fail (event
!= NULL
, FALSE
);
292 g_return_val_if_fail (data
!= NULL
, FALSE
);
296 ddisp
= (DDisplay
*)data
;
298 GTK_WIDGET_UNSET_FLAGS (widget
, GTK_HAS_FOCUS
);
300 gtk_im_context_focus_out(GTK_IM_CONTEXT(ddisp
->im_context
));
306 ddisplay_realize(GtkWidget
*widget
, gpointer data
)
310 g_return_if_fail(widget
!= NULL
);
311 g_return_if_fail(data
!= NULL
);
313 ddisp
= (DDisplay
*)data
;
315 gtk_im_context_set_client_window(GTK_IM_CONTEXT(ddisp
->im_context
),
316 GDK_WINDOW(ddisp
->shell
->window
));
320 ddisplay_unrealize (GtkWidget
*widget
, gpointer data
)
324 g_return_if_fail (widget
!= NULL
);
325 g_return_if_fail (data
!= NULL
);
327 ddisp
= (DDisplay
*) data
;
329 if (ddisp
->im_context
)
330 gtk_im_context_set_client_window(GTK_IM_CONTEXT(ddisp
->im_context
),
331 GDK_WINDOW(ddisp
->shell
->window
));
335 ddisplay_size_allocate (GtkWidget
*widget
,
336 GtkAllocation
*allocation
,
341 g_return_if_fail (widget
!= NULL
);
342 g_return_if_fail (allocation
!= NULL
);
343 g_return_if_fail (data
!= NULL
);
346 g_print ("ddisp::size_allocate: %d,%d -> %d,%d\n", allocation
->width
, allocation
->height
,
347 widget
->allocation
.width
, widget
->allocation
.height
);
349 widget
->allocation
= *allocation
;
350 ddisp
= (DDisplay
*)data
;
354 ddisplay_popup_menu(DDisplay
*ddisp
, GdkEventButton
*event
)
358 popup_shell
= ddisp
->shell
;
359 menus_get_image_menu(&menu
, NULL
);
361 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
362 event
->button
, event
->time
);
365 handle_key_event(DDisplay
*ddisp
, Focus
*focus
, guint keysym
,
366 const gchar
*str
, int strlen
)
368 DiaObject
*obj
= focus_get_object(focus
);
369 Point p
= obj
->position
;
370 ObjectChange
*obj_change
= NULL
;
373 object_add_updates(obj
, ddisp
->diagram
);
375 modified
= (focus
->key_event
)(focus
, keysym
, str
, strlen
,
378 /* Make sure object updates its data and its connected: */
380 (obj
->ops
->move
)(obj
,&p
);
381 diagram_update_connections_object(ddisp
->diagram
,obj
,TRUE
);
383 object_add_updates(obj
, ddisp
->diagram
);
386 if (obj_change
!= NULL
) {
387 undo_object_change(ddisp
->diagram
, obj
, obj_change
);
388 undo_set_transactionpoint(ddisp
->diagram
->undo
);
390 diagram_modified(ddisp
->diagram
);
393 diagram_flush(ddisp
->diagram
);
398 ddisplay_im_context_commit(GtkIMContext
*context
, const gchar
*str
,
401 /* When using IM, we'll not get many key events past the IM filter,
404 regardless of the platform, "str" should be a clean UTF8 string
405 (the default IM on X should perform the local->UTF8 conversion)
408 Focus
*focus
= active_focus();
410 ddisplay_im_context_preedit_reset(ddisp
, focus
);
413 handle_key_event(ddisp
, focus
, 0, str
, g_utf8_strlen(str
,-1));
417 ddisplay_im_context_preedit_changed(GtkIMContext
*context
,
421 Focus
*focus
= active_focus();
423 ddisplay_im_context_preedit_reset(ddisp
, focus
);
425 gtk_im_context_get_preedit_string(context
, &ddisp
->preedit_string
,
426 &ddisp
->preedit_attrs
, &cursor_pos
);
427 if (ddisp
->preedit_string
!= NULL
) {
429 handle_key_event(ddisp
, focus
, 0, ddisp
->preedit_string
,
430 g_utf8_strlen(ddisp
->preedit_string
,-1));
432 ddisplay_im_context_preedit_reset(ddisp
, focus
);
437 /** Main input handler for a diagram canvas.
440 ddisplay_canvas_events (GtkWidget
*canvas
,
444 GdkEventExpose
*eevent
;
445 GdkEventMotion
*mevent
;
446 GdkEventButton
*bevent
;
448 GdkEventScroll
*sevent
;
450 GdkModifierType tmask
;
461 static gboolean moving
= FALSE
;
471 eevent
= (GdkEventExpose
*) event
;
472 ddisplay_add_display_area(ddisp
,
473 eevent
->area
.x
, eevent
->area
.y
,
474 eevent
->area
.x
+ eevent
->area
.width
,
475 eevent
->area
.y
+ eevent
->area
.height
);
476 ddisplay_flush(ddisp
);
480 sevent
= (GdkEventScroll
*) event
;
482 switch (sevent
->direction
)
485 if (sevent
->state
& GDK_SHIFT_MASK
)
486 ddisplay_scroll_left(ddisp
);
487 else if (sevent
->state
& GDK_CONTROL_MASK
) {
488 ddisplay_untransform_coords(ddisp
, sevent
->x
, sevent
->y
, &middle
.x
, &middle
.y
);
489 ddisplay_zoom(ddisp
, &middle
, 2);
492 ddisplay_scroll_up(ddisp
);
494 case GDK_SCROLL_DOWN
:
495 if (sevent
->state
& GDK_SHIFT_MASK
)
496 ddisplay_scroll_right(ddisp
);
497 else if (sevent
->state
& GDK_CONTROL_MASK
) {
498 ddisplay_untransform_coords(ddisp
, sevent
->x
, sevent
->y
, &middle
.x
, &middle
.y
);
499 ddisplay_zoom(ddisp
, &middle
, 0.5);
502 ddisplay_scroll_down(ddisp
);
504 case GDK_SCROLL_LEFT
:
505 ddisplay_scroll_left(ddisp
);
507 case GDK_SCROLL_RIGHT
:
508 ddisplay_scroll_right(ddisp
);
513 ddisplay_flush (ddisp
);
517 if (ddisp
->renderer
!= NULL
) {
518 width
= dia_renderer_get_width_pixels (ddisp
->renderer
);
519 height
= dia_renderer_get_height_pixels (ddisp
->renderer
);
520 new_size
= ((width
!= ddisp
->canvas
->allocation
.width
) ||
521 (height
!= ddisp
->canvas
->allocation
.height
));
526 ddisplay_resize_canvas(ddisp
,
527 ddisp
->canvas
->allocation
.width
,
528 ddisp
->canvas
->allocation
.height
);
529 ddisplay_update_scrollbars(ddisp
);
531 display_set_active(ddisp
);
534 case GDK_FOCUS_CHANGE
: {
535 GdkEventFocus
*focus
= (GdkEventFocus
*)event
;
537 display_set_active(ddisp
);
538 ddisplay_do_update_menu_sensitivity(ddisp
);
542 case GDK_2BUTTON_PRESS
:
543 display_set_active(ddisp
);
544 bevent
= (GdkEventButton
*) event
;
545 state
= bevent
->state
;
547 switch (bevent
->button
)
552 if (active_tool
->double_click_func
)
553 (*active_tool
->double_click_func
) (active_tool
, bevent
, ddisp
);
567 case GDK_BUTTON_PRESS
:
568 display_set_active(ddisp
);
569 bevent
= (GdkEventButton
*) event
;
570 state
= bevent
->state
;
572 ddisplay_untransform_coords(ddisp
,
573 (int)bevent
->x
, (int)bevent
->y
,
574 &object_menu_clicked_point
.x
,
575 &object_menu_clicked_point
.y
);
577 switch (bevent
->button
)
582 /* get the focus again, may be lost by zoom combo */
584 gtk_widget_grab_focus(canvas
);
585 if (active_tool
->button_press_func
)
586 (*active_tool
->button_press_func
) (active_tool
, bevent
, ddisp
);
590 if (ddisp
->menu_bar
== NULL
) {
591 popup_object_menu(ddisp
, bevent
);
593 else if (!transient_tool
) {
594 gtk_widget_grab_focus(canvas
);
595 transient_tool
= create_scroll_tool();
596 (*transient_tool
->button_press_func
) (transient_tool
, bevent
, ddisp
);
603 if (ddisp
->menu_bar
== NULL
) {
604 if (bevent
->state
& GDK_CONTROL_MASK
) {
605 /* for two button mouse users ... */
606 popup_object_menu(ddisp
, bevent
);
609 ddisplay_popup_menu(ddisp
, bevent
);
613 popup_object_menu(ddisp
, bevent
);
621 case GDK_BUTTON_RELEASE
:
622 display_set_active(ddisp
);
623 bevent
= (GdkEventButton
*) event
;
624 state
= bevent
->state
;
626 switch (bevent
->button
)
631 if (active_tool
->button_release_func
)
632 (*active_tool
->button_release_func
) (active_tool
,
637 if (transient_tool
) {
638 (*transient_tool
->button_release_func
) (transient_tool
,
641 tool_free(transient_tool
);
642 transient_tool
= NULL
;
654 case GDK_MOTION_NOTIFY
:
655 /* get the pointer position */
656 gdk_window_get_pointer (canvas
->window
, &tx
, &ty
, &tmask
);
658 mevent
= (GdkEventMotion
*) event
;
659 state
= mevent
->state
;
661 if (mevent
->is_hint
) {
664 mevent
->state
= tmask
;
665 mevent
->is_hint
= FALSE
;
667 if (transient_tool
&& (*transient_tool
->motion_func
))
668 (*transient_tool
->motion_func
) (transient_tool
, mevent
, ddisp
);
669 else if (active_tool
->motion_func
)
670 (*active_tool
->motion_func
) (active_tool
, mevent
, ddisp
);
674 if (moving
) /*Disable Keyboard accels whilst draggin an object*/
676 display_set_active(ddisp
);
677 kevent
= (GdkEventKey
*)event
;
678 state
= kevent
->state
;
680 im_context_used
= FALSE
;
682 focus
= active_focus();
684 /* Keys goes to the active focus. */
685 obj
= focus_get_object(focus
);
686 if (diagram_is_selected(ddisp
->diagram
, obj
)) {
688 if (!gtk_im_context_filter_keypress(
689 GTK_IM_CONTEXT(ddisp
->im_context
), kevent
)) {
691 if (kevent
->keyval
== GDK_Tab
) {
692 focus
= textedit_move_focus(ddisp
, focus
,
693 (state
& GDK_SHIFT_MASK
) == 0);
694 obj
= focus_get_object(focus
);
696 /*! key event not swallowed by the input method ? */
697 handle_key_event(ddisp
, focus
, kevent
->keyval
,
698 kevent
->string
, kevent
->length
);
700 diagram_flush(ddisp
->diagram
);
703 return_val
= key_handled
= im_context_used
= TRUE
;
707 #if 0 /* modifier requirment added 2004-07-17, IMO reenabling unmodified keys here
708 * shouldn't break im_context handling. How to test? --hb
710 if (!key_handled
&& (state
& (GDK_CONTROL_MASK
| GDK_MOD1_MASK
))) {
714 /* IM doesn't need receive keys, take care of it ourselves. */
717 switch(kevent
->keyval
) {
719 ddisplay_scroll_up(ddisp
);
720 ddisplay_flush(ddisp
);
723 ddisplay_scroll_down(ddisp
);
724 ddisplay_flush(ddisp
);
727 ddisplay_scroll_left(ddisp
);
728 ddisplay_flush(ddisp
);
731 ddisplay_scroll_right(ddisp
);
732 ddisplay_flush(ddisp
);
736 visible
= &ddisp
->visible
;
737 middle
.x
= visible
->left
*0.5 + visible
->right
*0.5;
738 middle
.y
= visible
->top
*0.5 + visible
->bottom
*0.5;
740 ddisplay_zoom(ddisp
, &middle
, M_SQRT2
);
742 case GDK_KP_Subtract
:
744 visible
= &ddisp
->visible
;
745 middle
.x
= visible
->left
*0.5 + visible
->right
*0.5;
746 middle
.y
= visible
->top
*0.5 + visible
->bottom
*0.5;
748 ddisplay_zoom(ddisp
, &middle
, M_SQRT1_2
);
752 if (active_tool
->type
== MAGNIFY_TOOL
)
753 set_zoom_out(active_tool
);
759 if (kevent
->string
&& kevent
->keyval
== ' ') {
760 tool_select_former();
761 } else if ((kevent
->state
& (GDK_MOD1_MASK
|GDK_CONTROL_MASK
)) == 0 &&
762 kevent
->length
!= 0) {
763 /* Find first editable */
765 modify_edit_first_text(ddisp
);
772 if (!im_context_used
)
773 gtk_im_context_reset(GTK_IM_CONTEXT(ddisp
->im_context
));
777 case GDK_KEY_RELEASE
:
778 kevent
= (GdkEventKey
*) event
;
779 state
= kevent
->state
;
780 if (gtk_im_context_filter_keypress(GTK_IM_CONTEXT(ddisp
->im_context
),
784 switch(kevent
->keyval
) {
787 if (active_tool
->type
== MAGNIFY_TOOL
)
788 set_zoom_in(active_tool
);
804 ddisplay_hsb_update (GtkAdjustment
*adjustment
,
807 ddisplay_set_origo(ddisp
, adjustment
->value
, ddisp
->origo
.y
);
808 ddisplay_add_update_all(ddisp
);
809 ddisplay_flush(ddisp
);
814 ddisplay_vsb_update (GtkAdjustment
*adjustment
,
817 ddisplay_set_origo(ddisp
, ddisp
->origo
.x
, adjustment
->value
);
818 ddisplay_add_update_all(ddisp
);
819 ddisplay_flush(ddisp
);
824 ddisplay_delete (GtkWidget
*widget
, GdkEvent
*event
, gpointer data
)
828 ddisp
= (DDisplay
*)data
;
830 ddisplay_close(ddisp
);
835 ddisplay_destroy (GtkWidget
*widget
, gpointer data
)
839 ddisp
= (DDisplay
*) data
;
841 if (popup_shell
== ddisp
->shell
) {
845 ddisplay_really_destroy(ddisp
);
848 /* returns NULL if object cannot be created */
850 ddisplay_drop_object(DDisplay
*ddisp
, gint x
, gint y
, DiaObjectType
*otype
,
854 Point droppoint_orig
;
855 Handle
*handle1
, *handle2
;
856 DiaObject
*obj
, *p_obj
;
860 ddisplay_untransform_coords(ddisp
, x
, y
, &droppoint
.x
, &droppoint
.y
);
862 /* save it before snap_to_grid modifies it */
863 droppoint_orig
= droppoint
;
865 snap_to_grid(ddisp
, &droppoint
.x
, &droppoint
.y
);
867 obj
= dia_object_default_create (otype
, &droppoint
,
872 click_distance
= ddisplay_untransform_length(ddisp
, 3.0);
874 /* Notice that using diagram_find_clicked_object doesn't allow any object
875 * below the first to be a parent. This should be fixed.
878 p_obj
= diagram_find_clicked_object(ddisp
->diagram
, &droppoint_orig
,
881 if (p_obj
&& p_obj
->can_parent
) /* the tool was dropped inside an object that takes children*/
883 Rectangle p_ext
, c_ext
;
884 real parent_height
, child_height
, parent_width
, child_width
;
885 real vadjust
= 0.0, hadjust
= 0.0;
889 p_obj
->children
= g_list_append(p_obj
->children
, obj
);
891 /* This is not really what we want. We want the box containing all
892 * rendered parts of the object (not the bbox). But it'll do for now,
893 * since we don't have the 'rendered bbox'.
896 parent_handle_extents(p_obj
, &p_ext
);
897 parent_handle_extents(obj
, &c_ext
);
899 parent_height
= p_ext
.bottom
- p_ext
.top
;
900 child_height
= c_ext
.bottom
- c_ext
.top
;
902 parent_width
= p_ext
.right
- p_ext
.left
;
903 child_width
= c_ext
.right
- c_ext
.left
;
905 /* we need the pre-snap position, but must remember that handles can
906 * be to the left of the droppoint */
907 c_ext
.left
= droppoint_orig
.x
- (obj
->position
.x
- c_ext
.left
);
908 c_ext
.top
= droppoint_orig
.y
- (obj
->position
.y
- c_ext
.top
);
909 c_ext
.right
= c_ext
.left
+ child_width
;
910 c_ext
.bottom
= c_ext
.top
+ child_height
;
912 if (c_ext
.left
< p_ext
.left
) {
913 hadjust
= p_ext
.left
- c_ext
.left
;
914 } else if (c_ext
.right
> p_ext
.right
) {
915 hadjust
= p_ext
.right
- c_ext
.right
;
917 if (c_ext
.top
< p_ext
.top
) {
918 vadjust
= p_ext
.top
- c_ext
.top
;
919 } else if (c_ext
.bottom
> p_ext
.bottom
) {
920 vadjust
= p_ext
.bottom
- c_ext
.bottom
;
923 if (child_width
> parent_width
||
924 child_height
> parent_height
) {
925 message_error(_("The object you dropped cannot fit into its parent. \nEither expand the parent object, or drop the object elsewhere."));
926 obj
->parent
->children
= g_list_remove(obj
->parent
->children
, obj
);
927 obj
->ops
->destroy (obj
);
931 if (hadjust
|| vadjust
) {
932 new_pos
.x
= droppoint
.x
+ hadjust
;
933 new_pos
.y
= droppoint
.y
+ vadjust
;
934 obj
->ops
->move(obj
, &new_pos
);
938 diagram_add_object(ddisp
->diagram
, obj
);
939 diagram_remove_all_selected(ddisp
->diagram
, TRUE
); /* unselect all */
940 diagram_select(ddisp
->diagram
, obj
);
941 obj
->ops
->selectf(obj
, &droppoint
, ddisp
->renderer
);
942 textedit_activate_object(ddisp
, obj
, NULL
);
944 /* Connect first handle if possible: */
945 if ((handle1
!= NULL
) &&
946 (handle1
->connect_type
!= HANDLE_NONCONNECTABLE
)) {
947 object_connect_display(ddisp
, obj
, handle1
, FALSE
);
949 object_add_updates(obj
, ddisp
->diagram
);
950 ddisplay_do_update_menu_sensitivity(ddisp
);
951 diagram_flush(ddisp
->diagram
);
953 list
= g_list_prepend(NULL
, obj
);
954 undo_insert_objects(ddisp
->diagram
, list
, 1);
955 diagram_update_extents(ddisp
->diagram
);
957 undo_set_transactionpoint(ddisp
->diagram
->undo
);
958 diagram_modified(ddisp
->diagram
);
959 if (prefs
.reset_tools_after_create
)