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.
23 #include "modify_tool.h"
24 #include "handle_ops.h"
25 #include "object_ops.h"
26 #include "connectionpoint_ops.h"
28 #include "properties.h"
30 #include "preferences.h"
32 #include "highlight.h"
35 #include "diacanvas.h"
36 #include "prop_text.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
,
44 GdkEventButton
*event
);
45 static void modify_button_press(ModifyTool
*tool
, GdkEventButton
*event
,
47 static void modify_button_release(ModifyTool
*tool
, GdkEventButton
*event
,
49 static void modify_motion(ModifyTool
*tool
, GdkEventMotion
*event
,
51 static void modify_double_click(ModifyTool
*tool
, GdkEventButton
*event
,
53 static void modify_make_text_edit(DDisplay
*ddisp
, DiaObject
*obj
,
55 static void modify_start_text_edit(DDisplay
*ddisp
, Text
*text
, DiaObject
*obj
,
60 create_modify_tool(void)
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
;
71 tool
->state
= STATE_NONE
;
72 tool
->break_connections
= 0;
73 tool
->auto_scrolled
= FALSE
;
75 tool
->orig_pos
= NULL
;
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;
95 free_modify_tool(Tool
*tool
)
97 ModifyTool
*mtool
= (ModifyTool
*)tool
;
99 gdk_gc_unref(mtool
->gc
);
104 This function is buggy. Fix it later!
106 transitive_select(DDisplay *ddisp, Point *clickedpoint, DiaObject *obj)
112 for(i = 0; i < obj->num_connections; 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);
127 click_select_object(DDisplay
*ddisp
, Point
*clickedpoint
,
128 GdkEventButton
*event
)
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
,
144 /* Selected an object. */
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
);
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
);
184 /* Maybe start editing text */
186 modify_make_text_edit(ddisp
, obj
, clickedpoint
);
191 } /* Else part moved to allow union/intersection select */
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
)
213 dist
= diagram_find_closest_handle(ddisp
->diagram
, &handle
,
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
;
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
));
233 modify_button_press(ModifyTool
*tool
, GdkEventButton
*event
,
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
))
247 clicked_obj
= click_select_object(ddisp
, &clickedpoint
, event
);
248 if (do_if_clicked_handle(ddisp
, tool
, &clickedpoint
, event
))
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
));
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
,
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
);
290 modify_double_click(ModifyTool
*tool
, GdkEventButton
*event
,
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.
329 modify_move_already(ModifyTool
*tool
, DDisplay
*ddisp
, Point
*to
)
331 static gboolean settings_taken
= FALSE
;
332 static int double_click_time
= 250;
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
340 GtkSettings
*settings
= gtk_settings_get_default();
341 if (settings
== NULL
) {
342 g_message(_("Couldn't get GTK settings"));
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) {
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
) {
360 if (ddisplay_transform_length(ddisp
, dist
) > MIN_PIXELS
) {
361 return (ddisplay_transform_length(ddisp
, dist
) > MIN_PIXELS
);
367 /** Used for highlighting mainpoint connections. */
368 static Color mainpoint_color
= { 1.0, 0.8, 0.0 };
369 /** Used for highlighting normal connections. */
370 static Color cp_color
= { 1.0, 0.0, 0.0 };
373 modify_motion(ModifyTool
*tool
, GdkEventMotion
*event
,
377 Point now
, delta
, full_delta
;
378 gboolean auto_scroll
, vertical
= FALSE
;
379 ConnectionPoint
*connectionpoint
;
380 ObjectChange
*objchange
;
382 GtkStatusbar
*statusbar
;
385 if (tool
->state
==STATE_NONE
)
386 return; /* Fast path... */
388 auto_scroll
= ddisplay_autoscroll(ddisp
, event
->x
, event
->y
);
390 ddisplay_untransform_coords(ddisp
, event
->x
, event
->y
, &to
.x
, &to
.y
);
392 if (!modify_move_already(tool
, ddisp
, &to
)) return;
394 switch (tool
->state
) {
395 case STATE_MOVE_OBJECT
:
397 if (tool
->orig_pos
== NULL
) {
402 /* consider non-selected children affected */
403 list
= parent_list_affected(ddisp
->diagram
->data
->selected
);
404 tool
->orig_pos
= g_new(Point
, g_list_length(list
));
406 while (list
!= NULL
) {
407 obj
= (DiaObject
*) list
->data
;
408 tool
->orig_pos
[i
] = obj
->position
;
409 list
= g_list_next(list
); i
++;
413 if (tool
->break_connections
)
414 diagram_unconnect_selected(ddisp
->diagram
); /* Pushes UNDO info */
416 if (event
->state
& GDK_CONTROL_MASK
) {
418 point_sub(&full_delta
, &tool
->start_at
);
419 vertical
= (fabs(full_delta
.x
) < fabs(full_delta
.y
));
422 point_add(&to
, &tool
->move_compensate
);
423 snap_to_grid(ddisp
, &to
.x
, &to
.y
);
425 now
= tool
->object
->position
;
428 point_sub(&delta
, &now
);
430 if (event
->state
& GDK_CONTROL_MASK
) {
431 /* Up-down or left-right */
433 delta
.x
= tool
->start_at
.x
+ tool
->move_compensate
.x
- now
.x
;
435 delta
.y
= tool
->start_at
.y
+ tool
->move_compensate
.y
- now
.y
;
439 object_add_updates_list(ddisp
->diagram
->data
->selected
, ddisp
->diagram
);
440 objchange
= object_list_move_delta(ddisp
->diagram
->data
->selected
, &delta
);
441 if (objchange
!= NULL
) {
442 undo_object_change(ddisp
->diagram
, tool
->object
, objchange
);
444 object_add_updates_list(ddisp
->diagram
->data
->selected
, ddisp
->diagram
);
446 object_add_updates(tool
->object
, ddisp
->diagram
);
448 /* Put current mouse position in status bar */
449 statusbar
= GTK_STATUSBAR (ddisp
->modified_status
);
450 context_id
= gtk_statusbar_get_context_id (statusbar
, "ObjectPos");
452 postext
= g_strdup_printf("%.3f, %.3f - %.3f, %.3f",
453 tool
->object
->bounding_box
.left
,
454 tool
->object
->bounding_box
.top
,
455 tool
->object
->bounding_box
.right
,
456 tool
->object
->bounding_box
.bottom
);
458 gtk_statusbar_pop (statusbar
, context_id
);
459 gtk_statusbar_push (statusbar
, context_id
, postext
);
463 diagram_update_connections_selection(ddisp
->diagram
);
464 diagram_flush(ddisp
->diagram
);
466 case STATE_MOVE_HANDLE
:
468 point_sub(&full_delta
, &tool
->start_at
);
470 /* make sure resizing is restricted to its parent */
473 /* if resize was blocked by parent, that means the resizing was
474 outward, thus it won't bother the children so we don't have to
475 check the children */
476 if (!parent_handle_move_out_check(tool
->object
, &to
))
477 parent_handle_move_in_check(tool
->object
, &to
, &tool
->start_at
);
479 /* Move to ConnectionPoint if near: */
481 object_find_connectpoint_display(ddisp
, &to
, tool
->object
, TRUE
);
483 if (event
->state
& GDK_CONTROL_MASK
)
484 vertical
= (fabs(full_delta
.x
) < fabs(full_delta
.y
));
486 highlight_reset_all(ddisp
->diagram
);
487 if ((tool
->handle
->connect_type
!= HANDLE_NONCONNECTABLE
)) {
488 if (connectionpoint
!= NULL
) {
490 to
= connectionpoint
->pos
;
491 if (connectionpoint
->flags
& CP_FLAGS_MAIN
) {
492 hi_color
= &mainpoint_color
;
494 hi_color
= &cp_color
;
496 highlight_object(connectionpoint
->object
, hi_color
, ddisp
->diagram
);
497 ddisplay_set_all_cursor(get_cursor(CURSOR_CONNECT
));
500 if (connectionpoint
== NULL
) {
501 /* No connectionopoint near, then snap to grid (if enabled) */
502 snap_to_grid(ddisp
, &to
.x
, &to
.y
);
503 ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL
));
506 if (tool
->break_connections
) {
507 /* break connections to the handle currently selected. */
508 if (tool
->handle
->connected_to
!=NULL
) {
509 Change
*change
= undo_unconnect(ddisp
->diagram
, tool
->object
,
512 (change
->apply
)(change
, ddisp
->diagram
);
516 if (event
->state
& GDK_CONTROL_MASK
) {
517 /* Up-down or left-right */
519 to
.x
= tool
->start_at
.x
;
521 to
.y
= tool
->start_at
.y
;
525 if (tool
->orig_pos
== NULL
) {
526 tool
->orig_pos
= g_new(Point
, 1);
527 *tool
->orig_pos
= tool
->handle
->pos
;
530 /* Put current mouse position in status bar */
531 statusbar
= GTK_STATUSBAR (ddisp
->modified_status
);
532 context_id
= gtk_statusbar_get_context_id (statusbar
, "CursorPos");
534 postext
= g_strdup_printf("%.3f, %.3f", to
.x
, to
.y
);
536 gtk_statusbar_pop (statusbar
, context_id
);
537 gtk_statusbar_push (statusbar
, context_id
, postext
);
541 object_add_updates(tool
->object
, ddisp
->diagram
);
544 objchange
= tool
->object
->ops
->move_handle(tool
->object
, tool
->handle
,
545 &to
, connectionpoint
,
546 HANDLE_MOVE_USER
, gdk_event_to_dia_ModifierKeys(event
->state
));
547 if (objchange
!= NULL
) {
548 undo_object_change(ddisp
->diagram
, tool
->object
, objchange
);
550 object_add_updates(tool
->object
, ddisp
->diagram
);
552 diagram_update_connections_selection(ddisp
->diagram
);
553 diagram_flush(ddisp
->diagram
);
555 case STATE_BOX_SELECT
:
557 if (!auto_scroll
&& !tool
->auto_scrolled
)
559 gdk_draw_rectangle (ddisp
->canvas
->window
, tool
->gc
, FALSE
,
561 tool
->x2
- tool
->x1
, tool
->y2
- tool
->y1
);
566 ddisplay_transform_coords(ddisp
,
567 MIN(tool
->start_box
.x
, tool
->end_box
.x
),
568 MIN(tool
->start_box
.y
, tool
->end_box
.y
),
569 &tool
->x1
, &tool
->y1
);
570 ddisplay_transform_coords(ddisp
,
571 MAX(tool
->start_box
.x
, tool
->end_box
.x
),
572 MAX(tool
->start_box
.y
, tool
->end_box
.y
),
573 &tool
->x2
, &tool
->y2
);
575 gdk_draw_rectangle (ddisp
->canvas
->window
, tool
->gc
, FALSE
,
577 tool
->x2
- tool
->x1
, tool
->y2
- tool
->y1
);
583 message_error("Internal error: Strange state in modify_tool\n");
587 tool
->auto_scrolled
= auto_scroll
;
590 /** Find the list of objects selected by current rubberbanding.
591 * The list should be freed after use. */
593 find_selected_objects(DDisplay
*ddisp
, ModifyTool
*tool
)
596 r
.left
= MIN(tool
->start_box
.x
, tool
->end_box
.x
);
597 r
.right
= MAX(tool
->start_box
.x
, tool
->end_box
.x
);
598 r
.top
= MIN(tool
->start_box
.y
, tool
->end_box
.y
);
599 r
.bottom
= MAX(tool
->start_box
.y
, tool
->end_box
.y
);
601 if (prefs
.reverse_rubberbanding_intersects
) {
602 if (tool
->start_box
.x
> tool
->end_box
.x
) {
604 layer_find_objects_intersecting_rectangle(ddisp
->diagram
->data
->active_layer
, &r
);
607 layer_find_objects_in_rectangle(ddisp
->diagram
->data
->active_layer
, &r
);
611 layer_find_objects_in_rectangle(ddisp
->diagram
->data
->active_layer
, &r
);
616 modify_button_release(ModifyTool
*tool
, GdkEventButton
*event
,
622 DiaObject
*active_obj
= NULL
;
623 ObjectChange
*objchange
;
625 tool
->break_connections
= FALSE
;
626 ddisplay_set_all_cursor(default_cursor
);
628 switch (tool
->state
) {
629 case STATE_MOVE_OBJECT
:
630 /* Return to normal state */
631 gdk_pointer_ungrab (event
->time
);
633 ddisplay_untransform_coords(ddisp
, event
->x
, event
->y
, &to
.x
, &to
.y
);
634 if (!modify_move_already(tool
, ddisp
, &to
)) {
635 tool
->orig_pos
= NULL
;
636 tool
->state
= STATE_NONE
;
640 diagram_update_connections_selection(ddisp
->diagram
);
642 if (tool
->orig_pos
!= NULL
) {
643 /* consider the non-selected children affected */
644 list
= parent_list_affected(ddisp
->diagram
->data
->selected
);
645 dest_pos
= g_new(Point
, g_list_length(list
));
647 while (list
!= NULL
) {
648 DiaObject
*obj
= (DiaObject
*) list
->data
;
649 dest_pos
[i
] = obj
->position
;
650 list
= g_list_next(list
); i
++;
653 undo_move_objects(ddisp
->diagram
, tool
->orig_pos
, dest_pos
,
654 parent_list_affected(ddisp
->diagram
->data
->selected
));
657 ddisplay_connect_selected(ddisp
); /* pushes UNDO info */
658 diagram_update_extents(ddisp
->diagram
);
659 diagram_modified(ddisp
->diagram
);
660 diagram_flush(ddisp
->diagram
);
662 undo_set_transactionpoint(ddisp
->diagram
->undo
);
664 tool
->orig_pos
= NULL
;
665 tool
->state
= STATE_NONE
;
667 case STATE_MOVE_HANDLE
:
668 gdk_pointer_ungrab (event
->time
);
669 tool
->state
= STATE_NONE
;
671 if (tool
->orig_pos
!= NULL
) {
672 undo_move_handle(ddisp
->diagram
, tool
->handle
, tool
->object
,
673 *tool
->orig_pos
, tool
->last_to
);
677 object_add_updates(tool
->object
, ddisp
->diagram
);
678 objchange
= tool
->object
->ops
->move_handle(tool
->object
, tool
->handle
,
679 &tool
->last_to
, NULL
,
680 HANDLE_MOVE_USER_FINAL
,gdk_event_to_dia_ModifierKeys(event
->state
));
681 if (objchange
!= NULL
) {
682 undo_object_change(ddisp
->diagram
, tool
->object
, objchange
);
685 object_add_updates(tool
->object
, ddisp
->diagram
);
687 /* Connect if possible: */
688 if (tool
->handle
->connect_type
!= HANDLE_NONCONNECTABLE
) {
689 object_connect_display(ddisp
, tool
->object
, tool
->handle
, TRUE
); /* pushes UNDO info */
690 diagram_update_connections_selection(ddisp
->diagram
);
693 highlight_reset_all(ddisp
->diagram
);
694 diagram_flush(ddisp
->diagram
);
696 diagram_modified(ddisp
->diagram
);
697 diagram_update_extents(ddisp
->diagram
);
699 undo_set_transactionpoint(ddisp
->diagram
->undo
);
701 if (tool
->orig_pos
!= NULL
) {
702 g_free(tool
->orig_pos
);
703 tool
->orig_pos
= NULL
;
707 case STATE_BOX_SELECT
:
708 gdk_pointer_ungrab (event
->time
);
709 /* Remember the currently active object for reactivating. */
710 active_obj
= active_focus()!=NULL
?focus_get_object(active_focus()):NULL
;
711 /* Remove last box: */
712 if (!tool
->auto_scrolled
) {
713 gdk_draw_rectangle (ddisp
->canvas
->window
, tool
->gc
, FALSE
,
715 tool
->x2
- tool
->x1
, tool
->y2
- tool
->y1
);
719 GList
*list
, *list_to_free
;
721 list
= list_to_free
= find_selected_objects(ddisp
, tool
);
723 if (selection_style
== SELECT_REPLACE
&&
724 !(event
->state
& GDK_SHIFT_MASK
)) {
725 /* Not Multi-select => Remove all selected */
726 diagram_remove_all_selected(ddisp
->diagram
, TRUE
);
729 if (selection_style
== SELECT_INTERSECTION
) {
730 GList
*intersection
= NULL
;
732 while (list
!= NULL
) {
733 DiaObject
*obj
= (DiaObject
*)list
->data
;
735 if (diagram_is_selected(ddisp
->diagram
, obj
)) {
736 intersection
= g_list_append(intersection
, obj
);
739 list
= g_list_next(list
);
742 diagram_remove_all_selected(ddisp
->diagram
, TRUE
);
743 while (list
!= NULL
) {
744 DiaObject
*obj
= (DiaObject
*)list
->data
;
746 diagram_select(ddisp
->diagram
, obj
);
748 list
= g_list_next(list
);
750 g_list_free(intersection
);
752 while (list
!= NULL
) {
753 DiaObject
*obj
= (DiaObject
*)list
->data
;
755 if (selection_style
== SELECT_REMOVE
) {
756 if (diagram_is_selected(ddisp
->diagram
, obj
))
757 diagram_unselect_object(ddisp
->diagram
, obj
);
758 } else if (selection_style
== SELECT_INVERT
) {
759 if (diagram_is_selected(ddisp
->diagram
, obj
))
760 diagram_unselect_object(ddisp
->diagram
, obj
);
762 diagram_select(ddisp
->diagram
, obj
);
764 if (!diagram_is_selected(ddisp
->diagram
, obj
))
765 diagram_select(ddisp
->diagram
, obj
);
768 list
= g_list_next(list
);
772 g_list_free(list_to_free
);
776 if (active_obj
!= NULL
&&
777 diagram_is_selected(ddisp
->diagram
, active_obj
)) {
778 textedit_activate_object(ddisp
, active_obj
, NULL
);
780 textedit_activate_first(ddisp
);
782 ddisplay_do_update_menu_sensitivity(ddisp
);
783 ddisplay_flush(ddisp
);
785 tool
->state
= STATE_NONE
;
790 message_error("Internal error: Strange state in modify_tool\n");
795 #define EDIT_BORDER_WIDTH 5
798 modify_edit_end(GtkWidget
*widget
, GdkEventFocus
*event
, gpointer data
)
800 GtkTextView
*view
= GTK_TEXT_VIEW(widget
);
801 DiaObject
*obj
= (DiaObject
*)data
;
802 GQuark quark
= g_quark_from_string(PROP_TYPE_TEXT
);
803 const PropDescription
*props
= obj
->ops
->describe_props(obj
);
806 printf("Ending focus\n");
808 for (i
= 0; props
[i
].name
!= NULL
; i
++) {
809 printf("Testing to remove: %s\n", props
[i
].name
);
810 if (props
[i
].type_quark
== quark
) {
811 GPtrArray
*textprops
= g_ptr_array_sized_new(1);
812 TextProperty
*textprop
;
813 Property
*prop
= props
[i
].ops
->new_prop(&props
[i
], pdtpp_true
);
815 GtkTextIter start
, end
;
817 printf("Going to stop %d\n", i
);
818 buf
= gtk_text_view_get_buffer(view
);
819 g_ptr_array_add(textprops
, prop
);
820 obj
->ops
->get_props(obj
, textprops
);
821 textprop
= (TextProperty
*)prop
;
822 if (textprop
->text_data
!= NULL
) g_free(textprop
->text_data
);
823 gtk_text_buffer_get_bounds(buf
, &start
, &end
);
824 textprop
->text_data
= gtk_text_buffer_get_text(buf
, &start
, &end
, TRUE
);
825 printf("Setting text %s\n", textprop
->text_data
);
826 obj
->ops
->set_props(obj
, textprops
);
827 gtk_widget_destroy(widget
);
833 /** Start editing the first text among selected objects, if any */
835 modify_edit_first_text(DDisplay
*ddisp
)
837 GList
*edits
= ddisp
->diagram
->data
->text_edits
;
840 Text
*text
= (Text
*)edits
->data
;
841 modify_start_text_edit(ddisp
, text
, text
->parent_object
, NULL
);
846 modify_start_text_edit(DDisplay
*ddisp
, Text
*text
, DiaObject
*obj
, Point
*clickedpoint
)
848 GtkWidget
*view
= gtk_text_view_new();
852 GtkTextIter start
, end
;
856 printf("modify_start_text_edit\n");
857 ddisplay_transform_coords(ddisp
,
861 ascent
= dia_font_scaled_ascent(text
->line
[0],
865 printf("Text prop string %s pos %d, %d ascent %f\n",
866 text
->line
[0], x
, y
, ascent
);
867 ascent_pixels
= ddisplay_transform_length(ddisp
, ascent
);
869 dia_canvas_put(DIA_CANVAS(ddisp
->canvas
), view
,
870 x
-EDIT_BORDER_WIDTH
, y
-EDIT_BORDER_WIDTH
);
871 buf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(view
));
872 for (i
= 0; i
< text
->numlines
; i
++) {
873 gtk_text_buffer_insert_at_cursor(buf
, text
->line
[i
], -1);
876 gtk_text_buffer_create_tag(buf
,
879 dia_font_get_description(text
->font
),
881 gtk_text_buffer_get_bounds(buf
, &start
, &end
);
882 gtk_text_buffer_apply_tag(buf
, fonttag
, &start
, &end
);
884 printf("Above lines %d below %d\n",
885 gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(view
)),
886 gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(view
)));
888 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view
),
889 GTK_TEXT_WINDOW_LEFT
,
891 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view
),
892 GTK_TEXT_WINDOW_RIGHT
,
894 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view
),
897 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view
),
898 GTK_TEXT_WINDOW_BOTTOM
,
900 /* Using deprecated function because the fucking gobject documentation
903 gtk_signal_connect(GTK_OBJECT(view
), "focus-out-event",
904 modify_edit_end
, obj
);
906 gtk_widget_grab_focus(view
);
907 gtk_widget_show(view
);
911 modify_make_text_edit(DDisplay
*ddisp
, DiaObject
*obj
, Point
*clickedpoint
)
913 const PropDescription
*props
= obj
->ops
->describe_props(obj
);
915 for (i
= 0; props
[i
].name
!= NULL
; i
++) {
916 GQuark type
= g_quark_from_string(PROP_TYPE_TEXT
);
917 printf("Testing %s\n", props
[i
].type
);
918 if (props
[i
].type_quark
== type
) {
919 GtkWidget
*view
= gtk_text_view_new();
920 GPtrArray
*textprops
= g_ptr_array_sized_new(1);
921 TextProperty
*textprop
;
922 Property
*prop
= props
[i
].ops
->new_prop(&props
[i
], pdtpp_true
);
926 GtkTextIter start
, end
;
930 g_ptr_array_add(textprops
, prop
);
932 printf("Found text prop %d\n", i
);
933 obj
->ops
->get_props(obj
, textprops
);
934 textprop
= (TextProperty
*)prop
;
935 ddisplay_transform_coords(ddisp
,
936 textprop
->attr
.position
.x
,
937 textprop
->attr
.position
.y
,
939 ascent
= dia_font_scaled_ascent(textprop
->text_data
,
941 textprop
->attr
.height
,
943 printf("Text prop string %s pos %d, %d ascent %f\n",
944 textprop
->text_data
, x
, y
, ascent
);
945 ascent_pixels
= ddisplay_transform_length(ddisp
, ascent
);
947 dia_canvas_put(DIA_CANVAS(ddisp
->canvas
), view
,
948 x
-EDIT_BORDER_WIDTH
, y
-EDIT_BORDER_WIDTH
);
949 buf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(view
));
950 gtk_text_buffer_insert_at_cursor(buf
, textprop
->text_data
, -1);
952 gtk_text_buffer_create_tag(buf
,
955 dia_font_get_description(textprop
->attr
.font
),
957 gtk_text_buffer_get_bounds(buf
, &start
, &end
);
958 gtk_text_buffer_apply_tag(buf
, fonttag
, &start
, &end
);
960 printf("Above lines %d below %d\n",
961 gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(view
)),
962 gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(view
)));
964 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view
),
965 GTK_TEXT_WINDOW_LEFT
,
967 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view
),
968 GTK_TEXT_WINDOW_RIGHT
,
970 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view
),
973 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(view
),
974 GTK_TEXT_WINDOW_BOTTOM
,
976 /* Using deprecated function because the fucking gobject documentation
979 gtk_signal_connect(GTK_OBJECT(view
), "focus-out-event",
980 modify_edit_end
, obj
);
982 gtk_widget_grab_focus(view
);
983 gtk_widget_show(view
);