using dia-manual.chm instead of dia.chm
[dia.git] / app / load_save.c
blobf547a784c5a0a208974c8b28aec463bf0fb4f1e8
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 #include <config.h>
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23 #include <stdlib.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <string.h>
29 #include <glib.h>
30 #include <errno.h>
32 #include "intl.h"
34 #include <libxml/tree.h>
35 #include <libxml/parser.h>
36 #include <libxml/xmlmemory.h>
37 #include "dia_xml_libxml.h"
38 #include "dia_xml.h"
40 #include "load_save.h"
41 #include "group.h"
42 #include "diagramdata.h"
43 #include "message.h"
44 #include "preferences.h"
45 #include "diapagelayout.h"
46 #include "autosave.h"
48 #ifdef G_OS_WIN32
49 #include <io.h>
50 #define mkstemp(s) _open(_mktemp(s), O_CREAT | O_TRUNC | O_WRONLY | _O_BINARY, 0644)
51 #define fchmod(f,m) (0)
52 #endif
53 #ifdef __EMX__
54 #define mkstemp(s) _open(_mktemp(s), O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644)
55 #define fchmod(f,m) (0)
56 #endif
58 static void
59 GHFuncUnknownObjects(gpointer key,
60 gpointer value,
61 gpointer user_data)
63 GString* s = (GString*)user_data;
64 g_string_append(s, "\n");
65 g_string_append(s, (gchar*)key);
66 g_free(key);
69 static GList *
70 read_objects(xmlNodePtr objects, Layer *layer,
71 GHashTable *objects_hash,const char *filename, Object *parent)
73 GList *list;
74 ObjectType *type;
75 Object *obj;
76 ObjectNode obj_node;
77 char *typestr;
78 char *versionstr;
79 char *id;
80 int version;
81 GHashTable* unknown_hash;
82 GString* unknown_str;
83 xmlNodePtr child_node;
85 unknown_hash = g_hash_table_new(g_str_hash, g_str_equal);
86 unknown_str = g_string_new("Unknown types while reading diagram file");
88 list = NULL;
90 obj_node = objects->xmlChildrenNode;
92 while ( obj_node != NULL) {
93 if (xmlIsBlankNode(obj_node)) {
94 obj_node = obj_node->next;
95 continue;
98 if (!obj_node) break;
100 if (strcmp(obj_node->name, "object")==0) {
101 typestr = xmlGetProp(obj_node, "type");
102 versionstr = xmlGetProp(obj_node, "version");
103 id = xmlGetProp(obj_node, "id");
105 version = 0;
106 if (versionstr != NULL) {
107 version = atoi(versionstr);
108 xmlFree(versionstr);
111 type = object_get_type((char *)typestr);
113 if (!type) {
114 if (NULL == g_hash_table_lookup(unknown_hash, typestr))
115 g_hash_table_insert(unknown_hash, g_strdup(typestr), 0);
117 else
119 obj = type->ops->load(obj_node, version, filename);
120 layer_add_object(layer,obj);
121 list = g_list_append(list, obj);
123 if (parent)
125 obj->parent = parent;
126 parent->children = g_list_append(parent->children, obj);
129 g_hash_table_insert(objects_hash, g_strdup((char *)id), obj);
131 child_node = obj_node->children;
133 while(child_node)
135 if (strcmp(child_node->name, "children") == 0)
137 list = g_list_concat(list, read_objects(child_node, layer, objects_hash, filename, obj));
138 break;
140 child_node = child_node->next;
143 if (typestr) xmlFree(typestr);
144 if (id) xmlFree (id);
145 } else if (strcmp(obj_node->name, "group")==0) {
146 obj = group_create(read_objects(obj_node, layer,
147 objects_hash, filename, NULL));
148 layer_add_object(layer,obj);
150 list = g_list_append(list, obj);
151 } else {
152 /* silently ignore other nodes */
155 obj_node = obj_node->next;
157 /* show all the unknown types in one message */
158 if (0 < g_hash_table_size(unknown_hash)) {
159 g_hash_table_foreach(unknown_hash,
160 GHFuncUnknownObjects,
161 unknown_str);
162 message_error(unknown_str->str);
164 g_hash_table_destroy(unknown_hash);
165 g_string_free(unknown_str, TRUE);
167 return list;
170 static void
171 read_connections(GList *objects, xmlNodePtr layer_node,
172 GHashTable *objects_hash)
174 ObjectNode obj_node;
175 GList *list;
176 xmlNodePtr connections;
177 xmlNodePtr connection;
178 char *handlestr;
179 char *tostr;
180 char *connstr;
181 int handle, conn;
182 Object *to;
184 list = objects;
185 obj_node = layer_node->xmlChildrenNode;
186 while ((list != NULL) && (obj_node != NULL)) {
187 Object *obj = (Object *) list->data;
189 while (obj_node && xmlIsBlankNode(obj_node)) obj_node = obj_node->next;
190 if (!obj_node) break;
192 if IS_GROUP(obj) {
193 read_connections(group_objects(obj), obj_node, objects_hash);
194 } else {
195 connections = obj_node->xmlChildrenNode;
196 while ((connections!=NULL) &&
197 (strcmp(connections->name, "connections")!=0))
198 connections = connections->next;
199 if (connections != NULL) {
200 connection = connections->xmlChildrenNode;
201 while (connection != NULL) {
202 if (xmlIsBlankNode(connection)) {
203 connection = connection->next;
204 continue;
206 handlestr = xmlGetProp(connection, "handle");
207 tostr = xmlGetProp(connection, "to");
208 connstr = xmlGetProp(connection, "connection");
210 handle = atoi(handlestr);
211 conn = atoi(connstr);
213 to = g_hash_table_lookup(objects_hash, tostr);
215 if (handlestr) xmlFree(handlestr);
216 if (connstr) xmlFree(connstr);
217 if (tostr) xmlFree(tostr);
219 if (to == NULL) {
220 message_error(_("Error loading diagram.\n"
221 "Linked object not found in document."));
222 } else if (conn >= 0 && conn >= to->num_connections) {
223 message_error(_("Error loading diagram.\n"
224 "connection point does not exist."));
225 } else if (handle >= 0 && handle >= obj->num_handles) {
226 message_error(_("Error loading diagram.\n"
227 "connection handle does not exist."));
228 } else {
229 object_connect(obj, obj->handles[handle],
230 to->connections[conn]);
233 connection = connection->next;
239 list = g_list_next(list);
240 obj_node = obj_node->next;
244 static void
245 hash_free_string(gpointer key,
246 gpointer value,
247 gpointer user_data)
249 g_free(key);
252 static xmlNodePtr
253 find_node_named (xmlNodePtr p, const char *name)
255 while (p && 0 != strcmp(p->name, name))
256 p = p->next;
257 return p;
260 static gboolean
261 diagram_data_load(const char *filename, DiagramData *data, void* user_data)
263 GHashTable *objects_hash;
264 int fd;
265 GList *list;
266 xmlDocPtr doc;
267 xmlNodePtr diagramdata;
268 xmlNodePtr paperinfo, gridinfo, guideinfo;
269 xmlNodePtr layer_node;
270 AttributeNode attr;
271 Layer *layer;
272 xmlNsPtr namespace;
273 gchar firstchar;
275 if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
276 message_error(_("You must specify a file, not a directory.\n"));
277 return FALSE;
280 fd = open(filename, O_RDONLY);
282 if (fd==-1) {
283 message_error(_("Couldn't open: '%s' for reading.\n"), filename);
284 return FALSE;
287 if (read(fd, &firstchar, 1)) {
288 data->is_compressed = (firstchar != '<');
289 } else {
290 /* Couldn't read a single char? Set to default. */
291 data->is_compressed = prefs.new_diagram.compress_save;
294 /* Note that this closing and opening means we can't read from a pipe */
295 close(fd);
297 doc = xmlDiaParseFile(filename);
299 if (doc == NULL){
300 message_error(_("Error loading diagram %s.\nUnknown file type."),filename);
301 return FALSE;
304 if (doc->xmlRootNode == NULL) {
305 message_error(_("Error loading diagram %s.\nUnknown file type."),filename);
306 xmlFreeDoc (doc);
307 return FALSE;
310 namespace = xmlSearchNs(doc, doc->xmlRootNode, "dia");
311 if (strcmp (doc->xmlRootNode->name, "diagram") || (namespace == NULL)){
312 message_error(_("Error loading diagram %s.\nNot a Dia file."), filename);
313 xmlFreeDoc (doc);
314 return FALSE;
317 /* Destroy the default layer: */
318 g_ptr_array_remove(data->layers, data->active_layer);
319 layer_destroy(data->active_layer);
321 diagramdata =
322 find_node_named (doc->xmlRootNode->xmlChildrenNode, "diagramdata");
324 /* Read in diagram data: */
325 data->bg_color = prefs.new_diagram.bg_color;
326 attr = composite_find_attribute(diagramdata, "background");
327 if (attr != NULL)
328 data_color(attribute_first_data(attr), &data->bg_color);
330 data->pagebreak_color = prefs.new_diagram.pagebreak_color;
331 attr = composite_find_attribute(diagramdata, "pagebreak");
332 if (attr != NULL)
333 data_color(attribute_first_data(attr), &data->pagebreak_color);
335 /* load paper information from diagramdata section */
336 attr = composite_find_attribute(diagramdata, "paper");
337 if (attr != NULL) {
338 paperinfo = attribute_first_data(attr);
340 attr = composite_find_attribute(paperinfo, "name");
341 if (attr != NULL) {
342 g_free(data->paper.name);
343 data->paper.name = data_string(attribute_first_data(attr));
345 /* set default margins for paper size ... */
346 dia_page_layout_get_default_margins(data->paper.name,
347 &data->paper.tmargin,
348 &data->paper.bmargin,
349 &data->paper.lmargin,
350 &data->paper.rmargin);
352 attr = composite_find_attribute(paperinfo, "tmargin");
353 if (attr != NULL)
354 data->paper.tmargin = data_real(attribute_first_data(attr));
355 attr = composite_find_attribute(paperinfo, "bmargin");
356 if (attr != NULL)
357 data->paper.bmargin = data_real(attribute_first_data(attr));
358 attr = composite_find_attribute(paperinfo, "lmargin");
359 if (attr != NULL)
360 data->paper.lmargin = data_real(attribute_first_data(attr));
361 attr = composite_find_attribute(paperinfo, "rmargin");
362 if (attr != NULL)
363 data->paper.rmargin = data_real(attribute_first_data(attr));
365 attr = composite_find_attribute(paperinfo, "is_portrait");
366 data->paper.is_portrait = TRUE;
367 if (attr != NULL)
368 data->paper.is_portrait = data_boolean(attribute_first_data(attr));
370 attr = composite_find_attribute(paperinfo, "scaling");
371 data->paper.scaling = 1.0;
372 if (attr != NULL)
373 data->paper.scaling = data_real(attribute_first_data(attr));
375 attr = composite_find_attribute(paperinfo, "fitto");
376 data->paper.fitto = FALSE;
377 if (attr != NULL)
378 data->paper.fitto = data_boolean(attribute_first_data(attr));
380 attr = composite_find_attribute(paperinfo, "fitwidth");
381 data->paper.fitwidth = 1;
382 if (attr != NULL)
383 data->paper.fitwidth = data_int(attribute_first_data(attr));
385 attr = composite_find_attribute(paperinfo, "fitheight");
386 data->paper.fitheight = 1;
387 if (attr != NULL)
388 data->paper.fitheight = data_int(attribute_first_data(attr));
390 /* calculate effective width/height */
391 dia_page_layout_get_paper_size(data->paper.name,
392 &data->paper.width,
393 &data->paper.height);
394 if (!data->paper.is_portrait) {
395 gfloat tmp = data->paper.width;
397 data->paper.width = data->paper.height;
398 data->paper.height = tmp;
400 data->paper.width -= data->paper.lmargin;
401 data->paper.width -= data->paper.rmargin;
402 data->paper.height -= data->paper.tmargin;
403 data->paper.height -= data->paper.bmargin;
404 data->paper.width /= data->paper.scaling;
405 data->paper.height /= data->paper.scaling;
407 attr = composite_find_attribute(diagramdata, "grid");
408 if (attr != NULL) {
409 gridinfo = attribute_first_data(attr);
411 attr = composite_find_attribute(gridinfo, "width_x");
412 if (attr != NULL)
413 data->grid.width_x = data_real(attribute_first_data(attr));
414 attr = composite_find_attribute(gridinfo, "width_y");
415 if (attr != NULL)
416 data->grid.width_y = data_real(attribute_first_data(attr));
417 attr = composite_find_attribute(gridinfo, "visible_x");
418 if (attr != NULL)
419 data->grid.visible_x = data_int(attribute_first_data(attr));
420 attr = composite_find_attribute(gridinfo, "visible_y");
421 if (attr != NULL)
422 data->grid.visible_y = data_int(attribute_first_data(attr));
424 data->grid.colour = prefs.new_diagram.grid_color;
425 attr = composite_find_attribute(diagramdata, "color");
426 if (attr != NULL)
427 data_color(attribute_first_data(attr), &data->grid.colour);
430 attr = composite_find_attribute(diagramdata, "guides");
431 if (attr != NULL) {
432 guint i;
433 DataNode guide;
435 guideinfo = attribute_first_data(attr);
437 attr = composite_find_attribute(guideinfo, "hguides");
438 if (attr != NULL) {
439 data->guides.nhguides = attribute_num_data(attr);
440 g_free(data->guides.hguides);
441 data->guides.hguides = g_new(real, data->guides.nhguides);
443 guide = attribute_first_data(attr);
444 for (i = 0; i < data->guides.nhguides; i++, guide = data_next(guide))
445 data->guides.hguides[i] = data_real(guide);
447 attr = composite_find_attribute(guideinfo, "vguides");
448 if (attr != NULL) {
449 data->guides.nvguides = attribute_num_data(attr);
450 g_free(data->guides.vguides);
451 data->guides.vguides = g_new(real, data->guides.nvguides);
453 guide = attribute_first_data(attr);
454 for (i = 0; i < data->guides.nvguides; i++, guide = data_next(guide))
455 data->guides.vguides[i] = data_real(guide);
459 /* Read in all layers: */
460 layer_node =
461 find_node_named (doc->xmlRootNode->xmlChildrenNode, "layer");
463 objects_hash = g_hash_table_new(g_str_hash, g_str_equal);
465 while (layer_node != NULL) {
466 gchar *name;
467 char *visible;
469 if (xmlIsBlankNode(layer_node)) {
470 layer_node = layer_node->next;
471 continue;
474 if (!layer_node) break;
476 name = (char *)xmlGetProp(layer_node, "name");
477 if (!name) break; /* name is mandatory */
479 visible = (char *)xmlGetProp(layer_node, "visible");
481 layer = new_layer(g_strdup(name), data);
482 if (name) xmlFree(name);
484 layer->visible = FALSE;
485 if ((visible) && (strcmp(visible, "true")==0))
486 layer->visible = TRUE;
487 if (visible) xmlFree(visible);
489 /* Read in all objects: */
491 list = read_objects(layer_node, layer, objects_hash, filename, NULL);
492 layer->objects = list;
493 read_connections( list, layer_node, objects_hash);
495 data_add_layer(data, layer);
497 layer_node = layer_node->next;
500 data->active_layer = (Layer *) g_ptr_array_index(data->layers, 0);
502 xmlFreeDoc(doc);
504 g_hash_table_foreach(objects_hash, hash_free_string, NULL);
506 g_hash_table_destroy(objects_hash);
508 if (data->layers->len < 1) {
509 message_error (_("Error loading diagram:\n%s.\n"
510 "A valid Dia file defines at least one layer."),
511 filename);
512 return FALSE;
515 return TRUE;
518 static void
519 write_objects(GList *objects, xmlNodePtr objects_node,
520 GHashTable *objects_hash, int *obj_nr, const char *filename)
522 char buffer[31];
523 ObjectNode obj_node;
524 xmlNodePtr group_node;
525 xmlNodePtr parent_node;
526 GList *list;
528 list = objects;
529 while (list != NULL) {
530 Object *obj = (Object *) list->data;
532 if (g_hash_table_lookup(objects_hash, obj))
534 list = g_list_next(list);
535 continue;
538 if IS_GROUP(obj) {
539 group_node = xmlNewChild(objects_node, NULL, "group", NULL);
540 write_objects(group_objects(obj), group_node,
541 objects_hash, obj_nr, filename);
542 } else {
543 obj_node = xmlNewChild(objects_node, NULL, "object", NULL);
545 xmlSetProp(obj_node, "type", obj->type->name);
547 g_snprintf(buffer, 30, "%d", obj->type->version);
548 xmlSetProp(obj_node, "version", buffer);
550 g_snprintf(buffer, 30, "O%d", *obj_nr);
551 xmlSetProp(obj_node, "id", buffer);
553 (*obj->type->ops->save)(obj, obj_node, filename);
555 /* Add object -> obj_nr to hash table */
556 g_hash_table_insert(objects_hash, obj, GINT_TO_POINTER(*obj_nr));
557 (*obj_nr)++;
559 if (obj->can_parent && obj->children)
561 parent_node = xmlNewChild(obj_node, NULL, "children", NULL);
562 write_objects(obj->children, parent_node,
563 objects_hash, obj_nr, filename);
567 list = g_list_next(list);
571 static int
572 write_connections(GList *objects, xmlNodePtr layer_node,
573 GHashTable *objects_hash)
575 ObjectNode obj_node;
576 GList *list;
577 xmlNodePtr connections;
578 xmlNodePtr connection;
579 char buffer[31];
580 int i;
582 list = objects;
583 obj_node = layer_node->xmlChildrenNode;
584 while ((list != NULL) && (obj_node != NULL)) {
585 Object *obj = (Object *) list->data;
587 while (obj_node && xmlIsBlankNode(obj_node)) obj_node = obj_node->next;
588 if (!obj_node) break;
590 if IS_GROUP(obj) {
591 write_connections(group_objects(obj), obj_node, objects_hash);
592 } else {
593 connections = NULL;
595 for (i=0;i<obj->num_handles;i++) {
596 ConnectionPoint *con_point;
597 Handle *handle;
599 handle = obj->handles[i];
600 con_point = handle->connected_to;
602 if ( con_point != NULL ) {
603 Object *other_obj;
604 int con_point_nr;
606 other_obj = con_point->object;
608 con_point_nr=0;
609 while (other_obj->connections[con_point_nr] != con_point) {
610 con_point_nr++;
611 if (con_point_nr>=other_obj->num_connections) {
612 message_error("Internal error saving diagram\n con_point_nr >= other_obj->num_connections\n");
613 return FALSE;
617 if (connections == NULL)
618 connections = xmlNewChild(obj_node, NULL, "connections", NULL);
620 connection = xmlNewChild(connections, NULL, "connection", NULL);
621 /* from what handle on this object*/
622 g_snprintf(buffer, 30, "%d", i);
623 xmlSetProp(connection, "handle", buffer);
624 /* to what object */
625 g_snprintf(buffer, 30, "O%d",
626 GPOINTER_TO_INT(g_hash_table_lookup(objects_hash,
627 other_obj)));
628 xmlSetProp(connection, "to", buffer);
629 /* to what connection_point on that object */
630 g_snprintf(buffer, 30, "%d", con_point_nr);
631 xmlSetProp(connection, "connection", buffer);
636 list = g_list_next(list);
637 obj_node = obj_node->next;
639 return TRUE;
642 /* Filename seems to be junk, but is passed on to objects */
643 static xmlDocPtr
644 diagram_data_write_doc(DiagramData *data, const char *filename)
646 xmlDocPtr doc;
647 xmlNodePtr tree;
648 xmlNodePtr pageinfo, gridinfo, guideinfo;
649 xmlNodePtr layer_node;
650 GHashTable *objects_hash;
651 int res;
652 int obj_nr;
653 int i;
654 Layer *layer;
655 AttributeNode attr;
656 xmlNs *name_space;
658 doc = xmlNewDoc("1.0");
659 doc->encoding = xmlStrdup("UTF-8");
660 doc->xmlRootNode = xmlNewDocNode(doc, NULL, "diagram", NULL);
662 name_space = xmlNewNs(doc->xmlRootNode,
663 "http://www.lysator.liu.se/~alla/dia/",
664 "dia");
665 xmlSetNs(doc->xmlRootNode, name_space);
667 tree = xmlNewChild(doc->xmlRootNode, name_space, "diagramdata", NULL);
669 attr = new_attribute((ObjectNode)tree, "background");
670 data_add_color(attr, &data->bg_color);
672 attr = new_attribute((ObjectNode)tree, "pagebreak");
673 data_add_color(attr, &data->pagebreak_color);
675 attr = new_attribute((ObjectNode)tree, "paper");
676 pageinfo = data_add_composite(attr, "paper");
677 data_add_string(composite_add_attribute(pageinfo, "name"),
678 data->paper.name);
679 data_add_real(composite_add_attribute(pageinfo, "tmargin"),
680 data->paper.tmargin);
681 data_add_real(composite_add_attribute(pageinfo, "bmargin"),
682 data->paper.bmargin);
683 data_add_real(composite_add_attribute(pageinfo, "lmargin"),
684 data->paper.lmargin);
685 data_add_real(composite_add_attribute(pageinfo, "rmargin"),
686 data->paper.rmargin);
687 data_add_boolean(composite_add_attribute(pageinfo, "is_portrait"),
688 data->paper.is_portrait);
689 data_add_real(composite_add_attribute(pageinfo, "scaling"),
690 data->paper.scaling);
691 data_add_boolean(composite_add_attribute(pageinfo, "fitto"),
692 data->paper.fitto);
693 if (data->paper.fitto) {
694 data_add_int(composite_add_attribute(pageinfo, "fitwidth"),
695 data->paper.fitwidth);
696 data_add_int(composite_add_attribute(pageinfo, "fitheight"),
697 data->paper.fitheight);
700 attr = new_attribute((ObjectNode)tree, "grid");
701 gridinfo = data_add_composite(attr, "grid");
702 data_add_real(composite_add_attribute(gridinfo, "width_x"),
703 data->grid.width_x);
704 data_add_real(composite_add_attribute(gridinfo, "width_y"),
705 data->grid.width_y);
706 data_add_int(composite_add_attribute(gridinfo, "visible_x"),
707 data->grid.visible_x);
708 data_add_int(composite_add_attribute(gridinfo, "visible_y"),
709 data->grid.visible_y);
710 attr = new_attribute((ObjectNode)tree, "color");
711 data_add_composite(gridinfo, "color");
712 data_add_color(attr, &data->grid.colour);
715 attr = new_attribute((ObjectNode)tree, "guides");
716 guideinfo = data_add_composite(attr, "guides");
717 attr = composite_add_attribute(guideinfo, "hguides");
718 for (i = 0; i < data->guides.nhguides; i++)
719 data_add_real(attr, data->guides.hguides[i]);
720 attr = composite_add_attribute(guideinfo, "vguides");
721 for (i = 0; i < data->guides.nvguides; i++)
722 data_add_real(attr, data->guides.vguides[i]);
724 objects_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
726 obj_nr = 0;
728 for (i = 0; i < data->layers->len; i++) {
729 layer_node = xmlNewChild(doc->xmlRootNode, name_space, "layer", NULL);
730 layer = (Layer *) g_ptr_array_index(data->layers, i);
731 xmlSetProp(layer_node, "name", layer->name);
733 if (layer->visible)
734 xmlSetProp(layer_node, "visible", "true");
735 else
736 xmlSetProp(layer_node, "visible", "false");
738 write_objects(layer->objects, layer_node,
739 objects_hash, &obj_nr, filename);
741 res = write_connections(layer->objects, layer_node, objects_hash);
742 /* Why do we bail out like this? It leaks! */
743 if (!res)
744 return NULL;
746 g_hash_table_destroy(objects_hash);
748 if (data->is_compressed)
749 xmlSetDocCompressMode(doc, 9);
750 else
751 xmlSetDocCompressMode(doc, 0);
753 return doc;
756 /** This tries to save the diagram into a file, without any backup
757 * Returns >= 0 on success.
758 * Only for internal use. */
759 static int
760 diagram_data_raw_save(DiagramData *data, const char *filename)
762 xmlDocPtr doc;
763 int ret;
765 doc = diagram_data_write_doc(data, filename);
767 ret = xmlDiaSaveFile (filename, doc);
768 xmlFreeDoc(doc);
770 return ret;
773 /** This saves the diagram, using a backup in case of failure */
774 static int
775 diagram_data_save(DiagramData *data, const char *filename)
777 FILE *file;
778 char *bakname,*tmpname,*dirname,*p;
779 int mode,_umask;
780 int fildes;
781 int ret;
783 /* build the temporary and backup file names */
784 dirname = g_strdup(filename);
785 p = strrchr((char *)dirname,G_DIR_SEPARATOR);
786 if (p) {
787 *(p+1) = 0;
788 } else {
789 g_free(dirname);
790 dirname = g_strdup("." G_DIR_SEPARATOR_S);
792 tmpname = g_strconcat(dirname,"__diaXXXXXX",NULL);
793 bakname = g_strconcat(filename,"~",NULL);
795 /* open a temporary name, and fix the modes to match what fopen() would have
796 done (mkstemp() is (rightly so) a bit paranoid for what we do). */
797 fildes = mkstemp(tmpname);
798 _umask = umask(0); umask(_umask);
799 mode = 0666 & ~_umask;
800 ret = fchmod(fildes,mode);
801 file = fdopen(fildes,"wb");
803 /* Now write the data in the temporary file name. */
805 if (file==NULL) {
806 message_error(_("Can't open output file %s: %s\n"), tmpname, strerror(errno));
807 return FALSE;
809 fclose(file);
811 ret = diagram_data_raw_save(data, tmpname);
813 if (ret < 0) {
814 /* Save failed; we clean our stuff up, without touching the file named
815 "filename" if it existed. */
816 unlink(tmpname);
817 g_free(tmpname);
818 g_free(dirname);
819 g_free(bakname);
820 return FALSE;
822 /* save succeeded. We kill the old backup file, move the old file into
823 backup, and the temp file into the new saved file. */
824 unlink(bakname);
825 rename(filename,bakname);
826 ret = rename(tmpname,filename);
827 g_free(tmpname);
828 g_free(dirname);
829 g_free(bakname);
830 return (ret?FALSE:TRUE);
834 diagram_save(Diagram *dia, const char *filename)
836 gboolean res = diagram_data_save(dia->data, filename);
838 if (!res) {
839 message_error(_("Failed to save file '%s'.\n"), filename);
840 return res;
843 dia->unsaved = FALSE;
844 diagram_set_modified (dia, FALSE);
846 diagram_cleanup_autosave(dia);
848 return TRUE;
851 /* Autosave stuff. Needs to use low-level save to avoid setting and resetting flags */
852 void
853 diagram_cleanup_autosave(Diagram *dia)
855 gchar *savefile;
856 struct stat statbuf;
858 savefile = dia->autosavefilename;
860 if (savefile == NULL) return;
862 if (stat(savefile, &statbuf) == 0) { /* Success */
863 unlink(savefile);
865 g_free(savefile);
866 dia->autosavefilename = NULL;
867 dia->autosaved = FALSE;
870 /** Absolutely autosave a diagram.
871 * Called after a periodic check at the first idleness.
873 void
874 diagram_autosave(Diagram *dia)
876 gchar *save_filename;
878 /* Must check if the diagram is still valid, or Death Ensues! */
879 GList *diagrams = dia_open_diagrams();
880 Diagram *diagram;
881 while (diagrams != NULL) {
882 diagram = (Diagram *)diagrams->data;
883 if (diagram == dia &&
884 diagram->modified &&
885 !diagram->autosaved) {
886 save_filename = g_strdup_printf("%s.autosave", dia->filename);
888 if (dia->autosavefilename != NULL)
889 g_free(dia->autosavefilename);
890 dia->autosavefilename = save_filename;
891 diagram_data_raw_save(dia->data, save_filename);
892 dia->autosaved = TRUE;
893 return;
895 diagrams = g_list_next(diagrams);
899 /* --- filter interfaces --- */
900 static void
901 export_native(DiagramData *data, const gchar *filename,
902 const gchar *diafilename, void* user_data)
904 diagram_data_save(data, filename);
907 static const gchar *extensions[] = { "dia", NULL };
908 DiaExportFilter dia_export_filter = {
909 N_("Native Dia Diagram"),
910 extensions,
911 export_native
913 DiaImportFilter dia_import_filter = {
914 N_("Native Dia Diagram"),
915 extensions,
916 diagram_data_load