New group work, and utility funcs.
[dia.git] / app / diagram.c
bloba65641f94512ccf1b024f99e1ee7c7f0f2102cf6
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 #include <config.h>
21 #include <assert.h>
22 #include <string.h>
24 #include "intl.h"
25 #include "diagram.h"
26 #include "object.h"
27 #include "group.h"
28 #include "object_ops.h"
29 #include "render_eps.h"
30 #include "focus.h"
31 #include "message.h"
32 #include "menus.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"
38 #include "dia_dirs.h"
39 #include "load_save.h"
40 #include "recent_files.h"
41 #include "diagram_tree_window.h"
42 #include "autosave.h"
43 #include "dynamic_refresh.h"
44 #include "textedit.h"
45 #include "lib/diamarshal.h"
47 static GList *open_diagrams = NULL;
49 struct _ObjectExtent
51 DiaObject *object;
52 Rectangle extent;
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);
63 enum {
64 SELECTION_CHANGED,
65 REMOVED,
66 LAST_SIGNAL
69 static guint diagram_signals[LAST_SIGNAL] = { 0, };
70 static gpointer parent_class = NULL;
72 GType
73 diagram_get_type (void)
75 static GType object_type = 0;
77 if (!object_type)
79 static const GTypeInfo object_info =
81 sizeof (DiagramClass),
82 (GBaseInitFunc) NULL,
83 (GBaseFinalizeFunc) NULL,
84 (GClassInitFunc) diagram_class_init,
85 NULL, /* class_finalize */
86 NULL, /* class_data */
87 sizeof (Diagram),
88 0, /* n_preallocs */
89 NULL /* init */
92 object_type = g_type_register_static (DIA_TYPE_DIAGRAM_DATA,
93 "Diagram",
94 &object_info, 0);
97 return object_type;
100 static void
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();
110 if (dia->undo)
111 undo_destroy(dia->undo);
112 dia->undo = NULL;
114 diagram_tree_remove(diagram_tree(), dia);
116 diagram_cleanup_autosave(dia);
118 if (dia->filename)
119 g_free(dia->filename);
120 dia->filename = NULL;
122 G_OBJECT_CLASS (parent_class)->finalize (object);
125 static void
126 _diagram_removed (Diagram* dia)
130 static void
131 _diagram_selection_changed (Diagram* dia, int n)
135 static void
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),
145 G_SIGNAL_RUN_FIRST,
146 G_STRUCT_OFFSET (DiagramClass, removed),
147 NULL, NULL,
148 dia_marshal_VOID__VOID,
149 G_TYPE_NONE, 0);
151 diagram_signals[SELECTION_CHANGED] =
152 g_signal_new ("selection_changed",
153 G_TYPE_FROM_CLASS (klass),
154 G_SIGNAL_RUN_FIRST,
155 G_STRUCT_OFFSET (DiagramClass, selection_changed),
156 NULL, NULL,
157 dia_marshal_VOID__INT,
158 G_TYPE_NONE, 1,
159 G_TYPE_INT);
161 klass->removed = _diagram_removed;
162 klass->selection_changed = _diagram_selection_changed;
164 object_class->finalize = diagram_finalize;
167 GList *
168 dia_open_diagrams(void)
170 return open_diagrams;
173 /** Initializes a diagram with standard info and sets it to be called
174 * 'filename'.
175 * Returns TRUE if everything went ok, FALSE otherwise.
176 * Will return FALSE if filename is not a legal string in the current
177 * encoding.
179 static gboolean
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);
212 g_free(pwd);
213 filename = newfilename;
215 /* All Diagram functions assumes filename in filesystem encoding */
217 dia->filename = g_filename_to_utf8(filename, -1, NULL, NULL, &error);
218 if (error != NULL) {
219 message_error(_("Couldn't convert filename '%s' to UTF-8: %s\n"),
220 dia_message_filename(filename), error->message);
221 g_error_free(error);
222 dia->filename = g_strdup(_("Error"));
223 return FALSE;
226 dia->unsaved = TRUE;
227 dia->mollified = FALSE;
228 dia->autosavefilename = NULL;
230 if (dia->undo)
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();
240 g_free(newfilename);
241 return TRUE;
245 diagram_load_into(Diagram *diagram,
246 const char *filename,
247 DiaImportFilter *ifilter)
249 if (!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)) {
255 return FALSE;
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);
264 return TRUE;
265 } else
266 return FALSE;
269 Diagram *
270 diagram_load(const char *filename, DiaImportFilter *ifilter)
272 Diagram *diagram;
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);
280 diagram = NULL;
283 return 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.
290 Diagram *
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)) {
296 return dia;
297 } else {
298 g_object_unref(dia);
299 return NULL;
303 void
304 diagram_destroy(Diagram *dia)
306 g_signal_emit (dia, diagram_signals[REMOVED], 0);
307 g_object_unref(dia);
310 /** Returns true if we consider the diagram modified.
312 gboolean
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.
321 void
322 diagram_modified(Diagram *dia)
324 GSList *displays;
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.
339 void
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.
355 static gboolean
356 diagram_selected_any_groups(Diagram *dia) {
357 GList *selected;
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;
364 return FALSE;
367 static gboolean
368 diagram_selected_any_parents(Diagram *dia) {
369 GList *selected;
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)
375 return TRUE;
377 return FALSE;
380 static gboolean
381 diagram_selected_any_children(Diagram *dia) {
382 GList *selected;
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;
389 return FALSE;
392 /** This is slightly more complex -- must see if any non-parented objects
393 * are within a parenting object.
395 static gboolean
396 diagram_selected_can_parent(Diagram *dia) {
397 GList *selected;
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) {
411 GList *parent_tmp;
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",
422 obj_bb.left,
423 obj_bb.top,
424 obj_bb.right,
425 obj_bb.bottom,
426 p->bounding_box.left,
427 p->bounding_box.top,
428 p->bounding_box.right,
429 p->bounding_box.bottom);
431 g_list_free(parents);
432 return TRUE;
437 g_list_free(parents);
438 return FALSE;
442 This is the real implementation of the sensitivity update.
443 TODO: move it to the DDisplay as it belongs to it IMHO
445 void
446 diagram_update_menu_sensitivity (Diagram *dia, UpdatableMenuItems *items)
448 gint selected_count = g_list_length (dia->data->selected);
449 /* Edit menu */
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);
460 /* Objects menu */
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);
502 initialized = 1;
505 diagram_update_menu_sensitivity (dia, &items);
508 void
509 diagram_add_ddisplay(Diagram *dia, DDisplay *ddisp)
511 dia->displays = g_slist_prepend(dia->displays, ddisp);
512 dia->display_count++;
515 void
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);
529 void
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);
539 void
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);
549 void
550 diagram_selected_break_external(Diagram *dia)
552 GList *list;
553 GList *connected_list;
554 DiaObject *obj;
555 DiaObject *other_obj;
556 int i,j;
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)) {
595 Change *change;
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);
614 void
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));
623 void
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));
632 void
633 diagram_unselect_objects(Diagram *dia, GList *obj_list)
635 GList *list;
636 DiaObject *obj;
638 /* otherwise we would signal objects step by step */
639 g_signal_handlers_block_by_func (dia, _diagram_selection_changed, NULL);
640 list = obj_list;
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.
660 void
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));
672 void
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;
698 void
699 diagram_redraw_all()
701 GList *list;
702 Diagram *dia;
704 list = open_diagrams;
706 while (list != NULL) {
707 dia = (Diagram *) list->data;
709 diagram_add_update_all(dia);
710 diagram_flush(dia);
712 list = g_list_next(list);
714 return;
717 void
718 diagram_add_update_all(Diagram *dia)
720 GSList *l;
721 DDisplay *ddisp;
723 l = dia->displays;
724 while (l!=NULL) {
725 ddisp = (DDisplay *) l->data;
727 ddisplay_add_update_all(ddisp);
729 l = g_slist_next(l);
733 void
734 diagram_add_update(Diagram *dia, Rectangle *update)
736 GSList *l;
737 DDisplay *ddisp;
739 l = dia->displays;
740 while (l!=NULL) {
741 ddisp = (DDisplay *) l->data;
743 ddisplay_add_update(ddisp, update);
745 l = g_slist_next(l);
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.
753 * */
754 void
755 diagram_add_update_with_border(Diagram *dia, Rectangle *update,
756 int pixel_border)
758 GSList *l;
759 DDisplay *ddisp;
761 l = dia->displays;
762 while (l!=NULL) {
763 ddisp = (DDisplay *) l->data;
765 ddisplay_add_update_with_border(ddisp, update, pixel_border);
767 l = g_slist_next(l);
771 void
772 diagram_add_update_pixels(Diagram *dia, Point *point,
773 int pixel_width, int pixel_height)
775 GSList *l;
776 DDisplay *ddisp;
778 l = dia->displays;
779 while (l!=NULL) {
780 ddisp = (DDisplay *) l->data;
782 ddisplay_add_update_pixels(ddisp, point, pixel_width, pixel_height);
784 l = g_slist_next(l);
788 void
789 diagram_flush(Diagram *dia)
791 GSList *l;
792 DDisplay *ddisp;
793 l = dia->displays;
794 while (l!=NULL) {
795 ddisp = (DDisplay *) l->data;
797 ddisplay_flush(ddisp);
799 l = g_slist_next(l);
801 dynobj_refresh_kick();
804 DiaObject *
805 diagram_find_clicked_object(Diagram *dia, Point *pos,
806 real maxdist)
808 return layer_find_closest_object_except(dia->data->active_layer,
809 pos, maxdist, NULL);
812 DiaObject *
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,
817 maxdist, avoid);
821 * Always returns the last handle in an object that has
822 * the closest distance
824 real
825 diagram_find_closest_handle(Diagram *dia, Handle **closest,
826 DiaObject **object, Point *pos)
828 GList *l;
829 DiaObject *obj;
830 Handle *handle;
831 real mindist, dist;
832 int i;
834 mindist = 1000000.0; /* Realy big value... */
836 *closest = NULL;
838 l = dia->data->selected;
839 while (l!=NULL) {
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);
846 if (dist<=mindist) {
847 mindist = dist;
848 *closest = handle;
849 *object = obj;
853 l = g_list_next(l);
856 return mindist;
859 real
860 diagram_find_closest_connectionpoint(Diagram *dia,
861 ConnectionPoint **closest,
862 Point *pos,
863 DiaObject *notthis)
865 real dist = 100000000.0;
866 int i;
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;
870 real this_dist;
871 if (layer->connectable) {
872 this_dist = layer_find_closest_connectionpoint(layer,
873 &this_cp, pos, notthis);
874 if (this_dist < dist) {
875 dist = this_dist;
876 *closest = this_cp;
880 return dist;
883 void
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: */
891 GSList *l;
892 DDisplay *ddisp;
894 l = dia->displays;
895 while (l!=NULL) {
896 ddisp = (DDisplay *) l->data;
898 ddisplay_update_scrollbars(ddisp);
900 l = g_slist_next(l);
902 if (cur_scale != dia->data->paper.scaling) {
903 diagram_add_update_all(dia);
904 diagram_flush(dia);
909 /* Remove connections from obj to objects outside created group. */
910 static void
911 strip_connections(DiaObject *obj, GList *not_strip_list, Diagram *dia)
913 int i;
914 Handle *handle;
915 Change *change;
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);
928 /* GCompareFunc */
929 static gint
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)
936 return 1;
937 else if ((*a)->extent.left > (*b)->extent.left)
938 return -1;
939 else
940 if ((*a)->extent.top < (*b)->extent.top)
941 return 1;
942 else if ((*a)->extent.top > (*b)->extent.top)
943 return -1;
944 else
945 return 0;
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);
955 int idx, idx2;
956 ObjectExtent *oe;
957 gboolean any_parented = FALSE;
958 GPtrArray *extents = g_ptr_array_sized_new(length);
959 while (list)
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)
974 continue;
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))
980 continue;
982 if (oe1->extent.right <= oe2->extent.right
983 && oe1->extent.bottom <= oe2->extent.bottom)
985 Change *change;
986 change = undo_parenting(dia, oe2->object, oe1->object, TRUE);
987 (change->apply)(change, dia);
988 any_parented = TRUE;
990 oe1->object->parent = oe2->object;
991 oe2->object->children = g_list_append(oe2->object->children, oe1->object);
993 break;
997 g_ptr_array_free(extents, TRUE);
998 if (any_parented) {
999 diagram_modified(dia);
1000 diagram_flush(dia);
1001 undo_set_transactionpoint(dia->undo);
1005 /** Remove all selected objects from their parents (if any). */
1006 void diagram_unparent_selected(Diagram *dia)
1008 GList *list;
1009 DiaObject *obj, *parent;
1010 Change *change;
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;
1018 if (!parent)
1019 continue;
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);
1026 obj->parent = NULL;
1029 if (any_unparented) {
1030 diagram_modified(dia);
1031 diagram_flush(dia);
1032 undo_set_transactionpoint(dia->undo);
1036 /** Remove all children from the selected parents. */
1037 void diagram_unparent_children_selected(Diagram *dia)
1039 GList *list;
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)
1046 continue;
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) {
1054 Change *change;
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);
1066 diagram_flush(dia);
1067 undo_set_transactionpoint(dia->undo);
1071 void diagram_group_selected(Diagram *dia)
1073 GList *list;
1074 GList *group_list;
1075 DiaObject *group;
1076 DiaObject *obj;
1077 GList *orig_list;
1078 Change *change;
1080 #if 0
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);
1085 #endif
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);
1093 list = group_list;
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);
1117 diagram_flush(dia);
1119 undo_set_transactionpoint(dia->undo);
1122 void diagram_ungroup_selected(Diagram *dia)
1124 DiaObject *group;
1125 GList *group_list;
1126 GList *selected, *selection_copy;
1127 int group_index;
1128 int any_groups = 0;
1130 if (g_list_length(dia->data->selected) < 1) {
1131 message_error("Trying to ungroup with no selected objects.");
1132 return;
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)) {
1141 Change *change;
1143 /* Fix selection */
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);
1154 any_groups = 1;
1156 selected = g_list_next(selected);
1158 g_list_free(selection_copy);
1160 if (any_groups) {
1161 diagram_modified(dia);
1162 diagram_flush(dia);
1163 undo_set_transactionpoint(dia->undo);
1167 GList *
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.
1176 GList *
1177 diagram_get_sorted_selected_remove(Diagram *dia)
1179 diagram_modified(dia);
1181 return data_get_sorted_selected_remove(dia->data);
1184 void
1185 diagram_place_under_selected(Diagram *dia)
1187 GList *sorted_list;
1188 GList *orig_list;
1190 if (g_list_length (dia->data->selected) == 0)
1191 return;
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);
1202 diagram_flush(dia);
1203 undo_set_transactionpoint(dia->undo);
1206 void
1207 diagram_place_over_selected(Diagram *dia)
1209 GList *sorted_list;
1210 GList *orig_list;
1212 if (g_list_length (dia->data->selected) == 0)
1213 return;
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);
1224 diagram_flush(dia);
1225 undo_set_transactionpoint(dia->undo);
1228 void
1229 diagram_place_up_selected(Diagram *dia)
1231 GList *sorted_list;
1232 GList *orig_list;
1233 GList *tmp, *stmp;
1234 GList *new_list = NULL;
1236 if (g_list_length (dia->data->selected) == 0)
1237 return;
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);
1248 tmp != NULL;
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);
1267 diagram_flush(dia);
1268 undo_set_transactionpoint(dia->undo);
1271 void
1272 diagram_place_down_selected(Diagram *dia)
1274 GList *sorted_list;
1275 GList *orig_list;
1276 GList *tmp, *stmp;
1277 GList *new_list = NULL;
1279 if (g_list_length (dia->data->selected) == 0)
1280 return;
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);
1287 /* Sanity check */
1288 g_assert(g_list_length (dia->data->selected) == g_list_length(sorted_list));
1290 new_list = g_list_copy(orig_list);
1291 tmp = new_list;
1292 stmp = sorted_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);
1314 diagram_flush(dia);
1315 undo_set_transactionpoint(dia->undo);
1318 void
1319 diagram_set_filename(Diagram *dia, const char *filename)
1321 GSList *l;
1322 DDisplay *ddisp;
1323 char *title;
1325 g_free(dia->filename);
1326 dia->filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
1328 title = diagram_get_name(dia);
1330 l = dia->displays;
1331 while (l!=NULL) {
1332 ddisp = (DDisplay *) l->data;
1334 ddisplay_set_title(ddisp, title);
1336 l = g_slist_next(l);
1339 g_free(title);
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.
1351 gchar *
1352 diagram_get_name(Diagram *dia)
1354 gchar *title = strrchr(dia->filename, G_DIR_SEPARATOR);
1355 if (title==NULL) {
1356 title = dia->filename;
1357 } else {
1358 title++;
1361 return g_strdup(title);
1364 int diagram_modified_exists(void)
1366 GList *list;
1367 Diagram *dia;
1369 list = open_diagrams;
1371 while (list != NULL) {
1372 dia = (Diagram *) list->data;
1374 if (diagram_is_modified(dia))
1375 return TRUE;
1377 list = g_list_next(list);
1379 return FALSE;
1382 void diagram_object_modified(Diagram *dia, DiaObject *object)
1384 diagram_tree_update_object(diagram_tree(), dia, object);