* including scroll_tool.h
[dia.git] / app / disp_callbacks.c
blob05078cefc36f24dc0cb2d557bd93374363a063e4
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.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <string.h>
25 #include <math.h>
26 #include <gtk/gtk.h>
27 #include <gdk/gdkkeysyms.h>
29 #ifdef GNOME
30 #include <gnome.h>
31 #endif
32 #include "display.h"
33 #include "diagram.h"
34 #include "tool.h"
35 #include "interface.h"
36 #include "focus.h"
37 #include "object_ops.h"
38 #include "connectionpoint_ops.h"
39 #include "menus.h"
40 #include "message.h"
41 #include "intl.h"
42 #include "magnify.h"
43 #include "diamenu.h"
44 #include "preferences.h"
45 #include "scroll_tool.h"
47 /* This contains the point that was clicked to get this menu */
48 static Point object_menu_clicked_point;
50 static void
51 object_menu_proxy(GtkWidget *widget, gpointer data)
53 DiaMenuItem *dia_menu_item;
54 ObjectChange *obj_change;
56 DDisplay *ddisp = ddisplay_active();
57 Object *obj = (Object *)ddisp->diagram->data->selected->data;
59 dia_menu_item = (DiaMenuItem *) data;
62 object_add_updates(obj, ddisp->diagram);
63 obj_change = (dia_menu_item->callback)(obj, &object_menu_clicked_point,
64 dia_menu_item->callback_data);
65 object_add_updates(obj, ddisp->diagram);
66 diagram_update_connections_object(ddisp->diagram, obj, TRUE);
68 if (obj_change != NULL) {
69 undo_object_change(ddisp->diagram, obj, obj_change);
71 diagram_modified(ddisp->diagram);
73 diagram_update_extents(ddisp->diagram);
75 if (obj_change != NULL) {
76 undo_set_transactionpoint(ddisp->diagram->undo);
77 } else {
78 message_warning(_("This object doesn't support Undo/Redo.\n"
79 "Undo information erased."));
80 undo_clear(ddisp->diagram->undo);
84 diagram_flush(ddisp->diagram);
87 static void
88 dia_menu_free(DiaMenu *dia_menu) {
89 if (dia_menu->app_data)
90 gtk_object_destroy((GtkObject *)dia_menu->app_data);
91 dia_menu->app_data = NULL;
92 dia_menu->app_data_free = NULL;
95 static void
96 create_object_menu(DiaMenu *dia_menu)
98 int i;
99 GtkWidget *menu;
100 GtkWidget *menu_item;
102 menu = gtk_menu_new();
103 //FIXME?: gtk_menu_ensure_uline_accel_group (GTK_MENU (menu)) ;
105 if ( dia_menu->title ) {
106 menu_item = gtk_menu_item_new_with_label(gettext(dia_menu->title));
107 gtk_widget_set_sensitive(menu_item, FALSE);
108 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
109 gtk_widget_show(menu_item);
112 menu_item = gtk_menu_item_new();
113 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
114 gtk_widget_show(menu_item);
116 for (i=0;i<dia_menu->num_items;i++) {
117 DiaMenuItem *item = &dia_menu->items[i];
119 if (item->active & DIAMENU_TOGGLE) {
120 if (item->text)
121 menu_item = gtk_check_menu_item_new_with_label(gettext(item->text));
122 else
123 menu_item = gtk_check_menu_item_new();
124 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item),
125 item->active & DIAMENU_TOGGLE_ON);
126 gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(menu_item),
127 TRUE);
128 } else {
129 if (item->text)
130 menu_item = gtk_menu_item_new_with_label(gettext(item->text));
131 else
132 menu_item = gtk_menu_item_new();
134 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
135 gtk_widget_show(menu_item);
136 item->app_data = menu_item;
137 if ( dia_menu->items[i].callback ) {
138 /* only connect signal handler if there is actually a callback */
139 gtk_signal_connect(GTK_OBJECT(menu_item), "activate",
140 (GtkSignalFunc)object_menu_proxy, &dia_menu->items[i]);
141 } else {
142 if ( item->callback_data ) {
143 /* This menu item is a submenu if it has no callback, but does
144 * Have callback_data. In this case the callback_data is a
145 * DiaMenu pointer for the submenu. */
146 if ( ((DiaMenu*)item->callback_data)->app_data == NULL ) {
147 /* Create the popup menu items for the submenu. */
148 create_object_menu((DiaMenu*)(item->callback_data) ) ;
149 gtk_menu_item_set_submenu( GTK_MENU_ITEM (menu_item),
150 GTK_WIDGET(((DiaMenu*)(item->callback_data))->app_data));
156 dia_menu->app_data = menu;
157 dia_menu->app_data_free = dia_menu_free;
160 static void
161 popup_object_menu(DDisplay *ddisp, GdkEventButton *bevent)
163 Diagram *diagram;
164 Object *obj;
165 GtkMenu *menu = NULL;
166 DiaMenu *dia_menu = NULL;
167 GtkWidget *menu_item;
168 GList *selected_list;
169 static GtkWidget *no_menu = NULL;
170 int i;
172 diagram = ddisp->diagram;
173 if (diagram->data->selected_count != 1)
174 return;
176 selected_list = diagram->data->selected;
178 /* Have to have exactly one selected object */
179 if (selected_list == NULL || g_list_next(selected_list) != NULL) {
180 message_error("Selected list is %s while selected_count is %d\n",
181 (selected_list?"long":"empty"), diagram->data->selected_count);
182 return;
185 obj = (Object *)g_list_first(selected_list)->data;
187 /* Possibly react differently at a handle? */
189 /* Get its menu */
190 if (obj->ops->get_object_menu == NULL) {
191 dia_menu = NULL;
192 } else {
193 dia_menu = (obj->ops->get_object_menu)(obj, &object_menu_clicked_point);
196 if (dia_menu != NULL) {
197 if (dia_menu->app_data == NULL)
198 create_object_menu(dia_menu);
200 /* Update active/nonactive menuitems */
201 for (i=0;i<dia_menu->num_items;i++) {
202 DiaMenuItem *item = &dia_menu->items[i];
203 gtk_widget_set_sensitive(GTK_WIDGET(item->app_data),
204 item->active & DIAMENU_ACTIVE);
205 if (item->active & DIAMENU_TOGGLE) {
206 g_signal_handlers_block_by_func(GTK_CHECK_MENU_ITEM(item->app_data),
207 (GtkSignalFunc)object_menu_proxy, item);
208 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item->app_data),
209 item->active & DIAMENU_TOGGLE_ON);
210 g_signal_handlers_unblock_by_func(GTK_CHECK_MENU_ITEM(item->app_data),
211 (GtkSignalFunc)object_menu_proxy, item);
215 menu = GTK_MENU(dia_menu->app_data);
216 } else {
217 if (no_menu == NULL) {
218 no_menu = gtk_menu_new();
220 menu_item = gtk_menu_item_new_with_label(_("No object menu"));
221 gtk_menu_shell_append (GTK_MENU_SHELL (no_menu), menu_item);
222 gtk_widget_show(menu_item);
223 gtk_widget_set_sensitive(menu_item, FALSE);
225 menu = GTK_MENU(no_menu);
228 popup_shell = ddisp->shell;
229 gtk_menu_popup(menu, NULL, NULL, NULL, NULL, bevent->button, bevent->time);
232 gint
233 ddisplay_focus_in_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
235 DDisplay *ddisp;
237 g_return_val_if_fail (widget != NULL, FALSE);
238 g_return_val_if_fail (event != NULL, FALSE);
239 g_return_val_if_fail (data != NULL, FALSE);
241 ddisp = (DDisplay *)data;
243 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
244 /* FIXME?: gtk_widget_draw_focus(widget); */
245 gtk_im_context_focus_in(GTK_IM_CONTEXT(ddisp->im_context));
247 return FALSE;
250 gint
251 ddisplay_focus_out_event(GtkWidget *widget, GdkEventFocus *event,gpointer data)
253 DDisplay *ddisp;
254 int return_val;
256 g_return_val_if_fail (widget != NULL, FALSE);
257 g_return_val_if_fail (event != NULL, FALSE);
258 g_return_val_if_fail (data != NULL, FALSE);
260 return_val = FALSE;
262 ddisp = (DDisplay *)data;
264 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
266 gtk_im_context_focus_out(GTK_IM_CONTEXT(ddisp->im_context));
268 return return_val;
271 void
272 ddisplay_realize(GtkWidget *widget, gpointer data)
274 DDisplay *ddisp;
276 g_return_if_fail(widget != NULL);
277 g_return_if_fail(data != NULL);
279 ddisp = (DDisplay *)data;
281 gtk_im_context_set_client_window(GTK_IM_CONTEXT(ddisp->im_context),
282 GDK_WINDOW(ddisp->shell->window));
285 void
286 ddisplay_unrealize (GtkWidget *widget, gpointer data)
288 DDisplay *ddisp;
290 g_return_if_fail (widget != NULL);
291 g_return_if_fail (data != NULL);
293 ddisp = (DDisplay *) data;
295 if (ddisp->im_context)
296 gtk_im_context_set_client_window(GTK_IM_CONTEXT(ddisp->im_context),
297 GDK_WINDOW(ddisp->shell->window));
300 void
301 ddisplay_size_allocate (GtkWidget *widget,
302 GtkAllocation *allocation,
303 gpointer data)
305 DDisplay *ddisp;
307 g_return_if_fail (widget != NULL);
308 g_return_if_fail (allocation != NULL);
309 g_return_if_fail (data != NULL);
311 widget->allocation = *allocation;
312 ddisp = (DDisplay *)data;
315 void
316 ddisplay_popup_menu(DDisplay *ddisp, GdkEventButton *event)
318 GtkWidget *menu;
320 popup_shell = ddisp->shell;
321 menus_get_image_menu(&menu, NULL);
323 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
324 event->button, event->time);
327 static void handle_key_event(DDisplay *ddisp, Focus *focus, guint keysym,
328 const gchar *str, int strlen) {
329 Object *obj = focus->obj;
330 Point p = obj->position;
331 ObjectChange *obj_change = NULL;
332 gboolean modified;
334 object_add_updates(obj, ddisp->diagram);
336 modified = (focus->key_event)(focus, keysym, str, strlen,
337 &obj_change);
339 /* Make sure object updates its data and its connected: */
340 p = obj->position;
341 (obj->ops->move)(obj,&p);
342 diagram_update_connections_object(ddisp->diagram,obj,TRUE);
344 object_add_updates(obj, ddisp->diagram);
346 if (modified) {
347 diagram_modified(ddisp->diagram);
348 if (obj_change != NULL) {
349 undo_object_change(ddisp->diagram, obj, obj_change);
350 undo_set_transactionpoint(ddisp->diagram->undo);
354 diagram_flush(ddisp->diagram);
358 void ddisplay_im_context_commit(GtkIMContext *context, const gchar *str,
359 DDisplay *ddisp) {
360 /* When using IM, we'll not get many key events past the IM filter,
361 mostly IM Commits.
363 regardless of the platform, "str" should be a clean UTF8 string
364 (the default IM on X should perform the local->UTF8 conversion)
367 handle_key_event(ddisp, active_focus(), 0, str, g_utf8_strlen(str,-1));
370 void ddisplay_im_context_preedit_changed(GtkIMContext *context,
371 DDisplay *ddisp) {
372 /* char *str;
373 PangoAttrList *attrs;
374 gint cursor_pos;
376 gtk_im_context_get_preedit_string(ddisp->im_context,
377 &str,&attrs,&cursor_pos);
379 g_message("received a 'preedit changed'; str=%s cursor_pos=%i",
380 str,cursor_pos);
382 g_free(str);
383 pango_attr_list_unref(attrs);
387 gint
388 ddisplay_canvas_events (GtkWidget *canvas,
389 GdkEvent *event,
390 DDisplay *ddisp)
392 GdkEventExpose *eevent;
393 GdkEventMotion *mevent;
394 GdkEventButton *bevent;
395 GdkEventKey *kevent;
396 GdkEventScroll *sevent;
397 gint tx, ty;
398 GdkModifierType tmask;
399 guint state = 0;
400 Focus *focus;
401 Object *obj;
402 Rectangle *visible;
403 Point middle;
404 int return_val;
405 int key_handled;
406 int width, height;
407 int new_size;
409 return_val = FALSE;
411 if (!canvas->window)
412 return FALSE;
414 switch (event->type)
416 case GDK_EXPOSE:
417 eevent = (GdkEventExpose *) event;
418 ddisplay_add_display_area(ddisp,
419 eevent->area.x, eevent->area.y,
420 eevent->area.x + eevent->area.width,
421 eevent->area.y + eevent->area.height);
422 ddisplay_flush(ddisp);
423 break;
425 case GDK_SCROLL:
426 sevent = (GdkEventScroll *) event;
428 switch (sevent->direction)
430 case GDK_SCROLL_UP:
431 if (sevent->state & GDK_SHIFT_MASK)
432 ddisplay_scroll_left(ddisp);
433 else if (sevent->state & GDK_CONTROL_MASK) {
434 ddisplay_untransform_coords(ddisp, sevent->x, sevent->y, &middle.x, &middle.y);
435 ddisplay_zoom(ddisp, &middle, 2);
437 else
438 ddisplay_scroll_up(ddisp);
439 break;
440 case GDK_SCROLL_DOWN:
441 if (sevent->state & GDK_SHIFT_MASK)
442 ddisplay_scroll_right(ddisp);
443 else if (sevent->state & GDK_CONTROL_MASK) {
444 ddisplay_untransform_coords(ddisp, sevent->x, sevent->y, &middle.x, &middle.y);
445 ddisplay_zoom(ddisp, &middle, 0.5);
447 else
448 ddisplay_scroll_down(ddisp);
449 break;
450 case GDK_SCROLL_LEFT:
451 ddisplay_scroll_left(ddisp);
452 break;
453 case GDK_SCROLL_RIGHT:
454 ddisplay_scroll_right(ddisp);
455 break;
456 default:
457 break;
459 ddisplay_flush (ddisp);
460 break;
462 case GDK_CONFIGURE:
463 if (ddisp->renderer != NULL) {
464 width = dia_renderer_get_width_pixels (ddisp->renderer);
465 height = dia_renderer_get_height_pixels (ddisp->renderer);
466 new_size = ((width != ddisp->canvas->allocation.width) ||
467 (height != ddisp->canvas->allocation.height));
468 } else {
469 new_size = TRUE;
471 if (new_size) {
472 ddisplay_resize_canvas(ddisp,
473 ddisp->canvas->allocation.width,
474 ddisp->canvas->allocation.height);
475 ddisplay_update_scrollbars(ddisp);
477 display_set_active(ddisp);
478 break;
480 case GDK_FOCUS_CHANGE: {
481 GdkEventFocus *focus = (GdkEventFocus*)event;
482 if (focus->in) {
483 display_set_active(ddisp);
484 ddisplay_do_update_menu_sensitivity(ddisp);
486 break;
488 case GDK_2BUTTON_PRESS:
489 display_set_active(ddisp);
490 bevent = (GdkEventButton *) event;
491 state = bevent->state;
493 switch (bevent->button)
495 case 1:
496 if (transient_tool)
497 break;
498 if (*active_tool->double_click_func)
499 (*active_tool->double_click_func) (active_tool, bevent, ddisp);
500 break;
502 case 2:
503 break;
505 case 3:
506 break;
508 default:
509 break;
511 break;
513 case GDK_BUTTON_PRESS:
514 display_set_active(ddisp);
515 bevent = (GdkEventButton *) event;
516 state = bevent->state;
518 ddisplay_untransform_coords(ddisp,
519 (int)bevent->x, (int)bevent->y,
520 &object_menu_clicked_point.x,
521 &object_menu_clicked_point.y);
523 switch (bevent->button)
525 case 1:
526 if (transient_tool)
527 break;
528 /* get the focus again, may be lost by zoom combo */
529 gtk_widget_grab_focus(canvas);
530 if (*active_tool->button_press_func)
531 (*active_tool->button_press_func) (active_tool, bevent, ddisp);
532 break;
534 case 2:
535 if (ddisp->menu_bar == NULL) {
536 popup_object_menu(ddisp, bevent);
538 else if (!transient_tool) {
539 gtk_widget_grab_focus(canvas);
540 transient_tool = create_scroll_tool();
541 (*transient_tool->button_press_func) (transient_tool, bevent, ddisp);
543 break;
545 case 3:
546 if (transient_tool)
547 break;
548 if (ddisp->menu_bar == NULL) {
549 if (bevent->state & GDK_CONTROL_MASK) {
550 /* for two button mouse users ... */
551 popup_object_menu(ddisp, bevent);
552 break;
554 ddisplay_popup_menu(ddisp, bevent);
555 break;
557 else {
558 popup_object_menu(ddisp, bevent);
559 break;
561 default:
562 break;
564 break;
566 case GDK_BUTTON_RELEASE:
567 display_set_active(ddisp);
568 bevent = (GdkEventButton *) event;
569 state = bevent->state;
571 switch (bevent->button)
573 case 1:
574 if (*active_tool->button_release_func)
575 (*active_tool->button_release_func) (active_tool,
576 bevent, ddisp);
577 break;
579 case 2:
580 if (transient_tool) {
581 (*transient_tool->button_release_func) (transient_tool,
582 bevent, ddisp);
584 tool_free(transient_tool);
585 transient_tool = NULL;
587 break;
589 case 3:
590 break;
592 default:
593 break;
595 break;
597 case GDK_MOTION_NOTIFY:
598 /* get the pointer position */
599 gdk_window_get_pointer (canvas->window, &tx, &ty, &tmask);
601 mevent = (GdkEventMotion *) event;
602 state = mevent->state;
604 if (mevent->is_hint) {
605 mevent->x = tx;
606 mevent->y = ty;
607 mevent->state = tmask;
608 mevent->is_hint = FALSE;
610 if (transient_tool && (*transient_tool->motion_func))
611 (*transient_tool->motion_func) (transient_tool, mevent, ddisp);
612 else if (*active_tool->motion_func)
613 (*active_tool->motion_func) (active_tool, mevent, ddisp);
614 break;
616 case GDK_KEY_PRESS:
617 display_set_active(ddisp);
618 kevent = (GdkEventKey *)event;
619 state = kevent->state;
620 key_handled = FALSE;
622 focus = active_focus();
623 if ((focus != NULL) &&
624 !(state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) ) {
625 /* Keys goes to the active focus. */
626 obj = focus->obj;
627 if (diagram_is_selected(ddisp->diagram, obj)) {
629 if (!gtk_im_context_filter_keypress(
630 GTK_IM_CONTEXT(ddisp->im_context), kevent)) {
632 /*! key event not swallowed by the input method ? */
633 handle_key_event(ddisp, focus, kevent->keyval,
634 kevent->string, kevent->length);
636 diagram_flush(ddisp->diagram);
639 return_val = key_handled = TRUE;
642 if (!key_handled) {
643 /* No focus to receive keys, take care of it ourselves. */
644 return_val = TRUE;
645 gtk_im_context_reset(GTK_IM_CONTEXT(ddisp->im_context));
647 switch(kevent->keyval) {
648 case GDK_Up:
649 ddisplay_scroll_up(ddisp);
650 ddisplay_flush(ddisp);
651 break;
652 case GDK_Down:
653 ddisplay_scroll_down(ddisp);
654 ddisplay_flush(ddisp);
655 break;
656 case GDK_Left:
657 ddisplay_scroll_left(ddisp);
658 ddisplay_flush(ddisp);
659 break;
660 case GDK_Right:
661 ddisplay_scroll_right(ddisp);
662 ddisplay_flush(ddisp);
663 break;
664 case GDK_KP_Add:
665 case GDK_plus:
666 visible = &ddisp->visible;
667 middle.x = visible->left*0.5 + visible->right*0.5;
668 middle.y = visible->top*0.5 + visible->bottom*0.5;
670 ddisplay_zoom(ddisp, &middle, M_SQRT2);
671 break;
672 case GDK_KP_Subtract:
673 case GDK_minus:
674 visible = &ddisp->visible;
675 middle.x = visible->left*0.5 + visible->right*0.5;
676 middle.y = visible->top*0.5 + visible->bottom*0.5;
678 ddisplay_zoom(ddisp, &middle, M_SQRT1_2);
679 break;
680 case GDK_Shift_L:
681 case GDK_Shift_R:
682 if (active_tool->type == MAGNIFY_TOOL)
683 set_zoom_out(active_tool);
684 break;
685 default:
686 if (kevent->string && 0 == strcmp(" ",kevent->string)) {
687 tool_select_former();
688 } else {
689 return_val = FALSE;
693 break;
695 case GDK_KEY_RELEASE:
696 kevent = (GdkEventKey *) event;
697 state = kevent->state;
698 if (gtk_im_context_filter_keypress(GTK_IM_CONTEXT(ddisp->im_context),
699 kevent)) {
700 return_val = TRUE;
701 } else {
702 switch(kevent->keyval) {
703 case GDK_Shift_L:
704 case GDK_Shift_R:
705 if (active_tool->type == MAGNIFY_TOOL)
706 set_zoom_in(active_tool);
707 break;
708 default:
709 break;
712 break;
714 default:
715 break;
718 return return_val;
721 gint
722 ddisplay_hsb_update (GtkAdjustment *adjustment,
723 DDisplay *ddisp)
725 ddisplay_set_origo(ddisp, adjustment->value, ddisp->origo.y);
726 ddisplay_add_update_all(ddisp);
727 ddisplay_flush(ddisp);
728 return FALSE;
731 gint
732 ddisplay_vsb_update (GtkAdjustment *adjustment,
733 DDisplay *ddisp)
735 ddisplay_set_origo(ddisp, ddisp->origo.x, adjustment->value);
736 ddisplay_add_update_all(ddisp);
737 ddisplay_flush(ddisp);
738 return FALSE;
741 gint
742 ddisplay_delete (GtkWidget *widget, GdkEvent *event, gpointer data)
744 DDisplay *ddisp;
746 ddisp = (DDisplay *)data;
748 ddisplay_close(ddisp);
749 return TRUE;
752 void
753 ddisplay_destroy (GtkWidget *widget, gpointer data)
755 DDisplay *ddisp;
757 ddisp = (DDisplay *) data;
759 if (popup_shell == ddisp->shell) {
760 popup_shell = NULL;
763 ddisplay_really_destroy(ddisp);
766 inline int
767 round_up (double x)
769 if (x - (int) x > 0.001)
770 return (int) x + 1;
771 else
772 return (int) x ;
776 /* returns NULL if object cannot be created */
777 Object *
778 ddisplay_drop_object(DDisplay *ddisp, gint x, gint y, ObjectType *otype,
779 gpointer user_data)
781 Point droppoint;
782 Point droppoint_orig;
783 Handle *handle1, *handle2;
784 Object *obj, *p_obj;
785 GList *list;
786 real click_distance;
788 ddisplay_untransform_coords(ddisp, x, y, &droppoint.x, &droppoint.y);
790 /* save it before snap_to_grid modifies it */
791 droppoint_orig = droppoint;
793 snap_to_grid(ddisp, &droppoint.x, &droppoint.y);
795 obj = dia_object_default_create (otype, &droppoint,
796 user_data,
797 &handle1, &handle2);
800 click_distance = ddisplay_untransform_length(ddisp, 3.0);
802 p_obj = diagram_find_clicked_object(ddisp->diagram, &droppoint_orig,
803 click_distance);
805 if (p_obj && p_obj->can_parent) /* the tool was dropped inside an object that takes children*/
807 Rectangle *p_ext, *c_ext;
808 int new_height = 0, new_width = 0;
809 real parent_height, child_height, parent_width, child_width;
810 Point new_pos;
812 obj->parent = p_obj;
813 p_obj->children = g_list_append(p_obj->children, obj);
815 p_ext = parent_handle_extents(p_obj);
816 c_ext = parent_handle_extents(obj);
818 parent_height = p_ext->bottom - p_ext->top;
819 child_height = c_ext->bottom - c_ext->top;
821 parent_width = p_ext->right - p_ext->left;
822 child_width = c_ext->right - c_ext->left;
824 /* we need the pre-snap position */
825 c_ext->left = droppoint_orig.x;
826 c_ext->top = droppoint_orig.y;
827 c_ext->right = c_ext->left + child_width;
828 c_ext->bottom = c_ext->top + child_height;
830 /* check if the top of the child is inside the parent, but the bottom is not */
831 if (c_ext->top < p_ext->bottom && c_ext->bottom > p_ext->bottom)
833 /* check if child is smaller than parent height wise */
834 if (child_height < parent_height)
835 new_height = child_height;
836 /* check if parent is bigger than 1 in height */
837 else if (parent_height > 1)
838 new_height = round_up(parent_height) - 1;
840 else
842 new_height = child_height;
844 /* check if the left of the child is inside the partent, but the right is not */
845 if (c_ext->left < p_ext->right && c_ext->right > p_ext->right)
847 /* check if child is smaller than parent width wise */
848 if (child_width < parent_width)
849 new_width = child_width;
850 /* check if parent is bigger than 1 in width */
851 else if (parent_width > 1)
852 new_width = round_up(parent_width) - 1;
854 else
856 new_width = child_width;
859 g_free(p_ext);
860 g_free(c_ext);
862 /* if we can't fit in both directions, produce an error */
863 if (!new_height && !new_width)
865 message_error(_("The object you dropped cannot fit into its parent. \nEither expand the parent object, or drop the object elsewhere."));
866 obj->parent->children = g_list_remove(obj->parent->children, obj);
867 obj->ops->destroy (obj);
868 return NULL;
870 /* if we can't fit height wise, make height same as of the parent */
871 else if (!new_height)
872 new_height = parent_height;
873 /* if we can't fit width wise, make the width same as of the parent */
874 else if (!new_width)
875 new_width = parent_width;
877 new_pos.x = droppoint.x + new_width;
878 new_pos.y = droppoint.y + new_height;
879 obj->ops->move_handle(obj, handle2, &new_pos, NULL,
880 HANDLE_MOVE_USER, 0);
885 diagram_add_object(ddisp->diagram, obj);
886 diagram_remove_all_selected(ddisp->diagram, TRUE); /* unselect all */
887 diagram_select(ddisp->diagram, obj);
888 obj->ops->selectf(obj, &droppoint, ddisp->renderer);
890 /* Connect first handle if possible: */
891 if ((handle1 != NULL) &&
892 (handle1->connect_type != HANDLE_NONCONNECTABLE)) {
893 object_connect_display(ddisp, obj, handle1);
895 object_add_updates(obj, ddisp->diagram);
896 ddisplay_do_update_menu_sensitivity(ddisp);
897 diagram_flush(ddisp->diagram);
899 list = g_list_prepend(NULL, obj);
900 undo_insert_objects(ddisp->diagram, list, 1);
901 diagram_update_extents(ddisp->diagram);
902 diagram_modified(ddisp->diagram);
904 undo_set_transactionpoint(ddisp->diagram->undo);
905 if (prefs.reset_tools_after_create)
906 tool_reset();
907 return obj;