1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  This file is part of the GtkHTML library.
3  *
4  *  Copyright (C) 2000 Jonas Borgstr�m <jonas_b@bitsmart.com>.
5  *  Copyright (C) 2000, 2001, 2002 Ximian, Inc.
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public License
18  *  along with this library; see the file COPYING.LIB.  If not, write to
19  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301, USA.
21 */
22 
23 #include <config.h>
24 
25 #include "htmlselect.h"
26 #include <string.h>
27 
28 HTMLSelectClass html_select_class;
29 static HTMLEmbeddedClass *parent_class = NULL;
30 
31 static void
clear_paths(HTMLSelect * select)32 clear_paths (HTMLSelect *select)
33 {
34 	g_list_foreach (select->paths, (GFunc) gtk_tree_path_free, NULL);
35 	g_list_free (select->paths);
36 	select->paths = NULL;
37 }
38 
39 static void
destroy(HTMLObject * o)40 destroy (HTMLObject *o)
41 {
42 	clear_paths (HTML_SELECT (o));
43 
44 	HTML_OBJECT_CLASS (parent_class)->destroy (o);
45 }
46 
47 static void
copy(HTMLObject * self,HTMLObject * dest)48 copy (HTMLObject *self,
49       HTMLObject *dest)
50 {
51 	/* FIXME TODO */
52 	HTMLSelect *s = HTML_SELECT (self);
53 	HTMLSelect *d = HTML_SELECT (dest);
54 
55 	(* HTML_OBJECT_CLASS (parent_class)->copy) (self,dest);
56 
57 	/* FIXME g_warning ("HTMLSelect::copy() is not complete."); */
58 	d->size =    s->size;
59 	d->multi =   s->multi;
60 
61 	d->paths = NULL;
62 
63 	d->view = NULL;
64 }
65 
66 static void
reset(HTMLEmbedded * e)67 reset (HTMLEmbedded *e)
68 {
69 	HTMLSelect *s = HTML_SELECT (e);
70 
71 	if (s->multi || s->size > 1) {
72 		GtkTreeView *tree_view;
73 		GtkTreeSelection *selection;
74 		GList *iter;
75 
76 		tree_view = GTK_TREE_VIEW (s->view);
77 		selection = gtk_tree_view_get_selection (tree_view);
78 		gtk_tree_selection_unselect_all (selection);
79 		for (iter = s->paths; iter != NULL; iter = iter->next) {
80 			GtkTreePath *path = iter->data;
81 			gtk_tree_selection_select_path (selection, path);
82 		}
83 
84 	} else if (s->paths != NULL) {
85 		GtkTreePath *path = s->paths->data;
86 		GtkComboBox *combo_box;
87 		GtkTreeIter iter;
88 
89 		combo_box = GTK_COMBO_BOX (e->widget);
90 		if (gtk_tree_model_get_iter (s->model, &iter, path))
91 			gtk_combo_box_set_active_iter (combo_box, &iter);
92 	}
93 }
94 
95 struct EmbeddedSelectionInfo {
96 	HTMLEmbedded *embedded;
97 	GString *string;
98 };
99 
100 static void
add_selected(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,struct EmbeddedSelectionInfo * info,const gchar * codepage)101 add_selected (GtkTreeModel *model,
102               GtkTreePath *path,
103               GtkTreeIter *iter,
104               struct EmbeddedSelectionInfo *info,
105               const gchar *codepage)
106 {
107 	gchar *value, *encoded;
108 
109 	gtk_tree_model_get (model, iter, 0, &value, -1);
110 
111 	if (info->string->len)
112 		g_string_append_c (info->string, '&');
113 
114 	encoded = html_embedded_encode_string (info->embedded->name, codepage);
115 	g_string_append (info->string, encoded);
116 	g_free (encoded);
117 
118 	g_string_append_c (info->string, '=');
119 
120 	encoded = html_embedded_encode_string (value, codepage);
121 	g_string_append (info->string, encoded);
122 	g_free (encoded);
123 
124 	g_free (value);
125 }
126 
127 static gchar *
encode(HTMLEmbedded * e,const gchar * codepage)128 encode (HTMLEmbedded *e,
129         const gchar *codepage)
130 {
131 	struct EmbeddedSelectionInfo info;
132 	HTMLSelect *s = HTML_SELECT (e);
133 	GtkTreeIter iter;
134 
135 	info.embedded = e;
136 	info.string = g_string_sized_new (128);
137 
138 	if (e->name != NULL && *e->name != '\0') {
139 		if (s->size > 1) {
140 			gtk_tree_selection_selected_foreach (
141 				gtk_tree_view_get_selection (
142 				GTK_TREE_VIEW (s->view)),
143 				(GtkTreeSelectionForeachFunc)
144 				add_selected, &info);
145 		} else {
146 			GtkComboBox *combo_box;
147 
148 			combo_box = GTK_COMBO_BOX (e->widget);
149 			if (gtk_combo_box_get_active_iter (combo_box, &iter))
150 				add_selected (s->model, NULL, &iter, &info, codepage);
151 		}
152 	}
153 
154 	return g_string_free (info.string, FALSE);
155 }
156 
157 void
html_select_type_init(void)158 html_select_type_init (void)
159 {
160 	html_select_class_init (
161 		&html_select_class, HTML_TYPE_SELECT, sizeof (HTMLSelect));
162 }
163 
164 void
html_select_class_init(HTMLSelectClass * klass,HTMLType type,guint object_size)165 html_select_class_init (HTMLSelectClass *klass,
166                         HTMLType type,
167                         guint object_size)
168 {
169 	HTMLEmbeddedClass *element_class;
170 	HTMLObjectClass *object_class;
171 
172 	element_class = HTML_EMBEDDED_CLASS (klass);
173 	object_class = HTML_OBJECT_CLASS (klass);
174 
175 	html_embedded_class_init (element_class, type, object_size);
176 
177 	/* HTMLEmbedded methods.   */
178 	element_class->reset = reset;
179 	element_class->encode = encode;
180 
181 	/* HTMLObject methods.   */
182 	object_class->destroy = destroy;
183 	object_class->copy = copy;
184 
185 	parent_class = &html_embedded_class;
186 }
187 
188 void
html_select_init(HTMLSelect * select,HTMLSelectClass * klass,GtkWidget * parent,gchar * name,gint size,gboolean multi)189 html_select_init (HTMLSelect *select,
190                   HTMLSelectClass *klass,
191                   GtkWidget *parent,
192                   gchar *name,
193                   gint size,
194                   gboolean multi)
195 {
196 	HTMLEmbedded *element = HTML_EMBEDDED (select);
197 	GtkCellRenderer *renderer;
198 	GtkListStore *store;
199 	GtkWidget *widget;
200 
201 	html_embedded_init (
202 		element, HTML_EMBEDDED_CLASS (klass), parent, name, NULL);
203 
204 	store = gtk_list_store_new (1, G_TYPE_STRING);
205 	renderer = gtk_cell_renderer_text_new ();
206 	select->model = GTK_TREE_MODEL (store);
207 
208 	if (size > 1 || multi) {
209 		GtkTreeViewColumn *column;
210 		GtkRequisition req;
211 		GtkTreeIter iter;
212 
213 		select->view = gtk_tree_view_new_with_model (select->model);
214 		gtk_tree_view_set_headers_visible (
215 			GTK_TREE_VIEW (select->view), FALSE);
216 		gtk_tree_selection_set_mode (
217 			gtk_tree_view_get_selection (
218 			GTK_TREE_VIEW (select->view)),
219 			multi ? GTK_SELECTION_MULTIPLE :
220 			GTK_SELECTION_SINGLE);
221 
222 		column = gtk_tree_view_column_new ();
223 		gtk_tree_view_column_pack_start (
224 			column, renderer, FALSE);
225 		gtk_tree_view_column_add_attribute (
226 			column, renderer, "text", 0);
227 		gtk_tree_view_append_column (
228 			GTK_TREE_VIEW (select->view), column);
229 
230 		widget = gtk_scrolled_window_new (NULL, NULL);
231 		gtk_scrolled_window_set_shadow_type (
232 			GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
233 		gtk_scrolled_window_set_policy (
234 			GTK_SCROLLED_WINDOW (widget),
235 			GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
236 		gtk_container_add (GTK_CONTAINER (widget), select->view);
237 		gtk_widget_show_all (widget);
238 
239 		gtk_list_store_append (store, &iter);
240 		gtk_list_store_set (store, &iter, 0, "height", -1);
241 		gtk_widget_get_preferred_size (select->view, &req, NULL);
242 		gtk_widget_set_size_request (
243 			select->view, 120, req.height * size);
244 		gtk_list_store_remove (store, &iter);
245 	} else {
246 		widget = gtk_combo_box_new_with_model_and_entry (select->model);
247 		gtk_widget_set_size_request (widget, 120, -1);
248 	}
249 
250 	html_embedded_set_widget (element, widget);
251 
252 	select->size = size;
253 	select->multi = multi;
254 	select->longest = 0;
255 	select->paths = NULL;
256 }
257 
258 HTMLObject *
html_select_new(GtkWidget * parent,gchar * name,gint size,gboolean multi)259 html_select_new (GtkWidget *parent,
260                  gchar *name,
261                  gint size,
262                  gboolean multi)
263 {
264 	HTMLSelect *ti;
265 
266 	ti = g_new0 (HTMLSelect, 1);
267 	html_select_init (
268 		ti, &html_select_class, parent, name, size, multi);
269 
270 	return HTML_OBJECT (ti);
271 }
272 
273 void
html_select_add_option(HTMLSelect * select,const gchar * value,gboolean selected)274 html_select_add_option (HTMLSelect *select,
275                         const gchar *value,
276                         gboolean selected)
277 {
278 	GtkListStore *store;
279 	GtkTreeIter iter;
280 
281 	if (value == NULL)
282 		value = "";
283 
284 	store = GTK_LIST_STORE (select->model);
285 	gtk_list_store_append (store, &iter);
286 	gtk_list_store_set (store, &iter, 0, value, -1);
287 	select->longest = MAX (select->longest, strlen (value));
288 
289 	if (select->size > 1 || select->multi) {
290 		if (selected) {
291 			GtkTreeSelection *selection;
292 			GtkTreeView *tree_view;
293 
294 			clear_paths (select);
295 			tree_view = GTK_TREE_VIEW (select->view);
296 			selection = gtk_tree_view_get_selection (tree_view);
297 			gtk_tree_selection_select_iter (selection, &iter);
298 			select->paths =
299 				gtk_tree_selection_get_selected_rows (
300 				selection, NULL);
301 		}
302 	} else {
303 		GtkComboBox *combo_box;
304 
305 		combo_box = GTK_COMBO_BOX (HTML_EMBEDDED (select)->widget);
306 		selected |= (gtk_combo_box_get_active (combo_box) < 0);
307 
308 		if (selected) {
309 			GtkTreePath *path;
310 
311 			clear_paths (select);
312 			gtk_combo_box_set_active_iter (combo_box, &iter);
313 			path = gtk_tree_model_get_path (select->model, &iter);
314 			select->paths = g_list_prepend (NULL, path);
315 		}
316 	}
317 }
318 
319 void
html_select_set_text(HTMLSelect * select,const gchar * text)320 html_select_set_text (HTMLSelect *select,
321                       const gchar *text)
322 {
323 	GtkWidget *w = GTK_WIDGET (HTML_EMBEDDED (select)->widget);
324 	GtkListStore *store;
325 	GtkTreePath *path;
326 	GtkTreeIter iter;
327 	gint n_children;
328 
329 	if (text == NULL)
330 		text = "";
331 
332 	store = GTK_LIST_STORE (select->model);
333 	n_children = gtk_tree_model_iter_n_children (select->model, NULL);
334 	if (n_children > 0) {
335 		path = gtk_tree_path_new_from_indices (n_children - 1, -1);
336 		gtk_tree_model_get_iter (select->model, &iter, path);
337 		gtk_tree_path_free (path);
338 	} else
339 		gtk_list_store_append (store, &iter);
340 	gtk_list_store_set (store, &iter, 0, text, -1);
341 	select->longest = MAX (select->longest, strlen (text));
342 
343 	if (select->size > 1 || select->multi) {
344 		GtkRequisition req;
345 		GtkWidget *scrollbar;
346 		gint width;
347 
348 		scrollbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (w));
349 		gtk_widget_get_preferred_size (select->view, &req, NULL);
350 		width = req.width;
351 
352 		if (n_children > select->size && scrollbar != NULL) {
353 			gtk_widget_get_preferred_size (scrollbar, &req, NULL);
354 			width += req.width + 8;
355 		}
356 
357 		gtk_widget_set_size_request (w, width, -1);
358 		HTML_OBJECT (select)->width = width;
359 	} else {
360 		w = HTML_EMBEDDED (select)->widget;
361 		gtk_combo_box_set_active_iter (GTK_COMBO_BOX (w), &iter);
362 
363 		gtk_entry_set_width_chars (
364 			GTK_ENTRY (gtk_bin_get_child (GTK_BIN (w))),
365 			select->longest);
366 		gtk_widget_set_size_request (w, -1, -1);
367 	}
368 }
369