2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2006, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2012, Rob Norris <rw_norris@hotmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "background.h"
31 #include "datasources.h"
36 #include "preferences.h"
37 #include "icons/icons.h"
38 #include "vikexttools.h"
39 #include "garminsymbols.h"
52 #include <glib/gstdio.h>
53 #include <glib/gprintf.h>
54 #include <glib/gi18n.h>
56 #include <gdk/gdkkeysyms.h>
58 #define VIKING_WINDOW_WIDTH 1000
59 #define VIKING_WINDOW_HEIGHT 800
60 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
61 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
62 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
64 static void window_finalize ( GObject *gob );
65 static GObjectClass *parent_class;
67 static void window_init ( VikWindow *vw );
68 static void window_class_init ( VikWindowClass *klass );
69 static void window_set_filename ( VikWindow *vw, const gchar *filename );
71 static void draw_update ( VikWindow *vw );
73 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
77 static gboolean delete_event( VikWindow *vw );
79 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
81 static void window_configure_event ( VikWindow *vw );
82 static void draw_sync ( VikWindow *vw );
83 static void draw_redraw ( VikWindow *vw );
84 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
85 static void draw_click ( VikWindow *vw, GdkEventButton *event );
86 static void draw_release ( VikWindow *vw, GdkEventButton *event );
87 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
88 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
89 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
91 static void draw_status ( VikWindow *vw );
93 /* End Drawing Functions */
95 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
96 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
97 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
105 #define TOOL_LAYER_TYPE_NONE -1
110 toolbox_tool_t *tools;
114 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
115 static toolbox_tools_t* toolbox_create(VikWindow *vw);
116 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
117 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
118 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
119 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
120 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
121 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
122 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
126 static void window_create_ui( VikWindow *window );
127 static void register_vik_icons (GtkIconFactory *icon_factory);
130 static void load_file ( GtkAction *a, VikWindow *vw );
131 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
132 static gboolean save_file ( GtkAction *a, VikWindow *vw );
133 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
134 static gboolean window_save ( VikWindow *vw );
138 VikViewport *viking_vvp;
139 VikLayersPanel *viking_vlp;
140 VikStatusbar *viking_vs;
144 GtkItemFactory *item_factory;
146 /* tool management state */
149 guint16 tool_layer_id;
150 guint16 tool_tool_id;
152 GtkActionGroup *action_group;
157 guint draw_image_width, draw_image_height;
158 gboolean draw_image_save_as_png;
163 GtkWidget *open_dia, *save_dia;
164 GtkWidget *save_img_dia, *save_img_dir_dia;
166 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
169 /* half-drawn update */
171 VikCoord trigger_center;
173 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
174 /* Only one of these items can be selected at the same time */
175 gpointer selected_vtl; /* notionally VikTrwLayer */
176 gpointer selected_tracks; /* notionally GList */
177 gpointer selected_track; /* notionally VikTrack */
178 gpointer selected_waypoints; /* notionally GList */
179 gpointer selected_waypoint; /* notionally VikWaypoint */
180 /* only use for individual track or waypoint */
181 ////// NEED TO THINK ABOUT VALIDITY OF THESE //////
182 ////// i.e. what happens when stuff is deleted elsewhere //////
183 ////// Generally seems alright as can not access them //////
184 ////// containing_vtl now seems unecessary //////
185 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
186 gpointer containing_vtl; /* notionally VikTrwLayer */
200 VW_OPENWINDOW_SIGNAL,
201 VW_STATUSBAR_UPDATE_SIGNAL,
205 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
207 // TODO get rid of this as this is unnecessary duplication...
208 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
210 GType vik_window_get_type (void)
212 static GType vw_type = 0;
216 static const GTypeInfo vw_info =
218 sizeof (VikWindowClass),
219 NULL, /* base_init */
220 NULL, /* base_finalize */
221 (GClassInitFunc) window_class_init, /* class_init */
222 NULL, /* class_finalize */
223 NULL, /* class_data */
226 (GInstanceInitFunc) window_init,
228 vw_type = g_type_register_static ( GTK_TYPE_WINDOW, "VikWindow", &vw_info, 0 );
234 VikViewport * vik_window_viewport(VikWindow *vw)
236 return(vw->viking_vvp);
239 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
241 return(vw->viking_vlp);
245 * Returns the statusbar for the window
247 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
249 return vw->viking_vs;
253 * For signalling the update from a background thread
255 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message )
257 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message );
261 * For the actual statusbar update!
263 static gboolean statusbar_idle_update ( gpointer indata )
265 gpointer *data = indata;
266 vik_statusbar_set_message ( data[0], VIK_STATUSBAR_ITEMS, data[1] );
271 * Update statusbar in the main thread
273 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message )
275 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
276 static gpointer data[2];
277 data[0] = vw->viking_vs;
278 data[1] = (gchar*) message;
279 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
282 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
284 int i, j, tool_count;
285 VikLayerInterface *layer_interface;
287 if (!vw->action_group) return;
289 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
291 layer_interface = vik_layer_get_interface(i);
292 tool_count = layer_interface->tools_count;
294 for (j = 0; j < tool_count; j++) {
295 action = gtk_action_group_get_action(vw->action_group,
296 layer_interface->tools[j].radioActionEntry.name);
297 g_object_set(action, "sensitive", i == vl->type, NULL);
302 static void window_finalize ( GObject *gob )
304 VikWindow *vw = VIK_WINDOW(gob);
305 g_return_if_fail ( vw != NULL );
307 a_background_remove_window ( vw );
309 G_OBJECT_CLASS(parent_class)->finalize(gob);
313 static void window_class_init ( VikWindowClass *klass )
316 GObjectClass *object_class;
318 window_signals[VW_NEWWINDOW_SIGNAL] = g_signal_new ( "newwindow", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, newwindow), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
319 window_signals[VW_OPENWINDOW_SIGNAL] = g_signal_new ( "openwindow", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, openwindow), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
320 window_signals[VW_STATUSBAR_UPDATE_SIGNAL] = g_signal_new ( "statusbarupdate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, statusbarupdate), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
322 object_class = G_OBJECT_CLASS (klass);
324 object_class->finalize = window_finalize;
326 parent_class = g_type_class_peek_parent (klass);
330 static void window_init ( VikWindow *vw )
332 GtkWidget *main_vbox;
335 vw->action_group = NULL;
337 vw->viking_vvp = vik_viewport_new();
338 vw->viking_vlp = vik_layers_panel_new();
339 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
340 vw->viking_vs = vik_statusbar_new();
342 vw->vt = toolbox_create(vw);
343 window_create_ui(vw);
344 window_set_filename (vw, NULL);
345 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
347 // Set the default tool
348 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
351 vw->item_factory = NULL;
353 vw->modified = FALSE;
354 vw->only_updating_coord_mode_ui = FALSE;
356 vw->pan_move = FALSE;
357 vw->pan_x = vw->pan_y = -1;
358 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
359 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
360 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
362 main_vbox = gtk_vbox_new(FALSE, 1);
363 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
365 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
366 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
367 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
368 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
370 vik_ext_tools_add_menu_items ( vw, vw->uim );
372 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
374 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
375 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
376 gtk_widget_add_events ( GTK_WIDGET(vw->viking_vvp), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK );
377 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
378 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
379 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
380 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
381 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
383 g_signal_connect_swapped (G_OBJECT (vw->viking_vvp), "key_press_event", G_CALLBACK (key_press_event), vw);
385 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
387 hpaned = gtk_hpaned_new ();
388 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
389 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
391 /* This packs the button into the window (a gtk container). */
392 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
394 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
396 a_background_add_window ( vw );
400 vw->save_img_dia = NULL;
401 vw->save_img_dir_dia = NULL;
404 VikWindow *vik_window_new ()
406 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
409 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
411 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
412 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
413 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
414 if ( vl && ltype == vl->type )
415 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
418 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
419 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
420 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
423 /* Restore Main Menu via Escape key if the user has hidden it */
424 /* This key is more likely to be used as they may not remember the function key */
425 if ( event->keyval == GDK_Escape ) {
426 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
428 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
430 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
431 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
432 return TRUE; /* handled keypress */
437 return FALSE; /* don't handle the keypress */
440 static gboolean delete_event( VikWindow *vw )
442 #ifdef VIKING_PROMPT_IF_MODIFIED
449 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
450 _("Do you want to save the changes you made to the document \"%s\"?\n"
452 "Your changes will be lost if you don't save them."),
453 vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") ) );
454 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
455 switch ( gtk_dialog_run ( dia ) )
457 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
458 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
459 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
466 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
468 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
471 static void draw_update ( VikWindow *vw )
477 static void draw_sync ( VikWindow *vw )
479 vik_viewport_sync(vw->viking_vvp);
481 /* other things may be necc here later. */
484 static void draw_status ( VikWindow *vw )
486 static gchar zoom_level[22];
487 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
488 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
489 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
491 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
493 if ( (int)xmpp - xmpp < 0.0 )
494 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
496 /* xmpp should be a whole number so don't show useless .000 bit */
497 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
498 if ( vw->current_tool == TOOL_LAYER )
499 // Use tooltip rather than the internal name as the tooltip is i8n
500 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, vik_layer_get_interface(vw->tool_layer_id)->tools[vw->tool_tool_id].radioActionEntry.tooltip );
502 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
504 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
507 void vik_window_set_redraw_trigger(VikLayer *vl)
509 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
514 static void window_configure_event ( VikWindow *vw )
516 static int first = 1;
519 // This is a hack to set the cursor corresponding to the first tool
520 // FIXME find the correct way to initialize both tool and its cursor
521 const GdkCursor *cursor = NULL;
523 cursor = toolbox_get_cursor(vw->vt, "Pan");
524 /* We set cursor, even if it is NULL: it resets to default */
525 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
529 static void draw_redraw ( VikWindow *vw )
531 VikCoord old_center = vw->trigger_center;
532 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
533 VikLayer *new_trigger = vw->trigger;
535 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
538 ; /* do nothing -- have to redraw everything. */
539 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
540 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
542 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
545 vik_viewport_clear ( vw->viking_vvp);
546 vik_layers_panel_draw_all ( vw->viking_vlp );
547 vik_viewport_draw_scale ( vw->viking_vvp );
548 vik_viewport_draw_copyright ( vw->viking_vvp );
549 vik_viewport_draw_centermark ( vw->viking_vvp );
550 vik_viewport_draw_logo ( vw->viking_vvp );
552 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
555 gboolean draw_buf_done = TRUE;
557 static gboolean draw_buf(gpointer data)
559 gpointer *pass_along = data;
561 gdk_draw_drawable (pass_along[0], pass_along[1],
562 pass_along[2], 0, 0, 0, 0, -1, -1);
563 draw_buf_done = TRUE;
569 /* Mouse event handlers ************************************************************************/
571 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
573 /* set panning origin */
574 vw->pan_move = FALSE;
575 vw->pan_x = (gint) event->x;
576 vw->pan_y = (gint) event->y;
579 static void draw_click (VikWindow *vw, GdkEventButton *event)
581 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
583 /* middle button pressed. we reserve all middle button and scroll events
584 * for panning and zooming; tools only get left/right/movement
586 if ( event->button == 2) {
587 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
588 // Tool still may need to do something (such as disable something)
589 toolbox_click(vw->vt, event);
590 vik_window_pan_click ( vw, event );
593 toolbox_click(vw->vt, event);
597 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
599 if ( vw->pan_x != -1 ) {
600 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
601 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
603 vw->pan_x = event->x;
604 vw->pan_y = event->y;
609 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
611 static VikCoord coord;
612 static struct UTM utm;
613 static struct LatLon ll;
614 #define BUFFER_SIZE 50
615 static char pointer_buf[BUFFER_SIZE];
616 gchar *lat = NULL, *lon = NULL;
619 VikDemInterpol interpol_method;
621 /* This is a hack, but work far the best, at least for single pointer systems.
622 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
624 gdk_window_get_pointer (event->window, &x, &y, NULL);
628 toolbox_move(vw->vt, event);
630 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
631 vik_coord_to_utm ( &coord, &utm );
632 a_coords_utm_to_latlon ( &utm, &ll );
633 a_coords_latlon_to_string ( &ll, &lat, &lon );
634 /* Change interpolate method according to scale */
635 zoom = vik_viewport_get_zoom(vw->viking_vvp);
637 interpol_method = VIK_DEM_INTERPOL_NONE;
638 else if (zoom >= 1.0)
639 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
641 interpol_method = VIK_DEM_INTERPOL_BEST;
642 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
643 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
644 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
646 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
649 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
654 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
656 vik_window_pan_move ( vw, event );
658 /* This is recommended by the GTK+ documentation, but does not work properly.
659 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
660 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
662 /* gdk_event_request_motions ( event ); */
665 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
667 if ( vw->pan_move == FALSE )
668 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
670 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
671 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
672 vw->pan_move = FALSE;
673 vw->pan_x = vw->pan_y = -1;
677 static void draw_release ( VikWindow *vw, GdkEventButton *event )
679 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
681 if ( event->button == 2 ) { /* move / pan */
682 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
683 // Tool still may need to do something (such as reenable something)
684 toolbox_release(vw->vt, event);
685 vik_window_pan_release ( vw, event );
688 toolbox_release(vw->vt, event);
692 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
694 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
695 if ( modifiers == GDK_CONTROL_MASK ) {
696 /* control == pan up & down */
697 if ( event->direction == GDK_SCROLL_UP )
698 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
700 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)*2/3 );
701 } else if ( modifiers == GDK_SHIFT_MASK ) {
702 /* shift == pan left & right */
703 if ( event->direction == GDK_SCROLL_UP )
704 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
706 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)*2/3, vik_viewport_get_height(vw->viking_vvp)/2 );
707 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
708 // This zoom is on the center position
709 if ( event->direction == GDK_SCROLL_UP )
710 vik_viewport_zoom_in (vw->viking_vvp);
712 vik_viewport_zoom_out (vw->viking_vvp);
714 /* make sure mouse is still over the same point on the map when we zoom */
717 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
718 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
719 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
720 if ( event->direction == GDK_SCROLL_UP )
721 vik_viewport_zoom_in (vw->viking_vvp);
723 vik_viewport_zoom_out(vw->viking_vvp);
724 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
725 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
726 center_y + (y - event->y) );
734 /********************************************************************************
736 ********************************************************************************/
737 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
739 PangoFontDescription *pfd;
742 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
743 GdkGC *thickgc = gdk_gc_new(d);
745 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
746 gdouble dx = (x2-x1)/len*10;
747 gdouble dy = (y2-y1)/len*10;
748 gdouble c = cos(15.0 * M_PI/180.0);
749 gdouble s = sin(15.0 * M_PI/180.0);
751 gdouble baseangle = 0;
754 /* draw line with arrow ends */
756 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
757 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
758 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
761 a_viewport_clip_line(&x1, &y1, &x2, &y2);
762 gdk_draw_line(d, gc, x1, y1, x2, y2);
764 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
765 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
766 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
767 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
768 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
769 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
774 angle = atan2(dy, dx) + M_PI_2;
776 if ( vik_viewport_get_drawmode ( vvp ) == VIK_VIEWPORT_DRAWMODE_UTM) {
782 vik_viewport_screen_to_coord ( vvp, x1, y1, &test );
783 vik_coord_to_latlon ( &test, &ll );
784 ll.lat += vik_viewport_get_ympp ( vvp ) * vik_viewport_get_height ( vvp ) / 11000.0; // about 11km per degree latitude
785 a_coords_latlon_to_utm ( &ll, &u );
786 vik_coord_load_from_utm ( &test, VIK_VIEWPORT_DRAWMODE_UTM, &u );
787 vik_viewport_coord_to_screen ( vvp, &test, &tx, &ty );
789 baseangle = M_PI - atan2(tx-x1, ty-y1);
800 gdk_gc_copy(thickgc, gc);
801 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
802 gdk_color_parse("#2255cc", &color);
803 gdk_gc_set_rgb_fg_color(thickgc, &color);
805 gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - baseangle*180/M_PI)*64, -angle*180/M_PI*64);
808 gdk_gc_copy(thickgc, gc);
809 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
810 for (i=0; i<180; i++) {
811 c = cos(i*M_PI/90.0 + baseangle);
812 s = sin(i*M_PI/90.0 + baseangle);
815 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
817 gdouble ticksize = 2*CW;
818 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
822 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
823 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
824 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
825 c = (CR+CW*2)*cos(baseangle);
826 s = (CR+CW*2)*sin(baseangle);
827 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
828 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
831 #define LABEL(x, y, w, h) { \
832 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
833 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
834 gdk_draw_layout(d, gc, (x), (y), pl); }
839 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
841 pfd = pango_font_description_from_string ("Sans 8"); // FIXME: settable option? global variable?
842 pango_layout_set_font_description (pl, pfd);
843 pango_font_description_free (pfd);
845 pango_layout_set_text(pl, "N", -1);
846 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
848 /* draw label with distance */
849 vik_units_distance_t dist_units = a_vik_get_units_distance ();
850 switch (dist_units) {
851 case VIK_UNITS_DISTANCE_KILOMETRES:
852 if (distance >= 1000 && distance < 100000) {
853 g_sprintf(str, "%3.2f km", distance/1000.0);
854 } else if (distance < 1000) {
855 g_sprintf(str, "%d m", (int)distance);
857 g_sprintf(str, "%d km", (int)distance/1000);
860 case VIK_UNITS_DISTANCE_MILES:
861 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
862 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
863 } else if (distance < VIK_MILES_TO_METERS(1)) {
864 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
866 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
870 g_critical("Houston, we've had a problem. distance=%d", dist_units);
873 pango_layout_set_text(pl, str, -1);
875 pango_layout_get_pixel_size ( pl, &wd, &hd );
878 yd = (y1+y2)/2 - hd/2 - dx;
881 yd = (y1+y2)/2 - hd/2 + dx;
884 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
889 LABEL(xd, yd, wd, hd);
891 /* draw label with bearing */
892 g_sprintf(str, "%3.1f°", angle*180.0/M_PI);
893 pango_layout_set_text(pl, str, -1);
894 pango_layout_get_pixel_size ( pl, &wb, &hb );
895 xb = x1 + CR*cos(angle-M_PI_2);
896 yb = y1 + CR*sin(angle-M_PI_2);
898 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
904 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
905 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
909 LABEL(xb, yb, wb, hb);
913 g_object_unref ( G_OBJECT ( pl ) );
914 g_object_unref ( G_OBJECT ( labgc ) );
915 g_object_unref ( G_OBJECT ( thickgc ) );
921 gboolean has_oldcoord;
923 } ruler_tool_state_t;
925 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
927 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
930 s->has_oldcoord = FALSE;
934 static void ruler_destroy (ruler_tool_state_t *s)
939 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
944 if ( event->button == 1 ) {
945 gchar *lat=NULL, *lon=NULL;
946 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
947 vik_coord_to_latlon ( &coord, &ll );
948 a_coords_latlon_to_string ( &ll, &lat, &lon );
949 if ( s->has_oldcoord ) {
950 vik_units_distance_t dist_units = a_vik_get_units_distance ();
951 switch (dist_units) {
952 case VIK_UNITS_DISTANCE_KILOMETRES:
953 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
955 case VIK_UNITS_DISTANCE_MILES:
956 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
959 temp = g_strdup_printf ("Just to keep the compiler happy");
960 g_critical("Houston, we've had a problem. distance=%d", dist_units);
963 s->has_oldcoord = FALSE;
966 temp = g_strdup_printf ( "%s %s", lat, lon );
967 s->has_oldcoord = TRUE;
970 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
976 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
977 draw_update ( s->vw );
979 return VIK_LAYER_TOOL_ACK;
982 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
984 VikViewport *vvp = s->vvp;
985 VikWindow *vw = s->vw;
991 if ( s->has_oldcoord ) {
992 int oldx, oldy, w1, h1, w2, h2;
993 static GdkPixmap *buf = NULL;
994 gchar *lat=NULL, *lon=NULL;
995 w1 = vik_viewport_get_width(vvp);
996 h1 = vik_viewport_get_height(vvp);
998 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1000 gdk_drawable_get_size(buf, &w2, &h2);
1001 if (w1 != w2 || h1 != h2) {
1002 g_object_unref ( G_OBJECT ( buf ) );
1003 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1006 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1007 vik_coord_to_latlon ( &coord, &ll );
1008 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1010 gdk_draw_drawable (buf, GTK_WIDGET(vvp)->style->black_gc,
1011 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1012 draw_ruler(vvp, buf, GTK_WIDGET(vvp)->style->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1013 if (draw_buf_done) {
1014 static gpointer pass_along[3];
1015 pass_along[0] = GTK_WIDGET(vvp)->window;
1016 pass_along[1] = GTK_WIDGET(vvp)->style->black_gc;
1017 pass_along[2] = buf;
1018 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1019 draw_buf_done = FALSE;
1021 a_coords_latlon_to_string(&ll, &lat, &lon);
1022 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1023 switch (dist_units) {
1024 case VIK_UNITS_DISTANCE_KILOMETRES:
1025 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1027 case VIK_UNITS_DISTANCE_MILES:
1028 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1031 temp = g_strdup_printf ("Just to keep the compiler happy");
1032 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1034 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1037 return VIK_LAYER_TOOL_ACK;
1040 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1042 return VIK_LAYER_TOOL_ACK;
1045 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1047 draw_update ( s->vw );
1050 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1052 if (event->keyval == GDK_Escape) {
1053 s->has_oldcoord = FALSE;
1054 ruler_deactivate ( vl, s );
1060 static VikToolInterface ruler_tool =
1061 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>R", N_("Ruler Tool"), 2 },
1062 (VikToolConstructorFunc) ruler_create,
1063 (VikToolDestructorFunc) ruler_destroy,
1064 (VikToolActivationFunc) NULL,
1065 (VikToolActivationFunc) ruler_deactivate,
1066 (VikToolMouseFunc) ruler_click,
1067 (VikToolMouseMoveFunc) ruler_move,
1068 (VikToolMouseFunc) ruler_release,
1069 (VikToolKeyFunc) ruler_key_press,
1071 GDK_CURSOR_IS_PIXMAP,
1072 &cursor_ruler_pixbuf };
1073 /*** end ruler code ********************************************************/
1077 /********************************************************************************
1079 ********************************************************************************/
1084 // Track zoom bounds for zoom tool with shift modifier:
1085 gboolean bounds_active;
1088 } zoom_tool_state_t;
1091 * In case the screen size has changed
1093 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1097 // Allocate a drawing area the size of the viewport
1098 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1099 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1101 if ( !zts->pixmap ) {
1103 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1106 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1108 if ( w1 != w2 || h1 != h2 ) {
1109 // Has changed - delete and recreate with new values
1110 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1111 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1115 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1117 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1122 zts->bounds_active = FALSE;
1126 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1129 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1133 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1135 zts->vw->modified = TRUE;
1136 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1140 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1141 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1143 gboolean skip_update = FALSE;
1145 zts->bounds_active = FALSE;
1147 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1148 // This zoom is on the center position
1149 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1150 if ( event->button == 1 )
1151 vik_viewport_zoom_in (zts->vw->viking_vvp);
1152 else if ( event->button == 3 )
1153 vik_viewport_zoom_out (zts->vw->viking_vvp);
1155 else if ( modifiers == GDK_CONTROL_MASK ) {
1156 // This zoom is to recenter on the mouse position
1157 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1158 if ( event->button == 1 )
1159 vik_viewport_zoom_in (zts->vw->viking_vvp);
1160 else if ( event->button == 3 )
1161 vik_viewport_zoom_out (zts->vw->viking_vvp);
1163 else if ( modifiers == GDK_SHIFT_MASK ) {
1164 // Get start of new zoom bounds
1165 if ( event->button == 1 ) {
1166 zts->bounds_active = TRUE;
1167 zts->start_x = (gint) event->x;
1168 zts->start_y = (gint) event->y;
1173 /* make sure mouse is still over the same point on the map when we zoom */
1174 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1175 if ( event->button == 1 )
1176 vik_viewport_zoom_in (zts->vw->viking_vvp);
1177 else if ( event->button == 3 )
1178 vik_viewport_zoom_out(zts->vw->viking_vvp);
1179 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1180 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1181 center_x + (x - event->x),
1182 center_y + (y - event->y) );
1186 draw_update ( zts->vw );
1188 return VIK_LAYER_TOOL_ACK;
1191 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1193 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1195 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1196 zoomtool_resize_pixmap ( zts );
1198 // Blank out currently drawn area
1199 gdk_draw_drawable ( zts->pixmap,
1200 GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc,
1201 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1202 0, 0, 0, 0, -1, -1);
1204 // Calculate new box starting point & size in pixels
1205 int xx, yy, width, height;
1206 if ( event->y > zts->start_y ) {
1208 height = event->y-zts->start_y;
1212 height = zts->start_y-event->y;
1214 if ( event->x > zts->start_x ) {
1216 width = event->x-zts->start_x;
1220 width = zts->start_x-event->x;
1224 gdk_draw_rectangle (zts->pixmap, GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, FALSE, xx, yy, width, height);
1226 // Only actually draw when there's time to do so
1227 if (draw_buf_done) {
1228 static gpointer pass_along[3];
1229 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1230 pass_along[1] = GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc;
1231 pass_along[2] = zts->pixmap;
1232 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1233 draw_buf_done = FALSE;
1236 return VIK_LAYER_TOOL_ACK;
1239 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1241 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1243 zts->bounds_active = FALSE;
1245 // Ensure haven't just released on the exact same position
1246 // i.e. probably haven't moved the mouse at all
1247 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1249 VikCoord coord1, coord2;
1250 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1251 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1253 // From the extend of the bounds pick the best zoom level
1254 // c.f. trw_layer_zoom_to_show_latlons()
1255 // Maybe refactor...
1256 struct LatLon ll1, ll2;
1257 vik_coord_to_latlon(&coord1, &ll1);
1258 vik_coord_to_latlon(&coord2, &ll2);
1259 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1260 (ll1.lon+ll2.lon)/2 };
1262 VikCoord new_center;
1263 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1264 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1266 /* Convert into definite 'smallest' and 'largest' positions */
1267 struct LatLon minmin;
1268 if ( ll1.lat < ll2.lat )
1269 minmin.lat = ll1.lat;
1271 minmin.lat = ll2.lat;
1273 struct LatLon maxmax;
1274 if ( ll1.lon > ll2.lon )
1275 maxmax.lon = ll1.lon;
1277 maxmax.lon = ll2.lon;
1279 /* Always recalculate the 'best' zoom level */
1280 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1281 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1283 gdouble min_lat, max_lat, min_lon, max_lon;
1284 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1285 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1286 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1287 /* NB I think the logic used in this test to determine if the bounds is within view
1288 fails if track goes across 180 degrees longitude.
1289 Hopefully that situation is not too common...
1290 Mind you viking doesn't really do edge locations to well anyway */
1291 if ( min_lat < minmin.lat &&
1292 max_lat > minmin.lat &&
1293 min_lon < maxmax.lon &&
1294 max_lon > maxmax.lon )
1295 /* Found within zoom level */
1300 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1303 draw_update ( zts->vw );
1305 return VIK_LAYER_TOOL_ACK;
1308 static VikToolInterface zoom_tool =
1309 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1310 (VikToolConstructorFunc) zoomtool_create,
1311 (VikToolDestructorFunc) zoomtool_destroy,
1312 (VikToolActivationFunc) NULL,
1313 (VikToolActivationFunc) NULL,
1314 (VikToolMouseFunc) zoomtool_click,
1315 (VikToolMouseMoveFunc) zoomtool_move,
1316 (VikToolMouseFunc) zoomtool_release,
1319 GDK_CURSOR_IS_PIXMAP,
1320 &cursor_zoom_pixbuf };
1321 /*** end zoom code ********************************************************/
1323 /********************************************************************************
1325 ********************************************************************************/
1326 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1331 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1333 vw->modified = TRUE;
1334 if ( event->button == 1 )
1335 vik_window_pan_click ( vw, event );
1337 return VIK_LAYER_TOOL_ACK;
1340 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1342 vik_window_pan_move ( vw, event );
1343 return VIK_LAYER_TOOL_ACK;
1346 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1348 if ( event->button == 1 )
1349 vik_window_pan_release ( vw, event );
1350 return VIK_LAYER_TOOL_ACK;
1353 static VikToolInterface pan_tool =
1354 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1355 (VikToolConstructorFunc) pantool_create,
1356 (VikToolDestructorFunc) NULL,
1357 (VikToolActivationFunc) NULL,
1358 (VikToolActivationFunc) NULL,
1359 (VikToolMouseFunc) pantool_click,
1360 (VikToolMouseMoveFunc) pantool_move,
1361 (VikToolMouseFunc) pantool_release,
1365 /*** end pan code ********************************************************/
1367 /********************************************************************************
1369 ********************************************************************************/
1370 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1372 tool_ed_t *t = g_new(tool_ed_t, 1);
1376 t->is_waypoint = FALSE;
1380 static void selecttool_destroy (tool_ed_t *t)
1388 GdkEventButton *event;
1389 tool_ed_t *tool_edit;
1392 static void click_layer_selected (VikLayer *vl, clicker *ck)
1394 /* Do nothing when function call returns true; */
1395 /* i.e. stop on first found item */
1398 if ( vik_layer_get_interface(vl->type)->select_click )
1399 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1402 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1404 /* Only allow selection on primary button */
1405 if ( event->button == 1 ) {
1406 /* Enable click to apply callback to potentially all track/waypoint layers */
1407 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1408 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1411 ck.vvp = t->vw->viking_vvp;
1414 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1417 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1420 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1422 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1423 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1424 gint type = vik_treeview_item_get_type ( vtv, &iter );
1425 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1426 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1428 vik_treeview_item_unselect ( vtv, &iter );
1429 if ( vik_window_clear_highlight ( t->vw ) )
1430 draw_update ( t->vw );
1435 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1437 /* Act on currently selected item to show menu */
1438 if ( t->vw->selected_track || t->vw->selected_waypoint )
1439 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1440 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1443 return VIK_LAYER_TOOL_ACK;
1446 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1448 /* Only allow selection on primary button */
1449 if ( event->button == 1 ) {
1450 // Don't care about vl here
1452 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1453 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1455 return VIK_LAYER_TOOL_ACK;
1458 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1460 /* Only allow selection on primary button */
1461 if ( event->button == 1 ) {
1462 // Don't care about vl here
1464 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1465 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1467 return VIK_LAYER_TOOL_ACK;
1470 static VikToolInterface select_tool =
1471 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1472 (VikToolConstructorFunc) selecttool_create,
1473 (VikToolDestructorFunc) selecttool_destroy,
1474 (VikToolActivationFunc) NULL,
1475 (VikToolActivationFunc) NULL,
1476 (VikToolMouseFunc) selecttool_click,
1477 (VikToolMouseMoveFunc) selecttool_move,
1478 (VikToolMouseFunc) selecttool_release,
1479 (VikToolKeyFunc) NULL,
1484 /*** end select tool code ********************************************************/
1486 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1488 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1489 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1490 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1491 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1492 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1493 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1494 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1495 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1500 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1502 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1503 g_assert(check_box);
1504 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1506 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1508 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1511 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1515 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1518 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1521 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1524 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1528 gchar *s = (gchar *)gtk_action_get_name(a);
1534 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1535 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1536 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1537 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1538 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1543 void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1545 VikCoord new_center;
1547 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1548 struct LatLon ll, llold;
1549 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1550 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1551 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1555 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1556 struct UTM utm, utmold;
1557 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1558 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1559 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1564 g_critical("Houston, we've had a problem.");
1568 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1572 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1575 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1576 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1577 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1579 vw->modified = TRUE;
1585 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1587 a_clipboard_copy_selected ( vw->viking_vlp );
1590 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1592 vik_layers_panel_cut_selected ( vw->viking_vlp );
1593 vw->modified = TRUE;
1596 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1598 if ( a_clipboard_paste ( vw->viking_vlp ) )
1600 vw->modified = TRUE;
1604 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1606 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1607 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1610 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1613 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1615 #if GTK_CHECK_VERSION (2, 14, 0)
1617 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1618 gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, NULL);
1621 #endif /* WINDOWS */
1624 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1626 a_dialog_about(GTK_WINDOW(vw));
1629 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1631 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1633 vik_layers_panel_delete_selected ( vw->viking_vlp );
1634 vw->modified = TRUE;
1637 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1640 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1642 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1643 g_assert(check_box);
1644 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1646 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1648 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1651 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1653 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1656 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1658 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1660 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1663 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1665 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1668 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1670 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1672 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1675 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1677 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1680 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1682 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1684 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1687 /***************************************
1688 ** tool management routines
1690 ***************************************/
1692 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1694 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1697 vt->active_tool = -1;
1699 if (!vw->viking_vvp) {
1700 g_critical("no viewport found.");
1706 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1708 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1709 vt->tools[vt->n_tools].ti = *vti;
1710 vt->tools[vt->n_tools].layer_type = layer_type;
1712 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1715 vt->tools[vt->n_tools].state = NULL;
1720 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1723 for (i=0; i<vt->n_tools; i++) {
1724 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1731 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1733 int tool = toolbox_get_tool(vt, tool_name);
1734 toolbox_tool_t *t = &vt->tools[tool];
1735 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1737 if (tool == vt->n_tools) {
1738 g_critical("trying to activate a non-existent tool...");
1741 /* is the tool already active? */
1742 if (vt->active_tool == tool) {
1746 if (vt->active_tool != -1) {
1747 if (vt->tools[vt->active_tool].ti.deactivate) {
1748 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1751 if (t->ti.activate) {
1752 t->ti.activate(vl, t->state);
1754 vt->active_tool = tool;
1757 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1759 int tool = toolbox_get_tool(vt, tool_name);
1760 toolbox_tool_t *t = &vt->tools[tool];
1761 if (t->ti.cursor == NULL) {
1762 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1763 GError *cursor_load_err = NULL;
1764 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1765 /* TODO: settable offeset */
1766 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1767 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1769 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1772 return t->ti.cursor;
1775 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1777 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1778 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1779 gint ltype = vt->tools[vt->active_tool].layer_type;
1780 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1781 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
1785 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
1787 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1788 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
1789 gint ltype = vt->tools[vt->active_tool].layer_type;
1790 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1791 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
1792 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
1796 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
1798 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1799 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
1800 gint ltype = vt->tools[vt->active_tool].layer_type;
1801 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1802 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
1805 /** End tool management ************************************/
1807 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
1809 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
1812 /* this function gets called whenever a toolbar tool is clicked */
1813 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
1815 /* White Magic, my friends ... White Magic... */
1816 int layer_id, tool_id;
1817 const GdkCursor *cursor = NULL;
1819 toolbox_activate(vw->vt, gtk_action_get_name(a));
1821 cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
1823 if ( GTK_WIDGET(vw->viking_vvp)->window )
1824 /* We set cursor, even if it is NULL: it resets to default */
1825 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
1827 if (!strcmp(gtk_action_get_name(a), "Pan")) {
1828 vw->current_tool = TOOL_PAN;
1830 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
1831 vw->current_tool = TOOL_ZOOM;
1833 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
1834 vw->current_tool = TOOL_RULER;
1836 else if (!strcmp(gtk_action_get_name(a), "Select")) {
1837 vw->current_tool = TOOL_SELECT;
1840 /* TODO: only enable tools from active layer */
1841 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
1842 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
1843 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
1844 vw->current_tool = TOOL_LAYER;
1845 vw->tool_layer_id = layer_id;
1846 vw->tool_tool_id = tool_id;
1854 static void window_set_filename ( VikWindow *vw, const gchar *filename )
1859 g_free ( vw->filename );
1860 if ( filename == NULL )
1862 vw->filename = NULL;
1863 file = _("Untitled");
1867 vw->filename = g_strdup(filename);
1868 file = a_file_basename ( filename );
1870 title = g_strdup_printf( "%s - Viking", file );
1871 gtk_window_set_title ( GTK_WINDOW(vw), title );
1875 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
1877 GtkWidget *mode_button;
1880 #ifdef VIK_CONFIG_EXPEDIA
1881 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
1883 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
1884 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
1885 default: buttonname = "/ui/MainMenu/View/ModeUTM";
1887 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
1888 g_assert ( mode_button );
1893 * vik_window_get_pan_move:
1894 * @vw: some VikWindow
1896 * Retrieves @vw's pan_move.
1898 * Should be removed as soon as possible.
1900 * Returns: @vw's pan_move
1904 gboolean vik_window_get_pan_move ( VikWindow *vw )
1906 return vw->pan_move;
1909 static void on_activate_recent_item (GtkRecentChooser *chooser,
1914 filename = gtk_recent_chooser_get_current_uri (chooser);
1915 if (filename != NULL)
1917 GFile *file = g_file_new_for_uri ( filename );
1918 gchar *path = g_file_get_path ( file );
1919 g_object_unref ( file );
1920 if ( self->filename )
1922 GSList *filenames = NULL;
1923 filenames = g_slist_append ( filenames, path );
1924 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
1925 // NB: GSList & contents are freed by main.open_window
1928 vik_window_open_file ( self, path, TRUE );
1936 static void setup_recent_files (VikWindow *self)
1938 GtkRecentManager *manager;
1939 GtkRecentFilter *filter;
1940 GtkWidget *menu, *menu_item;
1942 filter = gtk_recent_filter_new ();
1943 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
1944 gtk_recent_filter_add_group(filter, "viking");
1946 manager = gtk_recent_manager_get_default ();
1947 menu = gtk_recent_chooser_menu_new_for_manager (manager);
1948 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
1949 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
1951 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
1952 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
1954 g_signal_connect (G_OBJECT (menu), "item-activated",
1955 G_CALLBACK (on_activate_recent_item), (gpointer) self);
1958 static void update_recently_used_document(const gchar *filename)
1960 /* Update Recently Used Document framework */
1961 GtkRecentManager *manager = gtk_recent_manager_get_default();
1962 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
1963 gchar *groups[] = {"viking", NULL};
1964 GFile *file = g_file_new_for_commandline_arg(filename);
1965 gchar *uri = g_file_get_uri(file);
1966 gchar *basename = g_path_get_basename(filename);
1967 g_object_unref(file);
1970 recent_data->display_name = basename;
1971 recent_data->description = NULL;
1972 recent_data->mime_type = "text/x-gps-data";
1973 recent_data->app_name = (gchar *) g_get_application_name ();
1974 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
1975 recent_data->groups = groups;
1976 recent_data->is_private = FALSE;
1977 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
1979 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
1984 g_free (recent_data->app_exec);
1985 g_slice_free (GtkRecentData, recent_data);
1988 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
1990 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
1992 case LOAD_TYPE_READ_FAILURE:
1993 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
1995 case LOAD_TYPE_GPSBABEL_FAILURE:
1996 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
1998 case LOAD_TYPE_GPX_FAILURE:
1999 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2001 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2002 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2004 case LOAD_TYPE_VIK_SUCCESS:
2006 GtkWidget *mode_button;
2008 if ( change_filename )
2009 window_set_filename ( vw, filename );
2010 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2011 vw->only_updating_coord_mode_ui = TRUE; /* if we don't set this, it will change the coord to UTM if we click Lat/Lon. I don't know why. */
2012 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2013 vw->only_updating_coord_mode_ui = FALSE;
2015 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2017 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2018 g_assert ( mode_button );
2019 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2021 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2022 g_assert ( mode_button );
2023 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2025 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2026 g_assert ( mode_button );
2027 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2029 //case LOAD_TYPE_OTHER_SUCCESS:
2031 update_recently_used_document(filename);
2036 static void load_file ( GtkAction *a, VikWindow *vw )
2038 GSList *files = NULL;
2039 GSList *cur_file = NULL;
2041 if (!strcmp(gtk_action_get_name(a), "Open")) {
2044 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2048 g_critical("Houston, we've had a problem.");
2052 if ( ! vw->open_dia )
2054 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2056 GTK_FILE_CHOOSER_ACTION_OPEN,
2057 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2058 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2060 GtkFileFilter *filter;
2061 // NB file filters are listed this way for alphabetical ordering
2062 #ifdef VIK_CONFIG_GEOCACHES
2063 filter = gtk_file_filter_new ();
2064 gtk_file_filter_set_name( filter, _("Geocaching") );
2065 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2066 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2069 filter = gtk_file_filter_new ();
2070 gtk_file_filter_set_name( filter, _("Google Earth") );
2071 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2072 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2074 filter = gtk_file_filter_new ();
2075 gtk_file_filter_set_name( filter, _("GPX") );
2076 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2077 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2079 filter = gtk_file_filter_new ();
2080 gtk_file_filter_set_name( filter, _("Viking") );
2081 gtk_file_filter_add_pattern ( filter, "*.vik" );
2082 gtk_file_filter_add_pattern ( filter, "*.viking" );
2083 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2085 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2086 // However assume this are barely used and thus not worthy of inclusion
2087 // as they'll just make the options too many and have no clear file pattern
2088 // one can always use the all option
2089 filter = gtk_file_filter_new ();
2090 gtk_file_filter_set_name( filter, _("All") );
2091 gtk_file_filter_add_pattern ( filter, "*" );
2092 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2093 // Default to any file - same as before open filters were added
2094 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2096 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2097 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2098 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2100 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2102 gtk_widget_hide ( vw->open_dia );
2103 #ifdef VIKING_PROMPT_IF_MODIFIED
2104 if ( (vw->modified || vw->filename) && newwindow )
2106 if ( vw->filename && newwindow )
2108 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2110 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2111 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2114 while ( cur_file ) {
2115 gchar *file_name = cur_file->data;
2116 vik_window_open_file ( vw, file_name, change_fn );
2118 cur_file = g_slist_next (cur_file);
2120 g_slist_free (files);
2124 gtk_widget_hide ( vw->open_dia );
2127 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2129 gboolean rv = FALSE;
2131 if ( ! vw->save_dia )
2133 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2135 GTK_FILE_CHOOSER_ACTION_SAVE,
2136 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2137 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2139 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2140 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2143 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2145 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2146 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(vw->save_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2148 window_set_filename ( vw, fn );
2149 rv = window_save ( vw );
2150 vw->modified = FALSE;
2154 gtk_widget_hide ( vw->save_dia );
2158 static gboolean window_save ( VikWindow *vw )
2160 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2162 update_recently_used_document ( vw->filename );
2167 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2172 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2174 if ( ! vw->filename )
2175 return save_file_as ( NULL, vw );
2178 vw->modified = FALSE;
2179 return window_save ( vw );
2183 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2185 // Via the file menu, acquiring from a GPS makes a new layer
2186 // this has always been the way (not entirely sure if this was the real intention!)
2187 // thus maintain the behaviour ATM.
2188 // Hence explicit setting here (as the value may be changed elsewhere)
2189 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2190 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2193 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2195 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2198 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2199 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2201 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2205 #ifdef VIK_CONFIG_OPENSTREETMAP
2206 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2208 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2212 #ifdef VIK_CONFIG_GEOCACHES
2213 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2215 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2219 #ifdef VIK_CONFIG_GEOTAG
2220 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2222 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2223 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2227 static void goto_default_location( GtkAction *a, VikWindow *vw)
2230 ll.lat = a_vik_get_default_lat();
2231 ll.lon = a_vik_get_default_long();
2232 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2233 vik_layers_panel_emit_update(vw->viking_vlp);
2237 static void goto_address( GtkAction *a, VikWindow *vw)
2239 a_vik_goto(vw, vw->viking_vlp, vw->viking_vvp);
2242 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2247 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2249 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2251 a_preferences_show_window ( GTK_WINDOW(vw) );
2253 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2254 if (wp_icon_size != a_vik_get_use_large_waypoint_icons())
2255 clear_garmin_icon_syms ();
2260 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2262 /* Simplistic repeat of preference setting
2263 Only the name & type are important for setting the preference via this 'external' way */
2264 VikLayerParam pref_lat[] = {
2265 { VIKING_PREFERENCES_NAMESPACE "default_latitude",
2266 VIK_LAYER_PARAM_DOUBLE,
2269 VIK_LAYER_WIDGET_SPINBUTTON,
2273 VikLayerParam pref_lon[] = {
2274 { VIKING_PREFERENCES_NAMESPACE "default_longitude",
2275 VIK_LAYER_PARAM_DOUBLE,
2278 VIK_LAYER_WIDGET_SPINBUTTON,
2283 /* Get current center */
2285 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2287 /* Apply to preferences */
2288 VikLayerParamData vlp_data;
2289 vlp_data.d = ll.lat;
2290 a_preferences_run_setparam (vlp_data, pref_lat);
2291 vlp_data.d = ll.lon;
2292 a_preferences_run_setparam (vlp_data, pref_lon);
2293 /* Remember to save */
2294 a_preferences_save_to_file();
2297 static void clear_cb ( GtkAction *a, VikWindow *vw )
2299 vik_layers_panel_clear ( vw->viking_vlp );
2300 window_set_filename ( vw, NULL );
2304 static void window_close ( GtkAction *a, VikWindow *vw )
2306 if ( ! delete_event ( vw ) )
2307 gtk_widget_destroy ( GTK_WIDGET(vw) );
2310 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2312 if (save_file( NULL, vw)) {
2313 window_close( NULL, vw);
2320 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2322 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2323 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2325 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2326 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2331 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2333 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2334 GdkPixbuf *pixbuf_to_save;
2335 gdouble old_xmpp, old_ympp;
2336 GError *error = NULL;
2338 /* backup old zoom & set new */
2339 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2340 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2341 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2343 /* reset width and height: */
2344 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2346 /* draw all layers */
2349 /* save buffer as file. */
2350 pixbuf_to_save = gdk_pixbuf_get_from_drawable ( NULL, GDK_DRAWABLE(vik_viewport_get_pixmap ( vw->viking_vvp )), NULL, 0, 0, 0, 0, w, h);
2351 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2354 g_warning("Unable to write to file %s: %s", fn, error->message );
2355 g_error_free (error);
2357 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2359 /* pretend like nothing happened ;) */
2360 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2361 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2362 vik_viewport_configure ( vw->viking_vvp );
2366 static void save_image_dir ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png, guint tiles_w, guint tiles_h )
2368 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2369 gchar *name_of_file = g_malloc ( size );
2371 struct UTM utm_orig, utm;
2373 /* *** copied from above *** */
2374 GdkPixbuf *pixbuf_to_save;
2375 gdouble old_xmpp, old_ympp;
2376 GError *error = NULL;
2378 /* backup old zoom & set new */
2379 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2380 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2381 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2383 /* reset width and height: do this only once for all images (same size) */
2384 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2385 /* *** end copy from above *** */
2387 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2391 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2393 for ( y = 1; y <= tiles_h; y++ )
2395 for ( x = 1; x <= tiles_w; x++ )
2397 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2399 if ( tiles_w & 0x1 )
2400 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2402 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2403 if ( tiles_h & 0x1 ) /* odd */
2404 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2406 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2408 /* move to correct place. */
2409 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2413 /* save buffer as file. */
2414 pixbuf_to_save = gdk_pixbuf_get_from_drawable ( NULL, GDK_DRAWABLE(vik_viewport_get_pixmap ( vw->viking_vvp )), NULL, 0, 0, 0, 0, w, h);
2415 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2418 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2419 g_error_free (error);
2422 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2426 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2427 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2428 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2429 vik_viewport_configure ( vw->viking_vvp );
2432 g_free ( name_of_file );
2435 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2437 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2438 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2439 GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]);
2440 gdouble width_min, width_max, height_min, height_max;
2443 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2444 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2446 /* TODO: support for xzoom and yzoom values */
2447 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin );
2448 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin );
2450 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2451 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2453 gtk_spin_button_set_value ( width_spin, width );
2454 gtk_spin_button_set_value ( height_spin, height );
2457 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2459 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2460 GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]);
2463 w = gtk_spin_button_get_value(width_spin) * gtk_spin_button_get_value(zoom_spin);
2464 h = gtk_spin_button_get_value(height_spin) * gtk_spin_button_get_value(zoom_spin);
2465 if (pass_along[4]) /* save many images; find TOTAL area covered */
2467 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2468 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2470 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2471 switch (dist_units) {
2472 case VIK_UNITS_DISTANCE_KILOMETRES:
2473 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2475 case VIK_UNITS_DISTANCE_MILES:
2476 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2479 label_text = g_strdup_printf ("Just to keep the compiler happy");
2480 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2483 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2484 g_free ( label_text );
2487 static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_image_only )
2489 /* todo: default for answers inside VikWindow or static (thruout instance) */
2490 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2491 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2493 GTK_RESPONSE_REJECT,
2495 GTK_RESPONSE_ACCEPT,
2497 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2498 GtkWidget *png_radio, *jpeg_radio;
2499 GtkWidget *current_window_button;
2500 gpointer current_window_pass_along[7];
2501 GtkWidget *zoom_label, *zoom_spin;
2502 GtkWidget *total_size_label;
2504 /* only used if (!one_image_only) */
2505 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2508 width_label = gtk_label_new ( _("Width (pixels):") );
2509 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 5000, 10, 100, 0 )), 10, 0 );
2510 height_label = gtk_label_new ( _("Height (pixels):") );
2511 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 5000, 10, 100, 0 )), 10, 0 );
2513 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2514 /* TODO: separate xzoom and yzoom factors */
2515 zoom_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vik_viewport_get_xmpp(vw->viking_vvp), VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM/2.0, 1, 100, 0 )), 16, 0);
2517 total_size_label = gtk_label_new ( NULL );
2519 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2520 current_window_pass_along [0] = vw;
2521 current_window_pass_along [1] = width_spin;
2522 current_window_pass_along [2] = height_spin;
2523 current_window_pass_along [3] = zoom_spin;
2524 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
2525 current_window_pass_along [5] = NULL;
2526 current_window_pass_along [6] = total_size_label;
2527 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
2529 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
2530 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
2532 if ( ! vw->draw_image_save_as_png )
2533 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
2535 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
2536 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
2537 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
2538 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
2539 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
2540 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
2541 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
2542 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
2543 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_spin, FALSE, FALSE, 0);
2545 if ( ! one_image_only )
2547 GtkWidget *tiles_width_label, *tiles_height_label;
2550 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
2551 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2552 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
2553 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2554 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
2555 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
2556 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
2557 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
2559 current_window_pass_along [4] = tiles_width_spin;
2560 current_window_pass_along [5] = tiles_height_spin;
2561 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2562 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2564 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
2565 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2566 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2567 g_signal_connect ( G_OBJECT(zoom_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2569 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
2571 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2573 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
2575 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
2577 gtk_widget_hide ( GTK_WIDGET(dialog) );
2578 if ( one_image_only )
2579 save_image_file ( vw, fn,
2580 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2581 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2582 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */
2583 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
2585 // NB is in UTM mode ATM
2586 save_image_dir ( vw, fn,
2587 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2588 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2589 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */
2590 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
2591 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
2592 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
2595 gtk_widget_destroy ( GTK_WIDGET(dialog) );
2599 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
2602 if (!vw->save_img_dia) {
2603 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2605 GTK_FILE_CHOOSER_ACTION_SAVE,
2606 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2607 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2609 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2610 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2613 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT )
2615 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2616 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(vw->save_img_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2618 draw_to_image_file ( vw, fn, TRUE );
2624 gtk_widget_hide ( vw->save_img_dia );
2627 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
2631 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2632 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2636 if (!vw->save_img_dir_dia) {
2637 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2639 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2640 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2641 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2643 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2644 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2647 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT )
2649 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2652 draw_to_image_file ( vw, fn, FALSE );
2658 gtk_widget_hide ( vw->save_img_dir_dia );
2661 #if GTK_CHECK_VERSION(2,10,0)
2662 static void print_cb ( GtkAction *a, VikWindow *vw )
2664 a_print(vw, vw->viking_vvp);
2668 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
2669 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
2671 VikViewportDrawMode drawmode;
2672 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
2673 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
2675 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
2676 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
2678 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
2679 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
2681 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
2682 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
2685 g_critical("Houston, we've had a problem.");
2689 if ( !vw->only_updating_coord_mode_ui )
2691 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
2692 if ( olddrawmode != drawmode )
2694 /* this takes care of coord mode too */
2695 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
2696 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
2697 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
2698 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
2699 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
2706 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
2708 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2709 g_assert(check_box);
2710 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2711 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
2715 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
2717 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2718 g_assert(check_box);
2719 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2720 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
2724 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
2726 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2727 g_assert(check_box);
2728 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2729 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
2733 static void set_bg_color ( GtkAction *a, VikWindow *vw )
2735 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
2736 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
2737 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2738 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2739 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
2741 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2742 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
2746 gtk_widget_destroy ( colorsd );
2749 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
2751 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
2752 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
2753 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2754 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2755 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
2757 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2758 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
2762 gtk_widget_destroy ( colorsd );
2767 /***********************************************************************************************
2769 ***********************************************************************************************/
2771 static GtkActionEntry entries[] = {
2772 { "File", NULL, N_("_File"), 0, 0, 0 },
2773 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
2774 { "View", NULL, N_("_View"), 0, 0, 0 },
2775 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
2776 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
2777 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
2778 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
2779 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
2780 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
2781 { "Help", NULL, N_("_Help"), 0, 0, 0 },
2783 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
2784 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
2785 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
2786 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
2787 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
2788 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
2789 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
2790 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2791 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
2793 #ifdef VIK_CONFIG_OPENSTREETMAP
2794 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
2796 #ifdef VIK_CONFIG_GEOCACHES
2797 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
2799 #ifdef VIK_CONFIG_GEOTAG
2800 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
2802 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
2803 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
2804 { "GenImg", GTK_STOCK_CLEAR, N_("_Generate Image File..."), NULL, N_("Save a snapshot of the workspace into a file"), (GCallback)draw_to_image_file_cb },
2805 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
2807 #if GTK_CHECK_VERSION(2,10,0)
2808 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
2811 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
2812 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
2814 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
2815 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
2816 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
2817 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
2818 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
2819 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
2820 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
2821 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
2822 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
2823 { "Zoom0.25", NULL, N_("0.25"), NULL, NULL, (GCallback)draw_zoom_cb },
2824 { "Zoom0.5", NULL, N_("0.5"), NULL, NULL, (GCallback)draw_zoom_cb },
2825 { "Zoom1", NULL, N_("1"), NULL, NULL, (GCallback)draw_zoom_cb },
2826 { "Zoom2", NULL, N_("2"), NULL, NULL, (GCallback)draw_zoom_cb },
2827 { "Zoom4", NULL, N_("4"), NULL, NULL, (GCallback)draw_zoom_cb },
2828 { "Zoom8", NULL, N_("8"), NULL, NULL, (GCallback)draw_zoom_cb },
2829 { "Zoom16", NULL, N_("16"), NULL, NULL, (GCallback)draw_zoom_cb },
2830 { "Zoom32", NULL, N_("32"), NULL, NULL, (GCallback)draw_zoom_cb },
2831 { "Zoom64", NULL, N_("64"), NULL, NULL, (GCallback)draw_zoom_cb },
2832 { "Zoom128", NULL, N_("128"), NULL, NULL, (GCallback)draw_zoom_cb },
2833 { "Zoom256", NULL, N_("256"), NULL, NULL, (GCallback)draw_zoom_cb },
2834 { "Zoom512", NULL, N_("512"), NULL, NULL, (GCallback)draw_zoom_cb },
2835 { "Zoom1024", NULL, N_("1024"), NULL, NULL, (GCallback)draw_zoom_cb },
2836 { "Zoom2048", NULL, N_("2048"), NULL, NULL, (GCallback)draw_zoom_cb },
2837 { "Zoom4096", NULL, N_("4096"), NULL, NULL, (GCallback)draw_zoom_cb },
2838 { "Zoom8192", NULL, N_("8192"), NULL, NULL, (GCallback)draw_zoom_cb },
2839 { "Zoom16384", NULL, N_("16384"), NULL, NULL, (GCallback)draw_zoom_cb },
2840 { "Zoom32768", NULL, N_("32768"), NULL, NULL, (GCallback)draw_zoom_cb },
2841 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
2842 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
2843 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
2844 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
2845 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
2847 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
2848 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
2849 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
2850 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
2851 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
2852 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
2853 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
2854 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
2855 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
2857 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
2858 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
2862 /* FIXME use VIEWPORT_DRAWMODE values */
2863 static GtkRadioActionEntry mode_entries[] = {
2864 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
2865 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
2866 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
2867 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
2870 static GtkToggleActionEntry toggle_entries[] = {
2871 { "ShowScale", NULL, N_("Show _Scale"), "F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
2872 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
2873 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
2874 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
2875 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
2876 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
2877 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
2878 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
2881 #include "menu.xml.h"
2882 static void window_create_ui( VikWindow *window )
2885 GtkActionGroup *action_group;
2886 GtkAccelGroup *accel_group;
2889 GtkIconFactory *icon_factory;
2890 GtkIconSet *icon_set;
2891 GtkRadioActionEntry *tools = NULL, *radio;
2894 uim = gtk_ui_manager_new ();
2897 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
2898 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
2899 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
2900 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
2903 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
2904 g_error_free (error);
2908 action_group = gtk_action_group_new ("MenuActions");
2909 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
2910 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
2911 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
2912 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
2914 icon_factory = gtk_icon_factory_new ();
2915 gtk_icon_factory_add_default (icon_factory);
2917 register_vik_icons(icon_factory);
2919 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
2920 // so that it can be applied to the UI in one action group add function call below
2922 for (i=0; i<window->vt->n_tools; i++) {
2923 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
2924 radio = &tools[ntools];
2926 *radio = window->vt->tools[i].ti.radioActionEntry;
2927 radio->value = ntools;
2930 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
2931 GtkActionEntry action;
2932 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
2933 vik_layer_get_interface(i)->name,
2934 vik_layer_get_interface(i)->name,
2935 GTK_UI_MANAGER_MENUITEM, FALSE);
2937 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
2938 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
2939 gtk_icon_set_unref (icon_set);
2941 action.name = vik_layer_get_interface(i)->name;
2942 action.stock_id = vik_layer_get_interface(i)->name;
2943 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
2944 action.accelerator = vik_layer_get_interface(i)->accelerator;
2945 action.tooltip = NULL;
2946 action.callback = (GCallback)menu_addlayer_cb;
2947 gtk_action_group_add_actions(action_group, &action, 1, window);
2949 if ( vik_layer_get_interface(i)->tools_count ) {
2950 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
2951 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
2954 // Further tool copying for to apply to the UI, also apply menu UI setup
2955 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
2956 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
2957 radio = &tools[ntools];
2960 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
2961 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
2962 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
2963 GTK_UI_MANAGER_MENUITEM, FALSE);
2964 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
2965 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
2966 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
2967 GTK_UI_MANAGER_TOOLITEM, FALSE);
2969 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
2971 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
2972 // Overwrite with actual number to use
2973 radio->value = ntools;
2976 g_object_unref (icon_factory);
2978 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
2981 gtk_ui_manager_insert_action_group (uim, action_group, 0);
2983 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
2984 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
2985 GtkAction *action = gtk_action_group_get_action(action_group,
2986 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
2987 g_object_set(action, "sensitive", FALSE, NULL);
2990 window->action_group = action_group;
2992 accel_group = gtk_ui_manager_get_accel_group (uim);
2993 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
2994 gtk_ui_manager_ensure_update (uim);
2996 setup_recent_files(window);
3000 // TODO - add method to add tool icons defined from outside this file
3001 // and remove the reverse dependency on icon definition from this file
3003 const GdkPixdata *data;
3006 { &mover_22_pixbuf, "vik-icon-pan" },
3007 { &zoom_18_pixbuf, "vik-icon-zoom" },
3008 { &ruler_18_pixbuf, "vik-icon-ruler" },
3009 { &select_18_pixbuf, "vik-icon-select" },
3010 { &begintr_18_pixbuf, "vik-icon-Begin Track" },
3011 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3012 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3013 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3014 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3015 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3016 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3017 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3018 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3019 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3020 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3023 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3026 register_vik_icons (GtkIconFactory *icon_factory)
3028 GtkIconSet *icon_set;
3031 for (i = 0; i < n_stock_icons; i++) {
3032 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3033 stock_icons[i].data, FALSE, NULL ));
3034 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3035 gtk_icon_set_unref (icon_set);
3039 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3041 return vw->selected_vtl;
3044 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3046 vw->selected_vtl = vtl;
3047 vw->containing_vtl = vtl;
3049 vw->selected_track = NULL;
3050 vw->selected_tracks = NULL;
3051 vw->selected_waypoint = NULL;
3052 vw->selected_waypoints = NULL;
3053 // Set highlight thickness
3054 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3057 gpointer vik_window_get_selected_tracks ( VikWindow *vw )
3059 return vw->selected_tracks;
3062 void vik_window_set_selected_tracks ( VikWindow *vw, gpointer gl, gpointer vtl )
3064 vw->selected_tracks = gl;
3065 vw->containing_vtl = vtl;
3067 vw->selected_vtl = NULL;
3068 vw->selected_track = NULL;
3069 vw->selected_waypoint = NULL;
3070 vw->selected_waypoints = NULL;
3071 // Set highlight thickness
3072 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3075 gpointer vik_window_get_selected_track ( VikWindow *vw )
3077 return vw->selected_track;
3080 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl, gpointer name )
3082 vw->selected_track = vt;
3083 vw->containing_vtl = vtl;
3085 vw->selected_vtl = NULL;
3086 vw->selected_tracks = NULL;
3087 vw->selected_waypoint = NULL;
3088 vw->selected_waypoints = NULL;
3089 // Set highlight thickness
3090 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3093 gpointer vik_window_get_selected_waypoints ( VikWindow *vw )
3095 return vw->selected_waypoints;
3098 void vik_window_set_selected_waypoints ( VikWindow *vw, gpointer gl, gpointer vtl )
3100 vw->selected_waypoints = gl;
3101 vw->containing_vtl = vtl;
3103 vw->selected_vtl = NULL;
3104 vw->selected_track = NULL;
3105 vw->selected_tracks = NULL;
3106 vw->selected_waypoint = NULL;
3109 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3111 return vw->selected_waypoint;
3114 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl, gpointer name )
3116 vw->selected_waypoint = vwp;
3117 vw->containing_vtl = vtl;
3119 vw->selected_vtl = NULL;
3120 vw->selected_track = NULL;
3121 vw->selected_tracks = NULL;
3122 vw->selected_waypoints = NULL;
3125 gboolean vik_window_clear_highlight ( VikWindow *vw )
3127 gboolean need_redraw = FALSE;
3128 if ( vw->selected_vtl != NULL ) {
3129 vw->selected_vtl = NULL;
3132 if ( vw->selected_track != NULL ) {
3133 vw->selected_track = NULL;
3136 if ( vw->selected_tracks != NULL ) {
3137 vw->selected_tracks = NULL;
3140 if ( vw->selected_waypoint != NULL ) {
3141 vw->selected_waypoint = NULL;
3144 if ( vw->selected_waypoints != NULL ) {
3145 vw->selected_waypoints = NULL;