+ gint x, y;
+ if ( !wp->visible )
+ return;
+
+ vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
+
+ if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
+ ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
+ abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
+ {
+ params->closest_wp_name = name;
+ params->closest_wp = wp;
+ params->closest_x = x;
+ params->closest_y = y;
+ }
+}
+
+static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
+{
+ GList *tpl = t->trackpoints;
+ VikTrackpoint *tp;
+
+ if ( !t->visible )
+ return;
+
+ while (tpl)
+ {
+ gint x, y;
+ tp = VIK_TRACKPOINT(tpl->data);
+
+ vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
+
+ if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
+ ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
+ abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
+ {
+ params->closest_track_name = name;
+ params->closest_tp = tp;
+ params->closest_tpl = tpl;
+ params->closest_x = x;
+ params->closest_y = y;
+ }
+ tpl = tpl->next;
+ }
+}
+
+static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
+{
+ TPSearchParams params;
+ params.x = x;
+ params.y = y;
+ params.vvp = vvp;
+ params.closest_track_name = NULL;
+ params.closest_tp = NULL;
+ g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
+ return params.closest_tp;
+}
+
+static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
+{
+ WPSearchParams params;
+ params.x = x;
+ params.y = y;
+ params.vvp = vvp;
+ params.closest_wp = NULL;
+ params.closest_wp_name = NULL;
+ g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
+ return params.closest_wp;
+}
+
+/* background drawing hook, to be passed the viewport */
+static gboolean tool_sync_done = TRUE;
+
+static gboolean tool_sync(gpointer data)
+{
+ VikViewport *vvp = data;
+ gdk_threads_enter();
+ vik_viewport_sync(vvp);
+ tool_sync_done = TRUE;
+ gdk_threads_leave();
+ return FALSE;
+}
+
+typedef struct {
+ VikViewport *vvp;
+ gboolean holding;
+ GdkGC *gc;
+ int oldx, oldy;
+} tool_ed_t;
+
+static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
+{
+ t->holding = TRUE;
+ t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
+ gdk_gc_set_function ( t->gc, GDK_INVERT );
+ vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
+ vik_viewport_sync(t->vvp);
+ t->oldx = x;
+ t->oldy = y;
+}
+
+static void marker_moveto ( tool_ed_t *t, gint x, gint y )
+{
+ VikViewport *vvp = t->vvp;
+ vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
+ vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
+ t->oldx = x;
+ t->oldy = y;
+
+ if (tool_sync_done) {
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
+ tool_sync_done = FALSE;
+ }
+}
+
+static void marker_end_move ( tool_ed_t *t )
+{
+ vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
+ g_object_unref ( t->gc );
+ t->holding = FALSE;
+}
+
+/*** Edit waypoint ****/
+
+static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
+{
+ tool_ed_t *t = g_new(tool_ed_t, 1);
+ t->vvp = vvp;
+ t->holding = FALSE;
+ return t;
+}
+
+static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
+{
+ WPSearchParams params;
+ tool_ed_t *t = data;
+ VikViewport *vvp = t->vvp;
+
+ if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
+ return FALSE;
+
+ if ( t->holding ) {
+ return TRUE;
+ }
+
+ if ( vtl->current_wp && vtl->current_wp->visible )
+ {
+ /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
+ gint x, y;
+ vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
+
+ if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
+ abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
+ {
+ if ( event->button == 3 )
+ vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
+ else {
+ marker_begin_move(t, event->x, event->y);
+ }
+ return TRUE;
+ }
+ }
+
+ params.vvp = vvp;
+ params.x = event->x;
+ params.y = event->y;
+ params.closest_wp_name = NULL;
+ /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
+ params.closest_wp = NULL;
+ g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
+ if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
+ {
+ /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
+ marker_begin_move(t, event->x, event->y);
+ g_critical("shouldn't be here");
+ exit(1);
+ }
+ else if ( params.closest_wp )
+ {
+ if ( event->button == 3 )
+ vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
+ else
+ vtl->waypoint_rightclick = FALSE;
+
+ vtl->current_wp = params.closest_wp;
+ vtl->current_wp_name = params.closest_wp_name;
+
+ if ( params.closest_wp )
+ vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
+
+ /* could make it so don't update if old WP is off screen and new is null but oh well */
+ vik_layer_emit_update ( VIK_LAYER(vtl) );
+ return TRUE;
+ }
+
+ vtl->current_wp = NULL;
+ vtl->current_wp_name = NULL;
+ vtl->waypoint_rightclick = FALSE;
+ vik_layer_emit_update ( VIK_LAYER(vtl) );
+ return FALSE;
+}
+
+static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
+{
+ tool_ed_t *t = data;
+ VikViewport *vvp = t->vvp;
+
+ if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
+ return FALSE;
+
+ if ( t->holding ) {
+ VikCoord new_coord;
+ vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
+
+ /* snap to TP */
+ if ( event->state & GDK_CONTROL_MASK )
+ {
+ VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
+ if ( tp )
+ new_coord = tp->coord;
+ }
+
+ /* snap to WP */
+ if ( event->state & GDK_SHIFT_MASK )
+ {
+ VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
+ if ( wp && wp != vtl->current_wp )
+ new_coord = wp->coord;
+ }
+
+ {
+ gint x, y;
+ vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
+
+ marker_moveto ( t, x, y );
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
+{
+ tool_ed_t *t = data;
+ VikViewport *vvp = t->vvp;
+
+ if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
+ return FALSE;
+
+ if ( t->holding && event->button == 1 )
+ {
+ VikCoord new_coord;
+ vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
+
+ /* snap to TP */
+ if ( event->state & GDK_CONTROL_MASK )
+ {
+ VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
+ if ( tp )
+ new_coord = tp->coord;
+ }
+
+ /* snap to WP */
+ if ( event->state & GDK_SHIFT_MASK )
+ {
+ VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
+ if ( wp && wp != vtl->current_wp )
+ new_coord = wp->coord;
+ }
+
+ marker_end_move ( t );
+
+ vtl->current_wp->coord = new_coord;
+ vik_layer_emit_update ( VIK_LAYER(vtl) );
+ return TRUE;
+ }
+ /* PUT IN RIGHT PLACE!!! */
+ if ( event->button == 3 && vtl->waypoint_rightclick )
+ {
+ if ( vtl->wp_right_click_menu )
+ gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
+ vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
+ vik_trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_name, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
+ gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
+ vtl->waypoint_rightclick = FALSE;
+ }
+ return FALSE;
+}
+
+/**** Begin track ***/
+static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
+{
+ return vvp;
+}
+
+static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
+{
+ vtl->current_track = NULL;
+ return tool_new_track_click ( vtl, event, vvp );
+}
+
+/*** New track ****/
+
+static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
+{
+ return vvp;
+}
+
+typedef struct {
+ VikTrwLayer *vtl;
+ VikViewport *vvp;
+ gint x1,y1,x2,y2;
+} new_track_move_passalong_t;
+
+/* sync and undraw, but only when we have time */
+static gboolean ct_sync ( gpointer passalong )
+{
+ new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
+ vik_viewport_sync ( p->vvp );
+ gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
+ vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
+ gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
+ p->vtl->ct_sync_done = TRUE;
+ g_free ( p );
+ return FALSE;
+}
+
+static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
+{
+ /* if we haven't sync'ed yet, we don't have time to do more. */
+ if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
+ GList *iter = vtl->current_track->trackpoints;
+ new_track_move_passalong_t *passalong;
+ gint x1, y1;
+
+ while ( iter->next )
+ iter = iter->next;
+ gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
+ vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
+ vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
+ gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
+
+ passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
+ passalong->vtl = vtl;
+ passalong->vvp = vvp;
+ passalong->x1 = x1;
+ passalong->y1 = y1;
+ passalong->x2 = event->x;
+ passalong->y2 = event->y;
+
+ /* this will sync and undraw when we have time to */
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
+ vtl->ct_sync_done = FALSE;
+ return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
+ }
+ return VIK_LAYER_TOOL_ACK;
+}
+
+static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
+{
+ if ( vtl->current_track && event->keyval == GDK_Escape ) {
+ vtl->current_track = NULL;
+ vik_layer_emit_update ( VIK_LAYER(vtl) );
+ return TRUE;
+ } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
+ /* undo */
+ if ( vtl->current_track->trackpoints )
+ {
+ GList *last = g_list_last(vtl->current_track->trackpoints);
+ g_free ( last->data );
+ vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
+ }
+ vik_layer_emit_update ( VIK_LAYER(vtl) );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
+{
+ VikTrackpoint *tp;
+
+ if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
+ return FALSE;
+
+ if ( event->button == 3 && vtl->current_track )
+ {
+ /* undo */
+ if ( vtl->current_track->trackpoints )
+ {
+ GList *last = g_list_last(vtl->current_track->trackpoints);
+ g_free ( last->data );
+ vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
+ }
+ vik_layer_emit_update ( VIK_LAYER(vtl) );
+ return TRUE;
+ }
+
+ if ( event->type == GDK_2BUTTON_PRESS )
+ {
+ /* subtract last (duplicate from double click) tp then end */
+ if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
+ {
+ GList *last = g_list_last(vtl->current_track->trackpoints);
+ g_free ( last->data );
+ vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
+ /* undo last, then end */
+ vtl->current_track = NULL;
+ }
+ vik_layer_emit_update ( VIK_LAYER(vtl) );
+ return TRUE;
+ }
+
+ if ( ! vtl->current_track )
+ {
+ gchar *name;
+ if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks ) ) )
+ {
+ vtl->current_track = vik_track_new();
+ vtl->current_track->visible = TRUE;
+ vik_trw_layer_add_track ( vtl, name, vtl->current_track );
+
+ /* incase it was created by begin track */
+ vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
+ }
+ else
+ return TRUE;
+ }
+ tp = vik_trackpoint_new();
+ vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
+
+ /* snap to other TP */
+ if ( event->state & GDK_CONTROL_MASK )
+ {
+ VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
+ if ( other_tp )
+ tp->coord = other_tp->coord;
+ }
+
+ tp->newsegment = FALSE;
+ tp->has_timestamp = FALSE;
+ tp->timestamp = 0;
+ vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
+
+ vtl->ct_x1 = vtl->ct_x2;
+ vtl->ct_y1 = vtl->ct_y2;
+ vtl->ct_x2 = event->x;
+ vtl->ct_y2 = event->y;
+
+ vik_layer_emit_update ( VIK_LAYER(vtl) );
+ return TRUE;
+}
+
+
+/*** New waypoint ****/
+
+static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
+{
+ return vvp;
+}
+
+static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
+{
+ VikCoord coord;
+ if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
+ return FALSE;
+ vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
+ if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
+ vik_layer_emit_update ( VIK_LAYER(vtl) );
+ return TRUE;
+}
+
+
+/*** Edit trackpoint ****/
+
+static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
+{
+ tool_ed_t *t = g_new(tool_ed_t, 1);
+ t->vvp = vvp;
+ t->holding = FALSE;
+ return t;
+}
+
+static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
+{
+ tool_ed_t *t = data;
+ VikViewport *vvp = t->vvp;
+ TPSearchParams params;
+ /* OUTDATED DOCUMENTATION:
+ find 5 pixel range on each side. then put these UTM, and a pointer
+ to the winning track name (and maybe the winning track itself), and a
+ pointer to the winning trackpoint, inside an array or struct. pass