Contents of /trunk/src/list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 264 - (hide annotations)
Tue Aug 11 14:33:14 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 14700 byte(s)
Fremantle treeview fixes
1 harbaum 146 /*
2 harbaum 262 * Copyright (C) 2008-2009 Till Harbaum <till@harbaum.org>.
3 harbaum 146 *
4     * This file is part of OSM2Go.
5     *
6     * OSM2Go is free software: you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * OSM2Go is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     /*
21     * list.c - generic implementation of a list style widget:
22     *
23     * +---------+-----------+
24     * | Key | Key |
25     * +---------+-----------+
26     * | Test1 Test2 ^|
27     * | Test3 Test4 #|
28     * | ||
29     * | v|
30     * +---------------------+
31     * ( Add )( Edit )(Remove)
32     */
33    
34     #include "appdata.h"
35    
36     #include <stdarg.h>
37    
38 harbaum 262 typedef struct {
39     GtkWidget *view;
40     GtkMenu *menu;
41    
42     struct {
43     gboolean(*func)(GtkTreeSelection *, GtkTreeModel *,
44     GtkTreePath *, gboolean,
45     gpointer);
46     gpointer data;
47     } sel;
48    
49     GtkWidget *table;
50    
51     struct {
52     gpointer data;
53     GtkWidget *widget[6];
54     } button;
55    
56     GtkTreeIter *iter;
57    
58     } list_priv_t;
59    
60     #if defined(USE_HILDON) && (MAEMO_VERSION_MAJOR == 5)
61     #define FREMANTLE
62 harbaum 264 #include <hildon/hildon-gtk.h>
63 harbaum 262 #include <hildon/hildon-pannable-area.h>
64 harbaum 264 // #define FREMANTLE_USE_POPUP
65 harbaum 262 #endif
66    
67 harbaum 264 #ifdef FREMANTLE_USE_POPUP
68 harbaum 262
69     static void cmenu_init(GtkWidget *list) {
70     list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
71     g_assert(priv);
72    
73     /* Create needed handles. */
74     priv->menu = GTK_MENU(gtk_menu_new());
75    
76     gtk_widget_tap_and_hold_setup(priv->view, GTK_WIDGET(priv->menu), NULL, 0);
77 harbaum 146 }
78    
79 harbaum 262 static GtkWidget *cmenu_append(GtkWidget *list, char *label,
80     GCallback cb, gpointer data) {
81     list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
82     g_assert(priv);
83    
84     GtkWidget *menu_item;
85    
86     /* Setup the map context menu. */
87     gtk_menu_append(priv->menu, menu_item
88     = gtk_menu_item_new_with_label(label));
89    
90     hildon_gtk_widget_set_theme_size(menu_item,
91     (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
92    
93 harbaum 264 gtk_signal_connect(GTK_OBJECT(menu_item), "activate",
94 harbaum 262 GTK_SIGNAL_FUNC(cb), data);
95    
96     gtk_widget_show_all(GTK_WIDGET(priv->menu));
97    
98     return menu_item;
99     }
100    
101     #endif
102    
103 harbaum 146 GtkWidget *list_get_view(GtkWidget *list) {
104 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
105     g_assert(priv);
106     return priv->view;
107 harbaum 146 }
108    
109     /* a list supports up to three user defined buttons besides */
110     /* add, edit and remove */
111     void list_set_user_buttons(GtkWidget *list, ...) {
112 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
113     g_assert(priv);
114    
115 harbaum 146 va_list ap;
116    
117     /* make space for user buttons */
118 harbaum 262 gtk_table_resize(GTK_TABLE(priv->table), 2, 3);
119 harbaum 146
120     va_start(ap, list);
121     list_button_t id = va_arg(ap, list_button_t);
122     while(id) {
123     char *label = va_arg(ap, char*);
124     GCallback cb = va_arg(ap, GCallback);
125    
126 harbaum 262 priv->button.widget[id] = gtk_button_new_with_label(label);
127     gtk_table_attach_defaults(GTK_TABLE(priv->table), priv->button.widget[id],
128 harbaum 146 id-LIST_BUTTON_USER0, id-LIST_BUTTON_USER0+1, 1, 2);
129 harbaum 262 gtk_signal_connect(GTK_OBJECT(priv->button.widget[id]), "clicked",
130     GTK_SIGNAL_FUNC(cb), priv->button.data);
131 harbaum 146
132     id = va_arg(ap, list_button_t);
133     }
134    
135     va_end(ap);
136     }
137    
138     void list_set_columns(GtkWidget *list, ...) {
139 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
140     g_assert(priv);
141 harbaum 146 va_list ap;
142    
143     va_start(ap, list);
144     char *name = va_arg(ap, char*);
145     while(name) {
146     int hlkey = -1, key = va_arg(ap, int);
147     int flags = va_arg(ap, int);
148    
149     if(flags & LIST_FLAG_CAN_HIGHLIGHT)
150     hlkey = va_arg(ap, int);
151    
152     GtkTreeViewColumn *column = NULL;
153 harbaum 148
154     if(flags & LIST_FLAG_TOGGLE) {
155     GCallback cb = va_arg(ap, GCallback);
156     gpointer data = va_arg(ap, gpointer);
157    
158     GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
159     column = gtk_tree_view_column_new_with_attributes(
160     name, renderer, "active", key, NULL);
161     g_signal_connect(renderer, "toggled", cb, data);
162    
163     } else if(flags & LIST_FLAG_STOCK_ICON) {
164     GtkCellRenderer *pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
165     column = gtk_tree_view_column_new_with_attributes(name,
166     pixbuf_renderer, "stock_id", key, NULL);
167     } else {
168 harbaum 146 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
169 harbaum 148
170     if(flags & LIST_FLAG_CAN_HIGHLIGHT)
171     g_object_set(renderer, "background", "red", NULL );
172    
173     if(flags & LIST_FLAG_ELLIPSIZE)
174     g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
175    
176 harbaum 146 column = gtk_tree_view_column_new_with_attributes(name, renderer,
177 harbaum 148 "text", key,
178 harbaum 146 (flags & LIST_FLAG_CAN_HIGHLIGHT)?"background-set":NULL, hlkey,
179     NULL);
180 harbaum 148
181 harbaum 155 gtk_tree_view_column_set_expand(column,
182     flags & (LIST_FLAG_EXPAND | LIST_FLAG_ELLIPSIZE));
183 harbaum 146 }
184    
185 harbaum 148 gtk_tree_view_column_set_sort_column_id(column, key);
186 harbaum 262 gtk_tree_view_insert_column(GTK_TREE_VIEW(priv->view), column, -1);
187 harbaum 146
188     name = va_arg(ap, char*);
189     }
190    
191     va_end(ap);
192     }
193    
194     static GtkWidget *list_button_get(GtkWidget *list, list_button_t id) {
195 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
196     g_assert(priv);
197    
198     return priv->button.widget[id];
199 harbaum 146 }
200    
201     void list_button_connect(GtkWidget *list, list_button_t id,
202     GCallback cb, gpointer data) {
203     GtkWidget *but = list_button_get(list, id);
204     gtk_signal_connect(GTK_OBJECT(but), "clicked", GTK_SIGNAL_FUNC(cb), data);
205     }
206    
207     /* put a custom widget into one of the button slots */
208     void list_set_custom_user_button(GtkWidget *list, list_button_t id,
209     GtkWidget *widget) {
210 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
211     g_assert(priv);
212 harbaum 146 g_assert((id >= 3) && (id < 6));
213    
214     /* make space for user buttons */
215 harbaum 262 gtk_table_resize(GTK_TABLE(priv->table), 2, 3);
216 harbaum 146
217 harbaum 262 gtk_table_attach_defaults(GTK_TABLE(priv->table), widget,
218 harbaum 146 id-LIST_BUTTON_USER0, id-LIST_BUTTON_USER0+1, 1, 2);
219 harbaum 262 priv->button.widget[id] = widget;
220 harbaum 146 }
221    
222     GtkTreeSelection *list_get_selection(GtkWidget *list) {
223 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
224     g_assert(priv);
225    
226     GtkTreeSelection *sel =
227     gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view));
228    
229     return sel;
230 harbaum 146 }
231    
232 harbaum 262 gboolean list_get_selected(GtkWidget *list, GtkTreeModel **model,
233     GtkTreeIter *iter) {
234     list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
235     g_assert(priv);
236    
237     GtkTreeSelection *sel =
238     gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view));
239    
240     return gtk_tree_selection_get_selected(sel, model, iter);
241     }
242    
243 harbaum 146 void list_button_enable(GtkWidget *list, list_button_t id, gboolean enable) {
244     GtkWidget *but = list_button_get(list, id);
245 harbaum 148 if(but) gtk_widget_set_sensitive(but, enable);
246 harbaum 146 }
247    
248     void list_set_store(GtkWidget *list, GtkListStore *store) {
249 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
250     g_assert(priv);
251    
252     gtk_tree_view_set_model(GTK_TREE_VIEW(priv->view), GTK_TREE_MODEL(store));
253 harbaum 146 }
254    
255     void list_set_selection_function(GtkWidget *list, GtkTreeSelectionFunc func,
256     gpointer data) {
257 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
258     g_assert(priv);
259    
260     priv->sel.func = func;
261     priv->sel.data = data;
262 harbaum 146 }
263    
264     /* default selection function enables edit and remove if a row is being */
265     /* selected */
266     static gboolean
267     list_selection_function(GtkTreeSelection *selection, GtkTreeModel *model,
268     GtkTreePath *path, gboolean path_currently_selected,
269     gpointer list) {
270 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
271     g_assert(priv);
272    
273 harbaum 146 GtkTreeIter iter;
274    
275 harbaum 262 printf("something has been selected\n");
276    
277 harbaum 146 if(gtk_tree_model_get_iter(model, &iter, path)) {
278     g_assert(gtk_tree_path_get_depth(path) == 1);
279    
280     list_button_enable(GTK_WIDGET(list), LIST_BUTTON_REMOVE, TRUE);
281     list_button_enable(GTK_WIDGET(list), LIST_BUTTON_EDIT, TRUE);
282     }
283 harbaum 262
284     if(priv->sel.func)
285     return priv->sel.func(selection, model, path, path_currently_selected,
286     priv->sel.data);
287    
288 harbaum 146 return TRUE; /* allow selection state to change */
289     }
290    
291 harbaum 262 static void on_row_activated(GtkTreeView *treeview,
292     GtkTreePath *path,
293     GtkTreeViewColumn *col,
294     gpointer userdata) {
295     GtkTreeIter iter;
296     GtkTreeModel *model = gtk_tree_view_get_model(treeview);
297    
298     if(gtk_tree_model_get_iter(model, &iter, path)) {
299     list_priv_t *priv = g_object_get_data(G_OBJECT(userdata), "priv");
300     g_assert(priv);
301    
302     GtkWidget *toplevel = gtk_widget_get_toplevel(GTK_WIDGET(treeview));
303     g_assert(GTK_IS_DIALOG(toplevel));
304    
305     /* XXX: save this selection to e.g. be accessible later on */
306    
307     /* emit a "response accept" signal so we might close the */
308     /* dialog */
309     gtk_dialog_response(GTK_DIALOG(toplevel), GTK_RESPONSE_ACCEPT);
310     }
311     }
312    
313 harbaum 218 void list_set_static_buttons(GtkWidget *list, gboolean first_new,
314     GCallback cb_new, GCallback cb_edit,
315     GCallback cb_remove, gpointer data) {
316 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
317     g_assert(priv);
318 harbaum 146
319 harbaum 262 priv->button.data = data;
320    
321 harbaum 146 /* add the three default buttons, but keep the disabled for now */
322     if(cb_new) {
323 harbaum 262 #if 0
324     /* this doesn't make sense in the context menu */
325     priv->button.widget[0] = cmenu_append(list, _(first_new?"New":"Add"),
326     GTK_SIGNAL_FUNC(cb_new), data);
327     #else
328     priv->button.widget[0] =
329     gtk_button_new_with_label(_(first_new?"New":"Add"));
330     #ifdef USE_HILDON
331     hildon_gtk_widget_set_theme_size(priv->button.widget[0],
332     (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
333     #endif
334     gtk_table_attach_defaults(GTK_TABLE(priv->table),
335     priv->button.widget[0], 0, 1, 0, 1);
336     gtk_signal_connect(GTK_OBJECT(priv->button.widget[0]), "clicked",
337 harbaum 146 GTK_SIGNAL_FUNC(cb_new), data);
338 harbaum 262 #endif
339     gtk_widget_set_sensitive(priv->button.widget[0], TRUE);
340 harbaum 146 }
341    
342     if(cb_edit) {
343 harbaum 264 #ifdef FREMANTLE_USE_POPUP
344 harbaum 262 priv->button.widget[1] = cmenu_append(list, _("Edit"),
345     GTK_SIGNAL_FUNC(cb_edit), data);
346     #else
347     priv->button.widget[1] = gtk_button_new_with_label(_("Edit"));
348     gtk_table_attach_defaults(GTK_TABLE(priv->table),
349     priv->button.widget[1], 1, 2, 0, 1);
350     gtk_signal_connect(GTK_OBJECT(priv->button.widget[1]), "clicked",
351 harbaum 146 GTK_SIGNAL_FUNC(cb_edit), data);
352 harbaum 262 #endif
353     gtk_widget_set_sensitive(priv->button.widget[1], FALSE);
354 harbaum 146 }
355    
356     if(cb_remove) {
357 harbaum 264 #ifdef FREMANTLE_USE_POPUP
358 harbaum 262 priv->button.widget[2] = cmenu_append(list, _("Remove"),
359     GTK_SIGNAL_FUNC(cb_remove), data);
360     #else
361     priv->button.widget[2] = gtk_button_new_with_label(_("Remove"));
362     gtk_table_attach_defaults(GTK_TABLE(priv->table),
363     priv->button.widget[2], 2, 3, 0, 1);
364     gtk_signal_connect(GTK_OBJECT(priv->button.widget[2]), "clicked",
365 harbaum 146 GTK_SIGNAL_FUNC(cb_remove), data);
366 harbaum 262 #endif
367     gtk_widget_set_sensitive(priv->button.widget[2], FALSE);
368 harbaum 146 }
369     }
370    
371     GtkTreeModel *list_get_model(GtkWidget *list) {
372 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
373     g_assert(priv);
374    
375     return gtk_tree_view_get_model(GTK_TREE_VIEW(priv->view));
376 harbaum 146 }
377    
378 harbaum 148 void list_pre_inplace_edit_tweak (GtkTreeModel *model) {
379     // Remove any current sort ordering, leaving items where they are.
380     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
381 harbaum 262 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
382     GTK_SORT_ASCENDING);
383 harbaum 148 }
384    
385    
386     /* Refocus a GtkTreeView an item specified by iter, unselecting the current
387     selection and optionally highlighting the new one. Typically called after
388     making an edit to an item with a covering sub-dialog. */
389    
390     void list_focus_on(GtkWidget *list, GtkTreeIter *iter, gboolean highlight) {
391 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
392     g_assert(priv);
393     GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->view));
394 harbaum 148
395     // Handle de/reselection
396 harbaum 262 GtkTreeSelection *sel =
397     gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view));
398 harbaum 148 gtk_tree_selection_unselect_all(sel);
399    
400     // Scroll to it, since it might now be out of view.
401     GtkTreePath *path = gtk_tree_model_get_path(model, iter);
402 harbaum 262 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(priv->view), path,
403     NULL, FALSE, 0, 0);
404 harbaum 148 gtk_tree_path_free(path);
405    
406     // reselect
407     if (highlight)
408     gtk_tree_selection_select_iter(sel, iter);
409     }
410    
411 harbaum 262 static gint on_list_destroy(GtkWidget *list, gpointer data) {
412     list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
413     g_assert(priv);
414 harbaum 148
415 harbaum 262 printf("destroy list\n");
416 harbaum 148
417 harbaum 262 g_free(priv);
418    
419     return FALSE;
420     }
421    
422 harbaum 146 /* a generic list widget with "add", "edit" and "remove" buttons as used */
423     /* for all kinds of lists in osm2go */
424 harbaum 148 #ifdef USE_HILDON
425     GtkWidget *list_new(gboolean show_headers)
426     #else
427     GtkWidget *list_new(void)
428     #endif
429     {
430 harbaum 262 list_priv_t *priv = g_new0(list_priv_t, 1);
431    
432 harbaum 146 GtkWidget *vbox = gtk_vbox_new(FALSE,3);
433 harbaum 262 g_object_set_data(G_OBJECT(vbox), "priv", priv);
434     g_signal_connect(G_OBJECT(vbox), "destroy",
435     G_CALLBACK(on_list_destroy), priv);
436 harbaum 148
437 harbaum 262 priv->view = gtk_tree_view_new();
438 harbaum 264 #ifdef FREMANTLE
439     hildon_gtk_tree_view_set_ui_mode(GTK_TREE_VIEW(priv->view),
440     HILDON_UI_MODE_EDIT);
441     #endif
442 harbaum 262
443 harbaum 148 #ifdef USE_HILDON
444     if(show_headers) {
445     /* hildon hides these by default */
446 harbaum 262 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(priv->view), TRUE);
447 harbaum 148 }
448     #endif
449    
450 harbaum 146 gtk_tree_selection_set_select_function(
451 harbaum 262 gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view)),
452 harbaum 146 list_selection_function, vbox, NULL);
453    
454 harbaum 262 #ifndef FREMANTLE
455 harbaum 146 /* put view into a scrolled window */
456     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
457     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
458     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
459     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
460     GTK_SHADOW_ETCHED_IN);
461 harbaum 262 gtk_container_add(GTK_CONTAINER(scrolled_window), priv->view);
462 harbaum 146 gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
463 harbaum 262 #else
464     /* put view into a pannable area */
465     GtkWidget *pannable_area = hildon_pannable_area_new();
466     gtk_container_add(GTK_CONTAINER(pannable_area), priv->view);
467     gtk_box_pack_start_defaults(GTK_BOX(vbox), pannable_area);
468 harbaum 146
469 harbaum 264 #ifdef FREMANTLE_USE_POPUP
470 harbaum 262 cmenu_init(vbox);
471     #endif
472 harbaum 264 #endif
473 harbaum 262
474     /* make list react on clicks (double clicks on pre-fremantle) */
475     g_signal_connect_after(GTK_OBJECT(priv->view), "row-activated",
476     (GCallback)on_row_activated, vbox);
477    
478 harbaum 146 /* add button box */
479 harbaum 262 priv->table = gtk_table_new(1, 3, TRUE);
480 harbaum 146
481 harbaum 262 gtk_box_pack_start(GTK_BOX(vbox), priv->table, FALSE, FALSE, 0);
482 harbaum 146
483     return vbox;
484     }
485