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 ¶m_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, >K_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