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 // This seems rather arbitary, quite large and pointless
59 // I mean, if you have a thousand windows open;
60 // why not be allowed to open a thousand more...
61 #define MAX_WINDOWS 1024
62 static guint window_count = 0;
64 #define VIKING_WINDOW_WIDTH 1000
65 #define VIKING_WINDOW_HEIGHT 800
66 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
67 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
68 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
70 static void window_finalize ( GObject *gob );
71 static GObjectClass *parent_class;
73 static void window_init ( VikWindow *vw );
74 static void window_class_init ( VikWindowClass *klass );
75 static void window_set_filename ( VikWindow *vw, const gchar *filename );
77 static VikWindow *window_new ();
79 static void draw_update ( VikWindow *vw );
81 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
84 static void open_window ( VikWindow *vw, GSList *files );
85 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type );
86 static void destroy_window ( GtkWidget *widget,
91 static gboolean delete_event( VikWindow *vw );
93 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
95 static void window_configure_event ( VikWindow *vw );
96 static void draw_sync ( VikWindow *vw );
97 static void draw_redraw ( VikWindow *vw );
98 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
99 static void draw_click ( VikWindow *vw, GdkEventButton *event );
100 static void draw_release ( VikWindow *vw, GdkEventButton *event );
101 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
102 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
103 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
105 static void draw_status ( VikWindow *vw );
107 /* End Drawing Functions */
109 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
110 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
111 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
113 /* tool management */
119 #define TOOL_LAYER_TYPE_NONE -1
124 toolbox_tool_t *tools;
128 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
129 static toolbox_tools_t* toolbox_create(VikWindow *vw);
130 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
131 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
132 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
133 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
134 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
135 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
136 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
140 static void window_create_ui( VikWindow *window );
141 static void register_vik_icons (GtkIconFactory *icon_factory);
144 static void load_file ( GtkAction *a, VikWindow *vw );
145 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
146 static gboolean save_file ( GtkAction *a, VikWindow *vw );
147 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
148 static gboolean window_save ( VikWindow *vw );
152 VikViewport *viking_vvp;
153 VikLayersPanel *viking_vlp;
154 VikStatusbar *viking_vs;
158 GtkItemFactory *item_factory;
160 /* tool management state */
163 guint16 tool_layer_id;
164 guint16 tool_tool_id;
166 GtkActionGroup *action_group;
171 guint draw_image_width, draw_image_height;
172 gboolean draw_image_save_as_png;
177 GtkWidget *open_dia, *save_dia;
178 GtkWidget *save_img_dia, *save_img_dir_dia;
180 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
183 /* half-drawn update */
185 VikCoord trigger_center;
187 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
188 /* Only one of these items can be selected at the same time */
189 gpointer selected_vtl; /* notionally VikTrwLayer */
190 gpointer selected_tracks; /* notionally GList */
191 gpointer selected_track; /* notionally VikTrack */
192 gpointer selected_waypoints; /* notionally GList */
193 gpointer selected_waypoint; /* notionally VikWaypoint */
194 /* only use for individual track or waypoint */
195 ////// NEED TO THINK ABOUT VALIDITY OF THESE //////
196 ////// i.e. what happens when stuff is deleted elsewhere //////
197 ////// Generally seems alright as can not access them //////
198 ////// containing_vtl now seems unecessary //////
199 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
200 gpointer containing_vtl; /* notionally VikTrwLayer */
214 VW_OPENWINDOW_SIGNAL,
215 VW_STATUSBAR_UPDATE_SIGNAL,
219 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
221 // TODO get rid of this as this is unnecessary duplication...
222 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
224 GType vik_window_get_type (void)
226 static GType vw_type = 0;
230 static const GTypeInfo vw_info =
232 sizeof (VikWindowClass),
233 NULL, /* base_init */
234 NULL, /* base_finalize */
235 (GClassInitFunc) window_class_init, /* class_init */
236 NULL, /* class_finalize */
237 NULL, /* class_data */
240 (GInstanceInitFunc) window_init,
242 vw_type = g_type_register_static ( GTK_TYPE_WINDOW, "VikWindow", &vw_info, 0 );
248 VikViewport * vik_window_viewport(VikWindow *vw)
250 return(vw->viking_vvp);
253 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
255 return(vw->viking_vlp);
259 * Returns the statusbar for the window
261 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
263 return vw->viking_vs;
267 * For signalling the update from a background thread
269 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
271 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message, vs_type );
275 * For the actual statusbar update!
277 static gboolean statusbar_idle_update ( gpointer indata )
279 gpointer *data = indata;
280 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
285 * Update statusbar in the main thread
287 static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
289 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
290 static gpointer data[3];
291 data[0] = vw->viking_vs;
292 data[1] = (gchar*) message;
293 data[2] = GINT_TO_POINTER(vs_type);
294 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
297 // Actual signal handlers
298 static void destroy_window ( GtkWidget *widget,
301 if ( ! --window_count )
305 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type )
307 window_statusbar_update ( vw, message, vs_type );
310 VikWindow *vik_window_new_window ()
312 if ( window_count < MAX_WINDOWS )
314 VikWindow *vw = window_new ();
316 g_signal_connect (G_OBJECT (vw), "destroy",
317 G_CALLBACK (destroy_window), NULL);
318 g_signal_connect (G_OBJECT (vw), "newwindow",
319 G_CALLBACK (vik_window_new_window), NULL);
320 g_signal_connect (G_OBJECT (vw), "openwindow",
321 G_CALLBACK (open_window), NULL);
322 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
323 G_CALLBACK (statusbar_update), vw);
325 gtk_widget_show_all ( GTK_WIDGET(vw) );
334 static void open_window ( VikWindow *vw, GSList *files )
336 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
337 GSList *cur_file = files;
339 // Only open a new window if a viking file
340 gchar *file_name = cur_file->data;
341 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
342 VikWindow *newvw = vik_window_new_window ();
344 vik_window_open_file ( newvw, file_name, change_fn );
347 vik_window_open_file ( vw, file_name, change_fn );
350 cur_file = g_slist_next (cur_file);
352 g_slist_free (files);
356 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
358 int i, j, tool_count;
359 VikLayerInterface *layer_interface;
361 if (!vw->action_group) return;
363 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
365 layer_interface = vik_layer_get_interface(i);
366 tool_count = layer_interface->tools_count;
368 for (j = 0; j < tool_count; j++) {
369 action = gtk_action_group_get_action(vw->action_group,
370 layer_interface->tools[j].radioActionEntry.name);
371 g_object_set(action, "sensitive", i == vl->type, NULL);
376 static void window_finalize ( GObject *gob )
378 VikWindow *vw = VIK_WINDOW(gob);
379 g_return_if_fail ( vw != NULL );
381 a_background_remove_window ( vw );
383 G_OBJECT_CLASS(parent_class)->finalize(gob);
387 static void window_class_init ( VikWindowClass *klass )
390 GObjectClass *object_class;
392 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);
393 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);
394 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, gtk_marshal_VOID__POINTER_UINT, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
396 object_class = G_OBJECT_CLASS (klass);
398 object_class->finalize = window_finalize;
400 parent_class = g_type_class_peek_parent (klass);
404 static void window_init ( VikWindow *vw )
406 GtkWidget *main_vbox;
409 vw->action_group = NULL;
411 vw->viking_vvp = vik_viewport_new();
412 vw->viking_vlp = vik_layers_panel_new();
413 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
414 vw->viking_vs = vik_statusbar_new();
416 vw->vt = toolbox_create(vw);
417 window_create_ui(vw);
418 window_set_filename (vw, NULL);
419 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
421 // Set the default tool
422 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
425 vw->item_factory = NULL;
427 vw->modified = FALSE;
428 vw->only_updating_coord_mode_ui = FALSE;
430 vw->pan_move = FALSE;
431 vw->pan_x = vw->pan_y = -1;
432 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
433 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
434 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
436 main_vbox = gtk_vbox_new(FALSE, 1);
437 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
439 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
440 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
441 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
442 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
444 vik_ext_tools_add_menu_items ( vw, vw->uim );
446 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
448 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
449 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
450 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 );
451 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
452 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
453 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
454 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
455 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
457 g_signal_connect_swapped (G_OBJECT (vw->viking_vvp), "key_press_event", G_CALLBACK (key_press_event), vw);
459 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
461 hpaned = gtk_hpaned_new ();
462 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
463 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
465 /* This packs the button into the window (a gtk container). */
466 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
468 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
470 a_background_add_window ( vw );
474 vw->save_img_dia = NULL;
475 vw->save_img_dir_dia = NULL;
478 static VikWindow *window_new ()
480 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
483 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
485 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
486 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
487 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
488 if ( vl && ltype == vl->type )
489 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
492 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
493 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
494 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
497 /* Restore Main Menu via Escape key if the user has hidden it */
498 /* This key is more likely to be used as they may not remember the function key */
499 if ( event->keyval == GDK_Escape ) {
500 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
502 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
504 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
505 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
506 return TRUE; /* handled keypress */
511 return FALSE; /* don't handle the keypress */
514 static gboolean delete_event( VikWindow *vw )
516 #ifdef VIKING_PROMPT_IF_MODIFIED
523 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
524 _("Do you want to save the changes you made to the document \"%s\"?\n"
526 "Your changes will be lost if you don't save them."),
527 vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") ) );
528 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
529 switch ( gtk_dialog_run ( dia ) )
531 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
532 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
533 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
540 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
542 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
545 static void draw_update ( VikWindow *vw )
551 static void draw_sync ( VikWindow *vw )
553 vik_viewport_sync(vw->viking_vvp);
555 /* other things may be necc here later. */
558 static void draw_status ( VikWindow *vw )
560 static gchar zoom_level[22];
561 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
562 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
563 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
565 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
567 if ( (int)xmpp - xmpp < 0.0 )
568 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
570 /* xmpp should be a whole number so don't show useless .000 bit */
571 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
572 if ( vw->current_tool == TOOL_LAYER )
573 // Use tooltip rather than the internal name as the tooltip is i8n
574 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 );
576 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
578 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
581 void vik_window_set_redraw_trigger(VikLayer *vl)
583 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
588 static void window_configure_event ( VikWindow *vw )
590 static int first = 1;
593 // This is a hack to set the cursor corresponding to the first tool
594 // FIXME find the correct way to initialize both tool and its cursor
595 const GdkCursor *cursor = NULL;
597 cursor = toolbox_get_cursor(vw->vt, "Pan");
598 /* We set cursor, even if it is NULL: it resets to default */
599 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
603 static void draw_redraw ( VikWindow *vw )
605 VikCoord old_center = vw->trigger_center;
606 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
607 VikLayer *new_trigger = vw->trigger;
609 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
612 ; /* do nothing -- have to redraw everything. */
613 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
614 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
616 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
619 vik_viewport_clear ( vw->viking_vvp);
620 vik_layers_panel_draw_all ( vw->viking_vlp );
621 vik_viewport_draw_scale ( vw->viking_vvp );
622 vik_viewport_draw_copyright ( vw->viking_vvp );
623 vik_viewport_draw_centermark ( vw->viking_vvp );
624 vik_viewport_draw_logo ( vw->viking_vvp );
626 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
629 gboolean draw_buf_done = TRUE;
631 static gboolean draw_buf(gpointer data)
633 gpointer *pass_along = data;
635 gdk_draw_drawable (pass_along[0], pass_along[1],
636 pass_along[2], 0, 0, 0, 0, -1, -1);
637 draw_buf_done = TRUE;
643 /* Mouse event handlers ************************************************************************/
645 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
647 /* set panning origin */
648 vw->pan_move = FALSE;
649 vw->pan_x = (gint) event->x;
650 vw->pan_y = (gint) event->y;
653 static void draw_click (VikWindow *vw, GdkEventButton *event)
655 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
657 /* middle button pressed. we reserve all middle button and scroll events
658 * for panning and zooming; tools only get left/right/movement
660 if ( event->button == 2) {
661 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
662 // Tool still may need to do something (such as disable something)
663 toolbox_click(vw->vt, event);
664 vik_window_pan_click ( vw, event );
667 toolbox_click(vw->vt, event);
671 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
673 if ( vw->pan_x != -1 ) {
674 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
675 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
677 vw->pan_x = event->x;
678 vw->pan_y = event->y;
683 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
685 static VikCoord coord;
686 static struct UTM utm;
687 static struct LatLon ll;
688 #define BUFFER_SIZE 50
689 static char pointer_buf[BUFFER_SIZE];
690 gchar *lat = NULL, *lon = NULL;
693 VikDemInterpol interpol_method;
695 /* This is a hack, but work far the best, at least for single pointer systems.
696 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
698 gdk_window_get_pointer (event->window, &x, &y, NULL);
702 toolbox_move(vw->vt, event);
704 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
705 vik_coord_to_utm ( &coord, &utm );
706 a_coords_utm_to_latlon ( &utm, &ll );
707 a_coords_latlon_to_string ( &ll, &lat, &lon );
708 /* Change interpolate method according to scale */
709 zoom = vik_viewport_get_zoom(vw->viking_vvp);
711 interpol_method = VIK_DEM_INTERPOL_NONE;
712 else if (zoom >= 1.0)
713 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
715 interpol_method = VIK_DEM_INTERPOL_BEST;
716 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
717 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
718 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
720 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
723 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
728 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
730 vik_window_pan_move ( vw, event );
732 /* This is recommended by the GTK+ documentation, but does not work properly.
733 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
734 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
736 /* gdk_event_request_motions ( event ); */
739 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
741 if ( vw->pan_move == FALSE )
742 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
744 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
745 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
746 vw->pan_move = FALSE;
747 vw->pan_x = vw->pan_y = -1;
751 static void draw_release ( VikWindow *vw, GdkEventButton *event )
753 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
755 if ( event->button == 2 ) { /* move / pan */
756 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
757 // Tool still may need to do something (such as reenable something)
758 toolbox_release(vw->vt, event);
759 vik_window_pan_release ( vw, event );
762 toolbox_release(vw->vt, event);
766 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
768 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
769 if ( modifiers == GDK_CONTROL_MASK ) {
770 /* control == pan up & down */
771 if ( event->direction == GDK_SCROLL_UP )
772 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
774 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 );
775 } else if ( modifiers == GDK_SHIFT_MASK ) {
776 /* shift == pan left & right */
777 if ( event->direction == GDK_SCROLL_UP )
778 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
780 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 );
781 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
782 // This zoom is on the center position
783 if ( event->direction == GDK_SCROLL_UP )
784 vik_viewport_zoom_in (vw->viking_vvp);
786 vik_viewport_zoom_out (vw->viking_vvp);
788 /* make sure mouse is still over the same point on the map when we zoom */
791 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
792 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
793 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
794 if ( event->direction == GDK_SCROLL_UP )
795 vik_viewport_zoom_in (vw->viking_vvp);
797 vik_viewport_zoom_out(vw->viking_vvp);
798 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
799 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
800 center_y + (y - event->y) );
808 /********************************************************************************
810 ********************************************************************************/
811 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
813 PangoFontDescription *pfd;
816 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
817 GdkGC *thickgc = gdk_gc_new(d);
819 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
820 gdouble dx = (x2-x1)/len*10;
821 gdouble dy = (y2-y1)/len*10;
822 gdouble c = cos(15.0 * M_PI/180.0);
823 gdouble s = sin(15.0 * M_PI/180.0);
825 gdouble baseangle = 0;
828 /* draw line with arrow ends */
830 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
831 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
832 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
835 a_viewport_clip_line(&x1, &y1, &x2, &y2);
836 gdk_draw_line(d, gc, x1, y1, x2, y2);
838 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
839 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
840 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
841 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
842 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
843 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
848 angle = atan2(dy, dx) + M_PI_2;
850 if ( vik_viewport_get_drawmode ( vvp ) == VIK_VIEWPORT_DRAWMODE_UTM) {
856 vik_viewport_screen_to_coord ( vvp, x1, y1, &test );
857 vik_coord_to_latlon ( &test, &ll );
858 ll.lat += vik_viewport_get_ympp ( vvp ) * vik_viewport_get_height ( vvp ) / 11000.0; // about 11km per degree latitude
859 a_coords_latlon_to_utm ( &ll, &u );
860 vik_coord_load_from_utm ( &test, VIK_VIEWPORT_DRAWMODE_UTM, &u );
861 vik_viewport_coord_to_screen ( vvp, &test, &tx, &ty );
863 baseangle = M_PI - atan2(tx-x1, ty-y1);
874 gdk_gc_copy(thickgc, gc);
875 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
876 gdk_color_parse("#2255cc", &color);
877 gdk_gc_set_rgb_fg_color(thickgc, &color);
879 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);
882 gdk_gc_copy(thickgc, gc);
883 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
884 for (i=0; i<180; i++) {
885 c = cos(i*M_PI/90.0 + baseangle);
886 s = sin(i*M_PI/90.0 + baseangle);
889 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
891 gdouble ticksize = 2*CW;
892 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
896 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
897 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
898 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
899 c = (CR+CW*2)*cos(baseangle);
900 s = (CR+CW*2)*sin(baseangle);
901 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
902 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
905 #define LABEL(x, y, w, h) { \
906 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
907 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
908 gdk_draw_layout(d, gc, (x), (y), pl); }
913 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
915 pfd = pango_font_description_from_string ("Sans 8"); // FIXME: settable option? global variable?
916 pango_layout_set_font_description (pl, pfd);
917 pango_font_description_free (pfd);
919 pango_layout_set_text(pl, "N", -1);
920 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
922 /* draw label with distance */
923 vik_units_distance_t dist_units = a_vik_get_units_distance ();
924 switch (dist_units) {
925 case VIK_UNITS_DISTANCE_KILOMETRES:
926 if (distance >= 1000 && distance < 100000) {
927 g_sprintf(str, "%3.2f km", distance/1000.0);
928 } else if (distance < 1000) {
929 g_sprintf(str, "%d m", (int)distance);
931 g_sprintf(str, "%d km", (int)distance/1000);
934 case VIK_UNITS_DISTANCE_MILES:
935 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
936 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
937 } else if (distance < VIK_MILES_TO_METERS(1)) {
938 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
940 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
944 g_critical("Houston, we've had a problem. distance=%d", dist_units);
947 pango_layout_set_text(pl, str, -1);
949 pango_layout_get_pixel_size ( pl, &wd, &hd );
952 yd = (y1+y2)/2 - hd/2 - dx;
955 yd = (y1+y2)/2 - hd/2 + dx;
958 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
963 LABEL(xd, yd, wd, hd);
965 /* draw label with bearing */
966 g_sprintf(str, "%3.1f°", angle*180.0/M_PI);
967 pango_layout_set_text(pl, str, -1);
968 pango_layout_get_pixel_size ( pl, &wb, &hb );
969 xb = x1 + CR*cos(angle-M_PI_2);
970 yb = y1 + CR*sin(angle-M_PI_2);
972 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
978 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
979 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
983 LABEL(xb, yb, wb, hb);
987 g_object_unref ( G_OBJECT ( pl ) );
988 g_object_unref ( G_OBJECT ( labgc ) );
989 g_object_unref ( G_OBJECT ( thickgc ) );
995 gboolean has_oldcoord;
997 } ruler_tool_state_t;
999 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1001 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1004 s->has_oldcoord = FALSE;
1008 static void ruler_destroy (ruler_tool_state_t *s)
1013 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1018 if ( event->button == 1 ) {
1019 gchar *lat=NULL, *lon=NULL;
1020 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1021 vik_coord_to_latlon ( &coord, &ll );
1022 a_coords_latlon_to_string ( &ll, &lat, &lon );
1023 if ( s->has_oldcoord ) {
1024 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1025 switch (dist_units) {
1026 case VIK_UNITS_DISTANCE_KILOMETRES:
1027 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1029 case VIK_UNITS_DISTANCE_MILES:
1030 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1033 temp = g_strdup_printf ("Just to keep the compiler happy");
1034 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1037 s->has_oldcoord = FALSE;
1040 temp = g_strdup_printf ( "%s %s", lat, lon );
1041 s->has_oldcoord = TRUE;
1044 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1047 s->oldcoord = coord;
1050 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1051 draw_update ( s->vw );
1053 return VIK_LAYER_TOOL_ACK;
1056 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1058 VikViewport *vvp = s->vvp;
1059 VikWindow *vw = s->vw;
1065 if ( s->has_oldcoord ) {
1066 int oldx, oldy, w1, h1, w2, h2;
1067 static GdkPixmap *buf = NULL;
1068 gchar *lat=NULL, *lon=NULL;
1069 w1 = vik_viewport_get_width(vvp);
1070 h1 = vik_viewport_get_height(vvp);
1072 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1074 gdk_drawable_get_size(buf, &w2, &h2);
1075 if (w1 != w2 || h1 != h2) {
1076 g_object_unref ( G_OBJECT ( buf ) );
1077 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1080 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1081 vik_coord_to_latlon ( &coord, &ll );
1082 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1084 gdk_draw_drawable (buf, GTK_WIDGET(vvp)->style->black_gc,
1085 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1086 draw_ruler(vvp, buf, GTK_WIDGET(vvp)->style->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1087 if (draw_buf_done) {
1088 static gpointer pass_along[3];
1089 pass_along[0] = GTK_WIDGET(vvp)->window;
1090 pass_along[1] = GTK_WIDGET(vvp)->style->black_gc;
1091 pass_along[2] = buf;
1092 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1093 draw_buf_done = FALSE;
1095 a_coords_latlon_to_string(&ll, &lat, &lon);
1096 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1097 switch (dist_units) {
1098 case VIK_UNITS_DISTANCE_KILOMETRES:
1099 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1101 case VIK_UNITS_DISTANCE_MILES:
1102 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1105 temp = g_strdup_printf ("Just to keep the compiler happy");
1106 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1108 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1111 return VIK_LAYER_TOOL_ACK;
1114 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1116 return VIK_LAYER_TOOL_ACK;
1119 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1121 draw_update ( s->vw );
1124 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1126 if (event->keyval == GDK_Escape) {
1127 s->has_oldcoord = FALSE;
1128 ruler_deactivate ( vl, s );
1134 static VikToolInterface ruler_tool =
1135 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>R", N_("Ruler Tool"), 2 },
1136 (VikToolConstructorFunc) ruler_create,
1137 (VikToolDestructorFunc) ruler_destroy,
1138 (VikToolActivationFunc) NULL,
1139 (VikToolActivationFunc) ruler_deactivate,
1140 (VikToolMouseFunc) ruler_click,
1141 (VikToolMouseMoveFunc) ruler_move,
1142 (VikToolMouseFunc) ruler_release,
1143 (VikToolKeyFunc) ruler_key_press,
1145 GDK_CURSOR_IS_PIXMAP,
1146 &cursor_ruler_pixbuf };
1147 /*** end ruler code ********************************************************/
1151 /********************************************************************************
1153 ********************************************************************************/
1158 // Track zoom bounds for zoom tool with shift modifier:
1159 gboolean bounds_active;
1162 } zoom_tool_state_t;
1165 * In case the screen size has changed
1167 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1171 // Allocate a drawing area the size of the viewport
1172 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1173 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1175 if ( !zts->pixmap ) {
1177 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1180 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1182 if ( w1 != w2 || h1 != h2 ) {
1183 // Has changed - delete and recreate with new values
1184 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1185 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1189 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1191 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1196 zts->bounds_active = FALSE;
1200 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1203 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1207 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1209 zts->vw->modified = TRUE;
1210 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1214 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1215 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1217 gboolean skip_update = FALSE;
1219 zts->bounds_active = FALSE;
1221 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1222 // This zoom is on the center position
1223 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1224 if ( event->button == 1 )
1225 vik_viewport_zoom_in (zts->vw->viking_vvp);
1226 else if ( event->button == 3 )
1227 vik_viewport_zoom_out (zts->vw->viking_vvp);
1229 else if ( modifiers == GDK_CONTROL_MASK ) {
1230 // This zoom is to recenter on the mouse position
1231 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1232 if ( event->button == 1 )
1233 vik_viewport_zoom_in (zts->vw->viking_vvp);
1234 else if ( event->button == 3 )
1235 vik_viewport_zoom_out (zts->vw->viking_vvp);
1237 else if ( modifiers == GDK_SHIFT_MASK ) {
1238 // Get start of new zoom bounds
1239 if ( event->button == 1 ) {
1240 zts->bounds_active = TRUE;
1241 zts->start_x = (gint) event->x;
1242 zts->start_y = (gint) event->y;
1247 /* make sure mouse is still over the same point on the map when we zoom */
1248 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1249 if ( event->button == 1 )
1250 vik_viewport_zoom_in (zts->vw->viking_vvp);
1251 else if ( event->button == 3 )
1252 vik_viewport_zoom_out(zts->vw->viking_vvp);
1253 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1254 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1255 center_x + (x - event->x),
1256 center_y + (y - event->y) );
1260 draw_update ( zts->vw );
1262 return VIK_LAYER_TOOL_ACK;
1265 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1267 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1269 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1270 zoomtool_resize_pixmap ( zts );
1272 // Blank out currently drawn area
1273 gdk_draw_drawable ( zts->pixmap,
1274 GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc,
1275 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1276 0, 0, 0, 0, -1, -1);
1278 // Calculate new box starting point & size in pixels
1279 int xx, yy, width, height;
1280 if ( event->y > zts->start_y ) {
1282 height = event->y-zts->start_y;
1286 height = zts->start_y-event->y;
1288 if ( event->x > zts->start_x ) {
1290 width = event->x-zts->start_x;
1294 width = zts->start_x-event->x;
1298 gdk_draw_rectangle (zts->pixmap, GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, FALSE, xx, yy, width, height);
1300 // Only actually draw when there's time to do so
1301 if (draw_buf_done) {
1302 static gpointer pass_along[3];
1303 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1304 pass_along[1] = GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc;
1305 pass_along[2] = zts->pixmap;
1306 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1307 draw_buf_done = FALSE;
1310 return VIK_LAYER_TOOL_ACK;
1313 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1315 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1317 zts->bounds_active = FALSE;
1319 // Ensure haven't just released on the exact same position
1320 // i.e. probably haven't moved the mouse at all
1321 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1323 VikCoord coord1, coord2;
1324 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1325 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1327 // From the extend of the bounds pick the best zoom level
1328 // c.f. trw_layer_zoom_to_show_latlons()
1329 // Maybe refactor...
1330 struct LatLon ll1, ll2;
1331 vik_coord_to_latlon(&coord1, &ll1);
1332 vik_coord_to_latlon(&coord2, &ll2);
1333 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1334 (ll1.lon+ll2.lon)/2 };
1336 VikCoord new_center;
1337 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1338 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1340 /* Convert into definite 'smallest' and 'largest' positions */
1341 struct LatLon minmin;
1342 if ( ll1.lat < ll2.lat )
1343 minmin.lat = ll1.lat;
1345 minmin.lat = ll2.lat;
1347 struct LatLon maxmax;
1348 if ( ll1.lon > ll2.lon )
1349 maxmax.lon = ll1.lon;
1351 maxmax.lon = ll2.lon;
1353 /* Always recalculate the 'best' zoom level */
1354 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1355 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1357 gdouble min_lat, max_lat, min_lon, max_lon;
1358 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1359 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1360 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1361 /* NB I think the logic used in this test to determine if the bounds is within view
1362 fails if track goes across 180 degrees longitude.
1363 Hopefully that situation is not too common...
1364 Mind you viking doesn't really do edge locations to well anyway */
1365 if ( min_lat < minmin.lat &&
1366 max_lat > minmin.lat &&
1367 min_lon < maxmax.lon &&
1368 max_lon > maxmax.lon )
1369 /* Found within zoom level */
1374 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1377 draw_update ( zts->vw );
1379 return VIK_LAYER_TOOL_ACK;
1382 static VikToolInterface zoom_tool =
1383 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1384 (VikToolConstructorFunc) zoomtool_create,
1385 (VikToolDestructorFunc) zoomtool_destroy,
1386 (VikToolActivationFunc) NULL,
1387 (VikToolActivationFunc) NULL,
1388 (VikToolMouseFunc) zoomtool_click,
1389 (VikToolMouseMoveFunc) zoomtool_move,
1390 (VikToolMouseFunc) zoomtool_release,
1393 GDK_CURSOR_IS_PIXMAP,
1394 &cursor_zoom_pixbuf };
1395 /*** end zoom code ********************************************************/
1397 /********************************************************************************
1399 ********************************************************************************/
1400 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1405 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1407 vw->modified = TRUE;
1408 if ( event->button == 1 )
1409 vik_window_pan_click ( vw, event );
1411 return VIK_LAYER_TOOL_ACK;
1414 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1416 vik_window_pan_move ( vw, event );
1417 return VIK_LAYER_TOOL_ACK;
1420 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1422 if ( event->button == 1 )
1423 vik_window_pan_release ( vw, event );
1424 return VIK_LAYER_TOOL_ACK;
1427 static VikToolInterface pan_tool =
1428 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1429 (VikToolConstructorFunc) pantool_create,
1430 (VikToolDestructorFunc) NULL,
1431 (VikToolActivationFunc) NULL,
1432 (VikToolActivationFunc) NULL,
1433 (VikToolMouseFunc) pantool_click,
1434 (VikToolMouseMoveFunc) pantool_move,
1435 (VikToolMouseFunc) pantool_release,
1439 /*** end pan code ********************************************************/
1441 /********************************************************************************
1443 ********************************************************************************/
1444 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1446 tool_ed_t *t = g_new(tool_ed_t, 1);
1450 t->is_waypoint = FALSE;
1454 static void selecttool_destroy (tool_ed_t *t)
1462 GdkEventButton *event;
1463 tool_ed_t *tool_edit;
1466 static void click_layer_selected (VikLayer *vl, clicker *ck)
1468 /* Do nothing when function call returns true; */
1469 /* i.e. stop on first found item */
1472 if ( vik_layer_get_interface(vl->type)->select_click )
1473 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1476 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1478 /* Only allow selection on primary button */
1479 if ( event->button == 1 ) {
1480 /* Enable click to apply callback to potentially all track/waypoint layers */
1481 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1482 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1485 ck.vvp = t->vw->viking_vvp;
1488 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1491 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1494 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1496 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1497 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1498 gint type = vik_treeview_item_get_type ( vtv, &iter );
1499 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1500 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1502 vik_treeview_item_unselect ( vtv, &iter );
1503 if ( vik_window_clear_highlight ( t->vw ) )
1504 draw_update ( t->vw );
1509 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1511 /* Act on currently selected item to show menu */
1512 if ( t->vw->selected_track || t->vw->selected_waypoint )
1513 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1514 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1517 return VIK_LAYER_TOOL_ACK;
1520 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1522 /* Only allow selection on primary button */
1523 if ( event->button == 1 ) {
1524 // Don't care about vl here
1526 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1527 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1529 return VIK_LAYER_TOOL_ACK;
1532 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1534 /* Only allow selection on primary button */
1535 if ( event->button == 1 ) {
1536 // Don't care about vl here
1538 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1539 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1541 return VIK_LAYER_TOOL_ACK;
1544 static VikToolInterface select_tool =
1545 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1546 (VikToolConstructorFunc) selecttool_create,
1547 (VikToolDestructorFunc) selecttool_destroy,
1548 (VikToolActivationFunc) NULL,
1549 (VikToolActivationFunc) NULL,
1550 (VikToolMouseFunc) selecttool_click,
1551 (VikToolMouseMoveFunc) selecttool_move,
1552 (VikToolMouseFunc) selecttool_release,
1553 (VikToolKeyFunc) NULL,
1558 /*** end select tool code ********************************************************/
1560 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1562 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1563 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1564 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1565 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1566 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1567 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1568 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1569 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1574 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1576 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1577 g_assert(check_box);
1578 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1580 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1582 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1585 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1589 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1592 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1595 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1598 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1602 gchar *s = (gchar *)gtk_action_get_name(a);
1608 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1609 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1610 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1611 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1612 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1617 void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1619 VikCoord new_center;
1621 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1622 struct LatLon ll, llold;
1623 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1624 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1625 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1629 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1630 struct UTM utm, utmold;
1631 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1632 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1633 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1638 g_critical("Houston, we've had a problem.");
1642 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1646 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1649 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1650 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1651 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1653 vw->modified = TRUE;
1659 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1661 a_clipboard_copy_selected ( vw->viking_vlp );
1664 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1666 vik_layers_panel_cut_selected ( vw->viking_vlp );
1667 vw->modified = TRUE;
1670 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1672 if ( a_clipboard_paste ( vw->viking_vlp ) )
1674 vw->modified = TRUE;
1678 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1680 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1681 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1684 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1687 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1689 #if GTK_CHECK_VERSION (2, 14, 0)
1691 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1692 gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, NULL);
1695 #endif /* WINDOWS */
1698 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1700 a_dialog_about(GTK_WINDOW(vw));
1703 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1705 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1707 vik_layers_panel_delete_selected ( vw->viking_vlp );
1708 vw->modified = TRUE;
1711 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1714 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1716 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1717 g_assert(check_box);
1718 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1720 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1722 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1725 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1727 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1730 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1732 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1734 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1737 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1739 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1742 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1744 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1746 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1749 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1751 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1754 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1756 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1758 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1761 /***************************************
1762 ** tool management routines
1764 ***************************************/
1766 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1768 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1771 vt->active_tool = -1;
1773 if (!vw->viking_vvp) {
1774 g_critical("no viewport found.");
1780 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1782 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1783 vt->tools[vt->n_tools].ti = *vti;
1784 vt->tools[vt->n_tools].layer_type = layer_type;
1786 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1789 vt->tools[vt->n_tools].state = NULL;
1794 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1797 for (i=0; i<vt->n_tools; i++) {
1798 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1805 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1807 int tool = toolbox_get_tool(vt, tool_name);
1808 toolbox_tool_t *t = &vt->tools[tool];
1809 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1811 if (tool == vt->n_tools) {
1812 g_critical("trying to activate a non-existent tool...");
1815 /* is the tool already active? */
1816 if (vt->active_tool == tool) {
1820 if (vt->active_tool != -1) {
1821 if (vt->tools[vt->active_tool].ti.deactivate) {
1822 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1825 if (t->ti.activate) {
1826 t->ti.activate(vl, t->state);
1828 vt->active_tool = tool;
1831 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1833 int tool = toolbox_get_tool(vt, tool_name);
1834 toolbox_tool_t *t = &vt->tools[tool];
1835 if (t->ti.cursor == NULL) {
1836 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1837 GError *cursor_load_err = NULL;
1838 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1839 /* TODO: settable offeset */
1840 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1841 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1843 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1846 return t->ti.cursor;
1849 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1851 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1852 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1853 gint ltype = vt->tools[vt->active_tool].layer_type;
1854 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1855 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
1859 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
1861 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1862 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
1863 gint ltype = vt->tools[vt->active_tool].layer_type;
1864 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1865 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
1866 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
1870 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
1872 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1873 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
1874 gint ltype = vt->tools[vt->active_tool].layer_type;
1875 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1876 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
1879 /** End tool management ************************************/
1881 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
1883 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
1886 /* this function gets called whenever a toolbar tool is clicked */
1887 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
1889 /* White Magic, my friends ... White Magic... */
1890 int layer_id, tool_id;
1891 const GdkCursor *cursor = NULL;
1893 toolbox_activate(vw->vt, gtk_action_get_name(a));
1895 cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
1897 if ( GTK_WIDGET(vw->viking_vvp)->window )
1898 /* We set cursor, even if it is NULL: it resets to default */
1899 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
1901 if (!strcmp(gtk_action_get_name(a), "Pan")) {
1902 vw->current_tool = TOOL_PAN;
1904 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
1905 vw->current_tool = TOOL_ZOOM;
1907 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
1908 vw->current_tool = TOOL_RULER;
1910 else if (!strcmp(gtk_action_get_name(a), "Select")) {
1911 vw->current_tool = TOOL_SELECT;
1914 /* TODO: only enable tools from active layer */
1915 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
1916 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
1917 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
1918 vw->current_tool = TOOL_LAYER;
1919 vw->tool_layer_id = layer_id;
1920 vw->tool_tool_id = tool_id;
1928 static void window_set_filename ( VikWindow *vw, const gchar *filename )
1933 g_free ( vw->filename );
1934 if ( filename == NULL )
1936 vw->filename = NULL;
1937 file = _("Untitled");
1941 vw->filename = g_strdup(filename);
1942 file = a_file_basename ( filename );
1944 title = g_strdup_printf( "%s - Viking", file );
1945 gtk_window_set_title ( GTK_WINDOW(vw), title );
1949 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
1951 GtkWidget *mode_button;
1954 #ifdef VIK_CONFIG_EXPEDIA
1955 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
1957 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
1958 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
1959 default: buttonname = "/ui/MainMenu/View/ModeUTM";
1961 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
1962 g_assert ( mode_button );
1967 * vik_window_get_pan_move:
1968 * @vw: some VikWindow
1970 * Retrieves @vw's pan_move.
1972 * Should be removed as soon as possible.
1974 * Returns: @vw's pan_move
1978 gboolean vik_window_get_pan_move ( VikWindow *vw )
1980 return vw->pan_move;
1983 static void on_activate_recent_item (GtkRecentChooser *chooser,
1988 filename = gtk_recent_chooser_get_current_uri (chooser);
1989 if (filename != NULL)
1991 GFile *file = g_file_new_for_uri ( filename );
1992 gchar *path = g_file_get_path ( file );
1993 g_object_unref ( file );
1994 if ( self->filename )
1996 GSList *filenames = NULL;
1997 filenames = g_slist_append ( filenames, path );
1998 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
1999 // NB: GSList & contents are freed by main.open_window
2002 vik_window_open_file ( self, path, TRUE );
2010 static void setup_recent_files (VikWindow *self)
2012 GtkRecentManager *manager;
2013 GtkRecentFilter *filter;
2014 GtkWidget *menu, *menu_item;
2016 filter = gtk_recent_filter_new ();
2017 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2018 gtk_recent_filter_add_group(filter, "viking");
2020 manager = gtk_recent_manager_get_default ();
2021 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2022 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2023 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2025 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2026 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2028 g_signal_connect (G_OBJECT (menu), "item-activated",
2029 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2032 static void update_recently_used_document(const gchar *filename)
2034 /* Update Recently Used Document framework */
2035 GtkRecentManager *manager = gtk_recent_manager_get_default();
2036 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2037 gchar *groups[] = {"viking", NULL};
2038 GFile *file = g_file_new_for_commandline_arg(filename);
2039 gchar *uri = g_file_get_uri(file);
2040 gchar *basename = g_path_get_basename(filename);
2041 g_object_unref(file);
2044 recent_data->display_name = basename;
2045 recent_data->description = NULL;
2046 recent_data->mime_type = "text/x-gps-data";
2047 recent_data->app_name = (gchar *) g_get_application_name ();
2048 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2049 recent_data->groups = groups;
2050 recent_data->is_private = FALSE;
2051 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2053 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2058 g_free (recent_data->app_exec);
2059 g_slice_free (GtkRecentData, recent_data);
2062 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2064 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2066 case LOAD_TYPE_READ_FAILURE:
2067 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2069 case LOAD_TYPE_GPSBABEL_FAILURE:
2070 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2072 case LOAD_TYPE_GPX_FAILURE:
2073 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2075 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2076 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2078 case LOAD_TYPE_VIK_SUCCESS:
2080 GtkWidget *mode_button;
2082 if ( change_filename )
2083 window_set_filename ( vw, filename );
2084 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2085 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. */
2086 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2087 vw->only_updating_coord_mode_ui = FALSE;
2089 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2091 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2092 g_assert ( mode_button );
2093 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2095 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2096 g_assert ( mode_button );
2097 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2099 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2100 g_assert ( mode_button );
2101 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2103 //case LOAD_TYPE_OTHER_SUCCESS:
2105 update_recently_used_document(filename);
2110 static void load_file ( GtkAction *a, VikWindow *vw )
2112 GSList *files = NULL;
2113 GSList *cur_file = NULL;
2115 if (!strcmp(gtk_action_get_name(a), "Open")) {
2118 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2122 g_critical("Houston, we've had a problem.");
2126 if ( ! vw->open_dia )
2128 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2130 GTK_FILE_CHOOSER_ACTION_OPEN,
2131 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2132 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2134 GtkFileFilter *filter;
2135 // NB file filters are listed this way for alphabetical ordering
2136 #ifdef VIK_CONFIG_GEOCACHES
2137 filter = gtk_file_filter_new ();
2138 gtk_file_filter_set_name( filter, _("Geocaching") );
2139 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2140 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2143 filter = gtk_file_filter_new ();
2144 gtk_file_filter_set_name( filter, _("Google Earth") );
2145 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2146 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2148 filter = gtk_file_filter_new ();
2149 gtk_file_filter_set_name( filter, _("GPX") );
2150 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2151 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2153 filter = gtk_file_filter_new ();
2154 gtk_file_filter_set_name( filter, _("Viking") );
2155 gtk_file_filter_add_pattern ( filter, "*.vik" );
2156 gtk_file_filter_add_pattern ( filter, "*.viking" );
2157 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2159 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2160 // However assume this are barely used and thus not worthy of inclusion
2161 // as they'll just make the options too many and have no clear file pattern
2162 // one can always use the all option
2163 filter = gtk_file_filter_new ();
2164 gtk_file_filter_set_name( filter, _("All") );
2165 gtk_file_filter_add_pattern ( filter, "*" );
2166 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2167 // Default to any file - same as before open filters were added
2168 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2170 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2171 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2172 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2174 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2176 gtk_widget_hide ( vw->open_dia );
2177 #ifdef VIKING_PROMPT_IF_MODIFIED
2178 if ( (vw->modified || vw->filename) && newwindow )
2180 if ( vw->filename && newwindow )
2182 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2184 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2185 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2188 while ( cur_file ) {
2189 gchar *file_name = cur_file->data;
2190 vik_window_open_file ( vw, file_name, change_fn );
2192 cur_file = g_slist_next (cur_file);
2194 g_slist_free (files);
2198 gtk_widget_hide ( vw->open_dia );
2201 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2203 gboolean rv = FALSE;
2205 if ( ! vw->save_dia )
2207 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2209 GTK_FILE_CHOOSER_ACTION_SAVE,
2210 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2211 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2213 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2214 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2217 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2219 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2220 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 ) ) )
2222 window_set_filename ( vw, fn );
2223 rv = window_save ( vw );
2224 vw->modified = FALSE;
2228 gtk_widget_hide ( vw->save_dia );
2232 static gboolean window_save ( VikWindow *vw )
2234 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2236 update_recently_used_document ( vw->filename );
2241 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2246 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2248 if ( ! vw->filename )
2249 return save_file_as ( NULL, vw );
2252 vw->modified = FALSE;
2253 return window_save ( vw );
2257 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2259 // Via the file menu, acquiring from a GPS makes a new layer
2260 // this has always been the way (not entirely sure if this was the real intention!)
2261 // thus maintain the behaviour ATM.
2262 // Hence explicit setting here (as the value may be changed elsewhere)
2263 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2264 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2267 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2269 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2272 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2273 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2275 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2279 #ifdef VIK_CONFIG_OPENSTREETMAP
2280 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2282 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2286 #ifdef VIK_CONFIG_GEOCACHES
2287 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2289 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2293 #ifdef VIK_CONFIG_GEOTAG
2294 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2296 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2297 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2301 static void goto_default_location( GtkAction *a, VikWindow *vw)
2304 ll.lat = a_vik_get_default_lat();
2305 ll.lon = a_vik_get_default_long();
2306 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2307 vik_layers_panel_emit_update(vw->viking_vlp);
2311 static void goto_address( GtkAction *a, VikWindow *vw)
2313 a_vik_goto(vw, vw->viking_vlp, vw->viking_vvp);
2316 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2321 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2323 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2325 a_preferences_show_window ( GTK_WINDOW(vw) );
2327 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2328 if (wp_icon_size != a_vik_get_use_large_waypoint_icons())
2329 clear_garmin_icon_syms ();
2334 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2336 /* Simplistic repeat of preference setting
2337 Only the name & type are important for setting the preference via this 'external' way */
2338 VikLayerParam pref_lat[] = {
2339 { VIKING_PREFERENCES_NAMESPACE "default_latitude",
2340 VIK_LAYER_PARAM_DOUBLE,
2343 VIK_LAYER_WIDGET_SPINBUTTON,
2347 VikLayerParam pref_lon[] = {
2348 { VIKING_PREFERENCES_NAMESPACE "default_longitude",
2349 VIK_LAYER_PARAM_DOUBLE,
2352 VIK_LAYER_WIDGET_SPINBUTTON,
2357 /* Get current center */
2359 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2361 /* Apply to preferences */
2362 VikLayerParamData vlp_data;
2363 vlp_data.d = ll.lat;
2364 a_preferences_run_setparam (vlp_data, pref_lat);
2365 vlp_data.d = ll.lon;
2366 a_preferences_run_setparam (vlp_data, pref_lon);
2367 /* Remember to save */
2368 a_preferences_save_to_file();
2371 static void clear_cb ( GtkAction *a, VikWindow *vw )
2373 vik_layers_panel_clear ( vw->viking_vlp );
2374 window_set_filename ( vw, NULL );
2378 static void window_close ( GtkAction *a, VikWindow *vw )
2380 if ( ! delete_event ( vw ) )
2381 gtk_widget_destroy ( GTK_WIDGET(vw) );
2384 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2386 if (save_file( NULL, vw)) {
2387 window_close( NULL, vw);
2394 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2396 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2397 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2399 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2400 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2405 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2407 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2408 GdkPixbuf *pixbuf_to_save;
2409 gdouble old_xmpp, old_ympp;
2410 GError *error = NULL;
2412 /* backup old zoom & set new */
2413 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2414 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2415 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2417 /* reset width and height: */
2418 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2420 /* draw all layers */
2423 /* save buffer as file. */
2424 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);
2425 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2428 g_warning("Unable to write to file %s: %s", fn, error->message );
2429 g_error_free (error);
2431 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2433 /* pretend like nothing happened ;) */
2434 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2435 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2436 vik_viewport_configure ( vw->viking_vvp );
2440 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 )
2442 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2443 gchar *name_of_file = g_malloc ( size );
2445 struct UTM utm_orig, utm;
2447 /* *** copied from above *** */
2448 GdkPixbuf *pixbuf_to_save;
2449 gdouble old_xmpp, old_ympp;
2450 GError *error = NULL;
2452 /* backup old zoom & set new */
2453 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2454 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2455 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2457 /* reset width and height: do this only once for all images (same size) */
2458 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2459 /* *** end copy from above *** */
2461 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2465 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2467 for ( y = 1; y <= tiles_h; y++ )
2469 for ( x = 1; x <= tiles_w; x++ )
2471 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2473 if ( tiles_w & 0x1 )
2474 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2476 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2477 if ( tiles_h & 0x1 ) /* odd */
2478 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2480 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2482 /* move to correct place. */
2483 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2487 /* save buffer as file. */
2488 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);
2489 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2492 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2493 g_error_free (error);
2496 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2500 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2501 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2502 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2503 vik_viewport_configure ( vw->viking_vvp );
2506 g_free ( name_of_file );
2509 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2511 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2512 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2513 GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]);
2514 gdouble width_min, width_max, height_min, height_max;
2517 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2518 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2520 /* TODO: support for xzoom and yzoom values */
2521 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin );
2522 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin );
2524 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2525 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2527 gtk_spin_button_set_value ( width_spin, width );
2528 gtk_spin_button_set_value ( height_spin, height );
2531 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2533 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2534 GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]);
2537 w = gtk_spin_button_get_value(width_spin) * gtk_spin_button_get_value(zoom_spin);
2538 h = gtk_spin_button_get_value(height_spin) * gtk_spin_button_get_value(zoom_spin);
2539 if (pass_along[4]) /* save many images; find TOTAL area covered */
2541 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2542 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2544 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2545 switch (dist_units) {
2546 case VIK_UNITS_DISTANCE_KILOMETRES:
2547 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2549 case VIK_UNITS_DISTANCE_MILES:
2550 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2553 label_text = g_strdup_printf ("Just to keep the compiler happy");
2554 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2557 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2558 g_free ( label_text );
2561 static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_image_only )
2563 /* todo: default for answers inside VikWindow or static (thruout instance) */
2564 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2565 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2567 GTK_RESPONSE_REJECT,
2569 GTK_RESPONSE_ACCEPT,
2571 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2572 GtkWidget *png_radio, *jpeg_radio;
2573 GtkWidget *current_window_button;
2574 gpointer current_window_pass_along[7];
2575 GtkWidget *zoom_label, *zoom_spin;
2576 GtkWidget *total_size_label;
2578 /* only used if (!one_image_only) */
2579 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2582 width_label = gtk_label_new ( _("Width (pixels):") );
2583 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 5000, 10, 100, 0 )), 10, 0 );
2584 height_label = gtk_label_new ( _("Height (pixels):") );
2585 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 5000, 10, 100, 0 )), 10, 0 );
2587 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2588 /* TODO: separate xzoom and yzoom factors */
2589 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);
2591 total_size_label = gtk_label_new ( NULL );
2593 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2594 current_window_pass_along [0] = vw;
2595 current_window_pass_along [1] = width_spin;
2596 current_window_pass_along [2] = height_spin;
2597 current_window_pass_along [3] = zoom_spin;
2598 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
2599 current_window_pass_along [5] = NULL;
2600 current_window_pass_along [6] = total_size_label;
2601 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
2603 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
2604 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
2606 if ( ! vw->draw_image_save_as_png )
2607 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
2609 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
2610 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
2611 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
2612 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
2613 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
2614 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
2615 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
2616 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
2617 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_spin, FALSE, FALSE, 0);
2619 if ( ! one_image_only )
2621 GtkWidget *tiles_width_label, *tiles_height_label;
2624 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
2625 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2626 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
2627 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2628 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
2629 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
2630 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
2631 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
2633 current_window_pass_along [4] = tiles_width_spin;
2634 current_window_pass_along [5] = tiles_height_spin;
2635 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2636 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2638 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
2639 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2640 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2641 g_signal_connect ( G_OBJECT(zoom_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2643 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
2645 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2647 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
2649 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
2651 gtk_widget_hide ( GTK_WIDGET(dialog) );
2652 if ( one_image_only )
2653 save_image_file ( vw, fn,
2654 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2655 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2656 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */
2657 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
2659 // NB is in UTM mode ATM
2660 save_image_dir ( vw, fn,
2661 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2662 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2663 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */
2664 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
2665 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
2666 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
2669 gtk_widget_destroy ( GTK_WIDGET(dialog) );
2673 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
2676 if (!vw->save_img_dia) {
2677 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2679 GTK_FILE_CHOOSER_ACTION_SAVE,
2680 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2681 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2683 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2684 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2687 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT )
2689 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2690 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 ) ) )
2692 draw_to_image_file ( vw, fn, TRUE );
2698 gtk_widget_hide ( vw->save_img_dia );
2701 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
2705 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2706 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2710 if (!vw->save_img_dir_dia) {
2711 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2713 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2714 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2715 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2717 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2718 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2721 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT )
2723 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2726 draw_to_image_file ( vw, fn, FALSE );
2732 gtk_widget_hide ( vw->save_img_dir_dia );
2735 #if GTK_CHECK_VERSION(2,10,0)
2736 static void print_cb ( GtkAction *a, VikWindow *vw )
2738 a_print(vw, vw->viking_vvp);
2742 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
2743 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
2745 VikViewportDrawMode drawmode;
2746 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
2747 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
2749 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
2750 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
2752 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
2753 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
2755 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
2756 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
2759 g_critical("Houston, we've had a problem.");
2763 if ( !vw->only_updating_coord_mode_ui )
2765 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
2766 if ( olddrawmode != drawmode )
2768 /* this takes care of coord mode too */
2769 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
2770 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
2771 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
2772 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
2773 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
2780 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
2782 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2783 g_assert(check_box);
2784 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2785 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
2789 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
2791 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2792 g_assert(check_box);
2793 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2794 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
2798 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
2800 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2801 g_assert(check_box);
2802 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2803 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
2807 static void set_bg_color ( GtkAction *a, VikWindow *vw )
2809 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
2810 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
2811 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2812 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2813 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
2815 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2816 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
2820 gtk_widget_destroy ( colorsd );
2823 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
2825 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
2826 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
2827 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2828 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2829 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
2831 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2832 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
2836 gtk_widget_destroy ( colorsd );
2841 /***********************************************************************************************
2843 ***********************************************************************************************/
2845 static GtkActionEntry entries[] = {
2846 { "File", NULL, N_("_File"), 0, 0, 0 },
2847 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
2848 { "View", NULL, N_("_View"), 0, 0, 0 },
2849 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
2850 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
2851 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
2852 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
2853 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
2854 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
2855 { "Help", NULL, N_("_Help"), 0, 0, 0 },
2857 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
2858 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
2859 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
2860 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
2861 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
2862 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
2863 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
2864 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2865 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
2867 #ifdef VIK_CONFIG_OPENSTREETMAP
2868 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
2870 #ifdef VIK_CONFIG_GEOCACHES
2871 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
2873 #ifdef VIK_CONFIG_GEOTAG
2874 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
2876 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
2877 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
2878 { "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 },
2879 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
2881 #if GTK_CHECK_VERSION(2,10,0)
2882 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
2885 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
2886 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
2888 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
2889 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
2890 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
2891 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
2892 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
2893 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
2894 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
2895 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
2896 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
2897 { "Zoom0.25", NULL, N_("0.25"), NULL, NULL, (GCallback)draw_zoom_cb },
2898 { "Zoom0.5", NULL, N_("0.5"), NULL, NULL, (GCallback)draw_zoom_cb },
2899 { "Zoom1", NULL, N_("1"), NULL, NULL, (GCallback)draw_zoom_cb },
2900 { "Zoom2", NULL, N_("2"), NULL, NULL, (GCallback)draw_zoom_cb },
2901 { "Zoom4", NULL, N_("4"), NULL, NULL, (GCallback)draw_zoom_cb },
2902 { "Zoom8", NULL, N_("8"), NULL, NULL, (GCallback)draw_zoom_cb },
2903 { "Zoom16", NULL, N_("16"), NULL, NULL, (GCallback)draw_zoom_cb },
2904 { "Zoom32", NULL, N_("32"), NULL, NULL, (GCallback)draw_zoom_cb },
2905 { "Zoom64", NULL, N_("64"), NULL, NULL, (GCallback)draw_zoom_cb },
2906 { "Zoom128", NULL, N_("128"), NULL, NULL, (GCallback)draw_zoom_cb },
2907 { "Zoom256", NULL, N_("256"), NULL, NULL, (GCallback)draw_zoom_cb },
2908 { "Zoom512", NULL, N_("512"), NULL, NULL, (GCallback)draw_zoom_cb },
2909 { "Zoom1024", NULL, N_("1024"), NULL, NULL, (GCallback)draw_zoom_cb },
2910 { "Zoom2048", NULL, N_("2048"), NULL, NULL, (GCallback)draw_zoom_cb },
2911 { "Zoom4096", NULL, N_("4096"), NULL, NULL, (GCallback)draw_zoom_cb },
2912 { "Zoom8192", NULL, N_("8192"), NULL, NULL, (GCallback)draw_zoom_cb },
2913 { "Zoom16384", NULL, N_("16384"), NULL, NULL, (GCallback)draw_zoom_cb },
2914 { "Zoom32768", NULL, N_("32768"), NULL, NULL, (GCallback)draw_zoom_cb },
2915 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
2916 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
2917 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
2918 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
2919 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
2921 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
2922 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
2923 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
2924 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
2925 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
2926 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
2927 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
2928 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
2929 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
2931 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
2932 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
2936 /* FIXME use VIEWPORT_DRAWMODE values */
2937 static GtkRadioActionEntry mode_entries[] = {
2938 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
2939 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
2940 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
2941 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
2944 static GtkToggleActionEntry toggle_entries[] = {
2945 { "ShowScale", NULL, N_("Show _Scale"), "F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
2946 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
2947 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
2948 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
2949 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
2950 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
2951 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
2952 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
2955 #include "menu.xml.h"
2956 static void window_create_ui( VikWindow *window )
2959 GtkActionGroup *action_group;
2960 GtkAccelGroup *accel_group;
2963 GtkIconFactory *icon_factory;
2964 GtkIconSet *icon_set;
2965 GtkRadioActionEntry *tools = NULL, *radio;
2968 uim = gtk_ui_manager_new ();
2971 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
2972 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
2973 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
2974 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
2977 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
2978 g_error_free (error);
2982 action_group = gtk_action_group_new ("MenuActions");
2983 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
2984 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
2985 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
2986 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
2988 icon_factory = gtk_icon_factory_new ();
2989 gtk_icon_factory_add_default (icon_factory);
2991 register_vik_icons(icon_factory);
2993 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
2994 // so that it can be applied to the UI in one action group add function call below
2996 for (i=0; i<window->vt->n_tools; i++) {
2997 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
2998 radio = &tools[ntools];
3000 *radio = window->vt->tools[i].ti.radioActionEntry;
3001 radio->value = ntools;
3004 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3005 GtkActionEntry action;
3006 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3007 vik_layer_get_interface(i)->name,
3008 vik_layer_get_interface(i)->name,
3009 GTK_UI_MANAGER_MENUITEM, FALSE);
3011 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3012 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3013 gtk_icon_set_unref (icon_set);
3015 action.name = vik_layer_get_interface(i)->name;
3016 action.stock_id = vik_layer_get_interface(i)->name;
3017 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3018 action.accelerator = vik_layer_get_interface(i)->accelerator;
3019 action.tooltip = NULL;
3020 action.callback = (GCallback)menu_addlayer_cb;
3021 gtk_action_group_add_actions(action_group, &action, 1, window);
3023 if ( vik_layer_get_interface(i)->tools_count ) {
3024 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3025 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3028 // Further tool copying for to apply to the UI, also apply menu UI setup
3029 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3030 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3031 radio = &tools[ntools];
3034 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3035 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3036 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3037 GTK_UI_MANAGER_MENUITEM, FALSE);
3038 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3039 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3040 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3041 GTK_UI_MANAGER_TOOLITEM, FALSE);
3043 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3045 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3046 // Overwrite with actual number to use
3047 radio->value = ntools;
3050 g_object_unref (icon_factory);
3052 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3055 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3057 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3058 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3059 GtkAction *action = gtk_action_group_get_action(action_group,
3060 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3061 g_object_set(action, "sensitive", FALSE, NULL);
3064 window->action_group = action_group;
3066 accel_group = gtk_ui_manager_get_accel_group (uim);
3067 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3068 gtk_ui_manager_ensure_update (uim);
3070 setup_recent_files(window);
3074 // TODO - add method to add tool icons defined from outside this file
3075 // and remove the reverse dependency on icon definition from this file
3077 const GdkPixdata *data;
3080 { &mover_22_pixbuf, "vik-icon-pan" },
3081 { &zoom_18_pixbuf, "vik-icon-zoom" },
3082 { &ruler_18_pixbuf, "vik-icon-ruler" },
3083 { &select_18_pixbuf, "vik-icon-select" },
3084 { &begintr_18_pixbuf, "vik-icon-Begin Track" },
3085 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3086 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3087 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3088 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3089 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3090 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3091 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3092 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3093 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3094 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3097 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3100 register_vik_icons (GtkIconFactory *icon_factory)
3102 GtkIconSet *icon_set;
3105 for (i = 0; i < n_stock_icons; i++) {
3106 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3107 stock_icons[i].data, FALSE, NULL ));
3108 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3109 gtk_icon_set_unref (icon_set);
3113 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3115 return vw->selected_vtl;
3118 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3120 vw->selected_vtl = vtl;
3121 vw->containing_vtl = vtl;
3123 vw->selected_track = NULL;
3124 vw->selected_tracks = NULL;
3125 vw->selected_waypoint = NULL;
3126 vw->selected_waypoints = NULL;
3127 // Set highlight thickness
3128 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3131 gpointer vik_window_get_selected_tracks ( VikWindow *vw )
3133 return vw->selected_tracks;
3136 void vik_window_set_selected_tracks ( VikWindow *vw, gpointer gl, gpointer vtl )
3138 vw->selected_tracks = gl;
3139 vw->containing_vtl = vtl;
3141 vw->selected_vtl = NULL;
3142 vw->selected_track = NULL;
3143 vw->selected_waypoint = NULL;
3144 vw->selected_waypoints = NULL;
3145 // Set highlight thickness
3146 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3149 gpointer vik_window_get_selected_track ( VikWindow *vw )
3151 return vw->selected_track;
3154 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl, gpointer name )
3156 vw->selected_track = vt;
3157 vw->containing_vtl = vtl;
3159 vw->selected_vtl = NULL;
3160 vw->selected_tracks = NULL;
3161 vw->selected_waypoint = NULL;
3162 vw->selected_waypoints = NULL;
3163 // Set highlight thickness
3164 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3167 gpointer vik_window_get_selected_waypoints ( VikWindow *vw )
3169 return vw->selected_waypoints;
3172 void vik_window_set_selected_waypoints ( VikWindow *vw, gpointer gl, gpointer vtl )
3174 vw->selected_waypoints = gl;
3175 vw->containing_vtl = vtl;
3177 vw->selected_vtl = NULL;
3178 vw->selected_track = NULL;
3179 vw->selected_tracks = NULL;
3180 vw->selected_waypoint = NULL;
3183 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3185 return vw->selected_waypoint;
3188 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl, gpointer name )
3190 vw->selected_waypoint = vwp;
3191 vw->containing_vtl = vtl;
3193 vw->selected_vtl = NULL;
3194 vw->selected_track = NULL;
3195 vw->selected_tracks = NULL;
3196 vw->selected_waypoints = NULL;
3199 gboolean vik_window_clear_highlight ( VikWindow *vw )
3201 gboolean need_redraw = FALSE;
3202 if ( vw->selected_vtl != NULL ) {
3203 vw->selected_vtl = NULL;
3206 if ( vw->selected_track != NULL ) {
3207 vw->selected_track = NULL;
3210 if ( vw->selected_tracks != NULL ) {
3211 vw->selected_tracks = NULL;
3214 if ( vw->selected_waypoint != NULL ) {
3215 vw->selected_waypoint = NULL;
3218 if ( vw->selected_waypoints != NULL ) {
3219 vw->selected_waypoints = NULL;