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