Contents of /trunk/src/list.c

Parent Directory Parent Directory | Revision Log Revision Log


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