Contents of /trunk/src/list.c

Parent Directory Parent Directory | Revision Log Revision Log


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