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
,
111 dia_sheet_sort_callback(gconstpointer a
, gconstpointer b
)
113 return strcmp(((Sheet
*)(a
))->name
, ((Sheet
*)(b
))->name
);
117 dia_sort_sheets(void)
119 sheets
= g_slist_sort(sheets
, dia_sheet_sort_callback
);
122 void load_all_sheets(void) {
126 home_dir
= dia_config_filename("sheets");
128 load_sheets_from_dir(home_dir
, SHEET_SCOPE_USER
);
132 sheet_path
= getenv("DIA_SHEET_PATH");
134 char **dirs
= g_strsplit(sheet_path
,G_SEARCHPATH_SEPARATOR_S
,0);
137 for (i
=0; dirs
[i
] != NULL
; i
++)
138 load_sheets_from_dir(dirs
[i
], SHEET_SCOPE_SYSTEM
);
141 char *thedir
= dia_get_data_directory("sheets");
142 load_sheets_from_dir(thedir
, SHEET_SCOPE_SYSTEM
);
146 /* Sorting their sheets alphabetically makes user merging easier */
152 load_sheets_from_dir(const gchar
*directory
, SheetScope scope
)
158 dp
= g_dir_open(directory
, 0, NULL
);
161 while ( (dentry
= g_dir_read_name(dp
)) ) {
162 gchar
*filename
= g_strconcat(directory
,G_DIR_SEPARATOR_S
,
165 if (!g_file_test(filename
, G_FILE_TEST_IS_REGULAR
)) {
170 /* take only .sheet files */
171 p
= filename
+ strlen(filename
) - 6 /* strlen(".sheet") */;
172 if (0!=strncmp(p
,".sheet",6)) {
177 load_register_sheet(directory
, filename
, scope
);
186 load_register_sheet(const gchar
*dirname
, const gchar
*filename
,
191 xmlNodePtr node
, contents
,subnode
,root
;
193 gchar
*name
= NULL
, *description
= NULL
;
195 int descr_score
= -1;
198 gboolean set_line_break
= FALSE
;
199 gboolean name_is_gmalloced
= FALSE
;
200 Sheet
*shadowing
= NULL
;
201 Sheet
*shadowing_sheet
= NULL
;
203 /* the XML fun begins here. */
205 doc
= xmlDoParseFile(filename
);
207 root
= doc
->xmlRootNode
;
208 while (root
&& (root
->type
!= XML_ELEMENT_NODE
)) root
=root
->next
;
210 if (xmlIsBlankNode(root
)) return;
212 if (!(ns
= xmlSearchNsByHref(doc
,root
,
213 DIA_XML_NAME_SPACE_BASE
"dia-sheet-ns"))) {
214 g_warning("could not find sheet namespace");
218 if ((root
->ns
!= ns
) || (strcmp(root
->name
,"sheet"))) {
219 g_warning("root element was %s -- expecting sheet",
220 doc
->xmlRootNode
->name
);
226 for (node
= root
->xmlChildrenNode
; node
!= NULL
; node
= node
->next
) {
227 if (xmlIsBlankNode(node
)) continue;
228 if (node
->type
!= XML_ELEMENT_NODE
)
231 if (node
->ns
== ns
&& !strcmp(node
->name
, "name")) {
234 /* compare the xml:lang property on this element to see if we get a
235 * better language match. LibXML seems to throw away attribute
236 * namespaces, so we use "lang" instead of "xml:lang" */
237 tmp
= xmlGetProp(node
, "xml:lang");
238 if (!tmp
) tmp
= xmlGetProp(node
, "lang");
239 score
= intl_score_locale(tmp
);
240 if (tmp
) xmlFree(tmp
);
242 if (name_score
< 0 || score
< name_score
) {
244 if (name
) xmlFree(name
);
245 name
= xmlNodeGetContent(node
);
247 } else if (node
->ns
== ns
&& !strcmp(node
->name
, "description")) {
250 /* compare the xml:lang property on this element to see if we get a
251 * better language match. LibXML seems to throw away attribute
252 * namespaces, so we use "lang" instead of "xml:lang" */
253 tmp
= xmlGetProp(node
, "xml:lang");
254 if (!tmp
) tmp
= xmlGetProp(node
, "lang");
255 score
= intl_score_locale(tmp
);
256 if (tmp
) xmlFree(tmp
);
258 if (descr_score
< 0 || score
< descr_score
) {
260 if (description
) xmlFree(description
);
261 description
= xmlNodeGetContent(node
);
264 } else if (node
->ns
== ns
&& !strcmp(node
->name
, "contents")) {
269 if (!name
|| !contents
) {
270 g_warning("No <name> and/or <contents> in sheet %s--skipping", filename
);
272 if (name
) xmlFree(name
);
273 if (description
) xmlFree(description
);
277 /* Notify the user when we load a sheet that appears to be an updated
278 version of a sheet loaded previously (i.e. from ~/.dia/sheets). */
280 sheetp
= get_sheets_list();
283 if (sheetp
->data
&& !strcmp(((Sheet
*)(sheetp
->data
))->name
, name
))
285 struct stat first_file
, this_file
;
288 stat_ret
= stat(((Sheet
*)(sheetp
->data
))->filename
, &first_file
);
291 stat_ret
= stat(filename
, &this_file
);
294 if (this_file
.st_mtime
> first_file
.st_mtime
)
296 gchar
*tmp
= g_strdup_printf("%s [Copy of system]", name
);
297 message_notice("The system sheet '%s' appears to be more recent"
298 " than your custom\n"
299 "version and has been loaded as '%s' for this session."
301 "Move new objects (if any) from '%s' into your custom"
303 "or remove '%s', using the 'Sheets and Objects' dialog.",
304 name
, tmp
, tmp
, tmp
);
307 name_is_gmalloced
= TRUE
;
308 shadowing
= sheetp
->data
; /* This copy-of-system sheet shadows
313 /* The already-created user sheet shadows this sheet (which will be
314 invisible), but we don't know this sheet's address yet */
315 shadowing_sheet
= sheetp
->data
;
318 sheetp
= g_slist_next(sheetp
);
321 sheet
= new_sheet(name
, description
, g_strdup(filename
), scope
, shadowing
);
324 shadowing_sheet
->shadowing
= sheet
; /* Hilarious :-) */
326 if (name_is_gmalloced
== TRUE
)
330 xmlFree(description
);
332 for (node
= contents
->xmlChildrenNode
; node
!= NULL
; node
= node
->next
) {
333 SheetObject
*sheet_obj
;
334 DiaObjectType
*otype
;
335 gchar
*iconname
= NULL
;
337 int subdesc_score
= -1;
338 gchar
*objdesc
= NULL
;
341 gchar
*chardata
= NULL
;
343 gboolean has_intdata
= FALSE
;
344 gboolean has_icon_on_sheet
= FALSE
;
346 if (xmlIsBlankNode(node
)) continue;
348 if (node
->type
!= XML_ELEMENT_NODE
)
350 if (node
->ns
!= ns
) continue;
351 if (!strcmp(node
->name
,"object")) {
353 } else if (!strcmp(node
->name
,"shape")) {
354 g_message("%s: you should use object tags rather than shape tags now",
356 } else if (!strcmp(node
->name
,"br")) {
357 /* Line break tag. */
358 set_line_break
= TRUE
;
361 continue; /* unknown tag */
363 tmp
= xmlGetProp(node
,"intdata");
366 intdata
= (gint
)strtol(tmp
,&p
,0);
367 if (*p
!= 0) intdata
= 0;
371 chardata
= xmlGetProp(node
,"chardata");
373 if (chardata
) xmlFree(chardata
);
375 for (subnode
= node
->xmlChildrenNode
;
377 subnode
= subnode
->next
) {
378 if (xmlIsBlankNode(subnode
)) continue;
380 if (subnode
->ns
== ns
&& !strcmp(subnode
->name
, "description")) {
383 /* compare the xml:lang property on this element to see if we get a
384 * better language match. LibXML seems to throw away attribute
385 * namespaces, so we use "lang" instead of "xml:lang" */
387 tmp
= xmlGetProp(subnode
, "xml:lang");
388 if (!tmp
) tmp
= xmlGetProp(subnode
, "lang");
389 score
= intl_score_locale(tmp
);
390 if (tmp
) xmlFree(tmp
);
392 if (subdesc_score
< 0 || score
< subdesc_score
) {
393 subdesc_score
= score
;
394 if (objdesc
) xmlFree(objdesc
);
395 objdesc
= xmlNodeGetContent(subnode
);
398 } else if (subnode
->ns
== ns
&& !strcmp(subnode
->name
,"icon")) {
399 tmp
= xmlNodeGetContent(subnode
);
400 iconname
= g_strconcat(dirname
,G_DIR_SEPARATOR_S
,tmp
,NULL
);
401 has_icon_on_sheet
= TRUE
;
402 if (tmp
) xmlFree(tmp
);
406 tmp
= xmlGetProp(node
,"name");
408 sheet_obj
= g_new(SheetObject
,1);
409 sheet_obj
->object_type
= g_strdup(tmp
);
410 sheet_obj
->description
= g_strdup(objdesc
);
411 xmlFree(objdesc
); objdesc
= NULL
;
413 sheet_obj
->pixmap
= NULL
;
414 sheet_obj
->user_data
= (void *)intdata
; /* XXX modify user_data type ? */
415 sheet_obj
->user_data_type
= has_intdata
? USER_DATA_IS_INTDATA
/* sure, */
416 : USER_DATA_IS_OTHER
; /* why not */
417 sheet_obj
->pixmap_file
= iconname
;
418 sheet_obj
->has_icon_on_sheet
= has_icon_on_sheet
;
419 sheet_obj
->line_break
= set_line_break
;
420 set_line_break
= FALSE
;
422 if ((otype
= object_get_type(tmp
)) == NULL
) {
423 /* Don't complain. This does happen when disabling plug-ins too.
424 g_warning("object_get_type(%s) returned NULL", tmp); */
425 if (sheet_obj
->description
) g_free(sheet_obj
->description
);
426 g_free(sheet_obj
->pixmap_file
);
427 g_free(sheet_obj
->object_type
);
429 if (tmp
) xmlFree(tmp
);
434 if (sheet_obj
->pixmap_file
== NULL
) {
435 g_assert(otype
->pixmap
|| otype
->pixmap_file
);
436 sheet_obj
->pixmap
= otype
->pixmap
;
437 sheet_obj
->pixmap_file
= otype
->pixmap_file
;
438 sheet_obj
->has_icon_on_sheet
= has_icon_on_sheet
;
440 if (sheet_obj
->user_data
== NULL
441 && sheet_obj
->user_data_type
!= USER_DATA_IS_INTDATA
)
442 sheet_obj
->user_data
= otype
->default_user_data
;
444 sheet_obj
->user_data_type
= USER_DATA_IS_INTDATA
;
446 if (tmp
) xmlFree(tmp
);
448 /* we don't need to fix up the icon and descriptions for simple objects,
449 since they don't have their own description, and their icon is
450 already automatically handled. */
451 sheet_append_sheet_obj(sheet
,sheet_obj
);
454 if (!shadowing_sheet
)
455 register_sheet(sheet
);