Diff of /trunk/src/relation_edit.c

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

revision 2 by achadwick, Wed Dec 10 00:00:05 2008 UTC revision 152 by harbaum, Sun Mar 29 19:19:30 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;    relation_item_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 38  enum { Line 55  enum {
55    RELITEM_NUM_COLS    RELITEM_NUM_COLS
56  };  };
57    
 static void relation_list_selected(relitem_context_t *context,  
                                    gboolean selected) {  
   gtk_widget_set_sensitive(context->but_remove, FALSE);  
   gtk_widget_set_sensitive(context->but_edit, selected);  
 }  
   
 static gboolean  
 relation_list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,  
                      GtkTreePath *path, gboolean path_currently_selected,  
                      gpointer userdata) {  
   relitem_context_t *context = (relitem_context_t*)userdata;  
   GtkTreeIter iter;  
   
   if(gtk_tree_model_get_iter(model, &iter, path)) {  
     g_assert(gtk_tree_path_get_depth(path) == 1);  
     relation_list_selected(context, TRUE);  
   }  
   
   return TRUE; /* allow selection state to change */  
 }  
   
 static void relation_remove_item(relation_t *relation, relation_item_t *item) {  
   
   printf("remove item of type %d from relation #%ld\n",  
          item->type, relation->id);  
   
   member_t **member = &relation->member;  
   while(*member) {  
     if(((*member)->type == item->type) &&  
        (((item->type == NODE) && (item->node == (*member)->node)) ||  
         ((item->type == WAY) && (item->way == (*member)->way)) ||  
       ((item->type == RELATION) && (item->relation == (*member)->relation)))) {  
   
       member_t *next = (*member)->next;  
       osm_member_free(*member);  
       *member = next;  
   
       relation->flags |= OSM_FLAG_DIRTY;  
   
       return;  
     } else  
       member = &(*member)->next;  
   }  
   g_assert(0);  
 }  
   
58  typedef struct role_chain_s {  typedef struct role_chain_s {
59    char *role;    char *role;
60    struct role_chain_s *next;    struct role_chain_s *next;
61  } role_chain_t;  } role_chain_t;
62    
63  static void relation_add_item(GtkWidget *parent,  static gboolean relation_add_item(GtkWidget *parent,
64                                relation_t *relation, relation_item_t *item) {                                relation_t *relation, relation_item_t *item) {
65    role_chain_t *chain = NULL, **chainP = &chain;    role_chain_t *chain = NULL, **chainP = &chain;
66    
# Line 167  static void relation_add_item(GtkWidget Line 138  static void relation_add_item(GtkWidget
138    if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {    if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
139      printf("user clicked cancel\n");      printf("user clicked cancel\n");
140      gtk_widget_destroy(dialog);      gtk_widget_destroy(dialog);
141      return;      return FALSE;
142    }    }
143    
144    printf("user clicked ok\n");    printf("user clicked ok\n");
# Line 210  static void relation_add_item(GtkWidget Line 181  static void relation_add_item(GtkWidget
181    }    }
182    
183    relation->flags |= OSM_FLAG_DIRTY;    relation->flags |= OSM_FLAG_DIRTY;
184      return TRUE;
185  }  }
186    
187  static void on_relation_add(GtkWidget *but, relitem_context_t *context) {  static void relation_remove_item(relation_t *relation, relation_item_t *item) {
188    /* open a dialog where the user can pick from a list of all */  
189    /* relations */    printf("remove item of type %d from relation #%ld\n",
190             item->type, relation->id);
191    
192      member_t **member = &relation->member;
193      while(*member) {
194        if(((*member)->type == item->type) &&
195           (((item->type == NODE) && (item->node == (*member)->node)) ||
196            ((item->type == WAY) && (item->way == (*member)->way)) ||
197          ((item->type == RELATION) && (item->relation == (*member)->relation)))) {
198    
199          member_t *next = (*member)->next;
200          osm_member_free(*member);
201          *member = next;
202    
203          relation->flags |= OSM_FLAG_DIRTY;
204    
205          return;
206        } else
207          member = &(*member)->next;
208      }
209      g_assert(0);
210    }
211    
212    static void relation_item_list_selected(relitem_context_t *context,
213                                       gboolean selected) {
214    
215      list_button_enable(context->list, LIST_BUTTON_REMOVE, selected);
216      list_button_enable(context->list, LIST_BUTTON_EDIT, selected);
217    }
218    
219    /* try to find something descriptive */
220    static char *relation_get_descriptive_name(relation_t *relation) {
221      char *name = osm_tag_get_by_key(relation->tag, "ref");
222      if (!name)
223        name = osm_tag_get_by_key(relation->tag, "name");
224      if (!name)
225        name = osm_tag_get_by_key(relation->tag, "note");
226      if (!name)
227        name = osm_tag_get_by_key(relation->tag, "fix" "me");
228      return name;
229    }
230    
231    static void on_relation_item_add(GtkWidget *but, relitem_context_t *context) {
232      /* create a new relation */
233    
234      relation_t *relation = osm_relation_new();
235      if(!info_dialog(context->dialog, context->appdata, relation)) {
236        printf("tag edit cancelled\n");
237        osm_relation_free(relation);
238      } else {
239        osm_relation_attach(context->appdata->osm, relation);
240    
241        /* add to list */
242    
243        /* append a row for the new data */
244        char *name = relation_get_descriptive_name(relation);
245    
246        GtkTreeIter iter;
247        gtk_list_store_append(context->store, &iter);
248        gtk_list_store_set(context->store, &iter,
249                           RELITEM_COL_SELECTED, FALSE,
250                           RELITEM_COL_TYPE,
251                           osm_tag_get_by_key(relation->tag, "type"),
252                           RELITEM_COL_NAME, name,
253                           RELITEM_COL_DATA, relation,
254                           -1);
255    
256        gtk_tree_selection_select_iter(list_get_selection(context->list), &iter);
257      }
258  }  }
259    
260  static relation_t *get_selection(relitem_context_t *context) {  static relation_t *get_selection(relitem_context_t *context) {
# Line 222  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 231  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 #%ld\n", sel->id);
279    
280    info_dialog(context->dialog, context->appdata, sel);    if (!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      // 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 #%ld\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) {
338      member_t *member = relation->member;
339      while(member) {
340        switch(member->type) {
341    
342        case NODE:
343          if((item->type == NODE) && (item->node == member->node))
344            return member->role;
345          break;
346    
347        case WAY:
348          if((item->type == WAY) && (item->way == member->way))
349            return member->role;
350          break;
351    
352        default:
353          break;
354        }
355        member = member->next;
356      }
357      return NULL;
358  }  }
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    
366    path = gtk_tree_path_new_from_string(path_str);    path = gtk_tree_path_new_from_string(path_str);
367    gtk_tree_model_get_iter(GTK_TREE_MODEL(context->store), &iter, path);    gtk_tree_model_get_iter(GTK_TREE_MODEL(context->store), &iter, path);
368      gtk_tree_path_free(path);
369    
370    /* get current enabled flag */    /* get current enabled flag */
371    gboolean enabled;    gboolean enabled;
372      relation_t *relation = NULL;
373    gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,    gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
374                       RELITEM_COL_SELECTED, &enabled, -1);                       RELITEM_COL_SELECTED, &enabled,
375                         RELITEM_COL_DATA, &relation,
376    /* change it and store it */                       -1);
377    enabled = !enabled;  
378    gtk_list_store_set(context->store, &iter, RELITEM_COL_SELECTED, enabled, -1);    list_pre_inplace_edit_tweak(GTK_TREE_MODEL(context->store));
379    
380      if(!enabled) {
381        printf("will now become be part of this relation\n");
382        if(relation_add_item(context->dialog, relation, context->item))
383          gtk_list_store_set(context->store, &iter,
384                     RELITEM_COL_SELECTED, TRUE,
385                     RELITEM_COL_ROLE,
386                     relitem_get_role_in_relation(context->item, relation),
387                     -1);
388      } else {
389        printf("item will not be part of this relation anymore\n");
390        relation_remove_item(relation, context->item);
391        gtk_list_store_set(context->store, &iter,
392                           RELITEM_COL_SELECTED, FALSE,
393                           RELITEM_COL_ROLE, NULL,
394                           -1);
395      }
396    
   gtk_tree_path_free(path);  
397  }  }
398    
399  static gboolean relitem_is_in_relation(relation_item_t *item, relation_t *relation) {  static gboolean relitem_is_in_relation(relation_item_t *item, relation_t *relation) {
# Line 291  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 char *relitem_get_role_in_relation(relation_item_t *item, relation_t *relation) {  static GtkWidget *relation_item_list_widget(relitem_context_t *context) {
423    member_t *member = relation->member;    context->list = list_new(LIST_HILDON_WITH_HEADERS);
   while(member) {  
     switch(member->type) {  
424    
425      case NODE:    list_set_columns(context->list,
426        if((item->type == NODE) && (item->node == member->node))                     _(""), RELITEM_COL_SELECTED, LIST_FLAG_TOGGLE,
427          return member->role;                              G_CALLBACK(relitem_toggled), context,
428        break;                     _("Type"), RELITEM_COL_TYPE, 0,
429                       _("Role"), RELITEM_COL_ROLE, 0,
430                       _("Name"), RELITEM_COL_NAME, LIST_FLAG_ELLIPSIZE,
431                       NULL);
432    
433      case WAY:    /* build and fill the store */
434        if((item->type == WAY) && (item->way == member->way))    context->store = gtk_list_store_new(RELITEM_NUM_COLS,
435          return member->role;                  G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING,
436        break;                  G_TYPE_STRING, G_TYPE_POINTER);
437    
438      default:    list_set_store(context->list, context->store);
439        break;  
440      }    // Debatable whether to sort by the "selected" or the "Name" column by
441      member = member->next;    // 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), context);
468    
469      relation_item_list_selected(context, FALSE);
470    
471      return context->list;
472    }
473    
474    void relation_add_dialog(appdata_t *appdata, relation_item_t *relitem) {
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 = relitem;
481    
482      char *str = NULL;
483      switch(relitem->type) {
484      case NODE:
485        str = g_strdup_printf(_("Relations for node #%ld"), relitem->node->id);
486        break;
487      case WAY:
488        str = g_strdup_printf(_("Relations for way #%ld"), relitem->way->id);
489        break;
490      default:
491        g_assert((relitem->type == NODE) || (relitem->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;
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;    return NULL;
548  }  }
549    
550  static GtkWidget *relation_list(relitem_context_t *context) {  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    
556      list_button_enable(context->list, LIST_BUTTON_REMOVE, selected != NULL);
557      list_button_enable(context->list, LIST_BUTTON_EDIT, selected != NULL);
558    }
559    
560    static gboolean
561    relation_list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
562                         GtkTreePath *path, gboolean path_currently_selected,
563                         gpointer userdata) {
564      relation_context_t *context = (relation_context_t*)userdata;
565      GtkTreeIter iter;
566    
567      if(gtk_tree_model_get_iter(model, &iter, path)) {
568        g_assert(gtk_tree_path_get_depth(path) == 1);
569    
570        relation_t *relation = NULL;
571        gtk_tree_model_get(model, &iter, RELATION_COL_DATA, &relation, -1);
572        relation_list_selected(context, relation);
573      }
574    
575      return TRUE; /* allow selection state to change */
576    }
577    
578    typedef struct {
579      relation_t *relation;
580      GtkWidget *dialog, *view;
581      GtkListStore *store;
582    } member_context_t;
583    
584    enum {
585      MEMBER_COL_TYPE = 0,
586      MEMBER_COL_ID,
587      MEMBER_COL_NAME,
588      MEMBER_COL_ROLE,
589      MEMBER_COL_REF_ONLY,
590      MEMBER_COL_DATA,
591      MEMBER_NUM_COLS
592    };
593    
594    static gboolean
595    member_list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
596                         GtkTreePath *path, gboolean path_currently_selected,
597                         gpointer userdata) {
598      GtkTreeIter iter;
599    
600      if(gtk_tree_model_get_iter(model, &iter, path)) {
601        g_assert(gtk_tree_path_get_depth(path) == 1);
602    
603        member_t *member = NULL;
604        gtk_tree_model_get(model, &iter, MEMBER_COL_DATA, &member, -1);
605        if(member && member->type < NODE_ID)
606          return TRUE;
607      }
608    
609      return FALSE;
610    }
611    
612    
613    static GtkWidget *member_list_widget(member_context_t *context) {
614    GtkWidget *vbox = gtk_vbox_new(FALSE,3);    GtkWidget *vbox = gtk_vbox_new(FALSE,3);
615    context->view = gtk_tree_view_new();    context->view = gtk_tree_view_new();
616    
617    gtk_tree_selection_set_select_function(    gtk_tree_selection_set_select_function(
618           gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),           gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),
619           relation_list_selection_func,           member_list_selection_func,
620           context, NULL);           context, NULL);
621    
622      /* --- "type" column --- */
623    /* --- "selected" column --- */    GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
624    GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();    g_object_set(renderer, "foreground", "grey", NULL);
   g_signal_connect(renderer, "toggled", G_CALLBACK(relitem_toggled), context);  
625    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
626          -1, _(""), renderer,          -1, _("Type"), renderer, "text", MEMBER_COL_TYPE,
627          "active", RELITEM_COL_SELECTED,          "foreground-set", MEMBER_COL_REF_ONLY,  NULL);
         NULL);  
628    
   /* --- "Type" column --- */  
   renderer = gtk_cell_renderer_text_new();  
   gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),  
         -1, _("Type"), renderer, "text", RELITEM_COL_TYPE, NULL);  
629    
630    /* --- "Role" column --- */    /* --- "id" column --- */
631    renderer = gtk_cell_renderer_text_new();    renderer = gtk_cell_renderer_text_new();
632      g_object_set(renderer, "foreground", "grey", NULL);
633    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
634          -1, _("Role"), renderer, "text", RELITEM_COL_ROLE, NULL);          -1, _("Id"), renderer, "text", MEMBER_COL_ID,
635            "foreground-set", MEMBER_COL_REF_ONLY, NULL);
636    
637    /* --- "Name" column --- */    /* --- "Name" column --- */
638    renderer = gtk_cell_renderer_text_new();    renderer = gtk_cell_renderer_text_new();
639      g_object_set(renderer, "foreground", "grey", NULL);
640    g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);    g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
641    GtkTreeViewColumn *column =    GtkTreeViewColumn *column =
642      gtk_tree_view_column_new_with_attributes(_("Name"), renderer,      gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
643                   "text", RELITEM_COL_NAME, NULL);                       "text", MEMBER_COL_NAME,
644                         "foreground-set", MEMBER_COL_REF_ONLY,  NULL);
645    gtk_tree_view_column_set_expand(column, TRUE);    gtk_tree_view_column_set_expand(column, TRUE);
646    gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);    gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
647    
648      /* --- "role" column --- */
649      renderer = gtk_cell_renderer_text_new();
650      g_object_set(renderer, "foreground", "grey", NULL);
651      gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
652            -1, _("Role"), renderer, "text", MEMBER_COL_ROLE,
653            "foreground-set", MEMBER_COL_REF_ONLY, NULL);
654    
655    /* build and fill the store */    /* build and fill the store */
656    context->store = gtk_list_store_new(RELITEM_NUM_COLS,    context->store = gtk_list_store_new(MEMBER_NUM_COLS,
657                  G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING,                G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
658                  G_TYPE_STRING, G_TYPE_POINTER);                G_TYPE_BOOLEAN, G_TYPE_POINTER);
659    
660    gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),    gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),
661                            GTK_TREE_MODEL(context->store));                            GTK_TREE_MODEL(context->store));
662    
663    GtkTreeIter iter;    GtkTreeIter iter;
664    relation_t *relation = context->appdata->osm->relation;    member_t *member = context->relation->member;
665    while(relation) {    while(member) {
666        tag_t *tags = osm_object_get_tags(member->type, member->ptr);
667        char *id = osm_id_string(member->type, member->ptr);
668    
669      /* try to find something descriptive */      /* try to find something descriptive */
670      char *name = osm_tag_get_by_key(relation->tag, "name");      char *name = NULL;
671      if(!name) name = osm_tag_get_by_key(relation->tag, "ref");      if(tags)
672          name = osm_tag_get_by_key(tags, "name");
673    
674      /* Append a row and fill in some data */      /* Append a row and fill in some data */
675      gtk_list_store_append(context->store, &iter);      gtk_list_store_append(context->store, &iter);
676      gtk_list_store_set(context->store, &iter,      gtk_list_store_set(context->store, &iter,
677         RELITEM_COL_SELECTED, relitem_is_in_relation(context->item, relation),         MEMBER_COL_TYPE, osm_type_string(member->type),
678         RELITEM_COL_TYPE, osm_tag_get_by_key(relation->tag, "type"),         MEMBER_COL_ID,   id,
679         RELITEM_COL_ROLE, relitem_get_role_in_relation(context->item, relation),         MEMBER_COL_NAME, name,
680         RELITEM_COL_NAME, name,         MEMBER_COL_ROLE, member->role,
681         RELITEM_COL_DATA, relation,         MEMBER_COL_REF_ONLY, member->type >= NODE_ID,
682           MEMBER_COL_DATA, member,
683         -1);         -1);
684    
685      relation = relation->next;      g_free(id);
686        member = member->next;
687    }    }
688    
689    g_object_unref(context->store);    g_object_unref(context->store);
690    
691    /* put it into a scrolled window */    /* put it into a scrolled window */
# Line 392  static GtkWidget *relation_list(relitem_ Line 698  static GtkWidget *relation_list(relitem_
698    
699    gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);    gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
700    
701    /* ------- button box ------------ */    return vbox;
702    }
703    
704    /* user clicked "members..." button in relation list */
705    static void on_relation_members(GtkWidget *but, relation_context_t *context) {
706      member_context_t *mcontext = g_new0(member_context_t, 1);
707    
708      /* display members list */
709      mcontext->relation = get_selected_relation(context);
710      if(!mcontext->relation) return;
711    
712      char *str = osm_tag_get_by_key(mcontext->relation->tag, "name");
713      if(!str) str = osm_tag_get_by_key(mcontext->relation->tag, "ref");
714      if(!str)
715        str = g_strdup_printf(_("Members of relation #%ld"),
716                              mcontext->relation->id);
717      else
718        str = g_strdup_printf(_("Members of relation \"%s\""), str);
719    
720      mcontext->dialog =
721        gtk_dialog_new_with_buttons(str,
722            GTK_WINDOW(context->dialog), GTK_DIALOG_MODAL,
723            GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
724            NULL);
725      g_free(str);
726    
727      gtk_dialog_set_default_response(GTK_DIALOG(mcontext->dialog),
728                                      GTK_RESPONSE_CLOSE);
729    
730      gtk_window_set_default_size(GTK_WINDOW(mcontext->dialog),
731                                  LIST_OF_MEMBERS_DIALOG_WIDTH,
732                                  LIST_OF_MEMBERS_DIALOG_HEIGHT);
733    
734    GtkWidget *hbox = gtk_hbox_new(TRUE,3);    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mcontext->dialog)->vbox),
735                         member_list_widget(mcontext), TRUE, TRUE, 0);
736    
737    context->but_add = gtk_button_new_with_label(_("Add..."));    /* ----------------------------------- */
   gtk_widget_set_sensitive(context->but_add, FALSE);  
   gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_add);  
   gtk_signal_connect(GTK_OBJECT(context->but_add), "clicked",  
                      GTK_SIGNAL_FUNC(on_relation_add), context);  
738    
739    context->but_edit = gtk_button_new_with_label(_("Edit..."));    gtk_widget_show_all(mcontext->dialog);
740    gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_edit);    gtk_dialog_run(GTK_DIALOG(mcontext->dialog));
741    gtk_signal_connect(GTK_OBJECT(context->but_edit), "clicked",    gtk_widget_destroy(mcontext->dialog);
                      GTK_SIGNAL_FUNC(on_relation_edit), context);  
742    
743    context->but_remove = gtk_button_new_with_label(_("Remove"));    g_free(mcontext);
744    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);  
745    
746    relation_list_selected(context, FALSE);  static void on_relation_add(GtkWidget *but, relation_context_t *context) {
747      /* create a new relation */
748    
749    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);    relation_t *relation = osm_relation_new();
750    return vbox;    if(!info_dialog(context->dialog, context->appdata, relation)) {
751        printf("tag edit cancelled\n");
752        osm_relation_free(relation);
753      } else {
754        osm_relation_attach(context->appdata->osm, relation);
755    
756        /* append a row for the new data */
757    
758        char *name = relation_get_descriptive_name(relation);
759    
760        guint num = osm_relation_members_num(relation);
761    
762        /* Append a row and fill in some data */
763        GtkTreeIter iter;
764        gtk_list_store_append(context->store, &iter);
765        gtk_list_store_set(context->store, &iter,
766                           RELATION_COL_ID, relation->id,
767                           RELATION_COL_TYPE,
768                           osm_tag_get_by_key(relation->tag, "type"),
769                           RELATION_COL_NAME, name,
770                           RELATION_COL_MEMBERS, num,
771                           RELATION_COL_DATA, relation,
772                           -1);
773    
774        gtk_tree_selection_select_iter(list_get_selection(context->list), &iter);
775    
776        /* scroll to end */
777        //    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment();
778        /* xyz */
779      }
780  }  }
781    
782  void relation_add_dialog(appdata_t *appdata, relation_item_t *relitem) {  /* user clicked "edit..." button in relation list */
783    relitem_context_t *context = g_new0(relitem_context_t, 1);  static void on_relation_edit(GtkWidget *but, relation_context_t *context) {
784    map_t *map = appdata->map;    relation_t *sel = get_selected_relation(context);
785    g_assert(map);    if(!sel) return;
786    
787    context->appdata = appdata;    printf("edit relation #%ld\n", sel->id);
   context->item = relitem;  
788    
789    char *str = NULL;    if (!info_dialog(context->dialog, context->appdata, sel))
790    switch(relitem->type) {      return;
791    case NODE:  
792      str = g_strdup_printf(_("Relations for node #%ld"), relitem->node->id);    // Locate the changed item
793      break;    GtkTreeIter iter;
794    case WAY:    gboolean valid = gtk_tree_model_get_iter_first(
795      str = g_strdup_printf(_("Relations for way #%ld"), relitem->way->id);      GTK_TREE_MODEL(context->store), &iter);
796      break;    while (valid) {
797    default:      relation_t *row_rel;
798      g_assert((relitem->type == NODE) || (relitem->type == WAY));      gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
799      break;                         RELATION_COL_DATA, &row_rel,
800                           -1);
801        if (row_rel == sel)
802          break;
803        valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(context->store), &iter);
804    }    }
805      if (!valid)
806        return;
807    
808      // Found it. Update all visible fields.
809      gtk_list_store_set(context->store, &iter,
810        RELATION_COL_ID,      sel->id,
811        RELATION_COL_TYPE,    osm_tag_get_by_key(sel->tag, "type"),
812        RELATION_COL_NAME,    relation_get_descriptive_name(sel),
813        RELATION_COL_MEMBERS, osm_relation_members_num(sel),
814        -1);
815    
816      // Order will probably have changed, so refocus
817      list_focus_on(context->list, &iter, TRUE);
818    }
819    
820    
821    /* remove the selected relation */
822    static void on_relation_remove(GtkWidget *but, relation_context_t *context) {
823      relation_t *sel = get_selected_relation(context);
824      if(!sel) return;
825    
826      printf("remove relation #%ld\n", sel->id);
827    
828      gint members = osm_relation_members_num(sel);
829    
830      if(members)
831        if(!yes_no_f(context->dialog, NULL, 0, 0,
832                     _("Delete non-empty relation?"),
833                     _("This relation still has %d members. "
834                       "Delete it anyway?"), members))
835          return;
836    
837    context->dialog = gtk_dialog_new_with_buttons(str,    /* first remove selected row from list */
838      GtkTreeIter       iter;
839      GtkTreeSelection *selection = list_get_selection(context->list);
840      if(gtk_tree_selection_get_selected(selection, NULL, &iter))
841        gtk_list_store_remove(context->store, &iter);
842    
843      /* then really delete it */
844      osm_relation_delete(context->appdata->osm, sel, FALSE);
845    
846      relation_list_selected(context, NULL);
847    }
848    
849    static GtkWidget *relation_list_widget(relation_context_t *context) {
850      context->list = list_new(LIST_HILDON_WITH_HEADERS);
851    
852      list_set_selection_function(context->list, relation_list_selection_func, context);
853    
854      list_set_columns(context->list,
855                       _("Id"),      RELATION_COL_ID, 0,
856                       _("Type"),    RELATION_COL_TYPE, 0,
857                       _("Name"),    RELATION_COL_NAME, LIST_FLAG_ELLIPSIZE,
858                       _("Members"), RELATION_COL_MEMBERS, 0,
859                       NULL);
860    
861      /* build and fill the store */
862      context->store = gtk_list_store_new(RELATION_NUM_COLS,
863                    G_TYPE_ITEM_ID_T, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT,
864                    G_TYPE_POINTER);
865    
866      list_set_store(context->list, context->store);
867    
868      // Sorting by ref/name by default is useful for places with lots of numbered
869      // bus routes. Especially for small screens.
870      gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(context->store),
871                                           RELATION_COL_NAME, GTK_SORT_ASCENDING);
872    
873      GtkTreeIter iter;
874      relation_t *relation = context->appdata->osm->relation;
875      while(relation) {
876        char *name = relation_get_descriptive_name(relation);
877    
878        guint num = osm_relation_members_num(relation);
879    
880        /* Append a row and fill in some data */
881        gtk_list_store_append(context->store, &iter);
882        gtk_list_store_set(context->store, &iter,
883                           RELATION_COL_ID, relation->id,
884                           RELATION_COL_TYPE,
885                           osm_tag_get_by_key(relation->tag, "type"),
886                           RELATION_COL_NAME, name,
887                           RELATION_COL_MEMBERS, num,
888                           RELATION_COL_DATA, relation,
889                           -1);
890    
891        relation = relation->next;
892      }
893    
894      g_object_unref(context->store);
895    
896      list_set_static_buttons(context->list, G_CALLBACK(on_relation_add),
897         G_CALLBACK(on_relation_edit), G_CALLBACK(on_relation_remove), context);
898    
899      list_set_user_buttons(context->list,
900            LIST_BUTTON_USER0, _("Members..."), G_CALLBACK(on_relation_members),
901            0);
902    
903      relation_list_selected(context, NULL);
904    
905      return context->list;
906    }
907    
908    /* a global view on all relations */
909    void relation_list(appdata_t *appdata) {
910      relation_context_t *context = g_new0(relation_context_t, 1);
911      context->appdata = appdata;
912    
913      printf("relation list\n");
914    
915      context->dialog =
916        gtk_dialog_new_with_buttons(_("All relations"),
917          GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,          GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
918          GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,          GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,  
919          NULL);          NULL);
   g_free(str);  
920    
921    gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),    gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
922                                    GTK_RESPONSE_ACCEPT);                                    GTK_RESPONSE_CLOSE);
923    
924    /* making the dialog a little wider makes it less "crowded" */    gtk_window_set_default_size(GTK_WINDOW(context->dialog),
925  #ifdef USE_HILDON                                LIST_OF_RELATIONS_DIALOG_WIDTH,
926    gtk_window_set_default_size(GTK_WINDOW(context->dialog), 500, 300);                                LIST_OF_RELATIONS_DIALOG_HEIGHT);
927  #else  
   gtk_window_set_default_size(GTK_WINDOW(context->dialog), 400, 200);  
 #endif  
928    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
929                       relation_list(context), TRUE, TRUE, 0);                       relation_list_widget(context), TRUE, TRUE, 0);
930    
931    /* ----------------------------------- */    /* ----------------------------------- */
932    
933    gtk_widget_show_all(context->dialog);    gtk_widget_show_all(context->dialog);
934    if(gtk_dialog_run(GTK_DIALOG(context->dialog)) == GTK_RESPONSE_ACCEPT) {    gtk_dialog_run(GTK_DIALOG(context->dialog));
     printf("accepting new relation memberships\n");  
   
     /* walk the entire store to get all values */  
     GtkTreeIter iter;  
     gboolean loop = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(context->store), &iter);  
     while(loop) {  
       gboolean enabled;  
       relation_t *relation;  
   
       gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,  
                          RELITEM_COL_SELECTED, &enabled,  
                          RELITEM_COL_DATA, &relation,  
                          -1);  
   
   
       if(relation && (enabled != relitem_is_in_relation(relitem, relation))) {  
         printf("membership for relation #%ld has changed to %s\n",  
                relation->id, enabled?"yes":"no");  
   
         if(!enabled) relation_remove_item(relation, relitem);  
         else         relation_add_item(context->dialog, relation, relitem);  
       }  
   
       loop = gtk_tree_model_iter_next(GTK_TREE_MODEL(context->store), &iter);  
     }  
   }  
   
935    gtk_widget_destroy(context->dialog);    gtk_widget_destroy(context->dialog);
936    
937    g_free(context);    g_free(context);
938  }  }
939    
940    
941    // vim:et:ts=8:sw=2:sts=2:ai

Legend:
Removed from v.2  
changed lines
  Added in v.152