db0e68f8191557aa144da315dfa489a953a44723
[claws.git] / src / gtk / combobox.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2006-2012 Andrej Kacian and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtk.h>
29 #include "gtkutils.h"
30 #include "combobox.h"
31 #include "utils.h"
32
33 typedef struct _combobox_sel_by_data_ctx {
34         GtkComboBox *combobox;
35         gint data;
36         const gchar *cdata;
37 } ComboboxSelCtx;
38
39 GtkWidget *combobox_text_new(const gboolean with_entry, const gchar *text, ...)
40 {
41         GtkWidget *combo;
42         va_list args;
43         gchar *string;
44         
45         if(text == NULL)
46                 return NULL;
47         
48 #if !GTK_CHECK_VERSION(2, 24, 0)
49         if (with_entry)
50                 combo = gtk_combo_box_entry_new_text();
51         else
52                 combo = gtk_combo_box_new_text();
53 #else
54         if (with_entry)
55                 combo = gtk_combo_box_text_new_with_entry();
56         else
57                 combo = gtk_combo_box_text_new();
58 #endif
59         gtk_widget_show(combo);
60
61 #if !GTK_CHECK_VERSION(2, 24, 0)
62         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), text);
63 #else
64         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), text);
65 #endif
66         va_start(args, text);
67         while ((string = va_arg(args, gchar*)) != NULL)
68 #if !GTK_CHECK_VERSION(2, 24, 0)
69                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), string);
70 #else
71                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), string);
72 #endif
73         va_end(args);
74                 
75         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
76         
77         return combo;
78 }
79
80 static gboolean _select_by_data_func(GtkTreeModel *model,       GtkTreePath *path,
81                 GtkTreeIter *iter, ComboboxSelCtx *ctx)
82 {
83         GtkComboBox *combobox = ctx->combobox;
84         gint data = ctx->data;
85         gint curdata;
86
87         gtk_tree_model_get(GTK_TREE_MODEL(model), iter, COMBOBOX_DATA, &curdata, -1);
88         if (data == curdata) {
89                 gtk_combo_box_set_active_iter(combobox, iter);
90                 return TRUE;
91         }
92
93         return FALSE;
94 }
95
96 void combobox_select_by_data(GtkComboBox *combobox, gint data)
97 {
98         GtkTreeModel *model;
99         ComboboxSelCtx *ctx = NULL;
100
101         cm_return_if_fail(combobox != NULL);
102         cm_return_if_fail(GTK_IS_COMBO_BOX (combobox));
103
104         model = gtk_combo_box_get_model(combobox);
105
106         ctx = g_new(ComboboxSelCtx,
107                         sizeof(ComboboxSelCtx));
108         ctx->combobox = combobox;
109         ctx->data = data;
110
111         gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)_select_by_data_func, ctx);
112         g_free(ctx);
113 }
114
115 static gboolean _select_by_text_func(GtkTreeModel *model,       GtkTreePath *path,
116                 GtkTreeIter *iter, ComboboxSelCtx *ctx)
117 {
118         GtkComboBox *combobox = ctx->combobox;
119         const gchar *data = ctx->cdata;
120         const gchar *curdata;
121
122         cm_return_val_if_fail(combobox != NULL, FALSE);
123         cm_return_val_if_fail(GTK_IS_COMBO_BOX (combobox), FALSE);
124
125         gtk_tree_model_get (GTK_TREE_MODEL(model), iter, 0, &curdata, -1);
126         if (!g_utf8_collate(data, curdata)) {
127                 gtk_combo_box_set_active_iter(combobox, iter);
128                 return TRUE;
129         }
130
131         return FALSE;
132 }
133
134 void combobox_select_by_text(GtkComboBox *combobox, const gchar *data)
135 {
136         GtkTreeModel *model;
137         ComboboxSelCtx *ctx = NULL;
138 #if !GTK_CHECK_VERSION(2, 24, 0)
139         GtkComboBoxClass *class;
140 #endif
141
142         cm_return_if_fail(combobox != NULL);
143 #if !GTK_CHECK_VERSION(2, 24, 0)
144         class = GTK_COMBO_BOX_GET_CLASS (combobox);
145
146         /* we can do that only with gtk_combo_box_new_text() combo boxes */
147         cm_return_if_fail(class->get_active_text != NULL);
148 #else
149         cm_return_if_fail(GTK_IS_COMBO_BOX (combobox));
150 #endif
151
152         model = gtk_combo_box_get_model(combobox);
153
154         ctx = g_new(ComboboxSelCtx,
155                         sizeof(ComboboxSelCtx));
156         ctx->combobox = combobox;
157         ctx->cdata = data;
158
159         gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)_select_by_text_func, ctx);
160         g_free(ctx);
161 }
162
163 gint combobox_get_active_data(GtkComboBox *combobox)
164 {
165         GtkTreeModel *model;
166         GtkTreeIter iter;
167         gint data;
168
169         cm_return_val_if_fail(combobox != NULL, -1);
170         cm_return_val_if_fail(GTK_IS_COMBO_BOX (combobox), -1);
171         cm_return_val_if_fail(gtk_combo_box_get_active_iter(combobox, &iter), -1);
172
173         model = gtk_combo_box_get_model(combobox);
174
175         gtk_tree_model_get(model, &iter, COMBOBOX_DATA, &data, -1);
176
177         return data;
178 }
179
180 #if !GTK_CHECK_VERSION(2, 24, 0)
181 void combobox_unset_popdown_strings(GtkComboBox *combobox)
182 #else
183 void combobox_unset_popdown_strings(GtkComboBoxText *combobox)
184 #endif
185 {
186         GtkTreeModel *model;
187         gint count, i;
188
189         cm_return_if_fail(combobox != NULL);
190 #if GTK_CHECK_VERSION(2, 24, 0)
191         cm_return_if_fail(GTK_IS_COMBO_BOX_TEXT (combobox));
192 #endif
193
194         model = gtk_combo_box_get_model(GTK_COMBO_BOX(combobox));
195         count = gtk_tree_model_iter_n_children(model, NULL);
196         for (i = 0; i < count; i++)
197 #if !GTK_CHECK_VERSION(2, 24, 0)
198                 gtk_combo_box_remove_text(combobox, 0);
199 #else
200                 gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(combobox), 0);
201 #endif
202 }
203
204 #if !GTK_CHECK_VERSION(2, 24, 0)
205 void combobox_set_popdown_strings(GtkComboBox *combobox,
206 #else
207 void combobox_set_popdown_strings(GtkComboBoxText *combobox,
208 #endif
209                                  GList       *list)
210 {
211         GList *cur;
212
213         cm_return_if_fail(combobox != NULL);
214 #if GTK_CHECK_VERSION(2, 24, 0)
215         cm_return_if_fail(GTK_IS_COMBO_BOX_TEXT (combobox));
216 #endif
217
218         for (cur = list; cur != NULL; cur = g_list_next(cur))
219 #if !GTK_CHECK_VERSION(2, 24, 0)
220                 gtk_combo_box_append_text(combobox, (const gchar*) cur->data);
221 #else
222                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox),
223                                                 (const gchar*) cur->data);
224 #endif
225 }
226
227 gboolean combobox_set_value_from_arrow_key(GtkComboBox *combobox,
228                                  guint keyval)
229 /* used from key_press events upon gtk_combo_box_entry with one text column 
230    (gtk_combo_box_new_text() and with GtkComboBoxEntry's for instance),
231    make sure that up and down arrow keys behave the same as old with old
232    gtk_combo widgets:
233     when pressing Up:
234           if the current text in entry widget is not found in combo list,
235             get last value from combo list
236           if the current text in entry widget exists in combo list,
237             get prev value from combo list
238     when pressing Down:
239           if the current text in entry widget is not found in combo list,
240             get first value from combo list
241           if the current text in entry widget exists in combo list,
242             get next value from combo list
243 */
244 {
245         gboolean valid = FALSE;
246
247         cm_return_val_if_fail(combobox != NULL, FALSE);
248
249         /* reproduce the behaviour of old gtk_combo_box */
250         GtkTreeModel *model = gtk_combo_box_get_model(combobox);
251         GtkTreeIter iter;
252
253         if (gtk_combo_box_get_active_iter(combobox, &iter)) {
254                 /* if current text is in list, get prev or next one */
255
256                 if (keyval == GDK_KEY_Up) {
257 #if !GTK_CHECK_VERSION(2, 24, 0)
258                         gchar *text = gtk_combo_box_get_active_text(combobox);
259                         if (!text)
260                                 text = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(combobox))),0,-1);
261 #else
262                         gchar *text = NULL;
263                         gtk_tree_model_get(model, &iter, 0, &text, -1);
264 #endif
265                         valid = gtkut_tree_model_text_iter_prev(model, &iter, text);
266                         g_free(text);
267                 } else
268                 if (keyval == GDK_KEY_Down)
269                         valid = gtk_tree_model_iter_next(model, &iter);
270
271                 if (valid)
272                         gtk_combo_box_set_active_iter(combobox, &iter);
273
274         } else {
275                 /* current text is not in list, get first or next one */
276
277                 if (keyval == GDK_KEY_Up)
278                         valid = gtkut_tree_model_get_iter_last(model, &iter);
279                 else
280                 if (keyval == GDK_KEY_Down)
281                         valid = gtk_tree_model_get_iter_first(model, &iter);
282
283                 if (valid)
284                         gtk_combo_box_set_active_iter(combobox, &iter);
285         }
286
287         /* return TRUE if value could be set */
288         return valid;
289 }
290
291 static void store_set_sensitive(GtkTreeModel *model, GtkTreeIter *iter,
292                                 const gboolean sensitive)
293 {
294         if(GTK_IS_LIST_STORE(model)) {
295                 gtk_list_store_set(GTK_LIST_STORE(model), iter,
296                                    COMBOBOX_SENS, sensitive,
297                                    -1);
298         } else {
299                 gtk_tree_store_set(GTK_TREE_STORE(model), iter,
300                                    COMBOBOX_SENS, sensitive,
301                                    -1);
302         }
303 }
304
305 void combobox_set_sensitive(GtkComboBox *combobox, const guint index,
306                             const gboolean sensitive)
307 {
308         GtkTreeModel *model;
309         GtkTreeIter iter, child;
310         guint i;
311         
312         if((model = gtk_combo_box_get_model(combobox)) == NULL)
313                 return;
314         
315         gtk_tree_model_get_iter_first(model, &iter);
316         for(i=0; i<index; i++) {
317                 if(gtk_tree_model_iter_next(model, &iter) == FALSE)
318                         return;
319         }
320         
321         store_set_sensitive(model, &iter, sensitive);
322
323         if(gtk_tree_model_iter_children(model, &child, &iter) == FALSE)
324                 return;
325         
326         do {
327                 store_set_sensitive(model, &child, sensitive);
328         } while (gtk_tree_model_iter_next(model, &child) == TRUE);
329 }