]> git.street.me.uk Git - andy/viking.git/blame - src/vikaggregatelayer.c
Make more portable .vik file, as don't save the map cache directory if it's the map...
[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>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include "viking.h"
5bfafde9 23#include "icons/icons.h"
50a14534
EB
24
25#include <string.h>
affcc0f2 26#include <glib/gi18n.h>
50a14534
EB
27
28#define DISCONNECT_UPDATE_SIGNAL(vl, val) g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, val)
29
0a6cab71
AF
30static void aggregate_layer_marshall( VikAggregateLayer *val, guint8 **data, gint *len );
31static VikAggregateLayer *aggregate_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
50a14534 32static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode );
70a23263 33static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
50a14534
EB
34
35VikLayerInterface vik_aggregate_layer_interface = {
db386630 36 "Aggregate",
affcc0f2 37 N_("Aggregate"),
75078768 38 "<control><shift>A",
5bfafde9 39 &vikaggregatelayer_pixbuf,
50a14534
EB
40
41 NULL,
42 0,
43
44 NULL,
45 0,
46 NULL,
47 0,
48
5a4a28bf
QT
49 VIK_MENU_ITEM_ALL,
50
50a14534
EB
51 (VikLayerFuncCreate) vik_aggregate_layer_create,
52 (VikLayerFuncRealize) vik_aggregate_layer_realize,
53 (VikLayerFuncPostRead) NULL,
54 (VikLayerFuncFree) vik_aggregate_layer_free,
55
56 (VikLayerFuncProperties) NULL,
57 (VikLayerFuncDraw) vik_aggregate_layer_draw,
58 (VikLayerFuncChangeCoordMode) aggregate_layer_change_coord_mode,
20c7a3a0
QT
59
60 (VikLayerFuncSetMenuItemsSelection) NULL,
61 (VikLayerFuncGetMenuItemsSelection) NULL,
50a14534
EB
62
63 (VikLayerFuncAddMenuItems) NULL,
64 (VikLayerFuncSublayerAddMenuItems) NULL,
65
66 (VikLayerFuncSublayerRenameRequest) NULL,
67 (VikLayerFuncSublayerToggleVisible) NULL,
9da7faf2 68 (VikLayerFuncSublayerTooltip) NULL,
1e8b7f57 69 (VikLayerFuncLayerTooltip) NULL,
a5dcfdb7 70 (VikLayerFuncLayerSelected) NULL,
50a14534 71
0a6cab71
AF
72 (VikLayerFuncMarshall) aggregate_layer_marshall,
73 (VikLayerFuncUnmarshall) aggregate_layer_unmarshall,
50a14534
EB
74
75 (VikLayerFuncSetParam) NULL,
76 (VikLayerFuncGetParam) NULL,
77
78 (VikLayerFuncReadFileData) NULL,
79 (VikLayerFuncWriteFileData) NULL,
80
33534cd8 81 (VikLayerFuncDeleteItem) NULL,
d5874ef9 82 (VikLayerFuncCutItem) NULL,
50a14534
EB
83 (VikLayerFuncCopyItem) NULL,
84 (VikLayerFuncPasteItem) NULL,
85 (VikLayerFuncFreeCopiedItem) NULL,
70a23263 86 (VikLayerFuncDragDropRequest) aggregate_layer_drag_drop_request,
77ad64fa
RN
87
88 (VikLayerFuncSelectClick) NULL,
08f14055
RN
89 (VikLayerFuncSelectMove) NULL,
90 (VikLayerFuncSelectRelease) NULL,
e46f259a 91 (VikLayerFuncSelectedViewportMenu) NULL,
50a14534
EB
92};
93
94struct _VikAggregateLayer {
95 VikLayer vl;
96 GList *children;
97};
98
99GType vik_aggregate_layer_get_type ()
100{
101 static GType val_type = 0;
102
103 if (!val_type)
104 {
105 static const GTypeInfo val_info =
106 {
107 sizeof (VikAggregateLayerClass),
108 NULL, /* base_init */
109 NULL, /* base_finalize */
110 NULL, /* class init */
111 NULL, /* class_finalize */
112 NULL, /* class_data */
113 sizeof (VikAggregateLayer),
114 0,
115 NULL /* instance init */
116 };
117 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikAggregateLayer", &val_info, 0 );
118 }
119
120 return val_type;
121}
122
123VikAggregateLayer *vik_aggregate_layer_create (VikViewport *vp)
124{
125 VikAggregateLayer *rv = vik_aggregate_layer_new ();
126 vik_layer_rename ( VIK_LAYER(rv), vik_aggregate_layer_interface.name );
127 return rv;
128}
129
0a6cab71
AF
130static void aggregate_layer_marshall( VikAggregateLayer *val, guint8 **data, gint *datalen )
131{
132 GList *child = val->children;
133 VikLayer *child_layer;
134 guint8 *ld;
135 gint ll;
136 GByteArray* b = g_byte_array_new ();
137 gint len;
138
139#define alm_append(obj, sz) \
140 len = (sz); \
141 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
142 g_byte_array_append ( b, (guint8 *)(obj), len );
143
144 vik_layer_marshall_params(VIK_LAYER(val), &ld, &ll);
145 alm_append(ld, ll);
146 g_free(ld);
147
148 while (child) {
149 child_layer = VIK_LAYER(child->data);
150 vik_layer_marshall ( child_layer, &ld, &ll );
151 if (ld) {
152 alm_append(ld, ll);
153 g_free(ld);
154 }
155 child = child->next;
156 }
157 *data = b->data;
158 *datalen = b->len;
159 g_byte_array_free(b, FALSE);
160#undef alm_append
161}
162
163static VikAggregateLayer *aggregate_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
164{
165#define alm_size (*(gint *)data)
166#define alm_next \
167 len -= sizeof(gint) + alm_size; \
168 data += sizeof(gint) + alm_size;
169
170 VikAggregateLayer *rv = vik_aggregate_layer_new();
171 VikLayer *child_layer;
172
173 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
174 alm_next;
175
176 while (len>0) {
177 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
178 if (child_layer) {
179 rv->children = g_list_append ( rv->children, child_layer );
0df66d57 180 g_signal_connect_swapped ( G_OBJECT(child_layer), "update", G_CALLBACK(vik_layer_emit_update_secondary), rv );
0a6cab71
AF
181 }
182 alm_next;
183 }
184 // g_print("aggregate_layer_unmarshall ended with len=%d\n", len);
185 return rv;
186#undef alm_size
187#undef alm_next
188}
189
50a14534
EB
190VikAggregateLayer *vik_aggregate_layer_new ()
191{
192 VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( g_object_new ( VIK_AGGREGATE_LAYER_TYPE, NULL ) );
a0c65899 193 vik_layer_set_type ( VIK_LAYER(val), VIK_LAYER_AGGREGATE );
50a14534
EB
194 val->children = NULL;
195 return val;
196}
197
198void vik_aggregate_layer_insert_layer ( VikAggregateLayer *val, VikLayer *l, GtkTreeIter *replace_iter )
199{
50a14534 200 GtkTreeIter iter;
405b74ed 201 VikLayer *vl = VIK_LAYER(val);
e673b75f 202
c3a02429
RN
203 // By default layers are inserted above the selected layer
204 gboolean put_above = TRUE;
205
206 // These types are 'base' types in that you what other information on top
207 if ( l->type == VIK_LAYER_MAPS || l->type == VIK_LAYER_DEM || l->type == VIK_LAYER_GEOREF )
208 put_above = FALSE;
209
405b74ed 210 if ( vl->realized )
50a14534 211 {
c3a02429 212 vik_treeview_insert_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type, replace_iter );
50a14534 213 if ( ! l->visible )
405b74ed
GB
214 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
215 vik_layer_realize ( l, vl->vt, &iter );
50a14534
EB
216
217 if ( val->children == NULL )
405b74ed 218 vik_treeview_expand ( vl->vt, &(vl->iter) );
50a14534 219 }
50a14534 220
e673b75f 221 if (replace_iter) {
405b74ed 222 GList *theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( vl->vt, replace_iter ) );
c3a02429
RN
223 if ( put_above )
224 val->children = g_list_insert ( val->children, l, g_list_position(val->children,theone)+1 );
225 else
226 // Thus insert 'here' (so don't add 1)
227 val->children = g_list_insert ( val->children, l, g_list_position(val->children,theone) );
e673b75f 228 } else {
cc6b165a
RN
229 // Effectively insert at 'end' of the list to match how displayed in the treeview
230 // - but since it is drawn from 'bottom first' it is actually the first in the child list
231 // This ordering is especially important if it is a map or similar type,
232 // which needs be drawn first for the layering draw method to work properly.
233 // ATM this only happens when a layer is drag/dropped to the end of an aggregate layer
234 val->children = g_list_prepend ( val->children, l );
e673b75f 235 }
0df66d57 236 g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update_secondary), val );
50a14534
EB
237}
238
c3a02429
RN
239/**
240 * vik_aggregate_layer_add_layer:
241 * @allow_reordering: should be set for GUI interactions,
242 * whereas loading from a file needs strict ordering and so should be FALSE
243 */
244void vik_aggregate_layer_add_layer ( VikAggregateLayer *val, VikLayer *l, gboolean allow_reordering )
50a14534
EB
245{
246 GtkTreeIter iter;
405b74ed 247 VikLayer *vl = VIK_LAYER(val);
50a14534 248
c3a02429
RN
249 // By default layers go to the top
250 gboolean put_above = TRUE;
251
252 if ( allow_reordering ) {
253 // These types are 'base' types in that you what other information on top
254 if ( l->type == VIK_LAYER_MAPS || l->type == VIK_LAYER_DEM || l->type == VIK_LAYER_GEOREF )
255 put_above = FALSE;
256 }
257
405b74ed 258 if ( vl->realized )
50a14534 259 {
c3a02429 260 vik_treeview_add_layer ( vl->vt, &(vl->iter), &iter, l->name, val, put_above, l, l->type, l->type);
50a14534 261 if ( ! l->visible )
405b74ed
GB
262 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
263 vik_layer_realize ( l, vl->vt, &iter );
50a14534
EB
264
265 if ( val->children == NULL )
405b74ed 266 vik_treeview_expand ( vl->vt, &(vl->iter) );
50a14534
EB
267 }
268
c3a02429
RN
269 if ( put_above )
270 val->children = g_list_append ( val->children, l );
271 else
272 val->children = g_list_prepend ( val->children, l );
273
0df66d57 274 g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update_secondary), val );
50a14534
EB
275}
276
277void vik_aggregate_layer_move_layer ( VikAggregateLayer *val, GtkTreeIter *child_iter, gboolean up )
278{
279 GList *theone, *first, *second;
405b74ed
GB
280 VikLayer *vl = VIK_LAYER(val);
281 vik_treeview_move_item ( vl->vt, child_iter, up );
50a14534 282
405b74ed 283 theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( vl->vt, child_iter ) );
50a14534
EB
284
285 g_assert ( theone != NULL );
286
287 /* the old switcheroo */
288 if ( up && theone->next )
289 {
290 first = theone;
291 second = theone->next;
292 }
293 else if ( !up && theone->prev )
294 {
295 first = theone->prev;
296 second = theone;
297 }
298 else
299 return;
300
301 first->next = second->next;
302 second->prev = first->prev;
303 first->prev = second;
304 second->next = first;
305
306 /* second is now first */
307
308 if ( second->prev )
309 second->prev->next = second;
310 if ( first->next )
311 first->next->prev = first;
312
313 if ( second->prev == NULL )
314 val->children = second;
315}
316
0df66d57
EB
317/* Draw the aggregate layer. If vik viewport is in half_drawn mode, this means we are only
318 * to draw the layers above and including the trigger layer.
319 * To do this we don't draw any layers if in half drawn mode, unless we find the
320 * trigger layer, in which case we pull up the saved pixmap, turn off half drawn mode and
321 * start drawing layers.
322 * Also, if we were never in half drawn mode, we save a snapshot
323 * of the pixmap before drawing the trigger layer so we can use it again
324 * later.
325 */
45c5ac8e 326void vik_aggregate_layer_draw ( VikAggregateLayer *val, VikViewport *vp )
50a14534 327{
0df66d57
EB
328 GList *iter = val->children;
329 VikLayer *vl;
45c5ac8e 330 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
0df66d57
EB
331 while ( iter ) {
332 vl = VIK_LAYER(iter->data);
333 if ( vl == trigger ) {
45c5ac8e
RN
334 if ( vik_viewport_get_half_drawn ( vp ) ) {
335 vik_viewport_set_half_drawn ( vp, FALSE );
336 vik_viewport_snapshot_load( vp );
0df66d57 337 } else {
45c5ac8e 338 vik_viewport_snapshot_save( vp );
0df66d57
EB
339 }
340 }
45c5ac8e
RN
341 if ( vl->type == VIK_LAYER_AGGREGATE || vl->type == VIK_LAYER_GPS || ! vik_viewport_get_half_drawn( vp ) )
342 vik_layer_draw ( vl, vp );
0df66d57
EB
343 iter = iter->next;
344 }
50a14534
EB
345}
346
347static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode )
348{
349 GList *iter = val->children;
350 while ( iter )
351 {
352 vik_layer_change_coord_mode ( VIK_LAYER(iter->data), mode );
353 iter = iter->next;
354 }
355}
356
357static void disconnect_layer_signal ( VikLayer *vl, VikAggregateLayer *val )
358{
359 g_assert(DISCONNECT_UPDATE_SIGNAL(vl,val)==1);
360}
361
362void vik_aggregate_layer_free ( VikAggregateLayer *val )
363{
364 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
365 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
366 g_list_free ( val->children );
367}
368
369static void delete_layer_iter ( VikLayer *vl )
370{
371 if ( vl->realized )
372 vik_treeview_item_delete ( vl->vt, &(vl->iter) );
373}
374
375void vik_aggregate_layer_clear ( VikAggregateLayer *val )
376{
377 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
378 g_list_foreach ( val->children, (GFunc)(delete_layer_iter), NULL );
379 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
380 g_list_free ( val->children );
381 val->children = NULL;
382}
383
384gboolean vik_aggregate_layer_delete ( VikAggregateLayer *val, GtkTreeIter *iter )
385{
386 VikLayer *l = VIK_LAYER( vik_treeview_item_get_pointer ( VIK_LAYER(val)->vt, iter ) );
387 gboolean was_visible = l->visible;
388
389 vik_treeview_item_delete ( VIK_LAYER(val)->vt, iter );
390 val->children = g_list_remove ( val->children, l );
391 g_assert(DISCONNECT_UPDATE_SIGNAL(l,val)==1);
392 g_object_unref ( l );
393
394 return was_visible;
395}
396
941aa6e9 397#if 0
50a14534 398/* returns 0 == we're good, 1 == didn't find any layers, 2 == got rejected */
b5926b35 399guint vik_aggregate_layer_tool ( VikAggregateLayer *val, VikLayerTypeEnum layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
400{
401 GList *iter = val->children;
402 gboolean found_rej = FALSE;
403 if (!iter)
404 return FALSE;
405 while (iter->next)
406 iter = iter->next;
407
408 while ( iter )
409 {
410 /* if this layer "accepts" the tool call */
411 if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == layer_type )
412 {
413 if ( tool_func ( VIK_LAYER(iter->data), event, vvp ) )
414 return 0;
415 else
416 found_rej = TRUE;
417 }
418
419 /* recursive -- try the same for the child aggregate layer. */
420 else if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == VIK_LAYER_AGGREGATE )
421 {
422 gint rv = vik_aggregate_layer_tool(VIK_AGGREGATE_LAYER(iter->data), layer_type, tool_func, event, vvp);
423 if ( rv == 0 )
424 return 0;
425 else if ( rv == 2 )
426 found_rej = TRUE;
427 }
428 iter = iter->prev;
429 }
430 return found_rej ? 2 : 1; /* no one wanted to accept the tool call in this layer */
431}
941aa6e9 432#endif
50a14534 433
b5926b35 434VikLayer *vik_aggregate_layer_get_top_visible_layer_of_type ( VikAggregateLayer *val, VikLayerTypeEnum type )
50a14534
EB
435{
436 VikLayer *rv;
437 GList *ls = val->children;
438 if (!ls)
439 return NULL;
440 while (ls->next)
441 ls = ls->next;
442
443 while ( ls )
444 {
405b74ed
GB
445 VikLayer *vl = VIK_LAYER(ls->data);
446 if ( vl->visible && vl->type == type )
447 return vl;
448 else if ( vl->visible && vl->type == VIK_LAYER_AGGREGATE )
50a14534 449 {
405b74ed 450 rv = vik_aggregate_layer_get_top_visible_layer_of_type(VIK_AGGREGATE_LAYER(vl), type);
50a14534
EB
451 if ( rv )
452 return rv;
453 }
454 ls = ls->prev;
455 }
456 return NULL;
457}
458
b5926b35 459GList *vik_aggregate_layer_get_all_layers_of_type(VikAggregateLayer *val, GList *layers, VikLayerTypeEnum type, gboolean include_invisible)
7114e879
QT
460{
461 GList *l = layers;
462 GList *children = val->children;
405b74ed 463 VikLayer *vl;
7114e879
QT
464 if (!children)
465 return layers;
aa7ed888
RN
466
467 // Where appropriate *don't* include non-visible layers
7114e879 468 while (children) {
405b74ed 469 vl = VIK_LAYER(children->data);
aa7ed888
RN
470 if (vl->type == VIK_LAYER_AGGREGATE ) {
471 // Don't even consider invisible aggregrates, unless told to
472 if (vl->visible || include_invisible)
b5926b35 473 l = vik_aggregate_layer_get_all_layers_of_type(VIK_AGGREGATE_LAYER(children->data), l, type, include_invisible);
aa7ed888
RN
474 }
475 else if (vl->type == type) {
476 if (vl->visible || include_invisible)
b5926b35 477 l = g_list_prepend(l, children->data); /* now in top down order */
aa7ed888 478 }
7b1a871d
RN
479 else if (type == VIK_LAYER_TRW) {
480 /* GPS layers contain TRW layers. cf with usage in file.c */
481 if (VIK_LAYER(children->data)->type == VIK_LAYER_GPS) {
aa7ed888
RN
482 if (VIK_LAYER(children->data)->visible || include_invisible) {
483 if (!vik_gps_layer_is_empty(VIK_GPS_LAYER(children->data))) {
484 /*
485 can not use g_list_concat due to wrong copy method - crashes if used a couple times !!
486 l = g_list_concat (l, vik_gps_layer_get_children (VIK_GPS_LAYER(children->data)));
487 */
488 /* create own copy method instead :( */
489 GList *gps_trw_layers = (GList *)vik_gps_layer_get_children (VIK_GPS_LAYER(children->data));
490 int n_layers = g_list_length (gps_trw_layers);
491 int layer = 0;
492 for ( layer = 0; layer < n_layers; layer++) {
493 l = g_list_prepend(l, gps_trw_layers->data);
494 gps_trw_layers = gps_trw_layers->next;
495 }
496 g_list_free(gps_trw_layers);
7b1a871d 497 }
7b1a871d
RN
498 }
499 }
500 }
7114e879
QT
501 children = children->next;
502 }
503 return l;
504}
505
50a14534
EB
506void vik_aggregate_layer_realize ( VikAggregateLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter )
507{
508 GList *i = val->children;
509 GtkTreeIter iter;
405b74ed
GB
510 VikLayer *vl = VIK_LAYER(val);
511 VikLayer *vli;
50a14534
EB
512 while ( i )
513 {
405b74ed 514 vli = VIK_LAYER(i->data);
c3a02429 515 vik_treeview_add_layer ( vl->vt, layer_iter, &iter, vli->name, val, TRUE,
405b74ed
GB
516 vli, vli->type, vli->type );
517 if ( ! vli->visible )
518 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
519 vik_layer_realize ( vli, vl->vt, &iter );
50a14534
EB
520 i = i->next;
521 }
522}
523
524const GList *vik_aggregate_layer_get_children ( VikAggregateLayer *val )
525{
526 return val->children;
527}
528
529gboolean vik_aggregate_layer_is_empty ( VikAggregateLayer *val )
530{
531 if ( val->children )
532 return FALSE;
533 return TRUE;
534}
70a23263
AF
535
536static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
537{
538 VikTreeview *vt = VIK_LAYER(val_src)->vt;
539 VikLayer *vl = vik_treeview_item_get_pointer(vt, src_item_iter);
e673b75f 540 GtkTreeIter dest_iter;
c390aa71
AF
541 gchar *dp;
542 gboolean target_exists;
e673b75f 543
c390aa71
AF
544 dp = gtk_tree_path_to_string(dest_path);
545 target_exists = vik_treeview_get_iter_from_path_str(vt, &dest_iter, dp);
801ce684
EB
546
547 /* vik_aggregate_layer_delete unrefs, but we don't want that here.
548 * we're still using the layer. */
549 g_object_ref ( vl );
550 vik_aggregate_layer_delete(val_src, src_item_iter);
551
c390aa71 552 if (target_exists) {
e673b75f
AF
553 vik_aggregate_layer_insert_layer(val_dest, vl, &dest_iter);
554 } else {
555 vik_aggregate_layer_insert_layer(val_dest, vl, NULL); /* append */
556 }
c390aa71 557 g_free(dp);
70a23263
AF
558}
559