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