2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / app / diagram.c
blob6d8114e75dc23f92464c886e8f7cb4a12dcdf715
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"
46 #include "parent.h"
48 static GList *open_diagrams = NULL;
50 struct _ObjectExtent
52 DiaObject *object;
53 Rectangle extent;
56 typedef struct _ObjectExtent ObjectExtent;
58 static gint diagram_parent_sort_cb(gconstpointer a, gconstpointer b);
61 static void diagram_class_init (DiagramClass *klass);
62 static gboolean diagram_init(Diagram *obj, const char *filename);
63 static void diagram_update_for_filename(Diagram *dia);
65 enum {
66 SELECTION_CHANGED,
67 REMOVED,
68 LAST_SIGNAL
71 static guint diagram_signals[LAST_SIGNAL] = { 0, };
72 static gpointer parent_class = NULL;
74 GType
75 diagram_get_type (void)
77 static GType object_type = 0;
79 if (!object_type)
81 static const GTypeInfo object_info =
83 sizeof (DiagramClass),
84 (GBaseInitFunc) NULL,
85 (GBaseFinalizeFunc) NULL,
86 (GClassInitFunc) diagram_class_init,
87 NULL, /* class_finalize */
88 NULL, /* class_data */
89 sizeof (Diagram),
90 0, /* n_preallocs */
91 NULL /* init */
94 object_type = g_type_register_static (DIA_TYPE_DIAGRAM_DATA,
95 "Diagram",
96 &object_info, 0);
99 return object_type;
102 static void
103 diagram_finalize(GObject *object)
105 Diagram *dia = DIA_DIAGRAM(object);
107 assert(dia->displays==NULL);
109 open_diagrams = g_list_remove(open_diagrams, dia);
110 layer_dialog_update_diagram_list();
112 if (dia->undo)
113 undo_destroy(dia->undo);
114 dia->undo = NULL;
116 diagram_tree_remove(diagram_tree(), dia);
118 diagram_cleanup_autosave(dia);
120 if (dia->filename)
121 g_free(dia->filename);
122 dia->filename = NULL;
124 G_OBJECT_CLASS (parent_class)->finalize (object);
127 static void
128 _diagram_removed (Diagram* dia)
132 static void
133 _diagram_selection_changed (Diagram* dia, int n)
137 static void
138 diagram_class_init (DiagramClass *klass)
140 GObjectClass *object_class = G_OBJECT_CLASS (klass);
142 parent_class = g_type_class_peek_parent (klass);
144 diagram_signals[REMOVED] =
145 g_signal_new ("removed",
146 G_TYPE_FROM_CLASS (klass),
147 G_SIGNAL_RUN_FIRST,
148 G_STRUCT_OFFSET (DiagramClass, removed),
149 NULL, NULL,
150 dia_marshal_VOID__VOID,
151 G_TYPE_NONE, 0);
153 diagram_signals[SELECTION_CHANGED] =
154 g_signal_new ("selection_changed",
155 G_TYPE_FROM_CLASS (klass),
156 G_SIGNAL_RUN_FIRST,
157 G_STRUCT_OFFSET (DiagramClass, selection_changed),
158 NULL, NULL,
159 dia_marshal_VOID__INT,
160 G_TYPE_NONE, 1,
161 G_TYPE_INT);
163 klass->removed = _diagram_removed;
164 klass->selection_changed = _diagram_selection_changed;
166 object_class->finalize = diagram_finalize;
169 GList *
170 dia_open_diagrams(void)
172 return open_diagrams;
175 /** Initializes a diagram with standard info and sets it to be called
176 * 'filename'.
177 * Returns TRUE if everything went ok, FALSE otherwise.
178 * Will return FALSE if filename is not a legal string in the current
179 * encoding.
181 static gboolean
182 diagram_init(Diagram *dia, const char *filename)
184 gchar *newfilename = NULL;
185 GError *error = NULL;
187 dia->data = &dia->parent_instance; /* compatibility */
189 dia->pagebreak_color = prefs.new_diagram.pagebreak_color;
191 dia->grid.width_x = prefs.grid.x;
192 dia->grid.width_y = prefs.grid.y;
193 dia->grid.width_w = prefs.grid.w;
194 dia->grid.hex_size = 1.0;
195 dia->grid.colour = prefs.new_diagram.grid_color;
196 dia->grid.hex = prefs.grid.hex;
197 dia->grid.visible_x = 1;
198 dia->grid.visible_y = 1;
199 dia->grid.dynamic = prefs.grid.dynamic;
200 dia->grid.major_lines = prefs.grid.major_lines;
202 dia->guides.nhguides = 0;
203 dia->guides.hguides = NULL;
204 dia->guides.nvguides = 0;
205 dia->guides.vguides = NULL;
207 if (dia->filename != NULL)
208 g_free(dia->filename);
209 /* Make sure the filename is absolute */
210 if (!g_path_is_absolute(filename)) {
211 gchar *pwd = g_get_current_dir();
213 newfilename = g_build_filename(pwd, filename, NULL);
214 g_free(pwd);
215 filename = newfilename;
217 /* All Diagram functions assumes filename in filesystem encoding */
219 dia->filename = g_filename_to_utf8(filename, -1, NULL, NULL, &error);
220 if (error != NULL) {
221 message_error(_("Couldn't convert filename '%s' to UTF-8: %s\n"),
222 dia_message_filename(filename), error->message);
223 g_error_free(error);
224 dia->filename = g_strdup(_("Error"));
225 return FALSE;
228 dia->unsaved = TRUE;
229 dia->mollified = FALSE;
230 dia->autosavefilename = NULL;
232 if (dia->undo)
233 undo_destroy(dia->undo);
234 dia->undo = new_undo_stack(dia);
236 if (!g_list_find(open_diagrams, dia))
237 open_diagrams = g_list_prepend(open_diagrams, dia);
239 if (app_is_interactive())
240 layer_dialog_update_diagram_list();
242 g_free(newfilename);
243 return TRUE;
247 diagram_load_into(Diagram *diagram,
248 const char *filename,
249 DiaImportFilter *ifilter)
251 if (!ifilter)
252 ifilter = filter_guess_import_filter(filename);
253 if (!ifilter) /* default to native format */
254 ifilter = &dia_import_filter;
256 if (!diagram_init(diagram, filename)) {
257 return FALSE;
260 if (ifilter->import_func(filename, diagram->data, ifilter->user_data)) {
261 diagram->unsaved = FALSE;
262 diagram_set_modified(diagram, FALSE);
263 if (app_is_interactive())
264 recent_file_history_add(filename);
265 diagram_tree_add(diagram_tree(), diagram);
266 return TRUE;
267 } else
268 return FALSE;
271 Diagram *
272 diagram_load(const char *filename, DiaImportFilter *ifilter)
274 Diagram *diagram = NULL;
275 GList *diagrams;
277 for (diagrams = open_diagrams; diagrams != NULL; diagrams = g_list_next(diagrams)) {
278 Diagram *old_diagram = (Diagram*)diagrams->data;
279 if (old_diagram->is_default) {
280 diagram = old_diagram;
281 break;
285 /* TODO: Make diagram not be initialized twice */
286 if (diagram == NULL) {
287 diagram = new_diagram(filename);
289 if (diagram == NULL) return NULL;
291 if (!diagram_load_into (diagram, filename, ifilter)) {
292 diagram_destroy(diagram);
293 diagram = NULL;
295 if (diagram != NULL && diagram->is_default) {
296 diagram_update_for_filename(diagram);
297 diagram->is_default = FALSE;
300 return diagram;
303 /** Create a new diagram with the given filename.
304 * If the diagram could not be created, e.g. because the filename is not
305 * a legal string in the current encoding, return NULL.
307 Diagram *
308 new_diagram(const char *filename) /* Note: filename is copied */
310 Diagram *dia = g_object_new(DIA_TYPE_DIAGRAM, NULL);
312 if (diagram_init(dia, filename)) {
313 return dia;
314 } else {
315 g_object_unref(dia);
316 return NULL;
320 void
321 diagram_destroy(Diagram *dia)
323 g_signal_emit (dia, diagram_signals[REMOVED], 0);
324 g_object_unref(dia);
327 /** Returns true if we consider the diagram modified.
329 gboolean
330 diagram_is_modified(Diagram *dia)
332 return dia->mollified || !undo_is_saved(dia->undo);
335 /** We might just have change the diagrams modified status.
336 * This doesn't set the status, but merely updates the display.
338 void
339 diagram_modified(Diagram *dia)
341 GSList *displays;
342 displays = dia->displays;
343 while (displays != NULL) {
344 DDisplay *display = (DDisplay*) displays->data;
345 ddisplay_update_statusbar(display);
346 displays = g_slist_next(displays);
348 if (diagram_is_modified(dia)) {
349 dia->autosaved = FALSE;
350 dia->is_default = FALSE;
352 /* diagram_set_modified(dia, TRUE);*/
355 /** Set this diagram explicitly modified. This should not be called
356 * by things that change the undo stack, as those modifications are
357 * noticed through changes in the undo stack.
359 void
360 diagram_set_modified(Diagram *dia, int modified)
362 if (dia->mollified != modified)
364 dia->mollified = modified;
366 diagram_modified(dia);
369 /* ************ Functions that check for menu sensitivity ********* */
371 /* Suggested optimization: The loops take a lot of the time. Collapse them
372 * into one, have them set flags for the things that have been found true.
373 * Harder to maintain.
375 static gboolean
376 diagram_selected_any_groups(Diagram *dia) {
377 GList *selected;
379 for (selected = dia->data->selected;
380 selected != NULL; selected = selected->next) {
381 DiaObject *obj = (DiaObject*)selected->data;
382 if (IS_GROUP(obj)) return TRUE;
384 return FALSE;
387 static gboolean
388 diagram_selected_any_parents(Diagram *dia) {
389 GList *selected;
391 for (selected = dia->data->selected;
392 selected != NULL; selected = selected->next) {
393 DiaObject *obj = (DiaObject*)selected->data;
394 if (object_flags_set(obj, DIA_OBJECT_CAN_PARENT) && obj->children != NULL)
395 return TRUE;
397 return FALSE;
400 static gboolean
401 diagram_selected_any_children(Diagram *dia) {
402 GList *selected;
404 for (selected = dia->data->selected;
405 selected != NULL; selected = selected->next) {
406 DiaObject *obj = (DiaObject*)selected->data;
407 if (obj->parent != NULL) return TRUE;
409 return FALSE;
412 /** This is slightly more complex -- must see if any non-parented objects
413 * are within a parenting object.
415 static gboolean
416 diagram_selected_can_parent(Diagram *dia) {
417 GList *selected;
418 GList *parents = NULL;
420 for (selected = dia->data->selected;
421 selected != NULL; selected = selected->next) {
422 DiaObject *obj = (DiaObject*)selected->data;
423 if (object_flags_set(obj, DIA_OBJECT_CAN_PARENT)) {
424 parents = g_list_prepend(parents, obj);
427 for (selected = dia->data->selected;
428 selected != NULL; selected = selected->next) {
429 DiaObject *obj = (DiaObject*)selected->data;
430 if (obj->parent == NULL) {
431 GList *parent_tmp;
432 Rectangle obj_bb = obj->bounding_box;
433 for (parent_tmp = parents; parent_tmp != NULL; parent_tmp = parent_tmp->next) {
434 DiaObject *p = (DiaObject*)parent_tmp->data;
435 if (p == obj) continue;
436 if (obj_bb.left > p->bounding_box.left &&
437 obj_bb.right < p->bounding_box.right &&
438 obj_bb.top > p->bounding_box.top &&
439 obj_bb.bottom < p->bounding_box.bottom) {
441 printf("Obj %f, %f x %f, %f inside %f, %f x %f, %f\n",
442 obj_bb.left,
443 obj_bb.top,
444 obj_bb.right,
445 obj_bb.bottom,
446 p->bounding_box.left,
447 p->bounding_box.top,
448 p->bounding_box.right,
449 p->bounding_box.bottom);
451 g_list_free(parents);
452 return TRUE;
457 g_list_free(parents);
458 return FALSE;
462 This is the real implementation of the sensitivity update.
463 TODO: move it to the DDisplay as it belongs to it IMHO
465 void
466 diagram_update_menu_sensitivity (Diagram *dia, UpdatableMenuItems *items)
468 gint selected_count = g_list_length (dia->data->selected);
469 /* Edit menu */
470 gtk_action_set_sensitive (items->copy, selected_count > 0);
471 gtk_action_set_sensitive (items->cut, selected_count > 0);
472 gtk_action_set_sensitive (items->paste, cnp_exist_stored_objects());
473 gtk_action_set_sensitive (items->edit_delete, selected_count > 0);
474 gtk_action_set_sensitive (items->edit_duplicate, selected_count > 0);
476 gtk_action_set_sensitive (items->copy_text, active_focus() != NULL);
477 gtk_action_set_sensitive (items->cut_text, active_focus() != NULL);
478 gtk_action_set_sensitive (items->paste_text, active_focus() != NULL);
480 /* Objects menu */
481 gtk_action_set_sensitive (items->send_to_back, selected_count > 0);
482 gtk_action_set_sensitive (items->bring_to_front, selected_count > 0);
483 gtk_action_set_sensitive (items->send_backwards, selected_count > 0);
484 gtk_action_set_sensitive (items->bring_forwards, selected_count > 0);
486 gtk_action_set_sensitive (items->parent, diagram_selected_can_parent (dia));
487 gtk_action_set_sensitive (items->unparent,
488 diagram_selected_any_children (dia));
489 gtk_action_set_sensitive (items->unparent_children,
490 diagram_selected_any_parents (dia));
491 gtk_action_set_sensitive (items->group, selected_count > 1);
492 gtk_action_set_sensitive (items->ungroup, diagram_selected_any_groups (dia));
493 gtk_action_set_sensitive (items->properties, selected_count > 0);
495 /* Objects->Align menu */
496 gtk_action_set_sensitive (items->align_h_l, selected_count > 1);
497 gtk_action_set_sensitive (items->align_h_c, selected_count > 1);
498 gtk_action_set_sensitive (items->align_h_r, selected_count > 1);
499 gtk_action_set_sensitive (items->align_h_e, selected_count > 1);
500 gtk_action_set_sensitive (items->align_h_a, selected_count > 1);
501 gtk_action_set_sensitive (items->align_v_t, selected_count > 1);
502 gtk_action_set_sensitive (items->align_v_c, selected_count > 1);
503 gtk_action_set_sensitive (items->align_v_b, selected_count > 1);
504 gtk_action_set_sensitive (items->align_v_e, selected_count > 1);
505 gtk_action_set_sensitive (items->align_v_a, selected_count > 1);
509 void diagram_update_menubar_sensitivity(Diagram *dia, UpdatableMenuItems *items)
511 diagram_update_menu_sensitivity (dia, items);
515 void diagram_update_popupmenu_sensitivity(Diagram *dia)
517 static int initialized = 0;
518 static UpdatableMenuItems items;
520 if (initialized==0) {
521 menus_initialize_updatable_items (&items, NULL);
522 initialized = 1;
525 diagram_update_menu_sensitivity (dia, &items);
528 void
529 diagram_add_ddisplay(Diagram *dia, DDisplay *ddisp)
531 dia->displays = g_slist_prepend(dia->displays, ddisp);
532 dia->display_count++;
535 void
536 diagram_remove_ddisplay(Diagram *dia, DDisplay *ddisp)
538 dia->displays = g_slist_remove(dia->displays, ddisp);
539 dia->display_count--;
541 if (dia->display_count == 0) {
542 if (!app_is_embedded()) {
543 /* Don't delete embedded diagram when last view is closed */
544 diagram_destroy(dia);
549 void
550 diagram_add_object(Diagram *dia, DiaObject *obj)
552 layer_add_object(dia->data->active_layer, obj);
554 diagram_modified(dia);
556 diagram_tree_add_object(diagram_tree(), dia, obj);
559 void
560 diagram_add_object_list(Diagram *dia, GList *list)
562 layer_add_objects(dia->data->active_layer, list);
564 diagram_modified(dia);
566 diagram_tree_add_objects(diagram_tree(), dia, list);
569 void
570 diagram_selected_break_external(Diagram *dia)
572 GList *list;
573 GList *connected_list;
574 DiaObject *obj;
575 DiaObject *other_obj;
576 int i,j;
578 list = dia->data->selected;
579 while (list != NULL) {
580 obj = (DiaObject *)list->data;
582 /* Break connections between this object and objects not selected: */
583 for (i=0;i<obj->num_handles;i++) {
584 ConnectionPoint *con_point;
585 con_point = obj->handles[i]->connected_to;
587 if ( con_point == NULL )
588 break; /* Not connected */
590 other_obj = con_point->object;
591 if (g_list_find(dia->data->selected, other_obj) == NULL) {
592 /* other_obj is not selected, break connection */
593 Change *change = undo_unconnect(dia, obj, obj->handles[i]);
594 (change->apply)(change, dia);
595 object_add_updates(obj, dia);
599 /* Break connections from non selected objects to this object: */
600 for (i=0;i<obj->num_connections;i++) {
601 connected_list = obj->connections[i]->connected;
603 while (connected_list != NULL) {
604 other_obj = (DiaObject *)connected_list->data;
606 if (g_list_find(dia->data->selected, other_obj) == NULL) {
607 /* other_obj is not in list, break all connections
608 to obj from other_obj */
610 for (j=0;j<other_obj->num_handles;j++) {
611 ConnectionPoint *con_point;
612 con_point = other_obj->handles[j]->connected_to;
614 if (con_point && (con_point->object == obj)) {
615 Change *change;
616 connected_list = g_list_previous(connected_list);
617 change = undo_unconnect(dia, other_obj,
618 other_obj->handles[j]);
619 (change->apply)(change, dia);
620 if (connected_list == NULL)
621 connected_list = obj->connections[i]->connected;
626 connected_list = g_list_next(connected_list);
629 diagram_tree_remove_object(diagram_tree(), obj);
630 list = g_list_next(list);
634 void
635 diagram_remove_all_selected(Diagram *diagram, int delete_empty)
637 object_add_updates_list(diagram->data->selected, diagram);
638 textedit_remove_focus_all(diagram);
639 data_remove_all_selected(diagram->data);
640 g_signal_emit (diagram, diagram_signals[SELECTION_CHANGED], 0, g_list_length (diagram->data->selected));
643 void
644 diagram_unselect_object(Diagram *diagram, DiaObject *obj)
646 object_add_updates(obj, diagram);
647 textedit_remove_focus(obj, diagram);
648 data_unselect(DIA_DIAGRAM_DATA(diagram), obj);
649 g_signal_emit (diagram, diagram_signals[SELECTION_CHANGED], 0,
650 g_list_length (DIA_DIAGRAM_DATA(diagram)->selected));
653 void
654 diagram_unselect_objects(Diagram *dia, GList *obj_list)
656 GList *list;
657 DiaObject *obj;
659 /* otherwise we would signal objects step by step */
660 g_signal_handlers_block_by_func (dia, _diagram_selection_changed, NULL);
661 list = obj_list;
662 while (list != NULL) {
663 obj = (DiaObject *) list->data;
665 if (g_list_find(dia->data->selected, obj) != NULL){
666 diagram_unselect_object(dia, obj);
669 list = g_list_next(list);
671 g_signal_handlers_unblock_by_func (dia, _diagram_selection_changed, NULL);
672 g_signal_emit (dia, diagram_signals[SELECTION_CHANGED], 0, g_list_length (dia->data->selected));
675 /** Make a single object selected.
676 * Note that an object inside a closed group cannot be made selected, nor
677 * can an object in a non-active layer.
678 * @param diagram The diagram that the object belongs to (sorta redundant now)
679 * @param obj The object that should be made selected.
681 void
682 diagram_select(Diagram *diagram, DiaObject *obj)
684 if (dia_object_is_selectable(obj)) {
685 data_select(diagram->data, obj);
686 obj->ops->selectf(obj, NULL, NULL);
687 object_add_updates(obj, diagram);
688 g_signal_emit (diagram, diagram_signals[SELECTION_CHANGED], 0,
689 g_list_length (diagram->data->selected));
693 void
694 diagram_select_list(Diagram *dia, GList *list)
696 g_return_if_fail (dia && list);
697 /* otherwise we would signal objects step by step */
698 g_signal_handlers_block_by_func (dia, _diagram_selection_changed, NULL);
699 while (list != NULL) {
700 DiaObject *obj = (DiaObject *)list->data;
702 diagram_select(dia, obj);
704 list = g_list_next(list);
706 if (active_focus() == NULL) {
707 textedit_activate_first(ddisplay_active());
709 g_signal_handlers_unblock_by_func (dia, _diagram_selection_changed, NULL);
710 g_signal_emit (dia, diagram_signals[SELECTION_CHANGED], 0, g_list_length (dia->data->selected));
714 diagram_is_selected(Diagram *diagram, DiaObject *obj)
716 return g_list_find(diagram->data->selected, obj) != NULL;
719 void
720 diagram_redraw_all()
722 GList *list;
723 Diagram *dia;
725 list = open_diagrams;
727 while (list != NULL) {
728 dia = (Diagram *) list->data;
730 diagram_add_update_all(dia);
731 diagram_flush(dia);
733 list = g_list_next(list);
735 return;
738 void
739 diagram_add_update_all(Diagram *dia)
741 GSList *l;
742 DDisplay *ddisp;
744 l = dia->displays;
745 while (l!=NULL) {
746 ddisp = (DDisplay *) l->data;
748 ddisplay_add_update_all(ddisp);
750 l = g_slist_next(l);
754 void
755 diagram_add_update(Diagram *dia, Rectangle *update)
757 GSList *l;
758 DDisplay *ddisp;
760 l = dia->displays;
761 while (l!=NULL) {
762 ddisp = (DDisplay *) l->data;
764 ddisplay_add_update(ddisp, update);
766 l = g_slist_next(l);
770 /** Add an update of the given rectangle, but with an additional
771 * border around it. The pixels are added after the rectangle has
772 * been converted to pixel coords.
773 * Currently used for leaving room for highlighting.
774 * */
775 void
776 diagram_add_update_with_border(Diagram *dia, Rectangle *update,
777 int pixel_border)
779 GSList *l;
780 DDisplay *ddisp;
782 l = dia->displays;
783 while (l!=NULL) {
784 ddisp = (DDisplay *) l->data;
786 ddisplay_add_update_with_border(ddisp, update, pixel_border);
788 l = g_slist_next(l);
792 void
793 diagram_add_update_pixels(Diagram *dia, Point *point,
794 int pixel_width, int pixel_height)
796 GSList *l;
797 DDisplay *ddisp;
799 l = dia->displays;
800 while (l!=NULL) {
801 ddisp = (DDisplay *) l->data;
803 ddisplay_add_update_pixels(ddisp, point, pixel_width, pixel_height);
805 l = g_slist_next(l);
809 void
810 diagram_flush(Diagram *dia)
812 GSList *l;
813 DDisplay *ddisp;
814 l = dia->displays;
815 while (l!=NULL) {
816 ddisp = (DDisplay *) l->data;
818 ddisplay_flush(ddisp);
820 l = g_slist_next(l);
822 dynobj_refresh_kick();
825 DiaObject *
826 diagram_find_clicked_object(Diagram *dia, Point *pos,
827 real maxdist)
829 return layer_find_closest_object_except(dia->data->active_layer,
830 pos, maxdist, NULL);
833 DiaObject *
834 diagram_find_clicked_object_except(Diagram *dia, Point *pos,
835 real maxdist, GList *avoid)
837 return layer_find_closest_object_except(dia->data->active_layer, pos,
838 maxdist, avoid);
842 * Always returns the last handle in an object that has
843 * the closest distance
845 real
846 diagram_find_closest_handle(Diagram *dia, Handle **closest,
847 DiaObject **object, Point *pos)
849 GList *l;
850 DiaObject *obj;
851 Handle *handle;
852 real mindist, dist;
853 int i;
855 mindist = 1000000.0; /* Realy big value... */
857 *closest = NULL;
859 l = dia->data->selected;
860 while (l!=NULL) {
861 obj = (DiaObject *) l->data;
863 for (i=0;i<obj->num_handles;i++) {
864 handle = obj->handles[i];
865 /* Note: Uses manhattan metric for speed... */
866 dist = distance_point_point_manhattan(pos, &handle->pos);
867 if (dist<=mindist) {
868 mindist = dist;
869 *closest = handle;
870 *object = obj;
874 l = g_list_next(l);
877 return mindist;
880 real
881 diagram_find_closest_connectionpoint(Diagram *dia,
882 ConnectionPoint **closest,
883 Point *pos,
884 DiaObject *notthis)
886 real dist = 100000000.0;
887 int i;
888 for (i=0;i<dia->data->layers->len;i++) {
889 Layer *layer = (Layer*)g_ptr_array_index(dia->data->layers, i);
890 ConnectionPoint *this_cp;
891 real this_dist;
892 if (layer->connectable) {
893 this_dist = layer_find_closest_connectionpoint(layer,
894 &this_cp, pos, notthis);
895 if (this_dist < dist) {
896 dist = this_dist;
897 *closest = this_cp;
901 return dist;
904 void
905 diagram_update_extents(Diagram *dia)
907 gfloat cur_scale = dia->data->paper.scaling;
908 /* anropar update_scrollbars() */
910 if (data_update_extents(dia->data)) {
911 /* Update scrollbars because extents were changed: */
912 GSList *l;
913 DDisplay *ddisp;
915 l = dia->displays;
916 while (l!=NULL) {
917 ddisp = (DDisplay *) l->data;
919 ddisplay_update_scrollbars(ddisp);
921 l = g_slist_next(l);
923 if (cur_scale != dia->data->paper.scaling) {
924 diagram_add_update_all(dia);
925 diagram_flush(dia);
930 /* Remove connections from obj to objects outside created group. */
931 static void
932 strip_connections(DiaObject *obj, GList *not_strip_list, Diagram *dia)
934 int i;
935 Handle *handle;
936 Change *change;
938 for (i=0;i<obj->num_handles;i++) {
939 handle = obj->handles[i];
940 if ((handle->connected_to != NULL) &&
941 (g_list_find(not_strip_list, handle->connected_to->object)==NULL)) {
942 change = undo_unconnect(dia, obj, handle);
943 (change->apply)(change, dia);
949 /* GCompareFunc */
950 static gint
951 diagram_parent_sort_cb(gconstpointer _a, gconstpointer _b)
953 ObjectExtent **a = (ObjectExtent **)_a;
954 ObjectExtent **b = (ObjectExtent **)_b;
956 if ((*a)->extent.left < (*b)->extent.left)
957 return 1;
958 else if ((*a)->extent.left > (*b)->extent.left)
959 return -1;
960 else
961 if ((*a)->extent.top < (*b)->extent.top)
962 return 1;
963 else if ((*a)->extent.top > (*b)->extent.top)
964 return -1;
965 else
966 return 0;
970 /* needs faster algorithm -- if we find that parenting is slow.
971 * If it works, don't optimize it until it's a hotspot. */
972 void diagram_parent_selected(Diagram *dia)
974 GList *list = dia->data->selected;
975 int length = g_list_length(list);
976 int idx, idx2;
977 ObjectExtent *oe;
978 gboolean any_parented = FALSE;
979 GPtrArray *extents = g_ptr_array_sized_new(length);
980 while (list)
982 oe = g_new(ObjectExtent, 1);
983 oe->object = list->data;
984 parent_handle_extents(list->data, &oe->extent);
985 g_ptr_array_add(extents, oe);
986 list = g_list_next(list);
988 /* sort all the objects by their left position */
989 g_ptr_array_sort(extents, diagram_parent_sort_cb);
991 for (idx = 0; idx < length; idx++)
993 ObjectExtent *oe1 = g_ptr_array_index(extents, idx);
994 if (oe1->object->parent)
995 continue;
997 for (idx2 = idx + 1; idx2 < length; idx2++)
999 ObjectExtent *oe2 = g_ptr_array_index(extents, idx2);
1000 if (!object_flags_set(oe2->object, DIA_OBJECT_CAN_PARENT))
1001 continue;
1003 if (oe1->extent.right <= oe2->extent.right
1004 && oe1->extent.bottom <= oe2->extent.bottom)
1006 Change *change;
1007 change = undo_parenting(dia, oe2->object, oe1->object, TRUE);
1008 (change->apply)(change, dia);
1009 any_parented = TRUE;
1011 oe1->object->parent = oe2->object;
1012 oe2->object->children = g_list_append(oe2->object->children, oe1->object);
1014 break;
1018 g_ptr_array_free(extents, TRUE);
1019 if (any_parented) {
1020 diagram_modified(dia);
1021 diagram_flush(dia);
1022 undo_set_transactionpoint(dia->undo);
1026 /** Remove all selected objects from their parents (if any). */
1027 void diagram_unparent_selected(Diagram *dia)
1029 GList *list;
1030 DiaObject *obj, *parent;
1031 Change *change;
1032 gboolean any_unparented = FALSE;
1034 for (list = dia->data->selected; list != NULL; list = g_list_next(list))
1036 obj = (DiaObject *) list->data;
1037 parent = obj->parent;
1039 if (!parent)
1040 continue;
1042 change = undo_parenting(dia, parent, obj, FALSE);
1043 (change->apply)(change, dia);
1044 any_unparented = TRUE;
1046 parent->children = g_list_remove(parent->children, obj);
1047 obj->parent = NULL;
1050 if (any_unparented) {
1051 diagram_modified(dia);
1052 diagram_flush(dia);
1053 undo_set_transactionpoint(dia->undo);
1057 /** Remove all children from the selected parents. */
1058 void diagram_unparent_children_selected(Diagram *dia)
1060 GList *list;
1061 DiaObject *obj, *child;
1062 gboolean any_unparented = FALSE;
1063 for (list = dia->data->selected; list != NULL; list = g_list_next(list))
1065 obj = (DiaObject *) list->data;
1066 if (!object_flags_set(obj, DIA_OBJECT_CAN_PARENT) || !obj->children)
1067 continue;
1069 any_unparented = TRUE;
1070 /* Yes, this creates a whole bunch of Changes. They're lightweight
1071 * structures, though, and it's easier to assure correctness this
1072 * way. If needed, we can make a parent undo with a list of children.
1074 while (obj->children != NULL) {
1075 Change *change;
1076 child = (DiaObject *) obj->children->data;
1077 change = undo_parenting(dia, obj, child, FALSE);
1078 /* This will remove one item from the list, so the while terminates. */
1079 (change->apply)(change, dia);
1081 if (obj->children != NULL)
1082 printf("Obj still has %d children\n",
1083 g_list_length(obj->children));
1085 if (any_unparented) {
1086 diagram_modified(dia);
1087 diagram_flush(dia);
1088 undo_set_transactionpoint(dia->undo);
1092 void diagram_group_selected(Diagram *dia)
1094 GList *list;
1095 GList *group_list;
1096 DiaObject *group;
1097 DiaObject *obj;
1098 GList *orig_list;
1099 Change *change;
1101 #if 0
1102 /* the following is wrong as it screws up the selected list, see bug #153525
1103 * I just don't get what was originally intented so please speak up if you know --hb
1105 dia->data->selected = parent_list_affected(dia->data->selected);
1106 #endif
1108 orig_list = g_list_copy(dia->data->active_layer->objects);
1110 /* We have to rebuild the selection list so that it is the same
1111 order as in the Diagram list. */
1112 group_list = diagram_get_sorted_selected_remove(dia);
1114 list = group_list;
1115 while (list != NULL) {
1116 obj = (DiaObject *)list->data;
1118 /* Remove connections from obj to objects outside created group. */
1119 /* strip_connections sets up its own undo info. */
1120 /* The connections aren't reattached by ungroup. */
1121 strip_connections(obj, dia->data->selected, dia);
1123 list = g_list_next(list);
1126 /* Remove list of selected objects */
1127 textedit_remove_focus_all(dia);
1128 data_remove_all_selected(dia->data);
1130 group = group_create(group_list);
1131 change = undo_group_objects(dia, group_list, group, orig_list);
1132 (change->apply)(change, dia);
1134 /* Select the created group */
1135 diagram_select(dia, group);
1137 diagram_modified(dia);
1138 diagram_flush(dia);
1140 undo_set_transactionpoint(dia->undo);
1143 void diagram_ungroup_selected(Diagram *dia)
1145 DiaObject *group;
1146 GList *group_list;
1147 GList *selected, *selection_copy;
1148 int group_index;
1149 int any_groups = 0;
1151 if (g_list_length(dia->data->selected) < 1) {
1152 message_error("Trying to ungroup with no selected objects.");
1153 return;
1156 selection_copy = g_list_copy(dia->data->selected);
1157 selected = selection_copy;
1158 while (selected != NULL) {
1159 group = (DiaObject *)selected->data;
1161 if (IS_GROUP(group)) {
1162 Change *change;
1164 /* Fix selection */
1165 diagram_unselect_object(dia, group);
1167 group_list = group_objects(group);
1168 diagram_select_list(dia, group_list);
1170 group_index = layer_object_index(dia->data->active_layer, group);
1172 change = undo_ungroup_objects(dia, group_list, group, group_index);
1173 (change->apply)(change, dia);
1175 any_groups = 1;
1177 selected = g_list_next(selected);
1179 g_list_free(selection_copy);
1181 if (any_groups) {
1182 diagram_modified(dia);
1183 diagram_flush(dia);
1184 undo_set_transactionpoint(dia->undo);
1188 GList *
1189 diagram_get_sorted_selected(Diagram *dia)
1191 return data_get_sorted_selected(dia->data);
1194 /** Remove the currently selected objects from the diagram's object list.
1195 * Returns a newly created list of the selected objects, in order.
1197 GList *
1198 diagram_get_sorted_selected_remove(Diagram *dia)
1200 diagram_modified(dia);
1202 return data_get_sorted_selected_remove(dia->data);
1205 void
1206 diagram_place_under_selected(Diagram *dia)
1208 GList *sorted_list;
1209 GList *orig_list;
1211 if (g_list_length (dia->data->selected) == 0)
1212 return;
1214 orig_list = g_list_copy(dia->data->active_layer->objects);
1216 sorted_list = diagram_get_sorted_selected_remove(dia);
1217 object_add_updates_list(sorted_list, dia);
1218 layer_add_objects_first(dia->data->active_layer, sorted_list);
1220 undo_reorder_objects(dia, g_list_copy(sorted_list), orig_list);
1222 diagram_modified(dia);
1223 diagram_flush(dia);
1224 undo_set_transactionpoint(dia->undo);
1227 void
1228 diagram_place_over_selected(Diagram *dia)
1230 GList *sorted_list;
1231 GList *orig_list;
1233 if (g_list_length (dia->data->selected) == 0)
1234 return;
1236 orig_list = g_list_copy(dia->data->active_layer->objects);
1238 sorted_list = diagram_get_sorted_selected_remove(dia);
1239 object_add_updates_list(sorted_list, dia);
1240 layer_add_objects(dia->data->active_layer, sorted_list);
1242 undo_reorder_objects(dia, g_list_copy(sorted_list), orig_list);
1244 diagram_modified(dia);
1245 diagram_flush(dia);
1246 undo_set_transactionpoint(dia->undo);
1249 void
1250 diagram_place_up_selected(Diagram *dia)
1252 GList *sorted_list;
1253 GList *orig_list;
1254 GList *tmp, *stmp;
1255 GList *new_list = NULL;
1257 if (g_list_length (dia->data->selected) == 0)
1258 return;
1260 orig_list = g_list_copy(dia->data->active_layer->objects);
1262 sorted_list = diagram_get_sorted_selected(dia);
1263 object_add_updates_list(orig_list, dia);
1265 new_list = g_list_copy(orig_list);
1266 stmp = g_list_last(sorted_list);
1268 for (tmp = g_list_last(new_list);
1269 tmp != NULL;
1270 tmp = g_list_previous(tmp)) {
1271 if (stmp == NULL) break;
1272 if (tmp->prev == NULL) break;
1273 if (tmp->data == stmp->data) {
1274 stmp = g_list_previous(stmp);
1275 } else if (tmp->prev->data == stmp->data) {
1276 void *swap = tmp->data;
1277 tmp->data = tmp->prev->data;
1278 tmp->prev->data = swap;
1279 stmp = g_list_previous(stmp);
1283 layer_set_object_list(dia->data->active_layer, new_list);
1285 undo_reorder_objects(dia, g_list_copy(sorted_list), orig_list);
1287 diagram_modified(dia);
1288 diagram_flush(dia);
1289 undo_set_transactionpoint(dia->undo);
1292 void
1293 diagram_place_down_selected(Diagram *dia)
1295 GList *sorted_list;
1296 GList *orig_list;
1297 GList *tmp, *stmp;
1298 GList *new_list = NULL;
1300 if (g_list_length (dia->data->selected) == 0)
1301 return;
1303 orig_list = g_list_copy(dia->data->active_layer->objects);
1305 sorted_list = diagram_get_sorted_selected(dia);
1306 object_add_updates_list(orig_list, dia);
1308 /* Sanity check */
1309 g_assert(g_list_length (dia->data->selected) == g_list_length(sorted_list));
1311 new_list = g_list_copy(orig_list);
1312 tmp = new_list;
1313 stmp = sorted_list;
1315 for (tmp = new_list; tmp != NULL; tmp = g_list_next(tmp)) {
1316 if (stmp == NULL) break;
1317 if (tmp->next == NULL) break;
1318 if (tmp->data == stmp->data) {
1319 /* This just takes care of any starting matches */
1320 stmp = g_list_next(stmp);
1321 } else if (tmp->next->data == stmp->data) {
1322 /* This flips the non-selected element forwards, ala bubblesort */
1323 void *swap = tmp->data;
1324 tmp->data = tmp->next->data;
1325 tmp->next->data = swap;
1326 stmp = g_list_next(stmp);
1330 layer_set_object_list(dia->data->active_layer, new_list);
1332 undo_reorder_objects(dia, g_list_copy(sorted_list), orig_list);
1334 diagram_modified(dia);
1335 diagram_flush(dia);
1336 undo_set_transactionpoint(dia->undo);
1339 void
1340 diagram_set_filename(Diagram *dia, const char *filename)
1342 g_free(dia->filename);
1343 dia->filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
1345 diagram_update_for_filename(dia);
1348 /** Update the various areas that require updating when changing filename
1349 * This will ensure that all places that use the filename are updated:
1350 * Window titles, layer dialog, recent files, diagram tree.
1351 * @param dia The diagram whose filename has changed.
1353 static void
1354 diagram_update_for_filename(Diagram *dia)
1356 GSList *l;
1357 DDisplay *ddisp;
1358 char *title;
1359 char *filename = dia->filename;
1361 title = diagram_get_name(dia);
1363 l = dia->displays;
1364 while (l!=NULL) {
1365 ddisp = (DDisplay *) l->data;
1367 ddisplay_set_title(ddisp, title);
1369 l = g_slist_next(l);
1372 g_free(title);
1374 layer_dialog_update_diagram_list();
1375 recent_file_history_add(filename);
1377 diagram_tree_update_name(diagram_tree(), dia);
1380 /** Returns a string with a 'sensible' (human-readable) name for the
1381 * diagram. The string should be freed after use.
1382 * This name may or may not be the same as the filename.
1384 gchar *
1385 diagram_get_name(Diagram *dia)
1387 gchar *title = strrchr(dia->filename, G_DIR_SEPARATOR);
1388 if (title==NULL) {
1389 title = dia->filename;
1390 } else {
1391 title++;
1394 return g_strdup(title);
1397 int diagram_modified_exists(void)
1399 GList *list;
1400 Diagram *dia;
1402 list = open_diagrams;
1404 while (list != NULL) {
1405 dia = (Diagram *) list->data;
1407 if (diagram_is_modified(dia))
1408 return TRUE;
1410 list = g_list_next(list);
1412 return FALSE;
1415 void diagram_object_modified(Diagram *dia, DiaObject *object)
1417 diagram_tree_update_object(diagram_tree(), dia, object);