]> git.street.me.uk Git - andy/viking.git/blame - src/vikaggregatelayer.c
help/Makefile.am: explicitly list figures.
[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
405b74ed 203 if ( vl->realized )
50a14534 204 {
405b74ed 205 vik_treeview_insert_layer ( vl->vt, &(vl->iter), &iter, l->name, val, l, l->type, l->type, replace_iter );
50a14534 206 if ( ! l->visible )
405b74ed
GB
207 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
208 vik_layer_realize ( l, vl->vt, &iter );
50a14534
EB
209
210 if ( val->children == NULL )
405b74ed 211 vik_treeview_expand ( vl->vt, &(vl->iter) );
50a14534 212 }
50a14534 213
e673b75f 214 if (replace_iter) {
405b74ed 215 GList *theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( vl->vt, replace_iter ) );
e673b75f
AF
216 val->children = g_list_insert ( val->children, l, g_list_position(val->children,theone)+1 );
217 } else {
cc6b165a
RN
218 // Effectively insert at 'end' of the list to match how displayed in the treeview
219 // - but since it is drawn from 'bottom first' it is actually the first in the child list
220 // This ordering is especially important if it is a map or similar type,
221 // which needs be drawn first for the layering draw method to work properly.
222 // ATM this only happens when a layer is drag/dropped to the end of an aggregate layer
223 val->children = g_list_prepend ( val->children, l );
e673b75f 224 }
0df66d57 225 g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update_secondary), val );
50a14534
EB
226}
227
228void vik_aggregate_layer_add_layer ( VikAggregateLayer *val, VikLayer *l )
229{
230 GtkTreeIter iter;
405b74ed 231 VikLayer *vl = VIK_LAYER(val);
50a14534 232
405b74ed 233 if ( vl->realized )
50a14534 234 {
405b74ed 235 vik_treeview_add_layer ( vl->vt, &(vl->iter), &iter, l->name, val, l, l->type, l->type);
50a14534 236 if ( ! l->visible )
405b74ed
GB
237 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
238 vik_layer_realize ( l, vl->vt, &iter );
50a14534
EB
239
240 if ( val->children == NULL )
405b74ed 241 vik_treeview_expand ( vl->vt, &(vl->iter) );
50a14534
EB
242 }
243
244 val->children = g_list_append ( val->children, l );
0df66d57 245 g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update_secondary), val );
50a14534
EB
246}
247
248void vik_aggregate_layer_move_layer ( VikAggregateLayer *val, GtkTreeIter *child_iter, gboolean up )
249{
250 GList *theone, *first, *second;
405b74ed
GB
251 VikLayer *vl = VIK_LAYER(val);
252 vik_treeview_move_item ( vl->vt, child_iter, up );
50a14534 253
405b74ed 254 theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( vl->vt, child_iter ) );
50a14534
EB
255
256 g_assert ( theone != NULL );
257
258 /* the old switcheroo */
259 if ( up && theone->next )
260 {
261 first = theone;
262 second = theone->next;
263 }
264 else if ( !up && theone->prev )
265 {
266 first = theone->prev;
267 second = theone;
268 }
269 else
270 return;
271
272 first->next = second->next;
273 second->prev = first->prev;
274 first->prev = second;
275 second->next = first;
276
277 /* second is now first */
278
279 if ( second->prev )
280 second->prev->next = second;
281 if ( first->next )
282 first->next->prev = first;
283
284 if ( second->prev == NULL )
285 val->children = second;
286}
287
0df66d57
EB
288/* Draw the aggregate layer. If vik viewport is in half_drawn mode, this means we are only
289 * to draw the layers above and including the trigger layer.
290 * To do this we don't draw any layers if in half drawn mode, unless we find the
291 * trigger layer, in which case we pull up the saved pixmap, turn off half drawn mode and
292 * start drawing layers.
293 * Also, if we were never in half drawn mode, we save a snapshot
294 * of the pixmap before drawing the trigger layer so we can use it again
295 * later.
296 */
50a14534
EB
297void vik_aggregate_layer_draw ( VikAggregateLayer *val, gpointer data )
298{
0df66d57
EB
299 GList *iter = val->children;
300 VikLayer *vl;
405b74ed
GB
301 VikViewport *viewport = VIK_VIEWPORT(data);
302 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( viewport ));
0df66d57
EB
303 while ( iter ) {
304 vl = VIK_LAYER(iter->data);
305 if ( vl == trigger ) {
405b74ed
GB
306 if ( vik_viewport_get_half_drawn ( viewport ) ) {
307 vik_viewport_set_half_drawn ( viewport, FALSE );
308 vik_viewport_snapshot_load( viewport );
0df66d57 309 } else {
405b74ed 310 vik_viewport_snapshot_save( viewport );
0df66d57
EB
311 }
312 }
405b74ed 313 if ( vl->type == VIK_LAYER_AGGREGATE || vl->type == VIK_LAYER_GPS || ! vik_viewport_get_half_drawn( viewport ) )
0df66d57
EB
314 vik_layer_draw ( vl, data );
315 iter = iter->next;
316 }
50a14534
EB
317}
318
319static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode )
320{
321 GList *iter = val->children;
322 while ( iter )
323 {
324 vik_layer_change_coord_mode ( VIK_LAYER(iter->data), mode );
325 iter = iter->next;
326 }
327}
328
329static void disconnect_layer_signal ( VikLayer *vl, VikAggregateLayer *val )
330{
331 g_assert(DISCONNECT_UPDATE_SIGNAL(vl,val)==1);
332}
333
334void vik_aggregate_layer_free ( VikAggregateLayer *val )
335{
336 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
337 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
338 g_list_free ( val->children );
339}
340
341static void delete_layer_iter ( VikLayer *vl )
342{
343 if ( vl->realized )
344 vik_treeview_item_delete ( vl->vt, &(vl->iter) );
345}
346
347void vik_aggregate_layer_clear ( VikAggregateLayer *val )
348{
349 g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val );
350 g_list_foreach ( val->children, (GFunc)(delete_layer_iter), NULL );
351 g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL );
352 g_list_free ( val->children );
353 val->children = NULL;
354}
355
356gboolean vik_aggregate_layer_delete ( VikAggregateLayer *val, GtkTreeIter *iter )
357{
358 VikLayer *l = VIK_LAYER( vik_treeview_item_get_pointer ( VIK_LAYER(val)->vt, iter ) );
359 gboolean was_visible = l->visible;
360
361 vik_treeview_item_delete ( VIK_LAYER(val)->vt, iter );
362 val->children = g_list_remove ( val->children, l );
363 g_assert(DISCONNECT_UPDATE_SIGNAL(l,val)==1);
364 g_object_unref ( l );
365
366 return was_visible;
367}
368
941aa6e9 369#if 0
50a14534
EB
370/* returns 0 == we're good, 1 == didn't find any layers, 2 == got rejected */
371guint vik_aggregate_layer_tool ( VikAggregateLayer *val, guint16 layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp )
372{
373 GList *iter = val->children;
374 gboolean found_rej = FALSE;
375 if (!iter)
376 return FALSE;
377 while (iter->next)
378 iter = iter->next;
379
380 while ( iter )
381 {
382 /* if this layer "accepts" the tool call */
383 if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == layer_type )
384 {
385 if ( tool_func ( VIK_LAYER(iter->data), event, vvp ) )
386 return 0;
387 else
388 found_rej = TRUE;
389 }
390
391 /* recursive -- try the same for the child aggregate layer. */
392 else if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == VIK_LAYER_AGGREGATE )
393 {
394 gint rv = vik_aggregate_layer_tool(VIK_AGGREGATE_LAYER(iter->data), layer_type, tool_func, event, vvp);
395 if ( rv == 0 )
396 return 0;
397 else if ( rv == 2 )
398 found_rej = TRUE;
399 }
400 iter = iter->prev;
401 }
402 return found_rej ? 2 : 1; /* no one wanted to accept the tool call in this layer */
403}
941aa6e9 404#endif
50a14534
EB
405
406VikLayer *vik_aggregate_layer_get_top_visible_layer_of_type ( VikAggregateLayer *val, gint type )
407{
408 VikLayer *rv;
409 GList *ls = val->children;
410 if (!ls)
411 return NULL;
412 while (ls->next)
413 ls = ls->next;
414
415 while ( ls )
416 {
405b74ed
GB
417 VikLayer *vl = VIK_LAYER(ls->data);
418 if ( vl->visible && vl->type == type )
419 return vl;
420 else if ( vl->visible && vl->type == VIK_LAYER_AGGREGATE )
50a14534 421 {
405b74ed 422 rv = vik_aggregate_layer_get_top_visible_layer_of_type(VIK_AGGREGATE_LAYER(vl), type);
50a14534
EB
423 if ( rv )
424 return rv;
425 }
426 ls = ls->prev;
427 }
428 return NULL;
429}
430
aa7ed888 431GList *vik_aggregate_layer_get_all_layers_of_type(VikAggregateLayer *val, GList *layers, gint type, gboolean include_invisible)
7114e879
QT
432{
433 GList *l = layers;
434 GList *children = val->children;
405b74ed 435 VikLayer *vl;
7114e879
QT
436 if (!children)
437 return layers;
aa7ed888
RN
438
439 // Where appropriate *don't* include non-visible layers
7114e879 440 while (children) {
405b74ed 441 vl = VIK_LAYER(children->data);
aa7ed888
RN
442 if (vl->type == VIK_LAYER_AGGREGATE ) {
443 // Don't even consider invisible aggregrates, unless told to
444 if (vl->visible || include_invisible)
445 l = vik_aggregate_layer_get_all_layers_of_type(VIK_AGGREGATE_LAYER(children->data), l, type, include_invisible);
446 }
447 else if (vl->type == type) {
448 if (vl->visible || include_invisible)
449 l = g_list_prepend(l, children->data); /* now in top down order */
450 }
7b1a871d
RN
451 else if (type == VIK_LAYER_TRW) {
452 /* GPS layers contain TRW layers. cf with usage in file.c */
453 if (VIK_LAYER(children->data)->type == VIK_LAYER_GPS) {
aa7ed888
RN
454 if (VIK_LAYER(children->data)->visible || include_invisible) {
455 if (!vik_gps_layer_is_empty(VIK_GPS_LAYER(children->data))) {
456 /*
457 can not use g_list_concat due to wrong copy method - crashes if used a couple times !!
458 l = g_list_concat (l, vik_gps_layer_get_children (VIK_GPS_LAYER(children->data)));
459 */
460 /* create own copy method instead :( */
461 GList *gps_trw_layers = (GList *)vik_gps_layer_get_children (VIK_GPS_LAYER(children->data));
462 int n_layers = g_list_length (gps_trw_layers);
463 int layer = 0;
464 for ( layer = 0; layer < n_layers; layer++) {
465 l = g_list_prepend(l, gps_trw_layers->data);
466 gps_trw_layers = gps_trw_layers->next;
467 }
468 g_list_free(gps_trw_layers);
7b1a871d 469 }
7b1a871d
RN
470 }
471 }
472 }
7114e879
QT
473 children = children->next;
474 }
475 return l;
476}
477
50a14534
EB
478void vik_aggregate_layer_realize ( VikAggregateLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter )
479{
480 GList *i = val->children;
481 GtkTreeIter iter;
405b74ed
GB
482 VikLayer *vl = VIK_LAYER(val);
483 VikLayer *vli;
50a14534
EB
484 while ( i )
485 {
405b74ed
GB
486 vli = VIK_LAYER(i->data);
487 vik_treeview_add_layer ( vl->vt, layer_iter, &iter, vli->name, val,
488 vli, vli->type, vli->type );
489 if ( ! vli->visible )
490 vik_treeview_item_set_visible ( vl->vt, &iter, FALSE );
491 vik_layer_realize ( vli, vl->vt, &iter );
50a14534
EB
492 i = i->next;
493 }
494}
495
496const GList *vik_aggregate_layer_get_children ( VikAggregateLayer *val )
497{
498 return val->children;
499}
500
501gboolean vik_aggregate_layer_is_empty ( VikAggregateLayer *val )
502{
503 if ( val->children )
504 return FALSE;
505 return TRUE;
506}
70a23263
AF
507
508static void aggregate_layer_drag_drop_request ( VikAggregateLayer *val_src, VikAggregateLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
509{
510 VikTreeview *vt = VIK_LAYER(val_src)->vt;
511 VikLayer *vl = vik_treeview_item_get_pointer(vt, src_item_iter);
e673b75f 512 GtkTreeIter dest_iter;
c390aa71
AF
513 gchar *dp;
514 gboolean target_exists;
e673b75f 515
c390aa71
AF
516 dp = gtk_tree_path_to_string(dest_path);
517 target_exists = vik_treeview_get_iter_from_path_str(vt, &dest_iter, dp);
801ce684
EB
518
519 /* vik_aggregate_layer_delete unrefs, but we don't want that here.
520 * we're still using the layer. */
521 g_object_ref ( vl );
522 vik_aggregate_layer_delete(val_src, src_item_iter);
523
c390aa71 524 if (target_exists) {
e673b75f
AF
525 vik_aggregate_layer_insert_layer(val_dest, vl, &dest_iter);
526 } else {
527 vik_aggregate_layer_insert_layer(val_dest, vl, NULL); /* append */
528 }
c390aa71 529 g_free(dp);
70a23263
AF
530}
531