UML class fix bigtime.
[dia.git] / app / modify_tool.c
blob5617e2f0c204382dd6813a201b03b69135417f43
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.
18 #include <config.h>
20 #include <stdio.h>
21 #include <math.h>
23 #include "modify_tool.h"
24 #include "handle_ops.h"
25 #include "object_ops.h"
26 #include "connectionpoint_ops.h"
27 #include "message.h"
28 #include "properties.h"
29 #include "select.h"
30 #include "preferences.h"
31 #include "cursor.h"
32 #include "highlight.h"
33 #include "textedit.h"
35 #include "diacanvas.h"
36 #include "prop_text.h"
37 #include "gtk/gtk.h"
40 static DiaObject *click_select_object(DDisplay *ddisp, Point *clickedpoint,
41 GdkEventButton *event);
42 static int do_if_clicked_handle(DDisplay *ddisp, ModifyTool *tool,
43 Point *clickedpoint,
44 GdkEventButton *event);
45 static void modify_button_press(ModifyTool *tool, GdkEventButton *event,
46 DDisplay *ddisp);
47 static void modify_button_release(ModifyTool *tool, GdkEventButton *event,
48 DDisplay *ddisp);
49 static void modify_motion(ModifyTool *tool, GdkEventMotion *event,
50 DDisplay *ddisp);
51 static void modify_double_click(ModifyTool *tool, GdkEventButton *event,
52 DDisplay *ddisp);
53 static void modify_make_text_edit(DDisplay *ddisp, DiaObject *obj,
54 Point *clickedpoint);
55 static void modify_start_text_edit(DDisplay *ddisp, Text *text, DiaObject *obj,
56 Point *clickedpoint);
59 Tool *
60 create_modify_tool(void)
62 ModifyTool *tool;
64 tool = g_new0(ModifyTool, 1);
65 tool->tool.type = MODIFY_TOOL;
66 tool->tool.button_press_func = (ButtonPressFunc) &modify_button_press;
67 tool->tool.button_release_func = (ButtonReleaseFunc) &modify_button_release;
68 tool->tool.motion_func = (MotionFunc) &modify_motion;
69 tool->tool.double_click_func = (DoubleClickFunc) &modify_double_click;
70 tool->gc = NULL;
71 tool->state = STATE_NONE;
72 tool->break_connections = 0;
73 tool->auto_scrolled = FALSE;
75 tool->orig_pos = NULL;
77 return (Tool *)tool;
80 static ModifierKeys
81 gdk_event_to_dia_ModifierKeys(guint event_state)
83 ModifierKeys mod = MODIFIER_NONE;
84 if (event_state & GDK_SHIFT_MASK)
85 mod |= MODIFIER_SHIFT;
86 /* Used intenally do not propagate
87 * if (event_state & GDK_CONTROL_MASK)
88 mod | MODIFIER_CONTROL;
90 return mod;
94 void
95 free_modify_tool(Tool *tool)
97 ModifyTool *mtool = (ModifyTool *)tool;
98 if (mtool->gc)
99 gdk_gc_unref(mtool->gc);
100 g_free(mtool);
104 This function is buggy. Fix it later!
105 static void
106 transitive_select(DDisplay *ddisp, Point *clickedpoint, DiaObject *obj)
108 guint i;
109 GList *j;
110 DiaObject *obj1;
112 for(i = 0; i < obj->num_connections; i++) {
113 printf("%d\n", i);
114 j = obj->connections[i]->connected;
115 while(j != NULL && (obj1 = (DiaObject *)j->data) != NULL) {
116 diagram_select(ddisp->diagram, obj1);
117 obj1->ops->select(obj1, clickedpoint,
118 (Renderer *)ddisp->renderer);
119 transitive_select(ddisp, clickedpoint, obj1);
120 j = g_list_next(j);
126 static DiaObject *
127 click_select_object(DDisplay *ddisp, Point *clickedpoint,
128 GdkEventButton *event)
130 Diagram *diagram;
131 real click_distance;
132 DiaObject *obj;
134 diagram = ddisp->diagram;
136 /* Find the closest object to select it: */
138 click_distance = ddisplay_untransform_length(ddisp, 3.0);
140 obj = diagram_find_clicked_object(diagram, clickedpoint,
141 click_distance);
143 if (obj!=NULL) {
144 /* Selected an object. */
145 GList *already;
146 /*printf("Selected object!\n");*/
148 already = g_list_find(diagram->data->selected, obj);
149 if (already == NULL) { /* Not already selected */
150 /*printf("Not already selected\n");*/
152 if (!(event->state & GDK_SHIFT_MASK)) {
153 /* Not Multi-select => remove current selection */
154 diagram_remove_all_selected(diagram, TRUE);
157 diagram_select(diagram, obj);
158 textedit_activate_object(ddisp, obj, clickedpoint);
161 This stuff is buggy, fix it later.
162 if (event->state & GDK_CONTROL_MASK) {
163 transitive_select(ddisp, clickedpoint, obj);
167 ddisplay_do_update_menu_sensitivity(ddisp);
168 object_add_updates_list(diagram->data->selected, diagram);
169 diagram_flush(diagram);
171 return obj;
172 } else { /* Clicked on already selected. */
173 /*printf("Already selected\n");*/
174 textedit_activate_object(ddisp, obj, clickedpoint);
175 object_add_updates_list(diagram->data->selected, diagram);
176 diagram_flush(diagram);
178 if (event->state & GDK_SHIFT_MASK) { /* Multi-select */
179 /* Remove the selected selected */
180 ddisplay_do_update_menu_sensitivity(ddisp);
181 diagram_unselect_object(ddisp->diagram, (DiaObject *)already->data);
182 diagram_flush(ddisp->diagram);
183 } else {
184 /* Maybe start editing text */
185 #ifdef NEW_TEXT_EDIT
186 modify_make_text_edit(ddisp, obj, clickedpoint);
187 #endif
188 return obj;
191 } /* Else part moved to allow union/intersection select */
193 return NULL;
196 static glong
197 time_micro()
199 GTimeVal tv;
201 g_get_current_time(&tv);
202 return tv.tv_sec*G_USEC_PER_SEC+tv.tv_usec;
205 static int do_if_clicked_handle(DDisplay *ddisp, ModifyTool *tool,
206 Point *clickedpoint, GdkEventButton *event)
208 DiaObject *obj;
209 Handle *handle;
210 real dist;
212 handle = NULL;
213 dist = diagram_find_closest_handle(ddisp->diagram, &handle,
214 &obj, clickedpoint);
215 if (handle_is_clicked(ddisp, handle, clickedpoint)) {
216 tool->state = STATE_MOVE_HANDLE;
217 tool->break_connections = TRUE;
218 tool->last_to = handle->pos;
219 tool->handle = handle;
220 tool->object = obj;
221 gdk_pointer_grab (ddisp->canvas->window, FALSE,
222 GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
223 NULL, NULL, event->time);
224 tool->start_at = handle->pos;
225 tool->start_time = time_micro();
226 ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL));
227 return TRUE;
229 return FALSE;
232 static void
233 modify_button_press(ModifyTool *tool, GdkEventButton *event,
234 DDisplay *ddisp)
236 Point clickedpoint;
237 DiaObject *clicked_obj;
239 ddisplay_untransform_coords(ddisp,
240 (int)event->x, (int)event->y,
241 &clickedpoint.x, &clickedpoint.y);
244 if (do_if_clicked_handle(ddisp, tool, &clickedpoint, event))
245 return;
247 clicked_obj = click_select_object(ddisp, &clickedpoint, event);
248 if (do_if_clicked_handle(ddisp, tool, &clickedpoint, event))
249 return;
251 if ( clicked_obj != NULL ) {
252 tool->state = STATE_MOVE_OBJECT;
253 tool->object = clicked_obj;
254 tool->move_compensate = clicked_obj->position;
255 point_sub(&tool->move_compensate, &clickedpoint);
256 tool->break_connections = TRUE;
257 gdk_pointer_grab (ddisp->canvas->window, FALSE,
258 GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
259 NULL, NULL, event->time);
260 tool->start_at = clickedpoint;
261 tool->start_time = time_micro();
262 ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL));
263 } else {
264 tool->state = STATE_BOX_SELECT;
265 tool->start_box = clickedpoint;
266 tool->end_box = clickedpoint;
267 tool->x1 = tool->x2 = (int) event->x;
268 tool->y1 = tool->y2 = (int) event->y;
270 if (tool->gc == NULL) {
271 tool->gc = gdk_gc_new(ddisp->canvas->window);
272 gdk_gc_set_line_attributes(tool->gc, 1, GDK_LINE_ON_OFF_DASH,
273 GDK_CAP_BUTT, GDK_JOIN_MITER);
274 gdk_gc_set_foreground(tool->gc, &color_gdk_white);
275 gdk_gc_set_function(tool->gc, GDK_XOR);
278 gdk_draw_rectangle (ddisp->canvas->window, tool->gc, FALSE,
279 tool->x1, tool->y1,
280 tool->x2 - tool->x1, tool->y2 - tool->y1);
282 gdk_pointer_grab (ddisp->canvas->window, FALSE,
283 GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
284 NULL, NULL, event->time);
289 static void
290 modify_double_click(ModifyTool *tool, GdkEventButton *event,
291 DDisplay *ddisp)
293 Point clickedpoint;
294 DiaObject *clicked_obj;
296 ddisplay_untransform_coords(ddisp,
297 (int)event->x, (int)event->y,
298 &clickedpoint.x, &clickedpoint.y);
300 clicked_obj = click_select_object(ddisp, &clickedpoint, event);
302 if ( clicked_obj != NULL ) {
303 properties_show(ddisp->diagram, clicked_obj);
304 } else { /* No object selected */
305 /*printf("didn't select object\n");*/
306 if (!(event->state & GDK_SHIFT_MASK)) {
307 /* Not Multi-select => Remove all selected */
308 ddisplay_do_update_menu_sensitivity(ddisp);
309 diagram_remove_all_selected(ddisp->diagram, TRUE);
310 diagram_flush(ddisp->diagram);
315 #define MIN_PIXELS 10
317 /** Makes sure that objects aren't accidentally moved when double-clicking
318 * for properties. Objects do not move unless double click time has passed
319 * or the move is 'significant'. Allowing the 'significant' move makes a
320 * regular grab-and-move less jerky.
322 * There's a slight chance that the user could move outside the
323 * minimum movement limit and back in within the double click time
324 * (normally .25 seconds), but that's very, very unlikely. If
325 * it happens, the cursor wouldn't reset just right.
328 static gboolean
329 modify_move_already(ModifyTool *tool, DDisplay *ddisp, Point *to)
331 static gboolean settings_taken = FALSE;
332 static int double_click_time = 250;
333 real dist;
335 if (!settings_taken) {
336 /* One could argue that if the settings were updated while running,
337 we should re-read them. But I don't see any way to get notified,
338 and I don't want to do this whole thing for each bit of the
339 move --Lars */
340 GtkSettings *settings = gtk_settings_get_default();
341 if (settings == NULL) {
342 g_message(_("Couldn't get GTK settings"));
343 } else {
344 g_object_get(G_OBJECT(settings),
345 "gtk-double-click-time", &double_click_time, NULL);
347 settings_taken = TRUE;
349 if (tool->start_time < time_micro()-double_click_time*1000) {
350 return TRUE;
352 dist = distance_point_point_manhattan(&tool->start_at, to);
353 if (ddisp->grid.snap) {
354 real grid_x = ddisp->diagram->grid.width_x;
355 real grid_y = ddisp->diagram->grid.width_y;
356 if (dist > grid_x || dist > grid_y) {
357 return TRUE;
360 if (ddisplay_transform_length(ddisp, dist) > MIN_PIXELS) {
361 return (ddisplay_transform_length(ddisp, dist) > MIN_PIXELS);
362 } else {
363 return FALSE;
368 static void
369 modify_motion(ModifyTool *tool, GdkEventMotion *event,
370 DDisplay *ddisp)
372 Point to;
373 Point now, delta, full_delta;
374 gboolean auto_scroll, vertical = FALSE;
375 ConnectionPoint *connectionpoint;
376 ObjectChange *objchange;
377 gchar *postext;
378 GtkStatusbar *statusbar;
379 guint context_id;
381 if (tool->state==STATE_NONE)
382 return; /* Fast path... */
384 auto_scroll = ddisplay_autoscroll(ddisp, event->x, event->y);
386 ddisplay_untransform_coords(ddisp, event->x, event->y, &to.x, &to.y);
388 if (!modify_move_already(tool, ddisp, &to)) return;
390 switch (tool->state) {
391 case STATE_MOVE_OBJECT:
393 if (tool->orig_pos == NULL) {
394 GList *list;
395 int i;
396 DiaObject *obj;
398 /* consider non-selected children affected */
399 list = parent_list_affected(ddisp->diagram->data->selected);
400 tool->orig_pos = g_new(Point, g_list_length(list));
401 i=0;
402 while (list != NULL) {
403 obj = (DiaObject *) list->data;
404 tool->orig_pos[i] = obj->position;
405 list = g_list_next(list); i++;
409 if (tool->break_connections)
410 diagram_unconnect_selected(ddisp->diagram); /* Pushes UNDO info */
412 if (event->state & GDK_CONTROL_MASK) {
413 full_delta = to;
414 point_sub(&full_delta, &tool->start_at);
415 vertical = (fabs(full_delta.x) < fabs(full_delta.y));
418 point_add(&to, &tool->move_compensate);
419 snap_to_grid(ddisp, &to.x, &to.y);
421 now = tool->object->position;
423 delta = to;
424 point_sub(&delta, &now);
426 if (event->state & GDK_CONTROL_MASK) {
427 /* Up-down or left-right */
428 if (vertical) {
429 delta.x = tool->start_at.x + tool->move_compensate.x - now.x;
430 } else {
431 delta.y = tool->start_at.y + tool->move_compensate.y - now.y;
435 object_add_updates_list(ddisp->diagram->data->selected, ddisp->diagram);
436 objchange = object_list_move_delta(ddisp->diagram->data->selected, &delta);
437 if (objchange != NULL) {
438 undo_object_change(ddisp->diagram, tool->object, objchange);
440 object_add_updates_list(ddisp->diagram->data->selected, ddisp->diagram);
442 object_add_updates(tool->object, ddisp->diagram);
444 /* Put current mouse position in status bar */
445 statusbar = GTK_STATUSBAR (ddisp->modified_status);
446 context_id = gtk_statusbar_get_context_id (statusbar, "ObjectPos");
448 postext = g_strdup_printf("%.3f, %.3f - %.3f, %.3f",
449 tool->object->bounding_box.left,
450 tool->object->bounding_box.top,
451 tool->object->bounding_box.right,
452 tool->object->bounding_box.bottom);
454 gtk_statusbar_pop (statusbar, context_id);
455 gtk_statusbar_push (statusbar, context_id, postext);
457 g_free(postext);
459 diagram_update_connections_selection(ddisp->diagram);
460 diagram_flush(ddisp->diagram);
461 break;
462 case STATE_MOVE_HANDLE:
463 full_delta = to;
464 point_sub(&full_delta, &tool->start_at);
466 /* make sure resizing is restricted to its parent */
469 /* if resize was blocked by parent, that means the resizing was
470 outward, thus it won't bother the children so we don't have to
471 check the children */
472 if (!parent_handle_move_out_check(tool->object, &to))
473 parent_handle_move_in_check(tool->object, &to, &tool->start_at);
475 /* Move to ConnectionPoint if near: */
476 connectionpoint =
477 object_find_connectpoint_display(ddisp, &to, tool->object, TRUE);
479 if (event->state & GDK_CONTROL_MASK)
480 vertical = (fabs(full_delta.x) < fabs(full_delta.y));
482 if ((tool->handle->connect_type != HANDLE_NONCONNECTABLE)) {
483 if (connectionpoint != NULL) {
484 to = connectionpoint->pos;
485 highlight_object(connectionpoint->object, NULL, ddisp->diagram);
486 ddisplay_set_all_cursor(get_cursor(CURSOR_CONNECT));
489 if (connectionpoint == NULL) {
490 /* No connectionopoint near, then snap to grid (if enabled) */
491 snap_to_grid(ddisp, &to.x, &to.y);
492 highlight_reset_all(ddisp->diagram);
493 ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL));
496 if (tool->break_connections) {
497 /* break connections to the handle currently selected. */
498 if (tool->handle->connected_to!=NULL) {
499 Change *change = undo_unconnect(ddisp->diagram, tool->object,
500 tool->handle);
502 (change->apply)(change, ddisp->diagram);
506 if (event->state & GDK_CONTROL_MASK) {
507 /* Up-down or left-right */
508 if (vertical) {
509 to.x = tool->start_at.x;
510 } else {
511 to.y = tool->start_at.y;
515 if (tool->orig_pos == NULL) {
516 tool->orig_pos = g_new(Point, 1);
517 *tool->orig_pos = tool->handle->pos;
520 /* Put current mouse position in status bar */
521 statusbar = GTK_STATUSBAR (ddisp->modified_status);
522 context_id = gtk_statusbar_get_context_id (statusbar, "CursorPos");
524 postext = g_strdup_printf("%.3f, %.3f", to.x, to.y);
526 gtk_statusbar_pop (statusbar, context_id);
527 gtk_statusbar_push (statusbar, context_id, postext);
529 g_free(postext);
531 object_add_updates(tool->object, ddisp->diagram);
533 /* Handle undo */
534 objchange = tool->object->ops->move_handle(tool->object, tool->handle,
535 &to, connectionpoint,
536 HANDLE_MOVE_USER, gdk_event_to_dia_ModifierKeys(event->state));
537 if (objchange != NULL) {
538 undo_object_change(ddisp->diagram, tool->object, objchange);
540 object_add_updates(tool->object, ddisp->diagram);
542 diagram_update_connections_selection(ddisp->diagram);
543 diagram_flush(ddisp->diagram);
544 break;
545 case STATE_BOX_SELECT:
547 if (!auto_scroll && !tool->auto_scrolled)
549 gdk_draw_rectangle (ddisp->canvas->window, tool->gc, FALSE,
550 tool->x1, tool->y1,
551 tool->x2 - tool->x1, tool->y2 - tool->y1);
554 tool->end_box = to;
556 ddisplay_transform_coords(ddisp,
557 MIN(tool->start_box.x, tool->end_box.x),
558 MIN(tool->start_box.y, tool->end_box.y),
559 &tool->x1, &tool->y1);
560 ddisplay_transform_coords(ddisp,
561 MAX(tool->start_box.x, tool->end_box.x),
562 MAX(tool->start_box.y, tool->end_box.y),
563 &tool->x2, &tool->y2);
565 gdk_draw_rectangle (ddisp->canvas->window, tool->gc, FALSE,
566 tool->x1, tool->y1,
567 tool->x2 - tool->x1, tool->y2 - tool->y1);
568 break;
569 case STATE_NONE:
571 break;
572 default:
573 message_error("Internal error: Strange state in modify_tool\n");
576 tool->last_to = to;
577 tool->auto_scrolled = auto_scroll;
580 /** Find the list of objects selected by current rubberbanding.
581 * The list should be freed after use. */
582 static GList *
583 find_selected_objects(DDisplay *ddisp, ModifyTool *tool)
585 Rectangle r;
586 r.left = MIN(tool->start_box.x, tool->end_box.x);
587 r.right = MAX(tool->start_box.x, tool->end_box.x);
588 r.top = MIN(tool->start_box.y, tool->end_box.y);
589 r.bottom = MAX(tool->start_box.y, tool->end_box.y);
591 if (prefs.reverse_rubberbanding_intersects) {
592 if (tool->start_box.x > tool->end_box.x) {
593 return
594 layer_find_objects_intersecting_rectangle(ddisp->diagram->data->active_layer, &r);
595 } else {
596 return
597 layer_find_objects_in_rectangle(ddisp->diagram->data->active_layer, &r);
599 } else {
600 return
601 layer_find_objects_in_rectangle(ddisp->diagram->data->active_layer, &r);
605 static void
606 modify_button_release(ModifyTool *tool, GdkEventButton *event,
607 DDisplay *ddisp)
609 Point *dest_pos, to;
610 GList *list;
611 int i;
612 DiaObject *active_obj = NULL;
613 ObjectChange *objchange;
615 tool->break_connections = FALSE;
616 ddisplay_set_all_cursor(default_cursor);
618 switch (tool->state) {
619 case STATE_MOVE_OBJECT:
620 /* Return to normal state */
621 gdk_pointer_ungrab (event->time);
623 ddisplay_untransform_coords(ddisp, event->x, event->y, &to.x, &to.y);
624 if (!modify_move_already(tool, ddisp, &to)) {
625 tool->orig_pos = NULL;
626 tool->state = STATE_NONE;
627 return;
630 diagram_update_connections_selection(ddisp->diagram);
632 if (tool->orig_pos != NULL) {
633 /* consider the non-selected children affected */
634 list = parent_list_affected(ddisp->diagram->data->selected);
635 dest_pos = g_new(Point, g_list_length(list));
636 i=0;
637 while (list != NULL) {
638 DiaObject *obj = (DiaObject *) list->data;
639 dest_pos[i] = obj->position;
640 list = g_list_next(list); i++;
643 undo_move_objects(ddisp->diagram, tool->orig_pos, dest_pos,
644 parent_list_affected(ddisp->diagram->data->selected));
647 ddisplay_connect_selected(ddisp); /* pushes UNDO info */
648 diagram_update_extents(ddisp->diagram);
649 diagram_modified(ddisp->diagram);
650 diagram_flush(ddisp->diagram);
652 undo_set_transactionpoint(ddisp->diagram->undo);
654 tool->orig_pos = NULL;
655 tool->state = STATE_NONE;
656 break;
657 case STATE_MOVE_HANDLE:
658 gdk_pointer_ungrab (event->time);
659 tool->state = STATE_NONE;
661 if (tool->orig_pos != NULL) {
662 undo_move_handle(ddisp->diagram, tool->handle, tool->object,
663 *tool->orig_pos, tool->last_to);
666 /* Final move: */
667 object_add_updates(tool->object, ddisp->diagram);
668 objchange = tool->object->ops->move_handle(tool->object, tool->handle,
669 &tool->last_to, NULL,
670 HANDLE_MOVE_USER_FINAL,gdk_event_to_dia_ModifierKeys(event->state));
671 if (objchange != NULL) {
672 undo_object_change(ddisp->diagram, tool->object, objchange);
675 object_add_updates(tool->object, ddisp->diagram);
677 /* Connect if possible: */
678 if (tool->handle->connect_type != HANDLE_NONCONNECTABLE) {
679 object_connect_display(ddisp, tool->object, tool->handle, TRUE); /* pushes UNDO info */
680 diagram_update_connections_selection(ddisp->diagram);
683 highlight_reset_all(ddisp->diagram);
684 diagram_flush(ddisp->diagram);
686 diagram_modified(ddisp->diagram);
687 diagram_update_extents(ddisp->diagram);
689 undo_set_transactionpoint(ddisp->diagram->undo);
691 if (tool->orig_pos != NULL) {
692 g_free(tool->orig_pos);
693 tool->orig_pos = NULL;
696 break;
697 case STATE_BOX_SELECT:
698 gdk_pointer_ungrab (event->time);
699 /* Remember the currently active object for reactivating. */
700 active_obj = active_focus()!=NULL?focus_get_object(active_focus()):NULL;
701 /* Remove last box: */
702 if (!tool->auto_scrolled) {
703 gdk_draw_rectangle (ddisp->canvas->window, tool->gc, FALSE,
704 tool->x1, tool->y1,
705 tool->x2 - tool->x1, tool->y2 - tool->y1);
709 GList *list, *list_to_free;
711 list = list_to_free = find_selected_objects(ddisp, tool);
713 if (selection_style == SELECT_REPLACE &&
714 !(event->state & GDK_SHIFT_MASK)) {
715 /* Not Multi-select => Remove all selected */
716 diagram_remove_all_selected(ddisp->diagram, TRUE);
719 if (selection_style == SELECT_INTERSECTION) {
720 GList *intersection = NULL;
722 while (list != NULL) {
723 DiaObject *obj = (DiaObject *)list->data;
725 if (diagram_is_selected(ddisp->diagram, obj)) {
726 intersection = g_list_append(intersection, obj);
729 list = g_list_next(list);
731 list = intersection;
732 diagram_remove_all_selected(ddisp->diagram, TRUE);
733 while (list != NULL) {
734 DiaObject *obj = (DiaObject *)list->data;
736 diagram_select(ddisp->diagram, obj);
738 list = g_list_next(list);
740 g_list_free(intersection);
741 } else {
742 while (list != NULL) {
743 DiaObject *obj = (DiaObject *)list->data;
745 if (selection_style == SELECT_REMOVE) {
746 if (diagram_is_selected(ddisp->diagram, obj))
747 diagram_unselect_object(ddisp->diagram, obj);
748 } else if (selection_style == SELECT_INVERT) {
749 if (diagram_is_selected(ddisp->diagram, obj))
750 diagram_unselect_object(ddisp->diagram, obj);
751 else
752 diagram_select(ddisp->diagram, obj);
753 } else {
754 if (!diagram_is_selected(ddisp->diagram, obj))
755 diagram_select(ddisp->diagram, obj);
758 list = g_list_next(list);
762 g_list_free(list_to_free);
766 if (active_obj != NULL &&
767 diagram_is_selected(ddisp->diagram, active_obj)) {
768 textedit_activate_object(ddisp, active_obj, NULL);
769 } else {
770 textedit_activate_first(ddisp);
772 ddisplay_do_update_menu_sensitivity(ddisp);
773 ddisplay_flush(ddisp);
775 tool->state = STATE_NONE;
776 break;
777 case STATE_NONE:
778 break;
779 default:
780 message_error("Internal error: Strange state in modify_tool\n");
785 #define EDIT_BORDER_WIDTH 5
787 static gboolean
788 modify_edit_end(GtkWidget *widget, GdkEventFocus *event, gpointer data)
790 GtkTextView *view = GTK_TEXT_VIEW(widget);
791 DiaObject *obj = (DiaObject*)data;
792 GQuark quark = g_quark_from_string(PROP_TYPE_TEXT);
793 const PropDescription *props = obj->ops->describe_props(obj);
794 int i;
796 printf("Ending focus\n");
798 for (i = 0; props[i].name != NULL; i++) {
799 printf("Testing to remove: %s\n", props[i].name);
800 if (props[i].type_quark == quark) {
801 GPtrArray *textprops = g_ptr_array_sized_new(1);
802 TextProperty *textprop;
803 Property *prop = props[i].ops->new_prop(&props[i], pdtpp_true);
804 GtkTextBuffer *buf;
805 GtkTextIter start, end;
807 printf("Going to stop %d\n", i);
808 buf = gtk_text_view_get_buffer(view);
809 g_ptr_array_add(textprops, prop);
810 obj->ops->get_props(obj, textprops);
811 textprop = (TextProperty*)prop;
812 if (textprop->text_data != NULL) g_free(textprop->text_data);
813 gtk_text_buffer_get_bounds(buf, &start, &end);
814 textprop->text_data = gtk_text_buffer_get_text(buf, &start, &end, TRUE);
815 printf("Setting text %s\n", textprop->text_data);
816 obj->ops->set_props(obj, textprops);
817 gtk_widget_destroy(widget);
820 return FALSE;
823 /** Start editing the first text among selected objects, if any */
824 static void
825 modify_edit_first_text(DDisplay *ddisp)
827 GList *edits = ddisp->diagram->data->text_edits;
829 if (edits != NULL) {
830 Text *text = (Text*)edits->data;
831 modify_start_text_edit(ddisp, text, text->parent_object, NULL);
835 void
836 modify_start_text_edit(DDisplay *ddisp, Text *text, DiaObject *obj, Point *clickedpoint)
838 GtkWidget *view = gtk_text_view_new();
839 int x, y, i;
840 GtkTextBuffer *buf;
841 GtkTextTag *fonttag;
842 GtkTextIter start, end;
843 real ascent;
844 int ascent_pixels;
846 printf("modify_start_text_edit\n");
847 ddisplay_transform_coords(ddisp,
848 text->position.x,
849 text->position.y,
850 &x, &y);
851 ascent = dia_font_scaled_ascent(text->line[0],
852 text->font,
853 text->height,
854 ddisp->zoom_factor);
855 printf("Text prop string %s pos %d, %d ascent %f\n",
856 text->line[0], x, y, ascent);
857 ascent_pixels = ddisplay_transform_length(ddisp, ascent);
858 y -= ascent_pixels;
859 dia_canvas_put(DIA_CANVAS(ddisp->canvas), view,
860 x-EDIT_BORDER_WIDTH, y-EDIT_BORDER_WIDTH);
861 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
862 for (i = 0; i < text->numlines; i++) {
863 gtk_text_buffer_insert_at_cursor(buf, text->line[i], -1);
865 fonttag =
866 gtk_text_buffer_create_tag(buf,
867 NULL,
868 "font-desc",
869 dia_font_get_description(text->font),
870 NULL);
871 gtk_text_buffer_get_bounds(buf, &start, &end);
872 gtk_text_buffer_apply_tag(buf, fonttag, &start, &end);
874 printf("Above lines %d below %d\n",
875 gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(view)),
876 gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(view)));
878 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view),
879 GTK_TEXT_WINDOW_LEFT,
880 EDIT_BORDER_WIDTH);
881 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view),
882 GTK_TEXT_WINDOW_RIGHT,
883 EDIT_BORDER_WIDTH);
884 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view),
885 GTK_TEXT_WINDOW_TOP,
886 EDIT_BORDER_WIDTH);
887 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view),
888 GTK_TEXT_WINDOW_BOTTOM,
889 EDIT_BORDER_WIDTH);
890 /* Using deprecated function because the fucking gobject documentation
891 * fucking sucks. */
892 #ifdef NEW_TEXT_EDIT
893 gtk_signal_connect(GTK_OBJECT(view), "focus-out-event",
894 modify_edit_end, obj);
895 #endif
896 gtk_widget_grab_focus(view);
897 gtk_widget_show(view);
900 void
901 modify_make_text_edit(DDisplay *ddisp, DiaObject *obj, Point *clickedpoint)
903 const PropDescription *props = obj->ops->describe_props(obj);
904 int i;
905 for (i = 0; props[i].name != NULL; i++) {
906 GQuark type = g_quark_from_string(PROP_TYPE_TEXT);
907 printf("Testing %s\n", props[i].type);
908 if (props[i].type_quark == type) {
909 GtkWidget *view = gtk_text_view_new();
910 GPtrArray *textprops = g_ptr_array_sized_new(1);
911 TextProperty *textprop;
912 Property *prop = props[i].ops->new_prop(&props[i], pdtpp_true);
913 int x, y;
914 GtkTextBuffer *buf;
915 GtkTextTag *fonttag;
916 GtkTextIter start, end;
917 real ascent;
918 int ascent_pixels;
920 g_ptr_array_add(textprops, prop);
922 printf("Found text prop %d\n", i);
923 obj->ops->get_props(obj, textprops);
924 textprop = (TextProperty*)prop;
925 ddisplay_transform_coords(ddisp,
926 textprop->attr.position.x,
927 textprop->attr.position.y,
928 &x, &y);
929 ascent = dia_font_scaled_ascent(textprop->text_data,
930 textprop->attr.font,
931 textprop->attr.height,
932 ddisp->zoom_factor);
933 printf("Text prop string %s pos %d, %d ascent %f\n",
934 textprop->text_data, x, y, ascent);
935 ascent_pixels = ddisplay_transform_length(ddisp, ascent);
936 y -= ascent_pixels;
937 dia_canvas_put(DIA_CANVAS(ddisp->canvas), view,
938 x-EDIT_BORDER_WIDTH, y-EDIT_BORDER_WIDTH);
939 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
940 gtk_text_buffer_insert_at_cursor(buf, textprop->text_data, -1);
941 fonttag =
942 gtk_text_buffer_create_tag(buf,
943 NULL,
944 "font-desc",
945 dia_font_get_description(textprop->attr.font),
946 NULL);
947 gtk_text_buffer_get_bounds(buf, &start, &end);
948 gtk_text_buffer_apply_tag(buf, fonttag, &start, &end);
950 printf("Above lines %d below %d\n",
951 gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(view)),
952 gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(view)));
954 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view),
955 GTK_TEXT_WINDOW_LEFT,
956 EDIT_BORDER_WIDTH);
957 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view),
958 GTK_TEXT_WINDOW_RIGHT,
959 EDIT_BORDER_WIDTH);
960 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view),
961 GTK_TEXT_WINDOW_TOP,
962 EDIT_BORDER_WIDTH);
963 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view),
964 GTK_TEXT_WINDOW_BOTTOM,
965 EDIT_BORDER_WIDTH);
966 /* Using deprecated function because the fucking gobject documentation
967 * fucking sucks. */
968 #ifdef NEW_TEXT_EDIT
969 gtk_signal_connect(GTK_OBJECT(view), "focus-out-event",
970 modify_edit_end, obj);
971 #endif
972 gtk_widget_grab_focus(view);
973 gtk_widget_show(view);