Code:
/* Compile with:
gcc -o output input.c $(pkg-config --cflags --libs gtk+-2.0 libxml-2.0)
*/
#include <gtk/gtk.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
enum
{
COL_0 = 0,
COL_1,
N_COLS,
};
/* create a new GtkTreeView */
GtkWidget *create_view(void)
{
GtkWidget *view;
GtkTreeModel *model;
GtkCellRenderer *cell;
model = GTK_TREE_MODEL(gtk_tree_store_new(N_COLS, G_TYPE_STRING, G_TYPE_STRING));
view = gtk_tree_view_new_with_model(model);
g_object_unref(model);
cell = gtk_cell_renderer_text_new();
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Column 1", cell, "text", COL_0, NULL);
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Column 2", cell, "text", COL_1, NULL);
return view;
}
/* Add some test data to the tree view */
void fill_in_data_cb(GtkWidget *button, GtkTreeView *view)
{
GtkTreeIter iter, parent;
GtkTreeStore *store;
store = GTK_TREE_STORE(gtk_tree_view_get_model(view));
/* toplevel */
gtk_tree_store_append(store, &parent, NULL);
gtk_tree_store_set(store, &parent, COL_0, "toplevel1", COL_1, "aaa", -1);
gtk_tree_store_append(store, &iter, &parent);
gtk_tree_store_set(store, &iter, COL_0, "child1", COL_1, "bbb", -1);
/* toplevel */
gtk_tree_store_append(store, &parent, NULL);
gtk_tree_store_set(store, &parent, COL_0, "toplevel2", COL_1, "ccc", -1);
gtk_tree_store_append(store, &parent, &parent);
gtk_tree_store_set(store, &parent, COL_0, "toplevel2_child2", COL_1, "ddd", -1);
gtk_tree_store_append(store, &iter, &parent);
gtk_tree_store_set(store, &iter, COL_0, "child3", COL_1, "eee", -1);
}
/* Go through each row and add its data to the xmlDocPtr */
gboolean save_to_file_foreach(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, xmlNodePtr root)
{
gchar *col1, *col2, *pathstr;
xmlNodePtr cur;
/* get the data stored in the model... */
gtk_tree_model_get(model, iter, COL_0, &col1, COL_1, &col2, -1);
/* ...and get the path of the current row */
pathstr = gtk_tree_path_to_string(path);
/* create a new child of the root node... */
/* (note that I'm using a (guchar*) cast; this is because it's the same thing as
* an (xmlChar*) cast, but easier to type) */
cur = xmlNewChild(root, NULL, (guchar*)"item", NULL);
/* ...and set some properties */
xmlSetProp(cur, (guchar*)"column1", (guchar*)col1);
xmlSetProp(cur, (guchar*)"column2", (guchar*)col2);
xmlSetProp(cur, (guchar*)"path", (guchar*)pathstr);
/* free our data we retrieved from the model */
g_free(col1);
g_free(col2);
g_free(pathstr);
/* return FALSE to keep iterating */
return FALSE;
}
/* Function handle saving an xml file; calls save_to_file_foreach */
void save_to_file(GtkTreeView *view, gchar *filename)
{
xmlDocPtr doc;
xmlNodePtr root;
GtkTreeModel *model;
/* try to create a new doc node */
doc = xmlNewDoc((guchar*)"1.0");
if(doc == NULL)
/* handle the error */
return;
/* try to create a new root element. You'll want to change "root" to something more specific to your program */
root = xmlNewDocNode(doc, NULL, (guchar*)"root", NULL);
if(root == NULL)
{
/* handle the error */
xmlFreeDoc(doc);
return;
}
/* set it as the root element */
xmlDocSetRootElement(doc, root);
/* get the tree view's model... */
model = gtk_tree_view_get_model(view);
/* ...and go through it with a foreach */
gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)save_to_file_foreach, (gpointer)root);
/* save the actual file */
xmlSaveFile(filename, doc);
/* free the doc node */
xmlFreeDoc(doc);
}
/* Callback for the "Save to file" button; calls save_to_file */
void save_to_file_cb(GtkWidget *button, GtkTreeView *view)
{
GtkWidget *win;
gchar *filename;
win = gtk_file_chooser_dialog_new("Select file name...", NULL, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(win), TRUE);
switch(gtk_dialog_run(GTK_DIALOG(win)))
{
case GTK_RESPONSE_ACCEPT:
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(win));
save_to_file(view, filename);
g_free(filename);
break;
}
gtk_widget_destroy(win);
}
/* Gets the parent of a path string.
* passing "0:1:2" would return "0:1",
* passing "0:1" would return "0",
* passing "0" would return NULL */
gchar *gtk_tree_path_string_get_parent(gchar *path)
{
gchar *colon;
g_return_val_if_fail(path != NULL, NULL);
colon = g_strrstr(path, ":");
if(colon == NULL)
return NULL;
return g_strndup(path, colon - path);
}
/* Make sure that path exists within model */
void gtk_tree_model_generate_path(GtkTreeModel *model, gchar *path)
{
GtkTreeIter iter, parent;
gchar *temp;
while(TRUE)
{
/* if this returns TRUE, then this path exists and we're fine */
if(gtk_tree_model_get_iter_from_string(model, &iter, path))
break;
temp = path;
path = gtk_tree_path_string_get_parent(path);
/* if there's no parent, then it's toplevel */
if(path == NULL)
{
if(GTK_IS_TREE_STORE(model))
gtk_tree_store_append(GTK_TREE_STORE(model), &parent, NULL);
else
gtk_list_store_append(GTK_LIST_STORE(model), &parent);
gtk_tree_model_generate_path(model, temp);
break;
}
if(GTK_IS_TREE_STORE(model))
{
gtk_tree_model_generate_path(model, path);
gtk_tree_model_get_iter_from_string(model, &parent, path);
gtk_tree_store_append(GTK_TREE_STORE(model), &iter, &parent);
}
}
}
/* Function to load from an xml file */
void load_from_file(GtkTreeView *view, gchar *filename)
{
xmlDocPtr doc;
xmlNodePtr cur;
GtkTreeModel *model;
GtkTreeIter iter;
xmlChar *col1, *col2, *path;
/* load the file */
doc = xmlParseFile(filename);
if(doc == NULL)
/* handle the error */
return;
/* get the root item */
cur = xmlDocGetRootElement(doc);
if(cur == NULL)
{
xmlFreeDoc(doc);
/* handle the error */
return;
}
/* get the tree view's model */
model = gtk_tree_view_get_model(view);
/* iterate through the root's children items */
cur = cur->xmlChildrenNode;
while(cur)
{
/* check for the proper element name */
if(!xmlStrcmp(cur->name, (guchar*)"item"))
{
/* get the saved properties */
col1 = xmlGetProp(cur, (guchar*)"column1");
col2 = xmlGetProp(cur, (guchar*)"column2");
path = xmlGetProp(cur, (guchar*)"path");
/* make sure that the path exists */
gtk_tree_model_generate_path(model, (gchar*)path);
/* get an iter to the path */
gtk_tree_model_get_iter_from_string(model, &iter, (gchar*)path);
/* set the data */
gtk_tree_store_set(GTK_TREE_STORE(model), &iter, COL_0, col1, COL_1, col2, -1);
/* free the data */
xmlFree(col1);
xmlFree(col2);
xmlFree(path);
}
cur = cur->next;
}
/* free the doc node */
xmlFreeDoc(doc);
}
/* Callback for the "Load from file" button; calls load_from_file */
void load_from_file_cb(GtkWidget *button, GtkTreeView *view)
{
GtkWidget *win;
gchar *filename;
win = gtk_file_chooser_dialog_new("Select file name...", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
switch(gtk_dialog_run(GTK_DIALOG(win)))
{
case GTK_RESPONSE_ACCEPT:
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(win));
load_from_file(view, filename);
g_free(filename);
break;
}
gtk_widget_destroy(win);
}
gint main(gint argc, gchar **argv)
{
GtkWidget *win;
GtkWidget *vbox;
GtkWidget *view;
GtkWidget *button;
gtk_init(&argc, &argv);
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(win, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(win), vbox);
gtk_widget_show(vbox);
view = create_view();
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);
gtk_widget_show(view);
button = gtk_button_new_with_label("Fill in data");
g_signal_connect(button, "clicked", G_CALLBACK(fill_in_data_cb), (gpointer)view);
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
gtk_widget_show(button);
button = gtk_button_new_with_label("Save to file");
g_signal_connect(button, "clicked", G_CALLBACK(save_to_file_cb), (gpointer)view);
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
gtk_widget_show(button);
button = gtk_button_new_with_label("Load from file");
g_signal_connect(button, "clicked", G_CALLBACK(load_from_file_cb), (gpointer)view);
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
gtk_widget_show(button);
button = gtk_button_new_with_label("Clear view");
g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_tree_store_clear), (gpointer)gtk_tree_view_get_model(GTK_TREE_VIEW(view)));
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
gtk_widget_show(button);
gtk_widget_show(win);
gtk_main();
return 0;
}
current preview is below
============
Required preview (Below)
---------------------------------------
Thanks