Diff of /trunk/src/relation_edit.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 73 by harbaum, Thu Feb 12 14:27:52 2009 UTC revision 161 by harbaum, Sat Apr 11 11:28:56 2009 UTC
# Line 19  Line 19 
19    
20  #include "appdata.h"  #include "appdata.h"
21    
22    /* UI sizes */
23    /* TH: All this dialog size stuff should imho go into one central place */
24    
25    #ifdef USE_HILDON
26    // Making the dialog a little wider makes it less "crowded"
27    static const guint LIST_OF_RELATIONS_DIALOG_WIDTH   = 500;
28    static const guint LIST_OF_RELATIONS_DIALOG_HEIGHT  = 300;
29    static const guint LIST_OF_MEMBERS_DIALOG_WIDTH   = 500;
30    static const guint LIST_OF_MEMBERS_DIALOG_HEIGHT  = 300;
31    #else
32    // Desktop mode dialogs should be narrower and taller
33    static const guint LIST_OF_RELATIONS_DIALOG_WIDTH  = 475;
34    static const guint LIST_OF_RELATIONS_DIALOG_HEIGHT = 350;
35    static const guint LIST_OF_MEMBERS_DIALOG_WIDTH   = 450;
36    static const guint LIST_OF_MEMBERS_DIALOG_HEIGHT  = 350;
37    #endif
38    
39    
40  /* --------------- relation dialog for an item (node or way) ----------- */  /* --------------- relation dialog for an item (node or way) ----------- */
41    
42  typedef struct {  typedef struct {
43    relation_item_t *item;    object_t *item;
44    appdata_t *appdata;    appdata_t *appdata;
45    GtkWidget *dialog, *view;    GtkWidget *dialog, *list;
46    GtkListStore *store;    GtkListStore *store;
   GtkWidget *but_add, *but_edit, *but_remove;  
47  } relitem_context_t;  } relitem_context_t;
48    
49  enum {  enum {
# Line 44  typedef struct role_chain_s { Line 61  typedef struct role_chain_s {
61  } role_chain_t;  } role_chain_t;
62    
63  static gboolean relation_add_item(GtkWidget *parent,  static gboolean relation_add_item(GtkWidget *parent,
64                                relation_t *relation, relation_item_t *item) {                                relation_t *relation, object_t *object) {
65    role_chain_t *chain = NULL, **chainP = &chain;    role_chain_t *chain = NULL, **chainP = &chain;
66    
67    printf("add item of type %d to relation #%ld\n",    printf("add object of type %d to relation #" ITEM_ID_FORMAT "\n",
68           item->type, relation->id);           object->type, relation->id);
69    
70    /* ask the user for the role of the new item in this relation */    /* ask the user for the role of the new object in this relation */
71    
72    /* collect roles first */    /* collect roles first */
73    member_t *member = relation->member;    member_t *member = relation->member;
# Line 87  static gboolean relation_add_item(GtkWid Line 104  static gboolean relation_add_item(GtkWid
104    
105    char *info_str = NULL;    char *info_str = NULL;
106    if(type) info_str = g_strdup_printf(_("In relation of type: %s"), type);    if(type) info_str = g_strdup_printf(_("In relation of type: %s"), type);
107    else     info_str = g_strdup_printf(_("In relation #%ld"), relation->id);    else     info_str = g_strdup_printf(_("In relation #" ITEM_ID_FORMAT),
108                                          relation->id);
109    gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),    gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
110                                gtk_label_new(info_str));                                gtk_label_new(info_str));
111    g_free(info_str);    g_free(info_str);
# Line 143  static gboolean relation_add_item(GtkWid Line 161  static gboolean relation_add_item(GtkWid
161    member_t **memberP = &relation->member;    member_t **memberP = &relation->member;
162    while(*memberP) memberP = &(*memberP)->next;    while(*memberP) memberP = &(*memberP)->next;
163    
164      g_assert((object->type == NODE)||(object->type == WAY)||
165               (object->type == RELATION));
166    
167    /* create new member */    /* create new member */
168    *memberP = g_new0(member_t, 1);    *memberP = g_new0(member_t, 1);
169    (*memberP)->type = item->type;    (*memberP)->object = *object;
170    (*memberP)->role = role;    (*memberP)->role = role;
   switch(item->type) {  
   case NODE:  
     (*memberP)->node = item->node;  
     break;  
   case WAY:  
     (*memberP)->way = item->way;  
     break;  
   case RELATION:  
     (*memberP)->relation = item->relation;  
     break;  
   default:  
     g_assert((item->type == NODE)||(item->type == WAY)||  
              (item->type == RELATION));  
     break;  
   }  
171    
172    relation->flags |= OSM_FLAG_DIRTY;    relation->flags |= OSM_FLAG_DIRTY;
173    return TRUE;    return TRUE;
174  }  }
175    
176  static void relation_remove_item(relation_t *relation, relation_item_t *item) {  static void relation_remove_item(relation_t *relation, object_t *object) {
177    
178    printf("remove item of type %d from relation #%ld\n",    printf("remove object of type %d from relation #" ITEM_ID_FORMAT "\n",
179           item->type, relation->id);           object->type, relation->id);
180    
181    member_t **member = &relation->member;    member_t **member = &relation->member;
182    while(*member) {    while(*member) {
183      if(((*member)->type == item->type) &&      if(((*member)->object.type == object->type) &&
184         (((item->type == NODE) && (item->node == (*member)->node)) ||         (((object->type == NODE) &&
185          ((item->type == WAY) && (item->way == (*member)->way)) ||           (object->node == (*member)->object.node)) ||
186        ((item->type == RELATION) && (item->relation == (*member)->relation)))) {          ((object->type == WAY) &&
187             (object->way == (*member)->object.way)) ||
188            ((object->type == RELATION) &&
189             (object->relation == (*member)->object.relation)))) {
190    
191        member_t *next = (*member)->next;        member_t *next = (*member)->next;
192        osm_member_free(*member);        osm_member_free(*member);
# Line 192  static void relation_remove_item(relatio Line 201  static void relation_remove_item(relatio
201    g_assert(0);    g_assert(0);
202  }  }
203    
204  static void relation_list_selected(relitem_context_t *context,  static void relation_item_list_selected(relitem_context_t *context,
205                                     gboolean selected) {                                     gboolean selected) {
206    
207    if(context->but_remove)    list_button_enable(context->list, LIST_BUTTON_REMOVE, selected);
208      gtk_widget_set_sensitive(context->but_remove, FALSE);    list_button_enable(context->list, LIST_BUTTON_EDIT, selected);
   if(context->but_edit)  
     gtk_widget_set_sensitive(context->but_edit, selected);  
209  }  }
210    
211  static gboolean  /* try to find something descriptive */
212  relation_list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,  static char *relation_get_descriptive_name(relation_t *relation) {
213                       GtkTreePath *path, gboolean path_currently_selected,    char *name = osm_tag_get_by_key(relation->tag, "ref");
214                       gpointer userdata) {    if (!name)
215    relitem_context_t *context = (relitem_context_t*)userdata;      name = osm_tag_get_by_key(relation->tag, "name");
216    GtkTreeIter iter;    if (!name)
217        name = osm_tag_get_by_key(relation->tag, "note");
218    if(gtk_tree_model_get_iter(model, &iter, path)) {    if (!name)
219      g_assert(gtk_tree_path_get_depth(path) == 1);      name = osm_tag_get_by_key(relation->tag, "fix" "me");
220      relation_list_selected(context, TRUE);    return name;
221    }  }
222    
223    return TRUE; /* allow selection state to change */  static gboolean relation_info_dialog(GtkWidget *parent, appdata_t *appdata,
224  }                                       relation_t *relation) {
225    
226      object_t object = { .type = RELATION };
227      object.relation = relation;
228      return info_dialog(parent, appdata, &object);
229    }
230    
231  static void on_relation_add(GtkWidget *but, relitem_context_t *context) {  static void on_relation_item_add(GtkWidget *but, relitem_context_t *context) {
232    /* create a new relation */    /* create a new relation */
233    
234    relation_t *relation = osm_relation_new();    relation_t *relation = osm_relation_new();
235    if(!info_dialog(context->dialog, context->appdata, relation)) {    if(!relation_info_dialog(context->dialog, context->appdata, relation)) {
236      printf("tag edit cancelled\n");      printf("tag edit cancelled\n");
237      osm_relation_free(relation);      osm_relation_free(relation);
238    } else {    } else {
# Line 229  static void on_relation_add(GtkWidget *b Line 241  static void on_relation_add(GtkWidget *b
241      /* add to list */      /* add to list */
242    
243      /* append a row for the new data */      /* append a row for the new data */
244      /* try to find something descriptive */      char *name = relation_get_descriptive_name(relation);
     char *name = osm_tag_get_by_key(relation->tag, "name");  
     if(!name) name = osm_tag_get_by_key(relation->tag, "ref");  
245    
246      GtkTreeIter iter;      GtkTreeIter iter;
247      gtk_list_store_append(context->store, &iter);      gtk_list_store_append(context->store, &iter);
# Line 243  static void on_relation_add(GtkWidget *b Line 253  static void on_relation_add(GtkWidget *b
253                         RELITEM_COL_DATA, relation,                         RELITEM_COL_DATA, relation,
254                         -1);                         -1);
255    
256      gtk_tree_selection_select_iter(gtk_tree_view_get_selection(      gtk_tree_selection_select_iter(list_get_selection(context->list), &iter);
                GTK_TREE_VIEW(context->view)), &iter);  
   
     /* scroll to end */  
     //    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment();  
     /* xyz */  
257    }    }
258  }  }
259    
# Line 257  static relation_t *get_selection(relitem Line 262  static relation_t *get_selection(relitem
262    GtkTreeModel     *model;    GtkTreeModel     *model;
263    GtkTreeIter       iter;    GtkTreeIter       iter;
264    
265    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));    selection = list_get_selection(context->list);
266    if(gtk_tree_selection_get_selected(selection, &model, &iter)) {    if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
267      relation_t *relation;      relation_t *relation;
268      gtk_tree_model_get(model, &iter, RELITEM_COL_DATA, &relation, -1);      gtk_tree_model_get(model, &iter, RELITEM_COL_DATA, &relation, -1);
# Line 266  static relation_t *get_selection(relitem Line 271  static relation_t *get_selection(relitem
271    return NULL;    return NULL;
272  }  }
273    
274  static void on_relation_edit(GtkWidget *but, relitem_context_t *context) {  static void on_relation_item_edit(GtkWidget *but, relitem_context_t *context) {
275    relation_t *sel = get_selection(context);    relation_t *sel = get_selection(context);
276    if(!sel) return;    if(!sel) return;
277    
278    printf("edit relation #%ld\n", sel->id);    printf("edit relation item #" ITEM_ID_FORMAT "\n", sel->id);
279    
280      if (!relation_info_dialog(context->dialog, context->appdata, sel))
281        return;
282    
283      // Locate the changed item
284      GtkTreeIter iter;
285      gboolean valid = gtk_tree_model_get_iter_first(
286        GTK_TREE_MODEL(context->store), &iter);
287      while (valid) {
288        relation_t *row_rel;
289        gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
290                           RELITEM_COL_DATA, &row_rel,
291                           -1);
292        if (row_rel == sel)
293          break;
294        valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(context->store), &iter);
295      }
296      if (!valid)
297        return;
298    
299    info_dialog(context->dialog, context->appdata, sel);    // Found it. Update all visible fields that belong to the relation iself.
300      gtk_list_store_set(context->store, &iter,
301        RELITEM_COL_TYPE,    osm_tag_get_by_key(sel->tag, "type"),
302        RELITEM_COL_NAME,    relation_get_descriptive_name(sel),
303        -1);
304    
305      // Order will probably have changed, so refocus
306      list_focus_on(context->list, &iter, TRUE);
307  }  }
308    
309  static void on_relation_remove(GtkWidget *but, relitem_context_t *context) {  /* remove the selected relation */
310    static void on_relation_item_remove(GtkWidget *but, relitem_context_t *context) {
311    relation_t *sel = get_selection(context);    relation_t *sel = get_selection(context);
312    if(!sel) return;    if(!sel) return;
313    
314    printf("remove relation #%ld\n", sel->id);    printf("remove relation #" ITEM_ID_FORMAT "\n", sel->id);
315    
316      gint members = osm_relation_members_num(sel);
317    
318      if(members)
319        if(!yes_no_f(context->dialog, NULL, 0, 0,
320                     _("Delete non-empty relation?"),
321                     _("This relation still has %d members. "
322                       "Delete it anyway?"), members))
323          return;
324    
325      /* first remove selected row from list */
326      GtkTreeIter       iter;
327      GtkTreeSelection *selection = list_get_selection(context->list);
328      if(gtk_tree_selection_get_selected(selection, NULL, &iter))
329        gtk_list_store_remove(context->store, &iter);
330    
331      /* then really delete it */
332      osm_relation_delete(context->appdata->osm, sel, FALSE);
333    
334      relation_item_list_selected(context, FALSE);
335  }  }
336    
337  static char *relitem_get_role_in_relation(relation_item_t *item, relation_t *relation) {  static char *relitem_get_role_in_relation(object_t *item, relation_t *relation) {
338    member_t *member = relation->member;    member_t *member = relation->member;
339    while(member) {    while(member) {
340      switch(member->type) {      switch(member->object.type) {
341    
342      case NODE:      case NODE:
343        if((item->type == NODE) && (item->node == member->node))        if((item->type == NODE) && (item->node == member->object.node))
344          return member->role;          return member->role;
345        break;        break;
346    
347      case WAY:      case WAY:
348        if((item->type == WAY) && (item->way == member->way))        if((item->type == WAY) && (item->way == member->object.way))
349          return member->role;          return member->role;
350        break;        break;
351    
# Line 307  static char *relitem_get_role_in_relatio Line 359  static char *relitem_get_role_in_relatio
359    
360  static void  static void
361  relitem_toggled(GtkCellRendererToggle *cell, const gchar *path_str,  relitem_toggled(GtkCellRendererToggle *cell, const gchar *path_str,
362                relitem_context_t *context) {                  relitem_context_t *context) {
363    GtkTreePath *path;    GtkTreePath *path;
364    GtkTreeIter iter;    GtkTreeIter iter;
365    
# Line 323  relitem_toggled(GtkCellRendererToggle *c Line 375  relitem_toggled(GtkCellRendererToggle *c
375                       RELITEM_COL_DATA, &relation,                       RELITEM_COL_DATA, &relation,
376                       -1);                       -1);
377    
378      list_pre_inplace_edit_tweak(GTK_TREE_MODEL(context->store));
379    
380    if(!enabled) {    if(!enabled) {
381      printf("will now become be part of this relation\n");      printf("will now become be part of this relation\n");
382      if(relation_add_item(context->dialog, relation, context->item))      if(relation_add_item(context->dialog, relation, context->item))
# Line 339  relitem_toggled(GtkCellRendererToggle *c Line 393  relitem_toggled(GtkCellRendererToggle *c
393                         RELITEM_COL_ROLE, NULL,                         RELITEM_COL_ROLE, NULL,
394                         -1);                         -1);
395    }    }
396    
397  }  }
398    
399  static gboolean relitem_is_in_relation(relation_item_t *item, relation_t *relation) {  static gboolean relitem_is_in_relation(object_t *item, relation_t *relation) {
400    member_t *member = relation->member;    member_t *member = relation->member;
401    while(member) {    while(member) {
402      switch(member->type) {      switch(member->object.type) {
403    
404      case NODE:      case NODE:
405        if((item->type == NODE) && (item->node == member->node))        if((item->type == NODE) && (item->node == member->object.node))
406          return TRUE;          return TRUE;
407        break;        break;
408    
409      case WAY:      case WAY:
410        if((item->type == WAY) && (item->way == member->way))        if((item->type == WAY) && (item->way == member->object.way))
411          return TRUE;          return TRUE;
412        break;        break;
413    
# Line 364  static gboolean relitem_is_in_relation(r Line 419  static gboolean relitem_is_in_relation(r
419    return FALSE;    return FALSE;
420  }  }
421    
422  static GtkWidget *relation_list(relitem_context_t *context) {  static GtkWidget *relation_item_list_widget(relitem_context_t *context) {
423      context->list = list_new(LIST_HILDON_WITH_HEADERS);
424    
425      list_set_columns(context->list,
426                       _(""), RELITEM_COL_SELECTED, LIST_FLAG_TOGGLE,
427                                G_CALLBACK(relitem_toggled), context,
428                       _("Type"), RELITEM_COL_TYPE, 0,
429                       _("Role"), RELITEM_COL_ROLE, 0,
430                       _("Name"), RELITEM_COL_NAME, LIST_FLAG_ELLIPSIZE,
431                       NULL);
432    
433      /* build and fill the store */
434      context->store = gtk_list_store_new(RELITEM_NUM_COLS,
435                    G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING,
436                    G_TYPE_STRING, G_TYPE_POINTER);
437    
438      list_set_store(context->list, context->store);
439    
440      // Debatable whether to sort by the "selected" or the "Name" column by
441      // default. Both are be useful, in different ways.
442      gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(context->store),
443                                           RELITEM_COL_NAME, GTK_SORT_ASCENDING);
444    
445      GtkTreeIter iter;
446      relation_t *relation = context->appdata->osm->relation;
447      while(relation) {
448        /* try to find something descriptive */
449        char *name = relation_get_descriptive_name(relation);
450    
451        /* Append a row and fill in some data */
452        gtk_list_store_append(context->store, &iter);
453        gtk_list_store_set(context->store, &iter,
454           RELITEM_COL_SELECTED, relitem_is_in_relation(context->item, relation),
455           RELITEM_COL_TYPE, osm_tag_get_by_key(relation->tag, "type"),
456           RELITEM_COL_ROLE, relitem_get_role_in_relation(context->item, relation),
457           RELITEM_COL_NAME, name,
458           RELITEM_COL_DATA, relation,
459           -1);
460    
461        relation = relation->next;
462      }
463    
464      g_object_unref(context->store);
465    
466      list_set_static_buttons(context->list, G_CALLBACK(on_relation_item_add),
467            G_CALLBACK(on_relation_item_edit),G_CALLBACK(on_relation_item_remove),
468            context);
469    
470      relation_item_list_selected(context, FALSE);
471    
472      return context->list;
473    }
474    
475    void relation_add_dialog(appdata_t *appdata, object_t *object) {
476      relitem_context_t *context = g_new0(relitem_context_t, 1);
477      map_t *map = appdata->map;
478      g_assert(map);
479    
480      context->appdata = appdata;
481      context->item = object;
482    
483      char *str = NULL;
484      switch(object->type) {
485      case NODE:
486        str = g_strdup_printf(_("Relations for node #" ITEM_ID_FORMAT),
487                              object->node->id);
488        break;
489      case WAY:
490        str = g_strdup_printf(_("Relations for way #" ITEM_ID_FORMAT),
491                              object->way->id);
492        break;
493      default:
494        g_assert((object->type == NODE) || (object->type == WAY));
495        break;
496      }
497    
498      context->dialog = gtk_dialog_new_with_buttons(str,
499            GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
500            GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
501            NULL);
502      g_free(str);
503    
504      gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
505                                      GTK_RESPONSE_CLOSE);
506    
507      gtk_window_set_default_size(GTK_WINDOW(context->dialog),
508                                  LIST_OF_RELATIONS_DIALOG_WIDTH,
509                                  LIST_OF_RELATIONS_DIALOG_HEIGHT);
510      gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
511                         relation_item_list_widget(context), TRUE, TRUE, 0);
512    
513      /* ----------------------------------- */
514    
515      gtk_widget_show_all(context->dialog);
516      gtk_dialog_run(GTK_DIALOG(context->dialog));
517      gtk_widget_destroy(context->dialog);
518    
519      g_free(context);
520    }
521    
522    /* -------------------- global relation list ----------------- */
523    
524    typedef struct {
525      appdata_t *appdata;
526      GtkWidget *dialog, *list, *show_btn;
527      GtkListStore *store;
528    } relation_context_t;
529    
530    enum {
531      RELATION_COL_ID = 0,
532      RELATION_COL_TYPE,
533      RELATION_COL_NAME,
534      RELATION_COL_MEMBERS,
535      RELATION_COL_DATA,
536      RELATION_NUM_COLS
537    };
538    
539    static relation_t *get_selected_relation(relation_context_t *context) {
540      GtkTreeSelection *selection;
541      GtkTreeModel     *model;
542      GtkTreeIter       iter;
543    
544      selection = list_get_selection(context->list);
545      if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
546        relation_t *relation;
547        gtk_tree_model_get(model, &iter, RELATION_COL_DATA, &relation, -1);
548        return(relation);
549      }
550      return NULL;
551    }
552    
553    static void relation_list_selected(relation_context_t *context,
554                                       relation_t *selected) {
555    
556      list_button_enable(context->list, LIST_BUTTON_USER0,
557                         (selected != NULL) && (selected->member != NULL));
558      gtk_widget_set_sensitive(context->show_btn,
559                         (selected != NULL) && (selected->member != NULL));
560    
561      list_button_enable(context->list, LIST_BUTTON_REMOVE, selected != NULL);
562      list_button_enable(context->list, LIST_BUTTON_EDIT, selected != NULL);
563    }
564    
565    static gboolean
566    relation_list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
567                         GtkTreePath *path, gboolean path_currently_selected,
568                         gpointer userdata) {
569      relation_context_t *context = (relation_context_t*)userdata;
570      GtkTreeIter iter;
571    
572      if(gtk_tree_model_get_iter(model, &iter, path)) {
573        g_assert(gtk_tree_path_get_depth(path) == 1);
574    
575        relation_t *relation = NULL;
576        gtk_tree_model_get(model, &iter, RELATION_COL_DATA, &relation, -1);
577        relation_list_selected(context, relation);
578      }
579    
580      return TRUE; /* allow selection state to change */
581    }
582    
583    typedef struct {
584      relation_t *relation;
585      GtkWidget *dialog, *view;
586      GtkListStore *store;
587    } member_context_t;
588    
589    enum {
590      MEMBER_COL_TYPE = 0,
591      MEMBER_COL_ID,
592      MEMBER_COL_NAME,
593      MEMBER_COL_ROLE,
594      MEMBER_COL_REF_ONLY,
595      MEMBER_COL_DATA,
596      MEMBER_NUM_COLS
597    };
598    
599    static gboolean
600    member_list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
601                         GtkTreePath *path, gboolean path_currently_selected,
602                         gpointer userdata) {
603      GtkTreeIter iter;
604    
605      if(gtk_tree_model_get_iter(model, &iter, path)) {
606        g_assert(gtk_tree_path_get_depth(path) == 1);
607    
608        member_t *member = NULL;
609        gtk_tree_model_get(model, &iter, MEMBER_COL_DATA, &member, -1);
610        if(member && member->object.type < NODE_ID)
611          return TRUE;
612      }
613    
614      return FALSE;
615    }
616    
617    
618    static GtkWidget *member_list_widget(member_context_t *context) {
619    GtkWidget *vbox = gtk_vbox_new(FALSE,3);    GtkWidget *vbox = gtk_vbox_new(FALSE,3);
620    context->view = gtk_tree_view_new();    context->view = gtk_tree_view_new();
621    
622    gtk_tree_selection_set_select_function(    gtk_tree_selection_set_select_function(
623           gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),           gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),
624           relation_list_selection_func,           member_list_selection_func,
625           context, NULL);           context, NULL);
626    
627      /* --- "type" column --- */
628      GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
629      g_object_set(renderer, "foreground", "grey", NULL);
630      GtkTreeViewColumn *column =
631        gtk_tree_view_column_new_with_attributes(_("Type"), renderer,
632                         "text", MEMBER_COL_TYPE,
633                         "foreground-set", MEMBER_COL_REF_ONLY,  NULL);
634      gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_TYPE);
635      gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
636    
637    /* --- "selected" column --- */    /* --- "id" column --- */
   GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();  
   g_signal_connect(renderer, "toggled", G_CALLBACK(relitem_toggled), context);  
   gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),  
         -1, _(""), renderer,  
         "active", RELITEM_COL_SELECTED,  
         NULL);  
   
   /* --- "Type" column --- */  
638    renderer = gtk_cell_renderer_text_new();    renderer = gtk_cell_renderer_text_new();
639    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),    g_object_set(renderer, "foreground", "grey", NULL);
640          -1, _("Type"), renderer, "text", RELITEM_COL_TYPE, NULL);    column = gtk_tree_view_column_new_with_attributes(_("Id"), renderer,
641                         "text", MEMBER_COL_ID,
642                         "foreground-set", MEMBER_COL_REF_ONLY,  NULL);
643      gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_ID);
644      gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
645    
   /* --- "Role" column --- */  
   renderer = gtk_cell_renderer_text_new();  
   gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),  
         -1, _("Role"), renderer, "text", RELITEM_COL_ROLE, NULL);  
646    
647    /* --- "Name" column --- */    /* --- "Name" column --- */
648    renderer = gtk_cell_renderer_text_new();    renderer = gtk_cell_renderer_text_new();
649      g_object_set(renderer, "foreground", "grey", NULL);
650    g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);    g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
651    GtkTreeViewColumn *column =    column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
652      gtk_tree_view_column_new_with_attributes(_("Name"), renderer,                       "text", MEMBER_COL_NAME,
653                   "text", RELITEM_COL_NAME, NULL);                       "foreground-set", MEMBER_COL_REF_ONLY,  NULL);
654    gtk_tree_view_column_set_expand(column, TRUE);    gtk_tree_view_column_set_expand(column, TRUE);
655      gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_NAME);
656      gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
657    
658      /* --- "role" column --- */
659      renderer = gtk_cell_renderer_text_new();
660      g_object_set(renderer, "foreground", "grey", NULL);
661      column = gtk_tree_view_column_new_with_attributes(_("Role"), renderer,
662                         "text", MEMBER_COL_ROLE,
663                         "foreground-set", MEMBER_COL_REF_ONLY,  NULL);
664      gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_ROLE);
665    gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);    gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
666    
667    
668    /* build and fill the store */    /* build and fill the store */
669    context->store = gtk_list_store_new(RELITEM_NUM_COLS,    context->store = gtk_list_store_new(MEMBER_NUM_COLS,
670                  G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING,                G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
671                  G_TYPE_STRING, G_TYPE_POINTER);                G_TYPE_BOOLEAN, G_TYPE_POINTER);
672    
673    gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),    gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),
674                            GTK_TREE_MODEL(context->store));                            GTK_TREE_MODEL(context->store));
675    
676    GtkTreeIter iter;    GtkTreeIter iter;
677    relation_t *relation = context->appdata->osm->relation;    member_t *member = context->relation->member;
678    while(relation) {    while(member) {
679        tag_t *tags = osm_object_get_tags(&member->object);
680        char *id = osm_object_id_string(&member->object);
681    
682      /* try to find something descriptive */      /* try to find something descriptive */
683      char *name = osm_tag_get_by_key(relation->tag, "name");      char *name = NULL;
684      if(!name) name = osm_tag_get_by_key(relation->tag, "ref");      if(tags)
685          name = osm_tag_get_by_key(tags, "name");
686    
687      /* Append a row and fill in some data */      /* Append a row and fill in some data */
688      gtk_list_store_append(context->store, &iter);      gtk_list_store_append(context->store, &iter);
689      gtk_list_store_set(context->store, &iter,      gtk_list_store_set(context->store, &iter,
690         RELITEM_COL_SELECTED, relitem_is_in_relation(context->item, relation),         MEMBER_COL_TYPE, osm_object_type_string(&member->object),
691         RELITEM_COL_TYPE, osm_tag_get_by_key(relation->tag, "type"),         MEMBER_COL_ID,   id,
692         RELITEM_COL_ROLE, relitem_get_role_in_relation(context->item, relation),         MEMBER_COL_NAME, name,
693         RELITEM_COL_NAME, name,         MEMBER_COL_ROLE, member->role,
694         RELITEM_COL_DATA, relation,         MEMBER_COL_REF_ONLY, member->object.type >= NODE_ID,
695           MEMBER_COL_DATA, member,
696         -1);         -1);
697    
698      relation = relation->next;      g_free(id);
699        member = member->next;
700    }    }
701    
702    g_object_unref(context->store);    g_object_unref(context->store);
703    
704    /* put it into a scrolled window */    /* put it into a scrolled window */
# Line 442  static GtkWidget *relation_list(relitem_ Line 711  static GtkWidget *relation_list(relitem_
711    
712    gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);    gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
713    
714    /* ------- button box ------------ */    return vbox;
715    }
716    
717    void relation_show_members(GtkWidget *parent, relation_t *relation) {
718      member_context_t *mcontext = g_new0(member_context_t, 1);
719      mcontext->relation = relation;
720    
721      char *str = osm_tag_get_by_key(mcontext->relation->tag, "name");
722      if(!str) str = osm_tag_get_by_key(mcontext->relation->tag, "ref");
723      if(!str)
724        str = g_strdup_printf(_("Members of relation #" ITEM_ID_FORMAT),
725                              mcontext->relation->id);
726      else
727        str = g_strdup_printf(_("Members of relation \"%s\""), str);
728    
729    GtkWidget *hbox = gtk_hbox_new(TRUE,3);    mcontext->dialog =
730        gtk_dialog_new_with_buttons(str,
731            GTK_WINDOW(parent), GTK_DIALOG_MODAL,
732            GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
733            NULL);
734      g_free(str);
735    
736      gtk_dialog_set_default_response(GTK_DIALOG(mcontext->dialog),
737                                      GTK_RESPONSE_CLOSE);
738    
739    context->but_add = gtk_button_new_with_label(_("Add..."));    gtk_window_set_default_size(GTK_WINDOW(mcontext->dialog),
740    //  gtk_widget_set_sensitive(context->but_add, FALSE);                                LIST_OF_MEMBERS_DIALOG_WIDTH,
741    gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_add);                                LIST_OF_MEMBERS_DIALOG_HEIGHT);
   gtk_signal_connect(GTK_OBJECT(context->but_add), "clicked",  
                      GTK_SIGNAL_FUNC(on_relation_add), context);  
742    
743    context->but_edit = gtk_button_new_with_label(_("Edit..."));    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mcontext->dialog)->vbox),
744    gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_edit);                       member_list_widget(mcontext), TRUE, TRUE, 0);
   gtk_signal_connect(GTK_OBJECT(context->but_edit), "clicked",  
                      GTK_SIGNAL_FUNC(on_relation_edit), context);  
745    
746    context->but_remove = gtk_button_new_with_label(_("Remove"));    /* ----------------------------------- */
   gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_remove);  
   gtk_signal_connect(GTK_OBJECT(context->but_remove), "clicked",  
                      GTK_SIGNAL_FUNC(on_relation_remove), context);  
747    
748    relation_list_selected(context, FALSE);    gtk_widget_show_all(mcontext->dialog);
749      gtk_dialog_run(GTK_DIALOG(mcontext->dialog));
750      gtk_widget_destroy(mcontext->dialog);
751    
752    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);    g_free(mcontext);
   return vbox;  
753  }  }
754    
755  void relation_add_dialog(appdata_t *appdata, relation_item_t *relitem) {  /* user clicked "members..." button in relation list */
756    relitem_context_t *context = g_new0(relitem_context_t, 1);  static void on_relation_members(GtkWidget *but, relation_context_t *context) {
757    map_t *map = appdata->map;    relation_t *sel = get_selected_relation(context);
758    g_assert(map);  
759      if(sel)
760        relation_show_members(context->dialog, sel);
761    }
762    
   context->appdata = appdata;  
   context->item = relitem;  
763    
764    char *str = NULL;  static void on_relation_add(GtkWidget *but, relation_context_t *context) {
765    switch(relitem->type) {    /* create a new relation */
766    case NODE:  
767      str = g_strdup_printf(_("Relations for node #%ld"), relitem->node->id);    relation_t *relation = osm_relation_new();
768      break;    if(!relation_info_dialog(context->dialog, context->appdata, relation)) {
769    case WAY:      printf("tag edit cancelled\n");
770      str = g_strdup_printf(_("Relations for way #%ld"), relitem->way->id);      osm_relation_free(relation);
771      break;    } else {
772    default:      osm_relation_attach(context->appdata->osm, relation);
773      g_assert((relitem->type == NODE) || (relitem->type == WAY));  
774      break;      /* append a row for the new data */
775    
776        char *name = relation_get_descriptive_name(relation);
777    
778        guint num = osm_relation_members_num(relation);
779    
780        /* Append a row and fill in some data */
781        GtkTreeIter iter;
782        gtk_list_store_append(context->store, &iter);
783        gtk_list_store_set(context->store, &iter,
784                           RELATION_COL_ID, relation->id,
785                           RELATION_COL_TYPE,
786                           osm_tag_get_by_key(relation->tag, "type"),
787                           RELATION_COL_NAME, name,
788                           RELATION_COL_MEMBERS, num,
789                           RELATION_COL_DATA, relation,
790                           -1);
791    
792        gtk_tree_selection_select_iter(list_get_selection(context->list), &iter);
793    
794        /* scroll to end */
795        //    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment();
796        /* xyz */
797    }    }
798    }
799    
800    /* user clicked "edit..." button in relation list */
801    static void on_relation_edit(GtkWidget *but, relation_context_t *context) {
802      relation_t *sel = get_selected_relation(context);
803      if(!sel) return;
804    
805      printf("edit relation #" ITEM_ID_FORMAT "\n", sel->id);
806    
807      if (!relation_info_dialog(context->dialog, context->appdata, sel))
808        return;
809    
810      // Locate the changed item
811      GtkTreeIter iter;
812      gboolean valid = gtk_tree_model_get_iter_first(
813        GTK_TREE_MODEL(context->store), &iter);
814      while (valid) {
815        relation_t *row_rel;
816        gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
817                           RELATION_COL_DATA, &row_rel,
818                           -1);
819        if (row_rel == sel)
820          break;
821        valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(context->store), &iter);
822      }
823      if (!valid)
824        return;
825    
826      // Found it. Update all visible fields.
827      gtk_list_store_set(context->store, &iter,
828        RELATION_COL_ID,      sel->id,
829        RELATION_COL_TYPE,    osm_tag_get_by_key(sel->tag, "type"),
830        RELATION_COL_NAME,    relation_get_descriptive_name(sel),
831        RELATION_COL_MEMBERS, osm_relation_members_num(sel),
832        -1);
833    
834      // Order will probably have changed, so refocus
835      list_focus_on(context->list, &iter, TRUE);
836    }
837    
838    
839    /* remove the selected relation */
840    static void on_relation_remove(GtkWidget *but, relation_context_t *context) {
841      relation_t *sel = get_selected_relation(context);
842      if(!sel) return;
843    
844      printf("remove relation #" ITEM_ID_FORMAT "\n", sel->id);
845    
846      gint members = osm_relation_members_num(sel);
847    
848      if(members)
849        if(!yes_no_f(context->dialog, NULL, 0, 0,
850                     _("Delete non-empty relation?"),
851                     _("This relation still has %d members. "
852                       "Delete it anyway?"), members))
853          return;
854    
855    context->dialog = gtk_dialog_new_with_buttons(str,    /* first remove selected row from list */
856      GtkTreeIter       iter;
857      GtkTreeSelection *selection = list_get_selection(context->list);
858      if(gtk_tree_selection_get_selected(selection, NULL, &iter))
859        gtk_list_store_remove(context->store, &iter);
860    
861      /* then really delete it */
862      osm_relation_delete(context->appdata->osm, sel, FALSE);
863    
864      relation_list_selected(context, NULL);
865    }
866    
867    static GtkWidget *relation_list_widget(relation_context_t *context) {
868      context->list = list_new(LIST_HILDON_WITH_HEADERS);
869    
870      list_set_selection_function(context->list, relation_list_selection_func,
871                                  context);
872    
873      list_set_columns(context->list,
874                       _("Id"),      RELATION_COL_ID, 0,
875                       _("Type"),    RELATION_COL_TYPE, 0,
876                       _("Name"),    RELATION_COL_NAME, LIST_FLAG_ELLIPSIZE,
877                       _("Members"), RELATION_COL_MEMBERS, 0,
878                       NULL);
879    
880      /* build and fill the store */
881      context->store = gtk_list_store_new(RELATION_NUM_COLS,
882                    G_TYPE_ITEM_ID_T, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT,
883                    G_TYPE_POINTER);
884    
885      list_set_store(context->list, context->store);
886    
887      // Sorting by ref/name by default is useful for places with lots of numbered
888      // bus routes. Especially for small screens.
889      gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(context->store),
890                                           RELATION_COL_NAME, GTK_SORT_ASCENDING);
891    
892      GtkTreeIter iter;
893      relation_t *relation = context->appdata->osm->relation;
894      while(relation) {
895        char *name = relation_get_descriptive_name(relation);
896    
897        guint num = osm_relation_members_num(relation);
898    
899        /* Append a row and fill in some data */
900        gtk_list_store_append(context->store, &iter);
901        gtk_list_store_set(context->store, &iter,
902                           RELATION_COL_ID, relation->id,
903                           RELATION_COL_TYPE,
904                           osm_tag_get_by_key(relation->tag, "type"),
905                           RELATION_COL_NAME, name,
906                           RELATION_COL_MEMBERS, num,
907                           RELATION_COL_DATA, relation,
908                           -1);
909    
910        relation = relation->next;
911      }
912    
913      g_object_unref(context->store);
914    
915      list_set_static_buttons(context->list, G_CALLBACK(on_relation_add),
916         G_CALLBACK(on_relation_edit), G_CALLBACK(on_relation_remove), context);
917    
918      list_set_user_buttons(context->list,
919            LIST_BUTTON_USER0, _("Members..."), G_CALLBACK(on_relation_members),
920            0);
921    
922      relation_list_selected(context, NULL);
923    
924      return context->list;
925    }
926    
927    /* a global view on all relations */
928    void relation_list(appdata_t *appdata) {
929      relation_context_t *context = g_new0(relation_context_t, 1);
930      context->appdata = appdata;
931    
932      printf("relation list\n");
933    
934      context->dialog =
935        gtk_dialog_new_with_buttons(_("All relations"),
936          GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,          GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
937          GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,          GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
938          NULL);          NULL);
   g_free(str);  
939    
940    gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),    gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
941                                    GTK_RESPONSE_ACCEPT);                                    GTK_RESPONSE_CLOSE);
942    
943      gtk_window_set_default_size(GTK_WINDOW(context->dialog),
944                                  LIST_OF_RELATIONS_DIALOG_WIDTH,
945                                  LIST_OF_RELATIONS_DIALOG_HEIGHT);
946    
947      context->show_btn = gtk_dialog_add_button(GTK_DIALOG(context->dialog),
948                                                _("Select"), GTK_RESPONSE_HELP);
949    
   /* making the dialog a little wider makes it less "crowded" */  
 #ifdef USE_HILDON  
   gtk_window_set_default_size(GTK_WINDOW(context->dialog), 500, 300);  
 #else  
   gtk_window_set_default_size(GTK_WINDOW(context->dialog), 400, 200);  
 #endif  
950    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
951                       relation_list(context), TRUE, TRUE, 0);                       relation_list_widget(context), TRUE, TRUE, 0);
952    
953    /* ----------------------------------- */    /* ----------------------------------- */
954    
955    
956    gtk_widget_show_all(context->dialog);    gtk_widget_show_all(context->dialog);
957    gtk_dialog_run(GTK_DIALOG(context->dialog));    if(gtk_dialog_run(GTK_DIALOG(context->dialog)) == GTK_RESPONSE_HELP) {
958    gtk_widget_destroy(context->dialog);      map_item_deselect(appdata);
959    
960        relation_t *sel = get_selected_relation(context);
961        if(sel) map_relation_select(appdata, sel);
962      }
963    
964      gtk_widget_destroy(context->dialog);
965    g_free(context);    g_free(context);
966  }  }
967    
968    
969    // vim:et:ts=8:sw=2:sts=2:ai

Legend:
Removed from v.73  
changed lines
  Added in v.161