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 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
493 if ( vw->current_tool < TOOL_LAYER ) {
494 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
495 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
496 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
500 /* Restore Main Menu via Escape key if the user has hidden it */
501 /* This key is more likely to be used as they may not remember the function key */
502 if ( event->keyval == GDK_Escape ) {
503 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
505 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
507 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
508 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
509 return TRUE; /* handled keypress */
514 return FALSE; /* don't handle the keypress */
517 static gboolean delete_event( VikWindow *vw )
519 #ifdef VIKING_PROMPT_IF_MODIFIED
526 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
527 _("Do you want to save the changes you made to the document \"%s\"?\n"
529 "Your changes will be lost if you don't save them."),
530 vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") ) );
531 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
532 switch ( gtk_dialog_run ( dia ) )
534 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
535 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
536 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
543 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
545 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
548 static void draw_update ( VikWindow *vw )
554 static void draw_sync ( VikWindow *vw )
556 vik_viewport_sync(vw->viking_vvp);
558 /* other things may be necc here later. */
561 static void draw_status ( VikWindow *vw )
563 static gchar zoom_level[22];
564 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
565 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
566 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
568 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
570 if ( (int)xmpp - xmpp < 0.0 )
571 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
573 /* xmpp should be a whole number so don't show useless .000 bit */
574 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
575 if ( vw->current_tool == TOOL_LAYER )
576 // Use tooltip rather than the internal name as the tooltip is i8n
577 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 );
579 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
581 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
584 void vik_window_set_redraw_trigger(VikLayer *vl)
586 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
591 static void window_configure_event ( VikWindow *vw )
593 static int first = 1;
596 // This is a hack to set the cursor corresponding to the first tool
597 // FIXME find the correct way to initialize both tool and its cursor
598 const GdkCursor *cursor = NULL;
600 cursor = toolbox_get_cursor(vw->vt, "Pan");
601 /* We set cursor, even if it is NULL: it resets to default */
602 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
606 static void draw_redraw ( VikWindow *vw )
608 VikCoord old_center = vw->trigger_center;
609 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
610 VikLayer *new_trigger = vw->trigger;
612 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
615 ; /* do nothing -- have to redraw everything. */
616 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
617 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
619 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
622 vik_viewport_clear ( vw->viking_vvp);
623 vik_layers_panel_draw_all ( vw->viking_vlp );
624 vik_viewport_draw_scale ( vw->viking_vvp );
625 vik_viewport_draw_copyright ( vw->viking_vvp );
626 vik_viewport_draw_centermark ( vw->viking_vvp );
627 vik_viewport_draw_logo ( vw->viking_vvp );
629 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
632 gboolean draw_buf_done = TRUE;
634 static gboolean draw_buf(gpointer data)
636 gpointer *pass_along = data;
638 gdk_draw_drawable (pass_along[0], pass_along[1],
639 pass_along[2], 0, 0, 0, 0, -1, -1);
640 draw_buf_done = TRUE;
646 /* Mouse event handlers ************************************************************************/
648 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
650 /* set panning origin */
651 vw->pan_move = FALSE;
652 vw->pan_x = (gint) event->x;
653 vw->pan_y = (gint) event->y;
656 static void draw_click (VikWindow *vw, GdkEventButton *event)
658 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
660 /* middle button pressed. we reserve all middle button and scroll events
661 * for panning and zooming; tools only get left/right/movement
663 if ( event->button == 2) {
664 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
665 // Tool still may need to do something (such as disable something)
666 toolbox_click(vw->vt, event);
667 vik_window_pan_click ( vw, event );
670 toolbox_click(vw->vt, event);
674 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
676 if ( vw->pan_x != -1 ) {
677 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
678 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
680 vw->pan_x = event->x;
681 vw->pan_y = event->y;
686 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
688 static VikCoord coord;
689 static struct UTM utm;
690 static struct LatLon ll;
691 #define BUFFER_SIZE 50
692 static char pointer_buf[BUFFER_SIZE];
693 gchar *lat = NULL, *lon = NULL;
696 VikDemInterpol interpol_method;
698 /* This is a hack, but work far the best, at least for single pointer systems.
699 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
701 gdk_window_get_pointer (event->window, &x, &y, NULL);
705 toolbox_move(vw->vt, event);
707 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
708 vik_coord_to_utm ( &coord, &utm );
709 a_coords_utm_to_latlon ( &utm, &ll );
710 a_coords_latlon_to_string ( &ll, &lat, &lon );
711 /* Change interpolate method according to scale */
712 zoom = vik_viewport_get_zoom(vw->viking_vvp);
714 interpol_method = VIK_DEM_INTERPOL_NONE;
715 else if (zoom >= 1.0)
716 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
718 interpol_method = VIK_DEM_INTERPOL_BEST;
719 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
720 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
721 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
723 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
726 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
731 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
733 vik_window_pan_move ( vw, event );
735 /* This is recommended by the GTK+ documentation, but does not work properly.
736 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
737 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
739 /* gdk_event_request_motions ( event ); */
742 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
744 if ( vw->pan_move == FALSE )
745 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
747 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
748 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
749 vw->pan_move = FALSE;
750 vw->pan_x = vw->pan_y = -1;
754 static void draw_release ( VikWindow *vw, GdkEventButton *event )
756 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
758 if ( event->button == 2 ) { /* move / pan */
759 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
760 // Tool still may need to do something (such as reenable something)
761 toolbox_release(vw->vt, event);
762 vik_window_pan_release ( vw, event );
765 toolbox_release(vw->vt, event);
769 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
771 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
772 if ( modifiers == GDK_CONTROL_MASK ) {
773 /* control == pan up & down */
774 if ( event->direction == GDK_SCROLL_UP )
775 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
777 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 );
778 } else if ( modifiers == GDK_SHIFT_MASK ) {
779 /* shift == pan left & right */
780 if ( event->direction == GDK_SCROLL_UP )
781 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
783 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 );
784 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
785 // This zoom is on the center position
786 if ( event->direction == GDK_SCROLL_UP )
787 vik_viewport_zoom_in (vw->viking_vvp);
789 vik_viewport_zoom_out (vw->viking_vvp);
791 /* make sure mouse is still over the same point on the map when we zoom */
794 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
795 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
796 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
797 if ( event->direction == GDK_SCROLL_UP )
798 vik_viewport_zoom_in (vw->viking_vvp);
800 vik_viewport_zoom_out(vw->viking_vvp);
801 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
802 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
803 center_y + (y - event->y) );
811 /********************************************************************************
813 ********************************************************************************/
814 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
816 PangoFontDescription *pfd;
819 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
820 GdkGC *thickgc = gdk_gc_new(d);
822 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
823 gdouble dx = (x2-x1)/len*10;
824 gdouble dy = (y2-y1)/len*10;
825 gdouble c = cos(15.0 * M_PI/180.0);
826 gdouble s = sin(15.0 * M_PI/180.0);
828 gdouble baseangle = 0;
831 /* draw line with arrow ends */
833 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
834 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
835 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
838 a_viewport_clip_line(&x1, &y1, &x2, &y2);
839 gdk_draw_line(d, gc, x1, y1, x2, y2);
841 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
842 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
843 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
844 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
845 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
846 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
851 angle = atan2(dy, dx) + M_PI_2;
853 if ( vik_viewport_get_drawmode ( vvp ) == VIK_VIEWPORT_DRAWMODE_UTM) {
859 vik_viewport_screen_to_coord ( vvp, x1, y1, &test );
860 vik_coord_to_latlon ( &test, &ll );
861 ll.lat += vik_viewport_get_ympp ( vvp ) * vik_viewport_get_height ( vvp ) / 11000.0; // about 11km per degree latitude
862 a_coords_latlon_to_utm ( &ll, &u );
863 vik_coord_load_from_utm ( &test, VIK_VIEWPORT_DRAWMODE_UTM, &u );
864 vik_viewport_coord_to_screen ( vvp, &test, &tx, &ty );
866 baseangle = M_PI - atan2(tx-x1, ty-y1);
877 gdk_gc_copy(thickgc, gc);
878 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
879 gdk_color_parse("#2255cc", &color);
880 gdk_gc_set_rgb_fg_color(thickgc, &color);
882 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);
885 gdk_gc_copy(thickgc, gc);
886 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
887 for (i=0; i<180; i++) {
888 c = cos(i*M_PI/90.0 + baseangle);
889 s = sin(i*M_PI/90.0 + baseangle);
892 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
894 gdouble ticksize = 2*CW;
895 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
899 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
900 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
901 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
902 c = (CR+CW*2)*cos(baseangle);
903 s = (CR+CW*2)*sin(baseangle);
904 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
905 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
908 #define LABEL(x, y, w, h) { \
909 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
910 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
911 gdk_draw_layout(d, gc, (x), (y), pl); }
916 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
918 pfd = pango_font_description_from_string ("Sans 8"); // FIXME: settable option? global variable?
919 pango_layout_set_font_description (pl, pfd);
920 pango_font_description_free (pfd);
922 pango_layout_set_text(pl, "N", -1);
923 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
925 /* draw label with distance */
926 vik_units_distance_t dist_units = a_vik_get_units_distance ();
927 switch (dist_units) {
928 case VIK_UNITS_DISTANCE_KILOMETRES:
929 if (distance >= 1000 && distance < 100000) {
930 g_sprintf(str, "%3.2f km", distance/1000.0);
931 } else if (distance < 1000) {
932 g_sprintf(str, "%d m", (int)distance);
934 g_sprintf(str, "%d km", (int)distance/1000);
937 case VIK_UNITS_DISTANCE_MILES:
938 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
939 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
940 } else if (distance < VIK_MILES_TO_METERS(1)) {
941 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
943 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
947 g_critical("Houston, we've had a problem. distance=%d", dist_units);
950 pango_layout_set_text(pl, str, -1);
952 pango_layout_get_pixel_size ( pl, &wd, &hd );
955 yd = (y1+y2)/2 - hd/2 - dx;
958 yd = (y1+y2)/2 - hd/2 + dx;
961 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
966 LABEL(xd, yd, wd, hd);
968 /* draw label with bearing */
969 g_sprintf(str, "%3.1f°", angle*180.0/M_PI);
970 pango_layout_set_text(pl, str, -1);
971 pango_layout_get_pixel_size ( pl, &wb, &hb );
972 xb = x1 + CR*cos(angle-M_PI_2);
973 yb = y1 + CR*sin(angle-M_PI_2);
975 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
981 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
982 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
986 LABEL(xb, yb, wb, hb);
990 g_object_unref ( G_OBJECT ( pl ) );
991 g_object_unref ( G_OBJECT ( labgc ) );
992 g_object_unref ( G_OBJECT ( thickgc ) );
998 gboolean has_oldcoord;
1000 } ruler_tool_state_t;
1002 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1004 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1007 s->has_oldcoord = FALSE;
1011 static void ruler_destroy (ruler_tool_state_t *s)
1016 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1021 if ( event->button == 1 ) {
1022 gchar *lat=NULL, *lon=NULL;
1023 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1024 vik_coord_to_latlon ( &coord, &ll );
1025 a_coords_latlon_to_string ( &ll, &lat, &lon );
1026 if ( s->has_oldcoord ) {
1027 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1028 switch (dist_units) {
1029 case VIK_UNITS_DISTANCE_KILOMETRES:
1030 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1032 case VIK_UNITS_DISTANCE_MILES:
1033 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1036 temp = g_strdup_printf ("Just to keep the compiler happy");
1037 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1040 s->has_oldcoord = FALSE;
1043 temp = g_strdup_printf ( "%s %s", lat, lon );
1044 s->has_oldcoord = TRUE;
1047 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1050 s->oldcoord = coord;
1053 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1054 draw_update ( s->vw );
1056 return VIK_LAYER_TOOL_ACK;
1059 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1061 VikViewport *vvp = s->vvp;
1062 VikWindow *vw = s->vw;
1068 if ( s->has_oldcoord ) {
1069 int oldx, oldy, w1, h1, w2, h2;
1070 static GdkPixmap *buf = NULL;
1071 gchar *lat=NULL, *lon=NULL;
1072 w1 = vik_viewport_get_width(vvp);
1073 h1 = vik_viewport_get_height(vvp);
1075 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1077 gdk_drawable_get_size(buf, &w2, &h2);
1078 if (w1 != w2 || h1 != h2) {
1079 g_object_unref ( G_OBJECT ( buf ) );
1080 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1083 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1084 vik_coord_to_latlon ( &coord, &ll );
1085 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1087 gdk_draw_drawable (buf, GTK_WIDGET(vvp)->style->black_gc,
1088 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1089 draw_ruler(vvp, buf, GTK_WIDGET(vvp)->style->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1090 if (draw_buf_done) {
1091 static gpointer pass_along[3];
1092 pass_along[0] = GTK_WIDGET(vvp)->window;
1093 pass_along[1] = GTK_WIDGET(vvp)->style->black_gc;
1094 pass_along[2] = buf;
1095 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1096 draw_buf_done = FALSE;
1098 a_coords_latlon_to_string(&ll, &lat, &lon);
1099 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1100 switch (dist_units) {
1101 case VIK_UNITS_DISTANCE_KILOMETRES:
1102 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1104 case VIK_UNITS_DISTANCE_MILES:
1105 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1108 temp = g_strdup_printf ("Just to keep the compiler happy");
1109 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1111 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1114 return VIK_LAYER_TOOL_ACK;
1117 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1119 return VIK_LAYER_TOOL_ACK;
1122 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1124 draw_update ( s->vw );
1127 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1129 if (event->keyval == GDK_Escape) {
1130 s->has_oldcoord = FALSE;
1131 ruler_deactivate ( vl, s );
1137 static VikToolInterface ruler_tool =
1138 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>R", N_("Ruler Tool"), 2 },
1139 (VikToolConstructorFunc) ruler_create,
1140 (VikToolDestructorFunc) ruler_destroy,
1141 (VikToolActivationFunc) NULL,
1142 (VikToolActivationFunc) ruler_deactivate,
1143 (VikToolMouseFunc) ruler_click,
1144 (VikToolMouseMoveFunc) ruler_move,
1145 (VikToolMouseFunc) ruler_release,
1146 (VikToolKeyFunc) ruler_key_press,
1148 GDK_CURSOR_IS_PIXMAP,
1149 &cursor_ruler_pixbuf };
1150 /*** end ruler code ********************************************************/
1154 /********************************************************************************
1156 ********************************************************************************/
1161 // Track zoom bounds for zoom tool with shift modifier:
1162 gboolean bounds_active;
1165 } zoom_tool_state_t;
1168 * In case the screen size has changed
1170 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1174 // Allocate a drawing area the size of the viewport
1175 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1176 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1178 if ( !zts->pixmap ) {
1180 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1183 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1185 if ( w1 != w2 || h1 != h2 ) {
1186 // Has changed - delete and recreate with new values
1187 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1188 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1192 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1194 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1199 zts->bounds_active = FALSE;
1203 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1206 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1210 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1212 zts->vw->modified = TRUE;
1213 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1217 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1218 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1220 gboolean skip_update = FALSE;
1222 zts->bounds_active = FALSE;
1224 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1225 // This zoom is on the center position
1226 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1227 if ( event->button == 1 )
1228 vik_viewport_zoom_in (zts->vw->viking_vvp);
1229 else if ( event->button == 3 )
1230 vik_viewport_zoom_out (zts->vw->viking_vvp);
1232 else if ( modifiers == GDK_CONTROL_MASK ) {
1233 // This zoom is to recenter on the mouse position
1234 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1235 if ( event->button == 1 )
1236 vik_viewport_zoom_in (zts->vw->viking_vvp);
1237 else if ( event->button == 3 )
1238 vik_viewport_zoom_out (zts->vw->viking_vvp);
1240 else if ( modifiers == GDK_SHIFT_MASK ) {
1241 // Get start of new zoom bounds
1242 if ( event->button == 1 ) {
1243 zts->bounds_active = TRUE;
1244 zts->start_x = (gint) event->x;
1245 zts->start_y = (gint) event->y;
1250 /* make sure mouse is still over the same point on the map when we zoom */
1251 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1252 if ( event->button == 1 )
1253 vik_viewport_zoom_in (zts->vw->viking_vvp);
1254 else if ( event->button == 3 )
1255 vik_viewport_zoom_out(zts->vw->viking_vvp);
1256 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1257 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1258 center_x + (x - event->x),
1259 center_y + (y - event->y) );
1263 draw_update ( zts->vw );
1265 return VIK_LAYER_TOOL_ACK;
1268 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1270 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1272 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1273 zoomtool_resize_pixmap ( zts );
1275 // Blank out currently drawn area
1276 gdk_draw_drawable ( zts->pixmap,
1277 GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc,
1278 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1279 0, 0, 0, 0, -1, -1);
1281 // Calculate new box starting point & size in pixels
1282 int xx, yy, width, height;
1283 if ( event->y > zts->start_y ) {
1285 height = event->y-zts->start_y;
1289 height = zts->start_y-event->y;
1291 if ( event->x > zts->start_x ) {
1293 width = event->x-zts->start_x;
1297 width = zts->start_x-event->x;
1301 gdk_draw_rectangle (zts->pixmap, GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, FALSE, xx, yy, width, height);
1303 // Only actually draw when there's time to do so
1304 if (draw_buf_done) {
1305 static gpointer pass_along[3];
1306 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1307 pass_along[1] = GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc;
1308 pass_along[2] = zts->pixmap;
1309 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1310 draw_buf_done = FALSE;
1313 return VIK_LAYER_TOOL_ACK;
1316 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1318 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1320 zts->bounds_active = FALSE;
1322 // Ensure haven't just released on the exact same position
1323 // i.e. probably haven't moved the mouse at all
1324 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1326 VikCoord coord1, coord2;
1327 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1328 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1330 // From the extend of the bounds pick the best zoom level
1331 // c.f. trw_layer_zoom_to_show_latlons()
1332 // Maybe refactor...
1333 struct LatLon ll1, ll2;
1334 vik_coord_to_latlon(&coord1, &ll1);
1335 vik_coord_to_latlon(&coord2, &ll2);
1336 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1337 (ll1.lon+ll2.lon)/2 };
1339 VikCoord new_center;
1340 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1341 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1343 /* Convert into definite 'smallest' and 'largest' positions */
1344 struct LatLon minmin;
1345 if ( ll1.lat < ll2.lat )
1346 minmin.lat = ll1.lat;
1348 minmin.lat = ll2.lat;
1350 struct LatLon maxmax;
1351 if ( ll1.lon > ll2.lon )
1352 maxmax.lon = ll1.lon;
1354 maxmax.lon = ll2.lon;
1356 /* Always recalculate the 'best' zoom level */
1357 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1358 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1360 gdouble min_lat, max_lat, min_lon, max_lon;
1361 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1362 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1363 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1364 /* NB I think the logic used in this test to determine if the bounds is within view
1365 fails if track goes across 180 degrees longitude.
1366 Hopefully that situation is not too common...
1367 Mind you viking doesn't really do edge locations to well anyway */
1368 if ( min_lat < minmin.lat &&
1369 max_lat > minmin.lat &&
1370 min_lon < maxmax.lon &&
1371 max_lon > maxmax.lon )
1372 /* Found within zoom level */
1377 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1380 draw_update ( zts->vw );
1382 return VIK_LAYER_TOOL_ACK;
1385 static VikToolInterface zoom_tool =
1386 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1387 (VikToolConstructorFunc) zoomtool_create,
1388 (VikToolDestructorFunc) zoomtool_destroy,
1389 (VikToolActivationFunc) NULL,
1390 (VikToolActivationFunc) NULL,
1391 (VikToolMouseFunc) zoomtool_click,
1392 (VikToolMouseMoveFunc) zoomtool_move,
1393 (VikToolMouseFunc) zoomtool_release,
1396 GDK_CURSOR_IS_PIXMAP,
1397 &cursor_zoom_pixbuf };
1398 /*** end zoom code ********************************************************/
1400 /********************************************************************************
1402 ********************************************************************************/
1403 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1408 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1410 vw->modified = TRUE;
1411 if ( event->button == 1 )
1412 vik_window_pan_click ( vw, event );
1414 return VIK_LAYER_TOOL_ACK;
1417 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1419 vik_window_pan_move ( vw, event );
1420 return VIK_LAYER_TOOL_ACK;
1423 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1425 if ( event->button == 1 )
1426 vik_window_pan_release ( vw, event );
1427 return VIK_LAYER_TOOL_ACK;
1430 static VikToolInterface pan_tool =
1431 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1432 (VikToolConstructorFunc) pantool_create,
1433 (VikToolDestructorFunc) NULL,
1434 (VikToolActivationFunc) NULL,
1435 (VikToolActivationFunc) NULL,
1436 (VikToolMouseFunc) pantool_click,
1437 (VikToolMouseMoveFunc) pantool_move,
1438 (VikToolMouseFunc) pantool_release,
1442 /*** end pan code ********************************************************/
1444 /********************************************************************************
1446 ********************************************************************************/
1447 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1449 tool_ed_t *t = g_new(tool_ed_t, 1);
1453 t->is_waypoint = FALSE;
1457 static void selecttool_destroy (tool_ed_t *t)
1465 GdkEventButton *event;
1466 tool_ed_t *tool_edit;
1469 static void click_layer_selected (VikLayer *vl, clicker *ck)
1471 /* Do nothing when function call returns true; */
1472 /* i.e. stop on first found item */
1475 if ( vik_layer_get_interface(vl->type)->select_click )
1476 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1479 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1481 /* Only allow selection on primary button */
1482 if ( event->button == 1 ) {
1483 /* Enable click to apply callback to potentially all track/waypoint layers */
1484 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1485 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1488 ck.vvp = t->vw->viking_vvp;
1491 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1494 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1497 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1499 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1500 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1501 gint type = vik_treeview_item_get_type ( vtv, &iter );
1502 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1503 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1505 vik_treeview_item_unselect ( vtv, &iter );
1506 if ( vik_window_clear_highlight ( t->vw ) )
1507 draw_update ( t->vw );
1512 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1514 /* Act on currently selected item to show menu */
1515 if ( t->vw->selected_track || t->vw->selected_waypoint )
1516 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1517 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1520 return VIK_LAYER_TOOL_ACK;
1523 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1525 /* Only allow selection on primary button */
1526 if ( event->button == 1 ) {
1527 // Don't care about vl here
1529 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1530 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1532 return VIK_LAYER_TOOL_ACK;
1535 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1537 /* Only allow selection on primary button */
1538 if ( event->button == 1 ) {
1539 // Don't care about vl here
1541 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1542 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1544 return VIK_LAYER_TOOL_ACK;
1547 static VikToolInterface select_tool =
1548 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1549 (VikToolConstructorFunc) selecttool_create,
1550 (VikToolDestructorFunc) selecttool_destroy,
1551 (VikToolActivationFunc) NULL,
1552 (VikToolActivationFunc) NULL,
1553 (VikToolMouseFunc) selecttool_click,
1554 (VikToolMouseMoveFunc) selecttool_move,
1555 (VikToolMouseFunc) selecttool_release,
1556 (VikToolKeyFunc) NULL,
1561 /*** end select tool code ********************************************************/
1563 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1565 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1566 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1567 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1568 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1569 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1570 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1571 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1572 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1577 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1579 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1580 g_assert(check_box);
1581 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1583 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1585 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1588 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1592 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1595 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1598 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1601 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1605 gchar *s = (gchar *)gtk_action_get_name(a);
1611 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1612 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1613 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1614 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1615 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1620 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1622 VikCoord new_center;
1624 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1625 struct LatLon ll, llold;
1626 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1627 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1628 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1632 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1633 struct UTM utm, utmold;
1634 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1635 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1636 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1641 g_critical("Houston, we've had a problem.");
1645 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1649 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1652 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1653 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1654 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1656 vw->modified = TRUE;
1662 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1664 a_clipboard_copy_selected ( vw->viking_vlp );
1667 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1669 vik_layers_panel_cut_selected ( vw->viking_vlp );
1670 vw->modified = TRUE;
1673 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1675 if ( a_clipboard_paste ( vw->viking_vlp ) )
1677 vw->modified = TRUE;
1681 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1683 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1684 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1687 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1690 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1692 #if GTK_CHECK_VERSION (2, 14, 0)
1694 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1695 gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, NULL);
1698 #endif /* WINDOWS */
1701 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1703 a_dialog_about(GTK_WINDOW(vw));
1706 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1708 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1710 vik_layers_panel_delete_selected ( vw->viking_vlp );
1711 vw->modified = TRUE;
1714 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1717 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1719 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1720 g_assert(check_box);
1721 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1723 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1725 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1728 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1730 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1733 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1735 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1737 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1740 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1742 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1745 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1747 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1749 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1752 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1754 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1757 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1759 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1761 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1764 /***************************************
1765 ** tool management routines
1767 ***************************************/
1769 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1771 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1774 vt->active_tool = -1;
1776 if (!vw->viking_vvp) {
1777 g_critical("no viewport found.");
1783 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1785 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1786 vt->tools[vt->n_tools].ti = *vti;
1787 vt->tools[vt->n_tools].layer_type = layer_type;
1789 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1792 vt->tools[vt->n_tools].state = NULL;
1797 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1800 for (i=0; i<vt->n_tools; i++) {
1801 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1808 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1810 int tool = toolbox_get_tool(vt, tool_name);
1811 toolbox_tool_t *t = &vt->tools[tool];
1812 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1814 if (tool == vt->n_tools) {
1815 g_critical("trying to activate a non-existent tool...");
1818 /* is the tool already active? */
1819 if (vt->active_tool == tool) {
1823 if (vt->active_tool != -1) {
1824 if (vt->tools[vt->active_tool].ti.deactivate) {
1825 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1828 if (t->ti.activate) {
1829 t->ti.activate(vl, t->state);
1831 vt->active_tool = tool;
1834 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1836 int tool = toolbox_get_tool(vt, tool_name);
1837 toolbox_tool_t *t = &vt->tools[tool];
1838 if (t->ti.cursor == NULL) {
1839 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1840 GError *cursor_load_err = NULL;
1841 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1842 /* TODO: settable offeset */
1843 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1844 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1846 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1849 return t->ti.cursor;
1852 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1854 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1855 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1856 gint ltype = vt->tools[vt->active_tool].layer_type;
1857 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1858 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
1862 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
1864 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1865 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
1866 gint ltype = vt->tools[vt->active_tool].layer_type;
1867 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1868 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
1869 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
1873 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
1875 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1876 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
1877 gint ltype = vt->tools[vt->active_tool].layer_type;
1878 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1879 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
1882 /** End tool management ************************************/
1884 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
1886 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
1889 /* this function gets called whenever a toolbar tool is clicked */
1890 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
1892 /* White Magic, my friends ... White Magic... */
1893 int layer_id, tool_id;
1894 const GdkCursor *cursor = NULL;
1896 toolbox_activate(vw->vt, gtk_action_get_name(a));
1898 cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
1900 if ( GTK_WIDGET(vw->viking_vvp)->window )
1901 /* We set cursor, even if it is NULL: it resets to default */
1902 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
1904 if (!strcmp(gtk_action_get_name(a), "Pan")) {
1905 vw->current_tool = TOOL_PAN;
1907 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
1908 vw->current_tool = TOOL_ZOOM;
1910 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
1911 vw->current_tool = TOOL_RULER;
1913 else if (!strcmp(gtk_action_get_name(a), "Select")) {
1914 vw->current_tool = TOOL_SELECT;
1917 /* TODO: only enable tools from active layer */
1918 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
1919 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
1920 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
1921 vw->current_tool = TOOL_LAYER;
1922 vw->tool_layer_id = layer_id;
1923 vw->tool_tool_id = tool_id;
1931 static void window_set_filename ( VikWindow *vw, const gchar *filename )
1936 g_free ( vw->filename );
1937 if ( filename == NULL )
1939 vw->filename = NULL;
1940 file = _("Untitled");
1944 vw->filename = g_strdup(filename);
1945 file = a_file_basename ( filename );
1947 title = g_strdup_printf( "%s - Viking", file );
1948 gtk_window_set_title ( GTK_WINDOW(vw), title );
1952 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
1954 GtkWidget *mode_button;
1957 #ifdef VIK_CONFIG_EXPEDIA
1958 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
1960 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
1961 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
1962 default: buttonname = "/ui/MainMenu/View/ModeUTM";
1964 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
1965 g_assert ( mode_button );
1970 * vik_window_get_pan_move:
1971 * @vw: some VikWindow
1973 * Retrieves @vw's pan_move.
1975 * Should be removed as soon as possible.
1977 * Returns: @vw's pan_move
1981 gboolean vik_window_get_pan_move ( VikWindow *vw )
1983 return vw->pan_move;
1986 static void on_activate_recent_item (GtkRecentChooser *chooser,
1991 filename = gtk_recent_chooser_get_current_uri (chooser);
1992 if (filename != NULL)
1994 GFile *file = g_file_new_for_uri ( filename );
1995 gchar *path = g_file_get_path ( file );
1996 g_object_unref ( file );
1997 if ( self->filename )
1999 GSList *filenames = NULL;
2000 filenames = g_slist_append ( filenames, path );
2001 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2002 // NB: GSList & contents are freed by main.open_window
2005 vik_window_open_file ( self, path, TRUE );
2013 static void setup_recent_files (VikWindow *self)
2015 GtkRecentManager *manager;
2016 GtkRecentFilter *filter;
2017 GtkWidget *menu, *menu_item;
2019 filter = gtk_recent_filter_new ();
2020 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2021 gtk_recent_filter_add_group(filter, "viking");
2023 manager = gtk_recent_manager_get_default ();
2024 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2025 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2026 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2028 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2029 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2031 g_signal_connect (G_OBJECT (menu), "item-activated",
2032 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2035 static void update_recently_used_document(const gchar *filename)
2037 /* Update Recently Used Document framework */
2038 GtkRecentManager *manager = gtk_recent_manager_get_default();
2039 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2040 gchar *groups[] = {"viking", NULL};
2041 GFile *file = g_file_new_for_commandline_arg(filename);
2042 gchar *uri = g_file_get_uri(file);
2043 gchar *basename = g_path_get_basename(filename);
2044 g_object_unref(file);
2047 recent_data->display_name = basename;
2048 recent_data->description = NULL;
2049 recent_data->mime_type = "text/x-gps-data";
2050 recent_data->app_name = (gchar *) g_get_application_name ();
2051 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2052 recent_data->groups = groups;
2053 recent_data->is_private = FALSE;
2054 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2056 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2061 g_free (recent_data->app_exec);
2062 g_slice_free (GtkRecentData, recent_data);
2065 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2067 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2069 case LOAD_TYPE_READ_FAILURE:
2070 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2072 case LOAD_TYPE_GPSBABEL_FAILURE:
2073 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2075 case LOAD_TYPE_GPX_FAILURE:
2076 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2078 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2079 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2081 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2083 // Since we can process .vik files with issues just show a warning in the status bar
2084 // Not that a user can do much about it... or tells them what this issue is yet...
2085 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2086 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2089 // No break, carry on to show any data
2090 case LOAD_TYPE_VIK_SUCCESS:
2092 GtkWidget *mode_button;
2094 if ( change_filename )
2095 window_set_filename ( vw, filename );
2096 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2097 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. */
2098 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2099 vw->only_updating_coord_mode_ui = FALSE;
2101 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2103 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2104 g_assert ( mode_button );
2105 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2107 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2108 g_assert ( mode_button );
2109 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2111 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2112 g_assert ( mode_button );
2113 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2115 //case LOAD_TYPE_OTHER_SUCCESS:
2117 update_recently_used_document(filename);
2122 static void load_file ( GtkAction *a, VikWindow *vw )
2124 GSList *files = NULL;
2125 GSList *cur_file = NULL;
2127 if (!strcmp(gtk_action_get_name(a), "Open")) {
2130 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2134 g_critical("Houston, we've had a problem.");
2138 if ( ! vw->open_dia )
2140 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2142 GTK_FILE_CHOOSER_ACTION_OPEN,
2143 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2144 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2146 GtkFileFilter *filter;
2147 // NB file filters are listed this way for alphabetical ordering
2148 #ifdef VIK_CONFIG_GEOCACHES
2149 filter = gtk_file_filter_new ();
2150 gtk_file_filter_set_name( filter, _("Geocaching") );
2151 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2152 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2155 filter = gtk_file_filter_new ();
2156 gtk_file_filter_set_name( filter, _("Google Earth") );
2157 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2158 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2160 filter = gtk_file_filter_new ();
2161 gtk_file_filter_set_name( filter, _("GPX") );
2162 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2163 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2165 filter = gtk_file_filter_new ();
2166 gtk_file_filter_set_name( filter, _("Viking") );
2167 gtk_file_filter_add_pattern ( filter, "*.vik" );
2168 gtk_file_filter_add_pattern ( filter, "*.viking" );
2169 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2171 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2172 // However assume this are barely used and thus not worthy of inclusion
2173 // as they'll just make the options too many and have no clear file pattern
2174 // one can always use the all option
2175 filter = gtk_file_filter_new ();
2176 gtk_file_filter_set_name( filter, _("All") );
2177 gtk_file_filter_add_pattern ( filter, "*" );
2178 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2179 // Default to any file - same as before open filters were added
2180 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2182 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2183 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2184 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2186 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2188 gtk_widget_hide ( vw->open_dia );
2189 #ifdef VIKING_PROMPT_IF_MODIFIED
2190 if ( (vw->modified || vw->filename) && newwindow )
2192 if ( vw->filename && newwindow )
2194 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2196 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2197 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2200 while ( cur_file ) {
2201 gchar *file_name = cur_file->data;
2202 vik_window_open_file ( vw, file_name, change_fn );
2204 cur_file = g_slist_next (cur_file);
2206 g_slist_free (files);
2210 gtk_widget_hide ( vw->open_dia );
2213 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2215 gboolean rv = FALSE;
2217 if ( ! vw->save_dia )
2219 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2221 GTK_FILE_CHOOSER_ACTION_SAVE,
2222 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2223 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2225 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2226 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2228 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2229 gchar* auto_save_name = strdup ( vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") );
2230 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2231 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2233 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2235 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2237 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2238 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 ) ) )
2240 window_set_filename ( vw, fn );
2241 rv = window_save ( vw );
2242 vw->modified = FALSE;
2246 g_free ( auto_save_name );
2247 gtk_widget_hide ( vw->save_dia );
2251 static gboolean window_save ( VikWindow *vw )
2253 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2255 update_recently_used_document ( vw->filename );
2260 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2265 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2267 if ( ! vw->filename )
2268 return save_file_as ( NULL, vw );
2271 vw->modified = FALSE;
2272 return window_save ( vw );
2276 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2278 // Via the file menu, acquiring from a GPS makes a new layer
2279 // this has always been the way (not entirely sure if this was the real intention!)
2280 // thus maintain the behaviour ATM.
2281 // Hence explicit setting here (as the value may be changed elsewhere)
2282 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2283 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2286 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2288 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2291 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2292 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2294 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2298 #ifdef VIK_CONFIG_OPENSTREETMAP
2299 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2301 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2305 #ifdef VIK_CONFIG_GEOCACHES
2306 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2308 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2312 #ifdef VIK_CONFIG_GEOTAG
2313 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2315 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2316 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2320 static void goto_default_location( GtkAction *a, VikWindow *vw)
2323 ll.lat = a_vik_get_default_lat();
2324 ll.lon = a_vik_get_default_long();
2325 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2326 vik_layers_panel_emit_update(vw->viking_vlp);
2330 static void goto_address( GtkAction *a, VikWindow *vw)
2332 a_vik_goto(vw, vw->viking_vlp, vw->viking_vvp);
2335 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2340 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2342 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2344 a_preferences_show_window ( GTK_WINDOW(vw) );
2346 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2347 if (wp_icon_size != a_vik_get_use_large_waypoint_icons())
2348 clear_garmin_icon_syms ();
2353 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2355 /* Simplistic repeat of preference setting
2356 Only the name & type are important for setting the preference via this 'external' way */
2357 VikLayerParam pref_lat[] = {
2358 { VIKING_PREFERENCES_NAMESPACE "default_latitude",
2359 VIK_LAYER_PARAM_DOUBLE,
2362 VIK_LAYER_WIDGET_SPINBUTTON,
2366 VikLayerParam pref_lon[] = {
2367 { VIKING_PREFERENCES_NAMESPACE "default_longitude",
2368 VIK_LAYER_PARAM_DOUBLE,
2371 VIK_LAYER_WIDGET_SPINBUTTON,
2376 /* Get current center */
2378 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2380 /* Apply to preferences */
2381 VikLayerParamData vlp_data;
2382 vlp_data.d = ll.lat;
2383 a_preferences_run_setparam (vlp_data, pref_lat);
2384 vlp_data.d = ll.lon;
2385 a_preferences_run_setparam (vlp_data, pref_lon);
2386 /* Remember to save */
2387 a_preferences_save_to_file();
2390 static void clear_cb ( GtkAction *a, VikWindow *vw )
2392 vik_layers_panel_clear ( vw->viking_vlp );
2393 window_set_filename ( vw, NULL );
2397 static void window_close ( GtkAction *a, VikWindow *vw )
2399 if ( ! delete_event ( vw ) )
2400 gtk_widget_destroy ( GTK_WIDGET(vw) );
2403 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2405 if (save_file( NULL, vw)) {
2406 window_close( NULL, vw);
2413 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2415 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2416 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2418 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2419 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2424 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2426 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2427 GdkPixbuf *pixbuf_to_save;
2428 gdouble old_xmpp, old_ympp;
2429 GError *error = NULL;
2431 /* backup old zoom & set new */
2432 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2433 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2434 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2436 /* reset width and height: */
2437 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2439 /* draw all layers */
2442 /* save buffer as file. */
2443 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);
2444 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2447 g_warning("Unable to write to file %s: %s", fn, error->message );
2448 g_error_free (error);
2450 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2452 /* pretend like nothing happened ;) */
2453 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2454 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2455 vik_viewport_configure ( vw->viking_vvp );
2459 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 )
2461 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2462 gchar *name_of_file = g_malloc ( size );
2464 struct UTM utm_orig, utm;
2466 /* *** copied from above *** */
2467 GdkPixbuf *pixbuf_to_save;
2468 gdouble old_xmpp, old_ympp;
2469 GError *error = NULL;
2471 /* backup old zoom & set new */
2472 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2473 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2474 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2476 /* reset width and height: do this only once for all images (same size) */
2477 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2478 /* *** end copy from above *** */
2480 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2484 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2486 for ( y = 1; y <= tiles_h; y++ )
2488 for ( x = 1; x <= tiles_w; x++ )
2490 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2492 if ( tiles_w & 0x1 )
2493 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2495 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2496 if ( tiles_h & 0x1 ) /* odd */
2497 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2499 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2501 /* move to correct place. */
2502 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2506 /* save buffer as file. */
2507 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);
2508 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2511 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2512 g_error_free (error);
2515 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2519 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2520 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2521 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2522 vik_viewport_configure ( vw->viking_vvp );
2525 g_free ( name_of_file );
2528 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2530 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2531 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2532 GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]);
2533 gdouble width_min, width_max, height_min, height_max;
2536 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2537 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2539 /* TODO: support for xzoom and yzoom values */
2540 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin );
2541 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin );
2543 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2544 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2546 gtk_spin_button_set_value ( width_spin, width );
2547 gtk_spin_button_set_value ( height_spin, height );
2550 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2552 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2553 GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]);
2556 w = gtk_spin_button_get_value(width_spin) * gtk_spin_button_get_value(zoom_spin);
2557 h = gtk_spin_button_get_value(height_spin) * gtk_spin_button_get_value(zoom_spin);
2558 if (pass_along[4]) /* save many images; find TOTAL area covered */
2560 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2561 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2563 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2564 switch (dist_units) {
2565 case VIK_UNITS_DISTANCE_KILOMETRES:
2566 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2568 case VIK_UNITS_DISTANCE_MILES:
2569 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2572 label_text = g_strdup_printf ("Just to keep the compiler happy");
2573 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2576 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2577 g_free ( label_text );
2580 static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_image_only )
2582 /* todo: default for answers inside VikWindow or static (thruout instance) */
2583 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2584 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2586 GTK_RESPONSE_REJECT,
2588 GTK_RESPONSE_ACCEPT,
2590 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2591 GtkWidget *png_radio, *jpeg_radio;
2592 GtkWidget *current_window_button;
2593 gpointer current_window_pass_along[7];
2594 GtkWidget *zoom_label, *zoom_spin;
2595 GtkWidget *total_size_label;
2597 /* only used if (!one_image_only) */
2598 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2601 width_label = gtk_label_new ( _("Width (pixels):") );
2602 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 5000, 10, 100, 0 )), 10, 0 );
2603 height_label = gtk_label_new ( _("Height (pixels):") );
2604 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 5000, 10, 100, 0 )), 10, 0 );
2606 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2607 /* TODO: separate xzoom and yzoom factors */
2608 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);
2610 total_size_label = gtk_label_new ( NULL );
2612 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2613 current_window_pass_along [0] = vw;
2614 current_window_pass_along [1] = width_spin;
2615 current_window_pass_along [2] = height_spin;
2616 current_window_pass_along [3] = zoom_spin;
2617 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
2618 current_window_pass_along [5] = NULL;
2619 current_window_pass_along [6] = total_size_label;
2620 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
2622 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
2623 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
2625 if ( ! vw->draw_image_save_as_png )
2626 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
2628 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
2629 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
2630 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
2631 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
2632 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
2633 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
2634 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
2635 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
2636 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_spin, FALSE, FALSE, 0);
2638 if ( ! one_image_only )
2640 GtkWidget *tiles_width_label, *tiles_height_label;
2643 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
2644 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2645 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
2646 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2647 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
2648 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
2649 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
2650 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
2652 current_window_pass_along [4] = tiles_width_spin;
2653 current_window_pass_along [5] = tiles_height_spin;
2654 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2655 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2657 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
2658 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2659 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2660 g_signal_connect ( G_OBJECT(zoom_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2662 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
2664 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2666 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
2668 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
2670 gtk_widget_hide ( GTK_WIDGET(dialog) );
2671 if ( one_image_only )
2672 save_image_file ( vw, fn,
2673 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2674 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2675 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */
2676 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
2678 // NB is in UTM mode ATM
2679 save_image_dir ( vw, fn,
2680 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2681 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2682 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */
2683 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
2684 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
2685 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
2688 gtk_widget_destroy ( GTK_WIDGET(dialog) );
2692 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
2695 if (!vw->save_img_dia) {
2696 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2698 GTK_FILE_CHOOSER_ACTION_SAVE,
2699 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2700 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2702 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2703 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2706 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT )
2708 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2709 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 ) ) )
2711 draw_to_image_file ( vw, fn, TRUE );
2717 gtk_widget_hide ( vw->save_img_dia );
2720 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
2724 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2725 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2729 if (!vw->save_img_dir_dia) {
2730 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2732 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2733 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2734 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2736 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2737 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2740 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT )
2742 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2745 draw_to_image_file ( vw, fn, FALSE );
2751 gtk_widget_hide ( vw->save_img_dir_dia );
2754 #if GTK_CHECK_VERSION(2,10,0)
2755 static void print_cb ( GtkAction *a, VikWindow *vw )
2757 a_print(vw, vw->viking_vvp);
2761 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
2762 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
2764 VikViewportDrawMode drawmode;
2765 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
2766 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
2768 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
2769 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
2771 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
2772 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
2774 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
2775 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
2778 g_critical("Houston, we've had a problem.");
2782 if ( !vw->only_updating_coord_mode_ui )
2784 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
2785 if ( olddrawmode != drawmode )
2787 /* this takes care of coord mode too */
2788 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
2789 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
2790 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
2791 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
2792 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
2799 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
2801 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2802 g_assert(check_box);
2803 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2804 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
2808 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
2810 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2811 g_assert(check_box);
2812 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2813 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
2817 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
2819 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2820 g_assert(check_box);
2821 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2822 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
2826 static void set_bg_color ( GtkAction *a, VikWindow *vw )
2828 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
2829 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
2830 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2831 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2832 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
2834 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2835 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
2839 gtk_widget_destroy ( colorsd );
2842 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
2844 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
2845 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
2846 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2847 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2848 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
2850 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
2851 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
2855 gtk_widget_destroy ( colorsd );
2860 /***********************************************************************************************
2862 ***********************************************************************************************/
2864 static GtkActionEntry entries[] = {
2865 { "File", NULL, N_("_File"), 0, 0, 0 },
2866 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
2867 { "View", NULL, N_("_View"), 0, 0, 0 },
2868 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
2869 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
2870 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
2871 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
2872 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
2873 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
2874 { "Help", NULL, N_("_Help"), 0, 0, 0 },
2876 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
2877 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
2878 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
2879 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
2880 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
2881 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
2882 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
2883 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2884 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
2886 #ifdef VIK_CONFIG_OPENSTREETMAP
2887 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
2889 #ifdef VIK_CONFIG_GEOCACHES
2890 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
2892 #ifdef VIK_CONFIG_GEOTAG
2893 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
2895 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
2896 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
2897 { "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 },
2898 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
2900 #if GTK_CHECK_VERSION(2,10,0)
2901 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
2904 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
2905 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
2907 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
2908 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
2909 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
2910 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
2911 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
2912 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
2913 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
2914 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
2915 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
2916 { "Zoom0.25", NULL, N_("0.25"), NULL, NULL, (GCallback)draw_zoom_cb },
2917 { "Zoom0.5", NULL, N_("0.5"), NULL, NULL, (GCallback)draw_zoom_cb },
2918 { "Zoom1", NULL, N_("1"), NULL, NULL, (GCallback)draw_zoom_cb },
2919 { "Zoom2", NULL, N_("2"), NULL, NULL, (GCallback)draw_zoom_cb },
2920 { "Zoom4", NULL, N_("4"), NULL, NULL, (GCallback)draw_zoom_cb },
2921 { "Zoom8", NULL, N_("8"), NULL, NULL, (GCallback)draw_zoom_cb },
2922 { "Zoom16", NULL, N_("16"), NULL, NULL, (GCallback)draw_zoom_cb },
2923 { "Zoom32", NULL, N_("32"), NULL, NULL, (GCallback)draw_zoom_cb },
2924 { "Zoom64", NULL, N_("64"), NULL, NULL, (GCallback)draw_zoom_cb },
2925 { "Zoom128", NULL, N_("128"), NULL, NULL, (GCallback)draw_zoom_cb },
2926 { "Zoom256", NULL, N_("256"), NULL, NULL, (GCallback)draw_zoom_cb },
2927 { "Zoom512", NULL, N_("512"), NULL, NULL, (GCallback)draw_zoom_cb },
2928 { "Zoom1024", NULL, N_("1024"), NULL, NULL, (GCallback)draw_zoom_cb },
2929 { "Zoom2048", NULL, N_("2048"), NULL, NULL, (GCallback)draw_zoom_cb },
2930 { "Zoom4096", NULL, N_("4096"), NULL, NULL, (GCallback)draw_zoom_cb },
2931 { "Zoom8192", NULL, N_("8192"), NULL, NULL, (GCallback)draw_zoom_cb },
2932 { "Zoom16384", NULL, N_("16384"), NULL, NULL, (GCallback)draw_zoom_cb },
2933 { "Zoom32768", NULL, N_("32768"), NULL, NULL, (GCallback)draw_zoom_cb },
2934 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
2935 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
2936 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
2937 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
2938 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
2940 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
2941 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
2942 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
2943 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
2944 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
2945 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
2946 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
2947 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
2948 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
2950 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
2951 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
2955 /* FIXME use VIEWPORT_DRAWMODE values */
2956 static GtkRadioActionEntry mode_entries[] = {
2957 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
2958 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
2959 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
2960 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
2963 static GtkToggleActionEntry toggle_entries[] = {
2964 { "ShowScale", NULL, N_("Show _Scale"), "F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
2965 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
2966 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
2967 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
2968 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
2969 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
2970 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
2971 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
2974 #include "menu.xml.h"
2975 static void window_create_ui( VikWindow *window )
2978 GtkActionGroup *action_group;
2979 GtkAccelGroup *accel_group;
2982 GtkIconFactory *icon_factory;
2983 GtkIconSet *icon_set;
2984 GtkRadioActionEntry *tools = NULL, *radio;
2987 uim = gtk_ui_manager_new ();
2990 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
2991 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
2992 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
2993 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
2996 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
2997 g_error_free (error);
3001 action_group = gtk_action_group_new ("MenuActions");
3002 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3003 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3004 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3005 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3007 icon_factory = gtk_icon_factory_new ();
3008 gtk_icon_factory_add_default (icon_factory);
3010 register_vik_icons(icon_factory);
3012 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3013 // so that it can be applied to the UI in one action group add function call below
3015 for (i=0; i<window->vt->n_tools; i++) {
3016 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3017 radio = &tools[ntools];
3019 *radio = window->vt->tools[i].ti.radioActionEntry;
3020 radio->value = ntools;
3023 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3024 GtkActionEntry action;
3025 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3026 vik_layer_get_interface(i)->name,
3027 vik_layer_get_interface(i)->name,
3028 GTK_UI_MANAGER_MENUITEM, FALSE);
3030 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3031 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3032 gtk_icon_set_unref (icon_set);
3034 action.name = vik_layer_get_interface(i)->name;
3035 action.stock_id = vik_layer_get_interface(i)->name;
3036 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3037 action.accelerator = vik_layer_get_interface(i)->accelerator;
3038 action.tooltip = NULL;
3039 action.callback = (GCallback)menu_addlayer_cb;
3040 gtk_action_group_add_actions(action_group, &action, 1, window);
3042 if ( vik_layer_get_interface(i)->tools_count ) {
3043 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3044 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3047 // Further tool copying for to apply to the UI, also apply menu UI setup
3048 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3049 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3050 radio = &tools[ntools];
3053 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3054 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3055 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3056 GTK_UI_MANAGER_MENUITEM, FALSE);
3057 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3058 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3059 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3060 GTK_UI_MANAGER_TOOLITEM, FALSE);
3062 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3064 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3065 // Overwrite with actual number to use
3066 radio->value = ntools;
3069 g_object_unref (icon_factory);
3071 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3074 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3076 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3077 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3078 GtkAction *action = gtk_action_group_get_action(action_group,
3079 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3080 g_object_set(action, "sensitive", FALSE, NULL);
3083 window->action_group = action_group;
3085 accel_group = gtk_ui_manager_get_accel_group (uim);
3086 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3087 gtk_ui_manager_ensure_update (uim);
3089 setup_recent_files(window);
3093 // TODO - add method to add tool icons defined from outside this file
3094 // and remove the reverse dependency on icon definition from this file
3096 const GdkPixdata *data;
3099 { &mover_22_pixbuf, "vik-icon-pan" },
3100 { &zoom_18_pixbuf, "vik-icon-zoom" },
3101 { &ruler_18_pixbuf, "vik-icon-ruler" },
3102 { &select_18_pixbuf, "vik-icon-select" },
3103 { &begintr_18_pixbuf, "vik-icon-Begin Track" },
3104 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3105 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3106 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3107 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3108 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3109 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3110 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3111 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3112 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3113 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3116 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3119 register_vik_icons (GtkIconFactory *icon_factory)
3121 GtkIconSet *icon_set;
3124 for (i = 0; i < n_stock_icons; i++) {
3125 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3126 stock_icons[i].data, FALSE, NULL ));
3127 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3128 gtk_icon_set_unref (icon_set);
3132 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3134 return vw->selected_vtl;
3137 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3139 vw->selected_vtl = vtl;
3140 vw->containing_vtl = vtl;
3142 vw->selected_track = NULL;
3143 vw->selected_tracks = NULL;
3144 vw->selected_waypoint = NULL;
3145 vw->selected_waypoints = NULL;
3146 // Set highlight thickness
3147 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3150 gpointer vik_window_get_selected_tracks ( VikWindow *vw )
3152 return vw->selected_tracks;
3155 void vik_window_set_selected_tracks ( VikWindow *vw, gpointer gl, gpointer vtl )
3157 vw->selected_tracks = gl;
3158 vw->containing_vtl = vtl;
3160 vw->selected_vtl = NULL;
3161 vw->selected_track = NULL;
3162 vw->selected_waypoint = NULL;
3163 vw->selected_waypoints = NULL;
3164 // Set highlight thickness
3165 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3168 gpointer vik_window_get_selected_track ( VikWindow *vw )
3170 return vw->selected_track;
3173 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl, gpointer name )
3175 vw->selected_track = vt;
3176 vw->containing_vtl = vtl;
3178 vw->selected_vtl = NULL;
3179 vw->selected_tracks = NULL;
3180 vw->selected_waypoint = NULL;
3181 vw->selected_waypoints = NULL;
3182 // Set highlight thickness
3183 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3186 gpointer vik_window_get_selected_waypoints ( VikWindow *vw )
3188 return vw->selected_waypoints;
3191 void vik_window_set_selected_waypoints ( VikWindow *vw, gpointer gl, gpointer vtl )
3193 vw->selected_waypoints = gl;
3194 vw->containing_vtl = vtl;
3196 vw->selected_vtl = NULL;
3197 vw->selected_track = NULL;
3198 vw->selected_tracks = NULL;
3199 vw->selected_waypoint = NULL;
3202 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3204 return vw->selected_waypoint;
3207 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl, gpointer name )
3209 vw->selected_waypoint = vwp;
3210 vw->containing_vtl = vtl;
3212 vw->selected_vtl = NULL;
3213 vw->selected_track = NULL;
3214 vw->selected_tracks = NULL;
3215 vw->selected_waypoints = NULL;
3218 gboolean vik_window_clear_highlight ( VikWindow *vw )
3220 gboolean need_redraw = FALSE;
3221 if ( vw->selected_vtl != NULL ) {
3222 vw->selected_vtl = NULL;
3225 if ( vw->selected_track != NULL ) {
3226 vw->selected_track = NULL;
3229 if ( vw->selected_tracks != NULL ) {
3230 vw->selected_tracks = NULL;
3233 if ( vw->selected_waypoint != NULL ) {
3234 vw->selected_waypoint = NULL;
3237 if ( vw->selected_waypoints != NULL ) {
3238 vw->selected_waypoints = NULL;