]> git.street.me.uk Git - andy/viking.git/blame - src/vikaggregatelayer.c
Improve update signal handler usage in GPS Layer.
[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>
123f1312 5 * Copyright (C) 2013, 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
EB
31
32#define DISCONNECT_UPDATE_SIGNAL(vl, val) g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, val)
33
0a6cab71
AF
34static void aggregate_layer_marshall( VikAggregateLayer *val, guint8 **data, gint *len );
35static VikAggregateLayer *aggregate_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
50a14534 36static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode );
70a23263 37static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
50a14534 38
b794c67c
RN
39static void aggregate_layer_add_menu_items ( VikAggregateLayer *val, GtkMenu *menu, gpointer vlp );
40
50a14534 41VikLayerInterface vik_aggregate_layer_interface = {
db386630 42 "Aggregate",
affcc0f2 43 N_("Aggregate"),
75078768 44 "<control><shift>A",
5bfafde9 45 &vikaggregatelayer_pixbuf,
50a14534
EB
46
47 NULL,
48 0,
49
50 NULL,
51 0,
52 NULL,
53 0,
54
5a4a28bf
QT
55 VIK_MENU_ITEM_ALL,
56
50a14534
EB
57 (VikLayerFuncCreate) vik_aggregate_layer_create,
58 (VikLayerFuncRealize) vik_aggregate_layer_realize,
59 (VikLayerFuncPostRead) NULL,
60 (VikLayerFuncFree) vik_aggregate_layer_free,
61
62 (VikLayerFuncProperties) NULL,
63 (VikLayerFuncDraw) vik_aggregate_layer_draw,
64 (VikLayerFuncChangeCoordMode) aggregate_layer_change_coord_mode,
20c7a3a0
QT
65
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,
1e8b7f57 75 (VikLayerFuncLayerTooltip) NULL,
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 {
c3a02429 221 vik_treeview_insert_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type, replace_iter );
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 {
c3a02429 269 vik_treeview_add_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type);
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
b4926b44
RN
460/**
461 * aggregate_layer_waypoint_create_list:
462 * @vl: The layer that should create the waypoint and layers list
463 * @user_data: Not used in this function
464 *
465 * Returns: A list of #vik_trw_waypoint_list_t
466 */
467static GList* aggregate_layer_waypoint_create_list ( VikLayer *vl, gpointer user_data )
468{
469 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
470
471 // Get all TRW layers
472 GList *layers = NULL;
473 layers = vik_aggregate_layer_get_all_layers_of_type ( val, layers, VIK_LAYER_TRW, TRUE );
474
475 // For each TRW layers keep adding the waypoints to build a list of all of them
476 GList *waypoints_and_layers = NULL;
477 layers = g_list_first ( layers );
478 while ( layers ) {
479 GList *waypoints = NULL;
480 waypoints = g_list_concat ( waypoints, g_hash_table_get_values ( vik_trw_layer_get_waypoints( VIK_TRW_LAYER(layers->data) ) ) );
481
482 waypoints_and_layers = g_list_concat ( waypoints_and_layers, vik_trw_layer_build_waypoint_list_t ( VIK_TRW_LAYER(layers->data), waypoints ) );
483
484 layers = g_list_next ( layers );
485 }
486 g_list_free ( layers );
487
488 return waypoints_and_layers;
489}
490
88f56e64 491static void aggregate_layer_waypoint_list_dialog ( menu_array_values values )
b4926b44 492{
88f56e64 493 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
b4926b44
RN
494 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(val)->name );
495 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(val), NULL, aggregate_layer_waypoint_create_list, TRUE );
496 g_free ( title );
497}
498
4fbfe28f 499/**
184ec0fa 500 * aggregate_layer_track_create_list:
4fbfe28f
RN
501 * @vl: The layer that should create the track and layers list
502 * @user_data: Not used in this function
503 *
504 * Returns: A list of #vik_trw_track_list_t
505 */
184ec0fa 506static GList* aggregate_layer_track_create_list ( VikLayer *vl, gpointer user_data )
4fbfe28f
RN
507{
508 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
509
510 // Get all TRW layers
511 GList *layers = NULL;
512 layers = vik_aggregate_layer_get_all_layers_of_type ( val, layers, VIK_LAYER_TRW, TRUE );
513
184ec0fa 514 // For each TRW layers keep adding the tracks and routes to build a list of all of them
4fbfe28f
RN
515 GList *tracks_and_layers = NULL;
516 layers = g_list_first ( layers );
517 while ( layers ) {
518 GList *tracks = NULL;
519 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks( VIK_TRW_LAYER(layers->data) ) ) );
520 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes( VIK_TRW_LAYER(layers->data) ) ) );
521
184ec0fa
RN
522 tracks_and_layers = g_list_concat ( tracks_and_layers, vik_trw_layer_build_track_list_t ( VIK_TRW_LAYER(layers->data), tracks ) );
523
4fbfe28f
RN
524 layers = g_list_next ( layers );
525 }
526 g_list_free ( layers );
527
528 return tracks_and_layers;
529}
530
88f56e64 531static void aggregate_layer_track_list_dialog ( menu_array_values values )
123f1312 532{
88f56e64 533 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
123f1312
RN
534 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(val)->name );
535 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(val), NULL, aggregate_layer_track_create_list, TRUE );
536 g_free ( title );
537}
538
4fbfe28f
RN
539/**
540 * aggregate_layer_analyse_close:
541 *
542 * Stuff to do on dialog closure
543 */
544static void aggregate_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
545{
546 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
547 gtk_widget_destroy ( dialog );
548 val->tracks_analysis_dialog = NULL;
549}
550
88f56e64 551static void aggregate_layer_analyse ( menu_array_values values )
4fbfe28f 552{
88f56e64 553 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
4fbfe28f
RN
554
555 // There can only be one!
556 if ( val->tracks_analysis_dialog )
557 return;
558
559 val->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(val)),
560 VIK_LAYER(val)->name,
561 VIK_LAYER(val),
562 NULL,
184ec0fa 563 aggregate_layer_track_create_list,
4fbfe28f
RN
564 aggregate_layer_analyse_close );
565}
566
b794c67c
RN
567static void aggregate_layer_add_menu_items ( VikAggregateLayer *val, GtkMenu *menu, gpointer vlp )
568{
569 // Data to pass on in menu functions
88f56e64
RN
570 static menu_array_values values;
571 values[MA_VAL] = val;
572 values[MA_VLP] = vlp;
b794c67c
RN
573
574 GtkWidget *item = gtk_menu_item_new();
575 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
576 gtk_widget_show ( item );
577
da264b19
RN
578 GtkWidget *vis_submenu = gtk_menu_new ();
579 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
580 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
581 gtk_widget_show ( item );
582 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
583
584 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All") );
585 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
88f56e64 586 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_on), values );
da264b19
RN
587 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
588 gtk_widget_show ( item );
589
590 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All") );
591 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
88f56e64 592 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_off), values );
da264b19
RN
593 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
594 gtk_widget_show ( item );
595
596 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
597 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
88f56e64 598 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_toggle), values );
da264b19
RN
599 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
600 gtk_widget_show ( item );
601
1e78a79f
RN
602 GtkWidget *submenu_sort = gtk_menu_new ();
603 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
604 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
605 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
606 gtk_widget_show ( item );
607 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
608
609 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
610 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
88f56e64 611 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_a2z), values );
1e78a79f
RN
612 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
613 gtk_widget_show ( item );
614
615 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
616 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
88f56e64 617 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_z2a), values );
1e78a79f
RN
618 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
619 gtk_widget_show ( item );
4fbfe28f
RN
620
621 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
88f56e64 622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_analyse), values );
4fbfe28f
RN
623 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
624 gtk_widget_show ( item );
123f1312
RN
625
626 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
627 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
88f56e64 628 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_track_list_dialog), values );
123f1312
RN
629 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
630 gtk_widget_show ( item );
b4926b44
RN
631
632 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
633 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
88f56e64 634 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_waypoint_list_dialog), values );
b4926b44
RN
635 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
636 gtk_widget_show ( item );
b794c67c
RN
637}
638
50a14534
EB
639static void disconnect_layer_signal ( VikLayer *vl, VikAggregateLayer *val )
640{
641 g_assert(DISCONNECT_UPDATE_SIGNAL(vl,val)==1);
642}
643
644void vik_aggregate_layer_free ( VikAggregateLayer *val )
645{
646 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
647 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
648 g_list_free ( val->children );
4fbfe28f
RN
649 if ( val->tracks_analysis_dialog != NULL )
650 gtk_widget_destroy ( val->tracks_analysis_dialog );
50a14534
EB
651}
652
653static void delete_layer_iter ( VikLayer *vl )
654{
655 if ( vl->realized )
656 vik_treeview_item_delete ( vl->vt, &(vl->iter) );
657}
658
659void vik_aggregate_layer_clear ( VikAggregateLayer *val )
660{
661 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
662 g_list_foreach ( val->children, (GFunc)(delete_layer_iter), NULL );
663 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
664 g_list_free ( val->children );
665 val->children = NULL;
666}
667
668gboolean vik_aggregate_layer_delete ( VikAggregateLayer *val, GtkTreeIter *iter )
669{
670 VikLayer *l = VIK_LAYER( vik_treeview_item_get_pointer ( VIK_LAYER(val)->vt, iter ) );
671 gboolean was_visible = l->visible;
672
673 vik_treeview_item_delete ( VIK_LAYER(val)->vt, iter );
674 val->children = g_list_remove ( val->children, l );
675 g_assert(DISCONNECT_UPDATE_SIGNAL(l,val)==1);
676 g_object_unref ( l );
677
678 return was_visible;
679}
680
941aa6e9 681#if 0
50a14534 682/* returns 0 == we're good, 1 == didn't find any layers, 2 == got rejected */
b5926b35 683guint vik_aggregate_layer_tool ( VikAggregateLayer *val, VikLayerTypeEnum layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
684{
685 GList *iter = val->children;
686 gboolean found_rej = FALSE;
687 if (!iter)
688 return FALSE;
689 while (iter->next)
690 iter = iter->next;
691
692 while ( iter )
693 {
694 /* if this layer "accepts" the tool call */
695 if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == layer_type )
696 {
697 if ( tool_func ( VIK_LAYER(iter->data), event, vvp ) )
698 return 0;
699 else
700 found_rej = TRUE;
701 }
702
703 /* recursive -- try the same for the child aggregate layer. */
704 else if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == VIK_LAYER_AGGREGATE )
705 {
706 gint rv = vik_aggregate_layer_tool(VIK_AGGREGATE_LAYER(iter->data), layer_type, tool_func, event, vvp);
707 if ( rv == 0 )
708 return 0;
709 else if ( rv == 2 )
710 found_rej = TRUE;
711 }
712 iter = iter->prev;
713 }
714 return found_rej ? 2 : 1; /* no one wanted to accept the tool call in this layer */
715}
941aa6e9 716#endif
50a14534 717
b5926b35 718VikLayer *vik_aggregate_layer_get_top_visible_layer_of_type ( VikAggregateLayer *val, VikLayerTypeEnum type )
50a14534
EB
719{
720 VikLayer *rv;
721 GList *ls = val->children;
722 if (!ls)
723 return NULL;
724 while (ls->next)
725 ls = ls->next;
726
727 while ( ls )
728 {
405b74ed
GB
729 VikLayer *vl = VIK_LAYER(ls->data);
730 if ( vl->visible && vl->type == type )
731 return vl;
732 else if ( vl->visible && vl->type == VIK_LAYER_AGGREGATE )
50a14534 733 {
405b74ed 734 rv = vik_aggregate_layer_get_top_visible_layer_of_type(VIK_AGGREGATE_LAYER(vl), type);
50a14534
EB
735 if ( rv )
736 return rv;
737 }
738 ls = ls->prev;
739 }
740 return NULL;
741}
742
b5926b35 743GList *vik_aggregate_layer_get_all_layers_of_type(VikAggregateLayer *val, GList *layers, VikLayerTypeEnum type, gboolean include_invisible)
7114e879
QT
744{
745 GList *l = layers;
746 GList *children = val->children;
405b74ed 747 VikLayer *vl;
7114e879
QT
748 if (!children)
749 return layers;
aa7ed888
RN
750
751 // Where appropriate *don't* include non-visible layers
7114e879 752 while (children) {
405b74ed 753 vl = VIK_LAYER(children->data);
aa7ed888
RN
754 if (vl->type == VIK_LAYER_AGGREGATE ) {
755 // Don't even consider invisible aggregrates, unless told to
756 if (vl->visible || include_invisible)
b5926b35 757 l = vik_aggregate_layer_get_all_layers_of_type(VIK_AGGREGATE_LAYER(children->data), l, type, include_invisible);
aa7ed888
RN
758 }
759 else if (vl->type == type) {
760 if (vl->visible || include_invisible)
b5926b35 761 l = g_list_prepend(l, children->data); /* now in top down order */
aa7ed888 762 }
7b1a871d
RN
763 else if (type == VIK_LAYER_TRW) {
764 /* GPS layers contain TRW layers. cf with usage in file.c */
765 if (VIK_LAYER(children->data)->type == VIK_LAYER_GPS) {
aa7ed888
RN
766 if (VIK_LAYER(children->data)->visible || include_invisible) {
767 if (!vik_gps_layer_is_empty(VIK_GPS_LAYER(children->data))) {
768 /*
769 can not use g_list_concat due to wrong copy method - crashes if used a couple times !!
770 l = g_list_concat (l, vik_gps_layer_get_children (VIK_GPS_LAYER(children->data)));
771 */
772 /* create own copy method instead :( */
773 GList *gps_trw_layers = (GList *)vik_gps_layer_get_children (VIK_GPS_LAYER(children->data));
774 int n_layers = g_list_length (gps_trw_layers);
775 int layer = 0;
776 for ( layer = 0; layer < n_layers; layer++) {
777 l = g_list_prepend(l, gps_trw_layers->data);
778 gps_trw_layers = gps_trw_layers->next;
779 }
780 g_list_free(gps_trw_layers);
7b1a871d 781 }
7b1a871d
RN
782 }
783 }
784 }
7114e879
QT
785 children = children->next;
786 }
787 return l;
788}
789
50a14534
EB
790void vik_aggregate_layer_realize ( VikAggregateLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter )
791{
792 GList *i = val->children;
793 GtkTreeIter iter;
405b74ed
GB
794 VikLayer *vl = VIK_LAYER(val);
795 VikLayer *vli;
50a14534
EB
796 while ( i )
797 {
405b74ed 798 vli = VIK_LAYER(i->data);
c3a02429 799 vik_treeview_add_layer ( vl->vt, layer_iter, &iter, vli->name, val, TRUE,
405b74ed
GB
800 vli, vli->type, vli->type );
801 if ( ! vli->visible )
802 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
803 vik_layer_realize ( vli, vl->vt, &iter );
50a14534
EB
804 i = i->next;
805 }
806}
807
808const GList *vik_aggregate_layer_get_children ( VikAggregateLayer *val )
809{
810 return val->children;
811}
812
813gboolean vik_aggregate_layer_is_empty ( VikAggregateLayer *val )
814{
815 if ( val->children )
816 return FALSE;
817 return TRUE;
818}
70a23263
AF
819
820static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
821{
822 VikTreeview *vt = VIK_LAYER(val_src)->vt;
823 VikLayer *vl = vik_treeview_item_get_pointer(vt, src_item_iter);
e673b75f 824 GtkTreeIter dest_iter;
c390aa71
AF
825 gchar *dp;
826 gboolean target_exists;
e673b75f 827
c390aa71
AF
828 dp = gtk_tree_path_to_string(dest_path);
829 target_exists = vik_treeview_get_iter_from_path_str(vt, &dest_iter, dp);
801ce684
EB
830
831 /* vik_aggregate_layer_delete unrefs, but we don't want that here.
832 * we're still using the layer. */
833 g_object_ref ( vl );
834 vik_aggregate_layer_delete(val_src, src_item_iter);
835
c390aa71 836 if (target_exists) {
e673b75f
AF
837 vik_aggregate_layer_insert_layer(val_dest, vl, &dest_iter);
838 } else {
839 vik_aggregate_layer_insert_layer(val_dest, vl, NULL); /* append */
840 }
c390aa71 841 g_free(dp);
70a23263
AF
842}
843