]> git.street.me.uk Git - andy/viking.git/blame - src/vikaggregatelayer.c
Use Waypoint list dialog in TrackWaypoint layer menus.
[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"
5bfafde9 26#include "icons/icons.h"
50a14534
EB
27
28#include <string.h>
affcc0f2 29#include <glib/gi18n.h>
50a14534
EB
30
31#define DISCONNECT_UPDATE_SIGNAL(vl, val) g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, val)
32
0a6cab71
AF
33static void aggregate_layer_marshall( VikAggregateLayer *val, guint8 **data, gint *len );
34static VikAggregateLayer *aggregate_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
50a14534 35static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode );
70a23263 36static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
50a14534 37
b794c67c
RN
38static void aggregate_layer_add_menu_items ( VikAggregateLayer *val, GtkMenu *menu, gpointer vlp );
39
50a14534 40VikLayerInterface vik_aggregate_layer_interface = {
db386630 41 "Aggregate",
affcc0f2 42 N_("Aggregate"),
75078768 43 "<control><shift>A",
5bfafde9 44 &vikaggregatelayer_pixbuf,
50a14534
EB
45
46 NULL,
47 0,
48
49 NULL,
50 0,
51 NULL,
52 0,
53
5a4a28bf
QT
54 VIK_MENU_ITEM_ALL,
55
50a14534
EB
56 (VikLayerFuncCreate) vik_aggregate_layer_create,
57 (VikLayerFuncRealize) vik_aggregate_layer_realize,
58 (VikLayerFuncPostRead) NULL,
59 (VikLayerFuncFree) vik_aggregate_layer_free,
60
61 (VikLayerFuncProperties) NULL,
62 (VikLayerFuncDraw) vik_aggregate_layer_draw,
63 (VikLayerFuncChangeCoordMode) aggregate_layer_change_coord_mode,
20c7a3a0
QT
64
65 (VikLayerFuncSetMenuItemsSelection) NULL,
66 (VikLayerFuncGetMenuItemsSelection) NULL,
50a14534 67
b794c67c 68 (VikLayerFuncAddMenuItems) aggregate_layer_add_menu_items,
50a14534
EB
69 (VikLayerFuncSublayerAddMenuItems) NULL,
70
71 (VikLayerFuncSublayerRenameRequest) NULL,
72 (VikLayerFuncSublayerToggleVisible) NULL,
9da7faf2 73 (VikLayerFuncSublayerTooltip) NULL,
1e8b7f57 74 (VikLayerFuncLayerTooltip) NULL,
a5dcfdb7 75 (VikLayerFuncLayerSelected) NULL,
50a14534 76
0a6cab71
AF
77 (VikLayerFuncMarshall) aggregate_layer_marshall,
78 (VikLayerFuncUnmarshall) aggregate_layer_unmarshall,
50a14534
EB
79
80 (VikLayerFuncSetParam) NULL,
81 (VikLayerFuncGetParam) NULL,
82
83 (VikLayerFuncReadFileData) NULL,
84 (VikLayerFuncWriteFileData) NULL,
85
33534cd8 86 (VikLayerFuncDeleteItem) NULL,
d5874ef9 87 (VikLayerFuncCutItem) NULL,
50a14534
EB
88 (VikLayerFuncCopyItem) NULL,
89 (VikLayerFuncPasteItem) NULL,
90 (VikLayerFuncFreeCopiedItem) NULL,
70a23263 91 (VikLayerFuncDragDropRequest) aggregate_layer_drag_drop_request,
77ad64fa
RN
92
93 (VikLayerFuncSelectClick) NULL,
08f14055
RN
94 (VikLayerFuncSelectMove) NULL,
95 (VikLayerFuncSelectRelease) NULL,
e46f259a 96 (VikLayerFuncSelectedViewportMenu) NULL,
50a14534
EB
97};
98
99struct _VikAggregateLayer {
100 VikLayer vl;
101 GList *children;
4fbfe28f
RN
102 // One per layer
103 GtkWidget *tracks_analysis_dialog;
50a14534
EB
104};
105
106GType vik_aggregate_layer_get_type ()
107{
108 static GType val_type = 0;
109
110 if (!val_type)
111 {
112 static const GTypeInfo val_info =
113 {
114 sizeof (VikAggregateLayerClass),
115 NULL, /* base_init */
116 NULL, /* base_finalize */
117 NULL, /* class init */
118 NULL, /* class_finalize */
119 NULL, /* class_data */
120 sizeof (VikAggregateLayer),
121 0,
122 NULL /* instance init */
123 };
124 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikAggregateLayer", &val_info, 0 );
125 }
126
127 return val_type;
128}
129
130VikAggregateLayer *vik_aggregate_layer_create (VikViewport *vp)
131{
132 VikAggregateLayer *rv = vik_aggregate_layer_new ();
133 vik_layer_rename ( VIK_LAYER(rv), vik_aggregate_layer_interface.name );
134 return rv;
135}
136
0a6cab71
AF
137static void aggregate_layer_marshall( VikAggregateLayer *val, guint8 **data, gint *datalen )
138{
139 GList *child = val->children;
140 VikLayer *child_layer;
141 guint8 *ld;
142 gint ll;
143 GByteArray* b = g_byte_array_new ();
144 gint len;
145
146#define alm_append(obj, sz) \
147 len = (sz); \
148 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
149 g_byte_array_append ( b, (guint8 *)(obj), len );
150
151 vik_layer_marshall_params(VIK_LAYER(val), &ld, &ll);
152 alm_append(ld, ll);
153 g_free(ld);
154
155 while (child) {
156 child_layer = VIK_LAYER(child->data);
157 vik_layer_marshall ( child_layer, &ld, &ll );
158 if (ld) {
159 alm_append(ld, ll);
160 g_free(ld);
161 }
162 child = child->next;
163 }
164 *data = b->data;
165 *datalen = b->len;
166 g_byte_array_free(b, FALSE);
167#undef alm_append
168}
169
170static VikAggregateLayer *aggregate_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
171{
172#define alm_size (*(gint *)data)
173#define alm_next \
174 len -= sizeof(gint) + alm_size; \
175 data += sizeof(gint) + alm_size;
176
177 VikAggregateLayer *rv = vik_aggregate_layer_new();
178 VikLayer *child_layer;
179
180 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
181 alm_next;
182
183 while (len>0) {
184 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
185 if (child_layer) {
186 rv->children = g_list_append ( rv->children, child_layer );
0df66d57 187 g_signal_connect_swapped ( G_OBJECT(child_layer), "update", G_CALLBACK(vik_layer_emit_update_secondary), rv );
0a6cab71
AF
188 }
189 alm_next;
190 }
191 // g_print("aggregate_layer_unmarshall ended with len=%d\n", len);
192 return rv;
193#undef alm_size
194#undef alm_next
195}
196
50a14534
EB
197VikAggregateLayer *vik_aggregate_layer_new ()
198{
199 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( g_object_new ( VIK_AGGREGATE_LAYER_TYPE, NULL ) );
a0c65899 200 vik_layer_set_type ( VIK_LAYER(val), VIK_LAYER_AGGREGATE );
50a14534
EB
201 val->children = NULL;
202 return val;
203}
204
205void vik_aggregate_layer_insert_layer ( VikAggregateLayer *val, VikLayer *l, GtkTreeIter *replace_iter )
206{
50a14534 207 GtkTreeIter iter;
405b74ed 208 VikLayer *vl = VIK_LAYER(val);
e673b75f 209
c3a02429
RN
210 // By default layers are inserted above the selected layer
211 gboolean put_above = TRUE;
212
213 // These types are 'base' types in that you what other information on top
214 if ( l->type == VIK_LAYER_MAPS || l->type == VIK_LAYER_DEM || l->type == VIK_LAYER_GEOREF )
215 put_above = FALSE;
216
405b74ed 217 if ( vl->realized )
50a14534 218 {
c3a02429 219 vik_treeview_insert_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type, replace_iter );
50a14534 220 if ( ! l->visible )
405b74ed
GB
221 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
222 vik_layer_realize ( l, vl->vt, &iter );
50a14534
EB
223
224 if ( val->children == NULL )
405b74ed 225 vik_treeview_expand ( vl->vt, &(vl->iter) );
50a14534 226 }
50a14534 227
e673b75f 228 if (replace_iter) {
405b74ed 229 GList *theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( vl->vt, replace_iter ) );
c3a02429
RN
230 if ( put_above )
231 val->children = g_list_insert ( val->children, l, g_list_position(val->children,theone)+1 );
232 else
233 // Thus insert 'here' (so don't add 1)
234 val->children = g_list_insert ( val->children, l, g_list_position(val->children,theone) );
e673b75f 235 } else {
cc6b165a
RN
236 // Effectively insert at 'end' of the list to match how displayed in the treeview
237 // - but since it is drawn from 'bottom first' it is actually the first in the child list
238 // This ordering is especially important if it is a map or similar type,
239 // which needs be drawn first for the layering draw method to work properly.
240 // ATM this only happens when a layer is drag/dropped to the end of an aggregate layer
241 val->children = g_list_prepend ( val->children, l );
e673b75f 242 }
0df66d57 243 g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update_secondary), val );
50a14534
EB
244}
245
c3a02429
RN
246/**
247 * vik_aggregate_layer_add_layer:
248 * @allow_reordering: should be set for GUI interactions,
249 * whereas loading from a file needs strict ordering and so should be FALSE
250 */
251void vik_aggregate_layer_add_layer ( VikAggregateLayer *val, VikLayer *l, gboolean allow_reordering )
50a14534
EB
252{
253 GtkTreeIter iter;
405b74ed 254 VikLayer *vl = VIK_LAYER(val);
50a14534 255
c3a02429
RN
256 // By default layers go to the top
257 gboolean put_above = TRUE;
258
259 if ( allow_reordering ) {
260 // These types are 'base' types in that you what other information on top
261 if ( l->type == VIK_LAYER_MAPS || l->type == VIK_LAYER_DEM || l->type == VIK_LAYER_GEOREF )
262 put_above = FALSE;
263 }
264
405b74ed 265 if ( vl->realized )
50a14534 266 {
c3a02429 267 vik_treeview_add_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type);
50a14534 268 if ( ! l->visible )
405b74ed
GB
269 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
270 vik_layer_realize ( l, vl->vt, &iter );
50a14534
EB
271
272 if ( val->children == NULL )
405b74ed 273 vik_treeview_expand ( vl->vt, &(vl->iter) );
50a14534
EB
274 }
275
c3a02429
RN
276 if ( put_above )
277 val->children = g_list_append ( val->children, l );
278 else
279 val->children = g_list_prepend ( val->children, l );
280
0df66d57 281 g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update_secondary), val );
50a14534
EB
282}
283
284void vik_aggregate_layer_move_layer ( VikAggregateLayer *val, GtkTreeIter *child_iter, gboolean up )
285{
286 GList *theone, *first, *second;
405b74ed
GB
287 VikLayer *vl = VIK_LAYER(val);
288 vik_treeview_move_item ( vl->vt, child_iter, up );
50a14534 289
405b74ed 290 theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( vl->vt, child_iter ) );
50a14534
EB
291
292 g_assert ( theone != NULL );
293
294 /* the old switcheroo */
295 if ( up && theone->next )
296 {
297 first = theone;
298 second = theone->next;
299 }
300 else if ( !up && theone->prev )
301 {
302 first = theone->prev;
303 second = theone;
304 }
305 else
306 return;
307
308 first->next = second->next;
309 second->prev = first->prev;
310 first->prev = second;
311 second->next = first;
312
313 /* second is now first */
314
315 if ( second->prev )
316 second->prev->next = second;
317 if ( first->next )
318 first->next->prev = first;
319
320 if ( second->prev == NULL )
321 val->children = second;
322}
323
0df66d57
EB
324/* Draw the aggregate layer. If vik viewport is in half_drawn mode, this means we are only
325 * to draw the layers above and including the trigger layer.
326 * To do this we don't draw any layers if in half drawn mode, unless we find the
327 * trigger layer, in which case we pull up the saved pixmap, turn off half drawn mode and
328 * start drawing layers.
329 * Also, if we were never in half drawn mode, we save a snapshot
330 * of the pixmap before drawing the trigger layer so we can use it again
331 * later.
332 */
45c5ac8e 333void vik_aggregate_layer_draw ( VikAggregateLayer *val, VikViewport *vp )
50a14534 334{
0df66d57
EB
335 GList *iter = val->children;
336 VikLayer *vl;
45c5ac8e 337 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
0df66d57
EB
338 while ( iter ) {
339 vl = VIK_LAYER(iter->data);
340 if ( vl == trigger ) {
45c5ac8e
RN
341 if ( vik_viewport_get_half_drawn ( vp ) ) {
342 vik_viewport_set_half_drawn ( vp, FALSE );
343 vik_viewport_snapshot_load( vp );
0df66d57 344 } else {
45c5ac8e 345 vik_viewport_snapshot_save( vp );
0df66d57
EB
346 }
347 }
45c5ac8e
RN
348 if ( vl->type == VIK_LAYER_AGGREGATE || vl->type == VIK_LAYER_GPS || ! vik_viewport_get_half_drawn( vp ) )
349 vik_layer_draw ( vl, vp );
0df66d57
EB
350 iter = iter->next;
351 }
50a14534
EB
352}
353
354static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode )
355{
356 GList *iter = val->children;
357 while ( iter )
358 {
359 vik_layer_change_coord_mode ( VIK_LAYER(iter->data), mode );
360 iter = iter->next;
361 }
362}
363
b866c21a 364static void aggregate_layer_child_visible_toggle ( gpointer data[2] )
da264b19
RN
365{
366 // Convert data back to correct types
367 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( data[0] );
368 VikLayersPanel *vlp = VIK_LAYERS_PANEL ( data[1] );
369 VikLayer *vl;
370
371 // Loop around all (child) layers applying visibility setting
372 // This does not descend the tree if there are aggregates within aggregrate - just the first level of layers held
373 GList *iter = val->children;
374 while ( iter ) {
375 vl = VIK_LAYER ( iter->data );
376 vl->visible = !vl->visible;
377 // Also set checkbox on/off
378 vik_treeview_item_toggle_visible ( vik_layers_panel_get_treeview ( vlp ), &(vl->iter) );
379 iter = iter->next;
380 }
381 // Redraw as view may have changed
382 vik_layer_emit_update ( VIK_LAYER ( val ) );
383}
384
b866c21a 385static void aggregate_layer_child_visible ( gpointer data[2], gboolean on_off)
da264b19
RN
386{
387 // Convert data back to correct types
388 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( data[0] );
389 VikLayersPanel *vlp = VIK_LAYERS_PANEL ( data[1] );
390 VikLayer *vl;
391
392 // Loop around all (child) layers applying visibility setting
393 // This does not descend the tree if there are aggregates within aggregrate - just the first level of layers held
394 GList *iter = val->children;
395 while ( iter ) {
396 vl = VIK_LAYER ( iter->data );
397 vl->visible = on_off;
398 // Also set checkbox on_off
399 vik_treeview_item_set_visible ( vik_layers_panel_get_treeview ( vlp ), &(vl->iter), on_off );
400 iter = iter->next;
401 }
402 // Redraw as view may have changed
403 vik_layer_emit_update ( VIK_LAYER ( val ) );
404}
405
b866c21a 406static void aggregate_layer_child_visible_on ( gpointer data[2] )
da264b19 407{
b866c21a 408 aggregate_layer_child_visible ( data, TRUE );
da264b19
RN
409}
410
b866c21a 411static void aggregate_layer_child_visible_off ( gpointer data[2] )
da264b19 412{
b866c21a 413 aggregate_layer_child_visible ( data, FALSE );
da264b19
RN
414}
415
1e78a79f
RN
416/**
417 * If order is true sort ascending, otherwise a descending sort
418 */
419static gint sort_layer_compare ( gconstpointer a, gconstpointer b, gpointer order )
420{
421 VikLayer *sa = (VikLayer *)a;
422 VikLayer *sb = (VikLayer *)b;
423
424 // Default ascending order
425 gint answer = g_strcmp0 ( sa->name, sb->name );
426
427 if ( GPOINTER_TO_INT(order) ) {
428 // Invert sort order for ascending order
429 answer = -answer;
430 }
431
432 return answer;
433}
434
435static void aggregate_layer_sort_a2z ( gpointer lav[2] )
436{
437 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(lav[0]);
438 vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_ALPHABETICAL_ASCENDING );
439 val->children = g_list_sort_with_data ( val->children, sort_layer_compare, GINT_TO_POINTER(TRUE) );
440}
441
442static void aggregate_layer_sort_z2a ( gpointer lav[2] )
443{
444 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(lav[0]);
445 vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_ALPHABETICAL_DESCENDING );
446 val->children = g_list_sort_with_data ( val->children, sort_layer_compare, GINT_TO_POINTER(FALSE) );
447}
448
4fbfe28f 449/**
184ec0fa 450 * aggregate_layer_track_create_list:
4fbfe28f
RN
451 * @vl: The layer that should create the track and layers list
452 * @user_data: Not used in this function
453 *
454 * Returns: A list of #vik_trw_track_list_t
455 */
184ec0fa 456static GList* aggregate_layer_track_create_list ( VikLayer *vl, gpointer user_data )
4fbfe28f
RN
457{
458 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
459
460 // Get all TRW layers
461 GList *layers = NULL;
462 layers = vik_aggregate_layer_get_all_layers_of_type ( val, layers, VIK_LAYER_TRW, TRUE );
463
184ec0fa 464 // For each TRW layers keep adding the tracks and routes to build a list of all of them
4fbfe28f
RN
465 GList *tracks_and_layers = NULL;
466 layers = g_list_first ( layers );
467 while ( layers ) {
468 GList *tracks = NULL;
469 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks( VIK_TRW_LAYER(layers->data) ) ) );
470 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes( VIK_TRW_LAYER(layers->data) ) ) );
471
184ec0fa
RN
472 tracks_and_layers = g_list_concat ( tracks_and_layers, vik_trw_layer_build_track_list_t ( VIK_TRW_LAYER(layers->data), tracks ) );
473
4fbfe28f
RN
474 layers = g_list_next ( layers );
475 }
476 g_list_free ( layers );
477
478 return tracks_and_layers;
479}
480
123f1312
RN
481static void aggregate_layer_track_list_dialog ( gpointer data[2] )
482{
483 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(data[0]);
484 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(val)->name );
485 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(val), NULL, aggregate_layer_track_create_list, TRUE );
486 g_free ( title );
487}
488
4fbfe28f
RN
489/**
490 * aggregate_layer_analyse_close:
491 *
492 * Stuff to do on dialog closure
493 */
494static void aggregate_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
495{
496 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
497 gtk_widget_destroy ( dialog );
498 val->tracks_analysis_dialog = NULL;
499}
500
501static void aggregate_layer_analyse ( gpointer data[2] )
502{
503 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(data[0]);
504
505 // There can only be one!
506 if ( val->tracks_analysis_dialog )
507 return;
508
509 val->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(val)),
510 VIK_LAYER(val)->name,
511 VIK_LAYER(val),
512 NULL,
184ec0fa 513 aggregate_layer_track_create_list,
4fbfe28f
RN
514 aggregate_layer_analyse_close );
515}
516
b794c67c
RN
517static void aggregate_layer_add_menu_items ( VikAggregateLayer *val, GtkMenu *menu, gpointer vlp )
518{
519 // Data to pass on in menu functions
520 static gpointer data[2];
521 data[0] = val;
522 data[1] = vlp;
523
524 GtkWidget *item = gtk_menu_item_new();
525 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
526 gtk_widget_show ( item );
527
da264b19
RN
528 GtkWidget *vis_submenu = gtk_menu_new ();
529 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
530 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
531 gtk_widget_show ( item );
532 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
533
534 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All") );
535 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
b866c21a 536 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_on), data );
da264b19
RN
537 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
538 gtk_widget_show ( item );
539
540 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All") );
541 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
b866c21a 542 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_off), data );
da264b19
RN
543 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
544 gtk_widget_show ( item );
545
546 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
547 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
b866c21a 548 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_toggle), data );
da264b19
RN
549 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
550 gtk_widget_show ( item );
551
1e78a79f
RN
552 GtkWidget *submenu_sort = gtk_menu_new ();
553 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
554 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
555 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
556 gtk_widget_show ( item );
557 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
558
559 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
560 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
561 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_a2z), data );
562 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
563 gtk_widget_show ( item );
564
565 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
566 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
567 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_z2a), data );
568 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
569 gtk_widget_show ( item );
4fbfe28f
RN
570
571 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
572 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_analyse), data );
573 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
574 gtk_widget_show ( item );
123f1312
RN
575
576 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
577 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
578 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_track_list_dialog), data );
579 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
580 gtk_widget_show ( item );
b794c67c
RN
581}
582
50a14534
EB
583static void disconnect_layer_signal ( VikLayer *vl, VikAggregateLayer *val )
584{
585 g_assert(DISCONNECT_UPDATE_SIGNAL(vl,val)==1);
586}
587
588void vik_aggregate_layer_free ( VikAggregateLayer *val )
589{
590 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
591 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
592 g_list_free ( val->children );
4fbfe28f
RN
593 if ( val->tracks_analysis_dialog != NULL )
594 gtk_widget_destroy ( val->tracks_analysis_dialog );
50a14534
EB
595}
596
597static void delete_layer_iter ( VikLayer *vl )
598{
599 if ( vl->realized )
600 vik_treeview_item_delete ( vl->vt, &(vl->iter) );
601}
602
603void vik_aggregate_layer_clear ( VikAggregateLayer *val )
604{
605 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
606 g_list_foreach ( val->children, (GFunc)(delete_layer_iter), NULL );
607 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
608 g_list_free ( val->children );
609 val->children = NULL;
610}
611
612gboolean vik_aggregate_layer_delete ( VikAggregateLayer *val, GtkTreeIter *iter )
613{
614 VikLayer *l = VIK_LAYER( vik_treeview_item_get_pointer ( VIK_LAYER(val)->vt, iter ) );
615 gboolean was_visible = l->visible;
616
617 vik_treeview_item_delete ( VIK_LAYER(val)->vt, iter );
618 val->children = g_list_remove ( val->children, l );
619 g_assert(DISCONNECT_UPDATE_SIGNAL(l,val)==1);
620 g_object_unref ( l );
621
622 return was_visible;
623}
624
941aa6e9 625#if 0
50a14534 626/* returns 0 == we're good, 1 == didn't find any layers, 2 == got rejected */
b5926b35 627guint vik_aggregate_layer_tool ( VikAggregateLayer *val, VikLayerTypeEnum layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
628{
629 GList *iter = val->children;
630 gboolean found_rej = FALSE;
631 if (!iter)
632 return FALSE;
633 while (iter->next)
634 iter = iter->next;
635
636 while ( iter )
637 {
638 /* if this layer "accepts" the tool call */
639 if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == layer_type )
640 {
641 if ( tool_func ( VIK_LAYER(iter->data), event, vvp ) )
642 return 0;
643 else
644 found_rej = TRUE;
645 }
646
647 /* recursive -- try the same for the child aggregate layer. */
648 else if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == VIK_LAYER_AGGREGATE )
649 {
650 gint rv = vik_aggregate_layer_tool(VIK_AGGREGATE_LAYER(iter->data), layer_type, tool_func, event, vvp);
651 if ( rv == 0 )
652 return 0;
653 else if ( rv == 2 )
654 found_rej = TRUE;
655 }
656 iter = iter->prev;
657 }
658 return found_rej ? 2 : 1; /* no one wanted to accept the tool call in this layer */
659}
941aa6e9 660#endif
50a14534 661
b5926b35 662VikLayer *vik_aggregate_layer_get_top_visible_layer_of_type ( VikAggregateLayer *val, VikLayerTypeEnum type )
50a14534
EB
663{
664 VikLayer *rv;
665 GList *ls = val->children;
666 if (!ls)
667 return NULL;
668 while (ls->next)
669 ls = ls->next;
670
671 while ( ls )
672 {
405b74ed
GB
673 VikLayer *vl = VIK_LAYER(ls->data);
674 if ( vl->visible && vl->type == type )
675 return vl;
676 else if ( vl->visible && vl->type == VIK_LAYER_AGGREGATE )
50a14534 677 {
405b74ed 678 rv = vik_aggregate_layer_get_top_visible_layer_of_type(VIK_AGGREGATE_LAYER(vl), type);
50a14534
EB
679 if ( rv )
680 return rv;
681 }
682 ls = ls->prev;
683 }
684 return NULL;
685}
686
b5926b35 687GList *vik_aggregate_layer_get_all_layers_of_type(VikAggregateLayer *val, GList *layers, VikLayerTypeEnum type, gboolean include_invisible)
7114e879
QT
688{
689 GList *l = layers;
690 GList *children = val->children;
405b74ed 691 VikLayer *vl;
7114e879
QT
692 if (!children)
693 return layers;
aa7ed888
RN
694
695 // Where appropriate *don't* include non-visible layers
7114e879 696 while (children) {
405b74ed 697 vl = VIK_LAYER(children->data);
aa7ed888
RN
698 if (vl->type == VIK_LAYER_AGGREGATE ) {
699 // Don't even consider invisible aggregrates, unless told to
700 if (vl->visible || include_invisible)
b5926b35 701 l = vik_aggregate_layer_get_all_layers_of_type(VIK_AGGREGATE_LAYER(children->data), l, type, include_invisible);
aa7ed888
RN
702 }
703 else if (vl->type == type) {
704 if (vl->visible || include_invisible)
b5926b35 705 l = g_list_prepend(l, children->data); /* now in top down order */
aa7ed888 706 }
7b1a871d
RN
707 else if (type == VIK_LAYER_TRW) {
708 /* GPS layers contain TRW layers. cf with usage in file.c */
709 if (VIK_LAYER(children->data)->type == VIK_LAYER_GPS) {
aa7ed888
RN
710 if (VIK_LAYER(children->data)->visible || include_invisible) {
711 if (!vik_gps_layer_is_empty(VIK_GPS_LAYER(children->data))) {
712 /*
713 can not use g_list_concat due to wrong copy method - crashes if used a couple times !!
714 l = g_list_concat (l, vik_gps_layer_get_children (VIK_GPS_LAYER(children->data)));
715 */
716 /* create own copy method instead :( */
717 GList *gps_trw_layers = (GList *)vik_gps_layer_get_children (VIK_GPS_LAYER(children->data));
718 int n_layers = g_list_length (gps_trw_layers);
719 int layer = 0;
720 for ( layer = 0; layer < n_layers; layer++) {
721 l = g_list_prepend(l, gps_trw_layers->data);
722 gps_trw_layers = gps_trw_layers->next;
723 }
724 g_list_free(gps_trw_layers);
7b1a871d 725 }
7b1a871d
RN
726 }
727 }
728 }
7114e879
QT
729 children = children->next;
730 }
731 return l;
732}
733
50a14534
EB
734void vik_aggregate_layer_realize ( VikAggregateLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter )
735{
736 GList *i = val->children;
737 GtkTreeIter iter;
405b74ed
GB
738 VikLayer *vl = VIK_LAYER(val);
739 VikLayer *vli;
50a14534
EB
740 while ( i )
741 {
405b74ed 742 vli = VIK_LAYER(i->data);
c3a02429 743 vik_treeview_add_layer ( vl->vt, layer_iter, &iter, vli->name, val, TRUE,
405b74ed
GB
744 vli, vli->type, vli->type );
745 if ( ! vli->visible )
746 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
747 vik_layer_realize ( vli, vl->vt, &iter );
50a14534
EB
748 i = i->next;
749 }
750}
751
752const GList *vik_aggregate_layer_get_children ( VikAggregateLayer *val )
753{
754 return val->children;
755}
756
757gboolean vik_aggregate_layer_is_empty ( VikAggregateLayer *val )
758{
759 if ( val->children )
760 return FALSE;
761 return TRUE;
762}
70a23263
AF
763
764static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
765{
766 VikTreeview *vt = VIK_LAYER(val_src)->vt;
767 VikLayer *vl = vik_treeview_item_get_pointer(vt, src_item_iter);
e673b75f 768 GtkTreeIter dest_iter;
c390aa71
AF
769 gchar *dp;
770 gboolean target_exists;
e673b75f 771
c390aa71
AF
772 dp = gtk_tree_path_to_string(dest_path);
773 target_exists = vik_treeview_get_iter_from_path_str(vt, &dest_iter, dp);
801ce684
EB
774
775 /* vik_aggregate_layer_delete unrefs, but we don't want that here.
776 * we're still using the layer. */
777 g_object_ref ( vl );
778 vik_aggregate_layer_delete(val_src, src_item_iter);
779
c390aa71 780 if (target_exists) {
e673b75f
AF
781 vik_aggregate_layer_insert_layer(val_dest, vl, &dest_iter);
782 } else {
783 vik_aggregate_layer_insert_layer(val_dest, vl, NULL); /* append */
784 }
c390aa71 785 g_free(dp);
70a23263
AF
786}
787