1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include <sys/types.h>
29 #include <libxml/tree.h>
30 #include <libxml/parser.h>
31 #include <libxml/xmlmemory.h>
32 #include "dia_xml_libxml.h"
42 static GSList
*sheets
= NULL
;
45 new_sheet(char *name
, gchar
*description
, char *filename
, SheetScope scope
,
50 sheet
= g_new(Sheet
, 1);
52 sheet
->name
= g_strdup(name
);
53 sheet
->description
= g_strdup(description
);
55 sheet
->filename
= filename
;
57 sheet
->shadowing
= shadowing
;
58 sheet
->objects
= NULL
;
63 sheet_prepend_sheet_obj(Sheet
*sheet
, SheetObject
*obj
)
67 type
= object_get_type(obj
->object_type
);
69 message_warning("DiaObject '%s' needed in sheet '%s' was not found.\n"
70 "It will not be available for use.",
71 obj
->object_type
, sheet
->name
);
73 sheet
->objects
= g_slist_prepend( sheet
->objects
, (gpointer
) obj
);
78 sheet_append_sheet_obj(Sheet
*sheet
, SheetObject
*obj
)
82 type
= object_get_type(obj
->object_type
);
84 message_warning("DiaObject '%s' needed in sheet '%s' was not found.\n"
85 "It will not be availible for use.",
86 obj
->object_type
, sheet
->name
);
88 sheet
->objects
= g_slist_append( sheet
->objects
, (gpointer
) obj
);
93 register_sheet(Sheet
*sheet
)
95 sheets
= g_slist_append(sheets
, (gpointer
) sheet
);
104 /* Sheet file management */
106 static void load_sheets_from_dir(const gchar
*directory
, SheetScope scope
);
107 static void load_register_sheet(const gchar
*directory
,const gchar
*filename
,
110 /** Sort the list of sheets by *locale*.
113 dia_sheet_sort_callback(gconstpointer a
, gconstpointer b
)
115 return g_utf8_collate(gettext( ((Sheet
*)(a
))->name
),
116 gettext( ((Sheet
*)(b
))->name
));
120 dia_sort_sheets(void)
122 sheets
= g_slist_sort(sheets
, dia_sheet_sort_callback
);
125 void load_all_sheets(void) {
129 home_dir
= dia_config_filename("sheets");
131 load_sheets_from_dir(home_dir
, SHEET_SCOPE_USER
);
135 sheet_path
= getenv("DIA_SHEET_PATH");
137 char **dirs
= g_strsplit(sheet_path
,G_SEARCHPATH_SEPARATOR_S
,0);
140 for (i
=0; dirs
[i
] != NULL
; i
++)
141 load_sheets_from_dir(dirs
[i
], SHEET_SCOPE_SYSTEM
);
144 char *thedir
= dia_get_data_directory("sheets");
145 load_sheets_from_dir(thedir
, SHEET_SCOPE_SYSTEM
);
149 /* Sorting their sheets alphabetically makes user merging easier */
155 load_sheets_from_dir(const gchar
*directory
, SheetScope scope
)
161 dp
= g_dir_open(directory
, 0, NULL
);
164 while ( (dentry
= g_dir_read_name(dp
)) ) {
165 gchar
*filename
= g_strconcat(directory
,G_DIR_SEPARATOR_S
,
168 if (!g_file_test(filename
, G_FILE_TEST_IS_REGULAR
)) {
173 /* take only .sheet files */
174 p
= filename
+ strlen(filename
) - 6 /* strlen(".sheet") */;
175 if (0!=strncmp(p
,".sheet",6)) {
180 load_register_sheet(directory
, filename
, scope
);
189 load_register_sheet(const gchar
*dirname
, const gchar
*filename
,
194 xmlNodePtr node
, contents
,subnode
,root
;
196 gchar
*name
= NULL
, *description
= NULL
;
198 int descr_score
= -1;
201 gboolean set_line_break
= FALSE
;
202 gboolean name_is_gmalloced
= FALSE
;
203 Sheet
*shadowing
= NULL
;
204 Sheet
*shadowing_sheet
= NULL
;
206 /* the XML fun begins here. */
208 doc
= xmlDoParseFile(filename
);
210 root
= doc
->xmlRootNode
;
211 while (root
&& (root
->type
!= XML_ELEMENT_NODE
)) root
=root
->next
;
213 if (xmlIsBlankNode(root
)) return;
215 if (!(ns
= xmlSearchNsByHref(doc
,root
,
216 DIA_XML_NAME_SPACE_BASE
"dia-sheet-ns"))) {
217 g_warning("could not find sheet namespace");
221 if ((root
->ns
!= ns
) || (strcmp(root
->name
,"sheet"))) {
222 g_warning("root element was %s -- expecting sheet",
223 doc
->xmlRootNode
->name
);
229 for (node
= root
->xmlChildrenNode
; node
!= NULL
; node
= node
->next
) {
230 if (xmlIsBlankNode(node
)) continue;
231 if (node
->type
!= XML_ELEMENT_NODE
)
234 if (node
->ns
== ns
&& !strcmp(node
->name
, "name")) {
237 /* compare the xml:lang property on this element to see if we get a
238 * better language match. LibXML seems to throw away attribute
239 * namespaces, so we use "lang" instead of "xml:lang" */
240 /* Now using the C locale for internal sheet names, instead gettexting
241 * when the name is used in the menus. Not going to figure out the
242 * XML lang system more than absolutely necessary now. --LC
245 tmp = xmlGetProp(node, "xml:lang");
246 if (!tmp) tmp = xmlGetProp(node, "lang");
248 score
= intl_score_locale("C");
250 if (tmp) xmlFree(tmp);
253 if (name_score
< 0 || score
< name_score
) {
255 if (name
) xmlFree(name
);
256 name
= xmlNodeGetContent(node
);
258 } else if (node
->ns
== ns
&& !strcmp(node
->name
, "description")) {
261 /* compare the xml:lang property on this element to see if we get a
262 * better language match. LibXML seems to throw away attribute
263 * namespaces, so we use "lang" instead of "xml:lang" */
264 tmp
= xmlGetProp(node
, "xml:lang");
265 if (!tmp
) tmp
= xmlGetProp(node
, "lang");
266 score
= intl_score_locale(tmp
);
267 if (tmp
) xmlFree(tmp
);
269 if (descr_score
< 0 || score
< descr_score
) {
271 if (description
) xmlFree(description
);
272 description
= xmlNodeGetContent(node
);
275 } else if (node
->ns
== ns
&& !strcmp(node
->name
, "contents")) {
280 if (!name
|| !contents
) {
281 g_warning("No <name> and/or <contents> in sheet %s--skipping", filename
);
283 if (name
) xmlFree(name
);
284 if (description
) xmlFree(description
);
288 /* Notify the user when we load a sheet that appears to be an updated
289 version of a sheet loaded previously (i.e. from ~/.dia/sheets). */
291 sheetp
= get_sheets_list();
294 if (sheetp
->data
&& !strcmp(((Sheet
*)(sheetp
->data
))->name
, name
))
296 struct stat first_file
, this_file
;
299 stat_ret
= stat(((Sheet
*)(sheetp
->data
))->filename
, &first_file
);
302 stat_ret
= stat(filename
, &this_file
);
305 if (this_file
.st_mtime
> first_file
.st_mtime
)
307 gchar
*tmp
= g_strdup_printf("%s [Copy of system]", name
);
308 message_notice("The system sheet '%s' appears to be more recent"
309 " than your custom\n"
310 "version and has been loaded as '%s' for this session."
312 "Move new objects (if any) from '%s' into your custom"
314 "or remove '%s', using the 'Sheets and Objects' dialog.",
315 name
, tmp
, tmp
, tmp
);
318 name_is_gmalloced
= TRUE
;
319 shadowing
= sheetp
->data
; /* This copy-of-system sheet shadows
324 /* The already-created user sheet shadows this sheet (which will be
325 invisible), but we don't know this sheet's address yet */
326 shadowing_sheet
= sheetp
->data
;
329 sheetp
= g_slist_next(sheetp
);
332 sheet
= new_sheet(name
, description
, g_strdup(filename
), scope
, shadowing
);
335 shadowing_sheet
->shadowing
= sheet
; /* Hilarious :-) */
337 if (name_is_gmalloced
== TRUE
)
341 xmlFree(description
);
343 for (node
= contents
->xmlChildrenNode
; node
!= NULL
; node
= node
->next
) {
344 SheetObject
*sheet_obj
;
345 DiaObjectType
*otype
;
346 gchar
*iconname
= NULL
;
348 int subdesc_score
= -1;
349 gchar
*objdesc
= NULL
;
352 gchar
*chardata
= NULL
;
354 gboolean has_intdata
= FALSE
;
355 gboolean has_icon_on_sheet
= FALSE
;
357 if (xmlIsBlankNode(node
)) continue;
359 if (node
->type
!= XML_ELEMENT_NODE
)
361 if (node
->ns
!= ns
) continue;
362 if (!strcmp(node
->name
,"object")) {
364 } else if (!strcmp(node
->name
,"shape")) {
365 g_message("%s: you should use object tags rather than shape tags now",
367 } else if (!strcmp(node
->name
,"br")) {
368 /* Line break tag. */
369 set_line_break
= TRUE
;
372 continue; /* unknown tag */
374 tmp
= xmlGetProp(node
,"intdata");
377 intdata
= (gint
)strtol(tmp
,&p
,0);
378 if (*p
!= 0) intdata
= 0;
382 chardata
= xmlGetProp(node
,"chardata");
384 if (chardata
) xmlFree(chardata
);
386 for (subnode
= node
->xmlChildrenNode
;
388 subnode
= subnode
->next
) {
389 if (xmlIsBlankNode(subnode
)) continue;
391 if (subnode
->ns
== ns
&& !strcmp(subnode
->name
, "description")) {
394 /* compare the xml:lang property on this element to see if we get a
395 * better language match. LibXML seems to throw away attribute
396 * namespaces, so we use "lang" instead of "xml:lang" */
398 tmp
= xmlGetProp(subnode
, "xml:lang");
399 if (!tmp
) tmp
= xmlGetProp(subnode
, "lang");
400 score
= intl_score_locale(tmp
);
401 if (tmp
) xmlFree(tmp
);
403 if (subdesc_score
< 0 || score
< subdesc_score
) {
404 subdesc_score
= score
;
405 if (objdesc
) xmlFree(objdesc
);
406 objdesc
= xmlNodeGetContent(subnode
);
409 } else if (subnode
->ns
== ns
&& !strcmp(subnode
->name
,"icon")) {
410 tmp
= xmlNodeGetContent(subnode
);
411 iconname
= g_strconcat(dirname
,G_DIR_SEPARATOR_S
,tmp
,NULL
);
412 has_icon_on_sheet
= TRUE
;
413 if (tmp
) xmlFree(tmp
);
417 tmp
= xmlGetProp(node
,"name");
419 sheet_obj
= g_new(SheetObject
,1);
420 sheet_obj
->object_type
= g_strdup(tmp
);
421 sheet_obj
->description
= g_strdup(objdesc
);
422 xmlFree(objdesc
); objdesc
= NULL
;
424 sheet_obj
->pixmap
= NULL
;
425 sheet_obj
->user_data
= (void *)intdata
; /* XXX modify user_data type ? */
426 sheet_obj
->user_data_type
= has_intdata
? USER_DATA_IS_INTDATA
/* sure, */
427 : USER_DATA_IS_OTHER
; /* why not */
428 sheet_obj
->pixmap_file
= iconname
;
429 sheet_obj
->has_icon_on_sheet
= has_icon_on_sheet
;
430 sheet_obj
->line_break
= set_line_break
;
431 set_line_break
= FALSE
;
433 if ((otype
= object_get_type(tmp
)) == NULL
) {
434 /* Don't complain. This does happen when disabling plug-ins too.
435 g_warning("object_get_type(%s) returned NULL", tmp); */
436 if (sheet_obj
->description
) g_free(sheet_obj
->description
);
437 g_free(sheet_obj
->pixmap_file
);
438 g_free(sheet_obj
->object_type
);
440 if (tmp
) xmlFree(tmp
);
445 if (sheet_obj
->pixmap_file
== NULL
) {
446 g_assert(otype
->pixmap
|| otype
->pixmap_file
);
447 sheet_obj
->pixmap
= otype
->pixmap
;
448 sheet_obj
->pixmap_file
= otype
->pixmap_file
;
449 sheet_obj
->has_icon_on_sheet
= has_icon_on_sheet
;
451 if (sheet_obj
->user_data
== NULL
452 && sheet_obj
->user_data_type
!= USER_DATA_IS_INTDATA
)
453 sheet_obj
->user_data
= otype
->default_user_data
;
455 sheet_obj
->user_data_type
= USER_DATA_IS_INTDATA
;
457 if (tmp
) xmlFree(tmp
);
459 /* we don't need to fix up the icon and descriptions for simple objects,
460 since they don't have their own description, and their icon is
461 already automatically handled. */
462 sheet_append_sheet_obj(sheet
,sheet_obj
);
465 if (!shadowing_sheet
)
466 register_sheet(sheet
);