From 03c97bc359e2823b1c968f82128e3d850576359b Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Sun, 15 Feb 2015 10:30:31 +0000 Subject: [PATCH] Add ability to sort Aggregate layers and sublayers of Tracks and Waypoints by date. Store the timestamp in the treeview on layer/sublayer creation to enable the comparison. Add comparison functions. --- help/C/viking.xml | 14 ++++++- src/uibuilder.h | 4 +- src/vikaggregatelayer.c | 56 +++++++++++++++++++++++-- src/vikcoordlayer.c | 2 + src/vikdemlayer.c | 2 + src/vikgeoreflayer.c | 2 + src/vikgpslayer.c | 4 +- src/viklayer.c | 7 ++++ src/viklayer.h | 6 +++ src/viklayerspanel.c | 2 +- src/vikmapniklayer.c | 2 + src/vikmapslayer.c | 2 + src/viktreeview.c | 93 ++++++++++++++++++++++++++++++----------- src/viktreeview.h | 6 +-- src/viktrwlayer.c | 62 +++++++++++++++++++++++---- 15 files changed, 218 insertions(+), 46 deletions(-) diff --git a/help/C/viking.xml b/help/C/viking.xml index a0b4151a..98421273 100644 --- a/help/C/viking.xml +++ b/help/C/viking.xml @@ -1020,7 +1020,7 @@ Opens a new dialog with the list. As described in the Aggregrate layer Also gives other options for handling multiple items: -Sort. Gives the ability to sort the items in the treeview alphabetically +Sort. Gives the ability to sort the items in the treeview alphabetically or by date. Visibility. Offers options to quickly control the visibility of all items within the sublayer, as per @@ -1411,6 +1411,11 @@ The record of the Google route is stored in the track's comment, so if the comme Delete Waypoints from Selection. Same as the layer Delete Waypoints from Selection List Waypoints. Opens a new dialog with the list. As described in the Aggregrate layer +Also gives other options for handling multiple items: + +Sort. Gives the ability to sort the items in the treeview alphabetically or by date. +Visibility. Offers options to quickly control the visibility of all items within the sublayer, as per +
Waypoint Properties @@ -1726,8 +1731,13 @@ Add a new layer of the selected type. The list within the treeview can be sorted. -Currently alphabetical (A to Z or Z to A) sorts are available, since only the name can be seen in the treeview. +Alphabetical and Date sorts are available. + + +Generally it is not useful to sort the Top most layer. Sort is mainly intended for Aggregate groups of TrackWaypoint layers. + +
Track List diff --git a/src/uibuilder.h b/src/uibuilder.h index 9c958ae5..59539ada 100644 --- a/src/uibuilder.h +++ b/src/uibuilder.h @@ -183,12 +183,12 @@ VikLayerParamData *a_uibuilder_run_dialog ( const gchar *dialog_name, GtkWindow /* frees data from last (if ness) */ void a_uibuilder_free_paramdatas ( VikLayerParamData *paramdatas, VikLayerParam *params, guint16 params_count ); -// Consider adding sort options such as by time -// However use within the treeview then is more complicated as one would need to store that data in the treeview... typedef enum { VL_SO_NONE = 0, VL_SO_ALPHABETICAL_ASCENDING, VL_SO_ALPHABETICAL_DESCENDING, + VL_SO_DATE_ASCENDING, + VL_SO_DATE_DESCENDING, VL_SO_LAST } vik_layer_sort_order_t; diff --git a/src/vikaggregatelayer.c b/src/vikaggregatelayer.c index 496aa999..a55888eb 100644 --- a/src/vikaggregatelayer.c +++ b/src/vikaggregatelayer.c @@ -2,7 +2,7 @@ * viking -- GPS Data and Topo Analyzer, Explorer, and Manager * * Copyright (C) 2003-2005, Evan Battaglia - * Copyright (C) 2013, Rob Norris + * Copyright (C) 2013-2015, Rob Norris * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -61,6 +61,8 @@ VikLayerInterface vik_aggregate_layer_interface = { (VikLayerFuncDraw) vik_aggregate_layer_draw, (VikLayerFuncChangeCoordMode) aggregate_layer_change_coord_mode, + (VikLayerFuncGetTimestamp) NULL, + (VikLayerFuncSetMenuItemsSelection) NULL, (VikLayerFuncGetMenuItemsSelection) NULL, @@ -216,7 +218,7 @@ void vik_aggregate_layer_insert_layer ( VikAggregateLayer *val, VikLayer *l, Gtk if ( vl->realized ) { - vik_treeview_insert_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type, replace_iter ); + vik_treeview_insert_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type, replace_iter, vik_layer_get_timestamp(l) ); if ( ! l->visible ) vik_treeview_item_set_visible ( vl->vt, &iter, FALSE ); vik_layer_realize ( l, vl->vt, &iter ); @@ -264,7 +266,7 @@ void vik_aggregate_layer_add_layer ( VikAggregateLayer *val, VikLayer *l, gboole if ( vl->realized ) { - vik_treeview_add_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type); + vik_treeview_add_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type, vik_layer_get_timestamp(l) ); if ( ! l->visible ) vik_treeview_item_set_visible ( vl->vt, &iter, FALSE ); vik_layer_realize ( l, vl->vt, &iter ); @@ -455,6 +457,40 @@ static void aggregate_layer_sort_z2a ( menu_array_values values ) val->children = g_list_sort_with_data ( val->children, sort_layer_compare, GINT_TO_POINTER(FALSE) ); } +/** + * If order is true sort ascending, otherwise a descending sort + */ +static gint sort_layer_compare_timestamp ( gconstpointer a, gconstpointer b, gpointer order ) +{ + VikLayer *sa = (VikLayer *)a; + VikLayer *sb = (VikLayer *)b; + + // Default ascending order + // NB This might be relatively slow... + gint answer = ( vik_layer_get_timestamp(sa) > vik_layer_get_timestamp(sb) ); + + if ( GPOINTER_TO_INT(order) ) { + // Invert sort order for ascending order + answer = !answer; + } + + return answer; +} + +static void aggregate_layer_sort_timestamp_ascend ( menu_array_values values ) +{ + VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] ); + vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_DATE_ASCENDING ); + val->children = g_list_sort_with_data ( val->children, sort_layer_compare_timestamp, GINT_TO_POINTER(TRUE) ); +} + +static void aggregate_layer_sort_timestamp_descend ( menu_array_values values ) +{ + VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] ); + vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_DATE_DESCENDING ); + val->children = g_list_sort_with_data ( val->children, sort_layer_compare_timestamp, GINT_TO_POINTER(FALSE) ); +} + /** * aggregate_layer_waypoint_create_list: * @vl: The layer that should create the waypoint and layers list @@ -657,6 +693,18 @@ static void aggregate_layer_add_menu_items ( VikAggregateLayer *val, GtkMenu *me gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item ); gtk_widget_show ( item ); + item = gtk_image_menu_item_new_with_mnemonic ( _("Date Ascending") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_timestamp_ascend), values ); + gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item ); + gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("Date Descending") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_timestamp_descend), values ); + gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item ); + gtk_widget_show ( item ); + item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") ); g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_analyse), values ); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); @@ -851,7 +899,7 @@ void vik_aggregate_layer_realize ( VikAggregateLayer *val, VikTreeview *vt, GtkT { vli = VIK_LAYER(i->data); vik_treeview_add_layer ( vl->vt, layer_iter, &iter, vli->name, val, TRUE, - vli, vli->type, vli->type ); + vli, vli->type, vli->type, vik_layer_get_timestamp(vli) ); if ( ! vli->visible ) vik_treeview_item_set_visible ( vl->vt, &iter, FALSE ); vik_layer_realize ( vli, vl->vt, &iter ); diff --git a/src/vikcoordlayer.c b/src/vikcoordlayer.c index 40c0cf4a..ee4bec2a 100644 --- a/src/vikcoordlayer.c +++ b/src/vikcoordlayer.c @@ -86,6 +86,8 @@ VikLayerInterface vik_coord_layer_interface = { (VikLayerFuncDraw) coord_layer_draw, (VikLayerFuncChangeCoordMode) NULL, + (VikLayerFuncGetTimestamp) NULL, + (VikLayerFuncSetMenuItemsSelection) NULL, (VikLayerFuncGetMenuItemsSelection) NULL, diff --git a/src/vikdemlayer.c b/src/vikdemlayer.c index 305caafe..5b74d470 100644 --- a/src/vikdemlayer.c +++ b/src/vikdemlayer.c @@ -208,6 +208,8 @@ VikLayerInterface vik_dem_layer_interface = { (VikLayerFuncDraw) dem_layer_draw, (VikLayerFuncChangeCoordMode) NULL, + (VikLayerFuncGetTimestamp) NULL, + (VikLayerFuncSetMenuItemsSelection) NULL, (VikLayerFuncGetMenuItemsSelection) NULL, diff --git a/src/vikgeoreflayer.c b/src/vikgeoreflayer.c index 86fc1edc..8b9bb472 100644 --- a/src/vikgeoreflayer.c +++ b/src/vikgeoreflayer.c @@ -132,6 +132,8 @@ VikLayerInterface vik_georef_layer_interface = { (VikLayerFuncDraw) georef_layer_draw, (VikLayerFuncChangeCoordMode) NULL, + (VikLayerFuncGetTimestamp) NULL, + (VikLayerFuncSetMenuItemsSelection) NULL, (VikLayerFuncGetMenuItemsSelection) NULL, diff --git a/src/vikgpslayer.c b/src/vikgpslayer.c index 8e3f681d..daf6d424 100644 --- a/src/vikgpslayer.c +++ b/src/vikgpslayer.c @@ -261,6 +261,8 @@ VikLayerInterface vik_gps_layer_interface = { (VikLayerFuncDraw) vik_gps_layer_draw, (VikLayerFuncChangeCoordMode) gps_layer_change_coord_mode, + (VikLayerFuncGetTimestamp) NULL, + (VikLayerFuncSetMenuItemsSelection) NULL, (VikLayerFuncGetMenuItemsSelection) NULL, @@ -837,7 +839,7 @@ static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIt VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]); vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter, _(trw_names[ix]), vgl, TRUE, - trw, trw->type, trw->type ); + trw, trw->type, trw->type, vik_layer_get_timestamp(trw) ); if ( ! trw->visible ) vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE ); vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter ); diff --git a/src/viklayer.c b/src/viklayer.c index 126037b1..76d494dd 100644 --- a/src/viklayer.c +++ b/src/viklayer.c @@ -214,6 +214,13 @@ const gchar *vik_layer_get_name ( VikLayer *l ) return l->name; } +time_t vik_layer_get_timestamp ( VikLayer *vl ) +{ + if ( vik_layer_interfaces[vl->type]->get_timestamp ) + return vik_layer_interfaces[vl->type]->get_timestamp ( vl ); + return 0; +} + VikLayer *vik_layer_create ( VikLayerTypeEnum type, VikViewport *vp, gboolean interactive ) { VikLayer *new_layer = NULL; diff --git a/src/viklayer.h b/src/viklayer.h index 9ee3068d..8abb37ea 100644 --- a/src/viklayer.h +++ b/src/viklayer.h @@ -181,6 +181,8 @@ typedef gboolean (*VikLayerFuncSelectMove) (VikLayer *, GdkEvent typedef gboolean (*VikLayerFuncSelectRelease) (VikLayer *, GdkEventButton *, VikViewport *, tool_ed_t*); typedef gboolean (*VikLayerFuncSelectedViewportMenu) (VikLayer *, GdkEventButton *, VikViewport *); +typedef time_t (*VikLayerFuncGetTimestamp) (VikLayer *); + typedef enum { VIK_MENU_ITEM_PROPERTY=1, VIK_MENU_ITEM_CUT=2, @@ -220,6 +222,8 @@ struct _VikLayerInterface { VikLayerFuncDraw draw; VikLayerFuncChangeCoordMode change_coord_mode; + VikLayerFuncGetTimestamp get_timestamp; + VikLayerFuncSetMenuItemsSelection set_menu_selection; VikLayerFuncGetMenuItemsSelection get_menu_selection; @@ -267,6 +271,8 @@ void vik_layer_rename ( VikLayer *l, const gchar *new_name ); void vik_layer_rename_no_copy ( VikLayer *l, gchar *new_name ); const gchar *vik_layer_get_name ( VikLayer *l ); +time_t vik_layer_get_timestamp ( VikLayer *vl ); + gboolean vik_layer_set_param (VikLayer *layer, guint16 id, VikLayerParamData data, gpointer vp, gboolean is_file_operation); void vik_layer_set_defaults ( VikLayer *vl, VikViewport *vvp ); diff --git a/src/viklayerspanel.c b/src/viklayerspanel.c index 96b0cbf7..4205eac9 100644 --- a/src/viklayerspanel.c +++ b/src/viklayerspanel.c @@ -180,7 +180,7 @@ static void vik_layers_panel_init ( VikLayersPanel *vlp ) vik_layer_rename ( VIK_LAYER(vlp->toplayer), _("Top Layer")); g_signal_connect_swapped ( G_OBJECT(vlp->toplayer), "update", G_CALLBACK(vik_layers_panel_emit_update), vlp ); - vik_treeview_add_layer ( vlp->vt, NULL, &(vlp->toplayer_iter), VIK_LAYER(vlp->toplayer)->name, NULL, TRUE, vlp->toplayer, VIK_LAYER_AGGREGATE, VIK_LAYER_AGGREGATE ); + vik_treeview_add_layer ( vlp->vt, NULL, &(vlp->toplayer_iter), VIK_LAYER(vlp->toplayer)->name, NULL, TRUE, vlp->toplayer, VIK_LAYER_AGGREGATE, VIK_LAYER_AGGREGATE, 0 ); vik_layer_realize ( VIK_LAYER(vlp->toplayer), vlp->vt, &(vlp->toplayer_iter) ); g_signal_connect_swapped ( vlp->vt, "popup_menu", G_CALLBACK(menu_popup_cb), vlp); diff --git a/src/vikmapniklayer.c b/src/vikmapniklayer.c index 449f0b5c..ca84ecd2 100644 --- a/src/vikmapniklayer.c +++ b/src/vikmapniklayer.c @@ -163,6 +163,8 @@ VikLayerInterface vik_mapnik_layer_interface = { (VikLayerFuncDraw) mapnik_layer_draw, (VikLayerFuncChangeCoordMode) NULL, + (VikLayerFuncGetTimestamp) NULL, + (VikLayerFuncSetMenuItemsSelection) NULL, (VikLayerFuncGetMenuItemsSelection) NULL, diff --git a/src/vikmapslayer.c b/src/vikmapslayer.c index 213f06fc..688a70db 100644 --- a/src/vikmapslayer.c +++ b/src/vikmapslayer.c @@ -232,6 +232,8 @@ VikLayerInterface vik_maps_layer_interface = { (VikLayerFuncDraw) maps_layer_draw, (VikLayerFuncChangeCoordMode) NULL, + (VikLayerFuncGetTimestamp) NULL, + (VikLayerFuncSetMenuItemsSelection) NULL, (VikLayerFuncGetMenuItemsSelection) NULL, diff --git a/src/viktreeview.c b/src/viktreeview.c index d10527a4..4d280787 100644 --- a/src/viktreeview.c +++ b/src/viktreeview.c @@ -2,6 +2,7 @@ * viking -- GPS Data and Topo Analyzer, Explorer, and Manager * * Copyright (C) 2003-2005, Evan Battaglia + * Copyright (C) 2010-2015, Rob Norris * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,7 +56,7 @@ enum ITEM_POINTER_COLUMN, ITEM_DATA_COLUMN, EDITABLE_COLUMN, - /* properties dialog, delete, rename, etc. */ + ITEM_TIMESTAMP_COLUMN, // Date timestamp stored in tree model to enable sorting on this value NUM_COLUMNS }; @@ -462,7 +463,19 @@ void vik_treeview_init ( VikTreeview *vt ) vt->was_a_toggle = FALSE; vt->editing = FALSE; - vt->model = GTK_TREE_MODEL(gtk_tree_store_new ( NUM_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_INT, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_BOOLEAN )); + // ATM The date is stored only on initial creation + // this should be good enough for most purposes, although it may get inaccurate especially if items are deleted + // NB implicit conversion of time_t to G_INT64 + vt->model = GTK_TREE_MODEL(gtk_tree_store_new ( NUM_COLUMNS, + G_TYPE_STRING, // Name + G_TYPE_BOOLEAN, // Visibility + GDK_TYPE_PIXBUF,// The Icon + G_TYPE_INT, // Layer Type + G_TYPE_POINTER, // pointer to TV parent + G_TYPE_POINTER, // pointer to the layer or sublayer + G_TYPE_INT, // type of the sublayer + G_TYPE_BOOLEAN, // Editable + G_TYPE_INT64 )); // Timestamp /* create tree view */ gtk_tree_selection_set_select_function(gtk_tree_view_get_selection (GTK_TREE_VIEW(vt)), vik_treeview_selection_filter, vt, NULL); @@ -641,21 +654,28 @@ void vik_treeview_item_unselect ( VikTreeview *vt, GtkTreeIter *iter ) } void vik_treeview_add_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gboolean above, - gpointer item, gint data, VikLayerTypeEnum layer_type ) + gpointer item, gint data, VikLayerTypeEnum layer_type, time_t timestamp ) { g_assert ( iter != NULL ); if ( above ) gtk_tree_store_prepend ( GTK_TREE_STORE(vt->model), iter, parent_iter ); else gtk_tree_store_append ( GTK_TREE_STORE(vt->model), iter, parent_iter ); - gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, NAME_COLUMN, name, VISIBLE_COLUMN, TRUE, - TYPE_COLUMN, VIK_TREEVIEW_TYPE_LAYER, ITEM_PARENT_COLUMN, parent, ITEM_POINTER_COLUMN, item, - ITEM_DATA_COLUMN, data, EDITABLE_COLUMN, parent == NULL ? FALSE : TRUE, - ICON_COLUMN, layer_type >= 0 ? vt->layer_type_icons[layer_type] : NULL, -1 ); + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, + NAME_COLUMN, name, + VISIBLE_COLUMN, TRUE, + TYPE_COLUMN, VIK_TREEVIEW_TYPE_LAYER, + ITEM_PARENT_COLUMN, parent, + ITEM_POINTER_COLUMN, item, + ITEM_DATA_COLUMN, data, + EDITABLE_COLUMN, parent == NULL ? FALSE : TRUE, + ICON_COLUMN, layer_type >= 0 ? vt->layer_type_icons[layer_type] : NULL, + ITEM_TIMESTAMP_COLUMN, timestamp, + -1 ); } void vik_treeview_insert_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gboolean above, - gpointer item, gint data, VikLayerTypeEnum layer_type, GtkTreeIter *sibling ) + gpointer item, gint data, VikLayerTypeEnum layer_type, GtkTreeIter *sibling, time_t timestamp ) { g_assert ( iter != NULL ); if (sibling) { @@ -670,19 +690,36 @@ void vik_treeview_insert_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkT gtk_tree_store_prepend ( GTK_TREE_STORE(vt->model), iter, parent_iter ); } - gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, NAME_COLUMN, name, VISIBLE_COLUMN, TRUE, - TYPE_COLUMN, VIK_TREEVIEW_TYPE_LAYER, ITEM_PARENT_COLUMN, parent, ITEM_POINTER_COLUMN, item, - ITEM_DATA_COLUMN, data, EDITABLE_COLUMN, TRUE, - ICON_COLUMN, layer_type >= 0 ? vt->layer_type_icons[layer_type] : NULL, -1 ); + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, + NAME_COLUMN, name, + VISIBLE_COLUMN, TRUE, + TYPE_COLUMN, VIK_TREEVIEW_TYPE_LAYER, + ITEM_PARENT_COLUMN, parent, + ITEM_POINTER_COLUMN, item, + ITEM_DATA_COLUMN, data, + EDITABLE_COLUMN, TRUE, + ICON_COLUMN, layer_type >= 0 ? vt->layer_type_icons[layer_type] : NULL, + ITEM_TIMESTAMP_COLUMN, timestamp, + -1 ); } void vik_treeview_add_sublayer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gpointer item, - gint data, GdkPixbuf *icon, gboolean editable ) + gint data, GdkPixbuf *icon, gboolean editable, time_t timestamp ) { g_assert ( iter != NULL ); gtk_tree_store_append ( GTK_TREE_STORE(vt->model), iter, parent_iter ); - gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, NAME_COLUMN, name, VISIBLE_COLUMN, TRUE, TYPE_COLUMN, VIK_TREEVIEW_TYPE_SUBLAYER, ITEM_PARENT_COLUMN, parent, ITEM_POINTER_COLUMN, item, ITEM_DATA_COLUMN, data, EDITABLE_COLUMN, editable, ICON_COLUMN, icon, -1 ); + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, + NAME_COLUMN, name, + VISIBLE_COLUMN, TRUE, + TYPE_COLUMN, VIK_TREEVIEW_TYPE_SUBLAYER, + ITEM_PARENT_COLUMN, parent, + ITEM_POINTER_COLUMN, item, + ITEM_DATA_COLUMN, data, + EDITABLE_COLUMN, editable, + ICON_COLUMN, icon, + ITEM_TIMESTAMP_COLUMN, time, + -1 ); } // Inspired by the internals of GtkTreeView sorting itself @@ -690,24 +727,33 @@ typedef struct _SortTuple { gint offset; gchar *name; + time_t timestamp; } SortTuple; /** - * If order is true sort ascending, otherwise a descending sort + * */ static gint sort_tuple_compare ( gconstpointer a, gconstpointer b, gpointer order ) { SortTuple *sa = (SortTuple *)a; SortTuple *sb = (SortTuple *)b; - // Default ascending order - gint answer = g_strcmp0 ( sa->name, sb->name ); - - if ( !GPOINTER_TO_INT(order) ) { + gint answer = 0; + if ( GPOINTER_TO_INT(order) < VL_SO_DATE_ASCENDING ) { + // Alphabetical comparison + // Default ascending order + answer = g_strcmp0 ( sa->name, sb->name ); // Invert sort order for descending order - answer = -answer; + if ( GPOINTER_TO_INT(order) == VL_SO_ALPHABETICAL_DESCENDING ) + answer = -answer; + } + else { + // Date comparison + answer = ( sa->timestamp > sb->timestamp ); + // Invert sort order for descending order + if ( GPOINTER_TO_INT(order) == VL_SO_DATE_DESCENDING ) + answer = !answer; } - return answer; } @@ -752,17 +798,16 @@ void vik_treeview_sort_children ( VikTreeview *vt, GtkTreeIter *parent, vik_laye do { sort_array[ii].offset = ii; gtk_tree_model_get ( model, &child, NAME_COLUMN, &(sort_array[ii].name), -1 ); + gtk_tree_model_get ( model, &child, ITEM_TIMESTAMP_COLUMN, &(sort_array[ii].timestamp), -1 ); ii++; } while ( gtk_tree_model_iter_next (model, &child) ); - gboolean sort_order = (order == VL_SO_ALPHABETICAL_ASCENDING ); - // Sort list... g_qsort_with_data (sort_array, length, sizeof (SortTuple), sort_tuple_compare, - GINT_TO_POINTER(sort_order)); + GINT_TO_POINTER(order)); // As the sorted list contains the reordered position offsets, extract this and then apply to the treeview gint *positions = g_malloc ( sizeof(gdouble) * length ); diff --git a/src/viktreeview.h b/src/viktreeview.h index a2e583ee..5db8bda9 100644 --- a/src/viktreeview.h +++ b/src/viktreeview.h @@ -90,11 +90,11 @@ void vik_treeview_expand_toplevel ( VikTreeview *vt ); void vik_treeview_expand ( VikTreeview *vt, GtkTreeIter *iter ); void vik_treeview_add_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gboolean above, - gpointer item, gint data, VikLayerTypeEnum layer_type ); + gpointer item, gint data, VikLayerTypeEnum layer_type, time_t timestamp ); void vik_treeview_insert_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gboolean above, - gpointer item, gint data, VikLayerTypeEnum layer_type, GtkTreeIter *sibling ); + gpointer item, gint data, VikLayerTypeEnum layer_type, GtkTreeIter *sibling, time_t timestamp ); void vik_treeview_add_sublayer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gpointer item, - gint data, GdkPixbuf *icon, gboolean editable ); + gint data, GdkPixbuf *icon, gboolean editable, time_t timestamp ); gboolean vik_treeview_get_iter_with_name ( VikTreeview *vt, GtkTreeIter *iter, GtkTreeIter *parent_iter, const gchar *name ); diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index 3ce47ad6..9f75cd5c 100644 --- a/src/viktrwlayer.c +++ b/src/viktrwlayer.c @@ -5,7 +5,7 @@ * Copyright (C) 2005-2008, Alex Foobarian * Copyright (C) 2007, Quy Tonthat * Copyright (C) 2009, Hein Ragas - * Copyright (c) 2012, Rob Norris + * Copyright (c) 2012-2015, Rob Norris * Copyright (c) 2012-2013, Guilhem Bonnefille * * This program is free software; you can redistribute it and/or modify @@ -533,6 +533,8 @@ static gchar* params_sort_order[] = { N_("None"), N_("Name Ascending"), N_("Name Descending"), + N_("Date Ascending"), + N_("Date Descending"), NULL }; @@ -740,6 +742,7 @@ VikLayerInterface vik_trw_layer_interface = { (VikLayerFuncProperties) NULL, (VikLayerFuncDraw) trw_layer_draw, (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode, + (VikLayerFuncGetTimestamp) trw_layer_get_timestamp, (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection, (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection, @@ -2669,7 +2672,12 @@ static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pas gdk_pixbuf_fill ( pixbuf, pixel ); } - vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE ); + time_t timestamp = 0; + VikTrackpoint *tpt = vik_track_get_tp_first(track); + if ( tpt && tpt->has_timestamp ) + timestamp = tpt->timestamp; + + vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, timestamp ); if ( pixbuf ) g_object_unref (pixbuf); @@ -2688,7 +2696,11 @@ static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer { GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter)); - vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE ); + time_t timestamp = 0; + if ( wp->has_timestamp ) + timestamp = wp->timestamp; + + vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, timestamp ); *new_iter = *((GtkTreeIter *) pass_along[1]); g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter ); @@ -2699,17 +2711,17 @@ static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) { - vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, FALSE ); + vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, FALSE, 0 ); } static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) { - vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, FALSE ); + vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, FALSE, 0 ); } static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) { - vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Rutes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, FALSE ); + vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, FALSE, 0 ); } static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) @@ -4358,8 +4370,12 @@ void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); + time_t timestamp = 0; + if ( wp->has_timestamp ) + timestamp = wp->timestamp; + // Visibility column always needed for waypoints - vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE ); + vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, timestamp ); // Actual setting of visibility dependent on the waypoint vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible ); @@ -4392,8 +4408,14 @@ void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t ) } GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); + + time_t timestamp = 0; + VikTrackpoint *tpt = vik_track_get_tp_first(t); + if ( tpt && tpt->has_timestamp ) + timestamp = tpt->timestamp; + // Visibility column always needed for tracks - vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE ); + vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, timestamp ); // Actual setting of visibility dependent on the track vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible ); @@ -4427,7 +4449,7 @@ void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t ) GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); // Visibility column always needed for routes - vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE ); + vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, 0 ); // Routes don't have times // Actual setting of visibility dependent on the route vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible ); @@ -7026,8 +7048,18 @@ static void trw_layer_sort_order_z2a ( menu_array_sublayer values ) { VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_DESCENDING ); +} +static void trw_layer_sort_order_timestamp_ascend ( menu_array_sublayer values ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); + trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_ASCENDING ); +} +static void trw_layer_sort_order_timestamp_descend ( menu_array_sublayer values ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]); + trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_DESCENDING ); } /** @@ -8129,6 +8161,18 @@ static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *men g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along ); gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item ); gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("Date Ascending") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_ascend), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item ); + gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("Date Descending") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_descend), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item ); + gtk_widget_show ( item ); } GtkWidget *upload_submenu = gtk_menu_new (); -- 2.39.5