]> git.street.me.uk Git - andy/viking.git/blame - src/vikaggregatelayer.c
Import Launchpad translation updates - French Update.
[andy/viking.git] / src / vikaggregatelayer.c
CommitLineData
50a14534
EB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
03c97bc3 5 * Copyright (C) 2013-2015, Rob Norris <rw_norris@hotmail.com>
50a14534
EB
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include "viking.h"
4fbfe28f 24#include "viktrwlayer_analysis.h"
123f1312 25#include "viktrwlayer_tracklist.h"
b4926b44 26#include "viktrwlayer_waypointlist.h"
5bfafde9 27#include "icons/icons.h"
50a14534
EB
28
29#include <string.h>
affcc0f2 30#include <glib/gi18n.h>
50a14534 31
0a6cab71
AF
32static void aggregate_layer_marshall( VikAggregateLayer *val, guint8 **data, gint *len );
33static VikAggregateLayer *aggregate_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
50a14534 34static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode );
70a23263 35static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
a553bf07 36static const gchar* aggregate_layer_tooltip ( VikAggregateLayer *val );
b794c67c
RN
37static void aggregate_layer_add_menu_items ( VikAggregateLayer *val, GtkMenu *menu, gpointer vlp );
38
50a14534 39VikLayerInterface vik_aggregate_layer_interface = {
db386630 40 "Aggregate",
affcc0f2 41 N_("Aggregate"),
75078768 42 "<control><shift>A",
5bfafde9 43 &vikaggregatelayer_pixbuf,
50a14534
EB
44
45 NULL,
46 0,
47
48 NULL,
49 0,
50 NULL,
51 0,
52
5a4a28bf
QT
53 VIK_MENU_ITEM_ALL,
54
50a14534
EB
55 (VikLayerFuncCreate) vik_aggregate_layer_create,
56 (VikLayerFuncRealize) vik_aggregate_layer_realize,
57 (VikLayerFuncPostRead) NULL,
58 (VikLayerFuncFree) vik_aggregate_layer_free,
59
60 (VikLayerFuncProperties) NULL,
61 (VikLayerFuncDraw) vik_aggregate_layer_draw,
62 (VikLayerFuncChangeCoordMode) aggregate_layer_change_coord_mode,
20c7a3a0 63
03c97bc3
RN
64 (VikLayerFuncGetTimestamp) NULL,
65
20c7a3a0
QT
66 (VikLayerFuncSetMenuItemsSelection) NULL,
67 (VikLayerFuncGetMenuItemsSelection) NULL,
50a14534 68
b794c67c 69 (VikLayerFuncAddMenuItems) aggregate_layer_add_menu_items,
50a14534
EB
70 (VikLayerFuncSublayerAddMenuItems) NULL,
71
72 (VikLayerFuncSublayerRenameRequest) NULL,
73 (VikLayerFuncSublayerToggleVisible) NULL,
9da7faf2 74 (VikLayerFuncSublayerTooltip) NULL,
a553bf07 75 (VikLayerFuncLayerTooltip) aggregate_layer_tooltip,
a5dcfdb7 76 (VikLayerFuncLayerSelected) NULL,
50a14534 77
0a6cab71
AF
78 (VikLayerFuncMarshall) aggregate_layer_marshall,
79 (VikLayerFuncUnmarshall) aggregate_layer_unmarshall,
50a14534
EB
80
81 (VikLayerFuncSetParam) NULL,
82 (VikLayerFuncGetParam) NULL,
db43cfa4 83 (VikLayerFuncChangeParam) NULL,
50a14534
EB
84
85 (VikLayerFuncReadFileData) NULL,
86 (VikLayerFuncWriteFileData) NULL,
87
33534cd8 88 (VikLayerFuncDeleteItem) NULL,
d5874ef9 89 (VikLayerFuncCutItem) NULL,
50a14534
EB
90 (VikLayerFuncCopyItem) NULL,
91 (VikLayerFuncPasteItem) NULL,
92 (VikLayerFuncFreeCopiedItem) NULL,
70a23263 93 (VikLayerFuncDragDropRequest) aggregate_layer_drag_drop_request,
77ad64fa
RN
94
95 (VikLayerFuncSelectClick) NULL,
08f14055
RN
96 (VikLayerFuncSelectMove) NULL,
97 (VikLayerFuncSelectRelease) NULL,
e46f259a 98 (VikLayerFuncSelectedViewportMenu) NULL,
50a14534
EB
99};
100
101struct _VikAggregateLayer {
102 VikLayer vl;
103 GList *children;
4fbfe28f
RN
104 // One per layer
105 GtkWidget *tracks_analysis_dialog;
50a14534
EB
106};
107
108GType vik_aggregate_layer_get_type ()
109{
110 static GType val_type = 0;
111
112 if (!val_type)
113 {
114 static const GTypeInfo val_info =
115 {
116 sizeof (VikAggregateLayerClass),
117 NULL, /* base_init */
118 NULL, /* base_finalize */
119 NULL, /* class init */
120 NULL, /* class_finalize */
121 NULL, /* class_data */
122 sizeof (VikAggregateLayer),
123 0,
124 NULL /* instance init */
125 };
126 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikAggregateLayer", &val_info, 0 );
127 }
128
129 return val_type;
130}
131
132VikAggregateLayer *vik_aggregate_layer_create (VikViewport *vp)
133{
134 VikAggregateLayer *rv = vik_aggregate_layer_new ();
135 vik_layer_rename ( VIK_LAYER(rv), vik_aggregate_layer_interface.name );
136 return rv;
137}
138
0a6cab71
AF
139static void aggregate_layer_marshall( VikAggregateLayer *val, guint8 **data, gint *datalen )
140{
141 GList *child = val->children;
142 VikLayer *child_layer;
143 guint8 *ld;
144 gint ll;
145 GByteArray* b = g_byte_array_new ();
146 gint len;
147
148#define alm_append(obj, sz) \
149 len = (sz); \
150 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
151 g_byte_array_append ( b, (guint8 *)(obj), len );
152
153 vik_layer_marshall_params(VIK_LAYER(val), &ld, &ll);
154 alm_append(ld, ll);
155 g_free(ld);
156
157 while (child) {
158 child_layer = VIK_LAYER(child->data);
159 vik_layer_marshall ( child_layer, &ld, &ll );
160 if (ld) {
161 alm_append(ld, ll);
162 g_free(ld);
163 }
164 child = child->next;
165 }
166 *data = b->data;
167 *datalen = b->len;
168 g_byte_array_free(b, FALSE);
169#undef alm_append
170}
171
172static VikAggregateLayer *aggregate_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
173{
174#define alm_size (*(gint *)data)
175#define alm_next \
176 len -= sizeof(gint) + alm_size; \
177 data += sizeof(gint) + alm_size;
178
179 VikAggregateLayer *rv = vik_aggregate_layer_new();
180 VikLayer *child_layer;
181
182 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
183 alm_next;
184
185 while (len>0) {
186 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
187 if (child_layer) {
188 rv->children = g_list_append ( rv->children, child_layer );
0df66d57 189 g_signal_connect_swapped ( G_OBJECT(child_layer), "update", G_CALLBACK(vik_layer_emit_update_secondary), rv );
0a6cab71
AF
190 }
191 alm_next;
192 }
193 // g_print("aggregate_layer_unmarshall ended with len=%d\n", len);
194 return rv;
195#undef alm_size
196#undef alm_next
197}
198
50a14534
EB
199VikAggregateLayer *vik_aggregate_layer_new ()
200{
201 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( g_object_new ( VIK_AGGREGATE_LAYER_TYPE, NULL ) );
a0c65899 202 vik_layer_set_type ( VIK_LAYER(val), VIK_LAYER_AGGREGATE );
50a14534
EB
203 val->children = NULL;
204 return val;
205}
206
207void vik_aggregate_layer_insert_layer ( VikAggregateLayer *val, VikLayer *l, GtkTreeIter *replace_iter )
208{
50a14534 209 GtkTreeIter iter;
405b74ed 210 VikLayer *vl = VIK_LAYER(val);
e673b75f 211
c3a02429
RN
212 // By default layers are inserted above the selected layer
213 gboolean put_above = TRUE;
214
215 // These types are 'base' types in that you what other information on top
216 if ( l->type == VIK_LAYER_MAPS || l->type == VIK_LAYER_DEM || l->type == VIK_LAYER_GEOREF )
217 put_above = FALSE;
218
405b74ed 219 if ( vl->realized )
50a14534 220 {
03c97bc3 221 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) );
50a14534 222 if ( ! l->visible )
405b74ed
GB
223 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
224 vik_layer_realize ( l, vl->vt, &iter );
50a14534
EB
225
226 if ( val->children == NULL )
405b74ed 227 vik_treeview_expand ( vl->vt, &(vl->iter) );
50a14534 228 }
50a14534 229
e673b75f 230 if (replace_iter) {
405b74ed 231 GList *theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( vl->vt, replace_iter ) );
c3a02429
RN
232 if ( put_above )
233 val->children = g_list_insert ( val->children, l, g_list_position(val->children,theone)+1 );
234 else
235 // Thus insert 'here' (so don't add 1)
236 val->children = g_list_insert ( val->children, l, g_list_position(val->children,theone) );
e673b75f 237 } else {
cc6b165a
RN
238 // Effectively insert at 'end' of the list to match how displayed in the treeview
239 // - but since it is drawn from 'bottom first' it is actually the first in the child list
240 // This ordering is especially important if it is a map or similar type,
241 // which needs be drawn first for the layering draw method to work properly.
242 // ATM this only happens when a layer is drag/dropped to the end of an aggregate layer
243 val->children = g_list_prepend ( val->children, l );
e673b75f 244 }
0df66d57 245 g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update_secondary), val );
50a14534
EB
246}
247
c3a02429
RN
248/**
249 * vik_aggregate_layer_add_layer:
250 * @allow_reordering: should be set for GUI interactions,
251 * whereas loading from a file needs strict ordering and so should be FALSE
252 */
253void vik_aggregate_layer_add_layer ( VikAggregateLayer *val, VikLayer *l, gboolean allow_reordering )
50a14534
EB
254{
255 GtkTreeIter iter;
405b74ed 256 VikLayer *vl = VIK_LAYER(val);
50a14534 257
c3a02429
RN
258 // By default layers go to the top
259 gboolean put_above = TRUE;
260
261 if ( allow_reordering ) {
262 // These types are 'base' types in that you what other information on top
263 if ( l->type == VIK_LAYER_MAPS || l->type == VIK_LAYER_DEM || l->type == VIK_LAYER_GEOREF )
264 put_above = FALSE;
265 }
266
405b74ed 267 if ( vl->realized )
50a14534 268 {
03c97bc3 269 vik_treeview_add_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type, vik_layer_get_timestamp(l) );
50a14534 270 if ( ! l->visible )
405b74ed
GB
271 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
272 vik_layer_realize ( l, vl->vt, &iter );
50a14534
EB
273
274 if ( val->children == NULL )
405b74ed 275 vik_treeview_expand ( vl->vt, &(vl->iter) );
50a14534
EB
276 }
277
c3a02429
RN
278 if ( put_above )
279 val->children = g_list_append ( val->children, l );
280 else
281 val->children = g_list_prepend ( val->children, l );
282
0df66d57 283 g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update_secondary), val );
50a14534
EB
284}
285
286void vik_aggregate_layer_move_layer ( VikAggregateLayer *val, GtkTreeIter *child_iter, gboolean up )
287{
288 GList *theone, *first, *second;
405b74ed
GB
289 VikLayer *vl = VIK_LAYER(val);
290 vik_treeview_move_item ( vl->vt, child_iter, up );
50a14534 291
405b74ed 292 theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( vl->vt, child_iter ) );
50a14534
EB
293
294 g_assert ( theone != NULL );
295
296 /* the old switcheroo */
297 if ( up && theone->next )
298 {
299 first = theone;
300 second = theone->next;
301 }
302 else if ( !up && theone->prev )
303 {
304 first = theone->prev;
305 second = theone;
306 }
307 else
308 return;
309
310 first->next = second->next;
311 second->prev = first->prev;
312 first->prev = second;
313 second->next = first;
314
315 /* second is now first */
316
317 if ( second->prev )
318 second->prev->next = second;
319 if ( first->next )
320 first->next->prev = first;
321
322 if ( second->prev == NULL )
323 val->children = second;
324}
325
0df66d57
EB
326/* Draw the aggregate layer. If vik viewport is in half_drawn mode, this means we are only
327 * to draw the layers above and including the trigger layer.
328 * To do this we don't draw any layers if in half drawn mode, unless we find the
329 * trigger layer, in which case we pull up the saved pixmap, turn off half drawn mode and
330 * start drawing layers.
331 * Also, if we were never in half drawn mode, we save a snapshot
332 * of the pixmap before drawing the trigger layer so we can use it again
333 * later.
334 */
45c5ac8e 335void vik_aggregate_layer_draw ( VikAggregateLayer *val, VikViewport *vp )
50a14534 336{
0df66d57
EB
337 GList *iter = val->children;
338 VikLayer *vl;
45c5ac8e 339 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
0df66d57
EB
340 while ( iter ) {
341 vl = VIK_LAYER(iter->data);
342 if ( vl == trigger ) {
45c5ac8e
RN
343 if ( vik_viewport_get_half_drawn ( vp ) ) {
344 vik_viewport_set_half_drawn ( vp, FALSE );
345 vik_viewport_snapshot_load( vp );
0df66d57 346 } else {
45c5ac8e 347 vik_viewport_snapshot_save( vp );
0df66d57
EB
348 }
349 }
45c5ac8e
RN
350 if ( vl->type == VIK_LAYER_AGGREGATE || vl->type == VIK_LAYER_GPS || ! vik_viewport_get_half_drawn( vp ) )
351 vik_layer_draw ( vl, vp );
0df66d57
EB
352 iter = iter->next;
353 }
50a14534
EB
354}
355
356static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode )
357{
358 GList *iter = val->children;
359 while ( iter )
360 {
361 vik_layer_change_coord_mode ( VIK_LAYER(iter->data), mode );
362 iter = iter->next;
363 }
364}
365
88f56e64
RN
366// A slightly better way of defining the menu callback information
367// This should be easier to extend/rework compared to previously
368typedef enum {
369 MA_VAL = 0,
370 MA_VLP,
371 MA_LAST
372} menu_array_index;
373
374typedef gpointer menu_array_values[MA_LAST];
375
376static void aggregate_layer_child_visible_toggle ( menu_array_values values )
da264b19 377{
88f56e64
RN
378 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
379 VikLayersPanel *vlp = VIK_LAYERS_PANEL ( values[MA_VLP] );
da264b19
RN
380 VikLayer *vl;
381
382 // Loop around all (child) layers applying visibility setting
383 // This does not descend the tree if there are aggregates within aggregrate - just the first level of layers held
384 GList *iter = val->children;
385 while ( iter ) {
386 vl = VIK_LAYER ( iter->data );
387 vl->visible = !vl->visible;
388 // Also set checkbox on/off
389 vik_treeview_item_toggle_visible ( vik_layers_panel_get_treeview ( vlp ), &(vl->iter) );
390 iter = iter->next;
391 }
392 // Redraw as view may have changed
393 vik_layer_emit_update ( VIK_LAYER ( val ) );
394}
395
88f56e64 396static void aggregate_layer_child_visible ( menu_array_values values, gboolean on_off)
da264b19
RN
397{
398 // Convert data back to correct types
88f56e64
RN
399 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
400 VikLayersPanel *vlp = VIK_LAYERS_PANEL ( values[MA_VLP] );
da264b19
RN
401 VikLayer *vl;
402
403 // Loop around all (child) layers applying visibility setting
404 // This does not descend the tree if there are aggregates within aggregrate - just the first level of layers held
405 GList *iter = val->children;
406 while ( iter ) {
407 vl = VIK_LAYER ( iter->data );
408 vl->visible = on_off;
409 // Also set checkbox on_off
410 vik_treeview_item_set_visible ( vik_layers_panel_get_treeview ( vlp ), &(vl->iter), on_off );
411 iter = iter->next;
412 }
413 // Redraw as view may have changed
414 vik_layer_emit_update ( VIK_LAYER ( val ) );
415}
416
88f56e64 417static void aggregate_layer_child_visible_on ( menu_array_values values )
da264b19 418{
88f56e64 419 aggregate_layer_child_visible ( values, TRUE );
da264b19
RN
420}
421
88f56e64 422static void aggregate_layer_child_visible_off ( menu_array_values values )
da264b19 423{
88f56e64 424 aggregate_layer_child_visible ( values, FALSE );
da264b19
RN
425}
426
1e78a79f
RN
427/**
428 * If order is true sort ascending, otherwise a descending sort
429 */
430static gint sort_layer_compare ( gconstpointer a, gconstpointer b, gpointer order )
431{
432 VikLayer *sa = (VikLayer *)a;
433 VikLayer *sb = (VikLayer *)b;
434
435 // Default ascending order
436 gint answer = g_strcmp0 ( sa->name, sb->name );
437
438 if ( GPOINTER_TO_INT(order) ) {
439 // Invert sort order for ascending order
440 answer = -answer;
441 }
442
443 return answer;
444}
445
88f56e64 446static void aggregate_layer_sort_a2z ( menu_array_values values )
1e78a79f 447{
88f56e64 448 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
1e78a79f
RN
449 vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_ALPHABETICAL_ASCENDING );
450 val->children = g_list_sort_with_data ( val->children, sort_layer_compare, GINT_TO_POINTER(TRUE) );
451}
452
88f56e64 453static void aggregate_layer_sort_z2a ( menu_array_values values )
1e78a79f 454{
88f56e64 455 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
1e78a79f
RN
456 vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_ALPHABETICAL_DESCENDING );
457 val->children = g_list_sort_with_data ( val->children, sort_layer_compare, GINT_TO_POINTER(FALSE) );
458}
459
03c97bc3
RN
460/**
461 * If order is true sort ascending, otherwise a descending sort
462 */
463static gint sort_layer_compare_timestamp ( gconstpointer a, gconstpointer b, gpointer order )
464{
465 VikLayer *sa = (VikLayer *)a;
466 VikLayer *sb = (VikLayer *)b;
467
468 // Default ascending order
469 // NB This might be relatively slow...
470 gint answer = ( vik_layer_get_timestamp(sa) > vik_layer_get_timestamp(sb) );
471
472 if ( GPOINTER_TO_INT(order) ) {
473 // Invert sort order for ascending order
474 answer = !answer;
475 }
476
477 return answer;
478}
479
480static void aggregate_layer_sort_timestamp_ascend ( menu_array_values values )
481{
482 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
483 vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_DATE_ASCENDING );
484 val->children = g_list_sort_with_data ( val->children, sort_layer_compare_timestamp, GINT_TO_POINTER(TRUE) );
485}
486
487static void aggregate_layer_sort_timestamp_descend ( menu_array_values values )
488{
489 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
490 vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_DATE_DESCENDING );
491 val->children = g_list_sort_with_data ( val->children, sort_layer_compare_timestamp, GINT_TO_POINTER(FALSE) );
492}
493
b4926b44
RN
494/**
495 * aggregate_layer_waypoint_create_list:
496 * @vl: The layer that should create the waypoint and layers list
497 * @user_data: Not used in this function
498 *
499 * Returns: A list of #vik_trw_waypoint_list_t
500 */
501static GList* aggregate_layer_waypoint_create_list ( VikLayer *vl, gpointer user_data )
502{
503 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
504
505 // Get all TRW layers
506 GList *layers = NULL;
507 layers = vik_aggregate_layer_get_all_layers_of_type ( val, layers, VIK_LAYER_TRW, TRUE );
508
509 // For each TRW layers keep adding the waypoints to build a list of all of them
510 GList *waypoints_and_layers = NULL;
511 layers = g_list_first ( layers );
512 while ( layers ) {
513 GList *waypoints = NULL;
514 waypoints = g_list_concat ( waypoints, g_hash_table_get_values ( vik_trw_layer_get_waypoints( VIK_TRW_LAYER(layers->data) ) ) );
515
516 waypoints_and_layers = g_list_concat ( waypoints_and_layers, vik_trw_layer_build_waypoint_list_t ( VIK_TRW_LAYER(layers->data), waypoints ) );
517
518 layers = g_list_next ( layers );
519 }
520 g_list_free ( layers );
521
522 return waypoints_and_layers;
523}
524
88f56e64 525static void aggregate_layer_waypoint_list_dialog ( menu_array_values values )
b4926b44 526{
88f56e64 527 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
b4926b44
RN
528 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(val)->name );
529 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(val), NULL, aggregate_layer_waypoint_create_list, TRUE );
530 g_free ( title );
531}
532
a77c32c8
RN
533/**
534 * Search all TrackWaypoint layers in this aggregate layer for an item on the user specified date
535 */
536static void aggregate_layer_search_date ( menu_array_values values )
537{
538 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
539 VikCoord position;
540 gchar *date_str = a_dialog_get_date ( VIK_GTK_WINDOW_FROM_LAYER(val), _("Search by Date") );
541
542 if ( !date_str )
543 return;
544
545 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(val)) );
546
547 GList *gl = NULL;
548 gl = vik_aggregate_layer_get_all_layers_of_type ( val, gl, VIK_LAYER_TRW, TRUE );
549 gboolean found = FALSE;
550 // Search tracks first
551 while ( gl && !found ) {
552 // Make it auto select the item if found
553 found = vik_trw_layer_find_date ( VIK_TRW_LAYER(gl->data), date_str, &position, vvp, TRUE, TRUE );
554 gl = g_list_next ( gl );
555 }
556 g_list_free ( gl );
557 if ( !found ) {
558 // Reset and try on Waypoints
559 gl = NULL;
560 gl = vik_aggregate_layer_get_all_layers_of_type ( val, gl, VIK_LAYER_TRW, TRUE );
561 while ( gl && !found ) {
562 // Make it auto select the item if found
563 found = vik_trw_layer_find_date ( VIK_TRW_LAYER(gl->data), date_str, &position, vvp, FALSE, TRUE );
564 gl = g_list_next ( gl );
565 }
566 g_list_free ( gl );
567 }
568
569 if ( !found )
570 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(val), _("No items found with the requested date.") );
571 g_free ( date_str );
572}
573
4fbfe28f 574/**
184ec0fa 575 * aggregate_layer_track_create_list:
4fbfe28f
RN
576 * @vl: The layer that should create the track and layers list
577 * @user_data: Not used in this function
578 *
579 * Returns: A list of #vik_trw_track_list_t
580 */
184ec0fa 581static GList* aggregate_layer_track_create_list ( VikLayer *vl, gpointer user_data )
4fbfe28f
RN
582{
583 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
584
585 // Get all TRW layers
586 GList *layers = NULL;
587 layers = vik_aggregate_layer_get_all_layers_of_type ( val, layers, VIK_LAYER_TRW, TRUE );
588
184ec0fa 589 // For each TRW layers keep adding the tracks and routes to build a list of all of them
4fbfe28f
RN
590 GList *tracks_and_layers = NULL;
591 layers = g_list_first ( layers );
592 while ( layers ) {
593 GList *tracks = NULL;
594 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks( VIK_TRW_LAYER(layers->data) ) ) );
595 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes( VIK_TRW_LAYER(layers->data) ) ) );
596
184ec0fa
RN
597 tracks_and_layers = g_list_concat ( tracks_and_layers, vik_trw_layer_build_track_list_t ( VIK_TRW_LAYER(layers->data), tracks ) );
598
4fbfe28f
RN
599 layers = g_list_next ( layers );
600 }
601 g_list_free ( layers );
602
603 return tracks_and_layers;
604}
605
88f56e64 606static void aggregate_layer_track_list_dialog ( menu_array_values values )
123f1312 607{
88f56e64 608 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
123f1312
RN
609 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(val)->name );
610 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(val), NULL, aggregate_layer_track_create_list, TRUE );
611 g_free ( title );
612}
613
4fbfe28f
RN
614/**
615 * aggregate_layer_analyse_close:
616 *
617 * Stuff to do on dialog closure
618 */
619static void aggregate_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
620{
621 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
622 gtk_widget_destroy ( dialog );
623 val->tracks_analysis_dialog = NULL;
624}
625
88f56e64 626static void aggregate_layer_analyse ( menu_array_values values )
4fbfe28f 627{
88f56e64 628 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
4fbfe28f
RN
629
630 // There can only be one!
631 if ( val->tracks_analysis_dialog )
632 return;
633
634 val->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(val)),
635 VIK_LAYER(val)->name,
636 VIK_LAYER(val),
637 NULL,
184ec0fa 638 aggregate_layer_track_create_list,
4fbfe28f
RN
639 aggregate_layer_analyse_close );
640}
641
b794c67c
RN
642static void aggregate_layer_add_menu_items ( VikAggregateLayer *val, GtkMenu *menu, gpointer vlp )
643{
644 // Data to pass on in menu functions
88f56e64
RN
645 static menu_array_values values;
646 values[MA_VAL] = val;
647 values[MA_VLP] = vlp;
b794c67c
RN
648
649 GtkWidget *item = gtk_menu_item_new();
650 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
651 gtk_widget_show ( item );
652
da264b19
RN
653 GtkWidget *vis_submenu = gtk_menu_new ();
654 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
655 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
656 gtk_widget_show ( item );
657 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
658
659 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All") );
660 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
88f56e64 661 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_on), values );
da264b19
RN
662 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
663 gtk_widget_show ( item );
664
665 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All") );
666 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
88f56e64 667 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_off), values );
da264b19
RN
668 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
669 gtk_widget_show ( item );
670
671 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
672 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
88f56e64 673 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_toggle), values );
da264b19
RN
674 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
675 gtk_widget_show ( item );
676
1e78a79f
RN
677 GtkWidget *submenu_sort = gtk_menu_new ();
678 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
679 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
680 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
681 gtk_widget_show ( item );
682 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
683
684 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
685 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
88f56e64 686 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_a2z), values );
1e78a79f
RN
687 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
688 gtk_widget_show ( item );
689
690 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
691 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
88f56e64 692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_z2a), values );
1e78a79f
RN
693 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
694 gtk_widget_show ( item );
4fbfe28f 695
03c97bc3
RN
696 item = gtk_image_menu_item_new_with_mnemonic ( _("Date Ascending") );
697 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
698 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_timestamp_ascend), values );
699 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
700 gtk_widget_show ( item );
701
702 item = gtk_image_menu_item_new_with_mnemonic ( _("Date Descending") );
703 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_timestamp_descend), values );
705 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
706 gtk_widget_show ( item );
707
4fbfe28f 708 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
88f56e64 709 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_analyse), values );
4fbfe28f
RN
710 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
711 gtk_widget_show ( item );
123f1312
RN
712
713 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
88f56e64 715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_track_list_dialog), values );
123f1312
RN
716 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
717 gtk_widget_show ( item );
b4926b44
RN
718
719 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
720 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
88f56e64 721 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_waypoint_list_dialog), values );
b4926b44
RN
722 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
723 gtk_widget_show ( item );
a77c32c8 724
183a5572
RN
725 GtkWidget *search_submenu = gtk_menu_new ();
726 item = gtk_image_menu_item_new_with_mnemonic ( _("Searc_h") );
a77c32c8 727 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
183a5572
RN
728 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
729 gtk_widget_show ( item );
730 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), search_submenu );
731
732 item = gtk_menu_item_new_with_mnemonic ( _("By _Date...") );
a77c32c8 733 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_search_date), values );
183a5572 734 gtk_menu_shell_append ( GTK_MENU_SHELL(search_submenu), item );
a77c32c8
RN
735 gtk_widget_set_tooltip_text (item, _("Find the first item with a specified date"));
736 gtk_widget_show ( item );
b794c67c
RN
737}
738
50a14534
EB
739static void disconnect_layer_signal ( VikLayer *vl, VikAggregateLayer *val )
740{
a135dffc
RN
741 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, val);
742 if ( number_handlers != 1 )
743 g_critical ("%s: Unexpected number of disconnect handlers: %d", __FUNCTION__, number_handlers);
50a14534
EB
744}
745
746void vik_aggregate_layer_free ( VikAggregateLayer *val )
747{
748 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
749 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
750 g_list_free ( val->children );
4fbfe28f
RN
751 if ( val->tracks_analysis_dialog != NULL )
752 gtk_widget_destroy ( val->tracks_analysis_dialog );
50a14534
EB
753}
754
755static void delete_layer_iter ( VikLayer *vl )
756{
757 if ( vl->realized )
758 vik_treeview_item_delete ( vl->vt, &(vl->iter) );
759}
760
761void vik_aggregate_layer_clear ( VikAggregateLayer *val )
762{
763 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
764 g_list_foreach ( val->children, (GFunc)(delete_layer_iter), NULL );
765 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
766 g_list_free ( val->children );
767 val->children = NULL;
768}
769
770gboolean vik_aggregate_layer_delete ( VikAggregateLayer *val, GtkTreeIter *iter )
771{
772 VikLayer *l = VIK_LAYER( vik_treeview_item_get_pointer ( VIK_LAYER(val)->vt, iter ) );
773 gboolean was_visible = l->visible;
774
775 vik_treeview_item_delete ( VIK_LAYER(val)->vt, iter );
776 val->children = g_list_remove ( val->children, l );
a135dffc 777 disconnect_layer_signal ( l, val );
50a14534
EB
778 g_object_unref ( l );
779
780 return was_visible;
781}
782
941aa6e9 783#if 0
50a14534 784/* returns 0 == we're good, 1 == didn't find any layers, 2 == got rejected */
b5926b35 785guint vik_aggregate_layer_tool ( VikAggregateLayer *val, VikLayerTypeEnum layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
786{
787 GList *iter = val->children;
788 gboolean found_rej = FALSE;
789 if (!iter)
790 return FALSE;
791 while (iter->next)
792 iter = iter->next;
793
794 while ( iter )
795 {
796 /* if this layer "accepts" the tool call */
797 if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == layer_type )
798 {
799 if ( tool_func ( VIK_LAYER(iter->data), event, vvp ) )
800 return 0;
801 else
802 found_rej = TRUE;
803 }
804
805 /* recursive -- try the same for the child aggregate layer. */
806 else if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == VIK_LAYER_AGGREGATE )
807 {
808 gint rv = vik_aggregate_layer_tool(VIK_AGGREGATE_LAYER(iter->data), layer_type, tool_func, event, vvp);
809 if ( rv == 0 )
810 return 0;
811 else if ( rv == 2 )
812 found_rej = TRUE;
813 }
814 iter = iter->prev;
815 }
816 return found_rej ? 2 : 1; /* no one wanted to accept the tool call in this layer */
817}
941aa6e9 818#endif
50a14534 819
b5926b35 820VikLayer *vik_aggregate_layer_get_top_visible_layer_of_type ( VikAggregateLayer *val, VikLayerTypeEnum type )
50a14534
EB
821{
822 VikLayer *rv;
823 GList *ls = val->children;
824 if (!ls)
825 return NULL;
826 while (ls->next)
827 ls = ls->next;
828
829 while ( ls )
830 {
405b74ed
GB
831 VikLayer *vl = VIK_LAYER(ls->data);
832 if ( vl->visible && vl->type == type )
833 return vl;
834 else if ( vl->visible && vl->type == VIK_LAYER_AGGREGATE )
50a14534 835 {
405b74ed 836 rv = vik_aggregate_layer_get_top_visible_layer_of_type(VIK_AGGREGATE_LAYER(vl), type);
50a14534
EB
837 if ( rv )
838 return rv;
839 }
840 ls = ls->prev;
841 }
842 return NULL;
843}
844
b5926b35 845GList *vik_aggregate_layer_get_all_layers_of_type(VikAggregateLayer *val, GList *layers, VikLayerTypeEnum type, gboolean include_invisible)
7114e879
QT
846{
847 GList *l = layers;
848 GList *children = val->children;
405b74ed 849 VikLayer *vl;
7114e879
QT
850 if (!children)
851 return layers;
aa7ed888
RN
852
853 // Where appropriate *don't* include non-visible layers
7114e879 854 while (children) {
405b74ed 855 vl = VIK_LAYER(children->data);
aa7ed888
RN
856 if (vl->type == VIK_LAYER_AGGREGATE ) {
857 // Don't even consider invisible aggregrates, unless told to
858 if (vl->visible || include_invisible)
b5926b35 859 l = vik_aggregate_layer_get_all_layers_of_type(VIK_AGGREGATE_LAYER(children->data), l, type, include_invisible);
aa7ed888
RN
860 }
861 else if (vl->type == type) {
862 if (vl->visible || include_invisible)
b5926b35 863 l = g_list_prepend(l, children->data); /* now in top down order */
aa7ed888 864 }
7b1a871d
RN
865 else if (type == VIK_LAYER_TRW) {
866 /* GPS layers contain TRW layers. cf with usage in file.c */
867 if (VIK_LAYER(children->data)->type == VIK_LAYER_GPS) {
aa7ed888
RN
868 if (VIK_LAYER(children->data)->visible || include_invisible) {
869 if (!vik_gps_layer_is_empty(VIK_GPS_LAYER(children->data))) {
870 /*
871 can not use g_list_concat due to wrong copy method - crashes if used a couple times !!
872 l = g_list_concat (l, vik_gps_layer_get_children (VIK_GPS_LAYER(children->data)));
873 */
874 /* create own copy method instead :( */
875 GList *gps_trw_layers = (GList *)vik_gps_layer_get_children (VIK_GPS_LAYER(children->data));
876 int n_layers = g_list_length (gps_trw_layers);
877 int layer = 0;
878 for ( layer = 0; layer < n_layers; layer++) {
879 l = g_list_prepend(l, gps_trw_layers->data);
880 gps_trw_layers = gps_trw_layers->next;
881 }
882 g_list_free(gps_trw_layers);
7b1a871d 883 }
7b1a871d
RN
884 }
885 }
886 }
7114e879
QT
887 children = children->next;
888 }
889 return l;
890}
891
50a14534
EB
892void vik_aggregate_layer_realize ( VikAggregateLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter )
893{
894 GList *i = val->children;
895 GtkTreeIter iter;
405b74ed
GB
896 VikLayer *vl = VIK_LAYER(val);
897 VikLayer *vli;
50a14534
EB
898 while ( i )
899 {
405b74ed 900 vli = VIK_LAYER(i->data);
c3a02429 901 vik_treeview_add_layer ( vl->vt, layer_iter, &iter, vli->name, val, TRUE,
03c97bc3 902 vli, vli->type, vli->type, vik_layer_get_timestamp(vli) );
405b74ed
GB
903 if ( ! vli->visible )
904 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
905 vik_layer_realize ( vli, vl->vt, &iter );
50a14534
EB
906 i = i->next;
907 }
908}
909
910const GList *vik_aggregate_layer_get_children ( VikAggregateLayer *val )
911{
912 return val->children;
913}
914
915gboolean vik_aggregate_layer_is_empty ( VikAggregateLayer *val )
916{
917 if ( val->children )
918 return FALSE;
919 return TRUE;
920}
70a23263
AF
921
922static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
923{
924 VikTreeview *vt = VIK_LAYER(val_src)->vt;
925 VikLayer *vl = vik_treeview_item_get_pointer(vt, src_item_iter);
e673b75f 926 GtkTreeIter dest_iter;
c390aa71
AF
927 gchar *dp;
928 gboolean target_exists;
e673b75f 929
c390aa71
AF
930 dp = gtk_tree_path_to_string(dest_path);
931 target_exists = vik_treeview_get_iter_from_path_str(vt, &dest_iter, dp);
801ce684
EB
932
933 /* vik_aggregate_layer_delete unrefs, but we don't want that here.
934 * we're still using the layer. */
935 g_object_ref ( vl );
936 vik_aggregate_layer_delete(val_src, src_item_iter);
937
c390aa71 938 if (target_exists) {
e673b75f
AF
939 vik_aggregate_layer_insert_layer(val_dest, vl, &dest_iter);
940 } else {
941 vik_aggregate_layer_insert_layer(val_dest, vl, NULL); /* append */
942 }
c390aa71 943 g_free(dp);
70a23263
AF
944}
945
a553bf07
RN
946/**
947 * Generate tooltip text for the layer.
948 */
949static const gchar* aggregate_layer_tooltip ( VikAggregateLayer *val )
950{
951 static gchar tmp_buf[128];
952 tmp_buf[0] = '\0';
953
954 GList *children = val->children;
955 if ( children ) {
956 gint nn = g_list_length (children);
957 // Could have a more complicated tooltip that numbers each type of layers,
958 // but for now a simple overall count
959 g_snprintf (tmp_buf, sizeof(tmp_buf), ngettext("One layer", "%d layers", nn), nn );
960 }
961 return tmp_buf;
962}