]> git.tdb.fi Git - ext/subsurface.git/commitdiff
Merge branch 'misc-fixes' of git://github.com/DataBeaver/subsurface
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 17 Aug 2012 17:57:24 +0000 (10:57 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 17 Aug 2012 17:57:24 +0000 (10:57 -0700)
Pull miscellaneous fixes, mostly UI stuff from Mikko Rasa.

Both this and the pull from Pierre-Yves Chibon created a "Save As" menu
entry and logic.  As a result, there were a fair number of conflicts,
but I tried to make the end result somewhat reasonable.  I might have
missed some semantic conflict, though.

Series-acked-by: Henrik Brautaset Aronsen <subsurface@henrik.synth.no>
* 'misc-fixes' of git://github.com/DataBeaver/subsurface:
  Add a separate "Save as" entry to the menu
  Changes to menu icons
  Improved depth info for dives without samples
  Divide the panes evenly in view_three

1  2 
dive.c
divelist.c
gtk-gui.c
info.c
profile.c

diff --combined dive.c
index f5d0828015e2b64990a390d877839e43335ffd48,36ee8e78c496ce88c375edfd001eb9b64f6c43a8..f4bf497a7da0edeafb91b5a26b1de067f87d64b7
--- 1/dive.c
--- 2/dive.c
+++ b/dive.c
@@@ -120,28 -120,6 +120,28 @@@ double get_depth_units(unsigned int mm
        return d;
  }
  
 +double get_weight_units(unsigned int grams, int *frac, const char **units)
 +{
 +      int decimals;
 +      double value;
 +      const char* unit;
 +
 +      if (output_units.weight == LBS) {
 +              value = grams_to_lbs(grams);
 +              unit = "lbs";
 +              decimals = 0;
 +      } else {
 +              value = grams / 1000.0;
 +              unit = "kg";
 +              decimals = 1;
 +      }
 +      if (frac)
 +              *frac = decimals;
 +      if (units)
 +              *units = unit;
 +      return value;
 +}
 +
  struct dive *alloc_dive(void)
  {
        const int initial_samples = 5;
@@@ -473,7 -451,12 +473,12 @@@ struct dive *fixup_dive(struct dive *di
                }
        }
        if (end < 0)
+       {
+               /* Assume an ascent/descent rate of 9 m/min */
+               int asc_desc_time = dive->maxdepth.mm*60/9000;
+               dive->meandepth.mm = dive->maxdepth.mm*(dive->duration.seconds-asc_desc_time)/dive->duration.seconds;
                return dive;
+       }
  
        update_duration(&dive->duration, end - start);
        if (start != end)
        add_people(dive->buddy);
        add_people(dive->divemaster);
        add_location(dive->location);
 +      add_suit(dive->suit);
        for (i = 0; i < MAX_CYLINDERS; i++) {
                cylinder_t *cyl = dive->cylinder + i;
                add_cylinder_description(&cyl->type);
                if (same_rounded_pressure(cyl->sample_end, cyl->end))
                        cyl->end.mbar = 0;
        }
 +      for (i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
 +              weightsystem_t *ws = dive->weightsystem + i;
 +              add_weightsystem_description(ws);
 +      }
  
        return dive;
  }
@@@ -704,7 -682,6 +709,7 @@@ struct dive *try_to_merge(struct dive *
        MERGE_TXT(res, a, b, buddy);
        MERGE_TXT(res, a, b, divemaster);
        MERGE_MAX(res, a, b, rating);
 +      MERGE_TXT(res, a, b, suit);
        MERGE_MAX(res, a, b, number);
        MERGE_MAX(res, a, b, maxdepth.mm);
        res->meandepth.mm = 0;
diff --combined divelist.c
index 0e1b40dd6f71abb64f9acd32082b6df06585875f,27d24243baa1e37cdda00c0c35dbf18a5fe0e2d9..5d6915256c5f08a2dea140fdd3ed884d4ed416ac
@@@ -24,9 -24,9 +24,9 @@@
  struct DiveList {
        GtkWidget    *tree_view;
        GtkWidget    *container_widget;
 -      GtkListStore *model;
 +      GtkTreeStore *model, *listmodel, *treemodel;
        GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location;
 -      GtkTreeViewColumn *temperature, *cylinder, *nitrox, *sac, *otu;
 +      GtkTreeViewColumn *temperature, *cylinder, *totalweight, *suit, *nitrox, *sac, *otu;
        int changed;
  };
  
@@@ -44,8 -44,6 +44,8 @@@ enum 
        DIVE_DEPTH,             /* int: dive->maxdepth in mm */
        DIVE_DURATION,          /* int: in seconds */
        DIVE_TEMPERATURE,       /* int: in mkelvin */
 +      DIVE_TOTALWEIGHT,       /* int: in grams */
 +      DIVE_SUIT,              /* "wet, 3mm" */
        DIVE_CYLINDER,
        DIVE_NITROX,            /* int: dummy */
        DIVE_SAC,               /* int: in ml/min */
        DIVELIST_COLUMNS
  };
  
 +/* magic numbers that indicate (as negative values) model entries that
 + * are summary entries for a divetrip */
 +#define NEW_TRIP 1
 +
 +#ifdef DEBUG_MODEL
 +static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path,
 +                              GtkTreeIter *iter, gpointer data)
 +{
 +      char *location;
 +      int idx, nr, rating, depth;
 +
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_RATING, &rating, DIVE_DEPTH, &depth, DIVE_LOCATION, &location, -1);
 +      printf("entry #%d : nr %d rating %d depth %d location %s \n", idx, nr, rating, depth, location);
 +      free(location);
 +
 +      return FALSE;
 +}
 +
 +static void dump_model(GtkListStore *store)
 +{
 +      gtk_tree_model_foreach(GTK_TREE_MODEL(store), dump_model_entry, NULL);
 +}
 +#endif
 +
  static GList *selected_dives;
 +static int st_size = 0;
  
 -static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model)
 +gboolean is_in_st(int idx, int *atpos)
  {
 -      GtkTreeIter iter;
 -      GValue value = {0, };
 -      GtkTreePath *path;
 +      int i;
  
 -      int nr_selected = gtk_tree_selection_count_selected_rows(selection);
 +      for (i = 0; i < amount_selected; i++)
 +              if (selectiontracker[i] == idx) {
 +                      if (atpos)
 +                              *atpos = i;
 +                      return TRUE;
 +              }
 +      return FALSE;
 +}
  
 -      if (selected_dives) {
 -              g_list_foreach (selected_dives, (GFunc) gtk_tree_path_free, NULL);
 -              g_list_free (selected_dives);
 -      }
 -      selected_dives = gtk_tree_selection_get_selected_rows(selection, NULL);
 +#if DEBUG_SELECTION_TRACKING
 +void dump_selection(void)
 +{
 +      int i;
  
 -      switch (nr_selected) {
 -      case 0: /* keep showing the last selected dive */
 +      printf("currently selected are ");
 +      for (i = 0; i < amount_selected; i++)
 +              printf("%d ", selectiontracker[i]);
 +      printf("\n");
 +}
 +#endif
 +
 +void track_select(int idx)
 +{
 +      if (idx < 0)
                return;
 -      case 1: 
 -              /* just pick that dive as selected */
 -              amount_selected = 1;
 -              path = g_list_nth_data(selected_dives, 0);
 -              if (gtk_tree_model_get_iter(model, &iter, path)) {
 -                      gtk_tree_model_get_value(model, &iter, DIVE_INDEX, &value);
 -                      selected_dive = g_value_get_int(&value);
 -                      repaint_dive();
 -              }
 +
 +#if DEBUG_SELECTION_TRACKING
 +      printf("add %d to selection of %d entries\n", idx, amount_selected);
 +#endif
 +      if (is_in_st(idx, NULL))
                return;
 -      default: /* multiple selections - what now? At this point I
 -                * don't want to change the selected dive unless
 -                * there is exactly one dive selected; not sure this
 -                * is the most intuitive solution.
 -                * I do however want to keep around which dives have
 -                * been selected */
 -              amount_selected = g_list_length(selected_dives);
 -              process_selected_dives(selected_dives, model);
 -              repaint_dive();
 +      if (amount_selected >= st_size) {
 +              selectiontracker = realloc(selectiontracker, dive_table.nr * sizeof(int));
 +              st_size = dive_table.nr;
 +      }
 +      selectiontracker[amount_selected] = idx;
 +      amount_selected++;
 +      if (amount_selected == 1)
 +              selected_dive = idx;
 +#if DEBUG_SELECTION_TRACKING
 +      printf("increased amount_selected to %d\n", amount_selected);
 +      dump_selection();
 +#endif
 +}
 +
 +void track_unselect(int idx)
 +{
 +      if (idx < 0)
 +              return;
 +
 +#if DEBUG_SELECTION_TRACKING
 +      printf("remove %d from selection of %d entries\n", idx, amount_selected);
 +#endif
 +      int atpos;
 +
 +      if (! is_in_st(idx, &atpos))
                return;
 +      memmove(selectiontracker + atpos,
 +              selectiontracker + atpos + 1,
 +              (amount_selected - atpos - 1) * sizeof(int));
 +      amount_selected--;
 +#if DEBUG_SELECTION_TRACKING
 +      printf("removed %d at pos %d and decreased amount_selected to %d\n", idx, atpos, amount_selected);
 +      dump_selection();
 +#endif
 +}
 +
 +/* when subsurface starts we want to have the last dive selected. So we simply
 +   walk to the first leaf (and skip the summary entries - which have negative
 +   DIVE_INDEX) */
 +static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx)
 +{
 +      GtkTreeIter parent;
 +      GtkTreePath *tpath;
 +
 +      while (*diveidx < 0) {
 +              memcpy(&parent, iter, sizeof(parent));
 +              tpath = gtk_tree_model_get_path(model, &parent);
 +              if (!gtk_tree_model_iter_children(model, iter, &parent))
 +                      /* we should never have a parent without child */
 +                      return;
 +              if(!gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath))
 +                      gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE);
 +              gtk_tree_model_get(GTK_TREE_MODEL(model), iter, DIVE_INDEX, diveidx, -1);
 +              track_select(*diveidx);
        }
  }
  
 +/* if we click on a summary dive, we actually want to select / unselect
 +   all the dives "below" it */
 +static void select_children(GtkTreeModel *model, GtkTreeSelection * selection,
 +                      GtkTreeIter *iter, gboolean was_selected)
 +{
 +      int i, nr_children;
 +      gboolean unexpand = FALSE;
 +      GtkTreeIter parent;
 +      GtkTreePath *tpath;
 +
 +      memcpy(&parent, iter, sizeof(parent));
 +
 +      tpath = gtk_tree_model_get_path(model, &parent);
 +
 +      /* stupid gtk doesn't allow us to select rows that are invisible; so if the
 +         user clicks on a row that isn't expanded, we briefly expand it, select the
 +         children, and then unexpand it again */
 +      if(!gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) {
 +              unexpand = TRUE;
 +              gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE);
 +      }
 +      nr_children = gtk_tree_model_iter_n_children(model, &parent);
 +      for (i = 0; i < nr_children; i++) {
 +              gtk_tree_model_iter_nth_child(model, iter, &parent, i);
 +              if (was_selected)
 +                      gtk_tree_selection_unselect_iter(selection, iter);
 +              else
 +                      gtk_tree_selection_select_iter(selection, iter);
 +      }
 +      if (unexpand)
 +              gtk_tree_view_collapse_row(GTK_TREE_VIEW(dive_list.tree_view), tpath);
 +}
 +
 +/* make sure that if we expand a summary row that is selected, the children show
 +   up as selected, too */
 +void row_expanded_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
 +{
 +      GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
 +
 +      if (gtk_tree_selection_path_is_selected(selection, path))
 +              select_children(GTK_TREE_MODEL(dive_list.model), selection, iter, FALSE);
 +}
 +
 +/* this is called _before_ the selection is changed, for every single entry;
 + * we simply have it call down the tree to make sure that summary items toggle
 + * their children */
 +gboolean modify_selection_cb(GtkTreeSelection *selection, GtkTreeModel *model,
 +                      GtkTreePath *path, gboolean was_selected, gpointer userdata)
 +{
 +      GtkTreeIter iter;
 +      int dive_idx;
 +
 +      if (gtk_tree_model_get_iter(model, &iter, path)) {
 +              gtk_tree_model_get(model, &iter, DIVE_INDEX, &dive_idx, -1);
 +              /* turns out we need to move the selectiontracker here */
 +              if (was_selected)
 +                      track_unselect(dive_idx);
 +              else
 +                      track_select(dive_idx);
 +              if (dive_idx < 0) {
 +                      select_children(model, selection, &iter, was_selected);
 +              }
 +      }
 +      /* allow this selection to proceed */
 +      return TRUE;
 +}
 +
 +/* this is called when gtk thinks that the selection has changed */
 +static void selection_cb(GtkTreeSelection *selection, gpointer userdata)
 +{
 +      process_selected_dives(selected_dives, selectiontracker, GTK_TREE_MODEL(dive_list.model));
 +      repaint_dive();
 +}
 +
  const char *star_strings[] = {
        ZERO_STARS,
        ONE_STARS,
@@@ -262,17 -109,13 +262,17 @@@ static void star_data_func(GtkTreeViewC
                           GtkTreeIter *iter,
                           gpointer data)
  {
 -      int nr_stars;
 +      int nr_stars, idx;
        char buffer[40];
  
 -      gtk_tree_model_get(model, iter, DIVE_RATING, &nr_stars, -1);
 -      if (nr_stars < 0 || nr_stars > 5)
 -              nr_stars = 0;
 -      snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]);
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_RATING, &nr_stars, -1);
 +      if (idx < 0) {
 +              *buffer = '\0';
 +      } else {
 +              if (nr_stars < 0 || nr_stars > 5)
 +                      nr_stars = 0;
 +              snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]);
 +      }
        g_object_set(renderer, "text", buffer, NULL);
  }
  
@@@ -282,33 -125,23 +282,33 @@@ static void date_data_func(GtkTreeViewC
                           GtkTreeIter *iter,
                           gpointer data)
  {
 -      int val;
 +      int val, idx;
        struct tm *tm;
        time_t when;
        char buffer[40];
  
 -      gtk_tree_model_get(model, iter, DIVE_DATE, &val, -1);
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, -1);
  
        /* 2038 problem */
        when = val;
  
        tm = gmtime(&when);
 -      snprintf(buffer, sizeof(buffer),
 -              "%s, %s %d, %d %02d:%02d",
 -              weekday(tm->tm_wday),
 -              monthname(tm->tm_mon),
 -              tm->tm_mday, tm->tm_year + 1900,
 -              tm->tm_hour, tm->tm_min);
 +      switch(idx) {
 +      case -NEW_TRIP:
 +              snprintf(buffer, sizeof(buffer),
 +                      "Trip %s, %s %d, %d",
 +                      weekday(tm->tm_wday),
 +                      monthname(tm->tm_mon),
 +                      tm->tm_mday, tm->tm_year + 1900);
 +              break;
 +      default:
 +              snprintf(buffer, sizeof(buffer),
 +                      "%s, %s %d, %d %02d:%02d",
 +                      weekday(tm->tm_wday),
 +                      monthname(tm->tm_mon),
 +                      tm->tm_mday, tm->tm_year + 1900,
 +                      tm->tm_hour, tm->tm_min);
 +      }
        g_object_set(renderer, "text", buffer, NULL);
  }
  
@@@ -318,37 -151,34 +318,37 @@@ static void depth_data_func(GtkTreeView
                            GtkTreeIter *iter,
                            gpointer data)
  {
 -      int depth, integer, frac, len;
 +      int depth, integer, frac, len, idx;
        char buffer[40];
  
 -      gtk_tree_model_get(model, iter, DIVE_DEPTH, &depth, -1);
 -
 -      switch (output_units.length) {
 -      case METERS:
 -              /* To tenths of meters */
 -              depth = (depth + 49) / 100;
 -              integer = depth / 10;
 -              frac = depth % 10;
 -              if (integer < 20)
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DEPTH, &depth, -1);
 +
 +      if (idx < 0) {
 +              *buffer = '\0';
 +      } else {
 +              switch (output_units.length) {
 +              case METERS:
 +                      /* To tenths of meters */
 +                      depth = (depth + 49) / 100;
 +                      integer = depth / 10;
 +                      frac = depth % 10;
 +                      if (integer < 20)
 +                              break;
 +                      if (frac >= 5)
 +                              integer++;
 +                      frac = -1;
                        break;
 -              if (frac >= 5)
 -                      integer++;
 -              frac = -1;
 -              break;
 -      case FEET:
 -              integer = mm_to_feet(depth) + 0.5;
 -              frac = -1;
 -              break;
 -      default:
 -              return;
 +              case FEET:
 +                      integer = mm_to_feet(depth) + 0.5;
 +                      frac = -1;
 +                      break;
 +              default:
 +                      return;
 +              }
 +              len = snprintf(buffer, sizeof(buffer), "%d", integer);
 +              if (frac >= 0)
 +                      len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac);
        }
 -      len = snprintf(buffer, sizeof(buffer), "%d", integer);
 -      if (frac >= 0)
 -              len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac);
 -
        g_object_set(renderer, "text", buffer, NULL);
  }
  
@@@ -359,14 -189,10 +359,14 @@@ static void duration_data_func(GtkTreeV
                               gpointer data)
  {
        unsigned int sec;
 +      int idx;
        char buffer[16];
  
 -      gtk_tree_model_get(model, iter, DIVE_DURATION, &sec, -1);
 -      snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60);
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DURATION, &sec, -1);
 +      if (idx < 0)
 +              *buffer = '\0';
 +      else
 +              snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60);
  
        g_object_set(renderer, "text", buffer, NULL);
  }
@@@ -377,13 -203,13 +377,13 @@@ static void temperature_data_func(GtkTr
                                  GtkTreeIter *iter,
                                  gpointer data)
  {
 -      int value;
 +      int value, idx;
        char buffer[80];
  
 -      gtk_tree_model_get(model, iter, DIVE_TEMPERATURE, &value, -1);
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_TEMPERATURE, &value, -1);
  
        *buffer = 0;
 -      if (value) {
 +      if (idx >= 0 && value) {
                double deg;
                switch (output_units.temperature) {
                case CELSIUS:
        g_object_set(renderer, "text", buffer, NULL);
  }
  
 +static void nr_data_func(GtkTreeViewColumn *col,
 +                         GtkCellRenderer *renderer,
 +                         GtkTreeModel *model,
 +                         GtkTreeIter *iter,
 +                         gpointer data)
 +{
 +      int idx, nr;
 +      char buffer[40];
 +
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, -1);
 +      if (idx < 0)
 +              *buffer = '\0';
 +      else
 +              snprintf(buffer, sizeof(buffer), "%d", nr);
 +      g_object_set(renderer, "text", buffer, NULL);
 +}
 +
  /*
   * Get "maximal" dive gas for a dive.
   * Rules:
@@@ -460,38 -269,6 +460,38 @@@ newmax
        *o2low_p = mino2;
  }
  
 +static int total_weight(struct dive *dive)
 +{
 +      int i, total_grams = 0;
 +
 +      if (dive)
 +              for (i=0; i< MAX_WEIGHTSYSTEMS; i++)
 +                      total_grams += dive->weightsystem[i].weight.grams;
 +      return total_grams;
 +}
 +
 +static void weight_data_func(GtkTreeViewColumn *col,
 +                           GtkCellRenderer *renderer,
 +                           GtkTreeModel *model,
 +                           GtkTreeIter *iter,
 +                           gpointer data)
 +{
 +      int indx, decimals;
 +      double value;
 +      char buffer[80];
 +      struct dive *dive;
 +
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &indx, -1);
 +      dive = get_dive(indx);
 +      value = get_weight_units(total_weight(dive), &decimals, NULL);
 +      if (value == 0.0)
 +              *buffer = '\0';
 +      else
 +              snprintf(buffer, sizeof(buffer), "%.*f", decimals, value);
 +
 +      g_object_set(renderer, "text", buffer, NULL);
 +}
 +
  static gint nitrox_sort_func(GtkTreeModel *model,
        GtkTreeIter *iter_a,
        GtkTreeIter *iter_b,
@@@ -527,16 -304,12 +527,16 @@@ static void nitrox_data_func(GtkTreeVie
                             GtkTreeIter *iter,
                             gpointer data)
  {
 -      int index, o2, he, o2low;
 +      int idx, o2, he, o2low;
        char buffer[80];
        struct dive *dive;
  
 -      gtk_tree_model_get(model, iter, DIVE_INDEX, &index, -1);
 -      dive = get_dive(index);
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
 +      if (idx < 0) {
 +              *buffer = '\0';
 +              goto exit;
 +      }
 +      dive = get_dive(idx);
        get_dive_gas(dive, &o2, &he, &o2low);
        o2 = (o2 + 5) / 10;
        he = (he + 5) / 10;
                        snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2);
        else
                strcpy(buffer, "air");
 -
 +exit:
        g_object_set(renderer, "text", buffer, NULL);
  }
  
@@@ -562,16 -335,16 +562,16 @@@ static void sac_data_func(GtkTreeViewCo
                          GtkTreeIter *iter,
                          gpointer data)
  {
 -      int value;
 +      int value, idx;
        const char *fmt;
        char buffer[16];
        double sac;
  
 -      gtk_tree_model_get(model, iter, DIVE_SAC, &value, -1);
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_SAC, &value, -1);
  
 -      if (!value) {
 -              g_object_set(renderer, "text", "", NULL);
 -              return;
 +      if (idx < 0 || !value) {
 +              *buffer = '\0';
 +              goto exit;
        }
  
        sac = value / 1000.0;
                break;
        }
        snprintf(buffer, sizeof(buffer), fmt, sac);
 -
 +exit:
        g_object_set(renderer, "text", buffer, NULL);
  }
  
@@@ -596,15 -369,17 +596,15 @@@ static void otu_data_func(GtkTreeViewCo
                          GtkTreeIter *iter,
                          gpointer data)
  {
 -      int value;
 +      int value, idx;
        char buffer[16];
  
 -      gtk_tree_model_get(model, iter, DIVE_OTU, &value, -1);
 -
 -      if (!value) {
 -              g_object_set(renderer, "text", "", NULL);
 -              return;
 -      }
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_OTU, &value, -1);
  
 -      snprintf(buffer, sizeof(buffer), "%d", value);
 +      if (idx < 0 || !value)
 +              *buffer = '\0';
 +      else
 +              snprintf(buffer, sizeof(buffer), "%d", value);
  
        g_object_set(renderer, "text", buffer, NULL);
  }
@@@ -726,55 -501,27 +726,55 @@@ static void get_cylinder(struct dive *d
        get_string(str, dive->cylinder[0].type.description);
  }
  
 +static void get_suit(struct dive *dive, char **str)
 +{
 +      get_string(str, dive->suit);
 +}
 +
  /*
   * Set up anything that could have changed due to editing
 - * of dive information
 + * of dive information; we need to do this for both models,
 + * so we simply call set_one_dive again with the non-current model
   */
 +/* forward declaration for recursion */
 +static gboolean set_one_dive(GtkTreeModel *model,
 +                           GtkTreePath *path,
 +                           GtkTreeIter *iter,
 +                           gpointer data);
 +
  static void fill_one_dive(struct dive *dive,
                          GtkTreeModel *model,
                          GtkTreeIter *iter)
  {
 -      char *location, *cylinder;
 +      char *location, *cylinder, *suit;
 +      GtkTreeStore *othermodel;
  
        get_cylinder(dive, &cylinder);
        get_location(dive, &location);
 +      get_suit(dive, &suit);
  
 -      gtk_list_store_set(GTK_LIST_STORE(model), iter,
 +      gtk_tree_store_set(GTK_TREE_STORE(model), iter,
                DIVE_NR, dive->number,
                DIVE_LOCATION, location,
                DIVE_CYLINDER, cylinder,
                DIVE_RATING, dive->rating,
                DIVE_SAC, dive->sac,
                DIVE_OTU, dive->otu,
 +              DIVE_TOTALWEIGHT, total_weight(dive),
 +              DIVE_SUIT, suit,
                -1);
 +
 +      free(location);
 +      free(cylinder);
 +      free(suit);
 +
 +      if (model == GTK_TREE_MODEL(dive_list.treemodel))
 +              othermodel = dive_list.listmodel;
 +      else
 +              othermodel = dive_list.treemodel;
 +      if (othermodel != dive_list.model)
 +              /* recursive call */
 +              gtk_tree_model_foreach(GTK_TREE_MODEL(othermodel), set_one_dive, dive);
  }
  
  static gboolean set_one_dive(GtkTreeModel *model,
                             GtkTreeIter *iter,
                             gpointer data)
  {
 -      GValue value = {0, };
 +      int idx;
        struct dive *dive;
  
        /* Get the dive number */
 -      gtk_tree_model_get_value(model, iter, DIVE_INDEX, &value);
 -      dive = get_dive(g_value_get_int(&value));
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
 +      if (idx < 0)
 +              return FALSE;
 +      dive = get_dive(idx);
        if (!dive)
                return TRUE;
        if (data && dive != data)
@@@ -824,9 -569,6 +824,9 @@@ void update_dive_list_units(void
        (void) get_temp_units(0, &unit);
        gtk_tree_view_column_set_title(dive_list.temperature, unit);
  
 +      (void) get_weight_units(0, NULL, &unit);
 +      gtk_tree_view_column_set_title(dive_list.totalweight, unit);
 +
        gtk_tree_model_foreach(model, set_one_dive, NULL);
  }
  
@@@ -834,112 -576,41 +834,112 @@@ void update_dive_list_col_visibility(vo
  {
        gtk_tree_view_column_set_visible(dive_list.cylinder, visible_cols.cylinder);
        gtk_tree_view_column_set_visible(dive_list.temperature, visible_cols.temperature);
 +      gtk_tree_view_column_set_visible(dive_list.totalweight, visible_cols.totalweight);
 +      gtk_tree_view_column_set_visible(dive_list.suit, visible_cols.suit);
        gtk_tree_view_column_set_visible(dive_list.nitrox, visible_cols.nitrox);
        gtk_tree_view_column_set_visible(dive_list.sac, visible_cols.sac);
        gtk_tree_view_column_set_visible(dive_list.otu, visible_cols.otu);
        return;
  }
  
 +/* random heuristic - not diving in three days implies new dive trip */
 +#define TRIP_THRESHOLD 3600*24*3
 +static int new_group(struct dive *dive, struct dive **last_dive, time_t *tm_date)
 +{
 +      if (!last_dive)
 +              return TRUE;
 +      if (*last_dive) {
 +              struct dive *ldive = *last_dive;
 +              if (abs(dive->when - ldive->when) < TRIP_THRESHOLD) {
 +                      *last_dive = dive;
 +                      return FALSE;
 +              }
 +      }
 +      *last_dive = dive;
 +      if (tm_date) {
 +              struct tm *tm1 = gmtime(&dive->when);
 +              tm1->tm_sec = 0;
 +              tm1->tm_min = 0;
 +              tm1->tm_hour = 0;
 +              *tm_date = mktime(tm1);
 +      }
 +      return TRUE;
 +}
 +
  static void fill_dive_list(void)
  {
        int i;
 -      GtkTreeIter iter;
 -      GtkListStore *store;
 +      GtkTreeIter iter, parent_iter;
 +      GtkTreeStore *liststore, *treestore;
 +      struct dive *last_dive = NULL;
 +      struct dive *first_trip_dive = NULL;
 +      struct dive *last_trip_dive = NULL;
 +      time_t dive_date;
  
 -      store = GTK_LIST_STORE(dive_list.model);
 +      treestore = GTK_TREE_STORE(dive_list.treemodel);
 +      liststore = GTK_TREE_STORE(dive_list.listmodel);
  
        i = dive_table.nr;
        while (--i >= 0) {
                struct dive *dive = dive_table.dives[i];
  
 +              if (new_group(dive, &last_dive, &dive_date))
 +              {
 +                      /* make sure we display the first date of the trip in previous summary */
 +                      if (first_trip_dive && last_trip_dive && last_trip_dive->when < first_trip_dive->when)
 +                              gtk_tree_store_set(treestore, &parent_iter,
 +                                              DIVE_DATE, last_trip_dive->when,
 +                                              DIVE_LOCATION, last_trip_dive->location,
 +                                              -1);
 +                      first_trip_dive = dive;
 +
 +                      gtk_tree_store_append(treestore, &parent_iter, NULL);
 +                      gtk_tree_store_set(treestore, &parent_iter,
 +                                      DIVE_INDEX, -NEW_TRIP,
 +                                      DIVE_NR, -NEW_TRIP,
 +                                      DIVE_DATE, dive_date,
 +                                      DIVE_LOCATION, dive->location,
 +                                      DIVE_TEMPERATURE, 0,
 +                                      DIVE_SAC, 0,
 +                                      -1);
 +              }
 +              last_trip_dive = dive;
                update_cylinder_related_info(dive);
 -              gtk_list_store_append(store, &iter);
 -              gtk_list_store_set(store, &iter,
 +              gtk_tree_store_append(treestore, &iter, &parent_iter);
 +              gtk_tree_store_set(treestore, &iter,
                        DIVE_INDEX, i,
                        DIVE_NR, dive->number,
                        DIVE_DATE, dive->when,
                        DIVE_DEPTH, dive->maxdepth,
                        DIVE_DURATION, dive->duration.seconds,
 -                      DIVE_LOCATION, "location",
 +                      DIVE_LOCATION, dive->location,
 +                      DIVE_RATING, dive->rating,
                        DIVE_TEMPERATURE, dive->watertemp.mkelvin,
                        DIVE_SAC, 0,
                        -1);
 +              gtk_tree_store_append(liststore, &iter, NULL);
 +              gtk_tree_store_set(liststore, &iter,
 +                      DIVE_INDEX, i,
 +                      DIVE_NR, dive->number,
 +                      DIVE_DATE, dive->when,
 +                      DIVE_DEPTH, dive->maxdepth,
 +                      DIVE_DURATION, dive->duration.seconds,
 +                      DIVE_LOCATION, dive->location,
 +                      DIVE_RATING, dive->rating,
 +                      DIVE_TEMPERATURE, dive->watertemp.mkelvin,
 +                      DIVE_TOTALWEIGHT, 0,
 +                      DIVE_SUIT, dive->suit,
 +                      DIVE_SAC, 0,
 +                      -1);
        }
  
        update_dive_list_units();
        if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dive_list.model), &iter)) {
                GtkTreeSelection *selection;
 +
 +              /* select the last dive (and make sure it's an actual dive that is selected) */
 +              gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1);
 +              first_leaf(GTK_TREE_MODEL(dive_list.model), &iter, &selected_dive);
                selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
                gtk_tree_selection_select_iter(selection, &iter);
        }
  
  void dive_list_update_dives(void)
  {
 -      gtk_list_store_clear(GTK_LIST_STORE(dive_list.model));
 +      gtk_tree_store_clear(GTK_TREE_STORE(dive_list.treemodel));
 +      gtk_tree_store_clear(GTK_TREE_STORE(dive_list.listmodel));
        fill_dive_list();
        repaint_dive();
  }
@@@ -960,14 -630,12 +960,14 @@@ static struct divelist_column 
        unsigned int flags;
        int *visible;
  } dl_column[] = {
 -      [DIVE_NR] = { "#", NULL, NULL, ALIGN_RIGHT | UNSORTABLE },
 +      [DIVE_NR] = { "#", nr_data_func, NULL, ALIGN_RIGHT | UNSORTABLE },
        [DIVE_DATE] = { "Date", date_data_func, NULL, ALIGN_LEFT },
        [DIVE_RATING] = { UTF8_BLACKSTAR, star_data_func, NULL, ALIGN_LEFT },
        [DIVE_DEPTH] = { "ft", depth_data_func, NULL, ALIGN_RIGHT },
        [DIVE_DURATION] = { "min", duration_data_func, NULL, ALIGN_RIGHT },
        [DIVE_TEMPERATURE] = { UTF8_DEGREE "F", temperature_data_func, NULL, ALIGN_RIGHT, &visible_cols.temperature },
 +      [DIVE_TOTALWEIGHT] = { "lbs", weight_data_func, NULL, ALIGN_RIGHT, &visible_cols.totalweight },
 +      [DIVE_SUIT] = { "Suit", NULL, NULL, ALIGN_LEFT, &visible_cols.suit },
        [DIVE_CYLINDER] = { "Cyl", NULL, NULL, 0, &visible_cols.cylinder },
        [DIVE_NITROX] = { "O" UTF8_SUBSCRIPT_2 "%", nitrox_data_func, nitrox_sort_func, 0, &visible_cols.nitrox },
        [DIVE_SAC] = { "SAC", sac_data_func, NULL, 0, &visible_cols.sac },
@@@ -985,20 -653,14 +985,20 @@@ static GtkTreeViewColumn *divelist_colu
        unsigned int flags = col->flags;
        int *visible = col->visible;
        GtkWidget *tree_view = dl->tree_view;
 -      GtkListStore *model = dl->model;
 +      GtkTreeStore *treemodel = dl->treemodel;
 +      GtkTreeStore *listmodel = dl->listmodel;
        GtkTreeViewColumn *ret;
  
        if (visible && !*visible)
                flags |= INVISIBLE;
        ret = tree_view_column(tree_view, index, title, data_func, flags);
 -      if (sort_func)
 -              gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), index, sort_func, NULL, NULL);
 +      if (sort_func) {
 +              /* the sort functions are needed in the corresponding models */
 +              if (index == DIVE_DATE)
 +                      gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(treemodel), index, sort_func, NULL, NULL);
 +              else
 +                      gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(listmodel), index, sort_func, NULL, NULL);
 +      }
        return ret;
  }
  
@@@ -1014,17 -676,15 +1014,17 @@@ static void realize_cb(GtkWidget *tree_
  static void row_activated_cb(GtkTreeView *tree_view,
                        GtkTreePath *path,
                        GtkTreeViewColumn *column,
 -                      GtkTreeModel *model)
 +                      gpointer userdata)
  {
        int index;
        GtkTreeIter iter;
  
 -      if (!gtk_tree_model_get_iter(model, &iter, path))
 +      if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path))
                return;
 -      gtk_tree_model_get(model, &iter, DIVE_INDEX, &index, -1);
 -      edit_dive_info(get_dive(index));
 +      gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &index, -1);
 +      /* a negative index is special for the "group by date" entries */
 +      if (index >= 0)
 +              edit_dive_info(get_dive(index));
  }
  
  void add_dive_cb(GtkWidget *menuitem, gpointer data)
        free(dive);
  }
  
 +void edit_dive_cb(GtkWidget *menuitem, gpointer data)
 +{
 +      edit_multi_dive_info(amount_selected, selectiontracker);
 +}
 +
  static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button)
  {
-       GtkWidget *menu, *menuitem;
+       GtkWidget *menu, *menuitem, *image;
 +      char editlabel[] = "Edit dives";
  
        menu = gtk_menu_new();
-       menuitem = gtk_menu_item_new_with_label("Add dive");
+       menuitem = gtk_image_menu_item_new_with_label("Add dive");
+       image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
+       gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
 -      g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), model);
 +      g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), NULL);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 +      if (amount_selected) {
 +              if (amount_selected == 1)
 +                      editlabel[strlen(editlabel) - 1] = '\0';
 +              menuitem = gtk_menu_item_new_with_label(editlabel);
 +              g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_cb), model);
 +              gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 +      }
        gtk_widget_show_all(menu);
  
        gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
                button, gtk_get_current_event_time());
  }
  
 -static void popup_menu_cb(GtkTreeView *tree_view,
 -                      GtkTreeModel *model)
 +static void popup_menu_cb(GtkTreeView *tree_view, gpointer userdata)
  {
 -      popup_divelist_menu(tree_view, model, 0);
 +      popup_divelist_menu(tree_view, GTK_TREE_MODEL(dive_list.model), 0);
  }
  
 -static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, GtkTreeModel *model)
 +static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpointer userdata)
  {
        /* Right-click? Bring up the menu */
        if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3) {
 -              popup_divelist_menu(GTK_TREE_VIEW(treeview), model, 3);
 +              popup_divelist_menu(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(dive_list.model), 3);
                return TRUE;
        }
        return FALSE;
  }
  
 +/* we need to have a temporary copy of the selected dives while
 +   switching model as the selection_cb function keeps getting called
 +   when gtk_tree_selection_select_path is called.  We also need to
 +   keep copies of the sort order so we can restore that as well after
 +   switching models. */
 +static int *oldselection;
 +static int old_nr_selected;
 +static gboolean second_call = FALSE;
 +static GtkSortType sortorder[] = { [0 ... DIVELIST_COLUMNS - 1] = GTK_SORT_DESCENDING, };
 +static int lastcol = DIVE_DATE;
 +
 +/* Check if this dive was selected previously and select it again in the new model;
 + * This is used after we switch models to maintain consistent selections.
 + * We always return FALSE to iterate through all dives */
 +static gboolean select_selected(GtkTreeModel *model, GtkTreePath *path,
 +                              GtkTreeIter *iter, gpointer data)
 +{
 +      int i, idx;
 +      GtkTreeSelection *selection = GTK_TREE_SELECTION(data);
 +
 +      gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
 +      for (i = 0; i < old_nr_selected; i++)
 +              if (oldselection[i] == idx) {
 +                      gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path);
 +                      gtk_tree_selection_select_path(selection, path);
 +
 +                      return FALSE;
 +              }
 +      return FALSE;
 +
 +}
 +
 +static void update_column_and_order(int colid)
 +{
 +      /* Careful: the index into treecolumns is off by one as we don't have a
 +         tree_view column for DIVE_INDEX */
 +      GtkTreeViewColumn **treecolumns = &dive_list.nr;
 +
 +      /* this will trigger a second call into sort_column_change_cb,
 +         so make sure we don't start an infinite recursion... */
 +      second_call = TRUE;
 +      gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dive_list.model), colid, sortorder[colid]);
 +      gtk_tree_view_column_set_sort_order(treecolumns[colid - 1], sortorder[colid]);
 +      second_call = FALSE;
 +}
 +
 +/* If the sort column is date (default), show the tree model.
 +   For every other sort column only show the list model.
 +   If the model changed, inform the new model of the chosen sort column and make
 +   sure the same dives are still selected.
 +
 +   The challenge with this function is that once we change the model
 +   we also need to change the sort column again (as it was changed in
 +   the other model) and that causes this function to be called
 +   recursively - so we need to catch that.
 +*/
 +static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data)
 +{
 +      int colid;
 +      GtkSortType order;
 +      GtkTreeStore *currentmodel = dive_list.model;
 +
 +      if (second_call)
 +              return;
 +
 +      gtk_tree_sortable_get_sort_column_id(treeview, &colid, &order);
 +      if(colid == lastcol) {
 +              /* we just changed sort order */
 +              sortorder[colid] = order;
 +              return;
 +      } else {
 +              lastcol = colid;
 +      }
 +      if(colid == DIVE_DATE)
 +              dive_list.model = dive_list.treemodel;
 +      else
 +              dive_list.model = dive_list.listmodel;
 +      if (dive_list.model != currentmodel) {
 +              GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
 +
 +              /* remember what is currently selected, switch models and reselect the selected rows */
 +              old_nr_selected = amount_selected;
 +              oldselection = malloc(old_nr_selected * sizeof(int));
 +              if (amount_selected)
 +                      memcpy(oldselection, selectiontracker, amount_selected * sizeof(int));
 +              gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), GTK_TREE_MODEL(dive_list.model));
 +
 +              update_column_and_order(colid);
 +
 +              if (old_nr_selected) {
 +                      /* we need to select all the dives that were selected */
 +                      /* this is fundamentally an n^2 algorithm as implemented - YUCK */
 +                      gtk_tree_model_foreach(GTK_TREE_MODEL(dive_list.model), select_selected, selection);
 +              }
 +      } else {
 +              if (order != sortorder[colid]) {
 +                      update_column_and_order(colid);
 +              }
 +      }
 +}
 +
  GtkWidget *dive_list_create(void)
  {
        GtkTreeSelection  *selection;
  
 -      dive_list.model = gtk_list_store_new(DIVELIST_COLUMNS,
 +      dive_list.listmodel = gtk_tree_store_new(DIVELIST_COLUMNS,
 +                              G_TYPE_INT,                     /* index */
 +                              G_TYPE_INT,                     /* nr */
 +                              G_TYPE_INT,                     /* Date */
 +                              G_TYPE_INT,                     /* Star rating */
 +                              G_TYPE_INT,                     /* Depth */
 +                              G_TYPE_INT,                     /* Duration */
 +                              G_TYPE_INT,                     /* Temperature */
 +                              G_TYPE_INT,                     /* Total weight */
 +                              G_TYPE_STRING,                  /* Suit */
 +                              G_TYPE_STRING,                  /* Cylinder */
 +                              G_TYPE_INT,                     /* Nitrox */
 +                              G_TYPE_INT,                     /* SAC */
 +                              G_TYPE_INT,                     /* OTU */
 +                              G_TYPE_STRING                   /* Location */
 +                              );
 +      dive_list.treemodel = gtk_tree_store_new(DIVELIST_COLUMNS,
                                G_TYPE_INT,                     /* index */
                                G_TYPE_INT,                     /* nr */
                                G_TYPE_INT,                     /* Date */
                                G_TYPE_INT,                     /* Depth */
                                G_TYPE_INT,                     /* Duration */
                                G_TYPE_INT,                     /* Temperature */
 +                              G_TYPE_INT,                     /* Total weight */
 +                              G_TYPE_STRING,                  /* Suit */
                                G_TYPE_STRING,                  /* Cylinder */
                                G_TYPE_INT,                     /* Nitrox */
                                G_TYPE_INT,                     /* SAC */
                                G_TYPE_INT,                     /* OTU */
                                G_TYPE_STRING                   /* Location */
                                );
 +      dive_list.model = dive_list.treemodel;
        dive_list.tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dive_list.model));
        set_divelist_font(divelist_font);
  
        dive_list.depth = divelist_column(&dive_list, dl_column + DIVE_DEPTH);
        dive_list.duration = divelist_column(&dive_list, dl_column + DIVE_DURATION);
        dive_list.temperature = divelist_column(&dive_list, dl_column + DIVE_TEMPERATURE);
 +      dive_list.totalweight = divelist_column(&dive_list, dl_column + DIVE_TOTALWEIGHT);
 +      dive_list.suit = divelist_column(&dive_list, dl_column + DIVE_SUIT);
        dive_list.cylinder = divelist_column(&dive_list, dl_column + DIVE_CYLINDER);
        dive_list.nitrox = divelist_column(&dive_list, dl_column + DIVE_NITROX);
        dive_list.sac = divelist_column(&dive_list, dl_column + DIVE_SAC);
                                          NULL);
  
        g_signal_connect_after(dive_list.tree_view, "realize", G_CALLBACK(realize_cb), NULL);
 -      g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), dive_list.model);
 -      g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), dive_list.model);
 -      g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), dive_list.model);
 -      g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), dive_list.model);
 +      g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), NULL);
 +      g_signal_connect(dive_list.tree_view, "row-expanded", G_CALLBACK(row_expanded_cb), NULL);
 +      g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), NULL);
 +      g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), NULL);
 +      g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), NULL);
 +      g_signal_connect(dive_list.listmodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL);
 +      g_signal_connect(dive_list.treemodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL);
 +
 +      gtk_tree_selection_set_select_function(selection, modify_selection_cb, NULL, NULL);
  
        dive_list.container_widget = gtk_scrolled_window_new(NULL, NULL);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dive_list.container_widget),
diff --combined gtk-gui.c
index 02463d91e24c475c3a60f2a377db71577a3e3b68,bef03a04b0898818c69844660f0d66a9c8aa72a1..5bc46d2190248be4c7a7c68b121c2766258b9f0b
+++ b/gtk-gui.c
@@@ -170,91 -170,57 +170,75 @@@ static void file_open(GtkWidget *w, gpo
        gtk_widget_destroy(dialog);
  }
  
- static void file_save(GtkWidget *w, gpointer data)
+ static void file_save_as(GtkWidget *w, gpointer data)
  {
        GtkWidget *dialog;
 -      dialog = gtk_file_chooser_dialog_new("Save File",
 +      char *filename;
-       if (!existing_filename) {
-               dialog = gtk_file_chooser_dialog_new("Save File",
++      dialog = gtk_file_chooser_dialog_new("Save File As",
                GTK_WINDOW(main_window),
                GTK_FILE_CHOOSER_ACTION_SAVE,
                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
                NULL);
-               gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
-               gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
-               if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
-                       filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
-               }
-               gtk_widget_destroy(dialog);
-       } else {
-               filename = existing_filename;
-       }
-       if (filename){
-               save_dives(filename);
-               mark_divelist_changed(FALSE);
-       }
- }
- static void file_save_as(GtkWidget *w, gpointer data)
- {
-       GtkWidget *dialog;
-       char *filename;
-       dialog = gtk_file_chooser_dialog_new("Save File As",
-       GTK_WINDOW(main_window),
-       GTK_FILE_CHOOSER_ACTION_SAVE,
-       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
-       NULL);
        gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
 -      if (!existing_filename) {
 -              gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
 -      } else
 -              gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), existing_filename);
  
 +      gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), existing_filename);
        if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
 -              char *filename;
                filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 +      }
 +      gtk_widget_destroy(dialog);
 +
 +      if (filename){
                save_dives(filename);
+               set_filename(filename);
+               g_free(filename);
                mark_divelist_changed(FALSE);
        }
 -      gtk_widget_destroy(dialog);
  }
  
 -static void ask_save_changes()
+ static void file_save(GtkWidget *w, gpointer data)
+ {
+       if (!existing_filename)
+               return file_save_as(w, data);
+       save_dives(existing_filename);
++      mark_divelist_changed(FALSE);
+ }
 +static gboolean ask_save_changes()
  {
        GtkWidget *dialog, *label, *content;
 +      gboolean quit = TRUE;
        dialog = gtk_dialog_new_with_buttons("Save Changes?",
                GTK_WINDOW(main_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
                GTK_STOCK_NO, GTK_RESPONSE_NO,
 +              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                NULL);
        content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
 -      label = gtk_label_new ("You have unsaved changes\nWould you like to save those before exiting the program?");
 +
 +      if (!existing_filename){
 +              label = gtk_label_new (
 +                      "You have unsaved changes\nWould you like to save those before exiting the program?");
 +      } else {
 +              char *label_text = (char*) malloc(sizeof(char) * (92 + strlen(existing_filename)));
 +              sprintf(label_text,
 +                      "You have unsaved changes to file: %s \nWould you like to save those before exiting the program?",
 +                      existing_filename);
 +              label = gtk_label_new (label_text);
 +              g_free(label_text);
 +      }
        gtk_container_add (GTK_CONTAINER (content), label);
        gtk_widget_show_all (dialog);
        gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
-       gint *outcode = gtk_dialog_run(GTK_DIALOG(dialog));
 -      if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
++      gint outcode = gtk_dialog_run(GTK_DIALOG(dialog));
 +      if (outcode == GTK_RESPONSE_ACCEPT) {
                file_save(NULL,NULL);
 +      } else if (outcode == GTK_RESPONSE_CANCEL) {
 +              quit = FALSE;
        }
        gtk_widget_destroy(dialog);
 +      return quit;
  }
  
  static gboolean on_delete(GtkWidget* w, gpointer data)
        /* Make sure to flush any modified dive data */
        update_dive(NULL);
  
 +      gboolean quit = TRUE;
        if (unsaved_changes())
 -              ask_save_changes();
 +              quit = ask_save_changes();
  
 -      return FALSE; /* go ahead, kill the program, we're good now */
 +      if (quit){
 +              return FALSE; /* go ahead, kill the program, we're good now */
 +      } else {
 +              return TRUE; /* We are not leaving */
 +      }
  }
  
  static void on_destroy(GtkWidget* w, gpointer data)
@@@ -283,13 -244,9 +267,13 @@@ static void quit(GtkWidget *w, gpointe
        /* Make sure to flush any modified dive data */
        update_dive(NULL);
  
 +      gboolean quit = TRUE;
        if (unsaved_changes())
 -              ask_save_changes();
 -      gtk_main_quit();
 +              quit = ask_save_changes();
 +
 +      if (quit){
 +              gtk_main_quit();
 +      }
  }
  
  GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title,
@@@ -400,8 -357,6 +384,8 @@@ OPTIONCALLBACK(otu_toggle, visible_cols
  OPTIONCALLBACK(sac_toggle, visible_cols.sac)
  OPTIONCALLBACK(nitrox_toggle, visible_cols.nitrox)
  OPTIONCALLBACK(temperature_toggle, visible_cols.temperature)
 +OPTIONCALLBACK(totalweight_toggle, visible_cols.totalweight)
 +OPTIONCALLBACK(suit_toggle, visible_cols.suit)
  OPTIONCALLBACK(cylinder_toggle, visible_cols.cylinder)
  
  static void event_toggle(GtkWidget *w, gpointer _data)
@@@ -457,47 -412,37 +441,47 @@@ static void preferences_dialog(GtkWidge
                "lbs",  set_lbs, (output_units.weight == LBS),
                NULL);
  
 -      frame = gtk_frame_new("Columns");
 +      frame = gtk_frame_new("Show Columns");
        gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
  
        box = gtk_hbox_new(FALSE, 6);
        gtk_container_add(GTK_CONTAINER(frame), box);
  
 -      button = gtk_check_button_new_with_label("Show Temp");
 +      button = gtk_check_button_new_with_label("Temp");
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.temperature);
        gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
        g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(temperature_toggle), NULL);
  
 -      button = gtk_check_button_new_with_label("Show Cyl");
 +      button = gtk_check_button_new_with_label("Cyl");
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.cylinder);
        gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
        g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(cylinder_toggle), NULL);
  
 -      button = gtk_check_button_new_with_label("Show O" UTF8_SUBSCRIPT_2 "%");
 +      button = gtk_check_button_new_with_label("O" UTF8_SUBSCRIPT_2 "%");
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.nitrox);
        gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
        g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(nitrox_toggle), NULL);
  
 -      button = gtk_check_button_new_with_label("Show SAC");
 +      button = gtk_check_button_new_with_label("SAC");
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.sac);
        gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
        g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(sac_toggle), NULL);
  
 -      button = gtk_check_button_new_with_label("Show OTU");
 +      button = gtk_check_button_new_with_label("OTU");
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.otu);
        gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
        g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL);
  
 +      button = gtk_check_button_new_with_label("Weight");
 +      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.totalweight);
 +      gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
 +      g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(totalweight_toggle), NULL);
 +
 +      button = gtk_check_button_new_with_label("Suit");
 +      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.suit);
 +      gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
 +      g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL);
 +
        font = gtk_font_button_new_with_font(divelist_font);
        gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
  
                subsurface_set_conf("fahrenheit", PREF_BOOL, BOOL_TO_PTR(output_units.temperature == FAHRENHEIT));
                subsurface_set_conf("lbs", PREF_BOOL, BOOL_TO_PTR(output_units.weight == LBS));
                subsurface_set_conf("TEMPERATURE", PREF_BOOL, BOOL_TO_PTR(visible_cols.temperature));
 +              subsurface_set_conf("TOTALWEIGHT", PREF_BOOL, BOOL_TO_PTR(visible_cols.totalweight));
 +              subsurface_set_conf("SUIT", PREF_BOOL, BOOL_TO_PTR(visible_cols.suit));
                subsurface_set_conf("CYLINDER", PREF_BOOL, BOOL_TO_PTR(visible_cols.cylinder));
                subsurface_set_conf("NITROX", PREF_BOOL, BOOL_TO_PTR(visible_cols.nitrox));
                subsurface_set_conf("SAC", PREF_BOOL, BOOL_TO_PTR(visible_cols.sac));
@@@ -675,26 -618,28 +659,28 @@@ static void view_info(GtkWidget *w, gpo
        gtk_paned_set_position(GTK_PANED(hpane), 65535);
  }
  
- /* Ooh. I don't know how to get the half-way size. So I'm just using random numbers */
  static void view_three(GtkWidget *w, gpointer data)
  {
-       gtk_paned_set_position(GTK_PANED(hpane), 400);
-       gtk_paned_set_position(GTK_PANED(vpane), 200);
+       GtkAllocation alloc;
+       gtk_widget_get_allocation(hpane, &alloc);
+       gtk_paned_set_position(GTK_PANED(hpane), alloc.width/2);
+       gtk_widget_get_allocation(vpane, &alloc);
+       gtk_paned_set_position(GTK_PANED(vpane), alloc.height/2);
  }
  
  static GtkActionEntry menu_items[] = {
-       { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
-       { "LogMenuAction",  GTK_STOCK_FILE, "Log", NULL, NULL, NULL},
-       { "ViewMenuAction",  GTK_STOCK_FILE, "View", NULL, NULL, NULL},
-       { "FilterMenuAction",  GTK_STOCK_FILE, "Filter", NULL, NULL, NULL},
-       { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
+       { "FileMenuAction", NULL, "File", NULL, NULL, NULL},
+       { "LogMenuAction",  NULL, "Log", NULL, NULL, NULL},
+       { "ViewMenuAction",  NULL, "View", NULL, NULL, NULL},
+       { "FilterMenuAction",  NULL, "Filter", NULL, NULL, NULL},
+       { "HelpMenuAction", NULL, "Help", NULL, NULL, NULL},
        { "OpenFile",       GTK_STOCK_OPEN, NULL,   CTRLCHAR "O", NULL, G_CALLBACK(file_open) },
        { "SaveFile",       GTK_STOCK_SAVE, NULL,   CTRLCHAR "S", NULL, G_CALLBACK(file_save) },
 -      { "SaveFileAs",     GTK_STOCK_SAVE_AS, NULL,   CTRLCHAR "<Shift>S", NULL, G_CALLBACK(file_save_as) },
 +      { "SaveAsFile",     GTK_STOCK_SAVE_AS, NULL,   SHIFTCHAR CTRLCHAR "S", NULL, G_CALLBACK(file_save_as) },
        { "Print",          GTK_STOCK_PRINT, NULL,  CTRLCHAR "P", NULL, G_CALLBACK(do_print) },
        { "Import",         NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) },
-       { "AddDive",        NULL, "Add Dive", NULL, NULL, G_CALLBACK(add_dive_cb) },
-       { "Preferences",    NULL, "Preferences", PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) },
+       { "AddDive",        GTK_STOCK_ADD, "Add Dive", NULL, NULL, G_CALLBACK(add_dive_cb) },
+       { "Preferences",    GTK_STOCK_PREFERENCES, "Preferences", PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) },
        { "Renumber",       NULL, "Renumber", NULL, NULL, G_CALLBACK(renumber_dialog) },
        { "SelectEvents",   NULL, "SelectEvents", NULL, NULL, G_CALLBACK(selectevents_dialog) },
        { "Quit",           GTK_STOCK_QUIT, NULL,   CTRLCHAR "Q", NULL, G_CALLBACK(quit) },
@@@ -712,7 -657,7 +698,7 @@@ static const gchar* ui_string = " 
                        <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
                                <menuitem name=\"Open\" action=\"OpenFile\" /> \
                                <menuitem name=\"Save\" action=\"SaveFile\" /> \
 -                              <menuitem name=\"Save as\" action=\"SaveFileAs\" /> \
 +                              <menuitem name=\"Save As\" action=\"SaveAsFile\" /> \
                                <menuitem name=\"Print\" action=\"Print\" /> \
                                <separator name=\"Separator1\"/> \
                                <menuitem name=\"Preferences\" action=\"Preferences\" /> \
@@@ -794,8 -739,6 +780,8 @@@ void init_ui(int *argcp, char ***argvp
        /* an unset key is FALSE - all these are hidden by default */
        visible_cols.cylinder = PTR_TO_BOOL(subsurface_get_conf("CYLINDER", PREF_BOOL));
        visible_cols.temperature = PTR_TO_BOOL(subsurface_get_conf("TEMPERATURE", PREF_BOOL));
 +      visible_cols.totalweight = PTR_TO_BOOL(subsurface_get_conf("TOTALWEIGHT", PREF_BOOL));
 +      visible_cols.suit = PTR_TO_BOOL(subsurface_get_conf("SUIT", PREF_BOOL));
        visible_cols.nitrox = PTR_TO_BOOL(subsurface_get_conf("NITROX", PREF_BOOL));
        visible_cols.otu = PTR_TO_BOOL(subsurface_get_conf("OTU", PREF_BOOL));
        visible_cols.sac = PTR_TO_BOOL(subsurface_get_conf("SAC", PREF_BOOL));
        gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Notes"));
  
        /* Frame for dive equipment */
 -      nb_page = equipment_widget();
 +      nb_page = equipment_widget(W_IDX_PRIMARY);
        gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Equipment"));
  
        /* Frame for single dive statistics */
@@@ -1255,7 -1198,9 +1241,9 @@@ void update_progressbar_text(progressba
  
  void set_filename(const char *filename)
  {
-       if (!existing_filename && filename)
+       if (existing_filename)
+               free(existing_filename);
+       existing_filename = NULL;
+       if (filename)
                existing_filename = strdup(filename);
-       return;
  }
diff --combined info.c
index 0ee401488062aa3105850863bf15c1c8feaca8b0,e935823b2ddb267b56d25ca63eda2d8f415fdaf4..38776487bae8b69b0fb24efe97e4641b7a628fe1
--- 1/info.c
--- 2/info.c
+++ b/info.c
@@@ -19,9 -19,9 +19,9 @@@
  #include "display-gtk.h"
  #include "divelist.h"
  
 -static GtkEntry *location, *buddy, *divemaster, *rating;
 +static GtkEntry *location, *buddy, *divemaster, *rating, *suit;
  static GtkTextView *notes;
 -static GtkListStore *location_list, *people_list, *star_list;
 +static GtkListStore *location_list, *people_list, *star_list, *suit_list;
  
  static char *get_text(GtkTextView *view)
  {
@@@ -43,50 -43,16 +43,50 @@@ static int text_changed(const char *old
                (!old && strcmp("",new));
  }
  
 -static char *get_combo_box_entry_text(GtkComboBoxEntry *combo_box, char **textp)
 +static const char *skip_space(const char *str)
 +{
 +      if (str) {
 +              while (isspace(*str))
 +                      str++;
 +              if (!*str)
 +                      str = NULL;
 +      }
 +      return str;
 +}
 +
 +/*
 + * Get the string from a combo box.
 + *
 + * The "master" string is the string of the current dive - we only consider it
 + * changed if the old string is either empty, or matches that master string.
 + */
 +static char *get_combo_box_entry_text(GtkComboBoxEntry *combo_box, char **textp, const char *master)
  {
        char *old = *textp;
 +      const char *old_text;
        const gchar *new;
        GtkEntry *entry;
  
 +      old_text = skip_space(old);
 +      master = skip_space(master);
 +
 +      /*
 +       * If we had a master string, and it doesn't match our old
 +       * string, we will always pick the old value (it means that
 +       * we're editing another dive's info that already had a
 +       * valid value).
 +       */
 +      if (master && old_text)
 +              if (strcmp(master, old_text))
 +                      return NULL;
 +
        entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_box)));
        new = gtk_entry_get_text(entry);
        while (isspace(*new))
                new++;
 +      /* If the master string didn't change, don't change other dives either! */
 +      if (!text_changed(master,new))
 +              return NULL;
        if (!text_changed(old,new))
                return NULL;
        free(old);
@@@ -130,7 -96,6 +130,7 @@@ void show_dive_info(struct dive *dive
        SET_TEXT_VALUE(divemaster);
        SET_TEXT_VALUE(buddy);
        SET_TEXT_VALUE(location);
 +      SET_TEXT_VALUE(suit);
        gtk_entry_set_text(rating, star_strings[dive->rating]);
        gtk_text_buffer_set_text(gtk_text_view_get_buffer(notes),
                dive && dive->notes ? dive->notes : "", -1);
@@@ -166,18 -131,25 +166,26 @@@ static int delete_dive_info(struct div
  
  static void info_menu_edit_cb(GtkMenuItem *menuitem, gpointer user_data)
  {
 -      edit_dive_info(current_dive);
 +      edit_multi_dive_info(amount_selected, selectiontracker);
  }
  
  static void info_menu_delete_cb(GtkMenuItem *menuitem, gpointer user_data)
  {
 +      /* this needs to delete all the selected dives as well, I guess? */
        delete_dive_info(current_dive);
  }
  
- static void add_menu_item(GtkMenu *menu, const char *label, void (*cb)(GtkMenuItem *, gpointer))
+ static void add_menu_item(GtkMenu *menu, const char *label, const char *icon, void (*cb)(GtkMenuItem *, gpointer))
  {
-       GtkWidget *item = gtk_menu_item_new_with_label(label);
+       GtkWidget *item;
+       if (icon) {
+               GtkWidget *image;
+               item = gtk_image_menu_item_new_with_label(label);
+               image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU);
+               gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
+       } else {
+               item = gtk_menu_item_new_with_label(label);
+       }
        g_signal_connect(item, "activate", G_CALLBACK(cb), NULL);
        gtk_widget_show(item); /* Yes, really */
        gtk_menu_prepend(menu, item);
  
  static void populate_popup_cb(GtkTextView *entry, GtkMenu *menu, gpointer user_data)
  {
-       add_menu_item(menu, "Delete", info_menu_delete_cb);
-       add_menu_item(menu, "Edit", info_menu_edit_cb);
+       add_menu_item(menu, "Delete", GTK_STOCK_DELETE, info_menu_delete_cb);
+       add_menu_item(menu, "Edit", GTK_STOCK_EDIT, info_menu_edit_cb);
  }
  
  static GtkEntry *text_value(GtkWidget *box, const char *label)
@@@ -278,8 -250,6 +286,8 @@@ static gboolean match_string_entry(GtkT
  
        gtk_tree_model_get(model, iter, 0, &entry, -1);
        cmp = strcmp(entry, string);
 +      if (entry)
 +              free(entry);
  
        /* Stop. The entry is bigger than the new one */
        if (cmp > 0)
@@@ -334,11 -304,6 +342,11 @@@ void add_location(const char *string
        add_string_list_entry(string, location_list);
  }
  
 +void add_suit(const char *string)
 +{
 +      add_string_list_entry(string, suit_list);
 +}
 +
  static int get_rating(const char *string)
  {
        int rating_val = 0;
  }
  
  struct dive_info {
 -      GtkComboBoxEntry *location, *divemaster, *buddy, *rating;
 +      GtkComboBoxEntry *location, *divemaster, *buddy, *rating, *suit;
        GtkTextView *notes;
  };
  
 -static void save_dive_info_changes(struct dive *dive, struct dive_info *info)
 +static void save_dive_info_changes(struct dive *dive, struct dive *master, struct dive_info *info)
  {
        char *old_text, *new_text;
        char *rating_string;
        int changed = 0;
  
 -      new_text = get_combo_box_entry_text(info->location, &dive->location);
 +      new_text = get_combo_box_entry_text(info->location, &dive->location, master->location);
        if (new_text) {
                add_location(new_text);
                changed = 1;
        }
  
 -      new_text = get_combo_box_entry_text(info->divemaster, &dive->divemaster);
 +      new_text = get_combo_box_entry_text(info->divemaster, &dive->divemaster, master->divemaster);
        if (new_text) {
                add_people(new_text);
                changed = 1;
        }
  
 -      new_text = get_combo_box_entry_text(info->buddy, &dive->buddy);
 +      new_text = get_combo_box_entry_text(info->buddy, &dive->buddy, master->buddy);
        if (new_text) {
                add_people(new_text);
                changed = 1;
        }
  
 +      new_text = get_combo_box_entry_text(info->suit, &dive->suit, master->suit);
 +      if (new_text) {
 +              add_suit(new_text);
 +              changed = 1;
 +      }
 +
        rating_string = strdup(star_strings[dive->rating]);
 -      new_text = get_combo_box_entry_text(info->rating, &rating_string);
 +      new_text = get_combo_box_entry_text(info->rating, &rating_string, star_strings[master->rating]);
        if (new_text) {
                dive->rating = get_rating(rating_string);
                free(rating_string);
                changed =1;
        }
  
 -      old_text = dive->notes;
 -      dive->notes = get_text(info->notes);
 -      if (text_changed(old_text,dive->notes))
 -              changed = 1;
 -      if (old_text)
 -              g_free(old_text);
 -
 +      if (info->notes) {
 +              old_text = dive->notes;
 +              dive->notes = get_text(info->notes);
 +              if (text_changed(old_text,dive->notes))
 +                      changed = 1;
 +              if (old_text)
 +                      g_free(old_text);
 +      }
        if (changed) {
                mark_divelist_changed(TRUE);
                update_dive(dive);
        }
  }
  
 -static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info *info)
 +static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info *info, gboolean multi)
  {
 -      GtkWidget *hbox, *label, *cylinder, *frame;
 -      char buffer[80];
 +      GtkWidget *hbox, *label, *frame, *equipment;
 +      char buffer[80] = "Edit multiple dives";
  
 -      divename(buffer, sizeof(buffer), dive);
 +      if (!multi)
 +              divename(buffer, sizeof(buffer), dive);
        label = gtk_label_new(buffer);
        gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0);
  
        gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0);
  
        info->rating = text_entry(hbox, "Rating", star_list, star_strings[dive->rating]);
 +      info->suit = text_entry(hbox, "Suit", suit_list, dive->suit);
  
 -      info->notes = text_view(box, "Notes", READ_WRITE);
 -      if (dive->notes && *dive->notes)
 -              gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1);
 -
 +      /* only show notes if editing a single dive */
 +      if (multi) {
 +              info->notes = NULL;
 +      } else {
 +              info->notes = text_view(box, "Notes", READ_WRITE);
 +              if (dive->notes && *dive->notes)
 +                      gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1);
 +      }
        hbox = gtk_hbox_new(FALSE, 3);
        gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0);
  
 -      frame = gtk_frame_new("Cylinder");
 -      cylinder = cylinder_list_widget();
 -      gtk_container_add(GTK_CONTAINER(frame), cylinder);
 +      /* create a secondary Equipment widget */
 +      frame = gtk_frame_new("Equipment");
 +      equipment = equipment_widget(W_IDX_SECONDARY);
 +      gtk_container_add(GTK_CONTAINER(frame), equipment);
        gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0);
  }
  
 -int edit_dive_info(struct dive *dive)
 +/* we use these to find out if we edited the cylinder or weightsystem entries */
 +static cylinder_t remember_cyl[MAX_CYLINDERS];
 +static weightsystem_t remember_ws[MAX_WEIGHTSYSTEMS];
 +
 +void save_equipment_data(struct dive *dive)
  {
 -      int success;
 +      if (dive) {
 +              memcpy(remember_cyl, dive->cylinder, sizeof(cylinder_t) * MAX_CYLINDERS);
 +              memcpy(remember_ws, dive->weightsystem, sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS);
 +      }
 +}
 +
 +void update_equipment_data(struct dive *dive, struct dive *master)
 +{
 +      if (dive == master)
 +              return;
 +      if (memcmp(remember_cyl, master->cylinder, sizeof(cylinder_t) * MAX_CYLINDERS)) {
 +              memcpy(dive->cylinder, master->cylinder, sizeof(cylinder_t) * MAX_CYLINDERS);
 +      }
 +      if (memcmp(remember_ws, master->weightsystem, sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS)) {
 +              memcpy(dive->weightsystem, master->weightsystem, sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS);
 +      }
 +}
 +
 +int edit_multi_dive_info(int nr, int *indices)
 +{
 +      int success, i;
        GtkWidget *dialog, *vbox;
        struct dive_info info;
 +      struct dive *master;
  
 -      if (!dive)
 +      if (!nr)
                return 0;
 -
        dialog = gtk_dialog_new_with_buttons("Dive Info",
                GTK_WINDOW(main_window),
                GTK_DIALOG_DESTROY_WITH_PARENT,
                NULL);
  
        vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
 -      dive_info_widget(vbox, dive, &info);
 -
 +      /* SCARY STUFF - IS THIS THE BEST WAY TO DO THIS???
 +       *
 +       * current_dive is one of our selected dives - and that is
 +       * the one that is used to pre-fill the edit widget. Its
 +       * data is used as the starting point for all selected dives
 +       * I think it would be better to somehow collect and combine
 +       * info from all the selected dives */
 +      master = current_dive;
 +      dive_info_widget(vbox, master, &info, (nr > 1));
 +      show_dive_equipment(master, W_IDX_SECONDARY);
 +      save_equipment_data(master);
        gtk_widget_show_all(dialog);
        success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT;
 -      if (success)
 -              save_dive_info_changes(dive, &info);
 -
 +      if (success) {
 +              /* Update the other non-current dives first */
 +              for (i = 0; i < nr; i++) {
 +                      int idx = indices[i];
 +                      struct dive *dive = get_dive(idx);
 +
 +                      if (!dive || dive == master)
 +                              continue;
 +                      /* copy all "info" fields */
 +                      save_dive_info_changes(dive, master, &info);
 +                      /* copy the cylinders / weightsystems */
 +                      update_equipment_data(dive, master);
 +                      /* this is extremely inefficient... it loops through all
 +                         dives to find the right one - but we KNOW the index already */
 +                      flush_divelist(dive);
 +              }
 +
 +              /* Update the master dive last! */
 +              save_dive_info_changes(master, master, &info);
 +              update_equipment_data(master, master);
 +              flush_divelist(master);
 +      }
        gtk_widget_destroy(dialog);
  
        return success;
  }
  
 +int edit_dive_info(struct dive *dive)
 +{
 +      int idx;
 +
 +      if (!dive)
 +              return 0;
 +      idx = dive->number;
 +      return edit_multi_dive_info(1, &idx);
 +}
 +
  static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...)
  {
        va_list ap;
@@@ -681,7 -570,6 +689,7 @@@ GtkWidget *extended_dive_info_widget(vo
        add_string_list_entry(THREE_STARS, star_list);
        add_string_list_entry(FOUR_STARS, star_list);
        add_string_list_entry(FIVE_STARS, star_list);
 +      suit_list = gtk_list_store_new(1, G_TYPE_STRING);
  
        gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
        location = text_value(vbox, "Location");
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
  
        rating = text_value(hbox, "Rating");
 +      suit = text_value(hbox, "Suit");
  
        notes = text_view(vbox, "Notes", READ_ONLY);
        return vbox;
diff --combined profile.c
index 7a0eac4975ac9b6e4755e23236407c142f211b24,614822809f53dd59bcb6ad2575f51b5e8207e398..90ed0b6098c1a7d3d651c8cf8b7e9bf0b2b8fcb7
+++ b/profile.c
@@@ -14,7 -14,6 +14,7 @@@
  #include "color.h"
  
  int selected_dive = 0;
 +int *selectiontracker;
  
  typedef enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity_t;
  
@@@ -1355,12 -1354,15 +1355,15 @@@ void plot(struct graphics_context *gc, 
        int nr = dive->samples;
  
        if (!nr) {
+               /* The dive has no samples, so create a few fake ones.  This assumes an
+               ascent/descent rate of 9 m/min, which is just below the limit for FAST. */
                int duration = dive->duration.seconds;
                int maxdepth = dive->maxdepth.mm;
+               int asc_desc_time = dive->maxdepth.mm*60/9000;
                sample = fake;
-               fake[1].time.seconds = duration * 0.05;
+               fake[1].time.seconds = asc_desc_time;
                fake[1].depth.mm = maxdepth;
-               fake[2].time.seconds = duration * 0.95;
+               fake[2].time.seconds = duration - asc_desc_time;
                fake[2].depth.mm = maxdepth;
                fake[3].time.seconds = duration * 1.00;
                nr = 4;