switching to XML DocBook
[dia.git] / app / disp_callbacks.c
blob87a98ea4a4907fd441331b0526169b7029e79faa
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"
45 /* This contains the point that was clicked to get this menu */
46 static Point object_menu_clicked_point;
48 static void
49 object_menu_proxy(GtkWidget *widget, gpointer data)
51 DiaMenuItem *dia_menu_item;
52 ObjectChange *obj_change;
54 DDisplay *ddisp = ddisplay_active();
55 Object *obj = (Object *)ddisp->diagram->data->selected->data;
57 dia_menu_item = (DiaMenuItem *) data;
60 object_add_updates(obj, ddisp->diagram);
61 obj_change = (dia_menu_item->callback)(obj, &object_menu_clicked_point,
62 dia_menu_item->callback_data);
63 object_add_updates(obj, ddisp->diagram);
64 diagram_update_connections_object(ddisp->diagram, obj, TRUE);
66 if (obj_change != NULL) {
67 undo_object_change(ddisp->diagram, obj, obj_change);
69 diagram_modified(ddisp->diagram);
71 diagram_update_extents(ddisp->diagram);
73 if (obj_change != NULL) {
74 undo_set_transactionpoint(ddisp->diagram->undo);
75 } else {
76 message_warning(_("This object doesn't support Undo/Redo.\n"
77 "Undo information erased."));
78 undo_clear(ddisp->diagram->undo);
82 diagram_flush(ddisp->diagram);
85 static void
86 dia_menu_free(DiaMenu *dia_menu) {
87 if (dia_menu->app_data)
88 gtk_object_destroy((GtkObject *)dia_menu->app_data);
89 dia_menu->app_data = NULL;
90 dia_menu->app_data_free = NULL;
93 static void
94 create_object_menu(DiaMenu *dia_menu)
96 int i;
97 GtkWidget *menu;
98 GtkWidget *menu_item;
100 menu = gtk_menu_new();
101 //FIXME?: gtk_menu_ensure_uline_accel_group (GTK_MENU (menu)) ;
103 if ( dia_menu->title ) {
104 menu_item = gtk_menu_item_new_with_label(gettext(dia_menu->title));
105 gtk_widget_set_sensitive(menu_item, FALSE);
106 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
107 gtk_widget_show(menu_item);
110 menu_item = gtk_menu_item_new();
111 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
112 gtk_widget_show(menu_item);
114 for (i=0;i<dia_menu->num_items;i++) {
115 DiaMenuItem *item = &dia_menu->items[i];
117 if (item->active & DIAMENU_TOGGLE) {
118 if (item->text)
119 menu_item = gtk_check_menu_item_new_with_label(gettext(item->text));
120 else
121 menu_item = gtk_check_menu_item_new();
122 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item),
123 item->active & DIAMENU_TOGGLE_ON);
124 gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(menu_item),
125 TRUE);
126 } else {
127 if (item->text)
128 menu_item = gtk_menu_item_new_with_label(gettext(item->text));
129 else
130 menu_item = gtk_menu_item_new();
132 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
133 gtk_widget_show(menu_item);
134 item->app_data = menu_item;
135 if ( dia_menu->items[i].callback ) {
136 /* only connect signal handler if there is actually a callback */
137 gtk_signal_connect(GTK_OBJECT(menu_item), "activate",
138 (GtkSignalFunc)object_menu_proxy, &dia_menu->items[i]);
139 } else {
140 if ( item->callback_data ) {
141 /* This menu item is a submenu if it has no callback, but does
142 * Have callback_data. In this case the callback_data is a
143 * DiaMenu pointer for the submenu. */
144 if ( ((DiaMenu*)item->callback_data)->app_data == NULL ) {
145 /* Create the popup menu items for the submenu. */
146 create_object_menu((DiaMenu*)(item->callback_data) ) ;
147 gtk_menu_item_set_submenu( GTK_MENU_ITEM (menu_item),
148 GTK_WIDGET(((DiaMenu*)(item->callback_data))->app_data));
154 dia_menu->app_data = menu;
155 dia_menu->app_data_free = dia_menu_free;
158 static void
159 popup_object_menu(DDisplay *ddisp, GdkEventButton *bevent)
161 Diagram *diagram;
162 Object *obj;
163 GtkMenu *menu = NULL;
164 DiaMenu *dia_menu = NULL;
165 GtkWidget *menu_item;
166 GList *selected_list;
167 static GtkWidget *no_menu = NULL;
168 int i;
170 diagram = ddisp->diagram;
171 if (diagram->data->selected_count != 1)
172 return;
174 selected_list = diagram->data->selected;
176 /* Have to have exactly one selected object */
177 if (selected_list == NULL || g_list_next(selected_list) != NULL) {
178 message_error("Selected list is %s while selected_count is %d\n",
179 (selected_list?"long":"empty"), diagram->data->selected_count);
180 return;
183 obj = (Object *)g_list_first(selected_list)->data;
185 /* Possibly react differently at a handle? */
187 /* Get its menu */
188 if (obj->ops->get_object_menu == NULL) {
189 dia_menu = NULL;
190 } else {
191 dia_menu = (obj->ops->get_object_menu)(obj, &object_menu_clicked_point);
194 if (dia_menu != NULL) {
195 if (dia_menu->app_data == NULL)
196 create_object_menu(dia_menu);
198 /* Update active/nonactive menuitems */
199 for (i=0;i<dia_menu->num_items;i++) {
200 DiaMenuItem *item = &dia_menu->items[i];
201 gtk_widget_set_sensitive(GTK_WIDGET(item->app_data),
202 item->active & DIAMENU_ACTIVE);
203 if (item->active & DIAMENU_TOGGLE)
204 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item->app_data),
205 item->active & DIAMENU_TOGGLE_ON);
208 menu = GTK_MENU(dia_menu->app_data);
209 } else {
210 if (no_menu == NULL) {
211 no_menu = gtk_menu_new();
213 menu_item = gtk_menu_item_new_with_label(_("No object menu"));
214 gtk_menu_shell_append (GTK_MENU_SHELL (no_menu), menu_item);
215 gtk_widget_show(menu_item);
216 gtk_widget_set_sensitive(menu_item, FALSE);
218 menu = GTK_MENU(no_menu);
221 popup_shell = ddisp->shell;
222 gtk_menu_popup(menu, NULL, NULL, NULL, NULL, bevent->button, bevent->time);
225 gint
226 ddisplay_focus_in_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
228 DDisplay *ddisp;
230 g_return_val_if_fail (widget != NULL, FALSE);
231 g_return_val_if_fail (event != NULL, FALSE);
232 g_return_val_if_fail (data != NULL, FALSE);
234 ddisp = (DDisplay *)data;
236 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
237 /* FIXME?: gtk_widget_draw_focus(widget); */
238 gtk_im_context_focus_in(GTK_IM_CONTEXT(ddisp->im_context));
240 return FALSE;
243 gint
244 ddisplay_focus_out_event(GtkWidget *widget, GdkEventFocus *event,gpointer data)
246 DDisplay *ddisp;
247 int return_val;
249 g_return_val_if_fail (widget != NULL, FALSE);
250 g_return_val_if_fail (event != NULL, FALSE);
251 g_return_val_if_fail (data != NULL, FALSE);
253 return_val = FALSE;
255 ddisp = (DDisplay *)data;
257 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
259 gtk_im_context_focus_out(GTK_IM_CONTEXT(ddisp->im_context));
261 return return_val;
264 void
265 ddisplay_realize(GtkWidget *widget, gpointer data)
267 DDisplay *ddisp;
269 g_return_if_fail(widget != NULL);
270 g_return_if_fail(data != NULL);
272 ddisp = (DDisplay *)data;
274 gtk_im_context_set_client_window(GTK_IM_CONTEXT(ddisp->im_context),
275 GDK_WINDOW(ddisp->shell->window));
278 void
279 ddisplay_unrealize (GtkWidget *widget, gpointer data)
281 DDisplay *ddisp;
283 g_return_if_fail (widget != NULL);
284 g_return_if_fail (data != NULL);
286 ddisp = (DDisplay *) data;
288 if (ddisp->im_context)
289 gtk_im_context_set_client_window(GTK_IM_CONTEXT(ddisp->im_context),
290 GDK_WINDOW(ddisp->shell->window));
293 void
294 ddisplay_size_allocate (GtkWidget *widget,
295 GtkAllocation *allocation,
296 gpointer data)
298 DDisplay *ddisp;
300 g_return_if_fail (widget != NULL);
301 g_return_if_fail (allocation != NULL);
302 g_return_if_fail (data != NULL);
304 widget->allocation = *allocation;
305 ddisp = (DDisplay *)data;
308 void
309 ddisplay_popup_menu(DDisplay *ddisp, GdkEventButton *event)
311 GtkWidget *menu;
313 popup_shell = ddisp->shell;
314 menus_get_image_menu(&menu, NULL);
316 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
317 event->button, event->time);
320 static void handle_key_event(DDisplay *ddisp, Focus *focus, guint keysym,
321 const gchar *str, int strlen) {
322 Object *obj = focus->obj;
323 Point p = obj->position;
324 ObjectChange *obj_change = NULL;
325 gboolean modified;
327 object_add_updates(obj, ddisp->diagram);
329 modified = (focus->key_event)(focus, keysym, str, strlen,
330 &obj_change);
332 /* Make sure object updates its data and its connected: */
333 p = obj->position;
334 (obj->ops->move)(obj,&p);
335 diagram_update_connections_object(ddisp->diagram,obj,TRUE);
337 object_add_updates(obj, ddisp->diagram);
339 if (modified) {
340 diagram_modified(ddisp->diagram);
341 if (obj_change != NULL) {
342 undo_object_change(ddisp->diagram, obj, obj_change);
343 undo_set_transactionpoint(ddisp->diagram->undo);
347 diagram_flush(ddisp->diagram);
351 void ddisplay_im_context_commit(GtkIMContext *context, const gchar *str,
352 DDisplay *ddisp) {
353 /* When using IM, we'll not get many key events past the IM filter,
354 mostly IM Commits.
356 regardless of the platform, "str" should be a clean UTF8 string
357 (the default IM on X should perform the local->UTF8 conversion)
360 handle_key_event(ddisp, active_focus(), 0, str, g_utf8_strlen(str,-1));
363 void ddisplay_im_context_preedit_changed(GtkIMContext *context,
364 DDisplay *ddisp) {
365 /* char *str;
366 PangoAttrList *attrs;
367 gint cursor_pos;
369 gtk_im_context_get_preedit_string(ddisp->im_context,
370 &str,&attrs,&cursor_pos);
372 g_message("received a 'preedit changed'; str=%s cursor_pos=%i",
373 str,cursor_pos);
375 g_free(str);
376 pango_attr_list_unref(attrs);
380 gint
381 ddisplay_canvas_events (GtkWidget *canvas,
382 GdkEvent *event,
383 DDisplay *ddisp)
385 GdkEventExpose *eevent;
386 GdkEventMotion *mevent;
387 GdkEventButton *bevent;
388 GdkEventKey *kevent;
389 GdkEventScroll *sevent;
390 gint tx, ty;
391 GdkModifierType tmask;
392 guint state = 0;
393 Focus *focus;
394 Object *obj;
395 Rectangle *visible;
396 Point middle;
397 int return_val;
398 int key_handled;
399 int width, height;
400 int new_size;
402 return_val = FALSE;
404 if (!canvas->window)
405 return FALSE;
407 switch (event->type)
409 case GDK_EXPOSE:
410 eevent = (GdkEventExpose *) event;
411 ddisplay_add_display_area(ddisp,
412 eevent->area.x, eevent->area.y,
413 eevent->area.x + eevent->area.width,
414 eevent->area.y + eevent->area.height);
415 ddisplay_flush(ddisp);
416 break;
418 case GDK_SCROLL:
419 sevent = (GdkEventScroll *) event;
421 switch (sevent->direction)
423 case GDK_SCROLL_UP:
424 ddisplay_scroll_up(ddisp);
425 break;
426 case GDK_SCROLL_DOWN:
427 ddisplay_scroll_down(ddisp);
428 break;
429 case GDK_SCROLL_LEFT:
430 ddisplay_scroll_left(ddisp);
431 break;
432 case GDK_SCROLL_RIGHT:
433 ddisplay_scroll_right(ddisp);
434 break;
435 default:
436 break;
438 ddisplay_flush (ddisp);
439 break;
441 case GDK_CONFIGURE:
442 if (ddisp->renderer != NULL) {
443 width = dia_renderer_get_width_pixels (ddisp->renderer);
444 height = dia_renderer_get_height_pixels (ddisp->renderer);
445 new_size = ((width != ddisp->canvas->allocation.width) ||
446 (height != ddisp->canvas->allocation.height));
447 } else {
448 new_size = TRUE;
450 if (new_size) {
451 ddisplay_resize_canvas(ddisp,
452 ddisp->canvas->allocation.width,
453 ddisp->canvas->allocation.height);
454 ddisplay_update_scrollbars(ddisp);
456 display_set_active(ddisp);
457 break;
459 case GDK_FOCUS_CHANGE: {
460 GdkEventFocus *focus = (GdkEventFocus*)event;
461 if (focus->in) {
462 display_set_active(ddisp);
463 ddisplay_do_update_menu_sensitivity(ddisp);
465 break;
467 case GDK_2BUTTON_PRESS:
468 display_set_active(ddisp);
469 bevent = (GdkEventButton *) event;
470 state = bevent->state;
472 switch (bevent->button)
474 case 1:
475 if (*active_tool->double_click_func)
476 (*active_tool->double_click_func) (active_tool, bevent, ddisp);
477 break;
479 case 2:
480 break;
482 case 3:
483 break;
485 default:
486 break;
488 break;
490 case GDK_BUTTON_PRESS:
491 display_set_active(ddisp);
492 bevent = (GdkEventButton *) event;
493 state = bevent->state;
495 ddisplay_untransform_coords(ddisp,
496 (int)bevent->x, (int)bevent->y,
497 &object_menu_clicked_point.x,
498 &object_menu_clicked_point.y);
500 switch (bevent->button)
502 case 1:
503 /* get the focus again, may be lost by zoom combo */
504 gtk_widget_grab_focus(canvas);
505 if (*active_tool->button_press_func)
506 (*active_tool->button_press_func) (active_tool, bevent, ddisp);
507 break;
509 case 2:
510 if (ddisp->menu_bar == NULL) {
511 popup_object_menu(ddisp, bevent);
513 break;
515 case 3:
516 if (ddisp->menu_bar == NULL) {
517 if (bevent->state & GDK_CONTROL_MASK) {
518 /* for two button mouse users ... */
519 popup_object_menu(ddisp, bevent);
520 break;
522 ddisplay_popup_menu(ddisp, bevent);
523 break;
525 else {
526 popup_object_menu(ddisp, bevent);
527 break;
529 default:
530 break;
532 break;
534 case GDK_BUTTON_RELEASE:
535 display_set_active(ddisp);
536 bevent = (GdkEventButton *) event;
537 state = bevent->state;
539 switch (bevent->button)
541 case 1:
542 if (*active_tool->button_release_func)
543 (*active_tool->button_release_func) (active_tool,
544 bevent, ddisp);
545 break;
547 case 2:
548 break;
550 case 3:
551 break;
553 default:
554 break;
556 break;
558 case GDK_MOTION_NOTIFY:
559 /* get the pointer position */
560 gdk_window_get_pointer (canvas->window, &tx, &ty, &tmask);
562 mevent = (GdkEventMotion *) event;
563 state = mevent->state;
565 if (mevent->is_hint) {
566 mevent->x = tx;
567 mevent->y = ty;
568 mevent->state = tmask;
569 mevent->is_hint = FALSE;
571 if (*active_tool->motion_func)
572 (*active_tool->motion_func) (active_tool, mevent, ddisp);
573 break;
575 case GDK_KEY_PRESS:
576 display_set_active(ddisp);
577 kevent = (GdkEventKey *)event;
578 state = kevent->state;
579 key_handled = FALSE;
581 focus = active_focus();
582 if ((focus != NULL) &&
583 !(state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) ) {
584 /* Keys goes to the active focus. */
585 obj = focus->obj;
586 if (diagram_is_selected(ddisp->diagram, obj)) {
588 if (!gtk_im_context_filter_keypress(
589 GTK_IM_CONTEXT(ddisp->im_context), kevent)) {
591 /*! key event not swallowed by the input method ? */
592 handle_key_event(ddisp, focus, kevent->keyval,
593 kevent->string, kevent->length);
595 diagram_flush(ddisp->diagram);
598 return_val = key_handled = TRUE;
601 if (!key_handled) {
602 /* No focus to receive keys, take care of it ourselves. */
603 return_val = TRUE;
604 gtk_im_context_reset(GTK_IM_CONTEXT(ddisp->im_context));
606 switch(kevent->keyval) {
607 case GDK_Up:
608 ddisplay_scroll_up(ddisp);
609 ddisplay_flush(ddisp);
610 break;
611 case GDK_Down:
612 ddisplay_scroll_down(ddisp);
613 ddisplay_flush(ddisp);
614 break;
615 case GDK_Left:
616 ddisplay_scroll_left(ddisp);
617 ddisplay_flush(ddisp);
618 break;
619 case GDK_Right:
620 ddisplay_scroll_right(ddisp);
621 ddisplay_flush(ddisp);
622 break;
623 case GDK_KP_Add:
624 case GDK_plus:
625 visible = &ddisp->visible;
626 middle.x = visible->left*0.5 + visible->right*0.5;
627 middle.y = visible->top*0.5 + visible->bottom*0.5;
629 ddisplay_zoom(ddisp, &middle, M_SQRT2);
630 break;
631 case GDK_KP_Subtract:
632 case GDK_minus:
633 visible = &ddisp->visible;
634 middle.x = visible->left*0.5 + visible->right*0.5;
635 middle.y = visible->top*0.5 + visible->bottom*0.5;
637 ddisplay_zoom(ddisp, &middle, M_SQRT1_2);
638 break;
639 case GDK_Shift_L:
640 case GDK_Shift_R:
641 if (active_tool->type == MAGNIFY_TOOL)
642 set_zoom_out(active_tool);
643 break;
644 default:
645 if (kevent->string && 0 == strcmp(" ",kevent->string)) {
646 tool_select_former();
647 } else {
648 return_val = FALSE;
652 break;
654 case GDK_KEY_RELEASE:
655 kevent = (GdkEventKey *) event;
656 state = kevent->state;
657 if (gtk_im_context_filter_keypress(GTK_IM_CONTEXT(ddisp->im_context),
658 kevent)) {
659 return_val = TRUE;
660 } else {
661 switch(kevent->keyval) {
662 case GDK_Shift_L:
663 case GDK_Shift_R:
664 if (active_tool->type == MAGNIFY_TOOL)
665 set_zoom_in(active_tool);
666 break;
667 default:
668 break;
671 break;
673 default:
674 break;
677 return return_val;
680 gint
681 ddisplay_hsb_update (GtkAdjustment *adjustment,
682 DDisplay *ddisp)
684 ddisplay_set_origo(ddisp, adjustment->value, ddisp->origo.y);
685 ddisplay_add_update_all(ddisp);
686 ddisplay_flush(ddisp);
687 return FALSE;
690 gint
691 ddisplay_vsb_update (GtkAdjustment *adjustment,
692 DDisplay *ddisp)
694 ddisplay_set_origo(ddisp, ddisp->origo.x, adjustment->value);
695 ddisplay_add_update_all(ddisp);
696 ddisplay_flush(ddisp);
697 return FALSE;
700 gint
701 ddisplay_delete (GtkWidget *widget, GdkEvent *event, gpointer data)
703 DDisplay *ddisp;
705 ddisp = (DDisplay *)data;
707 ddisplay_close(ddisp);
708 return TRUE;
711 void
712 ddisplay_destroy (GtkWidget *widget, gpointer data)
714 DDisplay *ddisp;
716 ddisp = (DDisplay *) data;
718 if (popup_shell == ddisp->shell) {
719 popup_shell = NULL;
722 ddisplay_really_destroy(ddisp);
725 void
726 ddisplay_drop_object(DDisplay *ddisp, gint x, gint y, ObjectType *otype,
727 gpointer user_data)
729 Point droppoint;
730 Handle *handle1, *handle2;
731 Object *obj;
732 GList *list;
734 ddisplay_untransform_coords(ddisp, x, y, &droppoint.x, &droppoint.y);
736 snap_to_grid(ddisp, &droppoint.x, &droppoint.y);
738 obj = dia_object_default_create (otype, &droppoint,
739 user_data,
740 &handle1, &handle2);
742 diagram_add_object(ddisp->diagram, obj);
743 diagram_remove_all_selected(ddisp->diagram, TRUE); /* unselect all */
744 diagram_select(ddisp->diagram, obj);
745 obj->ops->selectf(obj, &droppoint, ddisp->renderer);
747 /* Connect first handle if possible: */
748 if ((handle1 != NULL) &&
749 (handle1->connect_type != HANDLE_NONCONNECTABLE)) {
750 object_connect_display(ddisp, obj, handle1);
752 object_add_updates(obj, ddisp->diagram);
753 diagram_flush(ddisp->diagram);
755 list = g_list_prepend(NULL, obj);
756 undo_insert_objects(ddisp->diagram, list, 1);
757 diagram_update_extents(ddisp->diagram);
758 diagram_modified(ddisp->diagram);
760 undo_set_transactionpoint(ddisp->diagram->undo);