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