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

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