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.
28 #include "object_ops.h"
29 #include "render_eps.h"
33 #include "preferences.h"
34 #include "properties.h"
35 #include "cut_n_paste.h"
36 #include "layer_dialog.h"
37 #include "app_procs.h"
39 #include "load_save.h"
40 #include "recent_files.h"
41 #include "diagram_tree_window.h"
43 #include "dynamic_refresh.h"
45 #include "lib/diamarshal.h"
47 static GList
*open_diagrams
= NULL
;
55 typedef struct _ObjectExtent ObjectExtent
;
57 static gint
diagram_parent_sort_cb(gconstpointer a
, gconstpointer b
);
60 static void diagram_class_init (DiagramClass
*klass
);
61 static gboolean
diagram_init(Diagram
*obj
, const char *filename
);
69 static guint diagram_signals
[LAST_SIGNAL
] = { 0, };
70 static gpointer parent_class
= NULL
;
73 diagram_get_type (void)
75 static GType object_type
= 0;
79 static const GTypeInfo object_info
=
81 sizeof (DiagramClass
),
83 (GBaseFinalizeFunc
) NULL
,
84 (GClassInitFunc
) diagram_class_init
,
85 NULL
, /* class_finalize */
86 NULL
, /* class_data */
92 object_type
= g_type_register_static (DIA_TYPE_DIAGRAM_DATA
,
101 diagram_finalize(GObject
*object
)
103 Diagram
*dia
= DIA_DIAGRAM(object
);
105 assert(dia
->displays
==NULL
);
107 open_diagrams
= g_list_remove(open_diagrams
, dia
);
108 layer_dialog_update_diagram_list();
111 undo_destroy(dia
->undo
);
114 diagram_tree_remove(diagram_tree(), dia
);
116 diagram_cleanup_autosave(dia
);
119 g_free(dia
->filename
);
120 dia
->filename
= NULL
;
122 G_OBJECT_CLASS (parent_class
)->finalize (object
);
126 _diagram_removed (Diagram
* dia
)
131 _diagram_selection_changed (Diagram
* dia
, int n
)
136 diagram_class_init (DiagramClass
*klass
)
138 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
140 parent_class
= g_type_class_peek_parent (klass
);
142 diagram_signals
[REMOVED
] =
143 g_signal_new ("removed",
144 G_TYPE_FROM_CLASS (klass
),
146 G_STRUCT_OFFSET (DiagramClass
, removed
),
148 dia_marshal_VOID__VOID
,
151 diagram_signals
[SELECTION_CHANGED
] =
152 g_signal_new ("selection_changed",
153 G_TYPE_FROM_CLASS (klass
),
155 G_STRUCT_OFFSET (DiagramClass
, selection_changed
),
157 dia_marshal_VOID__INT
,
161 klass
->removed
= _diagram_removed
;
162 klass
->selection_changed
= _diagram_selection_changed
;
164 object_class
->finalize
= diagram_finalize
;
168 dia_open_diagrams(void)
170 return open_diagrams
;
173 /** Initializes a diagram with standard info and sets it to be called
175 * Returns TRUE if everything went ok, FALSE otherwise.
176 * Will return FALSE if filename is not a legal string in the current
180 diagram_init(Diagram
*dia
, const char *filename
)
182 gchar
*newfilename
= NULL
;
183 GError
*error
= NULL
;
185 dia
->data
= &dia
->parent_instance
; /* compatibility */
187 dia
->pagebreak_color
= prefs
.new_diagram
.pagebreak_color
;
189 dia
->grid
.width_x
= prefs
.grid
.x
;
190 dia
->grid
.width_y
= prefs
.grid
.y
;
191 dia
->grid
.width_w
= prefs
.grid
.w
;
192 dia
->grid
.hex_size
= 1.0;
193 dia
->grid
.colour
= prefs
.new_diagram
.grid_color
;
194 dia
->grid
.hex
= prefs
.grid
.hex
;
195 dia
->grid
.visible_x
= 1;
196 dia
->grid
.visible_y
= 1;
197 dia
->grid
.dynamic
= prefs
.grid
.dynamic
;
198 dia
->grid
.major_lines
= prefs
.grid
.major_lines
;
200 dia
->guides
.nhguides
= 0;
201 dia
->guides
.hguides
= NULL
;
202 dia
->guides
.nvguides
= 0;
203 dia
->guides
.vguides
= NULL
;
205 if (dia
->filename
!= NULL
)
206 g_free(dia
->filename
);
207 /* Make sure the filename is absolute */
208 if (!g_path_is_absolute(filename
)) {
209 gchar
*pwd
= g_get_current_dir();
211 newfilename
= g_build_filename(pwd
, filename
, NULL
);
213 filename
= newfilename
;
215 /* All Diagram functions assumes filename in filesystem encoding */
217 dia
->filename
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, &error
);
219 message_error(_("Couldn't convert filename '%s' to UTF-8: %s\n"),
220 dia_message_filename(filename
), error
->message
);
222 dia
->filename
= g_strdup(_("Error"));
227 dia
->mollified
= FALSE
;
228 dia
->autosavefilename
= NULL
;
231 undo_destroy(dia
->undo
);
232 dia
->undo
= new_undo_stack(dia
);
234 if (!g_list_find(open_diagrams
, dia
))
235 open_diagrams
= g_list_prepend(open_diagrams
, dia
);
237 if (app_is_interactive())
238 layer_dialog_update_diagram_list();
245 diagram_load_into(Diagram
*diagram
,
246 const char *filename
,
247 DiaImportFilter
*ifilter
)
250 ifilter
= filter_guess_import_filter(filename
);
251 if (!ifilter
) /* default to native format */
252 ifilter
= &dia_import_filter
;
254 if (!diagram_init(diagram
, filename
)) {
258 if (ifilter
->import_func(filename
, diagram
->data
, ifilter
->user_data
)) {
259 diagram
->unsaved
= FALSE
;
260 diagram_set_modified(diagram
, FALSE
);
261 if (app_is_interactive())
262 recent_file_history_add(filename
);
263 diagram_tree_add(diagram_tree(), diagram
);
270 diagram_load(const char *filename
, DiaImportFilter
*ifilter
)
274 /* TODO: Make diagram not be initialized twice */
275 diagram
= new_diagram(filename
);
276 if (diagram
== NULL
) return NULL
;
278 if (!diagram_load_into (diagram
, filename
, ifilter
)) {
279 diagram_destroy(diagram
);
286 /** Create a new diagram with the given filename.
287 * If the diagram could not be created, e.g. because the filename is not
288 * a legal string in the current encoding, return NULL.
291 new_diagram(const char *filename
) /* Note: filename is copied */
293 Diagram
*dia
= g_object_new(DIA_TYPE_DIAGRAM
, NULL
);
295 if (diagram_init(dia
, filename
)) {
304 diagram_destroy(Diagram
*dia
)
306 g_signal_emit (dia
, diagram_signals
[REMOVED
], 0);
310 /** Returns true if we consider the diagram modified.
313 diagram_is_modified(Diagram
*dia
)
315 return dia
->mollified
|| !undo_is_saved(dia
->undo
);
318 /** We might just have change the diagrams modified status.
319 * This doesn't set the status, but merely updates the display.
322 diagram_modified(Diagram
*dia
)
325 displays
= dia
->displays
;
326 while (displays
!= NULL
) {
327 DDisplay
*display
= (DDisplay
*) displays
->data
;
328 ddisplay_update_statusbar(display
);
329 displays
= g_slist_next(displays
);
331 if (diagram_is_modified(dia
)) dia
->autosaved
= FALSE
;
332 /* diagram_set_modified(dia, TRUE);*/
335 /** Set this diagram explicitly modified. This should not be called
336 * by things that change the undo stack, as those modifications are
337 * noticed through changes in the undo stack.
340 diagram_set_modified(Diagram
*dia
, int modified
)
342 if (dia
->mollified
!= modified
)
344 dia
->mollified
= modified
;
346 diagram_modified(dia
);
349 /* ************ Functions that check for menu sensitivity ********* */
351 /* Suggested optimization: The loops take a lot of the time. Collapse them
352 * into one, have them set flags for the things that have been found true.
353 * Harder to maintain.
356 diagram_selected_any_groups(Diagram
*dia
) {
359 for (selected
= dia
->data
->selected
;
360 selected
!= NULL
; selected
= selected
->next
) {
361 DiaObject
*obj
= (DiaObject
*)selected
->data
;
362 if (IS_GROUP(obj
)) return TRUE
;
368 diagram_selected_any_parents(Diagram
*dia
) {
371 for (selected
= dia
->data
->selected
;
372 selected
!= NULL
; selected
= selected
->next
) {
373 DiaObject
*obj
= (DiaObject
*)selected
->data
;
374 if (object_flags_set(obj
, DIA_OBJECT_CAN_PARENT
) && obj
->children
!= NULL
)
381 diagram_selected_any_children(Diagram
*dia
) {
384 for (selected
= dia
->data
->selected
;
385 selected
!= NULL
; selected
= selected
->next
) {
386 DiaObject
*obj
= (DiaObject
*)selected
->data
;
387 if (obj
->parent
!= NULL
) return TRUE
;
392 /** This is slightly more complex -- must see if any non-parented objects
393 * are within a parenting object.
396 diagram_selected_can_parent(Diagram
*dia
) {
398 GList
*parents
= NULL
;
400 for (selected
= dia
->data
->selected
;
401 selected
!= NULL
; selected
= selected
->next
) {
402 DiaObject
*obj
= (DiaObject
*)selected
->data
;
403 if (object_flags_set(obj
, DIA_OBJECT_CAN_PARENT
)) {
404 parents
= g_list_prepend(parents
, obj
);
407 for (selected
= dia
->data
->selected
;
408 selected
!= NULL
; selected
= selected
->next
) {
409 DiaObject
*obj
= (DiaObject
*)selected
->data
;
410 if (obj
->parent
== NULL
) {
412 Rectangle obj_bb
= obj
->bounding_box
;
413 for (parent_tmp
= parents
; parent_tmp
!= NULL
; parent_tmp
= parent_tmp
->next
) {
414 DiaObject
*p
= (DiaObject
*)parent_tmp
->data
;
415 if (p
== obj
) continue;
416 if (obj_bb
.left
> p
->bounding_box
.left
&&
417 obj_bb
.right
< p
->bounding_box
.right
&&
418 obj_bb
.top
> p
->bounding_box
.top
&&
419 obj_bb
.bottom
< p
->bounding_box
.bottom
) {
421 printf("Obj %f, %f x %f, %f inside %f, %f x %f, %f\n",
426 p->bounding_box.left,
428 p->bounding_box.right,
429 p->bounding_box.bottom);
431 g_list_free(parents
);
437 g_list_free(parents
);
442 This is the real implementation of the sensitivity update.
443 TODO: move it to the DDisplay as it belongs to it IMHO
446 diagram_update_menu_sensitivity (Diagram
*dia
, UpdatableMenuItems
*items
)
448 gint selected_count
= g_list_length (dia
->data
->selected
);
450 gtk_action_set_sensitive (items
->copy
, selected_count
> 0);
451 gtk_action_set_sensitive (items
->cut
, selected_count
> 0);
452 gtk_action_set_sensitive (items
->paste
, cnp_exist_stored_objects());
453 gtk_action_set_sensitive (items
->edit_delete
, selected_count
> 0);
454 gtk_action_set_sensitive (items
->edit_duplicate
, selected_count
> 0);
456 gtk_action_set_sensitive (items
->copy_text
, active_focus() != NULL
);
457 gtk_action_set_sensitive (items
->cut_text
, active_focus() != NULL
);
458 gtk_action_set_sensitive (items
->paste_text
, active_focus() != NULL
);
461 gtk_action_set_sensitive (items
->send_to_back
, selected_count
> 0);
462 gtk_action_set_sensitive (items
->bring_to_front
, selected_count
> 0);
463 gtk_action_set_sensitive (items
->send_backwards
, selected_count
> 0);
464 gtk_action_set_sensitive (items
->bring_forwards
, selected_count
> 0);
466 gtk_action_set_sensitive (items
->parent
, diagram_selected_can_parent (dia
));
467 gtk_action_set_sensitive (items
->unparent
,
468 diagram_selected_any_children (dia
));
469 gtk_action_set_sensitive (items
->unparent_children
,
470 diagram_selected_any_parents (dia
));
471 gtk_action_set_sensitive (items
->group
, selected_count
> 1);
472 gtk_action_set_sensitive (items
->ungroup
, diagram_selected_any_groups (dia
));
473 gtk_action_set_sensitive (items
->properties
, selected_count
> 0);
475 /* Objects->Align menu */
476 gtk_action_set_sensitive (items
->align_h_l
, selected_count
> 1);
477 gtk_action_set_sensitive (items
->align_h_c
, selected_count
> 1);
478 gtk_action_set_sensitive (items
->align_h_r
, selected_count
> 1);
479 gtk_action_set_sensitive (items
->align_h_e
, selected_count
> 1);
480 gtk_action_set_sensitive (items
->align_h_a
, selected_count
> 1);
481 gtk_action_set_sensitive (items
->align_v_t
, selected_count
> 1);
482 gtk_action_set_sensitive (items
->align_v_c
, selected_count
> 1);
483 gtk_action_set_sensitive (items
->align_v_b
, selected_count
> 1);
484 gtk_action_set_sensitive (items
->align_v_e
, selected_count
> 1);
485 gtk_action_set_sensitive (items
->align_v_a
, selected_count
> 1);
489 void diagram_update_menubar_sensitivity(Diagram
*dia
, UpdatableMenuItems
*items
)
491 diagram_update_menu_sensitivity (dia
, items
);
495 void diagram_update_popupmenu_sensitivity(Diagram
*dia
)
497 static int initialized
= 0;
498 static UpdatableMenuItems items
;
500 if (initialized
==0) {
501 menus_initialize_updatable_items (&items
, NULL
);
505 diagram_update_menu_sensitivity (dia
, &items
);
509 diagram_add_ddisplay(Diagram
*dia
, DDisplay
*ddisp
)
511 dia
->displays
= g_slist_prepend(dia
->displays
, ddisp
);
512 dia
->display_count
++;
516 diagram_remove_ddisplay(Diagram
*dia
, DDisplay
*ddisp
)
518 dia
->displays
= g_slist_remove(dia
->displays
, ddisp
);
519 dia
->display_count
--;
521 if (dia
->display_count
== 0) {
522 if (!app_is_embedded()) {
523 /* Don't delete embedded diagram when last view is closed */
524 diagram_destroy(dia
);
530 diagram_add_object(Diagram
*dia
, DiaObject
*obj
)
532 layer_add_object(dia
->data
->active_layer
, obj
);
534 diagram_modified(dia
);
536 diagram_tree_add_object(diagram_tree(), dia
, obj
);
540 diagram_add_object_list(Diagram
*dia
, GList
*list
)
542 layer_add_objects(dia
->data
->active_layer
, list
);
544 diagram_modified(dia
);
546 diagram_tree_add_objects(diagram_tree(), dia
, list
);
550 diagram_selected_break_external(Diagram
*dia
)
553 GList
*connected_list
;
555 DiaObject
*other_obj
;
558 list
= dia
->data
->selected
;
559 while (list
!= NULL
) {
560 obj
= (DiaObject
*)list
->data
;
562 /* Break connections between this object and objects not selected: */
563 for (i
=0;i
<obj
->num_handles
;i
++) {
564 ConnectionPoint
*con_point
;
565 con_point
= obj
->handles
[i
]->connected_to
;
567 if ( con_point
== NULL
)
568 break; /* Not connected */
570 other_obj
= con_point
->object
;
571 if (g_list_find(dia
->data
->selected
, other_obj
) == NULL
) {
572 /* other_obj is not selected, break connection */
573 Change
*change
= undo_unconnect(dia
, obj
, obj
->handles
[i
]);
574 (change
->apply
)(change
, dia
);
575 object_add_updates(obj
, dia
);
579 /* Break connections from non selected objects to this object: */
580 for (i
=0;i
<obj
->num_connections
;i
++) {
581 connected_list
= obj
->connections
[i
]->connected
;
583 while (connected_list
!= NULL
) {
584 other_obj
= (DiaObject
*)connected_list
->data
;
586 if (g_list_find(dia
->data
->selected
, other_obj
) == NULL
) {
587 /* other_obj is not in list, break all connections
588 to obj from other_obj */
590 for (j
=0;j
<other_obj
->num_handles
;j
++) {
591 ConnectionPoint
*con_point
;
592 con_point
= other_obj
->handles
[j
]->connected_to
;
594 if (con_point
&& (con_point
->object
== obj
)) {
596 connected_list
= g_list_previous(connected_list
);
597 change
= undo_unconnect(dia
, other_obj
,
598 other_obj
->handles
[j
]);
599 (change
->apply
)(change
, dia
);
600 if (connected_list
== NULL
)
601 connected_list
= obj
->connections
[i
]->connected
;
606 connected_list
= g_list_next(connected_list
);
609 diagram_tree_remove_object(diagram_tree(), obj
);
610 list
= g_list_next(list
);
615 diagram_remove_all_selected(Diagram
*diagram
, int delete_empty
)
617 object_add_updates_list(diagram
->data
->selected
, diagram
);
618 textedit_remove_focus_all(diagram
);
619 data_remove_all_selected(diagram
->data
);
620 g_signal_emit (diagram
, diagram_signals
[SELECTION_CHANGED
], 0, g_list_length (diagram
->data
->selected
));
624 diagram_unselect_object(Diagram
*diagram
, DiaObject
*obj
)
626 object_add_updates(obj
, diagram
);
627 textedit_remove_focus(obj
, diagram
);
628 data_unselect(diagram
->data
, obj
);
629 g_signal_emit (diagram
, diagram_signals
[SELECTION_CHANGED
], 0, g_list_length (diagram
->data
->selected
));
633 diagram_unselect_objects(Diagram
*dia
, GList
*obj_list
)
638 /* otherwise we would signal objects step by step */
639 g_signal_handlers_block_by_func (dia
, _diagram_selection_changed
, NULL
);
641 while (list
!= NULL
) {
642 obj
= (DiaObject
*) list
->data
;
644 if (g_list_find(dia
->data
->selected
, obj
) != NULL
){
645 diagram_unselect_object(dia
, obj
);
648 list
= g_list_next(list
);
650 g_signal_handlers_unblock_by_func (dia
, _diagram_selection_changed
, NULL
);
651 g_signal_emit (dia
, diagram_signals
[SELECTION_CHANGED
], 0, g_list_length (dia
->data
->selected
));
654 /** Make a single object selected.
655 * Note that an object inside a closed group cannot be made selected, nor
656 * can an object in a non-active layer.
657 * @param diagram The diagram that the object belongs to (sorta redundant now)
658 * @param obj The object that should be made selected.
661 diagram_select(Diagram
*diagram
, DiaObject
*obj
)
663 if (dia_object_is_selectable(obj
)) {
664 data_select(diagram
->data
, obj
);
665 obj
->ops
->selectf(obj
, NULL
, NULL
);
666 object_add_updates(obj
, diagram
);
667 g_signal_emit (diagram
, diagram_signals
[SELECTION_CHANGED
], 0,
668 g_list_length (diagram
->data
->selected
));
673 diagram_select_list(Diagram
*dia
, GList
*list
)
675 g_return_if_fail (dia
&& list
);
676 /* otherwise we would signal objects step by step */
677 g_signal_handlers_block_by_func (dia
, _diagram_selection_changed
, NULL
);
678 while (list
!= NULL
) {
679 DiaObject
*obj
= (DiaObject
*)list
->data
;
681 diagram_select(dia
, obj
);
683 list
= g_list_next(list
);
685 if (active_focus() == NULL
) {
686 textedit_activate_first(ddisplay_active());
688 g_signal_handlers_unblock_by_func (dia
, _diagram_selection_changed
, NULL
);
689 g_signal_emit (dia
, diagram_signals
[SELECTION_CHANGED
], 0, g_list_length (dia
->data
->selected
));
693 diagram_is_selected(Diagram
*diagram
, DiaObject
*obj
)
695 return g_list_find(diagram
->data
->selected
, obj
) != NULL
;
704 list
= open_diagrams
;
706 while (list
!= NULL
) {
707 dia
= (Diagram
*) list
->data
;
709 diagram_add_update_all(dia
);
712 list
= g_list_next(list
);
718 diagram_add_update_all(Diagram
*dia
)
725 ddisp
= (DDisplay
*) l
->data
;
727 ddisplay_add_update_all(ddisp
);
734 diagram_add_update(Diagram
*dia
, Rectangle
*update
)
741 ddisp
= (DDisplay
*) l
->data
;
743 ddisplay_add_update(ddisp
, update
);
749 /** Add an update of the given rectangle, but with an additional
750 * border around it. The pixels are added after the rectangle has
751 * been converted to pixel coords.
752 * Currently used for leaving room for highlighting.
755 diagram_add_update_with_border(Diagram
*dia
, Rectangle
*update
,
763 ddisp
= (DDisplay
*) l
->data
;
765 ddisplay_add_update_with_border(ddisp
, update
, pixel_border
);
772 diagram_add_update_pixels(Diagram
*dia
, Point
*point
,
773 int pixel_width
, int pixel_height
)
780 ddisp
= (DDisplay
*) l
->data
;
782 ddisplay_add_update_pixels(ddisp
, point
, pixel_width
, pixel_height
);
789 diagram_flush(Diagram
*dia
)
795 ddisp
= (DDisplay
*) l
->data
;
797 ddisplay_flush(ddisp
);
801 dynobj_refresh_kick();
805 diagram_find_clicked_object(Diagram
*dia
, Point
*pos
,
808 return layer_find_closest_object_except(dia
->data
->active_layer
,
813 diagram_find_clicked_object_except(Diagram
*dia
, Point
*pos
,
814 real maxdist
, GList
*avoid
)
816 return layer_find_closest_object_except(dia
->data
->active_layer
, pos
,
821 * Always returns the last handle in an object that has
822 * the closest distance
825 diagram_find_closest_handle(Diagram
*dia
, Handle
**closest
,
826 DiaObject
**object
, Point
*pos
)
834 mindist
= 1000000.0; /* Realy big value... */
838 l
= dia
->data
->selected
;
840 obj
= (DiaObject
*) l
->data
;
842 for (i
=0;i
<obj
->num_handles
;i
++) {
843 handle
= obj
->handles
[i
];
844 /* Note: Uses manhattan metric for speed... */
845 dist
= distance_point_point_manhattan(pos
, &handle
->pos
);
860 diagram_find_closest_connectionpoint(Diagram
*dia
,
861 ConnectionPoint
**closest
,
865 real dist
= 100000000.0;
867 for (i
=0;i
<dia
->data
->layers
->len
;i
++) {
868 Layer
*layer
= (Layer
*)g_ptr_array_index(dia
->data
->layers
, i
);
869 ConnectionPoint
*this_cp
;
871 if (layer
->connectable
) {
872 this_dist
= layer_find_closest_connectionpoint(layer
,
873 &this_cp
, pos
, notthis
);
874 if (this_dist
< dist
) {
884 diagram_update_extents(Diagram
*dia
)
886 gfloat cur_scale
= dia
->data
->paper
.scaling
;
887 /* anropar update_scrollbars() */
889 if (data_update_extents(dia
->data
)) {
890 /* Update scrollbars because extents were changed: */
896 ddisp
= (DDisplay
*) l
->data
;
898 ddisplay_update_scrollbars(ddisp
);
902 if (cur_scale
!= dia
->data
->paper
.scaling
) {
903 diagram_add_update_all(dia
);
909 /* Remove connections from obj to objects outside created group. */
911 strip_connections(DiaObject
*obj
, GList
*not_strip_list
, Diagram
*dia
)
917 for (i
=0;i
<obj
->num_handles
;i
++) {
918 handle
= obj
->handles
[i
];
919 if ((handle
->connected_to
!= NULL
) &&
920 (g_list_find(not_strip_list
, handle
->connected_to
->object
)==NULL
)) {
921 change
= undo_unconnect(dia
, obj
, handle
);
922 (change
->apply
)(change
, dia
);
930 diagram_parent_sort_cb(gconstpointer _a
, gconstpointer _b
)
932 ObjectExtent
**a
= (ObjectExtent
**)_a
;
933 ObjectExtent
**b
= (ObjectExtent
**)_b
;
935 if ((*a
)->extent
.left
< (*b
)->extent
.left
)
937 else if ((*a
)->extent
.left
> (*b
)->extent
.left
)
940 if ((*a
)->extent
.top
< (*b
)->extent
.top
)
942 else if ((*a
)->extent
.top
> (*b
)->extent
.top
)
949 /* needs faster algorithm -- if we find that parenting is slow.
950 * If it works, don't optimize it until it's a hotspot. */
951 void diagram_parent_selected(Diagram
*dia
)
953 GList
*list
= dia
->data
->selected
;
954 int length
= g_list_length(list
);
957 gboolean any_parented
= FALSE
;
958 GPtrArray
*extents
= g_ptr_array_sized_new(length
);
961 oe
= g_new(ObjectExtent
, 1);
962 oe
->object
= list
->data
;
963 parent_handle_extents(list
->data
, &oe
->extent
);
964 g_ptr_array_add(extents
, oe
);
965 list
= g_list_next(list
);
967 /* sort all the objects by their left position */
968 g_ptr_array_sort(extents
, diagram_parent_sort_cb
);
970 for (idx
= 0; idx
< length
; idx
++)
972 ObjectExtent
*oe1
= g_ptr_array_index(extents
, idx
);
973 if (oe1
->object
->parent
)
976 for (idx2
= idx
+ 1; idx2
< length
; idx2
++)
978 ObjectExtent
*oe2
= g_ptr_array_index(extents
, idx2
);
979 if (!object_flags_set(oe2
->object
, DIA_OBJECT_CAN_PARENT
))
982 if (oe1
->extent
.right
<= oe2
->extent
.right
983 && oe1
->extent
.bottom
<= oe2
->extent
.bottom
)
986 change
= undo_parenting(dia
, oe2
->object
, oe1
->object
, TRUE
);
987 (change
->apply
)(change
, dia
);
990 oe1->object->parent = oe2->object;
991 oe2->object->children = g_list_append(oe2->object->children, oe1->object);
997 g_ptr_array_free(extents
, TRUE
);
999 diagram_modified(dia
);
1001 undo_set_transactionpoint(dia
->undo
);
1005 /** Remove all selected objects from their parents (if any). */
1006 void diagram_unparent_selected(Diagram
*dia
)
1009 DiaObject
*obj
, *parent
;
1011 gboolean any_unparented
= FALSE
;
1013 for (list
= dia
->data
->selected
; list
!= NULL
; list
= g_list_next(list
))
1015 obj
= (DiaObject
*) list
->data
;
1016 parent
= obj
->parent
;
1021 change
= undo_parenting(dia
, parent
, obj
, FALSE
);
1022 (change
->apply
)(change
, dia
);
1023 any_unparented
= TRUE
;
1025 parent->children = g_list_remove(parent->children, obj);
1029 if (any_unparented
) {
1030 diagram_modified(dia
);
1032 undo_set_transactionpoint(dia
->undo
);
1036 /** Remove all children from the selected parents. */
1037 void diagram_unparent_children_selected(Diagram
*dia
)
1040 DiaObject
*obj
, *child
;
1041 gboolean any_unparented
= FALSE
;
1042 for (list
= dia
->data
->selected
; list
!= NULL
; list
= g_list_next(list
))
1044 obj
= (DiaObject
*) list
->data
;
1045 if (!object_flags_set(obj
, DIA_OBJECT_CAN_PARENT
) || !obj
->children
)
1048 any_unparented
= TRUE
;
1049 /* Yes, this creates a whole bunch of Changes. They're lightweight
1050 * structures, though, and it's easier to assure correctness this
1051 * way. If needed, we can make a parent undo with a list of children.
1053 while (obj
->children
!= NULL
) {
1055 child
= (DiaObject
*) obj
->children
->data
;
1056 change
= undo_parenting(dia
, obj
, child
, FALSE
);
1057 /* This will remove one item from the list, so the while terminates. */
1058 (change
->apply
)(change
, dia
);
1060 if (obj
->children
!= NULL
)
1061 printf("Obj still has %d children\n",
1062 g_list_length(obj
->children
));
1064 if (any_unparented
) {
1065 diagram_modified(dia
);
1067 undo_set_transactionpoint(dia
->undo
);
1071 void diagram_group_selected(Diagram
*dia
)
1081 /* the following is wrong as it screws up the selected list, see bug #153525
1082 * I just don't get what was originally intented so please speak up if you know --hb
1084 dia
->data
->selected
= parent_list_affected(dia
->data
->selected
);
1087 orig_list
= g_list_copy(dia
->data
->active_layer
->objects
);
1089 /* We have to rebuild the selection list so that it is the same
1090 order as in the Diagram list. */
1091 group_list
= diagram_get_sorted_selected_remove(dia
);
1094 while (list
!= NULL
) {
1095 obj
= (DiaObject
*)list
->data
;
1097 /* Remove connections from obj to objects outside created group. */
1098 /* strip_connections sets up its own undo info. */
1099 /* The connections aren't reattached by ungroup. */
1100 strip_connections(obj
, dia
->data
->selected
, dia
);
1102 list
= g_list_next(list
);
1105 /* Remove list of selected objects */
1106 textedit_remove_focus_all(dia
);
1107 data_remove_all_selected(dia
->data
);
1109 group
= group_create(group_list
);
1110 change
= undo_group_objects(dia
, group_list
, group
, orig_list
);
1111 (change
->apply
)(change
, dia
);
1113 /* Select the created group */
1114 diagram_select(dia
, group
);
1116 diagram_modified(dia
);
1119 undo_set_transactionpoint(dia
->undo
);
1122 void diagram_ungroup_selected(Diagram
*dia
)
1126 GList
*selected
, *selection_copy
;
1130 if (g_list_length(dia
->data
->selected
) < 1) {
1131 message_error("Trying to ungroup with no selected objects.");
1135 selection_copy
= g_list_copy(dia
->data
->selected
);
1136 selected
= selection_copy
;
1137 while (selected
!= NULL
) {
1138 group
= (DiaObject
*)selected
->data
;
1140 if (IS_GROUP(group
)) {
1144 diagram_unselect_object(dia
, group
);
1146 group_list
= group_objects(group
);
1147 diagram_select_list(dia
, group_list
);
1149 group_index
= layer_object_index(dia
->data
->active_layer
, group
);
1151 change
= undo_ungroup_objects(dia
, group_list
, group
, group_index
);
1152 (change
->apply
)(change
, dia
);
1156 selected
= g_list_next(selected
);
1158 g_list_free(selection_copy
);
1161 diagram_modified(dia
);
1163 undo_set_transactionpoint(dia
->undo
);
1168 diagram_get_sorted_selected(Diagram
*dia
)
1170 return data_get_sorted_selected(dia
->data
);
1173 /** Remove the currently selected objects from the diagram's object list.
1174 * Returns a newly created list of the selected objects, in order.
1177 diagram_get_sorted_selected_remove(Diagram
*dia
)
1179 diagram_modified(dia
);
1181 return data_get_sorted_selected_remove(dia
->data
);
1185 diagram_place_under_selected(Diagram
*dia
)
1190 if (g_list_length (dia
->data
->selected
) == 0)
1193 orig_list
= g_list_copy(dia
->data
->active_layer
->objects
);
1195 sorted_list
= diagram_get_sorted_selected_remove(dia
);
1196 object_add_updates_list(sorted_list
, dia
);
1197 layer_add_objects_first(dia
->data
->active_layer
, sorted_list
);
1199 undo_reorder_objects(dia
, g_list_copy(sorted_list
), orig_list
);
1201 diagram_modified(dia
);
1203 undo_set_transactionpoint(dia
->undo
);
1207 diagram_place_over_selected(Diagram
*dia
)
1212 if (g_list_length (dia
->data
->selected
) == 0)
1215 orig_list
= g_list_copy(dia
->data
->active_layer
->objects
);
1217 sorted_list
= diagram_get_sorted_selected_remove(dia
);
1218 object_add_updates_list(sorted_list
, dia
);
1219 layer_add_objects(dia
->data
->active_layer
, sorted_list
);
1221 undo_reorder_objects(dia
, g_list_copy(sorted_list
), orig_list
);
1223 diagram_modified(dia
);
1225 undo_set_transactionpoint(dia
->undo
);
1229 diagram_place_up_selected(Diagram
*dia
)
1234 GList
*new_list
= NULL
;
1236 if (g_list_length (dia
->data
->selected
) == 0)
1239 orig_list
= g_list_copy(dia
->data
->active_layer
->objects
);
1241 sorted_list
= diagram_get_sorted_selected(dia
);
1242 object_add_updates_list(orig_list
, dia
);
1244 new_list
= g_list_copy(orig_list
);
1245 stmp
= g_list_last(sorted_list
);
1247 for (tmp
= g_list_last(new_list
);
1249 tmp
= g_list_previous(tmp
)) {
1250 if (stmp
== NULL
) break;
1251 if (tmp
->prev
== NULL
) break;
1252 if (tmp
->data
== stmp
->data
) {
1253 stmp
= g_list_previous(stmp
);
1254 } else if (tmp
->prev
->data
== stmp
->data
) {
1255 void *swap
= tmp
->data
;
1256 tmp
->data
= tmp
->prev
->data
;
1257 tmp
->prev
->data
= swap
;
1258 stmp
= g_list_previous(stmp
);
1262 layer_set_object_list(dia
->data
->active_layer
, new_list
);
1264 undo_reorder_objects(dia
, g_list_copy(sorted_list
), orig_list
);
1266 diagram_modified(dia
);
1268 undo_set_transactionpoint(dia
->undo
);
1272 diagram_place_down_selected(Diagram
*dia
)
1277 GList
*new_list
= NULL
;
1279 if (g_list_length (dia
->data
->selected
) == 0)
1282 orig_list
= g_list_copy(dia
->data
->active_layer
->objects
);
1284 sorted_list
= diagram_get_sorted_selected(dia
);
1285 object_add_updates_list(orig_list
, dia
);
1288 g_assert(g_list_length (dia
->data
->selected
) == g_list_length(sorted_list
));
1290 new_list
= g_list_copy(orig_list
);
1294 for (tmp
= new_list
; tmp
!= NULL
; tmp
= g_list_next(tmp
)) {
1295 if (stmp
== NULL
) break;
1296 if (tmp
->next
== NULL
) break;
1297 if (tmp
->data
== stmp
->data
) {
1298 /* This just takes care of any starting matches */
1299 stmp
= g_list_next(stmp
);
1300 } else if (tmp
->next
->data
== stmp
->data
) {
1301 /* This flips the non-selected element forwards, ala bubblesort */
1302 void *swap
= tmp
->data
;
1303 tmp
->data
= tmp
->next
->data
;
1304 tmp
->next
->data
= swap
;
1305 stmp
= g_list_next(stmp
);
1309 layer_set_object_list(dia
->data
->active_layer
, new_list
);
1311 undo_reorder_objects(dia
, g_list_copy(sorted_list
), orig_list
);
1313 diagram_modified(dia
);
1315 undo_set_transactionpoint(dia
->undo
);
1319 diagram_set_filename(Diagram
*dia
, const char *filename
)
1325 g_free(dia
->filename
);
1326 dia
->filename
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
1328 title
= diagram_get_name(dia
);
1332 ddisp
= (DDisplay
*) l
->data
;
1334 ddisplay_set_title(ddisp
, title
);
1336 l
= g_slist_next(l
);
1341 layer_dialog_update_diagram_list();
1342 recent_file_history_add(filename
);
1344 diagram_tree_update_name(diagram_tree(), dia
);
1347 /** Returns a string with a 'sensible' (human-readable) name for the
1348 * diagram. The string should be freed after use.
1349 * This name may or may not be the same as the filename.
1352 diagram_get_name(Diagram
*dia
)
1354 gchar
*title
= strrchr(dia
->filename
, G_DIR_SEPARATOR
);
1356 title
= dia
->filename
;
1361 return g_strdup(title
);
1364 int diagram_modified_exists(void)
1369 list
= open_diagrams
;
1371 while (list
!= NULL
) {
1372 dia
= (Diagram
*) list
->data
;
1374 if (diagram_is_modified(dia
))
1377 list
= g_list_next(list
);
1382 void diagram_object_modified(Diagram
*dia
, DiaObject
*object
)
1384 diagram_tree_update_object(diagram_tree(), dia
, object
);