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 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <math.h>
26 #ifdef GNOME
27 #undef GTK_DISABLE_DEPRECATED
28 #include <gnome.h>
29 #endif
30 #include <gtk/gtk.h>
31 #include <gdk/gdkkeysyms.h>
32 
33 #include "display.h"
34 #include "diagram.h"
35 #include "tool.h"
36 #include "interface.h"
37 #include "focus.h"
38 #include "object_ops.h"
39 #include "connectionpoint_ops.h"
40 #include "menus.h"
41 #include "message.h"
42 #include "intl.h"
43 #include "magnify.h"
44 #include "diamenu.h"
45 #include "preferences.h"
46 #include "scroll_tool.h"
47 #include "commands.h"
48 #include "highlight.h"
49 #include "textedit.h"
50 #include "lib/parent.h"
51 
52 /* This contains the point that was clicked to get this menu */
53 static Point object_menu_clicked_point;
54 
55 typedef struct {
56 	GdkEvent *event; /* Button down event which may be holding */
57 	DDisplay *ddisp; /* DDisplay where event occurred */
58 	guint     tag;   /* Tag for timeout */
59 } HoldTimeoutData;
60 
61 static HoldTimeoutData hold_data = {NULL, NULL, 0};
62 
63 
64 
65 static void
object_menu_proxy(GtkWidget * widget,gpointer data)66 object_menu_proxy(GtkWidget *widget, gpointer data)
67 {
68   DiaMenuItem *dia_menu_item;
69   ObjectChange *obj_change;
70   DiaObject *obj;
71   DDisplay *ddisp = ddisplay_active();
72 
73   if (!ddisp) return;
74 
75   obj = (DiaObject *)ddisp->diagram->data->selected->data;
76   dia_menu_item = (DiaMenuItem *) data;
77 
78 
79   object_add_updates(obj, ddisp->diagram);
80   obj_change = (dia_menu_item->callback)(obj, &object_menu_clicked_point,
81 					 dia_menu_item->callback_data);
82   object_add_updates(obj, ddisp->diagram);
83   diagram_update_connections_object(ddisp->diagram, obj, TRUE);
84 
85   if (obj_change != NULL) {
86     undo_object_change(ddisp->diagram, obj, obj_change);
87   }
88   diagram_modified(ddisp->diagram);
89 
90   diagram_update_extents(ddisp->diagram);
91 
92   if (obj_change != NULL) {
93     undo_set_transactionpoint(ddisp->diagram->undo);
94   } else {
95     message_warning(_("This object doesn't support Undo/Redo.\n"
96 		      "Undo information erased."));
97     undo_clear(ddisp->diagram->undo);
98   }
99 
100   diagram_flush(ddisp->diagram);
101 }
102 
103 static void
dia_menu_free(DiaMenu * dia_menu)104 dia_menu_free(DiaMenu *dia_menu)
105 {
106   if (dia_menu->app_data)
107     gtk_object_destroy((GtkObject *)dia_menu->app_data);
108   dia_menu->app_data = NULL;
109   dia_menu->app_data_free = NULL;
110 }
111 
112 /*
113   This add a Properties... menu item to the GtkMenu passed, at the
114   end and set the callback to raise de properties dialog
115 
116   pass TRUE in separator if you want to insert a separator before the poperty
117   menu item.
118 */
119 static void
add_properties_menu_item(GtkMenu * menu,gboolean separator)120 add_properties_menu_item (GtkMenu *menu, gboolean separator)
121 {
122   GtkWidget *menu_item = NULL;
123 
124   if (separator) {
125     menu_item = gtk_menu_item_new();
126     gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
127     gtk_widget_show(menu_item);
128   }
129 
130   menu_item = gtk_menu_item_new_with_label(_("Properties..."));
131   g_signal_connect(GTK_OBJECT(menu_item), "activate", G_CALLBACK(dialogs_properties_callback), NULL);
132   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
133   gtk_widget_show(menu_item);
134 }
135 
136 static void
create_object_menu(DiaMenu * dia_menu)137 create_object_menu(DiaMenu *dia_menu)
138 {
139   int i;
140   GtkWidget *menu;
141   GtkWidget *menu_item;
142 
143   menu = gtk_menu_new();
144 
145   if ( dia_menu->title ) {
146     menu_item = gtk_menu_item_new_with_label(gettext(dia_menu->title));
147     gtk_widget_set_sensitive(menu_item, FALSE);
148     gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
149     gtk_widget_show(menu_item);
150   }
151 
152   /* separator below the menu title */
153   menu_item = gtk_menu_item_new();
154   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
155   gtk_widget_show(menu_item);
156 
157   for (i=0;i<dia_menu->num_items;i++) {
158     DiaMenuItem *item = &dia_menu->items[i];
159 
160     if (item->active & DIAMENU_TOGGLE) {
161       if (item->text)
162         menu_item = gtk_check_menu_item_new_with_label(gettext(item->text));
163       else
164         menu_item = gtk_check_menu_item_new();
165       gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item),
166                                      item->active & DIAMENU_TOGGLE_ON);
167     } else {
168       if (item->text)
169         menu_item = gtk_menu_item_new_with_label(gettext(item->text));
170       else
171         menu_item = gtk_menu_item_new();
172     }
173     gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
174     gtk_widget_show(menu_item);
175     item->app_data = menu_item;
176     if ( dia_menu->items[i].callback ) {
177           /* only connect signal handler if there is actually a callback */
178       g_signal_connect (G_OBJECT (menu_item), "activate",
179                         G_CALLBACK (object_menu_proxy), &dia_menu->items[i]);
180     } else {
181       if ( item->callback_data ) {
182             /* This menu item is a submenu if it has no callback, but does
183              * Have callback_data. In this case the callback_data is a
184              * DiaMenu pointer for the submenu. */
185         if ( ((DiaMenu*)item->callback_data)->app_data == NULL ) {
186               /* Create the popup menu items for the submenu. */
187           create_object_menu((DiaMenu*)(item->callback_data) ) ;
188           gtk_menu_item_set_submenu( GTK_MENU_ITEM (menu_item),
189                                      GTK_WIDGET(((DiaMenu*)(item->callback_data))->app_data));
190         }
191       }
192     }
193   }
194 
195   /* Finally add a Properties... menu item for objects*/
196   add_properties_menu_item(GTK_MENU (menu), i > 0);
197 
198   dia_menu->app_data = menu;
199   dia_menu->app_data_free = dia_menu_free;
200 }
201 
202 static DiaMenuItem empty_menu_items[] = { {0, } };
203 static DiaMenu empty_menu = {
204   NULL,
205   0, /* NOT: sizeof(empty_menu_items)/sizeof(DiaMenuItem), */
206   empty_menu_items,
207   NULL
208 };
209 
210 static void
popup_object_menu(DDisplay * ddisp,GdkEventButton * bevent)211 popup_object_menu(DDisplay *ddisp, GdkEventButton *bevent)
212 {
213   Diagram *diagram;
214   DiaObject *obj;
215   GtkMenu *menu = NULL;
216   DiaMenu *dia_menu = NULL;
217   GList *selected_list;
218   int i;
219   int num_items;
220 
221   diagram = ddisp->diagram;
222   if (g_list_length (diagram->data->selected) < 1)
223     return;
224 
225   selected_list = diagram->data->selected;
226 
227   /* Have to have exactly one selected object */
228   if (selected_list == NULL) {
229     message_error("Selected list is %s while selected_count is %d\n",
230 		  (selected_list?"long":"empty"), g_list_length (diagram->data->selected));
231     return;
232   }
233 
234   obj = (DiaObject *)g_list_first(selected_list)->data;
235 
236   /* Possibly react differently at a handle? */
237 
238   /* Get its menu, and remember the # of object-generated items */
239   if (    g_list_length (diagram->data->selected) > 1
240       ||  obj->ops->get_object_menu == NULL
241       || (obj->ops->get_object_menu)(obj, &object_menu_clicked_point) == NULL) {
242     dia_menu = &empty_menu;
243     if (dia_menu->title &&
244 	(0 != strcmp(dia_menu->title,obj->type->name))) {
245       dia_menu->app_data_free(dia_menu);
246     }
247     if (g_list_length (diagram->data->selected) > 1)
248       dia_menu->title = _("Selection");
249     else
250       dia_menu->title = obj->type->name;
251     num_items = 0;
252   } else {
253     dia_menu = (obj->ops->get_object_menu)(obj, &object_menu_clicked_point);
254     num_items = dia_menu->num_items;
255   }
256 
257   if (dia_menu->app_data == NULL) {
258     create_object_menu(dia_menu);
259   }
260   /* Update active/nonactive menuitems */
261   for (i=0;i<num_items;i++) {
262     DiaMenuItem *item = &dia_menu->items[i];
263     gtk_widget_set_sensitive(GTK_WIDGET(item->app_data),
264 			     item->active & DIAMENU_ACTIVE);
265     if (item->active & DIAMENU_TOGGLE) {
266       g_signal_handlers_block_by_func (G_OBJECT (GTK_CHECK_MENU_ITEM (item->app_data)),
267 				      G_CALLBACK (object_menu_proxy), item);
268       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(item->app_data),
269 				      item->active & DIAMENU_TOGGLE_ON);
270       g_signal_handlers_unblock_by_func (G_OBJECT (GTK_CHECK_MENU_ITEM(item->app_data)),
271 					G_CALLBACK (object_menu_proxy), item);
272     }
273   }
274 
275   menu = GTK_MENU(dia_menu->app_data);
276   /* add the properties menu item to raise the properties from the contextual menu */
277 
278   gtk_menu_popup(menu, NULL, NULL, NULL, NULL, bevent->button, bevent->time);
279 }
280 
281 gint
ddisplay_focus_in_event(GtkWidget * widget,GdkEventFocus * event,gpointer data)282 ddisplay_focus_in_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
283 {
284   DDisplay *ddisp;
285 
286   g_return_val_if_fail (widget != NULL, FALSE);
287   g_return_val_if_fail (event != NULL, FALSE);
288   g_return_val_if_fail (data != NULL, FALSE);
289 
290   ddisp = (DDisplay *)data;
291 
292   GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
293 
294   gtk_im_context_focus_in(GTK_IM_CONTEXT(ddisp->im_context));
295 
296   return FALSE;
297 }
298 
299 gint
ddisplay_focus_out_event(GtkWidget * widget,GdkEventFocus * event,gpointer data)300 ddisplay_focus_out_event(GtkWidget *widget, GdkEventFocus *event,gpointer data)
301 {
302   DDisplay *ddisp;
303   int return_val;
304 
305   g_return_val_if_fail (widget != NULL, FALSE);
306   g_return_val_if_fail (event != NULL, FALSE);
307   g_return_val_if_fail (data != NULL, FALSE);
308 
309   return_val = FALSE;
310 
311   ddisp = (DDisplay *)data;
312 
313   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
314 
315   gtk_im_context_focus_out(GTK_IM_CONTEXT(ddisp->im_context));
316 
317   return return_val;
318 }
319 
320 void
ddisplay_realize(GtkWidget * widget,gpointer data)321 ddisplay_realize(GtkWidget *widget, gpointer data)
322 {
323   DDisplay *ddisp;
324 
325   g_return_if_fail(widget != NULL);
326   g_return_if_fail(data != NULL);
327 
328   ddisp = (DDisplay *)data;
329 
330   gtk_im_context_set_client_window(GTK_IM_CONTEXT(ddisp->im_context),
331                                    GDK_WINDOW(ddisp->shell->window));
332 }
333 
334 void
ddisplay_unrealize(GtkWidget * widget,gpointer data)335 ddisplay_unrealize (GtkWidget *widget, gpointer data)
336 {
337   DDisplay *ddisp;
338 
339   g_return_if_fail (widget != NULL);
340   g_return_if_fail (data != NULL);
341 
342   ddisp = (DDisplay *) data;
343 
344   if (ddisp->im_context)
345     gtk_im_context_set_client_window(GTK_IM_CONTEXT(ddisp->im_context),
346                                      GDK_WINDOW(ddisp->shell->window));
347 }
348 
349 void
ddisplay_size_allocate(GtkWidget * widget,GtkAllocation * allocation,gpointer data)350 ddisplay_size_allocate (GtkWidget *widget,
351 			GtkAllocation *allocation,
352 			gpointer data)
353 {
354   DDisplay *ddisp;
355 
356   g_return_if_fail (widget != NULL);
357   g_return_if_fail (allocation != NULL);
358   g_return_if_fail (data != NULL);
359 
360 #if 0
361   g_print ("ddisp::size_allocate: %d,%d -> %d,%d\n", allocation->width, allocation->height,
362 	   widget->allocation.width, widget->allocation.height);
363 #endif
364   widget->allocation = *allocation;
365   ddisp = (DDisplay *)data;
366 }
367 
368 void
ddisplay_popup_menu(DDisplay * ddisp,GdkEventButton * event)369 ddisplay_popup_menu(DDisplay *ddisp, GdkEventButton *event)
370 {
371   GtkWidget *menu;
372 
373   menu = menus_get_display_popup();
374 
375   gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
376 		 event->button, event->time);
377 }
378 static void
handle_key_event(DDisplay * ddisp,Focus * focus,guint keysym,const gchar * str,int strlen)379 handle_key_event(DDisplay *ddisp, Focus *focus, guint keysym,
380                  const gchar *str, int strlen)
381 {
382   DiaObject *obj = focus_get_object(focus);
383   Point p = obj->position;
384   ObjectChange *obj_change = NULL;
385   gboolean modified;
386 
387   object_add_updates(obj, ddisp->diagram);
388 
389   modified = (focus->key_event)(focus, keysym, str, strlen,
390 				&obj_change);
391 
392   /* Make sure object updates its data and its connected: */
393   p = obj->position;
394   (obj->ops->move)(obj,&p);
395   diagram_update_connections_object(ddisp->diagram,obj,TRUE);
396 
397   object_add_updates(obj, ddisp->diagram);
398 
399   if (modified) {
400     if (obj_change != NULL) {
401       undo_object_change(ddisp->diagram, obj, obj_change);
402       undo_set_transactionpoint(ddisp->diagram->undo);
403     }
404     diagram_update_extents(ddisp->diagram);
405     diagram_modified(ddisp->diagram);
406   }
407 
408   diagram_flush(ddisp->diagram);
409 }
410 
411 
412 void
ddisplay_im_context_commit(GtkIMContext * context,const gchar * str,DDisplay * ddisp)413 ddisplay_im_context_commit(GtkIMContext *context, const gchar  *str,
414                            DDisplay     *ddisp)
415 {
416       /* When using IM, we'll not get many key events past the IM filter,
417          mostly IM Commits.
418 
419          regardless of the platform, "str" should be a clean UTF8 string
420          (the default IM on X should perform the local->UTF8 conversion)
421       */
422 
423   Focus *focus = get_active_focus((DiagramData *) ddisp->diagram);
424 
425   ddisplay_im_context_preedit_reset(ddisp, focus);
426 
427   if (focus != NULL)
428     handle_key_event(ddisp, focus, 0, str, g_utf8_strlen(str,-1));
429 }
430 
431 void
ddisplay_im_context_preedit_changed(GtkIMContext * context,DDisplay * ddisp)432 ddisplay_im_context_preedit_changed(GtkIMContext *context,
433                                     DDisplay *ddisp)
434 {
435   gint cursor_pos;
436   Focus *focus = get_active_focus((DiagramData *) ddisp->diagram);
437 
438   ddisplay_im_context_preedit_reset(ddisp, focus);
439 
440   gtk_im_context_get_preedit_string(context, &ddisp->preedit_string,
441                                     &ddisp->preedit_attrs, &cursor_pos);
442   if (ddisp->preedit_string != NULL) {
443     if (focus != NULL) {
444       handle_key_event(ddisp, focus, 0, ddisp->preedit_string,
445                        g_utf8_strlen(ddisp->preedit_string,-1));
446     } else {
447       ddisplay_im_context_preedit_reset(ddisp, focus);
448     }
449   }
450 }
451 
452 static void
_scroll_page(DDisplay * ddisp,Direction dir)453 _scroll_page (DDisplay *ddisp, Direction dir)
454 {
455   Point delta = {0, 0};
456 
457   switch (dir) {
458   case DIR_LEFT :
459     delta.x = ddisp->diagram->data->paper.width * ddisp->diagram->data->paper.scaling;
460     break;
461   case DIR_RIGHT :
462     delta.x = -ddisp->diagram->data->paper.width * ddisp->diagram->data->paper.scaling;
463     break;
464   case DIR_UP :
465     delta.y = -ddisp->diagram->data->paper.height * ddisp->diagram->data->paper.scaling;
466     break;
467   case DIR_DOWN :
468     delta.y = ddisp->diagram->data->paper.height * ddisp->diagram->data->paper.scaling;
469     break;
470   }
471   ddisplay_scroll(ddisp, &delta);
472   ddisplay_flush(ddisp);
473 }
474 
475 static void
_scroll_step(DDisplay * ddisp,guint keyval)476 _scroll_step (DDisplay *ddisp, guint keyval)
477 {
478   switch (keyval) {
479   case GDK_Up :
480     ddisplay_scroll_up(ddisp);
481     ddisplay_flush(ddisp);
482     break;
483   case GDK_Down:
484     ddisplay_scroll_down(ddisp);
485     ddisplay_flush(ddisp);
486     break;
487   case GDK_Left:
488     ddisplay_scroll_left(ddisp);
489     ddisplay_flush(ddisp);
490     break;
491   case GDK_Right:
492     ddisplay_scroll_right(ddisp);
493     ddisplay_flush(ddisp);
494     break;
495   default :
496     g_assert_not_reached  ();
497   }
498 }
499 
500 /** Cleanup/Remove Timeout Handler for Button Press and Hold
501  */
502 static void
hold_remove_handler(void)503 hold_remove_handler(void)
504 {
505   if (hold_data.tag != 0) {
506     g_source_remove(hold_data.tag);
507     hold_data.tag = 0;
508     gdk_event_free(hold_data.event);
509   }
510 }
511 
512 /** Timeout Handler for Button Press and Hold
513  * If this function is called, then the button must still be down,
514  * indicating that the user has pressed and held the button, but not moved
515  * the pointer (mouse).
516  * Dynamic data is cleaned up in ddisplay_canvas_events
517  */
518 static gboolean
hold_timeout_handler(gpointer data)519 hold_timeout_handler(gpointer data)
520 {
521   if (active_tool->button_hold_func)
522     (*active_tool->button_hold_func) (active_tool, (GdkEventButton *)(hold_data.event), hold_data.ddisp);
523   hold_remove_handler();
524   return FALSE;
525 }
526 
527 
528 /** Main input handler for a diagram canvas.
529  */
530 gint
ddisplay_canvas_events(GtkWidget * canvas,GdkEvent * event,DDisplay * ddisp)531 ddisplay_canvas_events (GtkWidget *canvas,
532 			GdkEvent  *event,
533 			DDisplay *ddisp)
534 {
535   GdkEventExpose *eevent;
536   GdkEventMotion *mevent;
537   GdkEventButton *bevent;
538   GdkEventKey *kevent;
539   GdkEventScroll *sevent;
540   gint tx, ty;
541   GdkModifierType tmask;
542   guint state = 0;
543   Focus *focus;
544   DiaObject *obj;
545   Rectangle *visible;
546   Point middle;
547   int return_val;
548   int key_handled;
549   int width, height;
550   int new_size;
551   int im_context_used;
552   static gboolean moving = FALSE;
553 
554   return_val = FALSE;
555 
556   if (!canvas->window)
557     return FALSE;
558 
559   switch (event->type)
560   {
561       case GDK_EXPOSE:
562         eevent = (GdkEventExpose *) event;
563         ddisplay_add_display_area(ddisp,
564                                   eevent->area.x, eevent->area.y,
565                                   eevent->area.x + eevent->area.width,
566                                   eevent->area.y + eevent->area.height);
567         ddisplay_flush(ddisp);
568         break;
569 
570       case GDK_SCROLL:
571         sevent = (GdkEventScroll *) event;
572 
573         switch (sevent->direction)
574         {
575             case GDK_SCROLL_UP:
576               if (sevent->state & GDK_SHIFT_MASK)
577                   ddisplay_scroll_left(ddisp);
578               else if (sevent->state & GDK_CONTROL_MASK) {
579                   ddisplay_untransform_coords(ddisp, (int)sevent->x, (int)sevent->y, &middle.x, &middle.y);
580 		  /* zooming with the wheel in small steps 1^(1/8) */
581                   ddisplay_zoom_centered(ddisp, &middle, 1.090508);
582               }
583               else
584                   ddisplay_scroll_up(ddisp);
585               break;
586             case GDK_SCROLL_DOWN:
587               if (sevent->state & GDK_SHIFT_MASK)
588                   ddisplay_scroll_right(ddisp);
589               else if (sevent->state & GDK_CONTROL_MASK) {
590                   ddisplay_untransform_coords(ddisp, (int)sevent->x, (int)sevent->y, &middle.x, &middle.y);
591 		  /* zooming with the wheel in small steps 1/(1^(1/8)) */
592                   ddisplay_zoom_centered(ddisp, &middle, 0.917004);
593               }
594               else
595                   ddisplay_scroll_down(ddisp);
596               break;
597             case GDK_SCROLL_LEFT:
598               ddisplay_scroll_left(ddisp);
599               break;
600             case GDK_SCROLL_RIGHT:
601               ddisplay_scroll_right(ddisp);
602               break;
603             default:
604               break;
605         }
606         ddisplay_flush (ddisp);
607         break;
608 
609       case GDK_CONFIGURE:
610         if (ddisp->renderer != NULL) {
611 	  width = dia_renderer_get_width_pixels (ddisp->renderer);
612 	  height = dia_renderer_get_height_pixels (ddisp->renderer);
613           new_size = ((width != ddisp->canvas->allocation.width) ||
614                       (height != ddisp->canvas->allocation.height));
615         } else {
616           new_size = TRUE;
617         }
618         if (new_size) {
619           ddisplay_resize_canvas(ddisp,
620                                  ddisp->canvas->allocation.width,
621                                  ddisp->canvas->allocation.height);
622           ddisplay_update_scrollbars(ddisp);
623         }
624 	/* If the UI is not integrated, resizing should set the resized
625 	 * window as active.  With integrated UI, there is only one window.
626 	 */
627         if (is_integrated_ui () == 0)
628           display_set_active(ddisp);
629         break;
630 
631       case GDK_FOCUS_CHANGE: {
632 	GdkEventFocus *focus = (GdkEventFocus*)event;
633 	hold_remove_handler();
634 	if (focus->in) {
635 	  display_set_active(ddisp);
636 	  ddisplay_do_update_menu_sensitivity(ddisp);
637 	}
638         break;
639       }
640       case GDK_2BUTTON_PRESS:
641         display_set_active(ddisp);
642 	hold_remove_handler();
643         bevent = (GdkEventButton *) event;
644         state = bevent->state;
645 
646         switch (bevent->button)
647         {
648             case 1:
649               if (transient_tool)
650                 break;
651               if (active_tool->double_click_func)
652                 (*active_tool->double_click_func) (active_tool, bevent, ddisp);
653               break;
654 
655             case 2:
656               break;
657 
658             case 3:
659               break;
660 
661             default:
662               break;
663         }
664         break;
665 
666       case GDK_BUTTON_PRESS:
667         display_set_active(ddisp);
668         bevent = (GdkEventButton *) event;
669         state = bevent->state;
670 
671         ddisplay_untransform_coords(ddisp,
672                                     (int)bevent->x, (int)bevent->y,
673                                     &object_menu_clicked_point.x,
674                                     &object_menu_clicked_point.y);
675 
676         switch (bevent->button)
677         {
678             case 1:
679               if (transient_tool)
680                 break;
681                   /* get the focus again, may be lost by zoom combo */
682 	      moving = TRUE;
683               gtk_widget_grab_focus(canvas);
684               if (active_tool->button_press_func)
685                 (*active_tool->button_press_func) (active_tool, bevent, ddisp);
686 
687 			  /* Detect user holding down the button.
688 			   * Set timeout for 1sec. If timeout is called, user must still
689 			   * be holding. If user releases button, remove timeout. */
690 			  hold_data.event = gdk_event_copy(event); /* need to free later */
691 			  hold_data.ddisp = ddisp;
692 			  hold_data.tag = g_timeout_add(1000, hold_timeout_handler, NULL);
693 
694               break;
695             case 2:
696               if (ddisp->menu_bar == NULL && !is_integrated_ui()) {
697                 popup_object_menu(ddisp, bevent);
698               }
699 	      else if (!transient_tool) {
700 		gtk_widget_grab_focus(canvas);
701 		transient_tool = create_scroll_tool();
702 		(*transient_tool->button_press_func) (transient_tool, bevent, ddisp);
703 	      }
704               break;
705 
706             case 3:
707               if (transient_tool)
708                 break;
709               if (ddisp->menu_bar == NULL) {
710                 if (bevent->state & GDK_CONTROL_MASK || is_integrated_ui ()) {
711                       /* for two button mouse users ... */
712                   popup_object_menu(ddisp, bevent);
713                   break;
714                 }
715                 ddisplay_popup_menu(ddisp, bevent);
716                 break;
717               }
718               else {
719                 popup_object_menu(ddisp, bevent);
720                 break;
721               }
722             default:
723               break;
724         }
725         break;
726 
727       case GDK_BUTTON_RELEASE:
728         display_set_active(ddisp);
729         bevent = (GdkEventButton *) event;
730         state = bevent->state;
731 
732         switch (bevent->button)
733         {
734             case 1:
735 	      if (moving)
736   		moving = FALSE;
737               if (active_tool->button_release_func)
738                 (*active_tool->button_release_func) (active_tool,
739                                                      bevent, ddisp);
740 	      /* Button Press and Hold - remove handler then deallocate memory */
741 	      hold_remove_handler();
742               break;
743 
744             case 2:
745 	      if (transient_tool) {
746 	        (*transient_tool->button_release_func) (transient_tool,
747   	                                             bevent, ddisp);
748 
749 	        tool_free(transient_tool);
750 	        transient_tool = NULL;
751 	      }
752               break;
753 
754             case 3:
755               break;
756 
757             default:
758               break;
759         }
760         break;
761 
762       case GDK_MOTION_NOTIFY:
763 	/*  get the pointer position  */
764 	gdk_window_get_pointer (canvas->window, &tx, &ty, &tmask);
765 	hold_remove_handler();
766 
767         mevent = (GdkEventMotion *) event;
768         state = mevent->state;
769 
770         if (mevent->is_hint) {
771           mevent->x = tx;
772           mevent->y = ty;
773           mevent->state = tmask;
774           mevent->is_hint = FALSE;
775         }
776         if (transient_tool && (*transient_tool->motion_func))
777           (*transient_tool->motion_func) (transient_tool, mevent, ddisp);
778         else if (active_tool->motion_func)
779           (*active_tool->motion_func) (active_tool, mevent, ddisp);
780         break;
781 
782       case GDK_KEY_PRESS:
783 	if (moving)	/*Disable Keyboard accels whilst draggin an object*/
784 		break;
785         display_set_active(ddisp);
786         kevent = (GdkEventKey *)event;
787         state = kevent->state;
788         key_handled = FALSE;
789         im_context_used = FALSE;
790 #if 0
791 	printf("Key input %d in state %d\n", kevent->keyval, textedit_mode(ddisp));
792 #endif
793         focus = get_active_focus((DiagramData *) ddisp->diagram);
794         if (focus != NULL) {
795 	  /* Keys goes to the active focus. */
796 	  obj = focus_get_object(focus);
797 	  if (diagram_is_selected(ddisp->diagram, obj)) {
798 
799 	    if (!gtk_im_context_filter_keypress(
800 		  GTK_IM_CONTEXT(ddisp->im_context), kevent)) {
801 
802 	      switch (kevent->keyval) {
803 		  case GDK_Tab:
804 		    focus = textedit_move_focus(ddisp, focus,
805 						(state & GDK_SHIFT_MASK) == 0);
806 		    obj = focus_get_object(focus);
807 		    break;
808 		  case GDK_Escape:
809 		    textedit_deactivate_focus();
810 		    tool_reset ();
811 		    ddisplay_do_update_menu_sensitivity(ddisp);
812 		    break;
813 		  default:
814 		    /*! key event not swallowed by the input method ? */
815 		    handle_key_event(ddisp, focus, kevent->keyval,
816 				     kevent->string, kevent->length);
817 
818 		    diagram_flush(ddisp->diagram);
819 	      }
820 	    }
821 	    return_val = key_handled = im_context_used = TRUE;
822 	  }
823 	}
824 
825 #if 0 /* modifier requirment added 2004-07-17, IMO reenabling unmodified keys here
826                * shouldn't break im_context handling. How to test?    --hb
827                */
828         if (!key_handled && (state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))) {
829 #else
830         if (!key_handled) {
831 #endif
832               /* IM doesn't need receive keys, take care of it ourselves. */
833           return_val = TRUE;
834 
835           switch(kevent->keyval) {
836 	      case GDK_Home :
837 	        /* match upper left corner of the diagram with it's view */
838 		ddisplay_set_origo(ddisp, ddisp->diagram->data->extents.left, ddisp->diagram->data->extents.top);
839 		ddisplay_update_scrollbars(ddisp);
840 		ddisplay_add_update_all(ddisp);
841 	        break;
842 	      case GDK_End :
843 	        /* match lower right corner of the diagram with it's view */
844 		visible = &ddisp->visible;
845 		ddisplay_set_origo(ddisp,
846 		                   ddisp->diagram->data->extents.right - (visible->right - visible->left),
847 				   ddisp->diagram->data->extents.bottom - (visible->bottom - visible->top));
848 		ddisplay_update_scrollbars(ddisp);
849 		ddisplay_add_update_all(ddisp);
850 	        break;
851 	      case GDK_Page_Up :
852 	        _scroll_page (ddisp, !(state & GDK_CONTROL_MASK) ? DIR_UP : DIR_LEFT);
853 	        break;
854 	      case GDK_Page_Down :
855 	        _scroll_page (ddisp, !(state & GDK_CONTROL_MASK) ? DIR_DOWN : DIR_RIGHT);
856 	        break;
857               case GDK_Up:
858               case GDK_Down:
859               case GDK_Left:
860               case GDK_Right:
861 	        if (g_list_length (ddisp->diagram->data->selected) > 0) {
862 		  Diagram *dia = ddisp->diagram;
863 		  GList *objects = dia->data->selected;
864 		  Direction dir = GDK_Up == kevent->keyval ? DIR_UP :
865 				  GDK_Down == kevent->keyval ? DIR_DOWN :
866 				  GDK_Right == kevent->keyval ? DIR_RIGHT : DIR_LEFT;
867 		  object_add_updates_list(objects, dia);
868 		  object_list_nudge(objects, dia, dir,
869 				    /* step one pixel or more with <ctrl> */
870 				    ddisplay_untransform_length (ddisp, (state & GDK_SHIFT_MASK) ? 10 : 1));
871 		  diagram_update_connections_selection(dia);
872 		  object_add_updates_list(objects, dia);
873 		  diagram_modified(dia);
874 		  diagram_flush(dia);
875 
876 		  undo_set_transactionpoint(dia->undo);
877 		} else {
878 		  _scroll_step (ddisp, kevent->keyval);
879 		}
880 		break;
881               case GDK_KP_Add:
882               case GDK_plus:
883                 visible = &ddisp->visible;
884                 middle.x = visible->left*0.5 + visible->right*0.5;
885                 middle.y = visible->top*0.5 + visible->bottom*0.5;
886 
887                 ddisplay_zoom(ddisp, &middle, M_SQRT2);
888                 break;
889               case GDK_KP_Subtract:
890               case GDK_minus:
891                 visible = &ddisp->visible;
892                 middle.x = visible->left*0.5 + visible->right*0.5;
893                 middle.y = visible->top*0.5 + visible->bottom*0.5;
894 
895                 ddisplay_zoom(ddisp, &middle, M_SQRT1_2);
896                 break;
897               case GDK_Shift_L:
898               case GDK_Shift_R:
899                 if (active_tool->type == MAGNIFY_TOOL)
900                   set_zoom_out(active_tool);
901                 break;
902               case GDK_Escape:
903                 view_unfullscreen();
904                 break;
905 	      case GDK_F2:
906 	      case GDK_Return:
907 		gtk_action_activate (menus_get_action ("ToolsTextedit"));
908 		break;
909               default:
910                 if (kevent->string && kevent->keyval == ' ') {
911                   tool_select_former();
912                 } else if ((kevent->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) == 0 &&
913 			   kevent->length != 0) {
914 		  /* Find first editable */
915 #ifdef NEW_TEXT_EDIT
916 		  modify_edit_first_text(ddisp);
917                   return_val = FALSE;
918 #endif
919                 }
920           }
921         }
922 
923         if (!im_context_used)
924           gtk_im_context_reset(GTK_IM_CONTEXT(ddisp->im_context));
925 
926         break;
927 
928       case GDK_KEY_RELEASE:
929         kevent = (GdkEventKey *) event;
930         state = kevent->state;
931         if (gtk_im_context_filter_keypress(GTK_IM_CONTEXT(ddisp->im_context),
932                                            kevent)) {
933           return_val = TRUE;
934         } else {
935           switch(kevent->keyval) {
936               case GDK_Shift_L:
937               case GDK_Shift_R:
938                 if (active_tool->type == MAGNIFY_TOOL)
939                   set_zoom_in(active_tool);
940                 break;
941               default:
942                 break;
943           }
944         }
945         break;
946 
947       default:
948         break;
949   }
950 
951   return return_val;
952 }
953 
954 gint
955 ddisplay_hsb_update (GtkAdjustment *adjustment,
956 		     DDisplay *ddisp)
957 {
958   ddisplay_set_origo(ddisp, adjustment->value, ddisp->origo.y);
959   ddisplay_add_update_all(ddisp);
960   ddisplay_flush(ddisp);
961   return FALSE;
962 }
963 
964 gint
965 ddisplay_vsb_update (GtkAdjustment *adjustment,
966 		     DDisplay *ddisp)
967 {
968   ddisplay_set_origo(ddisp, ddisp->origo.x, adjustment->value);
969   ddisplay_add_update_all(ddisp);
970   ddisplay_flush(ddisp);
971   return FALSE;
972 }
973 
974 gint
975 ddisplay_delete (GtkWidget *widget, GdkEvent  *event, gpointer data)
976 {
977   DDisplay *ddisp;
978 
979   ddisp = (DDisplay *)data;
980 
981   ddisplay_close(ddisp);
982   return TRUE;
983 }
984 
985 void
986 ddisplay_destroy (GtkWidget *widget, gpointer data)
987 {
988   DDisplay *ddisp;
989 
990   ddisp = (DDisplay *) data;
991 
992   ddisplay_really_destroy(ddisp);
993 }
994 
995 /* returns NULL if object cannot be created */
996 DiaObject *
997 ddisplay_drop_object(DDisplay *ddisp, gint x, gint y, DiaObjectType *otype,
998 		     gpointer user_data)
999 {
1000   Point droppoint;
1001   Point droppoint_orig;
1002   Handle *handle1, *handle2;
1003   DiaObject *obj, *p_obj;
1004   GList *list;
1005   real click_distance;
1006 
1007   ddisplay_untransform_coords(ddisp, x, y, &droppoint.x, &droppoint.y);
1008 
1009   /* save it before snap_to_grid modifies it */
1010   droppoint_orig = droppoint;
1011 
1012   snap_to_grid(ddisp, &droppoint.x, &droppoint.y);
1013 
1014   obj = dia_object_default_create (otype, &droppoint,
1015                                    user_data,
1016                                    &handle1, &handle2);
1017 
1018   if (!obj)
1019     return NULL;
1020 
1021   click_distance = ddisplay_untransform_length(ddisp, 3.0);
1022 
1023   /* Notice that using diagram_find_clicked_object doesn't allow any object
1024    * below the first to be a parent.  This should be fixed.
1025    * -Lars
1026    */
1027   p_obj = diagram_find_clicked_object(ddisp->diagram, &droppoint_orig,
1028 				    click_distance);
1029 
1030   if (p_obj && object_flags_set(p_obj, DIA_OBJECT_CAN_PARENT))
1031     /* the tool was dropped inside an object that takes children*/
1032   {
1033     Rectangle p_ext, c_ext;
1034     real parent_height, child_height, parent_width, child_width;
1035     real vadjust = 0.0, hadjust = 0.0;
1036     Point new_pos;
1037 
1038     obj->parent = p_obj;
1039     p_obj->children = g_list_append(p_obj->children, obj);
1040 
1041     /* This is not really what we want.  We want the box containing all
1042      * rendered parts of the object (not the bbox).  But it'll do for now,
1043      * since we don't have the 'rendered bbox'.
1044      * -Lars
1045      */
1046     parent_handle_extents(p_obj, &p_ext);
1047     parent_handle_extents(obj, &c_ext);
1048 
1049     parent_height = p_ext.bottom - p_ext.top;
1050     child_height = c_ext.bottom - c_ext.top;
1051 
1052     parent_width = p_ext.right - p_ext.left;
1053     child_width = c_ext.right - c_ext.left;
1054 
1055     /* we need the pre-snap position, but must remember that handles can
1056      * be to the left of the droppoint */
1057     c_ext.left = droppoint_orig.x - (obj->position.x - c_ext.left);
1058     c_ext.top  = droppoint_orig.y - (obj->position.y - c_ext.top);
1059     c_ext.right = c_ext.left + child_width;
1060     c_ext.bottom = c_ext.top + child_height;
1061 
1062     if (c_ext.left < p_ext.left) {
1063       hadjust = p_ext.left - c_ext.left;
1064     } else if (c_ext.right > p_ext.right) {
1065       hadjust = p_ext.right - c_ext.right;
1066     }
1067     if (c_ext.top < p_ext.top) {
1068       vadjust = p_ext.top - c_ext.top;
1069     } else if (c_ext.bottom > p_ext.bottom) {
1070       vadjust = p_ext.bottom - c_ext.bottom;
1071     }
1072 
1073     if (child_width > parent_width ||
1074 	child_height > parent_height) {
1075       message_error(_("The object you dropped cannot fit into its parent. \nEither expand the parent object, or drop the object elsewhere."));
1076       obj->parent->children = g_list_remove(obj->parent->children, obj);
1077       obj->ops->destroy (obj);
1078       return NULL;
1079     }
1080 
1081     if (hadjust || vadjust) {
1082       new_pos.x = droppoint.x + hadjust;
1083       new_pos.y = droppoint.y + vadjust;
1084       obj->ops->move(obj, &new_pos);
1085     }
1086   }
1087 
1088   diagram_add_object(ddisp->diagram, obj);
1089   diagram_remove_all_selected(ddisp->diagram, TRUE); /* unselect all */
1090   diagram_select(ddisp->diagram, obj);
1091   obj->ops->selectf(obj, &droppoint, ddisp->renderer);
1092   textedit_activate_object(ddisp, obj, NULL);
1093 
1094   /* Connect first handle if possible: */
1095   if ((handle1 != NULL) &&
1096       (handle1->connect_type != HANDLE_NONCONNECTABLE)) {
1097     object_connect_display(ddisp, obj, handle1, FALSE);
1098   }
1099   object_add_updates(obj, ddisp->diagram);
1100   ddisplay_do_update_menu_sensitivity(ddisp);
1101   diagram_flush(ddisp->diagram);
1102 
1103   list = g_list_prepend(NULL, obj);
1104   undo_insert_objects(ddisp->diagram, list, 1);
1105   diagram_update_extents(ddisp->diagram);
1106 
1107   undo_set_transactionpoint(ddisp->diagram->undo);
1108   diagram_modified(ddisp->diagram);
1109   if (prefs.reset_tools_after_create)
1110     tool_reset();
1111   return obj;
1112 }
1113