1 /* Fo
2  * xmlroff-gtktree.c: Demonstration command line XSL formatter program
3  *
4  * This version demonstrates accessing the FO tree as FoNode objects.
5  *
6  * Copyright (C) 2001-2004 Sun Microsystems
7  * Copyright (C) 2007-2009 Menteith Consulting Ltd
8  *
9  * See COPYING for the status of this software.
10  */
11 
12 #include "config.h"
13 #include <stdlib.h>
14 #include <string.h>
15 #include <libfo/fo-libfo.h>
16 #include <libfo/fo-node.h>
17 #include <libfo/property/fo-property.h>
18 #define GTK_ENABLE_BROKEN
19 #include <gtk/gtk.h>
20 #include <libfo/libfo-compat.h>
21 #if ENABLE_CAIRO
22 #include <libfo/fo-doc-cairo.h>
23 #endif
24 #if ENABLE_GP
25 #include <libfo/fo-doc-gp.h>
26 #endif
27 
28 enum
29 {
30    FO_NAME_COLUMN,
31    FO_OBJECT_COLUMN,
32    N_COLUMNS
33 };
34 
35 static void
close_application(GtkWidget * widget G_GNUC_UNUSED,gpointer data G_GNUC_UNUSED)36 close_application (GtkWidget *widget G_GNUC_UNUSED,
37 		   gpointer   data G_GNUC_UNUSED)
38 {
39        gtk_main_quit ();
40 }
41 
42 static void
row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)43 row_activated (GtkTreeView       *tree_view,
44 	       GtkTreePath       *path,
45 	       GtkTreeViewColumn *column,
46 	       gpointer           user_data)
47 {
48   g_message ("got a 'row-selected' signal\n");
49 
50   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
51   gint depth = 0;
52   gint path_depth = gtk_tree_path_get_depth (path);
53   gint *indices = gtk_tree_path_get_indices (path);
54   GtkTreeIter iter;
55   GtkTreeIter parent_iter;
56   for (depth = 0; depth < path_depth; depth++)
57     {
58       /* walk the tree to find the iterator */
59       gtk_tree_model_iter_nth_child (model,
60 				     &iter,
61 				     (depth == 0 ? NULL : &parent_iter),
62 				     indices[depth]);
63       parent_iter = iter;
64     }
65 
66   FoNode *fo_node;
67   gtk_tree_model_get (model, &iter,
68 		      FO_OBJECT_COLUMN, &fo_node,
69 		      -1);
70   /* Do something with the data */
71   GType node_type = G_TYPE_FROM_INSTANCE (fo_node);
72   g_print ("FoNode %s\n", g_type_name (node_type));
73 
74   guint param_count;
75   GParamSpec **params = g_object_class_list_properties (G_OBJECT_GET_CLASS (fo_node),
76 							&param_count);
77 
78   g_print ("Properties: %d\n", param_count);
79   guint param_index;
80   for (param_index = 0; param_index < param_count; param_index++)
81     {
82       GParamSpec *param_spec = params[param_index];
83 
84       gchar *value;
85       if (param_spec->value_type == G_TYPE_BOOLEAN)
86 	{
87 	  gboolean boolean;
88 	  g_object_get (fo_node,
89 			param_spec->name,
90 			&boolean,
91 			NULL);
92 	  value = boolean ? "true" : "false";
93 	}
94       else
95 	{
96 	  GObject *object;
97 	  g_object_get (fo_node,
98 			param_spec->name,
99 			&object,
100 			NULL);
101 	  if (FO_IS_PROPERTY (object))
102 	    {
103 	      value = fo_object_sprintf (fo_property_get_value (FO_PROPERTY (object)));
104 	    }
105 	  else
106 	    {
107 	      value = fo_object_sprintf (object);
108 	    }
109 	}
110       g_print ("%s: %s: %s\n",
111 	       param_spec->name,
112 	       g_type_name (param_spec->value_type),
113 	       value);
114     }
115 }
116 
117 static void
row_activated2(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)118 row_activated2 (GtkTreeView       *tree_view,
119 		GtkTreePath       *path,
120 		GtkTreeViewColumn *column,
121 		gpointer           user_data)
122 {
123   g_message ("got a 'row-selected' signal\n");
124 
125   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
126   gint depth = 0;
127   gint path_depth = gtk_tree_path_get_depth (path);
128   gint *indices = gtk_tree_path_get_indices (path);
129   GtkTreeIter iter;
130   GtkTreeIter parent_iter;
131   for (depth = 0; depth < path_depth; depth++)
132     {
133       /* walk the tree to find the iterator */
134       gtk_tree_model_iter_nth_child (model,
135 				     &iter,
136 				     (depth == 0 ? NULL : &parent_iter),
137 				     indices[depth]);
138       parent_iter = iter;
139     }
140 
141   FoNode *fo_node;
142   gtk_tree_model_get (model, &iter,
143 		      FO_OBJECT_COLUMN, &fo_node,
144 		      -1);
145   /* Do something with the data */
146   GdkFont *fixed_font =
147     gdk_font_load ("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*");
148 
149   gchar *fo_node_sprintf = fo_object_sprintf (fo_node);
150   gtk_text_insert (GTK_TEXT (user_data), fixed_font, &GTK_WIDGET (user_data)->style->black, NULL,
151 		   fo_node_sprintf, -1);
152   g_free (fo_node_sprintf);
153 }
154 
155 /*
156  * insert_tree:
157  * @text:   Text widget
158  * @object: #FoObject to be dumped.
159  * @depth:  Relative indent to add to the output.
160  *
161  * Dumps node tree anchored at @object to @text.
162  **/
163 static void
insert_tree(GtkTreeStore * store,GtkTreeIter * parent_iter,FoNode * fo_node)164 insert_tree (GtkTreeStore *store,
165 	     GtkTreeIter  *parent_iter,
166 	     FoNode       *fo_node)
167 {
168   GtkTreeIter   iter;
169 
170   gtk_tree_store_append (store, &iter, parent_iter);  /* Acquire an iterator */
171 
172   gchar *fo_node_sprintf = fo_object_sprintf (fo_node);
173 
174   gtk_tree_store_set (store, &iter,
175 		      FO_NAME_COLUMN, fo_node_sprintf,
176 		      FO_OBJECT_COLUMN, fo_node,
177 		      -1);
178 
179   g_free (fo_node_sprintf);
180 
181   FoNode *child = fo_node_first_child (fo_node);
182   while (child)
183     {
184       insert_tree (store,
185 		   &iter,
186 		   child);
187 
188       child = fo_node_next_sibling (child);
189     }
190 }
191 
192 static FoDoc *
init_fo_doc_cairo(const gchar * out_file,FoLibfoContext * libfo_context)193 init_fo_doc_cairo (const gchar    *out_file,
194 		   FoLibfoContext *libfo_context)
195 {
196   FoDoc *fo_doc = NULL;
197   GError *error = NULL;
198 
199 #if ENABLE_CAIRO
200   fo_doc = fo_doc_cairo_new ();
201 
202   fo_doc_open_file (fo_doc,
203 		    out_file,
204 		    libfo_context,
205 		    &error);
206 
207   if (error != NULL)
208     {
209       g_critical ("%s:: %s",
210 		  g_quark_to_string (error->domain),
211 		  error->message);
212       g_error_free (error);
213       exit (1);
214     }
215 #else
216   g_critical (_("Output using Cairo is not supported by this build of libfo."));
217   exit (1);
218 #endif /* ENABLE_CAIRO */
219 
220   return fo_doc;
221 }
222 
223 static FoDoc *
init_fo_doc_gp(const gchar * out_file,FoLibfoContext * libfo_context)224 init_fo_doc_gp (const gchar    *out_file,
225 		FoLibfoContext *libfo_context)
226 {
227   FoDoc *fo_doc = NULL;
228   GError *error = NULL;
229 
230 #if ENABLE_GP
231   fo_doc = fo_doc_gp_new ();
232 
233   fo_doc_open_file (fo_doc,
234 		    out_file,
235 		    libfo_context,
236 		    &error);
237 
238   if (error != NULL)
239     {
240       g_critical ("%s:: %s",
241 		  g_quark_to_string (error->domain),
242 		  error->message);
243       g_error_free (error);
244       exit (1);
245     }
246 #else
247   g_critical ("Output using GNOME Print is not supported by this build of libfo.");
248   exit (1);
249 #endif /* ENABLE_GP */
250 
251   return fo_doc;
252 }
253 
254 static void
exit_if_error(GError * error)255 exit_if_error (GError *error)
256 {
257   if (error != NULL)
258     {
259       g_error ("%s:: %s",
260 	       g_quark_to_string (error->domain),
261 	       error->message);
262       g_error_free (error);
263       exit (1);
264     }
265 }
266 
267 int
main(gint argc,gchar ** argv)268 main (gint    argc,
269       gchar **argv)
270 {
271   FoXmlDoc *xml_doc = NULL;
272   FoXmlDoc *stylesheet_doc = NULL;
273   FoXmlDoc *result_tree = NULL;
274   FoXslFormatter *fo_xsl_formatter;
275   FoDoc *fo_doc = NULL;
276   GError *error = NULL;
277   /* Variables set from command-line options. */
278   gchar *out_file = "layout.pdf";
279   const gchar *xml_file = NULL;
280   const gchar *xslt_file = NULL;
281   const gchar *backend_string = NULL;
282   const gchar *format_string = NULL;
283   FoFlagsFormat format_mode = FO_FLAG_FORMAT_UNKNOWN;
284   FoDebugFlag debug_mode = FO_DEBUG_NONE;
285   FoWarningFlag warning_mode = FO_WARNING_FO | FO_WARNING_PROPERTY;
286   gboolean continue_after_error = FALSE;
287   gboolean validation = FALSE;
288   gboolean version = FALSE;
289   gchar** files = NULL;
290   gboolean goption_success = FALSE;
291 
292   gtk_init (&argc, &argv);
293 
294   const GOptionEntry options[] = {
295     { "out-file",
296       'o',
297       0,
298       G_OPTION_ARG_STRING,
299       &out_file,
300       _("Output file"),
301       _("filename")
302     },
303     { "format",
304       0,
305       0,
306       G_OPTION_ARG_STRING,
307       &format_string,
308       _("Format of output file"),
309       _("{auto|pdf|postscript|svg}")
310     },
311     { "backend",
312       0,
313       0,
314       G_OPTION_ARG_STRING,
315       &backend_string,
316       _("Backend to use"),
317       _("{cairo|gp}")
318     },
319     { "continue",
320       0,
321       0,
322       G_OPTION_ARG_NONE,
323       &continue_after_error,
324       _("Continue after any formatting errors"),
325       NULL
326     },
327     { "valid",
328       0,
329       0,
330       G_OPTION_ARG_NONE,
331       &validation,
332       /* Describe both --valid and --novalid since --novalid is hidden. */
333       _("Do ('--valid') or do not ('--novalid') load the DTD "
334 	"(default is '--novalid')"),
335       NULL
336     },
337     { "novalid",
338       0,
339       G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_REVERSE,
340       G_OPTION_ARG_NONE,
341       &validation,
342       _("Skip the DTD loading phase"),
343       NULL
344     },
345     { "version",
346       'v',
347       0,
348       G_OPTION_ARG_NONE,
349       &version,
350       _("Print version number"),
351       NULL
352     },
353     { "warn",
354       'w',
355       0,
356       G_OPTION_ARG_INT,
357       &warning_mode,
358       _("Warning mode"),
359       _("integer")
360     },
361     { "debug",
362       'd',
363       0,
364       G_OPTION_ARG_INT,
365       &debug_mode,
366       _("Debug mode"),
367       _("integer")
368     },
369     { G_OPTION_REMAINING,
370       0,
371       0,
372       G_OPTION_ARG_FILENAME_ARRAY,
373       &files,
374       NULL,
375       _("file [stylesheet]")
376     },
377     {NULL, 0, 0, 0, NULL, NULL, NULL}
378   };
379 
380   GOptionContext *ctx = g_option_context_new (NULL);
381   g_option_context_add_main_entries (ctx, options, PACKAGE);
382   goption_success = g_option_context_parse (ctx, &argc, &argv, &error);
383   /* Finished with parsing command-line arguments. */
384   g_option_context_free(ctx);
385 
386   if (goption_success == FALSE)
387     {
388       exit (1);
389     }
390 
391   if (version != 0)
392     {
393       g_print ("%s\nSubmit bug reports to %s\n",
394 	       PACKAGE_STRING,
395 	       PACKAGE_BUGREPORT);
396       if (files == NULL)
397 	{
398 	  /* Nothing to do if just asking for version. */
399 	  exit (0);
400 	}
401     }
402 
403   if ((files == NULL) ||
404       (files[0] == NULL))
405     {
406       g_print ("No input file specified.\n");
407 
408       exit (1);
409     }
410   else
411     {
412       xml_file = files[0];
413     }
414 
415   if (files[1] != NULL)
416     {
417       xslt_file = files[1];
418 
419       if (files[2] != NULL)
420 	{
421 	  g_print("Unexpected additional parameter: '%s'\n",
422 		      files[2]);
423 
424 	  exit (1);
425 	}
426     }
427 
428   fo_libfo_init ();
429 
430   FoLibfoContext *libfo_context = fo_libfo_context_new ();
431 
432   fo_libfo_context_set_validation (libfo_context,
433 				   validation);
434 
435   fo_libfo_context_set_continue_after_error (libfo_context,
436 					     continue_after_error);
437 
438   /* Need to do 'format' before 'backend'. */
439   if ((format_string == NULL) ||
440       (strcmp (format_string, "auto") == 0))
441     {
442       format_mode = FO_FLAG_FORMAT_AUTO;
443     }
444   else if (strcmp (format_string, "pdf") == 0)
445     {
446       format_mode = FO_FLAG_FORMAT_PDF;
447     }
448   else if (strcmp (format_string, "postscript") == 0)
449     {
450       format_mode = FO_FLAG_FORMAT_POSTSCRIPT;
451     }
452   else if (strcmp (format_string, "svg") == 0)
453     {
454       format_mode = FO_FLAG_FORMAT_SVG;
455     }
456   else
457     {
458       g_print("Unsupported output format: '%s'\n",
459 	      format_string);
460 
461       exit (1);
462     }
463 
464   if (goption_success == TRUE)
465     {
466       fo_libfo_context_set_format (libfo_context,
467 				   format_mode);
468     }
469 
470   if (backend_string == NULL)
471     {
472 #if ENABLE_GP
473       fo_doc = init_fo_doc_gp (out_file, libfo_context);
474 #else
475 #if ENABLE_CAIRO
476       fo_doc = init_fo_doc_cairo (out_file, libfo_context);
477 #else
478       g_print("No backend type is supported by this build of libfo.\n");
479 
480       exit (1);
481 #endif /* ENABLE_CAIRO */
482 #endif /* ENABLE_GP */
483     }
484   else if (strcmp (backend_string, "cairo") == 0)
485     {
486       fo_doc = init_fo_doc_cairo (out_file, libfo_context);
487     }
488   else if (strcmp (backend_string, "gp") == 0)
489     {
490       fo_doc = init_fo_doc_gp (out_file, libfo_context);
491     }
492   else
493     {
494       g_print ("Unrecognised output type: '%s'\n", backend_string);
495 
496       exit (1);
497     }
498 
499   if (debug_mode != FO_DEBUG_NONE)
500     {
501       fo_libfo_context_set_debug_mode (libfo_context,
502 				       debug_mode);
503     }
504 
505   fo_libfo_context_set_warning_mode (libfo_context,
506 				     warning_mode);
507 
508   if (xslt_file != NULL)
509     {
510       /* When there is an XSLT file specified, need to
511 	 do a transform before formatting result. */
512       xml_doc = fo_xml_doc_new_from_filename (xml_file,
513 					      libfo_context,
514 					      &error);
515 
516       exit_if_error (error);
517 
518       stylesheet_doc = fo_xml_doc_new_from_filename (xslt_file,
519 						     libfo_context,
520 						     &error);
521 
522       exit_if_error (error);
523 
524       result_tree = fo_xslt_transformer_do_transform (xml_doc,
525 						      stylesheet_doc,
526 						      &error);
527       exit_if_error (error);
528 
529       fo_xml_doc_unref (xml_doc);
530     }
531   else
532     {
533       /* When there is no XSLT file specified, the XML file
534 	 is expected to be in the FO vocabulary, so just use it. */
535       result_tree = fo_xml_doc_new_from_filename (xml_file,
536 						  libfo_context,
537 						  &error);
538 
539       exit_if_error (error);
540     }
541 
542   /* Make sure the FO XML document is safe for libfo to process. */
543   FoXmlDoc *old_result_tree = result_tree;
544 
545   /* Remove or rewrite what libfo can't yet handle. */
546   result_tree = libfo_compat_make_compatible (old_result_tree,
547 					      libfo_context,
548 					      &error);
549 
550   fo_xml_doc_unref (old_result_tree);
551 
552   exit_if_error (error);
553 
554   fo_xsl_formatter = fo_xsl_formatter_new ();
555 
556   fo_xsl_formatter_set_result_tree (fo_xsl_formatter,
557 				    result_tree);
558 
559   fo_xsl_formatter_set_fo_doc (fo_xsl_formatter,
560 			       fo_doc);
561 
562   fo_xsl_formatter_format (fo_xsl_formatter,
563 			   libfo_context,
564 			   &error);
565 
566   exit_if_error (error);
567 
568   fo_xsl_formatter_draw (fo_xsl_formatter,
569 			 libfo_context,
570 			 &error);
571 
572   exit_if_error (error);
573 
574   FoNode *fo_tree = FO_NODE (fo_xsl_formatter_get_fo_tree (fo_xsl_formatter));
575 
576   GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
577   gtk_widget_set_size_request (window, 600, 500);
578   gtk_window_set_policy (GTK_WINDOW (window), TRUE, TRUE, FALSE);
579   g_signal_connect (G_OBJECT (window), "destroy",
580                     G_CALLBACK (close_application),
581                     NULL);
582   gtk_window_set_title (GTK_WINDOW (window), "Formatting Object Tree View");
583   gtk_container_set_border_width (GTK_CONTAINER (window), 0);
584 
585 
586   GtkWidget *box1 = gtk_vbox_new (FALSE, 0);
587   gtk_container_add (GTK_CONTAINER (window), box1);
588   gtk_widget_show (box1);
589 
590 
591   GtkWidget *box2 = gtk_vbox_new (FALSE, 10);
592   gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
593   gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
594   gtk_widget_show (box2);
595 
596 
597   GtkWidget *table = gtk_table_new (2, 3, FALSE);
598   gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
599   gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
600   gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
601   gtk_widget_show (table);
602 
603   /* Create the GtkTreeModel */
604 
605   /* Create a model.  We are using the store model for now, though we
606    * could use any other GtkTreeModel */
607   GtkTreeStore *store = gtk_tree_store_new (N_COLUMNS,
608 					    G_TYPE_STRING,
609 					    FO_TYPE_NODE);
610 
611   /* custom function to fill the model with data */
612   insert_tree (store, NULL, fo_tree);
613 
614   /* Create a view */
615   GtkWidget *tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
616 
617   /* The view now holds a reference.  We can get rid of our own
618    * reference */
619   g_object_unref (G_OBJECT (store));
620 
621   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree),
622 				     FALSE);
623 
624   g_signal_connect (G_OBJECT (tree),
625 		    "row-activated",
626 		    G_CALLBACK (row_activated),
627 		    fo_tree);
628 
629   /* Create a cell render and arbitrarily make it red for demonstration
630    * purposes */
631   GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
632   g_object_set (G_OBJECT (renderer),
633 		"foreground", "red",
634 		NULL);
635 
636   /* Create a column, associating the "text" attribute of the
637    * cell_renderer to the first column of the model */
638   GtkTreeViewColumn *column =
639     gtk_tree_view_column_new_with_attributes ("FO",
640 					      renderer,
641 					      "text",
642 					      FO_NAME_COLUMN,
643 					      NULL);
644 
645   /* Add the column to the view. */
646   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
647 
648   /* Second column. */
649   renderer = gtk_cell_renderer_text_new ();
650 
651   column =
652     gtk_tree_view_column_new_with_attributes ("Node",
653 					      renderer,
654 					      NULL);
655   gtk_tree_view_column_set_visible (column,
656 				    FALSE);
657 
658   /* Add the column to the view. */
659   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
660 
661   /* Now we can manipulate the view just like any other GTK widget */
662   gtk_table_attach (GTK_TABLE (table), tree, 0, 1, 0, 1,
663 		    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
664 		    GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
665   gtk_widget_show (tree);
666 
667   /* Realizing a widget creates a window for it,
668    * ready for us to insert some text */
669   gtk_widget_realize (tree);
670 
671   /* Create the GtkText widget */
672   GtkWidget *text = gtk_text_new (NULL, NULL);
673   gtk_text_set_editable (GTK_TEXT (text), FALSE);
674   gtk_table_attach (GTK_TABLE (table), text, 1, 2, 0, 1,
675 		    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
676 		    GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
677   gtk_widget_show (text);
678 
679   /* Add a vertical scrollbar to the GtkText widget */
680   GtkWidget *vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
681   gtk_table_attach (GTK_TABLE (table), vscrollbar, 2, 3, 0, 1,
682 		    GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
683   gtk_widget_show (vscrollbar);
684 
685   /* Realizing a widget creates a window for it,
686    * ready for us to insert some text */
687   gtk_widget_realize (text);
688 
689   g_signal_connect (G_OBJECT (tree),
690 		    "row-activated",
691 		    G_CALLBACK (row_activated2),
692 		    text);
693 
694   GtkWidget *hbox = gtk_hbutton_box_new ();
695   gtk_box_pack_start (GTK_BOX (box2), hbox, FALSE, FALSE, 0);
696   gtk_widget_show (hbox);
697 
698   box2 = gtk_vbox_new (FALSE, 10);
699   gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
700   gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
701   gtk_widget_show (box2);
702 
703   GtkWidget *button = gtk_button_new_with_label ("close");
704   g_signal_connect (G_OBJECT (button), "clicked",
705 	            G_CALLBACK (close_application),
706 	            NULL);
707   gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
708   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
709   gtk_widget_grab_default (button);
710   gtk_widget_show (button);
711 
712   gtk_widget_show (window);
713 
714   gtk_main ();
715 
716   g_object_unref (fo_xsl_formatter);
717   g_object_unref (fo_doc);
718   fo_libfo_shutdown ();
719 
720   return(0);
721 }
722