]> git.sur5r.net Git - glabels/blob - glabels2/src/template.c
When creating origins for labels, y is now sorted in reverse.
[glabels] / glabels2 / src / template.c
1 /*
2  *  (GLABELS) Label and Business Card Creation program for GNOME
3  *
4  *  template.c:  template module
5  *
6  *  Copyright (C) 2001-2002  Jim Evins <evins@snaught.com>.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21  */
22
23 #include <config.h>
24
25 #include <string.h>
26 #include <libgnomeprint/gnome-print-paper.h>
27
28 #include "prefs.h"
29 #include "util.h"
30 #include "template.h"
31
32 #include "debug.h"
33
34 #define GL_DATA_DIR gnome_program_locate_file (NULL,\
35                                          GNOME_FILE_DOMAIN_APP_DATADIR,\
36                                          "glabels",\
37                                          FALSE, NULL)
38
39 #define FULL_PAGE "Full-page"
40
41 /*===========================================*/
42 /* Private types                             */
43 /*===========================================*/
44
45 /*===========================================*/
46 /* Private globals                           */
47 /*===========================================*/
48
49 static GList *templates = NULL;
50
51 /*===========================================*/
52 /* Local function prototypes                 */
53 /*===========================================*/
54 static glTemplate *template_full_page           (const gchar *page_size);
55
56 static GList      *read_templates               (void);
57
58 static gchar      *get_home_data_dir            (void);
59 static GList      *read_template_files_from_dir (GList *templates,
60                                                  const gchar *dirname);
61 static GList      *read_templates_from_file     (GList *templates,
62                                                  gchar *xml_filename);
63
64 static void        xml_parse_label              (xmlNodePtr label_node,
65                                                  glTemplate *template);
66 static void        xml_parse_layout             (xmlNodePtr layout_node,
67                                                  glTemplate *template);
68 static void        xml_parse_markup             (xmlNodePtr markup_node,
69                                                  glTemplate *template);
70 static void        xml_parse_alias              (xmlNodePtr alias_node,
71                                                  glTemplate *template);
72
73 static void        xml_add_label                (const glTemplate *template,
74                                                  xmlNodePtr root,
75                                                  xmlNsPtr ns);
76 static void        xml_add_layout               (glTemplateLayout *layout,
77                                                  xmlNodePtr root,
78                                                  xmlNsPtr ns);
79 static void        xml_add_markup_margin        (glTemplateMarkupMargin *margin,
80                                                  xmlNodePtr root,
81                                                  xmlNsPtr ns);
82 static void        xml_add_alias                (gchar *name,
83                                                  xmlNodePtr root,
84                                                  xmlNsPtr ns);
85
86 static gint compare_origins (gconstpointer a,
87                              gconstpointer b,
88                              gpointer user_data);
89
90 static glTemplateLayout *layout_new  (gdouble nx,
91                                       gdouble ny,
92                                       gdouble x0,
93                                       gdouble y0,
94                                       gdouble dx,
95                                       gdouble dy);
96 static glTemplateLayout *layout_dup  (glTemplateLayout *orig_layout);
97 static void              layout_free (glTemplateLayout **layout);
98
99 static glTemplateMarkup *markup_margin_new  (gdouble size);
100 static glTemplateMarkup *markup_dup         (glTemplateMarkup *orig_markup);
101 static void              markup_free        (glTemplateMarkup **markup);
102
103 /*****************************************************************************/
104 /* Initialize module.                                                        */
105 /*****************************************************************************/
106 void
107 gl_template_init (void)
108 {
109         GList *page_sizes, *p;
110
111         gl_debug (DEBUG_TEMPLATE, "START");
112
113         templates = read_templates ();
114
115         page_sizes = gl_template_get_page_size_list ();
116         for ( p=page_sizes; p != NULL; p=p->next ) {
117                 templates = g_list_append (templates,
118                                            template_full_page (p->data));
119         }
120         gl_template_free_page_size_list (&page_sizes);
121
122         gl_debug (DEBUG_TEMPLATE, "END");
123 }
124
125 /*****************************************************************************/
126 /* Get a list of valid page size names                                       */
127 /*****************************************************************************/
128 GList *
129 gl_template_get_page_size_list (void)
130 {
131         GList *names = NULL;
132         GList *p, *paper_list;
133         GnomePrintPaper *paper;
134
135         gl_debug (DEBUG_TEMPLATE, "START");
136
137         paper_list = gnome_print_paper_get_list();
138         for ( p=paper_list; p != NULL; p=p->next ) {
139                 paper = (GnomePrintPaper *)p->data;
140                 if ( g_strcasecmp(paper->name, "custom") != 0 ) {
141                         names = g_list_append (names, g_strdup (paper->name));
142                 }
143         }
144
145         gl_debug (DEBUG_TEMPLATE, "END");
146         return names;
147 }
148
149 /*****************************************************************************/
150 /* Free a list of page size names.                                           */
151 /*****************************************************************************/
152 void
153 gl_template_free_page_size_list (GList ** names)
154 {
155         GList *p_name;
156
157         gl_debug (DEBUG_TEMPLATE, "START");
158
159         for (p_name = *names; p_name != NULL; p_name = p_name->next) {
160                 g_free (p_name->data);
161                 p_name->data = NULL;
162         }
163
164         g_list_free (*names);
165         *names = NULL;
166
167         gl_debug (DEBUG_TEMPLATE, "END");
168 }
169
170 /*****************************************************************************/
171 /* Get a list of valid template names for given page size                    */
172 /*****************************************************************************/
173 GList *
174 gl_template_get_name_list (const gchar * page_size)
175 {
176         GList *p_tmplt, *p_name;
177         glTemplate *template;
178         gchar *str;
179         GList *names = NULL;
180
181         gl_debug (DEBUG_TEMPLATE, "START");
182
183         for (p_tmplt = templates; p_tmplt != NULL; p_tmplt = p_tmplt->next) {
184                 template = (glTemplate *) p_tmplt->data;
185                 if (g_strcasecmp (page_size, template->page_size) == 0) {
186                         for (p_name = template->name; p_name != NULL;
187                              p_name = p_name->next) {
188                                 str = g_strdup_printf("%s: %s",
189                                                       (gchar *) p_name->data,
190                                                       template->description);
191                                 names = g_list_insert_sorted (names, str,
192                                                              (GCompareFunc)g_strcasecmp);
193                         }
194                 }
195         }
196
197         gl_debug (DEBUG_TEMPLATE, "templates = %p", templates);
198         gl_debug (DEBUG_TEMPLATE, "names = %p", names);
199
200         gl_debug (DEBUG_TEMPLATE, "END");
201         return names;
202 }
203
204 /*****************************************************************************/
205 /* Free a list of template names.                                            */
206 /*****************************************************************************/
207 void
208 gl_template_free_name_list (GList ** names)
209 {
210         GList *p_name;
211
212         gl_debug (DEBUG_TEMPLATE, "START");
213
214         for (p_name = *names; p_name != NULL; p_name = p_name->next) {
215                 g_free (p_name->data);
216                 p_name->data = NULL;
217         }
218
219         g_list_free (*names);
220         *names = NULL;
221
222         gl_debug (DEBUG_TEMPLATE, "END");
223 }
224
225 /*****************************************************************************/
226 /* Return a template structure from a name.                                  */
227 /*****************************************************************************/
228 glTemplate *
229 gl_template_from_name (const gchar * name)
230 {
231         GList *p_tmplt, *p_name;
232         glTemplate *template;
233         gchar **split_name;
234
235         gl_debug (DEBUG_TEMPLATE, "START");
236
237         if (name == NULL) {
238                 /* If no name, return first template as a default */
239                 return (glTemplate *) templates->data;
240         }
241
242         split_name = g_strsplit (name, ":", 2);
243
244         for (p_tmplt = templates; p_tmplt != NULL; p_tmplt = p_tmplt->next) {
245                 template = (glTemplate *) p_tmplt->data;
246                 for (p_name = template->name; p_name != NULL;
247                      p_name = p_name->next) {
248                         if (g_strcasecmp (p_name->data, split_name[0]) == 0) {
249                                 g_strfreev (split_name);
250                                 gl_debug (DEBUG_TEMPLATE, "END");
251                                 return gl_template_dup (template);
252                         }
253                 }
254         }
255
256         g_strfreev (split_name);
257
258         gl_debug (DEBUG_TEMPLATE, "END");
259         return NULL;
260 }
261
262 /*****************************************************************************/
263 /* Copy a template.                                                          */
264 /*****************************************************************************/
265 glTemplate *gl_template_dup (const glTemplate *orig_template)
266 {
267         glTemplate       *template;
268         GList            *p;
269         glTemplateLayout *layout;
270         glTemplateMarkup *markup;
271
272         gl_debug (DEBUG_TEMPLATE, "START");
273
274         template = g_new0 (glTemplate,1);
275
276         template->name = NULL;
277         for ( p=orig_template->name; p != NULL; p=p->next ) {
278                 template->name = g_list_append (template->name,
279                                                 g_strdup (p->data));
280         }
281         template->description = g_strdup (orig_template->description);
282         template->page_size   = g_strdup (orig_template->page_size);
283
284         template->label       = orig_template->label;
285
286         template->label.any.layouts = NULL;
287         for ( p=orig_template->label.any.layouts; p != NULL; p=p->next ) {
288                 layout = (glTemplateLayout *)p->data;
289                 template->label.any.layouts =
290                         g_list_append (template->label.any.layouts,
291                                        layout_dup (layout));
292         }
293
294         template->label.any.markups = NULL;
295         for ( p=orig_template->label.any.markups; p != NULL; p=p->next ) {
296                 markup = (glTemplateMarkup *)p->data;
297                 template->label.any.markups =
298                         g_list_append (template->label.any.markups,
299                                        markup_dup (markup));
300         }
301
302         gl_debug (DEBUG_TEMPLATE, "END");
303         return template;
304 }
305
306 /*****************************************************************************/
307 /* Free up a template.                                                       */
308 /*****************************************************************************/
309 void gl_template_free (glTemplate **template)
310 {
311         GList *p;
312
313         gl_debug (DEBUG_TEMPLATE, "START");
314
315         if ( *template != NULL ) {
316
317                 for ( p=(*template)->name; p != NULL; p=p->next ) {
318                         g_free (p->data);
319                         p->data = NULL;
320                 }
321                 g_list_free ((*template)->name);
322                 (*template)->name = NULL;
323
324                 g_free ((*template)->description);
325                 (*template)->description = NULL;
326
327                 g_free ((*template)->page_size);
328                 (*template)->page_size = NULL;
329
330                 for ( p=(*template)->label.any.layouts; p != NULL; p=p->next ) {
331                         layout_free ((glTemplateLayout **)&p->data);
332                 }
333                 g_list_free ((*template)->label.any.layouts);
334                 (*template)->label.any.layouts = NULL;
335
336                 for ( p=(*template)->label.any.markups; p != NULL; p=p->next ) {
337                         markup_free ((glTemplateMarkup **)&p->data);
338                 }
339                 g_list_free ((*template)->label.any.markups);
340                 (*template)->label.any.markups = NULL;
341
342                 g_free (*template);
343                 *template = NULL;
344
345         }
346
347         gl_debug (DEBUG_TEMPLATE, "END");
348 }
349
350 /*--------------------------------------------------------------------------*/
351 /* PRIVATE.  Make a template for a full page of the given page size.        */
352 /*--------------------------------------------------------------------------*/
353 static glTemplate *
354 template_full_page (const gchar *page_size)
355 {
356         const GnomePrintPaper *paper;
357         glTemplate *template;
358
359         paper = gnome_print_paper_get_by_name (page_size);
360         if ( paper == NULL ) {
361                 return NULL;
362         }
363
364         template = g_new0 (glTemplate, 1);
365
366         template->name         = g_list_append (template->name,
367                                          g_strdup_printf("*%s", page_size));
368         template->page_size    = g_strdup(page_size);
369         template->description  = g_strdup(FULL_PAGE);
370
371         template->label.style  = GL_TEMPLATE_STYLE_RECT;
372         template->label.rect.w = paper->width;
373         template->label.rect.h = paper->height;
374         template->label.rect.r = 0.0;
375
376         template->label.any.layouts =
377                 g_list_append (template->label.any.layouts,
378                                layout_new (1, 1, 0., 0., 0., 0.));
379
380         template->label.any.markups =
381                 g_list_append (template->label.any.markups,
382                                markup_margin_new (5.0));
383
384         return template;
385 }
386
387 /*--------------------------------------------------------------------------*/
388 /* PRIVATE.  Read templates from various  files.                            */
389 /*--------------------------------------------------------------------------*/
390 static GList *
391 read_templates (void)
392 {
393         gchar *home_data_dir = get_home_data_dir ();
394         GList *templates = NULL;
395
396         gl_debug (DEBUG_TEMPLATE, "START");
397
398         LIBXML_TEST_VERSION;
399
400         templates = read_template_files_from_dir (templates, GL_DATA_DIR);
401         templates = read_template_files_from_dir (templates, home_data_dir);
402
403         g_free (home_data_dir);
404
405         if (templates == NULL) {
406                 g_warning (_("No template files found!"));
407         }
408
409         gl_debug (DEBUG_TEMPLATE, "END");
410         return templates;
411 }
412
413 /*--------------------------------------------------------------------------*/
414 /* PRIVATE.  get '~/.glabels' directory path.                               */
415 /*--------------------------------------------------------------------------*/
416 static gchar *
417 get_home_data_dir (void)
418 {
419         gchar *dir = gnome_util_prepend_user_home (".glabels");
420
421         gl_debug (DEBUG_TEMPLATE, "START");
422
423         /* Try to create ~/.glabels directory.  If it exists, no problem. */
424         mkdir (dir, 0775);
425
426         gl_debug (DEBUG_TEMPLATE, "END");
427         return dir;
428 }
429
430 /*--------------------------------------------------------------------------*/
431 /* PRIVATE.  Read all template files from given directory.  Append to list. */
432 /*--------------------------------------------------------------------------*/
433 static GList *
434 read_template_files_from_dir (GList * templates,
435                               const gchar * dirname)
436 {
437         GDir *dp;
438         const gchar *filename, *extension;
439         gchar *full_filename = NULL;
440         GError *gerror = NULL;
441
442         gl_debug (DEBUG_TEMPLATE, "START");
443
444         if (dirname == NULL)
445                 return templates;
446
447         dp = g_dir_open (dirname, 0, &gerror);
448         if (gerror != NULL) {
449                 g_warning ("cannot open data directory: %s", gerror->message );
450                 gl_debug (DEBUG_TEMPLATE, "END");
451                 return templates;
452         }
453
454         while ((filename = g_dir_read_name (dp)) != NULL) {
455
456                 extension = strrchr (filename, '.');
457
458                 if (extension != NULL) {
459
460                         if (strcasecmp (extension, ".template") == 0) {
461
462                                 full_filename =
463                                     g_build_filename (dirname, filename, NULL);
464                                 templates =
465                                     read_templates_from_file (templates,
466                                                               full_filename);
467                                 g_free (full_filename);
468
469                         }
470
471                 }
472
473         }
474
475         g_dir_close (dp);
476
477         gl_debug (DEBUG_TEMPLATE, "END");
478         return templates;
479 }
480
481 /*--------------------------------------------------------------------------*/
482 /* PRIVATE.  Read templates from template file.                             */
483 /*--------------------------------------------------------------------------*/
484 static GList *
485 read_templates_from_file (GList * templates,
486                           gchar * xml_filename)
487 {
488         xmlDocPtr doc;
489         xmlNodePtr root, node;
490         glTemplate *template;
491
492         gl_debug (DEBUG_TEMPLATE, "START");
493
494         doc = xmlParseFile (xml_filename);
495         if (!doc) {
496                 g_warning ("\"%s\" is not a glabels template file (not XML)",
497                       xml_filename);
498                 return templates;
499         }
500
501         root = xmlDocGetRootElement (doc);
502         if (!root || !root->name) {
503                 g_warning ("\"%s\" is not a glabels template file (no root node)",
504                       xml_filename);
505                 xmlFreeDoc (doc);
506                 return templates;
507         }
508         if (g_strcasecmp (root->name, "glabels-templates") != 0) {
509                 g_warning ("\"%s\" is not a glabels template file (wrong root node)",
510                       xml_filename);
511                 xmlFreeDoc (doc);
512                 return templates;
513         }
514
515         for (node = root->xmlChildrenNode; node != NULL; node = node->next) {
516
517                 if (g_strcasecmp (node->name, "Sheet") == 0) {
518                         template = gl_template_xml_parse_sheet (node);
519                         templates = g_list_append (templates, template);
520                 } else {
521                         if ( !xmlNodeIsText(node) ) {
522                                 if (g_strcasecmp (node->name,"comment") != 0) {
523                                         g_warning ("bad node =  \"%s\"",node->name);
524                                 }
525                         }
526                 }
527         }
528
529         xmlFreeDoc (doc);
530
531         gl_debug (DEBUG_TEMPLATE, "END");
532         return templates;
533 }
534
535 /*****************************************************************************/
536 /* Parse XML template Node.                                                  */
537 /*****************************************************************************/
538 glTemplate *
539 gl_template_xml_parse_sheet (xmlNodePtr sheet_node)
540 {
541         glTemplate *template;
542         xmlNodePtr node;
543
544         gl_debug (DEBUG_TEMPLATE, "START");
545
546         template = g_new0 (glTemplate, 1);
547
548         template->name = g_list_append (template->name,
549                                         xmlGetProp (sheet_node, "name"));
550         template->page_size = xmlGetProp (sheet_node, "size");
551         if ( strcmp (template->page_size,"US-Letter") == 0 ) {
552                 /* Compatibility with old pre-1.0 template files.*/
553                 template->page_size = "US Letter";
554         }
555         template->description = xmlGetProp (sheet_node, "description");
556
557         for (node = sheet_node->xmlChildrenNode; node != NULL;
558              node = node->next) {
559                 if (g_strcasecmp (node->name, "Label") == 0) {
560                         xml_parse_label (node, template);
561                 } else if (g_strcasecmp (node->name, "Alias") == 0) {
562                         xml_parse_alias (node, template);
563                 } else {
564                         if (g_strcasecmp (node->name, "text") != 0) {
565                                 g_warning ("bad node =  \"%s\"", node->name);
566                         }
567                 }
568         }
569
570         gl_debug (DEBUG_TEMPLATE, "END");
571
572         return template;
573 }
574
575 /*--------------------------------------------------------------------------*/
576 /* PRIVATE.  Parse XML Sheet->Label Node.                                   */
577 /*--------------------------------------------------------------------------*/
578 static void
579 xml_parse_label (xmlNodePtr label_node,
580                  glTemplate * template)
581 {
582         xmlNodePtr node;
583         gchar *style;
584
585         gl_debug (DEBUG_TEMPLATE, "START");
586
587         style = xmlGetProp (label_node, "style");
588         if (g_strcasecmp (style, "rectangle") == 0) {
589                 template->label.style = GL_TEMPLATE_STYLE_RECT;
590         } else if (g_strcasecmp (style, "round") == 0) {
591                 template->label.style = GL_TEMPLATE_STYLE_ROUND;
592         } else if (g_strcasecmp (style, "cd") == 0) {
593                 template->label.style = GL_TEMPLATE_STYLE_CD;
594         } else {
595                 template->label.style = GL_TEMPLATE_STYLE_RECT;
596                 g_warning ("Unknown label style in template");
597         }
598
599         switch (template->label.style) {
600         case GL_TEMPLATE_STYLE_RECT:
601                 template->label.rect.w =
602                     g_strtod (xmlGetProp (label_node, "width"), NULL);
603                 template->label.rect.h =
604                     g_strtod (xmlGetProp (label_node, "height"), NULL);
605                 template->label.rect.r =
606                     g_strtod (xmlGetProp (label_node, "round"), NULL);
607                 break;
608         case GL_TEMPLATE_STYLE_ROUND:
609                 template->label.round.r =
610                     g_strtod (xmlGetProp (label_node, "radius"), NULL);
611                 break;
612         case GL_TEMPLATE_STYLE_CD:
613                 template->label.cd.r1 =
614                     g_strtod (xmlGetProp (label_node, "radius"), NULL);
615                 template->label.cd.r2 =
616                     g_strtod (xmlGetProp (label_node, "hole"), NULL);
617                 break;
618         default:
619                 break;
620         }
621
622         for (node = label_node->xmlChildrenNode; node != NULL;
623              node = node->next) {
624                 if (g_strcasecmp (node->name, "Layout") == 0) {
625                         xml_parse_layout (node, template);
626                 } else if (g_strcasecmp (node->name, "Markup") == 0) {
627                         xml_parse_markup (node, template);
628                 } else if (g_strcasecmp (node->name, "text") != 0) {
629                         g_warning ("bad node =  \"%s\"", node->name);
630                 }
631         }
632
633         gl_debug (DEBUG_TEMPLATE, "END");
634 }
635
636 /*--------------------------------------------------------------------------*/
637 /* PRIVATE.  Parse XML Sheet->Label->Layout Node.                           */
638 /*--------------------------------------------------------------------------*/
639 static void
640 xml_parse_layout (xmlNodePtr layout_node,
641                   glTemplate * template)
642 {
643         gint nx, ny;
644         gdouble x0, y0, dx, dy;
645         xmlNodePtr node;
646
647         gl_debug (DEBUG_TEMPLATE, "START");
648
649         sscanf (xmlGetProp (layout_node, "nx"), "%d", &nx);
650         sscanf (xmlGetProp (layout_node, "ny"), "%d", &ny);
651         x0 = g_strtod (xmlGetProp (layout_node, "x0"), NULL);
652         y0 = g_strtod (xmlGetProp (layout_node, "y0"), NULL);
653         dx = g_strtod (xmlGetProp (layout_node, "dx"), NULL);
654         dy = g_strtod (xmlGetProp (layout_node, "dy"), NULL);
655
656         for (node = layout_node->xmlChildrenNode; node != NULL;
657              node = node->next) {
658                 if (g_strcasecmp (node->name, "text") != 0) {
659                         g_warning ("bad node =  \"%s\"", node->name);
660                 }
661         }
662
663         template->label.any.layouts =
664                 g_list_append (template->label.any.layouts,
665                                layout_new (nx, ny, x0, y0, dx, dy));
666
667         gl_debug (DEBUG_TEMPLATE, "END");
668 }
669
670 /*--------------------------------------------------------------------------*/
671 /* PRIVATE.  Parse XML Sheet->Label->Markup Node.                           */
672 /*--------------------------------------------------------------------------*/
673 static void
674 xml_parse_markup (xmlNodePtr markup_node,
675                   glTemplate * template)
676 {
677         gchar *type;
678         gdouble size;
679         xmlNodePtr node;
680
681         gl_debug (DEBUG_TEMPLATE, "START");
682
683         type = xmlGetProp (markup_node, "type");
684         if (g_strcasecmp (type, "margin") == 0) {
685                 size = g_strtod (xmlGetProp (markup_node, "size"), NULL);
686                 template->label.any.markups =
687                         g_list_append (template->label.any.markups,
688                                        markup_margin_new (size));
689         }
690
691         for (node = markup_node->xmlChildrenNode; node != NULL;
692              node = node->next) {
693                 if (g_strcasecmp (node->name, "text") != 0) {
694                         g_warning ("bad node =  \"%s\"", node->name);
695                 }
696         }
697
698         gl_debug (DEBUG_TEMPLATE, "END");
699 }
700
701 /*--------------------------------------------------------------------------*/
702 /* PRIVATE.  Parse XML Sheet->Alias Node.                                   */
703 /*--------------------------------------------------------------------------*/
704 static void
705 xml_parse_alias (xmlNodePtr alias_node,
706                  glTemplate * template)
707 {
708         gl_debug (DEBUG_TEMPLATE, "START");
709
710         template->name = g_list_append (template->name,
711                                         xmlGetProp (alias_node, "name"));
712
713         gl_debug (DEBUG_TEMPLATE, "END");
714 }
715
716 /****************************************************************************/
717 /* Add XML Template Node                                                    */
718 /****************************************************************************/
719 void
720 gl_template_xml_add_sheet (const glTemplate * template,
721                            xmlNodePtr root,
722                            xmlNsPtr ns)
723 {
724         xmlNodePtr node;
725         GList *p;
726
727         gl_debug (DEBUG_TEMPLATE, "START");
728
729         node = xmlNewChild (root, ns, "Sheet", NULL);
730
731         xmlSetProp (node, "name", template->name->data);
732         xmlSetProp (node, "size", template->page_size);
733         xmlSetProp (node, "description", template->description);
734
735         xml_add_label (template, node, ns);
736
737         for ( p=template->name->next; p != NULL; p=p->next ) {
738                 xml_add_alias( p->data, node, ns );
739         }
740
741         gl_debug (DEBUG_TEMPLATE, "END");
742 }
743
744 /*--------------------------------------------------------------------------*/
745 /* PRIVATE.  Add XML Sheet->Label Node.                                     */
746 /*--------------------------------------------------------------------------*/
747 static void
748 xml_add_label (const glTemplate *template,
749                xmlNodePtr root,
750                xmlNsPtr ns)
751 {
752         xmlNodePtr node;
753         gchar *string;
754         GList *p;
755         glTemplateMarkup *markup;
756         glTemplateLayout *layout;
757
758         gl_debug (DEBUG_TEMPLATE, "START");
759
760         node = xmlNewChild(root, ns, "Label", NULL);
761
762         xmlSetProp (node, "id", "0");
763
764         switch (template->label.style) {
765         case GL_TEMPLATE_STYLE_RECT:
766                 xmlSetProp (node, "style", "rectangle");
767                 string = g_strdup_printf ("%g", template->label.rect.w);
768                 xmlSetProp (node, "width", string);
769                 g_free (string);
770                 string = g_strdup_printf ("%g", template->label.rect.h);
771                 xmlSetProp (node, "height", string);
772                 g_free (string);
773                 string = g_strdup_printf ("%g", template->label.rect.r);
774                 xmlSetProp (node, "round", string);
775                 g_free (string);
776                 break;
777         case GL_TEMPLATE_STYLE_ROUND:
778                 xmlSetProp (node, "style", "round");
779                 string = g_strdup_printf ("%g", template->label.round.r);
780                 xmlSetProp (node, "radius", string);
781                 g_free (string);
782                 break;
783         case GL_TEMPLATE_STYLE_CD:
784                 xmlSetProp (node, "style", "cd");
785                 string = g_strdup_printf ("%g", template->label.cd.r1);
786                 xmlSetProp (node, "radius", string);
787                 g_free (string);
788                 string = g_strdup_printf ("%g", template->label.cd.r2);
789                 xmlSetProp (node, "hole", string);
790                 g_free (string);
791                 break;
792         default:
793                 g_warning ("Unknown label style");
794                 break;
795         }
796
797         for ( p=template->label.any.markups; p != NULL; p=p->next ) {
798                 markup = (glTemplateMarkup *)p->data;
799                 switch (markup->type) {
800                 case GL_TEMPLATE_MARKUP_MARGIN:
801                         xml_add_markup_margin ((glTemplateMarkupMargin *)markup,
802                                                node, ns);
803                         break;
804                 default:
805                         g_warning ("Unknown markup type");
806                         break;
807                 }
808         }
809
810         for ( p=template->label.any.layouts; p != NULL; p=p->next ) {
811                 layout = (glTemplateLayout *)p->data;
812                 xml_add_layout (layout, node, ns);
813         }
814
815         gl_debug (DEBUG_TEMPLATE, "END");
816 }
817
818 /*--------------------------------------------------------------------------*/
819 /* PRIVATE.  Add XML Sheet->Label->Layout Node.                             */
820 /*--------------------------------------------------------------------------*/
821 static void
822 xml_add_layout (glTemplateLayout *layout,
823                 xmlNodePtr root,
824                 xmlNsPtr ns)
825 {
826         xmlNodePtr node;
827         gchar *string;
828
829         gl_debug (DEBUG_TEMPLATE, "START");
830
831         node = xmlNewChild(root, ns, "Layout", NULL);
832         string = g_strdup_printf ("%d", layout->nx);
833         xmlSetProp (node, "nx", string);
834         g_free (string);
835         string = g_strdup_printf ("%d", layout->ny);
836         xmlSetProp (node, "ny", string);
837         g_free (string);
838         string = g_strdup_printf ("%g", layout->x0);
839         xmlSetProp (node, "x0", string);
840         g_free (string);
841         string = g_strdup_printf ("%g", layout->y0);
842         xmlSetProp (node, "y0", string);
843         g_free (string);
844         string = g_strdup_printf ("%g", layout->dx);
845         xmlSetProp (node, "dx", string);
846         g_free (string);
847         string = g_strdup_printf ("%g", layout->dy);
848         xmlSetProp (node, "dy", string);
849         g_free (string);
850
851         gl_debug (DEBUG_TEMPLATE, "END");
852 }
853
854 /*--------------------------------------------------------------------------*/
855 /* PRIVATE.  Add XML Sheet->Label->Markup Node.                             */
856 /*--------------------------------------------------------------------------*/
857 static void
858 xml_add_markup_margin (glTemplateMarkupMargin *margin,
859                        xmlNodePtr root,
860                        xmlNsPtr ns)
861 {
862         xmlNodePtr node;
863         gchar *string;
864
865         gl_debug (DEBUG_TEMPLATE, "START");
866
867         node = xmlNewChild(root, ns, "Markup", NULL);
868         xmlSetProp (node, "type", "margin");
869
870         string = g_strdup_printf ("%g", margin->size);
871         xmlSetProp (node, "size", string);
872         g_free (string);
873
874         gl_debug (DEBUG_TEMPLATE, "END");
875 }
876
877 /*--------------------------------------------------------------------------*/
878 /* PRIVATE.  Add XML Sheet->Alias Node.                                     */
879 /*--------------------------------------------------------------------------*/
880 static void
881 xml_add_alias (gchar *name,
882                xmlNodePtr root,
883                xmlNsPtr ns)
884 {
885         xmlNodePtr node;
886
887         gl_debug (DEBUG_TEMPLATE, "START");
888
889         node = xmlNewChild (root, ns, "Alias", NULL);
890         xmlSetProp (node, "name", name);
891
892         gl_debug (DEBUG_TEMPLATE, "END");
893 }
894
895 /****************************************************************************/
896 /* Get label size description.                                              */ 
897 /****************************************************************************/
898 gchar *
899 gl_template_get_label_size_desc (const glTemplate *template)
900 {
901         glPrefsUnits units;
902         const gchar *units_string;
903         gdouble units_per_point;
904         gchar *string = NULL;
905
906         units           = gl_prefs_get_units ();
907         units_string    = gl_prefs_get_units_string ();
908         units_per_point = gl_prefs_get_units_per_point ();
909
910         switch (template->label.style) {
911         case GL_TEMPLATE_STYLE_RECT:
912                 if ( units == GL_PREFS_UNITS_INCHES ) {
913                         gchar *xstr, *ystr;
914
915                         xstr = gl_util_fraction (template->label.rect.w * units_per_point);
916                         ystr = gl_util_fraction (template->label.rect.h * units_per_point);
917                         string = g_strdup_printf (_("%s x %s %s"),
918                                                   xstr, ystr, units_string);
919                         g_free (xstr);
920                         g_free (ystr);
921                 } else {
922                         string = g_strdup_printf (_("%.5g x %.5g %s"),
923                                                   template->label.rect.w * units_per_point,
924                                                   template->label.rect.h * units_per_point,
925                                                   units_string);
926                 }
927                 break;
928         case GL_TEMPLATE_STYLE_ROUND:
929                 if ( units == GL_PREFS_UNITS_INCHES ) {
930                         gchar *dstr;
931
932                         dstr = gl_util_fraction (2.0 * template->label.round.r * units_per_point);
933                         string = g_strdup_printf (_("%s %s diameter"),
934                                                   dstr, units_string);
935                         g_free (dstr);
936                 } else {
937                         string = g_strdup_printf (_("%.5g %s diameter"),
938                                                   2.0 * template->label.round.r * units_per_point,
939                                                   units_string);
940                 }
941                 break;
942         case GL_TEMPLATE_STYLE_CD:
943                 if ( units == GL_PREFS_UNITS_INCHES ) {
944                         gchar *dstr;
945
946                         dstr = gl_util_fraction (2.0 * template->label.cd.r1 * units_per_point);
947                         string = g_strdup_printf (_("%s %s diameter"),
948                                                   dstr, units_string);
949                         g_free (dstr);
950                 } else {
951                         string = g_strdup_printf (_("%.5g %s diameter"),
952                                                   2.0 * template->label.cd.r1 * units_per_point,
953                                                   units_string);
954                 }
955                 break;
956         default:
957                 break;
958         }
959
960         return string;
961 }
962
963 /****************************************************************************/
964 /* Get raw label size (width and height).                                   */
965 /****************************************************************************/
966 void
967 gl_template_get_label_size (const glTemplate *template,
968                             gdouble          *w,
969                             gdouble          *h)
970 {
971         switch (template->label.style) {
972         case GL_TEMPLATE_STYLE_RECT:
973                 *w = template->label.rect.w;
974                 *h = template->label.rect.h;
975                 break;
976         case GL_TEMPLATE_STYLE_ROUND:
977                 *w = 2.0 * template->label.round.r;
978                 *h = 2.0 * template->label.round.r;
979                 break;
980         case GL_TEMPLATE_STYLE_CD:
981                 *w = 2.0 * template->label.cd.r1;
982                 *h = 2.0 * template->label.cd.r1;
983                 break;
984         default:
985                 *w = 0.0;
986                 *h = 0.0;
987                 break;
988         }
989 }
990
991 /****************************************************************************/
992 /* Get total number of labels per sheet.                                    */
993 /****************************************************************************/
994 gint
995 gl_template_get_n_labels (const glTemplate *template)
996 {
997         gint n_labels = 0;
998         GList *p;
999         glTemplateLayout *layout;
1000
1001         for ( p=template->label.any.layouts; p != NULL; p=p->next ) {
1002                 layout = (glTemplateLayout *)p->data;
1003
1004                 n_labels += layout->nx * layout->ny;
1005         }
1006
1007         return n_labels;
1008 }
1009
1010 /****************************************************************************/
1011 /* Get array of origins of individual labels.                               */
1012 /****************************************************************************/
1013 glTemplateOrigin *
1014 gl_template_get_origins (const glTemplate *template)
1015 {
1016         gint i_label, n_labels, ix, iy;
1017         glTemplateOrigin *origins;
1018         GList *p;
1019         glTemplateLayout *layout;
1020
1021         n_labels = gl_template_get_n_labels (template);
1022         origins = g_new0 (glTemplateOrigin, n_labels);
1023
1024         i_label = 0;
1025         for ( p=template->label.any.layouts; p != NULL; p=p->next ) {
1026                 layout = (glTemplateLayout *)p->data;
1027
1028                 for (iy = 0; iy < layout->ny; iy++) {
1029                         for (ix = 0; ix < layout->nx; ix++, i_label++) {
1030                                 origins[i_label].x = ix*layout->dx + layout->x0;
1031                                 origins[i_label].y = iy*layout->dy + layout->y0;
1032                         }
1033                 }
1034         }
1035
1036         g_qsort_with_data (origins, n_labels, sizeof(glTemplateOrigin),
1037                            compare_origins, NULL);
1038
1039         return origins;
1040 }
1041
1042 /*--------------------------------------------------------------------------*/
1043 /* PRIVATE.  Sort origins comparison function, first by y then by x.        */
1044 /*--------------------------------------------------------------------------*/
1045 static gint
1046 compare_origins (gconstpointer a,
1047                  gconstpointer b,
1048                  gpointer user_data)
1049 {
1050         const glTemplateOrigin *a_origin = a, *b_origin = b;
1051
1052         if ( a_origin->y < b_origin->y ) {
1053                 return +1;
1054         } else if ( a_origin->y > b_origin->y ) {
1055                 return -1;
1056         } else {
1057                 if ( a_origin->x < b_origin->x ) {
1058                         return -1;
1059                 } else if ( a_origin->x > b_origin->x ) {
1060                         return +1;
1061                 } else {
1062                         return 0; /* hopefully 2 labels won't have the same origin */
1063                 }
1064         }
1065 }
1066
1067 /****************************************************************************/
1068 /* Get a description of the layout and number of labels.                    */
1069 /****************************************************************************/
1070 gchar *
1071 gl_template_get_layout_desc (const glTemplate *template)
1072 {
1073         gint n_labels;
1074         glTemplateLayout *layout;
1075         gchar *string;
1076
1077         n_labels = gl_template_get_n_labels (template);
1078
1079         if ( template->label.any.layouts->next == NULL ) {
1080                 layout = (glTemplateLayout *)template->label.any.layouts->data;
1081                 string = g_strdup_printf (_("%d x %d  (%d per sheet)"),
1082                                           layout->nx, layout->ny,
1083                                           n_labels);
1084         } else {
1085                 string = g_strdup_printf (_("%d per sheet"),
1086                                           n_labels);
1087         }
1088
1089         return string;
1090 }
1091
1092 /*--------------------------------------------------------------------------*/
1093 /* PRIVATE.  Create new layout structure.                                   */
1094 /*--------------------------------------------------------------------------*/
1095 static glTemplateLayout *
1096 layout_new (gdouble nx,
1097             gdouble ny,
1098             gdouble x0,
1099             gdouble y0,
1100             gdouble dx,
1101             gdouble dy)
1102 {
1103         glTemplateLayout *layout;
1104
1105         layout = g_new0 (glTemplateLayout, 1);
1106
1107         layout->nx = nx;
1108         layout->ny = ny;
1109         layout->x0 = x0;
1110         layout->y0 = y0;
1111         layout->dx = dx;
1112         layout->dy = dy;
1113
1114         return layout;
1115 }
1116
1117 /*--------------------------------------------------------------------------*/
1118 /* PRIVATE.  Duplicate layout structure.                                    */
1119 /*--------------------------------------------------------------------------*/
1120 static glTemplateLayout *
1121 layout_dup (glTemplateLayout *orig_layout)
1122 {
1123         glTemplateLayout *layout;
1124
1125         layout = g_new0 (glTemplateLayout, 1);
1126
1127         /* copy contents */
1128         *layout = *orig_layout;
1129
1130         return layout;
1131 }
1132
1133 /*--------------------------------------------------------------------------*/
1134 /* PRIVATE.  Free layout structure.                                         */
1135 /*--------------------------------------------------------------------------*/
1136 static void
1137 layout_free (glTemplateLayout **layout)
1138 {
1139         g_free (*layout);
1140         *layout = NULL;
1141 }
1142
1143 /*--------------------------------------------------------------------------*/
1144 /* PRIVATE.  Create new margin markup structure.                            */
1145 /*--------------------------------------------------------------------------*/
1146 static glTemplateMarkup *
1147 markup_margin_new (gdouble size)
1148 {
1149         glTemplateMarkup *markup;
1150
1151         markup = g_new0 (glTemplateMarkup, 1);
1152
1153         markup->type        = GL_TEMPLATE_MARKUP_MARGIN;
1154         markup->margin.size = size;
1155
1156         return markup;
1157 }
1158
1159 /*--------------------------------------------------------------------------*/
1160 /* PRIVATE.  Duplicate markup structure.                                    */
1161 /*--------------------------------------------------------------------------*/
1162 static glTemplateMarkup *
1163 markup_dup (glTemplateMarkup *orig_markup)
1164 {
1165         glTemplateMarkup *markup;
1166
1167         markup = g_new0 (glTemplateMarkup, 1);
1168
1169         *markup = *orig_markup;
1170
1171         return markup;
1172 }
1173
1174 /*--------------------------------------------------------------------------*/
1175 /* PRIVATE.  Free markup structure.                                         */
1176 /*--------------------------------------------------------------------------*/
1177 static void
1178 markup_free (glTemplateMarkup **markup)
1179 {
1180         g_free (*markup);
1181         *markup = NULL;
1182 }