]> git.street.me.uk Git - andy/viking.git/blame - src/vikaggregatelayer.c
Shift GTK+ compatibility definitions into vik_compat.h
[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 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
QT
63
64 (VikLayerFuncSetMenuItemsSelection) NULL,
65 (VikLayerFuncGetMenuItemsSelection) NULL,
50a14534 66
b794c67c 67 (VikLayerFuncAddMenuItems) aggregate_layer_add_menu_items,
50a14534
EB
68 (VikLayerFuncSublayerAddMenuItems) NULL,
69
70 (VikLayerFuncSublayerRenameRequest) NULL,
71 (VikLayerFuncSublayerToggleVisible) NULL,
9da7faf2 72 (VikLayerFuncSublayerTooltip) NULL,
a553bf07 73 (VikLayerFuncLayerTooltip) aggregate_layer_tooltip,
a5dcfdb7 74 (VikLayerFuncLayerSelected) NULL,
50a14534 75
0a6cab71
AF
76 (VikLayerFuncMarshall) aggregate_layer_marshall,
77 (VikLayerFuncUnmarshall) aggregate_layer_unmarshall,
50a14534
EB
78
79 (VikLayerFuncSetParam) NULL,
80 (VikLayerFuncGetParam) NULL,
db43cfa4 81 (VikLayerFuncChangeParam) NULL,
50a14534
EB
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
88f56e64
RN
364// A slightly better way of defining the menu callback information
365// This should be easier to extend/rework compared to previously
366typedef enum {
367 MA_VAL = 0,
368 MA_VLP,
369 MA_LAST
370} menu_array_index;
371
372typedef gpointer menu_array_values[MA_LAST];
373
374static void aggregate_layer_child_visible_toggle ( menu_array_values values )
da264b19 375{
88f56e64
RN
376 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
377 VikLayersPanel *vlp = VIK_LAYERS_PANEL ( values[MA_VLP] );
da264b19
RN
378 VikLayer *vl;
379
380 // Loop around all (child) layers applying visibility setting
381 // This does not descend the tree if there are aggregates within aggregrate - just the first level of layers held
382 GList *iter = val->children;
383 while ( iter ) {
384 vl = VIK_LAYER ( iter->data );
385 vl->visible = !vl->visible;
386 // Also set checkbox on/off
387 vik_treeview_item_toggle_visible ( vik_layers_panel_get_treeview ( vlp ), &(vl->iter) );
388 iter = iter->next;
389 }
390 // Redraw as view may have changed
391 vik_layer_emit_update ( VIK_LAYER ( val ) );
392}
393
88f56e64 394static void aggregate_layer_child_visible ( menu_array_values values, gboolean on_off)
da264b19
RN
395{
396 // Convert data back to correct types
88f56e64
RN
397 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
398 VikLayersPanel *vlp = VIK_LAYERS_PANEL ( values[MA_VLP] );
da264b19
RN
399 VikLayer *vl;
400
401 // Loop around all (child) layers applying visibility setting
402 // This does not descend the tree if there are aggregates within aggregrate - just the first level of layers held
403 GList *iter = val->children;
404 while ( iter ) {
405 vl = VIK_LAYER ( iter->data );
406 vl->visible = on_off;
407 // Also set checkbox on_off
408 vik_treeview_item_set_visible ( vik_layers_panel_get_treeview ( vlp ), &(vl->iter), on_off );
409 iter = iter->next;
410 }
411 // Redraw as view may have changed
412 vik_layer_emit_update ( VIK_LAYER ( val ) );
413}
414
88f56e64 415static void aggregate_layer_child_visible_on ( menu_array_values values )
da264b19 416{
88f56e64 417 aggregate_layer_child_visible ( values, TRUE );
da264b19
RN
418}
419
88f56e64 420static void aggregate_layer_child_visible_off ( menu_array_values values )
da264b19 421{
88f56e64 422 aggregate_layer_child_visible ( values, FALSE );
da264b19
RN
423}
424
1e78a79f
RN
425/**
426 * If order is true sort ascending, otherwise a descending sort
427 */
428static gint sort_layer_compare ( gconstpointer a, gconstpointer b, gpointer order )
429{
430 VikLayer *sa = (VikLayer *)a;
431 VikLayer *sb = (VikLayer *)b;
432
433 // Default ascending order
434 gint answer = g_strcmp0 ( sa->name, sb->name );
435
436 if ( GPOINTER_TO_INT(order) ) {
437 // Invert sort order for ascending order
438 answer = -answer;
439 }
440
441 return answer;
442}
443
88f56e64 444static void aggregate_layer_sort_a2z ( menu_array_values values )
1e78a79f 445{
88f56e64 446 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
1e78a79f
RN
447 vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_ALPHABETICAL_ASCENDING );
448 val->children = g_list_sort_with_data ( val->children, sort_layer_compare, GINT_TO_POINTER(TRUE) );
449}
450
88f56e64 451static void aggregate_layer_sort_z2a ( menu_array_values values )
1e78a79f 452{
88f56e64 453 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
1e78a79f
RN
454 vik_treeview_sort_children ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), VL_SO_ALPHABETICAL_DESCENDING );
455 val->children = g_list_sort_with_data ( val->children, sort_layer_compare, GINT_TO_POINTER(FALSE) );
456}
457
b4926b44
RN
458/**
459 * aggregate_layer_waypoint_create_list:
460 * @vl: The layer that should create the waypoint and layers list
461 * @user_data: Not used in this function
462 *
463 * Returns: A list of #vik_trw_waypoint_list_t
464 */
465static GList* aggregate_layer_waypoint_create_list ( VikLayer *vl, gpointer user_data )
466{
467 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
468
469 // Get all TRW layers
470 GList *layers = NULL;
471 layers = vik_aggregate_layer_get_all_layers_of_type ( val, layers, VIK_LAYER_TRW, TRUE );
472
473 // For each TRW layers keep adding the waypoints to build a list of all of them
474 GList *waypoints_and_layers = NULL;
475 layers = g_list_first ( layers );
476 while ( layers ) {
477 GList *waypoints = NULL;
478 waypoints = g_list_concat ( waypoints, g_hash_table_get_values ( vik_trw_layer_get_waypoints( VIK_TRW_LAYER(layers->data) ) ) );
479
480 waypoints_and_layers = g_list_concat ( waypoints_and_layers, vik_trw_layer_build_waypoint_list_t ( VIK_TRW_LAYER(layers->data), waypoints ) );
481
482 layers = g_list_next ( layers );
483 }
484 g_list_free ( layers );
485
486 return waypoints_and_layers;
487}
488
88f56e64 489static void aggregate_layer_waypoint_list_dialog ( menu_array_values values )
b4926b44 490{
88f56e64 491 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
b4926b44
RN
492 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(val)->name );
493 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(val), NULL, aggregate_layer_waypoint_create_list, TRUE );
494 g_free ( title );
495}
496
a77c32c8
RN
497/**
498 * Search all TrackWaypoint layers in this aggregate layer for an item on the user specified date
499 */
500static void aggregate_layer_search_date ( menu_array_values values )
501{
502 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
503 VikCoord position;
504 gchar *date_str = a_dialog_get_date ( VIK_GTK_WINDOW_FROM_LAYER(val), _("Search by Date") );
505
506 if ( !date_str )
507 return;
508
509 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(val)) );
510
511 GList *gl = NULL;
512 gl = vik_aggregate_layer_get_all_layers_of_type ( val, gl, VIK_LAYER_TRW, TRUE );
513 gboolean found = FALSE;
514 // Search tracks first
515 while ( gl && !found ) {
516 // Make it auto select the item if found
517 found = vik_trw_layer_find_date ( VIK_TRW_LAYER(gl->data), date_str, &position, vvp, TRUE, TRUE );
518 gl = g_list_next ( gl );
519 }
520 g_list_free ( gl );
521 if ( !found ) {
522 // Reset and try on Waypoints
523 gl = NULL;
524 gl = vik_aggregate_layer_get_all_layers_of_type ( val, gl, VIK_LAYER_TRW, TRUE );
525 while ( gl && !found ) {
526 // Make it auto select the item if found
527 found = vik_trw_layer_find_date ( VIK_TRW_LAYER(gl->data), date_str, &position, vvp, FALSE, TRUE );
528 gl = g_list_next ( gl );
529 }
530 g_list_free ( gl );
531 }
532
533 if ( !found )
534 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(val), _("No items found with the requested date.") );
535 g_free ( date_str );
536}
537
4fbfe28f 538/**
184ec0fa 539 * aggregate_layer_track_create_list:
4fbfe28f
RN
540 * @vl: The layer that should create the track and layers list
541 * @user_data: Not used in this function
542 *
543 * Returns: A list of #vik_trw_track_list_t
544 */
184ec0fa 545static GList* aggregate_layer_track_create_list ( VikLayer *vl, gpointer user_data )
4fbfe28f
RN
546{
547 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
548
549 // Get all TRW layers
550 GList *layers = NULL;
551 layers = vik_aggregate_layer_get_all_layers_of_type ( val, layers, VIK_LAYER_TRW, TRUE );
552
184ec0fa 553 // For each TRW layers keep adding the tracks and routes to build a list of all of them
4fbfe28f
RN
554 GList *tracks_and_layers = NULL;
555 layers = g_list_first ( layers );
556 while ( layers ) {
557 GList *tracks = NULL;
558 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks( VIK_TRW_LAYER(layers->data) ) ) );
559 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes( VIK_TRW_LAYER(layers->data) ) ) );
560
184ec0fa
RN
561 tracks_and_layers = g_list_concat ( tracks_and_layers, vik_trw_layer_build_track_list_t ( VIK_TRW_LAYER(layers->data), tracks ) );
562
4fbfe28f
RN
563 layers = g_list_next ( layers );
564 }
565 g_list_free ( layers );
566
567 return tracks_and_layers;
568}
569
88f56e64 570static void aggregate_layer_track_list_dialog ( menu_array_values values )
123f1312 571{
88f56e64 572 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
123f1312
RN
573 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(val)->name );
574 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(val), NULL, aggregate_layer_track_create_list, TRUE );
575 g_free ( title );
576}
577
4fbfe28f
RN
578/**
579 * aggregate_layer_analyse_close:
580 *
581 * Stuff to do on dialog closure
582 */
583static void aggregate_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
584{
585 VikAggregateLayer *val = VIK_AGGREGATE_LAYER(vl);
586 gtk_widget_destroy ( dialog );
587 val->tracks_analysis_dialog = NULL;
588}
589
88f56e64 590static void aggregate_layer_analyse ( menu_array_values values )
4fbfe28f 591{
88f56e64 592 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] );
4fbfe28f
RN
593
594 // There can only be one!
595 if ( val->tracks_analysis_dialog )
596 return;
597
598 val->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(val)),
599 VIK_LAYER(val)->name,
600 VIK_LAYER(val),
601 NULL,
184ec0fa 602 aggregate_layer_track_create_list,
4fbfe28f
RN
603 aggregate_layer_analyse_close );
604}
605
b794c67c
RN
606static void aggregate_layer_add_menu_items ( VikAggregateLayer *val, GtkMenu *menu, gpointer vlp )
607{
608 // Data to pass on in menu functions
88f56e64
RN
609 static menu_array_values values;
610 values[MA_VAL] = val;
611 values[MA_VLP] = vlp;
b794c67c
RN
612
613 GtkWidget *item = gtk_menu_item_new();
614 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
615 gtk_widget_show ( item );
616
da264b19
RN
617 GtkWidget *vis_submenu = gtk_menu_new ();
618 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
619 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
620 gtk_widget_show ( item );
621 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
622
623 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All") );
624 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
88f56e64 625 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_on), values );
da264b19
RN
626 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
627 gtk_widget_show ( item );
628
629 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All") );
630 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
88f56e64 631 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_off), values );
da264b19
RN
632 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
633 gtk_widget_show ( item );
634
635 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
636 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
88f56e64 637 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_child_visible_toggle), values );
da264b19
RN
638 gtk_menu_shell_append (GTK_MENU_SHELL (vis_submenu), item);
639 gtk_widget_show ( item );
640
1e78a79f
RN
641 GtkWidget *submenu_sort = gtk_menu_new ();
642 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
643 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
644 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
645 gtk_widget_show ( item );
646 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
647
648 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
649 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
88f56e64 650 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_a2z), values );
1e78a79f
RN
651 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
652 gtk_widget_show ( item );
653
654 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
655 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
88f56e64 656 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_sort_z2a), values );
1e78a79f
RN
657 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
658 gtk_widget_show ( item );
4fbfe28f
RN
659
660 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
88f56e64 661 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_analyse), values );
4fbfe28f
RN
662 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
663 gtk_widget_show ( item );
123f1312
RN
664
665 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
666 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
88f56e64 667 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_track_list_dialog), values );
123f1312
RN
668 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
669 gtk_widget_show ( item );
b4926b44
RN
670
671 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
672 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
88f56e64 673 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_waypoint_list_dialog), values );
b4926b44
RN
674 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
675 gtk_widget_show ( item );
a77c32c8 676
183a5572
RN
677 GtkWidget *search_submenu = gtk_menu_new ();
678 item = gtk_image_menu_item_new_with_mnemonic ( _("Searc_h") );
a77c32c8 679 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
183a5572
RN
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), search_submenu );
683
684 item = gtk_menu_item_new_with_mnemonic ( _("By _Date...") );
a77c32c8 685 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_search_date), values );
183a5572 686 gtk_menu_shell_append ( GTK_MENU_SHELL(search_submenu), item );
a77c32c8
RN
687 gtk_widget_set_tooltip_text (item, _("Find the first item with a specified date"));
688 gtk_widget_show ( item );
b794c67c
RN
689}
690
50a14534
EB
691static void disconnect_layer_signal ( VikLayer *vl, VikAggregateLayer *val )
692{
a135dffc
RN
693 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, val);
694 if ( number_handlers != 1 )
695 g_critical ("%s: Unexpected number of disconnect handlers: %d", __FUNCTION__, number_handlers);
50a14534
EB
696}
697
698void vik_aggregate_layer_free ( VikAggregateLayer *val )
699{
700 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
701 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
702 g_list_free ( val->children );
4fbfe28f
RN
703 if ( val->tracks_analysis_dialog != NULL )
704 gtk_widget_destroy ( val->tracks_analysis_dialog );
50a14534
EB
705}
706
707static void delete_layer_iter ( VikLayer *vl )
708{
709 if ( vl->realized )
710 vik_treeview_item_delete ( vl->vt, &(vl->iter) );
711}
712
713void vik_aggregate_layer_clear ( VikAggregateLayer *val )
714{
715 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
716 g_list_foreach ( val->children, (GFunc)(delete_layer_iter), NULL );
717 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
718 g_list_free ( val->children );
719 val->children = NULL;
720}
721
722gboolean vik_aggregate_layer_delete ( VikAggregateLayer *val, GtkTreeIter *iter )
723{
724 VikLayer *l = VIK_LAYER( vik_treeview_item_get_pointer ( VIK_LAYER(val)->vt, iter ) );
725 gboolean was_visible = l->visible;
726
727 vik_treeview_item_delete ( VIK_LAYER(val)->vt, iter );
728 val->children = g_list_remove ( val->children, l );
a135dffc 729 disconnect_layer_signal ( l, val );
50a14534
EB
730 g_object_unref ( l );
731
732 return was_visible;
733}
734
941aa6e9 735#if 0
50a14534 736/* returns 0 == we're good, 1 == didn't find any layers, 2 == got rejected */
b5926b35 737guint vik_aggregate_layer_tool ( VikAggregateLayer *val, VikLayerTypeEnum layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
738{
739 GList *iter = val->children;
740 gboolean found_rej = FALSE;
741 if (!iter)
742 return FALSE;
743 while (iter->next)
744 iter = iter->next;
745
746 while ( iter )
747 {
748 /* if this layer "accepts" the tool call */
749 if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == layer_type )
750 {
751 if ( tool_func ( VIK_LAYER(iter->data), event, vvp ) )
752 return 0;
753 else
754 found_rej = TRUE;
755 }
756
757 /* recursive -- try the same for the child aggregate layer. */
758 else if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == VIK_LAYER_AGGREGATE )
759 {
760 gint rv = vik_aggregate_layer_tool(VIK_AGGREGATE_LAYER(iter->data), layer_type, tool_func, event, vvp);
761 if ( rv == 0 )
762 return 0;
763 else if ( rv == 2 )
764 found_rej = TRUE;
765 }
766 iter = iter->prev;
767 }
768 return found_rej ? 2 : 1; /* no one wanted to accept the tool call in this layer */
769}
941aa6e9 770#endif
50a14534 771
b5926b35 772VikLayer *vik_aggregate_layer_get_top_visible_layer_of_type ( VikAggregateLayer *val, VikLayerTypeEnum type )
50a14534
EB
773{
774 VikLayer *rv;
775 GList *ls = val->children;
776 if (!ls)
777 return NULL;
778 while (ls->next)
779 ls = ls->next;
780
781 while ( ls )
782 {
405b74ed
GB
783 VikLayer *vl = VIK_LAYER(ls->data);
784 if ( vl->visible && vl->type == type )
785 return vl;
786 else if ( vl->visible && vl->type == VIK_LAYER_AGGREGATE )
50a14534 787 {
405b74ed 788 rv = vik_aggregate_layer_get_top_visible_layer_of_type(VIK_AGGREGATE_LAYER(vl), type);
50a14534
EB
789 if ( rv )
790 return rv;
791 }
792 ls = ls->prev;
793 }
794 return NULL;
795}
796
b5926b35 797GList *vik_aggregate_layer_get_all_layers_of_type(VikAggregateLayer *val, GList *layers, VikLayerTypeEnum type, gboolean include_invisible)
7114e879
QT
798{
799 GList *l = layers;
800 GList *children = val->children;
405b74ed 801 VikLayer *vl;
7114e879
QT
802 if (!children)
803 return layers;
aa7ed888
RN
804
805 // Where appropriate *don't* include non-visible layers
7114e879 806 while (children) {
405b74ed 807 vl = VIK_LAYER(children->data);
aa7ed888
RN
808 if (vl->type == VIK_LAYER_AGGREGATE ) {
809 // Don't even consider invisible aggregrates, unless told to
810 if (vl->visible || include_invisible)
b5926b35 811 l = vik_aggregate_layer_get_all_layers_of_type(VIK_AGGREGATE_LAYER(children->data), l, type, include_invisible);
aa7ed888
RN
812 }
813 else if (vl->type == type) {
814 if (vl->visible || include_invisible)
b5926b35 815 l = g_list_prepend(l, children->data); /* now in top down order */
aa7ed888 816 }
7b1a871d
RN
817 else if (type == VIK_LAYER_TRW) {
818 /* GPS layers contain TRW layers. cf with usage in file.c */
819 if (VIK_LAYER(children->data)->type == VIK_LAYER_GPS) {
aa7ed888
RN
820 if (VIK_LAYER(children->data)->visible || include_invisible) {
821 if (!vik_gps_layer_is_empty(VIK_GPS_LAYER(children->data))) {
822 /*
823 can not use g_list_concat due to wrong copy method - crashes if used a couple times !!
824 l = g_list_concat (l, vik_gps_layer_get_children (VIK_GPS_LAYER(children->data)));
825 */
826 /* create own copy method instead :( */
827 GList *gps_trw_layers = (GList *)vik_gps_layer_get_children (VIK_GPS_LAYER(children->data));
828 int n_layers = g_list_length (gps_trw_layers);
829 int layer = 0;
830 for ( layer = 0; layer < n_layers; layer++) {
831 l = g_list_prepend(l, gps_trw_layers->data);
832 gps_trw_layers = gps_trw_layers->next;
833 }
834 g_list_free(gps_trw_layers);
7b1a871d 835 }
7b1a871d
RN
836 }
837 }
838 }
7114e879
QT
839 children = children->next;
840 }
841 return l;
842}
843
50a14534
EB
844void vik_aggregate_layer_realize ( VikAggregateLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter )
845{
846 GList *i = val->children;
847 GtkTreeIter iter;
405b74ed
GB
848 VikLayer *vl = VIK_LAYER(val);
849 VikLayer *vli;
50a14534
EB
850 while ( i )
851 {
405b74ed 852 vli = VIK_LAYER(i->data);
c3a02429 853 vik_treeview_add_layer ( vl->vt, layer_iter, &iter, vli->name, val, TRUE,
405b74ed
GB
854 vli, vli->type, vli->type );
855 if ( ! vli->visible )
856 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
857 vik_layer_realize ( vli, vl->vt, &iter );
50a14534
EB
858 i = i->next;
859 }
860}
861
862const GList *vik_aggregate_layer_get_children ( VikAggregateLayer *val )
863{
864 return val->children;
865}
866
867gboolean vik_aggregate_layer_is_empty ( VikAggregateLayer *val )
868{
869 if ( val->children )
870 return FALSE;
871 return TRUE;
872}
70a23263
AF
873
874static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
875{
876 VikTreeview *vt = VIK_LAYER(val_src)->vt;
877 VikLayer *vl = vik_treeview_item_get_pointer(vt, src_item_iter);
e673b75f 878 GtkTreeIter dest_iter;
c390aa71
AF
879 gchar *dp;
880 gboolean target_exists;
e673b75f 881
c390aa71
AF
882 dp = gtk_tree_path_to_string(dest_path);
883 target_exists = vik_treeview_get_iter_from_path_str(vt, &dest_iter, dp);
801ce684
EB
884
885 /* vik_aggregate_layer_delete unrefs, but we don't want that here.
886 * we're still using the layer. */
887 g_object_ref ( vl );
888 vik_aggregate_layer_delete(val_src, src_item_iter);
889
c390aa71 890 if (target_exists) {
e673b75f
AF
891 vik_aggregate_layer_insert_layer(val_dest, vl, &dest_iter);
892 } else {
893 vik_aggregate_layer_insert_layer(val_dest, vl, NULL); /* append */
894 }
c390aa71 895 g_free(dp);
70a23263
AF
896}
897
a553bf07
RN
898/**
899 * Generate tooltip text for the layer.
900 */
901static const gchar* aggregate_layer_tooltip ( VikAggregateLayer *val )
902{
903 static gchar tmp_buf[128];
904 tmp_buf[0] = '\0';
905
906 GList *children = val->children;
907 if ( children ) {
908 gint nn = g_list_length (children);
909 // Could have a more complicated tooltip that numbers each type of layers,
910 // but for now a simple overall count
911 g_snprintf (tmp_buf, sizeof(tmp_buf), ngettext("One layer", "%d layers", nn), nn );
912 }
913 return tmp_buf;
914}