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 "viklayer_defaults.h"
38 #include "icons/icons.h"
39 #include "vikexttools.h"
40 #include "garminsymbols.h"
41 #include "vikmapslayer.h"
42 #include "geonamessearch.h"
55 #include <glib/gstdio.h>
56 #include <glib/gprintf.h>
57 #include <glib/gi18n.h>
59 #include <gdk/gdkkeysyms.h>
61 // This seems rather arbitary, quite large and pointless
62 // I mean, if you have a thousand windows open;
63 // why not be allowed to open a thousand more...
64 #define MAX_WINDOWS 1024
65 static guint window_count = 0;
66 static GSList *window_list = NULL;
68 #define VIKING_WINDOW_WIDTH 1000
69 #define VIKING_WINDOW_HEIGHT 800
70 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
71 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
72 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
74 static void window_finalize ( GObject *gob );
75 static GObjectClass *parent_class;
77 static void window_set_filename ( VikWindow *vw, const gchar *filename );
78 static const gchar *window_get_filename ( VikWindow *vw );
80 static VikWindow *window_new ();
82 static void draw_update ( VikWindow *vw );
84 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
87 static void open_window ( VikWindow *vw, GSList *files );
88 static void statusbar_update ( VikWindow *vw, vik_statusbar_type_t vs_type, const gchar *message );
89 static void destroy_window ( GtkWidget *widget,
94 static gboolean delete_event( VikWindow *vw );
96 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
98 static void window_configure_event ( VikWindow *vw );
99 static void draw_sync ( VikWindow *vw );
100 static void draw_redraw ( VikWindow *vw );
101 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
102 static void draw_click ( VikWindow *vw, GdkEventButton *event );
103 static void draw_release ( VikWindow *vw, GdkEventButton *event );
104 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
105 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
106 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
107 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
109 static void draw_status ( VikWindow *vw );
111 /* End Drawing Functions */
113 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
114 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
115 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
117 /* tool management */
123 #define TOOL_LAYER_TYPE_NONE -1
128 toolbox_tool_t *tools;
132 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
133 static toolbox_tools_t* toolbox_create(VikWindow *vw);
134 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
135 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
136 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
137 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
138 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
139 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
140 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
144 static void window_create_ui( VikWindow *window );
145 static void register_vik_icons (GtkIconFactory *icon_factory);
148 static void load_file ( GtkAction *a, VikWindow *vw );
149 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
150 static gboolean save_file ( GtkAction *a, VikWindow *vw );
151 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
152 static gboolean window_save ( VikWindow *vw );
156 VikViewport *viking_vvp;
157 VikLayersPanel *viking_vlp;
158 VikStatusbar *viking_vs;
162 GdkCursor *busy_cursor;
163 GdkCursor *viewport_cursor; // only a reference
165 /* tool management state */
168 guint16 tool_layer_id;
169 guint16 tool_tool_id;
171 GtkActionGroup *action_group;
176 guint draw_image_width, draw_image_height;
177 gboolean draw_image_save_as_png;
182 GtkWidget *open_dia, *save_dia;
183 GtkWidget *save_img_dia, *save_img_dir_dia;
185 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
189 /* half-drawn update */
191 VikCoord trigger_center;
193 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
194 /* Only one of these items can be selected at the same time */
195 gpointer selected_vtl; /* notionally VikTrwLayer */
196 GHashTable *selected_tracks;
197 gpointer selected_track; /* notionally VikTrack */
198 GHashTable *selected_waypoints;
199 gpointer selected_waypoint; /* notionally VikWaypoint */
200 /* only use for individual track or waypoint */
201 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
202 gpointer containing_vtl; /* notionally VikTrwLayer */
216 VW_OPENWINDOW_SIGNAL,
217 VW_STATUSBAR_UPDATE_SIGNAL,
221 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
223 // TODO get rid of this as this is unnecessary duplication...
224 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
226 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
228 VikViewport * vik_window_viewport(VikWindow *vw)
230 return(vw->viking_vvp);
233 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
235 return(vw->viking_vlp);
239 * Returns the statusbar for the window
241 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
243 return vw->viking_vs;
247 * For signalling the update from a background thread
249 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
251 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, vs_type, message );
255 * For the actual statusbar update!
257 static gboolean statusbar_idle_update ( gpointer indata )
259 gpointer *data = indata;
260 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
265 * Update statusbar in the main thread
267 static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
269 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
270 static gpointer data[3];
271 data[0] = vw->viking_vs;
272 data[1] = (gchar*) message;
273 data[2] = GINT_TO_POINTER(vs_type);
274 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
277 // Actual signal handlers
278 static void destroy_window ( GtkWidget *widget,
281 if ( ! --window_count )
285 static void statusbar_update ( VikWindow *vw, vik_statusbar_type_t vs_type, const gchar *message )
287 window_statusbar_update ( vw, message, vs_type );
290 VikWindow *vik_window_new_window ()
292 if ( window_count < MAX_WINDOWS )
294 VikWindow *vw = window_new ();
296 g_signal_connect (G_OBJECT (vw), "destroy",
297 G_CALLBACK (destroy_window), NULL);
298 g_signal_connect (G_OBJECT (vw), "newwindow",
299 G_CALLBACK (vik_window_new_window), NULL);
300 g_signal_connect (G_OBJECT (vw), "openwindow",
301 G_CALLBACK (open_window), NULL);
302 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
303 G_CALLBACK (statusbar_update), vw);
305 gtk_widget_show_all ( GTK_WIDGET(vw) );
314 static void open_window ( VikWindow *vw, GSList *files )
316 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
317 GSList *cur_file = files;
319 // Only open a new window if a viking file
320 gchar *file_name = cur_file->data;
321 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
322 VikWindow *newvw = vik_window_new_window ();
324 vik_window_open_file ( newvw, file_name, TRUE );
327 vik_window_open_file ( vw, file_name, change_fn );
330 cur_file = g_slist_next (cur_file);
332 g_slist_free (files);
336 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
338 int i, j, tool_count;
339 VikLayerInterface *layer_interface;
341 if (!vw->action_group) return;
343 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
345 layer_interface = vik_layer_get_interface(i);
346 tool_count = layer_interface->tools_count;
348 for (j = 0; j < tool_count; j++) {
349 action = gtk_action_group_get_action(vw->action_group,
350 layer_interface->tools[j].radioActionEntry.name);
351 g_object_set(action, "sensitive", i == vl->type, NULL);
356 static void window_finalize ( GObject *gob )
358 VikWindow *vw = VIK_WINDOW(gob);
359 g_return_if_fail ( vw != NULL );
361 a_background_remove_window ( vw );
363 window_list = g_slist_remove ( window_list, vw );
365 gdk_cursor_unref ( vw->busy_cursor );
367 G_OBJECT_CLASS(parent_class)->finalize(gob);
371 static void vik_window_class_init ( VikWindowClass *klass )
374 GObjectClass *object_class;
376 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);
377 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);
378 window_signals[VW_STATUSBAR_UPDATE_SIGNAL] = g_signal_new ( "statusbarupdate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, statusbarupdate), NULL, NULL, g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
380 object_class = G_OBJECT_CLASS (klass);
382 object_class->finalize = window_finalize;
384 parent_class = g_type_class_peek_parent (klass);
388 static void zoom_changed (GtkMenuShell *menushell,
391 VikWindow *vw = VIK_WINDOW (user_data);
393 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
394 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
396 gdouble zoom_request = pow (2, active-2 );
398 // But has it really changed?
399 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
400 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
401 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
402 // Force drawing update
407 static GtkWidget * create_zoom_menu_all_levels ()
409 GtkWidget *menu = gtk_menu_new ();
410 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768", NULL };
413 for (i = 0 ; itemLabels[i] != NULL ; i++)
415 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
416 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
417 gtk_widget_show (item);
418 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
424 static GtkWidget *create_zoom_combo_all_levels ()
426 GtkWidget *combo = vik_combo_box_text_new();
427 vik_combo_box_text_append ( combo, "0.25");
428 vik_combo_box_text_append ( combo, "0.5");
429 vik_combo_box_text_append ( combo, "1");
430 vik_combo_box_text_append ( combo, "2");
431 vik_combo_box_text_append ( combo, "4");
432 vik_combo_box_text_append ( combo, "8");
433 vik_combo_box_text_append ( combo, "16");
434 vik_combo_box_text_append ( combo, "32");
435 vik_combo_box_text_append ( combo, "64");
436 vik_combo_box_text_append ( combo, "128");
437 vik_combo_box_text_append ( combo, "256");
438 vik_combo_box_text_append ( combo, "512");
439 vik_combo_box_text_append ( combo, "1024");
440 vik_combo_box_text_append ( combo, "2048");
441 vik_combo_box_text_append ( combo, "4096");
442 vik_combo_box_text_append ( combo, "8192");
443 vik_combo_box_text_append ( combo, "16384");
444 vik_combo_box_text_append ( combo, "32768");
446 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
450 static gint zoom_popup_handler (GtkWidget *widget)
454 g_return_val_if_fail (widget != NULL, FALSE);
455 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
457 /* The "widget" is the menu that was supplied when
458 * g_signal_connect_swapped() was called.
460 menu = GTK_MENU (widget);
462 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
463 1, gtk_get_current_event_time());
467 static void vik_window_init ( VikWindow *vw )
469 GtkWidget *main_vbox;
472 vw->action_group = NULL;
474 vw->viking_vvp = vik_viewport_new();
475 vw->viking_vlp = vik_layers_panel_new();
476 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
477 vw->viking_vs = vik_statusbar_new();
479 vw->vt = toolbox_create(vw);
480 window_create_ui(vw);
481 window_set_filename (vw, NULL);
482 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
484 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
486 // Set the default tool
487 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
491 vw->modified = FALSE;
492 vw->only_updating_coord_mode_ui = FALSE;
494 vw->pan_move = FALSE;
495 vw->pan_x = vw->pan_y = -1;
496 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
497 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
498 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
500 main_vbox = gtk_vbox_new(FALSE, 1);
501 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
503 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
504 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
505 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
506 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
508 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
509 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ();
510 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
511 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
512 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
514 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
516 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
517 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
518 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 );
519 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
520 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
521 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
522 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
523 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
525 // Allow key presses to be processed anywhere
526 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
528 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
530 hpaned = gtk_hpaned_new ();
531 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
532 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
534 /* This packs the button into the window (a gtk container). */
535 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
537 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
539 a_background_add_window ( vw );
541 window_list = g_slist_prepend ( window_list, vw);
545 vw->save_img_dia = NULL;
546 vw->save_img_dir_dia = NULL;
548 // Store the thread value so comparisons can be made to determine the gdk update method
549 // Hopefully we are storing the main thread value here :)
550 // [ATM any window initialization is always be performed by the main thread]
551 vw->thread = g_thread_self();
554 static VikWindow *window_new ()
556 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
560 * Update the displayed map
561 * Only update the top most visible map layer
562 * ATM this assumes (as per defaults) the top most map has full alpha setting
563 * such that other other maps even though they may be active will not be seen
564 * It's more complicated to work out which maps are actually visible due to alpha settings
565 * and overkill for this simple refresh method.
567 static void simple_map_update ( VikWindow *vw, gboolean only_new )
569 // Find the most relevent single map layer to operate on
570 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
572 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
576 * This is the global key press handler
577 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
579 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
581 // The keys handled here are not in the menuing system for a couple of reasons:
582 // . Keeps the menu size compact (alebit at expense of discoverably)
583 // . Allows differing key bindings to perform the same actions
585 // First decide if key events are related to the maps layer
586 gboolean map_download = FALSE;
587 gboolean map_download_only_new = TRUE; // Only new or reload
589 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
591 // Standard 'Refresh' keys: F5 or Ctrl+r
592 // Note 'F5' is actually handled via draw_refresh_cb() later on
593 // (not 'R' it's 'r' notice the case difference!!)
594 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
596 map_download_only_new = TRUE;
598 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
599 // Note the use of uppercase R here since shift key has been pressed
600 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
601 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
603 map_download_only_new = FALSE;
606 if ( map_download ) {
607 simple_map_update ( vw, map_download_only_new );
610 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
611 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
612 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
613 if ( vl && ltype == vl->type )
614 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
617 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
618 if ( vw->current_tool < TOOL_LAYER ) {
619 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
620 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
621 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
625 /* Restore Main Menu via Escape key if the user has hidden it */
626 /* This key is more likely to be used as they may not remember the function key */
627 if ( event->keyval == GDK_Escape ) {
628 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
630 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
632 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
633 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
634 return TRUE; /* handled keypress */
639 return FALSE; /* don't handle the keypress */
642 static gboolean delete_event( VikWindow *vw )
644 #ifdef VIKING_PROMPT_IF_MODIFIED
651 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
652 _("Do you want to save the changes you made to the document \"%s\"?\n"
654 "Your changes will be lost if you don't save them."),
655 window_get_filename ( vw ) ) );
656 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
657 switch ( gtk_dialog_run ( dia ) )
659 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
660 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
661 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
668 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
670 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
673 static void draw_update ( VikWindow *vw )
679 static void draw_sync ( VikWindow *vw )
681 vik_viewport_sync(vw->viking_vvp);
686 * Split the status update, as sometimes only need to update the tool part
687 * also on initialization the zoom related stuff is not ready to be used
689 static void draw_status_tool ( VikWindow *vw )
691 if ( vw->current_tool == TOOL_LAYER )
692 // Use tooltip rather than the internal name as the tooltip is i8n
693 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 );
695 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
698 static void draw_status ( VikWindow *vw )
700 static gchar zoom_level[22];
701 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
702 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
703 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
705 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
707 if ( (int)xmpp - xmpp < 0.0 )
708 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
710 /* xmpp should be a whole number so don't show useless .000 bit */
711 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
713 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
715 draw_status_tool ( vw );
718 void vik_window_set_redraw_trigger(VikLayer *vl)
720 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
725 static void window_configure_event ( VikWindow *vw )
727 static int first = 1;
730 // This is a hack to set the cursor corresponding to the first tool
731 // FIXME find the correct way to initialize both tool and its cursor
733 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
734 /* We set cursor, even if it is NULL: it resets to default */
735 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
739 static void draw_redraw ( VikWindow *vw )
741 VikCoord old_center = vw->trigger_center;
742 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
743 VikLayer *new_trigger = vw->trigger;
745 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
748 ; /* do nothing -- have to redraw everything. */
749 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
750 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
752 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
755 vik_viewport_clear ( vw->viking_vvp);
756 vik_layers_panel_draw_all ( vw->viking_vlp );
757 vik_viewport_draw_scale ( vw->viking_vvp );
758 vik_viewport_draw_copyright ( vw->viking_vvp );
759 vik_viewport_draw_centermark ( vw->viking_vvp );
760 vik_viewport_draw_logo ( vw->viking_vvp );
762 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
765 gboolean draw_buf_done = TRUE;
767 static gboolean draw_buf(gpointer data)
769 gpointer *pass_along = data;
771 gdk_draw_drawable (pass_along[0], pass_along[1],
772 pass_along[2], 0, 0, 0, 0, -1, -1);
773 draw_buf_done = TRUE;
779 /* Mouse event handlers ************************************************************************/
781 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
783 /* set panning origin */
784 vw->pan_move = FALSE;
785 vw->pan_x = (gint) event->x;
786 vw->pan_y = (gint) event->y;
789 static void draw_click (VikWindow *vw, GdkEventButton *event)
791 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
793 /* middle button pressed. we reserve all middle button and scroll events
794 * for panning and zooming; tools only get left/right/movement
796 if ( event->button == 2) {
797 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
798 // Tool still may need to do something (such as disable something)
799 toolbox_click(vw->vt, event);
800 vik_window_pan_click ( vw, event );
803 toolbox_click(vw->vt, event);
807 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
809 if ( vw->pan_x != -1 ) {
810 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
811 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
813 vw->pan_x = event->x;
814 vw->pan_y = event->y;
819 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
821 static VikCoord coord;
822 static struct UTM utm;
823 static struct LatLon ll;
824 #define BUFFER_SIZE 50
825 static char pointer_buf[BUFFER_SIZE];
826 gchar *lat = NULL, *lon = NULL;
829 VikDemInterpol interpol_method;
831 /* This is a hack, but work far the best, at least for single pointer systems.
832 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
834 gdk_window_get_pointer (event->window, &x, &y, NULL);
838 toolbox_move(vw->vt, event);
840 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
841 vik_coord_to_utm ( &coord, &utm );
843 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
844 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
845 // ZONE[N|S] EASTING NORTHING
846 lat = g_malloc(4*sizeof(gchar));
847 // NB zone is stored in a char but is an actual number
848 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
849 lon = g_malloc(16*sizeof(gchar));
850 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
853 a_coords_utm_to_latlon ( &utm, &ll );
854 a_coords_latlon_to_string ( &ll, &lat, &lon );
857 /* Change interpolate method according to scale */
858 zoom = vik_viewport_get_zoom(vw->viking_vvp);
860 interpol_method = VIK_DEM_INTERPOL_NONE;
861 else if (zoom >= 1.0)
862 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
864 interpol_method = VIK_DEM_INTERPOL_BEST;
865 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
866 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
867 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
869 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
872 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
877 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
879 vik_window_pan_move ( vw, event );
881 /* This is recommended by the GTK+ documentation, but does not work properly.
882 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
883 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
885 /* gdk_event_request_motions ( event ); */
888 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
890 if ( vw->pan_move == FALSE )
891 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
893 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
894 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
895 vw->pan_move = FALSE;
896 vw->pan_x = vw->pan_y = -1;
900 static void draw_release ( VikWindow *vw, GdkEventButton *event )
902 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
904 if ( event->button == 2 ) { /* move / pan */
905 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
906 // Tool still may need to do something (such as reenable something)
907 toolbox_release(vw->vt, event);
908 vik_window_pan_release ( vw, event );
911 toolbox_release(vw->vt, event);
915 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
917 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
918 if ( modifiers == GDK_CONTROL_MASK ) {
919 /* control == pan up & down */
920 if ( event->direction == GDK_SCROLL_UP )
921 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
923 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 );
924 } else if ( modifiers == GDK_SHIFT_MASK ) {
925 /* shift == pan left & right */
926 if ( event->direction == GDK_SCROLL_UP )
927 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
929 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 );
930 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
931 // This zoom is on the center position
932 if ( event->direction == GDK_SCROLL_UP )
933 vik_viewport_zoom_in (vw->viking_vvp);
935 vik_viewport_zoom_out (vw->viking_vvp);
937 /* make sure mouse is still over the same point on the map when we zoom */
940 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
941 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
942 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
943 if ( event->direction == GDK_SCROLL_UP )
944 vik_viewport_zoom_in (vw->viking_vvp);
946 vik_viewport_zoom_out(vw->viking_vvp);
947 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
948 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
949 center_y + (y - event->y) );
957 /********************************************************************************
959 ********************************************************************************/
960 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
964 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
965 GdkGC *thickgc = gdk_gc_new(d);
967 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
968 gdouble dx = (x2-x1)/len*10;
969 gdouble dy = (y2-y1)/len*10;
970 gdouble c = cos(DEG2RAD(15.0));
971 gdouble s = sin(DEG2RAD(15.0));
973 gdouble baseangle = 0;
976 /* draw line with arrow ends */
978 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
979 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
980 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
983 a_viewport_clip_line(&x1, &y1, &x2, &y2);
984 gdk_draw_line(d, gc, x1, y1, x2, y2);
986 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
987 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
988 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
989 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
990 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
991 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
997 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1001 gdk_gc_copy(thickgc, gc);
1002 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1003 gdk_color_parse("#2255cc", &color);
1004 gdk_gc_set_rgb_fg_color(thickgc, &color);
1006 gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - RAD2DEG(baseangle))*64, -RAD2DEG(angle)*64);
1009 gdk_gc_copy(thickgc, gc);
1010 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1011 for (i=0; i<180; i++) {
1012 c = cos(DEG2RAD(i)*2 + baseangle);
1013 s = sin(DEG2RAD(i)*2 + baseangle);
1016 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1018 gdouble ticksize = 2*CW;
1019 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1023 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1024 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1025 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1026 c = (CR+CW*2)*cos(baseangle);
1027 s = (CR+CW*2)*sin(baseangle);
1028 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1029 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1032 #define LABEL(x, y, w, h) { \
1033 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1034 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1035 gdk_draw_layout(d, gc, (x), (y), pl); }
1037 gint wd, hd, xd, yd;
1038 gint wb, hb, xb, yb;
1040 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1041 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1042 pango_layout_set_text(pl, "N", -1);
1043 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1045 /* draw label with distance */
1046 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1047 switch (dist_units) {
1048 case VIK_UNITS_DISTANCE_KILOMETRES:
1049 if (distance >= 1000 && distance < 100000) {
1050 g_sprintf(str, "%3.2f km", distance/1000.0);
1051 } else if (distance < 1000) {
1052 g_sprintf(str, "%d m", (int)distance);
1054 g_sprintf(str, "%d km", (int)distance/1000);
1057 case VIK_UNITS_DISTANCE_MILES:
1058 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1059 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1060 } else if (distance < VIK_MILES_TO_METERS(1)) {
1061 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1063 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1067 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1070 pango_layout_set_text(pl, str, -1);
1072 pango_layout_get_pixel_size ( pl, &wd, &hd );
1074 xd = (x1+x2)/2 + dy;
1075 yd = (y1+y2)/2 - hd/2 - dx;
1077 xd = (x1+x2)/2 - dy;
1078 yd = (y1+y2)/2 - hd/2 + dx;
1081 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1086 LABEL(xd, yd, wd, hd);
1088 /* draw label with bearing */
1089 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1090 pango_layout_set_text(pl, str, -1);
1091 pango_layout_get_pixel_size ( pl, &wb, &hb );
1092 xb = x1 + CR*cos(angle-M_PI_2);
1093 yb = y1 + CR*sin(angle-M_PI_2);
1095 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1101 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1102 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1106 LABEL(xb, yb, wb, hb);
1110 g_object_unref ( G_OBJECT ( pl ) );
1111 g_object_unref ( G_OBJECT ( labgc ) );
1112 g_object_unref ( G_OBJECT ( thickgc ) );
1118 gboolean has_oldcoord;
1120 } ruler_tool_state_t;
1122 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1124 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1127 s->has_oldcoord = FALSE;
1131 static void ruler_destroy (ruler_tool_state_t *s)
1136 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1141 if ( event->button == 1 ) {
1142 gchar *lat=NULL, *lon=NULL;
1143 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1144 vik_coord_to_latlon ( &coord, &ll );
1145 a_coords_latlon_to_string ( &ll, &lat, &lon );
1146 if ( s->has_oldcoord ) {
1147 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1148 switch (dist_units) {
1149 case VIK_UNITS_DISTANCE_KILOMETRES:
1150 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1152 case VIK_UNITS_DISTANCE_MILES:
1153 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1156 temp = g_strdup_printf ("Just to keep the compiler happy");
1157 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1160 s->has_oldcoord = FALSE;
1163 temp = g_strdup_printf ( "%s %s", lat, lon );
1164 s->has_oldcoord = TRUE;
1167 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1170 s->oldcoord = coord;
1173 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1174 draw_update ( s->vw );
1176 return VIK_LAYER_TOOL_ACK;
1179 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1181 VikViewport *vvp = s->vvp;
1182 VikWindow *vw = s->vw;
1188 if ( s->has_oldcoord ) {
1189 int oldx, oldy, w1, h1, w2, h2;
1190 static GdkPixmap *buf = NULL;
1191 gchar *lat=NULL, *lon=NULL;
1192 w1 = vik_viewport_get_width(vvp);
1193 h1 = vik_viewport_get_height(vvp);
1195 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1197 gdk_drawable_get_size(buf, &w2, &h2);
1198 if (w1 != w2 || h1 != h2) {
1199 g_object_unref ( G_OBJECT ( buf ) );
1200 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1203 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1204 vik_coord_to_latlon ( &coord, &ll );
1205 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1207 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1208 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1209 draw_ruler(vvp, buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1210 if (draw_buf_done) {
1211 static gpointer pass_along[3];
1212 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1213 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1214 pass_along[2] = buf;
1215 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1216 draw_buf_done = FALSE;
1218 a_coords_latlon_to_string(&ll, &lat, &lon);
1219 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1220 switch (dist_units) {
1221 case VIK_UNITS_DISTANCE_KILOMETRES:
1222 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1224 case VIK_UNITS_DISTANCE_MILES:
1225 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1228 temp = g_strdup_printf ("Just to keep the compiler happy");
1229 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1231 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1234 return VIK_LAYER_TOOL_ACK;
1237 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1239 return VIK_LAYER_TOOL_ACK;
1242 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1244 draw_update ( s->vw );
1247 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1249 if (event->keyval == GDK_Escape) {
1250 s->has_oldcoord = FALSE;
1251 ruler_deactivate ( vl, s );
1254 // Regardless of whether we used it, return false so other GTK things may use it
1258 static VikToolInterface ruler_tool =
1259 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1260 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1261 (VikToolConstructorFunc) ruler_create,
1262 (VikToolDestructorFunc) ruler_destroy,
1263 (VikToolActivationFunc) NULL,
1264 (VikToolActivationFunc) ruler_deactivate,
1265 (VikToolMouseFunc) ruler_click,
1266 (VikToolMouseMoveFunc) ruler_move,
1267 (VikToolMouseFunc) ruler_release,
1268 (VikToolKeyFunc) ruler_key_press,
1270 GDK_CURSOR_IS_PIXMAP,
1271 &cursor_ruler_pixbuf };
1272 /*** end ruler code ********************************************************/
1276 /********************************************************************************
1278 ********************************************************************************/
1283 // Track zoom bounds for zoom tool with shift modifier:
1284 gboolean bounds_active;
1287 } zoom_tool_state_t;
1290 * In case the screen size has changed
1292 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1296 // Allocate a drawing area the size of the viewport
1297 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1298 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1300 if ( !zts->pixmap ) {
1302 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1305 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1307 if ( w1 != w2 || h1 != h2 ) {
1308 // Has changed - delete and recreate with new values
1309 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1310 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1314 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1316 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1321 zts->bounds_active = FALSE;
1325 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1328 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1332 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1334 zts->vw->modified = TRUE;
1335 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1339 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1340 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1342 gboolean skip_update = FALSE;
1344 zts->bounds_active = FALSE;
1346 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1347 // This zoom is on the center position
1348 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1349 if ( event->button == 1 )
1350 vik_viewport_zoom_in (zts->vw->viking_vvp);
1351 else if ( event->button == 3 )
1352 vik_viewport_zoom_out (zts->vw->viking_vvp);
1354 else if ( modifiers == GDK_CONTROL_MASK ) {
1355 // This zoom is to recenter on the mouse position
1356 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1357 if ( event->button == 1 )
1358 vik_viewport_zoom_in (zts->vw->viking_vvp);
1359 else if ( event->button == 3 )
1360 vik_viewport_zoom_out (zts->vw->viking_vvp);
1362 else if ( modifiers == GDK_SHIFT_MASK ) {
1363 // Get start of new zoom bounds
1364 if ( event->button == 1 ) {
1365 zts->bounds_active = TRUE;
1366 zts->start_x = (gint) event->x;
1367 zts->start_y = (gint) event->y;
1372 /* make sure mouse is still over the same point on the map when we zoom */
1373 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1374 if ( event->button == 1 )
1375 vik_viewport_zoom_in (zts->vw->viking_vvp);
1376 else if ( event->button == 3 )
1377 vik_viewport_zoom_out(zts->vw->viking_vvp);
1378 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1379 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1380 center_x + (x - event->x),
1381 center_y + (y - event->y) );
1385 draw_update ( zts->vw );
1387 return VIK_LAYER_TOOL_ACK;
1390 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1392 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1394 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1395 zoomtool_resize_pixmap ( zts );
1397 // Blank out currently drawn area
1398 gdk_draw_drawable ( zts->pixmap,
1399 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1400 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1401 0, 0, 0, 0, -1, -1);
1403 // Calculate new box starting point & size in pixels
1404 int xx, yy, width, height;
1405 if ( event->y > zts->start_y ) {
1407 height = event->y-zts->start_y;
1411 height = zts->start_y-event->y;
1413 if ( event->x > zts->start_x ) {
1415 width = event->x-zts->start_x;
1419 width = zts->start_x-event->x;
1423 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1425 // Only actually draw when there's time to do so
1426 if (draw_buf_done) {
1427 static gpointer pass_along[3];
1428 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1429 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1430 pass_along[2] = zts->pixmap;
1431 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1432 draw_buf_done = FALSE;
1435 return VIK_LAYER_TOOL_ACK;
1438 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1440 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1442 zts->bounds_active = FALSE;
1444 // Ensure haven't just released on the exact same position
1445 // i.e. probably haven't moved the mouse at all
1446 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1448 VikCoord coord1, coord2;
1449 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1450 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1452 // From the extend of the bounds pick the best zoom level
1453 // c.f. trw_layer_zoom_to_show_latlons()
1454 // Maybe refactor...
1455 struct LatLon ll1, ll2;
1456 vik_coord_to_latlon(&coord1, &ll1);
1457 vik_coord_to_latlon(&coord2, &ll2);
1458 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1459 (ll1.lon+ll2.lon)/2 };
1461 VikCoord new_center;
1462 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1463 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1465 /* Convert into definite 'smallest' and 'largest' positions */
1466 struct LatLon minmin;
1467 if ( ll1.lat < ll2.lat )
1468 minmin.lat = ll1.lat;
1470 minmin.lat = ll2.lat;
1472 struct LatLon maxmax;
1473 if ( ll1.lon > ll2.lon )
1474 maxmax.lon = ll1.lon;
1476 maxmax.lon = ll2.lon;
1478 /* Always recalculate the 'best' zoom level */
1479 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1480 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1482 gdouble min_lat, max_lat, min_lon, max_lon;
1483 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1484 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1485 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1486 /* NB I think the logic used in this test to determine if the bounds is within view
1487 fails if track goes across 180 degrees longitude.
1488 Hopefully that situation is not too common...
1489 Mind you viking doesn't really do edge locations to well anyway */
1490 if ( min_lat < minmin.lat &&
1491 max_lat > minmin.lat &&
1492 min_lon < maxmax.lon &&
1493 max_lon > maxmax.lon )
1494 /* Found within zoom level */
1499 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1502 draw_update ( zts->vw );
1504 return VIK_LAYER_TOOL_ACK;
1507 static VikToolInterface zoom_tool =
1508 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1509 (VikToolConstructorFunc) zoomtool_create,
1510 (VikToolDestructorFunc) zoomtool_destroy,
1511 (VikToolActivationFunc) NULL,
1512 (VikToolActivationFunc) NULL,
1513 (VikToolMouseFunc) zoomtool_click,
1514 (VikToolMouseMoveFunc) zoomtool_move,
1515 (VikToolMouseFunc) zoomtool_release,
1518 GDK_CURSOR_IS_PIXMAP,
1519 &cursor_zoom_pixbuf };
1520 /*** end zoom code ********************************************************/
1522 /********************************************************************************
1524 ********************************************************************************/
1525 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1530 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1532 vw->modified = TRUE;
1533 if ( event->button == 1 )
1534 vik_window_pan_click ( vw, event );
1536 return VIK_LAYER_TOOL_ACK;
1539 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1541 vik_window_pan_move ( vw, event );
1542 return VIK_LAYER_TOOL_ACK;
1545 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1547 if ( event->button == 1 )
1548 vik_window_pan_release ( vw, event );
1549 return VIK_LAYER_TOOL_ACK;
1552 static VikToolInterface pan_tool =
1553 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1554 (VikToolConstructorFunc) pantool_create,
1555 (VikToolDestructorFunc) NULL,
1556 (VikToolActivationFunc) NULL,
1557 (VikToolActivationFunc) NULL,
1558 (VikToolMouseFunc) pantool_click,
1559 (VikToolMouseMoveFunc) pantool_move,
1560 (VikToolMouseFunc) pantool_release,
1564 /*** end pan code ********************************************************/
1566 /********************************************************************************
1568 ********************************************************************************/
1569 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1571 tool_ed_t *t = g_new(tool_ed_t, 1);
1575 t->is_waypoint = FALSE;
1579 static void selecttool_destroy (tool_ed_t *t)
1587 GdkEventButton *event;
1588 tool_ed_t *tool_edit;
1591 static void click_layer_selected (VikLayer *vl, clicker *ck)
1593 /* Do nothing when function call returns true; */
1594 /* i.e. stop on first found item */
1597 if ( vik_layer_get_interface(vl->type)->select_click )
1598 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1601 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1603 /* Only allow selection on primary button */
1604 if ( event->button == 1 ) {
1605 /* Enable click to apply callback to potentially all track/waypoint layers */
1606 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1607 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1610 ck.vvp = t->vw->viking_vvp;
1613 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1616 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1619 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1621 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1622 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1623 gint type = vik_treeview_item_get_type ( vtv, &iter );
1624 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1625 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1627 vik_treeview_item_unselect ( vtv, &iter );
1628 if ( vik_window_clear_highlight ( t->vw ) )
1629 draw_update ( t->vw );
1634 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1636 /* Act on currently selected item to show menu */
1637 if ( t->vw->selected_track || t->vw->selected_waypoint )
1638 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1639 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1642 return VIK_LAYER_TOOL_ACK;
1645 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1647 /* Only allow selection on primary button */
1648 if ( event->button == 1 ) {
1649 // Don't care about vl here
1651 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1652 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1654 return VIK_LAYER_TOOL_ACK;
1657 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1659 /* Only allow selection on primary button */
1660 if ( event->button == 1 ) {
1661 // Don't care about vl here
1663 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1664 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1666 return VIK_LAYER_TOOL_ACK;
1669 static VikToolInterface select_tool =
1670 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1671 (VikToolConstructorFunc) selecttool_create,
1672 (VikToolDestructorFunc) selecttool_destroy,
1673 (VikToolActivationFunc) NULL,
1674 (VikToolActivationFunc) NULL,
1675 (VikToolMouseFunc) selecttool_click,
1676 (VikToolMouseMoveFunc) selecttool_move,
1677 (VikToolMouseFunc) selecttool_release,
1678 (VikToolKeyFunc) NULL,
1683 /*** end select tool code ********************************************************/
1685 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1687 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
1688 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
1689 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
1690 if ( sel && vik_treeview_get_editing ( sel->vt ) )
1693 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1694 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1695 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1696 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1697 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1698 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1699 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1700 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1705 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1707 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1708 g_assert(check_box);
1709 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1711 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1713 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1716 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1720 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1723 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1726 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1729 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1733 gchar *s = (gchar *)gtk_action_get_name(a);
1739 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1740 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1741 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1742 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1743 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1748 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1750 VikCoord new_center;
1752 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1753 struct LatLon ll, llold;
1754 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1755 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1756 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1760 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1761 struct UTM utm, utmold;
1762 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1763 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1764 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1769 g_critical("Houston, we've had a problem.");
1773 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1778 * Refresh maps displayed
1780 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1782 // Only get 'new' maps
1783 simple_map_update ( vw, TRUE );
1786 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1788 VikLayerTypeEnum type;
1789 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1790 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1791 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1793 vw->modified = TRUE;
1799 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1801 a_clipboard_copy_selected ( vw->viking_vlp );
1804 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1806 vik_layers_panel_cut_selected ( vw->viking_vlp );
1807 vw->modified = TRUE;
1810 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1812 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
1814 vw->modified = TRUE;
1818 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1820 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1821 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1824 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1827 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1830 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1831 GError *error = NULL;
1832 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1833 if ( !show && !error )
1834 // No error to show, so unlikely this will get called
1835 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1838 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Help is not available because: %s.\nEnsure a Mime Type ghelp handler program is installed (e.g. yelp)."), error->message );
1839 g_error_free ( error );
1842 #endif /* WINDOWS */
1845 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1847 a_dialog_about(GTK_WINDOW(vw));
1850 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1852 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1854 vik_layers_panel_delete_selected ( vw->viking_vlp );
1855 vw->modified = TRUE;
1858 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1861 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1863 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1864 g_assert(check_box);
1865 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1867 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1869 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1872 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1874 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1877 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1879 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1881 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1884 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1886 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1889 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1891 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1893 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1896 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1898 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1901 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1903 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1905 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1908 /***************************************
1909 ** tool management routines
1911 ***************************************/
1913 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1915 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1918 vt->active_tool = -1;
1923 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1925 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1926 vt->tools[vt->n_tools].ti = *vti;
1927 vt->tools[vt->n_tools].layer_type = layer_type;
1929 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1932 vt->tools[vt->n_tools].state = NULL;
1937 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1940 for (i=0; i<vt->n_tools; i++) {
1941 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1948 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1950 int tool = toolbox_get_tool(vt, tool_name);
1951 toolbox_tool_t *t = &vt->tools[tool];
1952 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1954 if (tool == vt->n_tools) {
1955 g_critical("trying to activate a non-existent tool...");
1958 /* is the tool already active? */
1959 if (vt->active_tool == tool) {
1963 if (vt->active_tool != -1) {
1964 if (vt->tools[vt->active_tool].ti.deactivate) {
1965 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1968 if (t->ti.activate) {
1969 t->ti.activate(vl, t->state);
1971 vt->active_tool = tool;
1974 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1976 int tool = toolbox_get_tool(vt, tool_name);
1977 toolbox_tool_t *t = &vt->tools[tool];
1978 if (t->ti.cursor == NULL) {
1979 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1980 GError *cursor_load_err = NULL;
1981 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1982 /* TODO: settable offeset */
1983 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1984 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1986 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1989 return t->ti.cursor;
1992 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1994 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1995 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1996 gint ltype = vt->tools[vt->active_tool].layer_type;
1997 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1998 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2002 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2004 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2005 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2006 gint ltype = vt->tools[vt->active_tool].layer_type;
2007 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2008 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2009 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2013 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2015 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2016 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2017 gint ltype = vt->tools[vt->active_tool].layer_type;
2018 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2019 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2022 /** End tool management ************************************/
2024 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2026 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2029 /* this function gets called whenever a toolbar tool is clicked */
2030 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2032 /* White Magic, my friends ... White Magic... */
2034 toolbox_activate(vw->vt, gtk_action_get_name(a));
2036 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2038 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2039 /* We set cursor, even if it is NULL: it resets to default */
2040 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2042 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2043 vw->current_tool = TOOL_PAN;
2045 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2046 vw->current_tool = TOOL_ZOOM;
2048 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2049 vw->current_tool = TOOL_RULER;
2051 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2052 vw->current_tool = TOOL_SELECT;
2055 /* TODO: only enable tools from active layer */
2056 VikLayerTypeEnum layer_id;
2057 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2058 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2059 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2060 vw->current_tool = TOOL_LAYER;
2061 vw->tool_layer_id = layer_id;
2062 vw->tool_tool_id = tool_id;
2067 draw_status_tool ( vw );
2070 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2075 g_free ( vw->filename );
2076 if ( filename == NULL )
2078 vw->filename = NULL;
2082 vw->filename = g_strdup(filename);
2085 /* Refresh window's title */
2086 file = window_get_filename ( vw );
2087 title = g_strdup_printf( "%s - Viking", file );
2088 gtk_window_set_title ( GTK_WINDOW(vw), title );
2092 static const gchar *window_get_filename ( VikWindow *vw )
2094 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2097 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2099 GtkWidget *mode_button;
2102 #ifdef VIK_CONFIG_EXPEDIA
2103 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2105 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2106 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2107 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2109 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2110 g_assert ( mode_button );
2115 * vik_window_get_pan_move:
2116 * @vw: some VikWindow
2118 * Retrieves @vw's pan_move.
2120 * Should be removed as soon as possible.
2122 * Returns: @vw's pan_move
2126 gboolean vik_window_get_pan_move ( VikWindow *vw )
2128 return vw->pan_move;
2131 static void on_activate_recent_item (GtkRecentChooser *chooser,
2136 filename = gtk_recent_chooser_get_current_uri (chooser);
2137 if (filename != NULL)
2139 GFile *file = g_file_new_for_uri ( filename );
2140 gchar *path = g_file_get_path ( file );
2141 g_object_unref ( file );
2142 if ( self->filename )
2144 GSList *filenames = NULL;
2145 filenames = g_slist_append ( filenames, path );
2146 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2147 // NB: GSList & contents are freed by main.open_window
2150 vik_window_open_file ( self, path, TRUE );
2158 static void setup_recent_files (VikWindow *self)
2160 GtkRecentManager *manager;
2161 GtkRecentFilter *filter;
2162 GtkWidget *menu, *menu_item;
2164 filter = gtk_recent_filter_new ();
2165 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2166 gtk_recent_filter_add_group(filter, "viking");
2168 manager = gtk_recent_manager_get_default ();
2169 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2170 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2171 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2173 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2174 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2176 g_signal_connect (G_OBJECT (menu), "item-activated",
2177 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2180 static void update_recently_used_document(const gchar *filename)
2182 /* Update Recently Used Document framework */
2183 GtkRecentManager *manager = gtk_recent_manager_get_default();
2184 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2185 gchar *groups[] = {"viking", NULL};
2186 GFile *file = g_file_new_for_commandline_arg(filename);
2187 gchar *uri = g_file_get_uri(file);
2188 gchar *basename = g_path_get_basename(filename);
2189 g_object_unref(file);
2192 recent_data->display_name = basename;
2193 recent_data->description = NULL;
2194 recent_data->mime_type = "text/x-gps-data";
2195 recent_data->app_name = (gchar *) g_get_application_name ();
2196 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2197 recent_data->groups = groups;
2198 recent_data->is_private = FALSE;
2199 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2201 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2206 g_free (recent_data->app_exec);
2207 g_slice_free (GtkRecentData, recent_data);
2211 * Call this before doing things that may take a long time and otherwise not show any other feedback
2212 * such as loading and saving files
2214 void vik_window_set_busy_cursor ( VikWindow *vw )
2216 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2217 // Viewport has a separate cursor
2218 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->busy_cursor );
2219 // Ensure cursor updated before doing stuff
2220 while( gtk_events_pending() )
2221 gtk_main_iteration();
2224 void vik_window_clear_busy_cursor ( VikWindow *vw )
2226 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2227 // Restore viewport cursor
2228 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->viewport_cursor );
2231 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2233 vik_window_set_busy_cursor ( vw );
2235 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2237 case LOAD_TYPE_READ_FAILURE:
2238 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2240 case LOAD_TYPE_GPSBABEL_FAILURE:
2241 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2243 case LOAD_TYPE_GPX_FAILURE:
2244 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2246 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2247 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2249 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2251 // Since we can process .vik files with issues just show a warning in the status bar
2252 // Not that a user can do much about it... or tells them what this issue is yet...
2253 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2254 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2257 // No break, carry on to show any data
2258 case LOAD_TYPE_VIK_SUCCESS:
2260 GtkWidget *mode_button;
2262 if ( change_filename )
2263 window_set_filename ( vw, filename );
2264 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2265 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. */
2266 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2267 vw->only_updating_coord_mode_ui = FALSE;
2269 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2271 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2272 g_assert ( mode_button );
2273 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2275 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2276 g_assert ( mode_button );
2277 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2279 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2280 g_assert ( mode_button );
2281 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2283 //case LOAD_TYPE_OTHER_SUCCESS:
2285 update_recently_used_document(filename);
2290 vik_window_clear_busy_cursor ( vw );
2293 static void load_file ( GtkAction *a, VikWindow *vw )
2295 GSList *files = NULL;
2296 GSList *cur_file = NULL;
2298 if (!strcmp(gtk_action_get_name(a), "Open")) {
2301 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2305 g_critical("Houston, we've had a problem.");
2309 if ( ! vw->open_dia )
2311 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2313 GTK_FILE_CHOOSER_ACTION_OPEN,
2314 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2315 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2317 gchar *cwd = g_get_current_dir();
2319 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2323 GtkFileFilter *filter;
2324 // NB file filters are listed this way for alphabetical ordering
2325 #ifdef VIK_CONFIG_GEOCACHES
2326 filter = gtk_file_filter_new ();
2327 gtk_file_filter_set_name( filter, _("Geocaching") );
2328 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2329 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2332 filter = gtk_file_filter_new ();
2333 gtk_file_filter_set_name( filter, _("Google Earth") );
2334 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2335 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2337 filter = gtk_file_filter_new ();
2338 gtk_file_filter_set_name( filter, _("GPX") );
2339 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2340 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2342 filter = gtk_file_filter_new ();
2343 gtk_file_filter_set_name( filter, _("Viking") );
2344 gtk_file_filter_add_pattern ( filter, "*.vik" );
2345 gtk_file_filter_add_pattern ( filter, "*.viking" );
2346 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2348 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2349 // However assume this are barely used and thus not worthy of inclusion
2350 // as they'll just make the options too many and have no clear file pattern
2351 // one can always use the all option
2352 filter = gtk_file_filter_new ();
2353 gtk_file_filter_set_name( filter, _("All") );
2354 gtk_file_filter_add_pattern ( filter, "*" );
2355 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2356 // Default to any file - same as before open filters were added
2357 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2359 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2360 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2361 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2363 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2365 gtk_widget_hide ( vw->open_dia );
2366 #ifdef VIKING_PROMPT_IF_MODIFIED
2367 if ( (vw->modified || vw->filename) && newwindow )
2369 if ( vw->filename && newwindow )
2371 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2373 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2374 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2375 gboolean first_vik_file = TRUE;
2377 while ( cur_file ) {
2379 gchar *file_name = cur_file->data;
2380 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2381 // Load first of many .vik files in current window
2382 if ( first_vik_file ) {
2383 vik_window_open_file ( vw, file_name, TRUE );
2384 first_vik_file = FALSE;
2387 // Load each subsequent .vik file in a separate window
2388 VikWindow *newvw = vik_window_new_window ();
2390 vik_window_open_file ( newvw, file_name, TRUE );
2395 vik_window_open_file ( vw, file_name, change_fn );
2398 cur_file = g_slist_next (cur_file);
2400 g_slist_free (files);
2404 gtk_widget_hide ( vw->open_dia );
2407 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2409 gboolean rv = FALSE;
2411 if ( ! vw->save_dia )
2413 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2415 GTK_FILE_CHOOSER_ACTION_SAVE,
2416 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2417 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2419 gchar *cwd = g_get_current_dir();
2421 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2425 GtkFileFilter *filter;
2426 filter = gtk_file_filter_new ();
2427 gtk_file_filter_set_name( filter, _("All") );
2428 gtk_file_filter_add_pattern ( filter, "*" );
2429 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2431 filter = gtk_file_filter_new ();
2432 gtk_file_filter_set_name( filter, _("Viking") );
2433 gtk_file_filter_add_pattern ( filter, "*.vik" );
2434 gtk_file_filter_add_pattern ( filter, "*.viking" );
2435 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2436 // Default to a Viking file
2437 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2439 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2440 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2442 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2443 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2444 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2445 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2447 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2449 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2451 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2452 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 ) ) )
2454 window_set_filename ( vw, fn );
2455 rv = window_save ( vw );
2456 vw->modified = FALSE;
2460 g_free ( auto_save_name );
2461 gtk_widget_hide ( vw->save_dia );
2465 static gboolean window_save ( VikWindow *vw )
2467 vik_window_set_busy_cursor ( vw );
2468 gboolean success = TRUE;
2470 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2472 update_recently_used_document ( vw->filename );
2476 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2479 vik_window_clear_busy_cursor ( vw );
2483 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2485 if ( ! vw->filename )
2486 return save_file_as ( NULL, vw );
2489 vw->modified = FALSE;
2490 return window_save ( vw );
2497 * Export all TRW Layers in the list to individual files in the specified directory
2499 * Returns: %TRUE on success
2501 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2503 gboolean success = TRUE;
2505 gint export_count = 0;
2507 vik_window_set_busy_cursor ( vw );
2511 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
2513 // Some protection in attempting to write too many same named files
2514 // As this will get horribly slow...
2515 gboolean safe = FALSE;
2517 while ( ii < 5000 ) {
2518 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
2521 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
2532 // NB: We allow exporting empty layers
2534 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
2536 // Show some progress
2537 if ( this_success ) {
2539 gchar *message = g_strconcat ( _("Exporting to file: "), fn, NULL );
2540 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2541 while ( gtk_events_pending() )
2542 gtk_main_iteration ();
2546 success = success && this_success;
2550 gl = g_list_next ( gl );
2553 vik_window_clear_busy_cursor ( vw );
2555 // Confirm what happened.
2556 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
2557 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2563 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
2565 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2568 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
2572 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Export to directory"),
2574 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2576 GTK_RESPONSE_REJECT,
2578 GTK_RESPONSE_ACCEPT,
2581 GtkWidget *gw = gtk_file_chooser_widget_new ( GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
2582 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), gw, TRUE, TRUE, 0 );
2584 // try to make it a nice size - otherwise seems to default to something impractically small
2585 gtk_window_set_default_size ( GTK_WINDOW(dialog), 600, 300 );
2587 gtk_widget_show_all ( dialog );
2589 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
2590 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(gw) );
2591 gtk_widget_destroy ( dialog );
2593 if ( !export_to ( vw, gl, vft, dir, extension ) )
2594 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
2599 gtk_widget_destroy ( dialog );
2604 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
2606 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
2609 static void export_to_kml ( GtkAction *a, VikWindow *vw )
2611 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
2614 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2616 // Via the file menu, acquiring from a GPS makes a new layer
2617 // this has always been the way (not entirely sure if this was the real intention!)
2618 // thus maintain the behaviour ATM.
2619 // Hence explicit setting here (as the value may be changed elsewhere)
2620 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2621 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2624 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2626 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2629 #ifdef VIK_CONFIG_GOOGLE
2630 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2632 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2636 #ifdef VIK_CONFIG_OPENSTREETMAP
2637 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2639 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2642 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
2644 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface );
2648 #ifdef VIK_CONFIG_GEOCACHES
2649 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2651 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2655 #ifdef VIK_CONFIG_GEOTAG
2656 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2658 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2659 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2663 #ifdef VIK_CONFIG_GEONAMES
2664 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
2666 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface );
2670 static void goto_default_location( GtkAction *a, VikWindow *vw)
2673 ll.lat = a_vik_get_default_lat();
2674 ll.lon = a_vik_get_default_long();
2675 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2676 vik_layers_panel_emit_update(vw->viking_vlp);
2680 static void goto_address( GtkAction *a, VikWindow *vw)
2682 a_vik_goto ( vw, vw->viking_vvp );
2683 vik_layers_panel_emit_update ( vw->viking_vlp );
2686 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2691 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
2693 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
2696 return; // Internally broken :(
2698 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
2699 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
2700 // NB no update needed
2702 g_strfreev ( texts );
2705 static void preferences_change_update ( VikWindow *vw, gpointer data )
2707 // Want to update all TrackWaypoint layers
2708 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2710 GList *iter = g_list_first ( layers );
2712 // Reset the individual waypoints themselves due to the preferences change
2713 VikTrwLayer *vtl = VIK_TRW_LAYER(VIK_LAYER(layers->data));
2714 vik_trw_layer_reset_waypoints ( vtl );
2715 iter = g_list_next ( iter );
2718 g_list_free ( layers );
2723 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2725 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2727 a_preferences_show_window ( GTK_WINDOW(vw) );
2729 // Has the waypoint size setting changed?
2730 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
2731 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2732 clear_garmin_icon_syms ();
2734 // Update all windows
2735 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
2739 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2741 /* Simplistic repeat of preference setting
2742 Only the name & type are important for setting the preference via this 'external' way */
2743 VikLayerParam pref_lat[] = {
2744 { VIK_LAYER_NUM_TYPES,
2745 VIKING_PREFERENCES_NAMESPACE "default_latitude",
2746 VIK_LAYER_PARAM_DOUBLE,
2749 VIK_LAYER_WIDGET_SPINBUTTON,
2754 VikLayerParam pref_lon[] = {
2755 { VIK_LAYER_NUM_TYPES,
2756 VIKING_PREFERENCES_NAMESPACE "default_longitude",
2757 VIK_LAYER_PARAM_DOUBLE,
2760 VIK_LAYER_WIDGET_SPINBUTTON,
2766 /* Get current center */
2768 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2770 /* Apply to preferences */
2771 VikLayerParamData vlp_data;
2772 vlp_data.d = ll.lat;
2773 a_preferences_run_setparam (vlp_data, pref_lat);
2774 vlp_data.d = ll.lon;
2775 a_preferences_run_setparam (vlp_data, pref_lon);
2776 /* Remember to save */
2777 a_preferences_save_to_file();
2780 static void clear_cb ( GtkAction *a, VikWindow *vw )
2782 vik_layers_panel_clear ( vw->viking_vlp );
2783 window_set_filename ( vw, NULL );
2787 static void window_close ( GtkAction *a, VikWindow *vw )
2789 if ( ! delete_event ( vw ) )
2790 gtk_widget_destroy ( GTK_WIDGET(vw) );
2793 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2795 if (save_file( NULL, vw)) {
2796 window_close( NULL, vw);
2803 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2805 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2806 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2808 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2809 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2814 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2816 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2817 GdkPixbuf *pixbuf_to_save;
2818 gdouble old_xmpp, old_ympp;
2819 GError *error = NULL;
2821 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2822 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2825 _("Generating image file...") );
2827 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2828 // Ensure dialog shown
2829 gtk_widget_show_all ( msgbox );
2831 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2832 while ( gtk_events_pending() )
2833 gtk_main_iteration ();
2834 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2835 // At least the empty box can give a clue something's going on + the statusbar msg...
2836 // Windows version under Wine OK!
2838 /* backup old zoom & set new */
2839 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2840 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2841 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2843 /* reset width and height: */
2844 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2846 /* draw all layers */
2849 /* save buffer as file. */
2850 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);
2851 if ( !pixbuf_to_save ) {
2852 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2853 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
2857 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2860 g_warning("Unable to write to file %s: %s", fn, error->message );
2861 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
2862 g_error_free (error);
2866 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2868 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2871 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2872 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2873 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2875 /* pretend like nothing happened ;) */
2876 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2877 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2878 vik_viewport_configure ( vw->viking_vvp );
2882 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 )
2884 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2885 gchar *name_of_file = g_malloc ( size );
2887 struct UTM utm_orig, utm;
2889 /* *** copied from above *** */
2890 GdkPixbuf *pixbuf_to_save;
2891 gdouble old_xmpp, old_ympp;
2892 GError *error = NULL;
2894 /* backup old zoom & set new */
2895 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2896 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2897 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2899 /* reset width and height: do this only once for all images (same size) */
2900 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2901 /* *** end copy from above *** */
2903 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2907 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2909 for ( y = 1; y <= tiles_h; y++ )
2911 for ( x = 1; x <= tiles_w; x++ )
2913 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2915 if ( tiles_w & 0x1 )
2916 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2918 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2919 if ( tiles_h & 0x1 ) /* odd */
2920 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2922 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2924 /* move to correct place. */
2925 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2929 /* save buffer as file. */
2930 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);
2931 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2934 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2935 g_error_free (error);
2938 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2942 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2943 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2944 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2945 vik_viewport_configure ( vw->viking_vvp );
2948 g_free ( name_of_file );
2951 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2953 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2954 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2956 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2957 gdouble zoom = pow (2, active-2 );
2959 gdouble width_min, width_max, height_min, height_max;
2962 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2963 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2965 /* TODO: support for xzoom and yzoom values */
2966 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2967 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2969 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2970 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2972 gtk_spin_button_set_value ( width_spin, width );
2973 gtk_spin_button_set_value ( height_spin, height );
2976 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2978 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2980 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2981 gdouble zoom = pow (2, active-2 );
2985 w = gtk_spin_button_get_value(width_spin) * zoom;
2986 h = gtk_spin_button_get_value(height_spin) * zoom;
2987 if (pass_along[4]) /* save many images; find TOTAL area covered */
2989 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2990 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2992 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2993 switch (dist_units) {
2994 case VIK_UNITS_DISTANCE_KILOMETRES:
2995 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2997 case VIK_UNITS_DISTANCE_MILES:
2998 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3001 label_text = g_strdup_printf ("Just to keep the compiler happy");
3002 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3005 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3006 g_free ( label_text );
3010 * Get an allocated filename (or directory as specified)
3012 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3015 if ( one_image_only )
3018 if (!vw->save_img_dia) {
3019 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3021 GTK_FILE_CHOOSER_ACTION_SAVE,
3022 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3023 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3026 gchar *cwd = g_get_current_dir();
3028 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3032 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3034 GtkFileFilter *filter;
3035 filter = gtk_file_filter_new ();
3036 gtk_file_filter_set_name ( filter, _("All") );
3037 gtk_file_filter_add_pattern ( filter, "*" );
3038 gtk_file_chooser_add_filter ( chooser, filter );
3040 filter = gtk_file_filter_new ();
3041 gtk_file_filter_set_name ( filter, _("JPG") );
3042 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3043 gtk_file_chooser_add_filter ( chooser, filter );
3045 filter = gtk_file_filter_new ();
3046 gtk_file_filter_set_name ( filter, _("PNG") );
3047 gtk_file_filter_add_mime_type ( filter, "image/png");
3048 gtk_file_chooser_add_filter ( chooser, filter );
3051 gtk_file_chooser_set_filter ( chooser, filter );
3053 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3054 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3057 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3058 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3059 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3060 if ( ! 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 ) ) )
3063 gtk_widget_hide ( vw->save_img_dia );
3067 // For some reason this method is only written to work in UTM...
3068 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3069 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3073 if (!vw->save_img_dir_dia) {
3074 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3076 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3077 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3078 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3080 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3081 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3084 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3085 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3087 gtk_widget_hide ( vw->save_img_dir_dia );
3092 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3094 /* todo: default for answers inside VikWindow or static (thruout instance) */
3095 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3096 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3098 GTK_RESPONSE_REJECT,
3100 GTK_RESPONSE_ACCEPT,
3102 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3103 GtkWidget *png_radio, *jpeg_radio;
3104 GtkWidget *current_window_button;
3105 gpointer current_window_pass_along[7];
3106 GtkWidget *zoom_label, *zoom_combo;
3107 GtkWidget *total_size_label;
3109 /* only used if (!one_image_only) */
3110 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3112 width_label = gtk_label_new ( _("Width (pixels):") );
3113 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3114 height_label = gtk_label_new ( _("Height (pixels):") );
3115 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3117 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3119 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3120 /* TODO: separate xzoom and yzoom factors */
3121 zoom_combo = create_zoom_combo_all_levels();
3123 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3124 gint active = 2 + round ( log (mpp) / log (2) );
3126 // Can we not hard code size here?
3129 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3131 total_size_label = gtk_label_new ( NULL );
3133 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3134 current_window_pass_along [0] = vw;
3135 current_window_pass_along [1] = width_spin;
3136 current_window_pass_along [2] = height_spin;
3137 current_window_pass_along [3] = zoom_combo;
3138 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3139 current_window_pass_along [5] = NULL;
3140 current_window_pass_along [6] = total_size_label;
3141 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3143 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3144 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3146 if ( ! vw->draw_image_save_as_png )
3147 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3149 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3150 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3151 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3152 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3154 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3156 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3157 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3158 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3159 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3160 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3162 if ( ! one_image_only )
3164 GtkWidget *tiles_width_label, *tiles_height_label;
3166 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3167 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3168 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3169 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3170 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3171 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3172 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3173 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3175 current_window_pass_along [4] = tiles_width_spin;
3176 current_window_pass_along [5] = tiles_height_spin;
3177 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3178 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3180 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3181 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3182 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3183 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3185 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3187 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3189 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3191 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3193 gtk_widget_hide ( GTK_WIDGET(dialog) );
3195 gchar *fn = draw_image_filename ( vw, one_image_only );
3199 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3200 gdouble zoom = pow (2, active-2 );
3202 if ( one_image_only )
3203 save_image_file ( vw, fn,
3204 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3205 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3207 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3209 // NB is in UTM mode ATM
3210 save_image_dir ( vw, fn,
3211 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3212 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3214 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3215 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3216 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3221 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3225 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3227 draw_to_image_file ( vw, TRUE );
3230 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3232 draw_to_image_file ( vw, FALSE );
3235 static void print_cb ( GtkAction *a, VikWindow *vw )
3237 a_print(vw, vw->viking_vvp);
3240 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3241 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3243 VikViewportDrawMode drawmode;
3244 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3245 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3247 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3248 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3250 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3251 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3253 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3254 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3257 g_critical("Houston, we've had a problem.");
3261 if ( !vw->only_updating_coord_mode_ui )
3263 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3264 if ( olddrawmode != drawmode )
3266 /* this takes care of coord mode too */
3267 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3268 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3269 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3270 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3271 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3278 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3280 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3281 g_assert(check_box);
3282 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3283 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3287 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3289 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3290 g_assert(check_box);
3291 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3292 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3296 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3298 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3299 g_assert(check_box);
3300 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3301 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3305 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3307 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3308 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3309 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3310 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3311 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3313 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3314 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3318 gtk_widget_destroy ( colorsd );
3321 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3323 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3324 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3325 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3326 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3327 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3329 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3330 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3334 gtk_widget_destroy ( colorsd );
3339 /***********************************************************************************************
3341 ***********************************************************************************************/
3343 static GtkActionEntry entries[] = {
3344 { "File", NULL, N_("_File"), 0, 0, 0 },
3345 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3346 { "View", NULL, N_("_View"), 0, 0, 0 },
3347 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3348 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3349 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3350 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3351 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3352 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3353 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3355 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3356 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3357 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3358 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3359 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3360 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3361 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3362 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3363 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3364 #ifdef VIK_CONFIG_GOOGLE
3365 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
3367 #ifdef VIK_CONFIG_OPENSTREETMAP
3368 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3369 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3371 #ifdef VIK_CONFIG_GEOCACHES
3372 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3374 #ifdef VIK_CONFIG_GEOTAG
3375 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3377 #ifdef VIK_CONFIG_GEONAMES
3378 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3380 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3381 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3382 { "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 },
3383 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3384 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3385 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3386 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3388 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3389 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3390 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3391 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3392 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3393 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3394 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3395 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3396 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3397 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3398 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3399 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3400 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3401 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3402 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3404 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3405 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3406 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3407 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3408 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3409 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3410 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3411 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3412 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3413 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3415 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3416 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3419 static GtkActionEntry entries_gpsbabel[] = {
3420 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3424 /* FIXME use VIEWPORT_DRAWMODE values */
3425 static GtkRadioActionEntry mode_entries[] = {
3426 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3427 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3428 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3429 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3432 static GtkToggleActionEntry toggle_entries[] = {
3433 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3434 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3435 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3436 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3437 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3438 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3439 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3440 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3443 #include "menu.xml.h"
3444 static void window_create_ui( VikWindow *window )
3447 GtkActionGroup *action_group;
3448 GtkAccelGroup *accel_group;
3451 GtkIconFactory *icon_factory;
3452 GtkIconSet *icon_set;
3453 GtkRadioActionEntry *tools = NULL, *radio;
3456 uim = gtk_ui_manager_new ();
3459 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3460 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3461 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3462 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3465 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3466 g_error_free (error);
3470 action_group = gtk_action_group_new ("MenuActions");
3471 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3472 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3473 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3474 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3476 // Use this to see if GPSBabel is available:
3477 if ( a_babel_device_list ) {
3478 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
3479 if ( gtk_ui_manager_add_ui_from_string ( uim,
3480 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
3482 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
3485 icon_factory = gtk_icon_factory_new ();
3486 gtk_icon_factory_add_default (icon_factory);
3488 register_vik_icons(icon_factory);
3490 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3491 // so that it can be applied to the UI in one action group add function call below
3493 for (i=0; i<window->vt->n_tools; i++) {
3494 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3495 radio = &tools[ntools];
3497 *radio = window->vt->tools[i].ti.radioActionEntry;
3498 radio->value = ntools;
3501 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3502 GtkActionEntry action;
3503 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3504 vik_layer_get_interface(i)->name,
3505 vik_layer_get_interface(i)->name,
3506 GTK_UI_MANAGER_MENUITEM, FALSE);
3508 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3509 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3510 gtk_icon_set_unref (icon_set);
3512 action.name = vik_layer_get_interface(i)->name;
3513 action.stock_id = vik_layer_get_interface(i)->name;
3514 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3515 action.accelerator = vik_layer_get_interface(i)->accelerator;
3516 action.tooltip = NULL;
3517 action.callback = (GCallback)menu_addlayer_cb;
3518 gtk_action_group_add_actions(action_group, &action, 1, window);
3520 if ( vik_layer_get_interface(i)->tools_count ) {
3521 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3522 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3525 // Further tool copying for to apply to the UI, also apply menu UI setup
3526 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3527 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3528 radio = &tools[ntools];
3531 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3532 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3533 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3534 GTK_UI_MANAGER_MENUITEM, FALSE);
3535 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3536 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3537 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3538 GTK_UI_MANAGER_TOOLITEM, FALSE);
3540 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3542 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3543 // Overwrite with actual number to use
3544 radio->value = ntools;
3547 GtkActionEntry action_dl;
3548 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
3549 vik_layer_get_interface(i)->name,
3550 g_strdup_printf("Layer%s", vik_layer_get_interface(i)->fixed_layer_name),
3551 GTK_UI_MANAGER_MENUITEM, FALSE);
3553 // For default layers use action names of the form 'Layer<LayerName>'
3554 // This is to avoid clashing with just the layer name used above for the tool actions
3555 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
3556 action_dl.stock_id = NULL;
3557 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
3558 action_dl.accelerator = NULL;
3559 action_dl.tooltip = NULL;
3560 action_dl.callback = (GCallback)layer_defaults_cb;
3561 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
3563 g_object_unref (icon_factory);
3565 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3568 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3570 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3571 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3572 GtkAction *action = gtk_action_group_get_action(action_group,
3573 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3574 g_object_set(action, "sensitive", FALSE, NULL);
3578 // This is done last so we don't need to track the value of mid anymore
3579 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
3581 window->action_group = action_group;
3583 accel_group = gtk_ui_manager_get_accel_group (uim);
3584 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3585 gtk_ui_manager_ensure_update (uim);
3587 setup_recent_files(window);
3591 // TODO - add method to add tool icons defined from outside this file
3592 // and remove the reverse dependency on icon definition from this file
3594 const GdkPixdata *data;
3597 { &mover_22_pixbuf, "vik-icon-pan" },
3598 { &zoom_18_pixbuf, "vik-icon-zoom" },
3599 { &ruler_18_pixbuf, "vik-icon-ruler" },
3600 { &select_18_pixbuf, "vik-icon-select" },
3601 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3602 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3603 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3604 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3605 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3606 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3607 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3608 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3609 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3610 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3611 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3614 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3617 register_vik_icons (GtkIconFactory *icon_factory)
3619 GtkIconSet *icon_set;
3622 for (i = 0; i < n_stock_icons; i++) {
3623 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3624 stock_icons[i].data, FALSE, NULL ));
3625 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3626 gtk_icon_set_unref (icon_set);
3630 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3632 return vw->selected_vtl;
3635 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3637 vw->selected_vtl = vtl;
3638 vw->containing_vtl = vtl;
3640 vw->selected_track = NULL;
3641 vw->selected_tracks = NULL;
3642 vw->selected_waypoint = NULL;
3643 vw->selected_waypoints = NULL;
3644 // Set highlight thickness
3645 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3648 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
3650 return vw->selected_tracks;
3653 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3655 vw->selected_tracks = ght;
3656 vw->containing_vtl = vtl;
3658 vw->selected_vtl = NULL;
3659 vw->selected_track = NULL;
3660 vw->selected_waypoint = NULL;
3661 vw->selected_waypoints = NULL;
3662 // Set highlight thickness
3663 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3666 gpointer vik_window_get_selected_track ( VikWindow *vw )
3668 return vw->selected_track;
3671 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
3673 vw->selected_track = vt;
3674 vw->containing_vtl = vtl;
3676 vw->selected_vtl = NULL;
3677 vw->selected_tracks = NULL;
3678 vw->selected_waypoint = NULL;
3679 vw->selected_waypoints = NULL;
3680 // Set highlight thickness
3681 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3684 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
3686 return vw->selected_waypoints;
3689 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3691 vw->selected_waypoints = ght;
3692 vw->containing_vtl = vtl;
3694 vw->selected_vtl = NULL;
3695 vw->selected_track = NULL;
3696 vw->selected_tracks = NULL;
3697 vw->selected_waypoint = NULL;
3700 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3702 return vw->selected_waypoint;
3705 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
3707 vw->selected_waypoint = vwp;
3708 vw->containing_vtl = vtl;
3710 vw->selected_vtl = NULL;
3711 vw->selected_track = NULL;
3712 vw->selected_tracks = NULL;
3713 vw->selected_waypoints = NULL;
3716 gboolean vik_window_clear_highlight ( VikWindow *vw )
3718 gboolean need_redraw = FALSE;
3719 if ( vw->selected_vtl != NULL ) {
3720 vw->selected_vtl = NULL;
3723 if ( vw->selected_track != NULL ) {
3724 vw->selected_track = NULL;
3727 if ( vw->selected_tracks != NULL ) {
3728 vw->selected_tracks = NULL;
3731 if ( vw->selected_waypoint != NULL ) {
3732 vw->selected_waypoint = NULL;
3735 if ( vw->selected_waypoints != NULL ) {
3736 vw->selected_waypoints = NULL;
3742 GThread *vik_window_get_thread ( VikWindow *vw )