Contents of /trunk/src/list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 315 - (hide annotations)
Wed Dec 16 20:07:58 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 15127 byte(s)
Various fremantleization
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 harbaum 289 int flags;
55 harbaum 262 } button;
56    
57     } list_priv_t;
58    
59 harbaum 264 #ifdef FREMANTLE_USE_POPUP
60 harbaum 262
61     static void cmenu_init(GtkWidget *list) {
62     list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
63     g_assert(priv);
64    
65     /* Create needed handles. */
66     priv->menu = GTK_MENU(gtk_menu_new());
67    
68     gtk_widget_tap_and_hold_setup(priv->view, GTK_WIDGET(priv->menu), NULL, 0);
69 harbaum 146 }
70    
71 harbaum 262 static GtkWidget *cmenu_append(GtkWidget *list, char *label,
72     GCallback cb, gpointer data) {
73     list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
74     g_assert(priv);
75    
76     GtkWidget *menu_item;
77    
78     /* Setup the map context menu. */
79     gtk_menu_append(priv->menu, menu_item
80     = gtk_menu_item_new_with_label(label));
81    
82     hildon_gtk_widget_set_theme_size(menu_item,
83     (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
84    
85 harbaum 264 gtk_signal_connect(GTK_OBJECT(menu_item), "activate",
86 harbaum 262 GTK_SIGNAL_FUNC(cb), data);
87    
88     gtk_widget_show_all(GTK_WIDGET(priv->menu));
89    
90     return menu_item;
91     }
92    
93     #endif
94    
95 harbaum 146 GtkWidget *list_get_view(GtkWidget *list) {
96 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
97     g_assert(priv);
98     return priv->view;
99 harbaum 146 }
100    
101     /* a list supports up to three user defined buttons besides */
102     /* add, edit and remove */
103     void list_set_user_buttons(GtkWidget *list, ...) {
104 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
105     g_assert(priv);
106    
107 harbaum 146 va_list ap;
108    
109     /* make space for user buttons */
110 harbaum 315 if(!(priv->button.flags & LIST_BTN_WIDE))
111     gtk_table_resize(GTK_TABLE(priv->table), 2, 3);
112     else
113     gtk_table_resize(GTK_TABLE(priv->table), 1, 5);
114 harbaum 146
115     va_start(ap, list);
116     list_button_t id = va_arg(ap, list_button_t);
117     while(id) {
118     char *label = va_arg(ap, char*);
119     GCallback cb = va_arg(ap, GCallback);
120    
121 harbaum 315 priv->button.widget[id] = button_new_with_label(label);
122     if(!(priv->button.flags & LIST_BTN_WIDE))
123     gtk_table_attach_defaults(GTK_TABLE(priv->table), priv->button.widget[id],
124     id-LIST_BUTTON_USER0, id-LIST_BUTTON_USER0+1, 1, 2);
125     else
126     gtk_table_attach_defaults(GTK_TABLE(priv->table), priv->button.widget[id],
127     3+id-LIST_BUTTON_USER0, 3+id-LIST_BUTTON_USER0+1, 0, 1);
128    
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 315 if(!(priv->button.flags & LIST_BTN_WIDE))
218     gtk_table_attach_defaults(GTK_TABLE(priv->table), widget,
219     id-LIST_BUTTON_USER0, id-LIST_BUTTON_USER0+1, 1, 2);
220     else
221     gtk_table_attach_defaults(GTK_TABLE(priv->table), widget,
222     3+id-LIST_BUTTON_USER0, 3+id-LIST_BUTTON_USER0+1, 0, 1);
223    
224 harbaum 262 priv->button.widget[id] = widget;
225 harbaum 146 }
226    
227     GtkTreeSelection *list_get_selection(GtkWidget *list) {
228 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
229     g_assert(priv);
230    
231     GtkTreeSelection *sel =
232     gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view));
233    
234     return sel;
235 harbaum 146 }
236    
237 harbaum 262 gboolean list_get_selected(GtkWidget *list, GtkTreeModel **model,
238     GtkTreeIter *iter) {
239     list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
240     g_assert(priv);
241    
242     GtkTreeSelection *sel =
243     gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view));
244    
245     return gtk_tree_selection_get_selected(sel, model, iter);
246     }
247    
248 harbaum 146 void list_button_enable(GtkWidget *list, list_button_t id, gboolean enable) {
249     GtkWidget *but = list_button_get(list, id);
250 harbaum 148 if(but) gtk_widget_set_sensitive(but, enable);
251 harbaum 146 }
252    
253     void list_set_store(GtkWidget *list, GtkListStore *store) {
254 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
255     g_assert(priv);
256    
257     gtk_tree_view_set_model(GTK_TREE_VIEW(priv->view), GTK_TREE_MODEL(store));
258 harbaum 146 }
259    
260     void list_set_selection_function(GtkWidget *list, GtkTreeSelectionFunc func,
261     gpointer data) {
262 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
263     g_assert(priv);
264    
265     priv->sel.func = func;
266     priv->sel.data = data;
267 harbaum 146 }
268    
269     /* default selection function enables edit and remove if a row is being */
270     /* selected */
271     static gboolean
272     list_selection_function(GtkTreeSelection *selection, GtkTreeModel *model,
273     GtkTreePath *path, gboolean path_currently_selected,
274     gpointer list) {
275 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
276     g_assert(priv);
277    
278 harbaum 146 GtkTreeIter iter;
279    
280     if(gtk_tree_model_get_iter(model, &iter, path)) {
281     g_assert(gtk_tree_path_get_depth(path) == 1);
282    
283 harbaum 298 /* this is now handled by the "changed" event handler */
284     // list_button_enable(GTK_WIDGET(list), LIST_BUTTON_REMOVE, TRUE);
285     // list_button_enable(GTK_WIDGET(list), LIST_BUTTON_EDIT, TRUE);
286 harbaum 146 }
287 harbaum 262
288     if(priv->sel.func)
289     return priv->sel.func(selection, model, path, path_currently_selected,
290     priv->sel.data);
291    
292 harbaum 146 return TRUE; /* allow selection state to change */
293     }
294    
295 harbaum 262 static void on_row_activated(GtkTreeView *treeview,
296     GtkTreePath *path,
297     GtkTreeViewColumn *col,
298     gpointer userdata) {
299     GtkTreeIter iter;
300     GtkTreeModel *model = gtk_tree_view_get_model(treeview);
301    
302 harbaum 266 printf("row activated\n");
303    
304 harbaum 262 if(gtk_tree_model_get_iter(model, &iter, path)) {
305     list_priv_t *priv = g_object_get_data(G_OBJECT(userdata), "priv");
306     g_assert(priv);
307    
308     GtkWidget *toplevel = gtk_widget_get_toplevel(GTK_WIDGET(treeview));
309     g_assert(GTK_IS_DIALOG(toplevel));
310    
311     /* emit a "response accept" signal so we might close the */
312     /* dialog */
313     gtk_dialog_response(GTK_DIALOG(toplevel), GTK_RESPONSE_ACCEPT);
314     }
315     }
316    
317 harbaum 266 void list_set_static_buttons(GtkWidget *list, int flags,
318 harbaum 218 GCallback cb_new, GCallback cb_edit,
319     GCallback cb_remove, gpointer data) {
320 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
321     g_assert(priv);
322 harbaum 146
323 harbaum 262 priv->button.data = data;
324 harbaum 289 priv->button.flags = flags;
325 harbaum 262
326 harbaum 146 /* add the three default buttons, but keep the disabled for now */
327     if(cb_new) {
328 harbaum 262 priv->button.widget[0] =
329 harbaum 315 button_new_with_label(_((flags&LIST_BTN_NEW)?"New":"Add"));
330 harbaum 262 gtk_table_attach_defaults(GTK_TABLE(priv->table),
331     priv->button.widget[0], 0, 1, 0, 1);
332     gtk_signal_connect(GTK_OBJECT(priv->button.widget[0]), "clicked",
333 harbaum 146 GTK_SIGNAL_FUNC(cb_new), data);
334 harbaum 262 gtk_widget_set_sensitive(priv->button.widget[0], TRUE);
335 harbaum 146 }
336    
337     if(cb_edit) {
338 harbaum 264 #ifdef FREMANTLE_USE_POPUP
339 harbaum 262 priv->button.widget[1] = cmenu_append(list, _("Edit"),
340     GTK_SIGNAL_FUNC(cb_edit), data);
341     #else
342 harbaum 315 priv->button.widget[1] = button_new_with_label(_("Edit"));
343 harbaum 262 gtk_table_attach_defaults(GTK_TABLE(priv->table),
344     priv->button.widget[1], 1, 2, 0, 1);
345     gtk_signal_connect(GTK_OBJECT(priv->button.widget[1]), "clicked",
346 harbaum 146 GTK_SIGNAL_FUNC(cb_edit), data);
347 harbaum 262 #endif
348     gtk_widget_set_sensitive(priv->button.widget[1], FALSE);
349 harbaum 146 }
350    
351     if(cb_remove) {
352 harbaum 264 #ifdef FREMANTLE_USE_POPUP
353 harbaum 262 priv->button.widget[2] = cmenu_append(list, _("Remove"),
354     GTK_SIGNAL_FUNC(cb_remove), data);
355     #else
356 harbaum 315 priv->button.widget[2] = button_new_with_label(_("Remove"));
357 harbaum 262 gtk_table_attach_defaults(GTK_TABLE(priv->table),
358     priv->button.widget[2], 2, 3, 0, 1);
359     gtk_signal_connect(GTK_OBJECT(priv->button.widget[2]), "clicked",
360 harbaum 146 GTK_SIGNAL_FUNC(cb_remove), data);
361 harbaum 262 #endif
362     gtk_widget_set_sensitive(priv->button.widget[2], FALSE);
363 harbaum 146 }
364     }
365    
366     GtkTreeModel *list_get_model(GtkWidget *list) {
367 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
368     g_assert(priv);
369    
370     return gtk_tree_view_get_model(GTK_TREE_VIEW(priv->view));
371 harbaum 146 }
372    
373 harbaum 148 void list_pre_inplace_edit_tweak (GtkTreeModel *model) {
374     // Remove any current sort ordering, leaving items where they are.
375     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
376 harbaum 262 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
377     GTK_SORT_ASCENDING);
378 harbaum 148 }
379    
380    
381     /* Refocus a GtkTreeView an item specified by iter, unselecting the current
382     selection and optionally highlighting the new one. Typically called after
383     making an edit to an item with a covering sub-dialog. */
384    
385     void list_focus_on(GtkWidget *list, GtkTreeIter *iter, gboolean highlight) {
386 harbaum 262 list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
387     g_assert(priv);
388     GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->view));
389 harbaum 148
390     // Handle de/reselection
391 harbaum 262 GtkTreeSelection *sel =
392     gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view));
393 harbaum 148 gtk_tree_selection_unselect_all(sel);
394    
395     // Scroll to it, since it might now be out of view.
396     GtkTreePath *path = gtk_tree_model_get_path(model, iter);
397 harbaum 262 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(priv->view), path,
398     NULL, FALSE, 0, 0);
399 harbaum 148 gtk_tree_path_free(path);
400    
401     // reselect
402     if (highlight)
403     gtk_tree_selection_select_iter(sel, iter);
404     }
405    
406 harbaum 262 static gint on_list_destroy(GtkWidget *list, gpointer data) {
407     list_priv_t *priv = g_object_get_data(G_OBJECT(list), "priv");
408     g_assert(priv);
409 harbaum 148
410 harbaum 262 g_free(priv);
411    
412     return FALSE;
413     }
414    
415 harbaum 298 static void changed(GtkTreeSelection *treeselection, gpointer user_data) {
416     GtkWidget *list = (GtkWidget*)user_data;
417    
418     GtkTreeModel *model;
419     GtkTreeIter iter;
420     gboolean selected = list_get_selected(list, &model, &iter);
421    
422     list_button_enable(GTK_WIDGET(list), LIST_BUTTON_REMOVE, selected);
423     list_button_enable(GTK_WIDGET(list), LIST_BUTTON_EDIT, selected);
424     }
425    
426 harbaum 146 /* a generic list widget with "add", "edit" and "remove" buttons as used */
427     /* for all kinds of lists in osm2go */
428 harbaum 148 #ifdef USE_HILDON
429     GtkWidget *list_new(gboolean show_headers)
430     #else
431     GtkWidget *list_new(void)
432     #endif
433     {
434 harbaum 262 list_priv_t *priv = g_new0(list_priv_t, 1);
435    
436 harbaum 146 GtkWidget *vbox = gtk_vbox_new(FALSE,3);
437 harbaum 262 g_object_set_data(G_OBJECT(vbox), "priv", priv);
438     g_signal_connect(G_OBJECT(vbox), "destroy",
439     G_CALLBACK(on_list_destroy), priv);
440 harbaum 148
441 harbaum 283 #ifndef FREMANTLE_PANNABLE_AREA
442 harbaum 262 priv->view = gtk_tree_view_new();
443 harbaum 283 #else
444     priv->view = hildon_gtk_tree_view_new(HILDON_UI_MODE_EDIT);
445 harbaum 264 #endif
446 harbaum 262
447 harbaum 148 #ifdef USE_HILDON
448     if(show_headers) {
449     /* hildon hides these by default */
450 harbaum 262 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(priv->view), TRUE);
451 harbaum 148 }
452     #endif
453    
454 harbaum 298 GtkTreeSelection *sel =
455     gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->view));
456    
457     gtk_tree_selection_set_select_function(sel,
458 harbaum 284 list_selection_function, vbox, NULL);
459 harbaum 146
460 harbaum 298 g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(changed), vbox);
461    
462 harbaum 282 #ifndef FREMANTLE_PANNABLE_AREA
463 harbaum 146 /* put view into a scrolled window */
464     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
465     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
466     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
467     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
468     GTK_SHADOW_ETCHED_IN);
469 harbaum 262 gtk_container_add(GTK_CONTAINER(scrolled_window), priv->view);
470 harbaum 146 gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
471 harbaum 262 #else
472     /* put view into a pannable area */
473     GtkWidget *pannable_area = hildon_pannable_area_new();
474     gtk_container_add(GTK_CONTAINER(pannable_area), priv->view);
475     gtk_box_pack_start_defaults(GTK_BOX(vbox), pannable_area);
476 harbaum 146
477 harbaum 264 #ifdef FREMANTLE_USE_POPUP
478 harbaum 262 cmenu_init(vbox);
479     #endif
480 harbaum 264 #endif
481 harbaum 262
482     /* make list react on clicks (double clicks on pre-fremantle) */
483     g_signal_connect_after(GTK_OBJECT(priv->view), "row-activated",
484     (GCallback)on_row_activated, vbox);
485    
486 harbaum 146 /* add button box */
487 harbaum 262 priv->table = gtk_table_new(1, 3, TRUE);
488 harbaum 146
489 harbaum 262 gtk_box_pack_start(GTK_BOX(vbox), priv->table, FALSE, FALSE, 0);
490 harbaum 146
491     return vbox;
492     }
493