1 /*
2  * TilEm II
3  *
4  * Copyright (c) 2011 Benjamin Moody
5  *
6  * This program is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <gtk/gtk.h>
28 #include <gobject/gvaluecollector.h>
29 
30 #include "fixedtreeview.h"
31 
32 /* Style set on tree view; update column sizes */
ftv_style_set(GtkWidget * treeview,G_GNUC_UNUSED GtkStyle * oldstyle,G_GNUC_UNUSED gpointer data)33 static void ftv_style_set(GtkWidget *treeview,
34                           G_GNUC_UNUSED GtkStyle *oldstyle,
35                           G_GNUC_UNUSED gpointer data)
36 {
37 	GtkTreeModel *template;
38 	GtkTreeIter iter;
39 	GList *cols, *cp;
40 	GtkTreeViewColumn *col;
41 	int width;
42 
43 	template = g_object_get_data(G_OBJECT(treeview), "ftv-template");
44 	if (!template)
45 		return;
46 
47 	if (!gtk_tree_model_get_iter_first(template, &iter))
48 		return;
49 
50 	cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(treeview));
51 	for (cp = cols; cp; cp = cp->next) {
52 		col = cp->data;
53 		gtk_tree_view_column_cell_set_cell_data(col, template, &iter,
54 		                                        FALSE, FALSE);
55 		gtk_tree_view_column_cell_get_size(col, NULL, NULL, NULL,
56 		                                   &width, NULL);
57 		gtk_tree_view_column_set_fixed_width(col, width + 2);
58 	}
59 	g_list_free(cols);
60 }
61 
62 /* Widget destroyed */
ftv_destroy(GtkWidget * treeview,G_GNUC_UNUSED gpointer data)63 static void ftv_destroy(GtkWidget *treeview, G_GNUC_UNUSED gpointer data)
64 {
65 	GtkTreeModel *template;
66 
67 	template = g_object_get_data(G_OBJECT(treeview), "ftv-template");
68 	if (template)
69 		g_object_unref(template);
70 	g_object_set_data(G_OBJECT(treeview), "ftv-template", NULL);
71 }
72 
fixed_tree_view_init_with_template(GtkWidget * treeview,GtkTreeModel * template)73 void fixed_tree_view_init_with_template(GtkWidget *treeview,
74                                         GtkTreeModel *template)
75 {
76 	GtkTreeModel *oldtemplate;
77 
78 	if (template)
79 		g_object_ref_sink(template);
80 
81 	oldtemplate = g_object_get_data(G_OBJECT(treeview), "ftv-template");
82 	if (oldtemplate) {
83 		g_object_unref(oldtemplate);
84 	}
85 	else {
86 		g_signal_connect(treeview, "style-set",
87 		                 G_CALLBACK(ftv_style_set), NULL);
88 		g_signal_connect(treeview, "destroy",
89 		                 G_CALLBACK(ftv_destroy), NULL);
90 	}
91 	g_object_set_data(G_OBJECT(treeview), "ftv-template", template);
92 
93 	if (template && GTK_WIDGET_REALIZED(treeview))
94 		ftv_style_set(treeview, NULL, NULL);
95 }
96 
fixed_tree_view_init(GtkWidget * treeview,int colgroupsize,...)97 void fixed_tree_view_init(GtkWidget *treeview, int colgroupsize, ...)
98 {
99 	GtkTreeModel *real_model;
100 	int ncols, i, col;
101 	GType *types;
102 	GtkListStore *store;
103 	GtkTreeIter iter;
104 	GValue value;
105 	gchar *error = NULL;
106 	va_list ap;
107 
108 	g_return_if_fail(GTK_IS_TREE_VIEW(treeview));
109 	g_return_if_fail(colgroupsize >= 0);
110 
111 	real_model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
112 	g_return_if_fail(real_model != NULL);
113 
114 	ncols = gtk_tree_model_get_n_columns(real_model);
115 	g_return_if_fail(ncols > 0);
116 
117 	if (colgroupsize == 0)
118 		colgroupsize = ncols;
119 
120 	g_return_if_fail(ncols % colgroupsize == 0);
121 
122 	types = g_new(GType, ncols);
123 	for (i = 0; i < ncols; i++) {
124 		types[i] = gtk_tree_model_get_column_type(real_model, i);
125 		if (i > colgroupsize)
126 			g_return_if_fail(types[i] == types[i - colgroupsize]);
127 	}
128 	store = gtk_list_store_newv(ncols, types);
129 
130 	va_start(ap, colgroupsize);
131 	gtk_list_store_append(store, &iter);
132 
133 	memset(&value, 0, sizeof(value));
134 
135 	col = va_arg(ap, int);
136 	while (col != -1) {
137 		if (col < 0 || col >= colgroupsize) {
138 			g_critical("missing sentinel");
139 			break;
140 		}
141 
142 		g_value_init(&value, types[col]);
143 
144 		G_VALUE_COLLECT(&value, ap, 0, &error);
145 
146 		if (error) {
147 			g_critical("%s", error);
148 			g_free(error);
149 			break;
150 		}
151 
152 		for (i = col; i < ncols; i += colgroupsize)
153 			gtk_list_store_set_value(store, &iter, i, &value);
154 
155 		g_value_unset(&value);
156 
157 		col = va_arg(ap, int);
158 	}
159 
160 	va_end(ap);
161 
162 	g_free(types);
163 
164 	fixed_tree_view_init_with_template(treeview, GTK_TREE_MODEL(store));
165 }
166