Contents of /trunk/src/list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 282 - (hide annotations)
Fri Sep 4 11:35:48 2009 UTC (14 years, 9 months ago) by harbaum
File MIME type: text/plain
File size: 14527 byte(s)
Some debugging on hildontreeview crash
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     } list_priv_t;
57    
58 harbaum 282 #if 1
59 harbaum 262 #if defined(USE_HILDON) && (MAEMO_VERSION_MAJOR == 5)
60 harbaum 282 #define FREMANTLE_PANNABLE_AREA
61 harbaum 264 #include <hildon/hildon-gtk.h>
62 harbaum 262 #include <hildon/hildon-pannable-area.h>
63 harbaum 264 // #define FREMANTLE_USE_POPUP
64 harbaum 262 #endif
65 harbaum 282 #endif
66 harbaum 262
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 harbaum 266
185     gtk_tree_view_column_set_sort_column_id(column, key);
186     gtk_tree_view_insert_column(GTK_TREE_VIEW(priv->view), column, -1);
187    
188 harbaum 146 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     if(gtk_tree_model_get_iter(model, &iter, path)) {
276     g_assert(gtk_tree_path_get_depth(path) == 1);
277    
278     list_button_enable(GTK_WIDGET(list), LIST_BUTTON_REMOVE, TRUE);
279     list_button_enable(GTK_WIDGET(list), LIST_BUTTON_EDIT, TRUE);
280     }
281 harbaum 262
282     if(priv->sel.func)
283     return priv->sel.func(selection, model, path, path_currently_selected,
284     priv->sel.data);
285    
286 harbaum 146 return TRUE; /* allow selection state to change */
287     }
288    
289 harbaum 262 static void on_row_activated(GtkTreeView *treeview,
290     GtkTreePath *path,
291     GtkTreeViewColumn *col,
292     gpointer userdata) {
293     GtkTreeIter iter;
294     GtkTreeModel *model = gtk_tree_view_get_model(treeview);
295    
296 harbaum 266 printf("row activated\n");
297    
298 harbaum 262 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     /* emit a "response accept" signal so we might close the */
306     /* dialog */
307     gtk_dialog_response(GTK_DIALOG(toplevel), GTK_RESPONSE_ACCEPT);
308     }
309     }
310    
311 harbaum 266 void list_set_static_buttons(GtkWidget *list, int flags,
312 harbaum 218 GCallback cb_new, GCallback cb_edit,
313     GCallback cb_remove, gpointer data) {
314 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
315     g_assert(priv);
316 harbaum 146
317 harbaum 262 priv->button.data = data;
318    
319 harbaum 146 /* add the three default buttons, but keep the disabled for now */
320     if(cb_new) {
321 harbaum 262 priv->button.widget[0] =
322 harbaum 266 gtk_button_new_with_label(_((flags&LIST_BTN_NEW)?"New":"Add"));
323 harbaum 282 #ifdef FREMANTLE_PANNABLE_AREA
324 harbaum 266 if(flags & LIST_BTN_BIG)
325     hildon_gtk_widget_set_theme_size(priv->button.widget[0],
326     (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
327 harbaum 262 #endif
328     gtk_table_attach_defaults(GTK_TABLE(priv->table),
329     priv->button.widget[0], 0, 1, 0, 1);
330     gtk_signal_connect(GTK_OBJECT(priv->button.widget[0]), "clicked",
331 harbaum 146 GTK_SIGNAL_FUNC(cb_new), data);
332 harbaum 262 gtk_widget_set_sensitive(priv->button.widget[0], TRUE);
333 harbaum 146 }
334    
335     if(cb_edit) {
336 harbaum 264 #ifdef FREMANTLE_USE_POPUP
337 harbaum 262 priv->button.widget[1] = cmenu_append(list, _("Edit"),
338     GTK_SIGNAL_FUNC(cb_edit), data);
339     #else
340     priv->button.widget[1] = gtk_button_new_with_label(_("Edit"));
341     gtk_table_attach_defaults(GTK_TABLE(priv->table),
342     priv->button.widget[1], 1, 2, 0, 1);
343     gtk_signal_connect(GTK_OBJECT(priv->button.widget[1]), "clicked",
344 harbaum 146 GTK_SIGNAL_FUNC(cb_edit), data);
345 harbaum 262 #endif
346     gtk_widget_set_sensitive(priv->button.widget[1], FALSE);
347 harbaum 146 }
348    
349     if(cb_remove) {
350 harbaum 264 #ifdef FREMANTLE_USE_POPUP
351 harbaum 262 priv->button.widget[2] = cmenu_append(list, _("Remove"),
352     GTK_SIGNAL_FUNC(cb_remove), data);
353     #else
354     priv->button.widget[2] = gtk_button_new_with_label(_("Remove"));
355     gtk_table_attach_defaults(GTK_TABLE(priv->table),
356     priv->button.widget[2], 2, 3, 0, 1);
357     gtk_signal_connect(GTK_OBJECT(priv->button.widget[2]), "clicked",
358 harbaum 146 GTK_SIGNAL_FUNC(cb_remove), data);
359 harbaum 262 #endif
360     gtk_widget_set_sensitive(priv->button.widget[2], FALSE);
361 harbaum 146 }
362     }
363    
364     GtkTreeModel *list_get_model(GtkWidget *list) {
365 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
366     g_assert(priv);
367    
368     return gtk_tree_view_get_model(GTK_TREE_VIEW(priv->view));
369 harbaum 146 }
370    
371 harbaum 148 void list_pre_inplace_edit_tweak (GtkTreeModel *model) {
372     // Remove any current sort ordering, leaving items where they are.
373     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
374 harbaum 262 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
375     GTK_SORT_ASCENDING);
376 harbaum 148 }
377    
378    
379     /* Refocus a GtkTreeView an item specified by iter, unselecting the current
380     selection and optionally highlighting the new one. Typically called after
381     making an edit to an item with a covering sub-dialog. */
382    
383     void list_focus_on(GtkWidget *list, GtkTreeIter *iter, gboolean highlight) {
384 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
385     g_assert(priv);
386     GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->view));
387 harbaum 148
388     // Handle de/reselection
389 harbaum 262 GtkTreeSelection *sel =
390     gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view));
391 harbaum 148 gtk_tree_selection_unselect_all(sel);
392    
393     // Scroll to it, since it might now be out of view.
394     GtkTreePath *path = gtk_tree_model_get_path(model, iter);
395 harbaum 262 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(priv->view), path,
396     NULL, FALSE, 0, 0);
397 harbaum 148 gtk_tree_path_free(path);
398    
399     // reselect
400     if (highlight)
401     gtk_tree_selection_select_iter(sel, iter);
402     }
403    
404 harbaum 262 static gint on_list_destroy(GtkWidget *list, gpointer data) {
405     list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
406     g_assert(priv);
407 harbaum 148
408 harbaum 262 printf("destroy list\n");
409 harbaum 148
410 harbaum 262 g_free(priv);
411    
412     return FALSE;
413     }
414    
415 harbaum 146 /* a generic list widget with "add", "edit" and "remove" buttons as used */
416     /* for all kinds of lists in osm2go */
417 harbaum 148 #ifdef USE_HILDON
418     GtkWidget *list_new(gboolean show_headers)
419     #else
420     GtkWidget *list_new(void)
421     #endif
422     {
423 harbaum 262 list_priv_t *priv = g_new0(list_priv_t, 1);
424    
425 harbaum 146 GtkWidget *vbox = gtk_vbox_new(FALSE,3);
426 harbaum 262 g_object_set_data(G_OBJECT(vbox), "priv", priv);
427     g_signal_connect(G_OBJECT(vbox), "destroy",
428     G_CALLBACK(on_list_destroy), priv);
429 harbaum 148
430 harbaum 262 priv->view = gtk_tree_view_new();
431 harbaum 282 #ifdef FREMANTLE_PANNABLE_AREA
432 harbaum 264 hildon_gtk_tree_view_set_ui_mode(GTK_TREE_VIEW(priv->view),
433 harbaum 282 HILDON_UI_MODE_EDIT);
434 harbaum 264 #endif
435 harbaum 262
436 harbaum 148 #ifdef USE_HILDON
437     if(show_headers) {
438     /* hildon hides these by default */
439 harbaum 262 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(priv->view), TRUE);
440 harbaum 148 }
441     #endif
442    
443 harbaum 146 gtk_tree_selection_set_select_function(
444 harbaum 262 gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view)),
445 harbaum 146 list_selection_function, vbox, NULL);
446    
447 harbaum 282 #ifndef FREMANTLE_PANNABLE_AREA
448 harbaum 146 /* put view into a scrolled window */
449     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
450     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
451     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
452     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
453     GTK_SHADOW_ETCHED_IN);
454 harbaum 262 gtk_container_add(GTK_CONTAINER(scrolled_window), priv->view);
455 harbaum 146 gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
456 harbaum 262 #else
457     /* put view into a pannable area */
458     GtkWidget *pannable_area = hildon_pannable_area_new();
459     gtk_container_add(GTK_CONTAINER(pannable_area), priv->view);
460     gtk_box_pack_start_defaults(GTK_BOX(vbox), pannable_area);
461 harbaum 146
462 harbaum 264 #ifdef FREMANTLE_USE_POPUP
463 harbaum 262 cmenu_init(vbox);
464     #endif
465 harbaum 264 #endif
466 harbaum 262
467     /* make list react on clicks (double clicks on pre-fremantle) */
468     g_signal_connect_after(GTK_OBJECT(priv->view), "row-activated",
469     (GCallback)on_row_activated, vbox);
470    
471 harbaum 146 /* add button box */
472 harbaum 262 priv->table = gtk_table_new(1, 3, TRUE);
473 harbaum 146
474 harbaum 262 gtk_box_pack_start(GTK_BOX(vbox), priv->table, FALSE, FALSE, 0);
475 harbaum 146
476     return vbox;
477     }
478