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