//
// Sample program that displays an XML file in a GtkTextView.
//
// Copyright (C) 2008 Emmanuel Rodriguez
//
// This program is free software; you can redistribute it and/or modify it under
// the same terms as Perl itself, either Perl version 5.8.8 or, at your option,
// any later version of Perl 5 you may have available.
//
//
#include "code.h"
#include "logger.h"
#include <glib/gprintf.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#define TAG(name, ...) gtk_text_buffer_create_tag(buffer, name, __VA_ARGS__, NULL)
static void my_create_buffer_tags (GtkTextBuffer *buffer);
static void my_create_widgets (GtkTextView **textview, GtkTreeView **treeview);
static GtkTreeViewColumn* my_add_text_column (GtkTreeView *treeview, DomModelColumnsEnum field, const gchar *title, gint width);
static GtkWidget* my_create_textview (void);
static GtkWidget* my_create_treeview (void);
static GtkWidget* my_wrap_in_scrolls (GtkWidget *widget);
static xmlDoc* my_parse_document (const gchar *filename);
int main (int argc, char **argv) {
gboolean no_xml = FALSE;
gboolean no_source = FALSE;
gboolean no_dom = FALSE;
gboolean quit = FALSE;
// Parse the arguments
gtk_init(&argc, &argv);
GOptionEntry entries[] = {
{ "no-xml", 'X', 0, G_OPTION_ARG_NONE, &no_xml, "Don't load the XML document", NULL },
{ "no-source", 'S', 0, G_OPTION_ARG_NONE, &no_source, "Don't show the XML source", NULL },
{ "no-dom", 'D', 0, G_OPTION_ARG_NONE, &no_dom, "Don't show the DOM tree", NULL },
{ "quit", 'q', 0, G_OPTION_ARG_NONE, &quit, "Quit as soon as the proram is ready", NULL },
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL },
};
GError *error = NULL;
GOptionContext *context = g_option_context_new("- memory profiling");
g_option_context_add_main_entries(context, entries, NULL);
g_option_context_add_group(context, gtk_get_option_group(TRUE));
if (!g_option_context_parse(context, &argc, &argv, &error)) {
ERROR("option parsing failed: %s", error->message);
g_error_free(error);
return 1;
}
if (argc < 1) {
ERROR("Usage: %s file\n", argv[0]);
return 1;
}
char *filename = argv[1];
// Load the XML document
DEBUG("Reading file %s", filename);
xmlDoc *document = !no_xml ? my_parse_document(filename) : NULL;
if (!no_xml && document == NULL) {
ERROR("Failed to parse %s", filename);
return 1;
}
INFO("Read file %s", filename);
// Render the XML document
GtkTextView *textview = NULL;
GtkTreeView *treeview = NULL;
my_create_widgets(&textview, &treeview);
// Fill the TextView (it's faster to remove the buffer and to put it back)
GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview);
gtk_text_view_set_buffer(textview, NULL);
if (!no_source) xacobeo_populate_gtk_text_buffer(buffer, (xmlNode *) document, NULL);
gtk_text_view_set_buffer(textview, buffer);
// Scroll to the beginning of the text
GtkTextIter iter;
gtk_text_buffer_get_start_iter(buffer, &iter);
gtk_text_view_scroll_to_iter(textview, &iter, 0.0, FALSE, 0.0, 0.0);
GtkTreeStore *store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
gtk_tree_view_set_model(treeview, NULL);
gtk_tree_store_clear(store);
if (!no_dom) xacobeo_populate_gtk_tree_store(store, (xmlNode *) document, NULL);
gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store));
INFO("Freeing XML document");
if (document) xmlFreeDoc(document);
// If we just want to time the execution time we don't need an event loop
if (!quit) {
// Main event loop
INFO("Starting main loop");
gtk_main();
}
INFO("Cleaning XML parser");
xmlCleanupParser();
INFO("End of program");
return 0;
}
//
// Creates the main widgets and prepares them for displaying. This function
// returns the GtkTextView casted as a GtkWidget.
//
static void my_create_widgets (GtkTextView **ptr_textview, GtkTreeView **ptr_treeview) {
// The main widgets
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *textview = my_create_textview();
GtkWidget *treeview = my_create_treeview();
// Pack the widgets together
GtkWidget *pane = gtk_hpaned_new();
gtk_paned_set_position(GTK_PANED(pane), 200);
gtk_paned_add1(GTK_PANED(pane), my_wrap_in_scrolls(treeview));
gtk_paned_add2(GTK_PANED(pane), my_wrap_in_scrolls(textview));
gtk_container_add(GTK_CONTAINER(window), pane);
gtk_widget_show_all(window);
// Connect the signals
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_main_quit), NULL);
// Set the return values
*ptr_textview = GTK_TEXT_VIEW(textview);
*ptr_treeview = GTK_TREE_VIEW(treeview);
}
//
// Creates the text view and sets its model (text buffer)
//
static GtkWidget* my_create_textview (void) {
// Prepare the text view
GtkTextTagTable *table = gtk_text_tag_table_new();
GtkTextBuffer *buffer = gtk_text_buffer_new(table);
GtkWidget *textview = gtk_text_view_new_with_buffer(buffer);
my_create_buffer_tags(buffer);
gtk_widget_set_size_request(textview, 600, 400);
gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview), FALSE);
return textview;
}
//
// Creates the tree view and sets its model (tree store)
//
static GtkWidget* my_create_treeview (void) {
// Prepre the tree view
GtkWidget *treeview = gtk_tree_view_new();
GtkTreeStore *store = gtk_tree_store_new(5,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING
);
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store));
gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), TRUE);
// Element name
GtkTreeViewColumn *column = my_add_text_column(
GTK_TREE_VIEW(treeview),
DOM_COL_ELEMENT_NAME,
"Element",
150
);
// Icon
GtkCellRenderer *cell = gtk_cell_renderer_pixbuf_new();
gtk_tree_view_column_pack_end(column, cell, FALSE);
gtk_tree_view_column_set_attributes(column, cell, "stock-id", DOM_COL_ICON, NULL);
// XML::ID
my_add_text_column(GTK_TREE_VIEW(treeview), DOM_COL_ID_NAME, "ID name", 75);
my_add_text_column(GTK_TREE_VIEW(treeview), DOM_COL_ID_VALUE, "ID value", 75);
return treeview;
}
static GtkTreeViewColumn* my_add_text_column (GtkTreeView *treeview, DomModelColumnsEnum field, const gchar *title, gint width) {
GtkCellRenderer *cell = gtk_cell_renderer_text_new();
GtkTreeViewColumn *column = gtk_tree_view_column_new();
gtk_tree_view_column_pack_end(column, cell, TRUE);
gtk_tree_view_column_set_title(column, title);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_fixed_width(column, width);
gtk_tree_view_column_set_attributes(column, cell, "text", field, NULL);
gtk_tree_view_append_column(treeview, column);
return column;
}
//
// Wraps the a widget with scroll bars.
//
static GtkWidget* my_wrap_in_scrolls (GtkWidget *widget) {
GtkWidget *scrolls = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolls), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolls), GTK_SHADOW_OUT);
gtk_container_add(GTK_CONTAINER(scrolls), widget);
return scrolls;
}
//
// Creates the markup rules to use for displaying XML.
//
static void my_create_buffer_tags (GtkTextBuffer *buffer) {
TAG("result_count",
"family", "Courier 10 Pitch",
"background", "#EDE9E3",
"foreground", "black",
"style", PANGO_STYLE_ITALIC,
"weight", PANGO_WEIGHT_LIGHT
);
TAG("boolean",
"family", "Courier 10 Pitch",
"foreground", "black",
"weight", PANGO_WEIGHT_BOLD
);
TAG("number",
"family", "Courier 10 Pitch",
"foreground", "black",
"weight", PANGO_WEIGHT_BOLD
);
TAG("attribute_name",
"foreground", "red"
);
TAG("attribute_value",
"foreground", "blue"
);
TAG("comment",
"foreground", "#008000",
"style", PANGO_STYLE_ITALIC,
"weight", PANGO_WEIGHT_LIGHT
);
TAG("dtd",
"foreground", "#558CBA",
"style", PANGO_STYLE_ITALIC
);
TAG("element",
"foreground", "#800080",
"weight", PANGO_WEIGHT_BOLD
);
TAG("pi",
"foreground", "#558CBA",
"style", PANGO_STYLE_ITALIC
);
TAG("pi_data",
"foreground", "red",
"style", PANGO_STYLE_ITALIC
);
TAG("syntax",
"foreground", "black",
"weight", PANGO_WEIGHT_BOLD
);
TAG("text",
"foreground", "black"
);
TAG("literal",
"foreground", "black"
);
TAG("cdata",
"foreground", "red",
"weight", PANGO_WEIGHT_BOLD
);
TAG("cdata_content",
"foreground", "purple",
"weight", PANGO_WEIGHT_BOLD,
"style", PANGO_STYLE_ITALIC,
"weight", PANGO_WEIGHT_LIGHT
);
TAG("namespace_name",
"foreground", "red",
"style", PANGO_STYLE_ITALIC,
"weight", PANGO_WEIGHT_LIGHT
);
TAG("namespace_uri",
"foreground", "blue",
"style", PANGO_STYLE_ITALIC,
"weight", PANGO_WEIGHT_LIGHT
);
TAG("entity_ref",
"foreground", "red",
"weight", PANGO_WEIGHT_BOLD
);
}
//
// Parses the XML document. Returns an XML document if the parsing was
// successful otherwise NULL.
//
// The document has to be freed with xmlFreeDoc();
//
static xmlDoc* my_parse_document (const gchar *filename) {
// Construct a parser contenxt
xmlParserCtxt *parserCtxt = xmlCreateFileParserCtxt(filename);
parserCtxt->loadsubset = XML_DETECT_IDS;
// Parse the document
xmlDoc *document = NULL;
if (xmlParseDocument(parserCtxt) == 0) {
document = parserCtxt->myDoc;
parserCtxt->myDoc = NULL;
}
xmlFreeParserCtxt(parserCtxt);
return document;
}