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, const gchar *message, vik_statusbar_type_t vs_type );
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 /* tool management state */
165 guint16 tool_layer_id;
166 guint16 tool_tool_id;
168 GtkActionGroup *action_group;
173 guint draw_image_width, draw_image_height;
174 gboolean draw_image_save_as_png;
179 GtkWidget *open_dia, *save_dia;
180 GtkWidget *save_img_dia, *save_img_dir_dia;
182 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
186 /* half-drawn update */
188 VikCoord trigger_center;
190 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
191 /* Only one of these items can be selected at the same time */
192 gpointer selected_vtl; /* notionally VikTrwLayer */
193 GHashTable *selected_tracks;
194 gpointer selected_track; /* notionally VikTrack */
195 GHashTable *selected_waypoints;
196 gpointer selected_waypoint; /* notionally VikWaypoint */
197 /* only use for individual track or waypoint */
198 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
199 gpointer containing_vtl; /* notionally VikTrwLayer */
213 VW_OPENWINDOW_SIGNAL,
214 VW_STATUSBAR_UPDATE_SIGNAL,
218 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
220 // TODO get rid of this as this is unnecessary duplication...
221 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
223 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
225 VikViewport * vik_window_viewport(VikWindow *vw)
227 return(vw->viking_vvp);
230 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
232 return(vw->viking_vlp);
236 * Returns the statusbar for the window
238 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
240 return vw->viking_vs;
244 * For signalling the update from a background thread
246 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
248 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message, vs_type );
252 * For the actual statusbar update!
254 static gboolean statusbar_idle_update ( gpointer indata )
256 gpointer *data = indata;
257 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
262 * Update statusbar in the main thread
264 static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
266 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
267 static gpointer data[3];
268 data[0] = vw->viking_vs;
269 data[1] = (gchar*) message;
270 data[2] = GINT_TO_POINTER(vs_type);
271 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
274 // Actual signal handlers
275 static void destroy_window ( GtkWidget *widget,
278 if ( ! --window_count )
282 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type )
284 window_statusbar_update ( vw, message, vs_type );
287 VikWindow *vik_window_new_window ()
289 if ( window_count < MAX_WINDOWS )
291 VikWindow *vw = window_new ();
293 g_signal_connect (G_OBJECT (vw), "destroy",
294 G_CALLBACK (destroy_window), NULL);
295 g_signal_connect (G_OBJECT (vw), "newwindow",
296 G_CALLBACK (vik_window_new_window), NULL);
297 g_signal_connect (G_OBJECT (vw), "openwindow",
298 G_CALLBACK (open_window), NULL);
299 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
300 G_CALLBACK (statusbar_update), vw);
302 gtk_widget_show_all ( GTK_WIDGET(vw) );
311 static void open_window ( VikWindow *vw, GSList *files )
313 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
314 GSList *cur_file = files;
316 // Only open a new window if a viking file
317 gchar *file_name = cur_file->data;
318 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
319 VikWindow *newvw = vik_window_new_window ();
321 vik_window_open_file ( newvw, file_name, TRUE );
324 vik_window_open_file ( vw, file_name, change_fn );
327 cur_file = g_slist_next (cur_file);
329 g_slist_free (files);
333 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
335 int i, j, tool_count;
336 VikLayerInterface *layer_interface;
338 if (!vw->action_group) return;
340 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
342 layer_interface = vik_layer_get_interface(i);
343 tool_count = layer_interface->tools_count;
345 for (j = 0; j < tool_count; j++) {
346 action = gtk_action_group_get_action(vw->action_group,
347 layer_interface->tools[j].radioActionEntry.name);
348 g_object_set(action, "sensitive", i == vl->type, NULL);
353 static void window_finalize ( GObject *gob )
355 VikWindow *vw = VIK_WINDOW(gob);
356 g_return_if_fail ( vw != NULL );
358 a_background_remove_window ( vw );
360 window_list = g_slist_remove ( window_list, vw );
362 G_OBJECT_CLASS(parent_class)->finalize(gob);
366 static void vik_window_class_init ( VikWindowClass *klass )
369 GObjectClass *object_class;
371 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);
372 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);
373 window_signals[VW_STATUSBAR_UPDATE_SIGNAL] = g_signal_new ( "statusbarupdate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, statusbarupdate), NULL, NULL, gtk_marshal_VOID__POINTER_UINT, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
375 object_class = G_OBJECT_CLASS (klass);
377 object_class->finalize = window_finalize;
379 parent_class = g_type_class_peek_parent (klass);
383 static void zoom_changed (GtkMenuShell *menushell,
386 VikWindow *vw = VIK_WINDOW (user_data);
388 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
389 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
391 gdouble zoom_request = pow (2, active-2 );
393 // But has it really changed?
394 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
395 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
396 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
397 // Force drawing update
402 static GtkWidget * create_zoom_menu_all_levels ()
404 GtkWidget *menu = gtk_menu_new ();
405 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768", NULL };
408 for (i = 0 ; itemLabels[i] != NULL ; i++)
410 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
411 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
412 gtk_widget_show (item);
413 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
419 static GtkWidget *create_zoom_combo_all_levels ()
421 GtkWidget *zoom_combo = gtk_combo_box_new_text();
422 GtkComboBox *combo = GTK_COMBO_BOX ( zoom_combo );
423 gtk_combo_box_append_text ( combo, "0.25");
424 gtk_combo_box_append_text ( combo, "0.5");
425 gtk_combo_box_append_text ( combo, "1");
426 gtk_combo_box_append_text ( combo, "2");
427 gtk_combo_box_append_text ( combo, "4");
428 gtk_combo_box_append_text ( combo, "8");
429 gtk_combo_box_append_text ( combo, "16");
430 gtk_combo_box_append_text ( combo, "32");
431 gtk_combo_box_append_text ( combo, "64");
432 gtk_combo_box_append_text ( combo, "128");
433 gtk_combo_box_append_text ( combo, "256");
434 gtk_combo_box_append_text ( combo, "512");
435 gtk_combo_box_append_text ( combo, "1024");
436 gtk_combo_box_append_text ( combo, "2048");
437 gtk_combo_box_append_text ( combo, "4096");
438 gtk_combo_box_append_text ( combo, "8192");
439 gtk_combo_box_append_text ( combo, "16384");
440 gtk_combo_box_append_text ( combo, "32768");
442 gtk_widget_set_tooltip_text (GTK_WIDGET (combo), _("Select zoom level"));
446 static gint zoom_popup_handler (GtkWidget *widget)
450 g_return_val_if_fail (widget != NULL, FALSE);
451 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
453 /* The "widget" is the menu that was supplied when
454 * g_signal_connect_swapped() was called.
456 menu = GTK_MENU (widget);
458 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
459 1, gtk_get_current_event_time());
463 static void vik_window_init ( VikWindow *vw )
465 GtkWidget *main_vbox;
468 vw->action_group = NULL;
470 vw->viking_vvp = vik_viewport_new();
471 vw->viking_vlp = vik_layers_panel_new();
472 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
473 vw->viking_vs = vik_statusbar_new();
475 vw->vt = toolbox_create(vw);
476 window_create_ui(vw);
477 window_set_filename (vw, NULL);
478 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
480 // Set the default tool
481 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
485 vw->modified = FALSE;
486 vw->only_updating_coord_mode_ui = FALSE;
488 vw->pan_move = FALSE;
489 vw->pan_x = vw->pan_y = -1;
490 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
491 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
492 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
494 main_vbox = gtk_vbox_new(FALSE, 1);
495 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
497 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
498 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
499 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
500 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
502 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
503 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ();
504 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
505 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
506 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
508 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
510 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
511 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
512 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 );
513 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
514 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
515 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
516 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
517 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
519 // Allow key presses to be processed anywhere
520 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
522 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
524 hpaned = gtk_hpaned_new ();
525 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
526 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
528 /* This packs the button into the window (a gtk container). */
529 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
531 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
533 a_background_add_window ( vw );
535 window_list = g_slist_prepend ( window_list, vw);
539 vw->save_img_dia = NULL;
540 vw->save_img_dir_dia = NULL;
542 // Store the thread value so comparisons can be made to determine the gdk update method
543 // Hopefully we are storing the main thread value here :)
544 // [ATM any window initialization is always be performed by the main thread]
545 vw->thread = g_thread_self();
548 static VikWindow *window_new ()
550 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
554 * Update the displayed map
555 * Only update the top most visible map layer
556 * ATM this assumes (as per defaults) the top most map has full alpha setting
557 * such that other other maps even though they may be active will not be seen
558 * It's more complicated to work out which maps are actually visible due to alpha settings
559 * and overkill for this simple refresh method.
561 static void simple_map_update ( VikWindow *vw, gboolean only_new )
563 // Find the most relevent single map layer to operate on
564 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
566 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
570 * This is the global key press handler
571 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
573 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
575 // The keys handled here are not in the menuing system for a couple of reasons:
576 // . Keeps the menu size compact (alebit at expense of discoverably)
577 // . Allows differing key bindings to perform the same actions
579 // First decide if key events are related to the maps layer
580 gboolean map_download = FALSE;
581 gboolean map_download_only_new = TRUE; // Only new or reload
583 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
585 // Standard 'Refresh' keys: F5 or Ctrl+r
586 // Note 'F5' is actually handled via draw_refresh_cb() later on
587 // (not 'R' it's 'r' notice the case difference!!)
588 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
590 map_download_only_new = TRUE;
592 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
593 // Note the use of uppercase R here since shift key has been pressed
594 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
595 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
597 map_download_only_new = FALSE;
600 if ( map_download ) {
601 simple_map_update ( vw, map_download_only_new );
604 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
605 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
606 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
607 if ( vl && ltype == vl->type )
608 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
611 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
612 if ( vw->current_tool < TOOL_LAYER ) {
613 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
614 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
615 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
619 /* Restore Main Menu via Escape key if the user has hidden it */
620 /* This key is more likely to be used as they may not remember the function key */
621 if ( event->keyval == GDK_Escape ) {
622 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
624 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
626 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
627 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
628 return TRUE; /* handled keypress */
633 return FALSE; /* don't handle the keypress */
636 static gboolean delete_event( VikWindow *vw )
638 #ifdef VIKING_PROMPT_IF_MODIFIED
645 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
646 _("Do you want to save the changes you made to the document \"%s\"?\n"
648 "Your changes will be lost if you don't save them."),
649 window_get_filename ( vw ) ) );
650 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
651 switch ( gtk_dialog_run ( dia ) )
653 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
654 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
655 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
662 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
664 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
667 static void draw_update ( VikWindow *vw )
673 static void draw_sync ( VikWindow *vw )
675 vik_viewport_sync(vw->viking_vvp);
680 * Split the status update, as sometimes only need to update the tool part
681 * also on initialization the zoom related stuff is not ready to be used
683 static void draw_status_tool ( VikWindow *vw )
685 if ( vw->current_tool == TOOL_LAYER )
686 // Use tooltip rather than the internal name as the tooltip is i8n
687 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 );
689 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
692 static void draw_status ( VikWindow *vw )
694 static gchar zoom_level[22];
695 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
696 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
697 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
699 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
701 if ( (int)xmpp - xmpp < 0.0 )
702 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
704 /* xmpp should be a whole number so don't show useless .000 bit */
705 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
707 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
709 draw_status_tool ( vw );
712 void vik_window_set_redraw_trigger(VikLayer *vl)
714 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
719 static void window_configure_event ( VikWindow *vw )
721 static int first = 1;
724 // This is a hack to set the cursor corresponding to the first tool
725 // FIXME find the correct way to initialize both tool and its cursor
726 const GdkCursor *cursor = NULL;
728 cursor = toolbox_get_cursor(vw->vt, "Pan");
729 /* We set cursor, even if it is NULL: it resets to default */
730 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
734 static void draw_redraw ( VikWindow *vw )
736 VikCoord old_center = vw->trigger_center;
737 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
738 VikLayer *new_trigger = vw->trigger;
740 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
743 ; /* do nothing -- have to redraw everything. */
744 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
745 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
747 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
750 vik_viewport_clear ( vw->viking_vvp);
751 vik_layers_panel_draw_all ( vw->viking_vlp );
752 vik_viewport_draw_scale ( vw->viking_vvp );
753 vik_viewport_draw_copyright ( vw->viking_vvp );
754 vik_viewport_draw_centermark ( vw->viking_vvp );
755 vik_viewport_draw_logo ( vw->viking_vvp );
757 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
760 gboolean draw_buf_done = TRUE;
762 static gboolean draw_buf(gpointer data)
764 gpointer *pass_along = data;
766 gdk_draw_drawable (pass_along[0], pass_along[1],
767 pass_along[2], 0, 0, 0, 0, -1, -1);
768 draw_buf_done = TRUE;
774 /* Mouse event handlers ************************************************************************/
776 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
778 /* set panning origin */
779 vw->pan_move = FALSE;
780 vw->pan_x = (gint) event->x;
781 vw->pan_y = (gint) event->y;
784 static void draw_click (VikWindow *vw, GdkEventButton *event)
786 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
788 /* middle button pressed. we reserve all middle button and scroll events
789 * for panning and zooming; tools only get left/right/movement
791 if ( event->button == 2) {
792 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
793 // Tool still may need to do something (such as disable something)
794 toolbox_click(vw->vt, event);
795 vik_window_pan_click ( vw, event );
798 toolbox_click(vw->vt, event);
802 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
804 if ( vw->pan_x != -1 ) {
805 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
806 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
808 vw->pan_x = event->x;
809 vw->pan_y = event->y;
814 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
816 static VikCoord coord;
817 static struct UTM utm;
818 static struct LatLon ll;
819 #define BUFFER_SIZE 50
820 static char pointer_buf[BUFFER_SIZE];
821 gchar *lat = NULL, *lon = NULL;
824 VikDemInterpol interpol_method;
826 /* This is a hack, but work far the best, at least for single pointer systems.
827 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
829 gdk_window_get_pointer (event->window, &x, &y, NULL);
833 toolbox_move(vw->vt, event);
835 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
836 vik_coord_to_utm ( &coord, &utm );
838 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
839 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
840 // ZONE[N|S] EASTING NORTHING
841 lat = g_malloc(4*sizeof(gchar));
842 // NB zone is stored in a char but is an actual number
843 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
844 lon = g_malloc(16*sizeof(gchar));
845 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
848 a_coords_utm_to_latlon ( &utm, &ll );
849 a_coords_latlon_to_string ( &ll, &lat, &lon );
852 /* Change interpolate method according to scale */
853 zoom = vik_viewport_get_zoom(vw->viking_vvp);
855 interpol_method = VIK_DEM_INTERPOL_NONE;
856 else if (zoom >= 1.0)
857 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
859 interpol_method = VIK_DEM_INTERPOL_BEST;
860 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
861 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
862 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
864 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
867 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
872 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
874 vik_window_pan_move ( vw, event );
876 /* This is recommended by the GTK+ documentation, but does not work properly.
877 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
878 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
880 /* gdk_event_request_motions ( event ); */
883 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
885 if ( vw->pan_move == FALSE )
886 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
888 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
889 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
890 vw->pan_move = FALSE;
891 vw->pan_x = vw->pan_y = -1;
895 static void draw_release ( VikWindow *vw, GdkEventButton *event )
897 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
899 if ( event->button == 2 ) { /* move / pan */
900 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
901 // Tool still may need to do something (such as reenable something)
902 toolbox_release(vw->vt, event);
903 vik_window_pan_release ( vw, event );
906 toolbox_release(vw->vt, event);
910 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
912 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
913 if ( modifiers == GDK_CONTROL_MASK ) {
914 /* control == pan up & down */
915 if ( event->direction == GDK_SCROLL_UP )
916 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
918 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 );
919 } else if ( modifiers == GDK_SHIFT_MASK ) {
920 /* shift == pan left & right */
921 if ( event->direction == GDK_SCROLL_UP )
922 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
924 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 );
925 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
926 // This zoom is on the center position
927 if ( event->direction == GDK_SCROLL_UP )
928 vik_viewport_zoom_in (vw->viking_vvp);
930 vik_viewport_zoom_out (vw->viking_vvp);
932 /* make sure mouse is still over the same point on the map when we zoom */
935 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
936 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
937 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
938 if ( event->direction == GDK_SCROLL_UP )
939 vik_viewport_zoom_in (vw->viking_vvp);
941 vik_viewport_zoom_out(vw->viking_vvp);
942 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
943 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
944 center_y + (y - event->y) );
952 /********************************************************************************
954 ********************************************************************************/
955 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
959 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
960 GdkGC *thickgc = gdk_gc_new(d);
962 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
963 gdouble dx = (x2-x1)/len*10;
964 gdouble dy = (y2-y1)/len*10;
965 gdouble c = cos(DEG2RAD(15.0));
966 gdouble s = sin(DEG2RAD(15.0));
968 gdouble baseangle = 0;
971 /* draw line with arrow ends */
973 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
974 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
975 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
978 a_viewport_clip_line(&x1, &y1, &x2, &y2);
979 gdk_draw_line(d, gc, x1, y1, x2, y2);
981 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
982 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
983 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
984 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
985 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
986 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
992 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
996 gdk_gc_copy(thickgc, gc);
997 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
998 gdk_color_parse("#2255cc", &color);
999 gdk_gc_set_rgb_fg_color(thickgc, &color);
1001 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);
1004 gdk_gc_copy(thickgc, gc);
1005 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1006 for (i=0; i<180; i++) {
1007 c = cos(DEG2RAD(i)*2 + baseangle);
1008 s = sin(DEG2RAD(i)*2 + baseangle);
1011 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1013 gdouble ticksize = 2*CW;
1014 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1018 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1019 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1020 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1021 c = (CR+CW*2)*cos(baseangle);
1022 s = (CR+CW*2)*sin(baseangle);
1023 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1024 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1027 #define LABEL(x, y, w, h) { \
1028 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1029 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1030 gdk_draw_layout(d, gc, (x), (y), pl); }
1032 gint wd, hd, xd, yd;
1033 gint wb, hb, xb, yb;
1035 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1036 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1037 pango_layout_set_text(pl, "N", -1);
1038 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1040 /* draw label with distance */
1041 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1042 switch (dist_units) {
1043 case VIK_UNITS_DISTANCE_KILOMETRES:
1044 if (distance >= 1000 && distance < 100000) {
1045 g_sprintf(str, "%3.2f km", distance/1000.0);
1046 } else if (distance < 1000) {
1047 g_sprintf(str, "%d m", (int)distance);
1049 g_sprintf(str, "%d km", (int)distance/1000);
1052 case VIK_UNITS_DISTANCE_MILES:
1053 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1054 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1055 } else if (distance < VIK_MILES_TO_METERS(1)) {
1056 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1058 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1062 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1065 pango_layout_set_text(pl, str, -1);
1067 pango_layout_get_pixel_size ( pl, &wd, &hd );
1069 xd = (x1+x2)/2 + dy;
1070 yd = (y1+y2)/2 - hd/2 - dx;
1072 xd = (x1+x2)/2 - dy;
1073 yd = (y1+y2)/2 - hd/2 + dx;
1076 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1081 LABEL(xd, yd, wd, hd);
1083 /* draw label with bearing */
1084 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1085 pango_layout_set_text(pl, str, -1);
1086 pango_layout_get_pixel_size ( pl, &wb, &hb );
1087 xb = x1 + CR*cos(angle-M_PI_2);
1088 yb = y1 + CR*sin(angle-M_PI_2);
1090 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1096 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1097 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1101 LABEL(xb, yb, wb, hb);
1105 g_object_unref ( G_OBJECT ( pl ) );
1106 g_object_unref ( G_OBJECT ( labgc ) );
1107 g_object_unref ( G_OBJECT ( thickgc ) );
1113 gboolean has_oldcoord;
1115 } ruler_tool_state_t;
1117 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1119 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1122 s->has_oldcoord = FALSE;
1126 static void ruler_destroy (ruler_tool_state_t *s)
1131 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1136 if ( event->button == 1 ) {
1137 gchar *lat=NULL, *lon=NULL;
1138 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1139 vik_coord_to_latlon ( &coord, &ll );
1140 a_coords_latlon_to_string ( &ll, &lat, &lon );
1141 if ( s->has_oldcoord ) {
1142 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1143 switch (dist_units) {
1144 case VIK_UNITS_DISTANCE_KILOMETRES:
1145 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1147 case VIK_UNITS_DISTANCE_MILES:
1148 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1151 temp = g_strdup_printf ("Just to keep the compiler happy");
1152 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1155 s->has_oldcoord = FALSE;
1158 temp = g_strdup_printf ( "%s %s", lat, lon );
1159 s->has_oldcoord = TRUE;
1162 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1165 s->oldcoord = coord;
1168 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1169 draw_update ( s->vw );
1171 return VIK_LAYER_TOOL_ACK;
1174 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1176 VikViewport *vvp = s->vvp;
1177 VikWindow *vw = s->vw;
1183 if ( s->has_oldcoord ) {
1184 int oldx, oldy, w1, h1, w2, h2;
1185 static GdkPixmap *buf = NULL;
1186 gchar *lat=NULL, *lon=NULL;
1187 w1 = vik_viewport_get_width(vvp);
1188 h1 = vik_viewport_get_height(vvp);
1190 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1192 gdk_drawable_get_size(buf, &w2, &h2);
1193 if (w1 != w2 || h1 != h2) {
1194 g_object_unref ( G_OBJECT ( buf ) );
1195 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1198 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1199 vik_coord_to_latlon ( &coord, &ll );
1200 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1202 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1203 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1204 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)) );
1205 if (draw_buf_done) {
1206 static gpointer pass_along[3];
1207 pass_along[0] = GTK_WIDGET(vvp)->window;
1208 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1209 pass_along[2] = buf;
1210 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1211 draw_buf_done = FALSE;
1213 a_coords_latlon_to_string(&ll, &lat, &lon);
1214 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1215 switch (dist_units) {
1216 case VIK_UNITS_DISTANCE_KILOMETRES:
1217 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1219 case VIK_UNITS_DISTANCE_MILES:
1220 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1223 temp = g_strdup_printf ("Just to keep the compiler happy");
1224 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1226 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1229 return VIK_LAYER_TOOL_ACK;
1232 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1234 return VIK_LAYER_TOOL_ACK;
1237 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1239 draw_update ( s->vw );
1242 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1244 if (event->keyval == GDK_Escape) {
1245 s->has_oldcoord = FALSE;
1246 ruler_deactivate ( vl, s );
1249 // Regardless of whether we used it, return false so other GTK things may use it
1253 static VikToolInterface ruler_tool =
1254 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1255 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1256 (VikToolConstructorFunc) ruler_create,
1257 (VikToolDestructorFunc) ruler_destroy,
1258 (VikToolActivationFunc) NULL,
1259 (VikToolActivationFunc) ruler_deactivate,
1260 (VikToolMouseFunc) ruler_click,
1261 (VikToolMouseMoveFunc) ruler_move,
1262 (VikToolMouseFunc) ruler_release,
1263 (VikToolKeyFunc) ruler_key_press,
1265 GDK_CURSOR_IS_PIXMAP,
1266 &cursor_ruler_pixbuf };
1267 /*** end ruler code ********************************************************/
1271 /********************************************************************************
1273 ********************************************************************************/
1278 // Track zoom bounds for zoom tool with shift modifier:
1279 gboolean bounds_active;
1282 } zoom_tool_state_t;
1285 * In case the screen size has changed
1287 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1291 // Allocate a drawing area the size of the viewport
1292 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1293 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1295 if ( !zts->pixmap ) {
1297 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1300 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1302 if ( w1 != w2 || h1 != h2 ) {
1303 // Has changed - delete and recreate with new values
1304 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1305 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1309 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1311 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1316 zts->bounds_active = FALSE;
1320 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1323 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1327 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1329 zts->vw->modified = TRUE;
1330 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1334 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1335 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1337 gboolean skip_update = FALSE;
1339 zts->bounds_active = FALSE;
1341 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1342 // This zoom is on the center position
1343 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1344 if ( event->button == 1 )
1345 vik_viewport_zoom_in (zts->vw->viking_vvp);
1346 else if ( event->button == 3 )
1347 vik_viewport_zoom_out (zts->vw->viking_vvp);
1349 else if ( modifiers == GDK_CONTROL_MASK ) {
1350 // This zoom is to recenter on the mouse position
1351 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1352 if ( event->button == 1 )
1353 vik_viewport_zoom_in (zts->vw->viking_vvp);
1354 else if ( event->button == 3 )
1355 vik_viewport_zoom_out (zts->vw->viking_vvp);
1357 else if ( modifiers == GDK_SHIFT_MASK ) {
1358 // Get start of new zoom bounds
1359 if ( event->button == 1 ) {
1360 zts->bounds_active = TRUE;
1361 zts->start_x = (gint) event->x;
1362 zts->start_y = (gint) event->y;
1367 /* make sure mouse is still over the same point on the map when we zoom */
1368 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1369 if ( event->button == 1 )
1370 vik_viewport_zoom_in (zts->vw->viking_vvp);
1371 else if ( event->button == 3 )
1372 vik_viewport_zoom_out(zts->vw->viking_vvp);
1373 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1374 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1375 center_x + (x - event->x),
1376 center_y + (y - event->y) );
1380 draw_update ( zts->vw );
1382 return VIK_LAYER_TOOL_ACK;
1385 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1387 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1389 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1390 zoomtool_resize_pixmap ( zts );
1392 // Blank out currently drawn area
1393 gdk_draw_drawable ( zts->pixmap,
1394 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1395 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1396 0, 0, 0, 0, -1, -1);
1398 // Calculate new box starting point & size in pixels
1399 int xx, yy, width, height;
1400 if ( event->y > zts->start_y ) {
1402 height = event->y-zts->start_y;
1406 height = zts->start_y-event->y;
1408 if ( event->x > zts->start_x ) {
1410 width = event->x-zts->start_x;
1414 width = zts->start_x-event->x;
1418 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1420 // Only actually draw when there's time to do so
1421 if (draw_buf_done) {
1422 static gpointer pass_along[3];
1423 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1424 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1425 pass_along[2] = zts->pixmap;
1426 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1427 draw_buf_done = FALSE;
1430 return VIK_LAYER_TOOL_ACK;
1433 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1435 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1437 zts->bounds_active = FALSE;
1439 // Ensure haven't just released on the exact same position
1440 // i.e. probably haven't moved the mouse at all
1441 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1443 VikCoord coord1, coord2;
1444 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1445 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1447 // From the extend of the bounds pick the best zoom level
1448 // c.f. trw_layer_zoom_to_show_latlons()
1449 // Maybe refactor...
1450 struct LatLon ll1, ll2;
1451 vik_coord_to_latlon(&coord1, &ll1);
1452 vik_coord_to_latlon(&coord2, &ll2);
1453 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1454 (ll1.lon+ll2.lon)/2 };
1456 VikCoord new_center;
1457 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1458 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1460 /* Convert into definite 'smallest' and 'largest' positions */
1461 struct LatLon minmin;
1462 if ( ll1.lat < ll2.lat )
1463 minmin.lat = ll1.lat;
1465 minmin.lat = ll2.lat;
1467 struct LatLon maxmax;
1468 if ( ll1.lon > ll2.lon )
1469 maxmax.lon = ll1.lon;
1471 maxmax.lon = ll2.lon;
1473 /* Always recalculate the 'best' zoom level */
1474 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1475 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1477 gdouble min_lat, max_lat, min_lon, max_lon;
1478 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1479 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1480 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1481 /* NB I think the logic used in this test to determine if the bounds is within view
1482 fails if track goes across 180 degrees longitude.
1483 Hopefully that situation is not too common...
1484 Mind you viking doesn't really do edge locations to well anyway */
1485 if ( min_lat < minmin.lat &&
1486 max_lat > minmin.lat &&
1487 min_lon < maxmax.lon &&
1488 max_lon > maxmax.lon )
1489 /* Found within zoom level */
1494 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1497 draw_update ( zts->vw );
1499 return VIK_LAYER_TOOL_ACK;
1502 static VikToolInterface zoom_tool =
1503 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1504 (VikToolConstructorFunc) zoomtool_create,
1505 (VikToolDestructorFunc) zoomtool_destroy,
1506 (VikToolActivationFunc) NULL,
1507 (VikToolActivationFunc) NULL,
1508 (VikToolMouseFunc) zoomtool_click,
1509 (VikToolMouseMoveFunc) zoomtool_move,
1510 (VikToolMouseFunc) zoomtool_release,
1513 GDK_CURSOR_IS_PIXMAP,
1514 &cursor_zoom_pixbuf };
1515 /*** end zoom code ********************************************************/
1517 /********************************************************************************
1519 ********************************************************************************/
1520 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1525 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1527 vw->modified = TRUE;
1528 if ( event->button == 1 )
1529 vik_window_pan_click ( vw, event );
1531 return VIK_LAYER_TOOL_ACK;
1534 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1536 vik_window_pan_move ( vw, event );
1537 return VIK_LAYER_TOOL_ACK;
1540 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1542 if ( event->button == 1 )
1543 vik_window_pan_release ( vw, event );
1544 return VIK_LAYER_TOOL_ACK;
1547 static VikToolInterface pan_tool =
1548 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1549 (VikToolConstructorFunc) pantool_create,
1550 (VikToolDestructorFunc) NULL,
1551 (VikToolActivationFunc) NULL,
1552 (VikToolActivationFunc) NULL,
1553 (VikToolMouseFunc) pantool_click,
1554 (VikToolMouseMoveFunc) pantool_move,
1555 (VikToolMouseFunc) pantool_release,
1559 /*** end pan code ********************************************************/
1561 /********************************************************************************
1563 ********************************************************************************/
1564 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1566 tool_ed_t *t = g_new(tool_ed_t, 1);
1570 t->is_waypoint = FALSE;
1574 static void selecttool_destroy (tool_ed_t *t)
1582 GdkEventButton *event;
1583 tool_ed_t *tool_edit;
1586 static void click_layer_selected (VikLayer *vl, clicker *ck)
1588 /* Do nothing when function call returns true; */
1589 /* i.e. stop on first found item */
1592 if ( vik_layer_get_interface(vl->type)->select_click )
1593 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1596 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1598 /* Only allow selection on primary button */
1599 if ( event->button == 1 ) {
1600 /* Enable click to apply callback to potentially all track/waypoint layers */
1601 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1602 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1605 ck.vvp = t->vw->viking_vvp;
1608 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1611 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1614 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1616 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1617 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1618 gint type = vik_treeview_item_get_type ( vtv, &iter );
1619 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1620 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1622 vik_treeview_item_unselect ( vtv, &iter );
1623 if ( vik_window_clear_highlight ( t->vw ) )
1624 draw_update ( t->vw );
1629 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1631 /* Act on currently selected item to show menu */
1632 if ( t->vw->selected_track || t->vw->selected_waypoint )
1633 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1634 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1637 return VIK_LAYER_TOOL_ACK;
1640 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1642 /* Only allow selection on primary button */
1643 if ( event->button == 1 ) {
1644 // Don't care about vl here
1646 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1647 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1649 return VIK_LAYER_TOOL_ACK;
1652 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1654 /* Only allow selection on primary button */
1655 if ( event->button == 1 ) {
1656 // Don't care about vl here
1658 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1659 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1661 return VIK_LAYER_TOOL_ACK;
1664 static VikToolInterface select_tool =
1665 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1666 (VikToolConstructorFunc) selecttool_create,
1667 (VikToolDestructorFunc) selecttool_destroy,
1668 (VikToolActivationFunc) NULL,
1669 (VikToolActivationFunc) NULL,
1670 (VikToolMouseFunc) selecttool_click,
1671 (VikToolMouseMoveFunc) selecttool_move,
1672 (VikToolMouseFunc) selecttool_release,
1673 (VikToolKeyFunc) NULL,
1678 /*** end select tool code ********************************************************/
1680 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1682 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1683 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1684 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1685 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1686 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1687 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1688 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1689 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1694 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1696 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1697 g_assert(check_box);
1698 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1700 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1702 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1705 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1709 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1712 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1715 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1718 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1722 gchar *s = (gchar *)gtk_action_get_name(a);
1728 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1729 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1730 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1731 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1732 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1737 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1739 VikCoord new_center;
1741 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1742 struct LatLon ll, llold;
1743 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1744 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1745 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1749 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1750 struct UTM utm, utmold;
1751 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1752 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1753 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1758 g_critical("Houston, we've had a problem.");
1762 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1767 * Refresh maps displayed
1769 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1771 // Only get 'new' maps
1772 simple_map_update ( vw, TRUE );
1775 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1777 VikLayerTypeEnum type;
1778 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1779 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1780 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1782 vw->modified = TRUE;
1788 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1790 a_clipboard_copy_selected ( vw->viking_vlp );
1793 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1795 vik_layers_panel_cut_selected ( vw->viking_vlp );
1796 vw->modified = TRUE;
1799 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1801 if ( a_clipboard_paste ( vw->viking_vlp ) )
1803 vw->modified = TRUE;
1807 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1809 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1810 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1813 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1816 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1818 #if GTK_CHECK_VERSION (2, 14, 0)
1820 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1821 GError *error = NULL;
1822 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1823 if ( !show && !error )
1824 // No error to show, so unlikely this will get called
1825 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1828 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 );
1829 g_error_free ( error );
1833 a_dialog_error_msg ( GTK_WINDOW(vw), "Help is not available in this build." ); // Unlikely to happen so not going to bother with I8N
1835 #endif /* WINDOWS */
1838 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1840 a_dialog_about(GTK_WINDOW(vw));
1843 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1845 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1847 vik_layers_panel_delete_selected ( vw->viking_vlp );
1848 vw->modified = TRUE;
1851 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1854 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1856 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1857 g_assert(check_box);
1858 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1860 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1862 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1865 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1867 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1870 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1872 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1874 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1877 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1879 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1882 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1884 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1886 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1889 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1891 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1894 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1896 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1898 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1901 /***************************************
1902 ** tool management routines
1904 ***************************************/
1906 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1908 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1911 vt->active_tool = -1;
1916 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1918 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1919 vt->tools[vt->n_tools].ti = *vti;
1920 vt->tools[vt->n_tools].layer_type = layer_type;
1922 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1925 vt->tools[vt->n_tools].state = NULL;
1930 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1933 for (i=0; i<vt->n_tools; i++) {
1934 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1941 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1943 int tool = toolbox_get_tool(vt, tool_name);
1944 toolbox_tool_t *t = &vt->tools[tool];
1945 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1947 if (tool == vt->n_tools) {
1948 g_critical("trying to activate a non-existent tool...");
1951 /* is the tool already active? */
1952 if (vt->active_tool == tool) {
1956 if (vt->active_tool != -1) {
1957 if (vt->tools[vt->active_tool].ti.deactivate) {
1958 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1961 if (t->ti.activate) {
1962 t->ti.activate(vl, t->state);
1964 vt->active_tool = tool;
1967 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1969 int tool = toolbox_get_tool(vt, tool_name);
1970 toolbox_tool_t *t = &vt->tools[tool];
1971 if (t->ti.cursor == NULL) {
1972 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1973 GError *cursor_load_err = NULL;
1974 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1975 /* TODO: settable offeset */
1976 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1977 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1979 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1982 return t->ti.cursor;
1985 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1987 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1988 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1989 gint ltype = vt->tools[vt->active_tool].layer_type;
1990 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1991 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
1995 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
1997 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1998 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
1999 gint ltype = vt->tools[vt->active_tool].layer_type;
2000 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2001 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2002 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2006 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2008 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2009 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2010 gint ltype = vt->tools[vt->active_tool].layer_type;
2011 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2012 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2015 /** End tool management ************************************/
2017 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2019 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2022 /* this function gets called whenever a toolbar tool is clicked */
2023 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2025 /* White Magic, my friends ... White Magic... */
2027 const GdkCursor *cursor = NULL;
2029 toolbox_activate(vw->vt, gtk_action_get_name(a));
2031 cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2033 if ( GTK_WIDGET(vw->viking_vvp)->window )
2034 /* We set cursor, even if it is NULL: it resets to default */
2035 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
2037 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2038 vw->current_tool = TOOL_PAN;
2040 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2041 vw->current_tool = TOOL_ZOOM;
2043 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2044 vw->current_tool = TOOL_RULER;
2046 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2047 vw->current_tool = TOOL_SELECT;
2050 /* TODO: only enable tools from active layer */
2051 VikLayerTypeEnum layer_id;
2052 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2053 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2054 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2055 vw->current_tool = TOOL_LAYER;
2056 vw->tool_layer_id = layer_id;
2057 vw->tool_tool_id = tool_id;
2062 draw_status_tool ( vw );
2065 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2070 g_free ( vw->filename );
2071 if ( filename == NULL )
2073 vw->filename = NULL;
2077 vw->filename = g_strdup(filename);
2080 /* Refresh window's title */
2081 file = window_get_filename ( vw );
2082 title = g_strdup_printf( "%s - Viking", file );
2083 gtk_window_set_title ( GTK_WINDOW(vw), title );
2087 static const gchar *window_get_filename ( VikWindow *vw )
2089 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2092 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2094 GtkWidget *mode_button;
2097 #ifdef VIK_CONFIG_EXPEDIA
2098 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2100 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2101 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2102 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2104 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2105 g_assert ( mode_button );
2110 * vik_window_get_pan_move:
2111 * @vw: some VikWindow
2113 * Retrieves @vw's pan_move.
2115 * Should be removed as soon as possible.
2117 * Returns: @vw's pan_move
2121 gboolean vik_window_get_pan_move ( VikWindow *vw )
2123 return vw->pan_move;
2126 static void on_activate_recent_item (GtkRecentChooser *chooser,
2131 filename = gtk_recent_chooser_get_current_uri (chooser);
2132 if (filename != NULL)
2134 GFile *file = g_file_new_for_uri ( filename );
2135 gchar *path = g_file_get_path ( file );
2136 g_object_unref ( file );
2137 if ( self->filename )
2139 GSList *filenames = NULL;
2140 filenames = g_slist_append ( filenames, path );
2141 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2142 // NB: GSList & contents are freed by main.open_window
2145 vik_window_open_file ( self, path, TRUE );
2153 static void setup_recent_files (VikWindow *self)
2155 GtkRecentManager *manager;
2156 GtkRecentFilter *filter;
2157 GtkWidget *menu, *menu_item;
2159 filter = gtk_recent_filter_new ();
2160 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2161 gtk_recent_filter_add_group(filter, "viking");
2163 manager = gtk_recent_manager_get_default ();
2164 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2165 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2166 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2168 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2169 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2171 g_signal_connect (G_OBJECT (menu), "item-activated",
2172 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2175 static void update_recently_used_document(const gchar *filename)
2177 /* Update Recently Used Document framework */
2178 GtkRecentManager *manager = gtk_recent_manager_get_default();
2179 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2180 gchar *groups[] = {"viking", NULL};
2181 GFile *file = g_file_new_for_commandline_arg(filename);
2182 gchar *uri = g_file_get_uri(file);
2183 gchar *basename = g_path_get_basename(filename);
2184 g_object_unref(file);
2187 recent_data->display_name = basename;
2188 recent_data->description = NULL;
2189 recent_data->mime_type = "text/x-gps-data";
2190 recent_data->app_name = (gchar *) g_get_application_name ();
2191 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2192 recent_data->groups = groups;
2193 recent_data->is_private = FALSE;
2194 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2196 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2201 g_free (recent_data->app_exec);
2202 g_slice_free (GtkRecentData, recent_data);
2205 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2207 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2209 case LOAD_TYPE_READ_FAILURE:
2210 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2212 case LOAD_TYPE_GPSBABEL_FAILURE:
2213 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2215 case LOAD_TYPE_GPX_FAILURE:
2216 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2218 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2219 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2221 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2223 // Since we can process .vik files with issues just show a warning in the status bar
2224 // Not that a user can do much about it... or tells them what this issue is yet...
2225 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2226 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2229 // No break, carry on to show any data
2230 case LOAD_TYPE_VIK_SUCCESS:
2232 GtkWidget *mode_button;
2234 if ( change_filename )
2235 window_set_filename ( vw, filename );
2236 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2237 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. */
2238 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2239 vw->only_updating_coord_mode_ui = FALSE;
2241 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2243 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2244 g_assert ( mode_button );
2245 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2247 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2248 g_assert ( mode_button );
2249 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2251 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2252 g_assert ( mode_button );
2253 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2255 //case LOAD_TYPE_OTHER_SUCCESS:
2257 update_recently_used_document(filename);
2262 static void load_file ( GtkAction *a, VikWindow *vw )
2264 GSList *files = NULL;
2265 GSList *cur_file = NULL;
2267 if (!strcmp(gtk_action_get_name(a), "Open")) {
2270 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2274 g_critical("Houston, we've had a problem.");
2278 if ( ! vw->open_dia )
2280 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2282 GTK_FILE_CHOOSER_ACTION_OPEN,
2283 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2284 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2286 GtkFileFilter *filter;
2287 // NB file filters are listed this way for alphabetical ordering
2288 #ifdef VIK_CONFIG_GEOCACHES
2289 filter = gtk_file_filter_new ();
2290 gtk_file_filter_set_name( filter, _("Geocaching") );
2291 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2292 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2295 filter = gtk_file_filter_new ();
2296 gtk_file_filter_set_name( filter, _("Google Earth") );
2297 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2298 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2300 filter = gtk_file_filter_new ();
2301 gtk_file_filter_set_name( filter, _("GPX") );
2302 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2303 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2305 filter = gtk_file_filter_new ();
2306 gtk_file_filter_set_name( filter, _("Viking") );
2307 gtk_file_filter_add_pattern ( filter, "*.vik" );
2308 gtk_file_filter_add_pattern ( filter, "*.viking" );
2309 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2311 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2312 // However assume this are barely used and thus not worthy of inclusion
2313 // as they'll just make the options too many and have no clear file pattern
2314 // one can always use the all option
2315 filter = gtk_file_filter_new ();
2316 gtk_file_filter_set_name( filter, _("All") );
2317 gtk_file_filter_add_pattern ( filter, "*" );
2318 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2319 // Default to any file - same as before open filters were added
2320 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2322 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2323 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2324 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2326 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2328 gtk_widget_hide ( vw->open_dia );
2329 #ifdef VIKING_PROMPT_IF_MODIFIED
2330 if ( (vw->modified || vw->filename) && newwindow )
2332 if ( vw->filename && newwindow )
2334 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2336 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2337 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2338 gboolean first_vik_file = TRUE;
2340 while ( cur_file ) {
2342 gchar *file_name = cur_file->data;
2343 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2344 // Load first of many .vik files in current window
2345 if ( first_vik_file ) {
2346 vik_window_open_file ( vw, file_name, TRUE );
2347 first_vik_file = FALSE;
2350 // Load each subsequent .vik file in a separate window
2351 VikWindow *newvw = vik_window_new_window ();
2353 vik_window_open_file ( newvw, file_name, TRUE );
2358 vik_window_open_file ( vw, file_name, change_fn );
2361 cur_file = g_slist_next (cur_file);
2363 g_slist_free (files);
2367 gtk_widget_hide ( vw->open_dia );
2370 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2372 gboolean rv = FALSE;
2374 if ( ! vw->save_dia )
2376 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2378 GTK_FILE_CHOOSER_ACTION_SAVE,
2379 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2380 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2382 GtkFileFilter *filter;
2383 filter = gtk_file_filter_new ();
2384 gtk_file_filter_set_name( filter, _("All") );
2385 gtk_file_filter_add_pattern ( filter, "*" );
2386 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2388 filter = gtk_file_filter_new ();
2389 gtk_file_filter_set_name( filter, _("Viking") );
2390 gtk_file_filter_add_pattern ( filter, "*.vik" );
2391 gtk_file_filter_add_pattern ( filter, "*.viking" );
2392 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2393 // Default to a Viking file
2394 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2396 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2397 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2399 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2400 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2401 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2402 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2404 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2406 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2408 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2409 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 ) ) )
2411 window_set_filename ( vw, fn );
2412 rv = window_save ( vw );
2413 vw->modified = FALSE;
2417 g_free ( auto_save_name );
2418 gtk_widget_hide ( vw->save_dia );
2422 static gboolean window_save ( VikWindow *vw )
2424 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2426 update_recently_used_document ( vw->filename );
2431 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2436 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2438 if ( ! vw->filename )
2439 return save_file_as ( NULL, vw );
2442 vw->modified = FALSE;
2443 return window_save ( vw );
2447 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2449 // Via the file menu, acquiring from a GPS makes a new layer
2450 // this has always been the way (not entirely sure if this was the real intention!)
2451 // thus maintain the behaviour ATM.
2452 // Hence explicit setting here (as the value may be changed elsewhere)
2453 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2454 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2457 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2459 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2462 #ifdef VIK_CONFIG_GOOGLE
2463 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2465 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2469 #ifdef VIK_CONFIG_OPENSTREETMAP
2470 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2472 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2475 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
2477 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface );
2481 #ifdef VIK_CONFIG_GEOCACHES
2482 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2484 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2488 #ifdef VIK_CONFIG_GEOTAG
2489 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2491 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2492 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2496 #ifdef VIK_CONFIG_GEONAMES
2497 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
2499 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface );
2503 static void goto_default_location( GtkAction *a, VikWindow *vw)
2506 ll.lat = a_vik_get_default_lat();
2507 ll.lon = a_vik_get_default_long();
2508 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2509 vik_layers_panel_emit_update(vw->viking_vlp);
2513 static void goto_address( GtkAction *a, VikWindow *vw)
2515 a_vik_goto ( vw, vw->viking_vvp );
2516 vik_layers_panel_emit_update ( vw->viking_vlp );
2519 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2524 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
2526 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
2529 return; // Internally broken :(
2531 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
2532 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
2533 // NB no update needed
2535 g_strfreev ( texts );
2538 static void preferences_change_update ( VikWindow *vw, gpointer data )
2540 // Want to update all TrackWaypoint layers
2541 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2543 GList *iter = g_list_first ( layers );
2545 // Reset the individual waypoints themselves due to the preferences change
2546 VikTrwLayer *vtl = VIK_TRW_LAYER(VIK_LAYER(layers->data));
2547 vik_trw_layer_reset_waypoints ( vtl );
2548 iter = g_list_next ( iter );
2551 g_list_free ( layers );
2556 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2558 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2560 a_preferences_show_window ( GTK_WINDOW(vw) );
2562 // Has the waypoint size setting changed?
2563 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
2564 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2565 clear_garmin_icon_syms ();
2567 // Update all windows
2568 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
2572 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2574 /* Simplistic repeat of preference setting
2575 Only the name & type are important for setting the preference via this 'external' way */
2576 VikLayerParam pref_lat[] = {
2577 { VIK_LAYER_NUM_TYPES,
2578 VIKING_PREFERENCES_NAMESPACE "default_latitude",
2579 VIK_LAYER_PARAM_DOUBLE,
2582 VIK_LAYER_WIDGET_SPINBUTTON,
2587 VikLayerParam pref_lon[] = {
2588 { VIK_LAYER_NUM_TYPES,
2589 VIKING_PREFERENCES_NAMESPACE "default_longitude",
2590 VIK_LAYER_PARAM_DOUBLE,
2593 VIK_LAYER_WIDGET_SPINBUTTON,
2599 /* Get current center */
2601 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2603 /* Apply to preferences */
2604 VikLayerParamData vlp_data;
2605 vlp_data.d = ll.lat;
2606 a_preferences_run_setparam (vlp_data, pref_lat);
2607 vlp_data.d = ll.lon;
2608 a_preferences_run_setparam (vlp_data, pref_lon);
2609 /* Remember to save */
2610 a_preferences_save_to_file();
2613 static void clear_cb ( GtkAction *a, VikWindow *vw )
2615 vik_layers_panel_clear ( vw->viking_vlp );
2616 window_set_filename ( vw, NULL );
2620 static void window_close ( GtkAction *a, VikWindow *vw )
2622 if ( ! delete_event ( vw ) )
2623 gtk_widget_destroy ( GTK_WIDGET(vw) );
2626 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2628 if (save_file( NULL, vw)) {
2629 window_close( NULL, vw);
2636 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2638 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2639 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2641 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2642 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2647 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2649 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2650 GdkPixbuf *pixbuf_to_save;
2651 gdouble old_xmpp, old_ympp;
2652 GError *error = NULL;
2654 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2655 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2658 _("Generating image file...") );
2660 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2661 // Ensure dialog shown
2662 gtk_widget_show_all ( msgbox );
2664 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2665 while ( gtk_events_pending() )
2666 gtk_main_iteration ();
2667 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2668 // At least the empty box can give a clue something's going on + the statusbar msg...
2669 // Windows version under Wine OK!
2671 /* backup old zoom & set new */
2672 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2673 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2674 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2676 /* reset width and height: */
2677 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2679 /* draw all layers */
2682 /* save buffer as file. */
2683 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);
2684 if ( !pixbuf_to_save ) {
2685 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2686 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
2690 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2693 g_warning("Unable to write to file %s: %s", fn, error->message );
2694 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
2695 g_error_free (error);
2699 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2701 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2704 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2705 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2706 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2708 /* pretend like nothing happened ;) */
2709 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2710 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2711 vik_viewport_configure ( vw->viking_vvp );
2715 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 )
2717 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2718 gchar *name_of_file = g_malloc ( size );
2720 struct UTM utm_orig, utm;
2722 /* *** copied from above *** */
2723 GdkPixbuf *pixbuf_to_save;
2724 gdouble old_xmpp, old_ympp;
2725 GError *error = NULL;
2727 /* backup old zoom & set new */
2728 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2729 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2730 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2732 /* reset width and height: do this only once for all images (same size) */
2733 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2734 /* *** end copy from above *** */
2736 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2740 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2742 for ( y = 1; y <= tiles_h; y++ )
2744 for ( x = 1; x <= tiles_w; x++ )
2746 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2748 if ( tiles_w & 0x1 )
2749 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2751 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2752 if ( tiles_h & 0x1 ) /* odd */
2753 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2755 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2757 /* move to correct place. */
2758 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2762 /* save buffer as file. */
2763 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);
2764 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2767 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2768 g_error_free (error);
2771 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2775 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2776 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2777 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2778 vik_viewport_configure ( vw->viking_vvp );
2781 g_free ( name_of_file );
2784 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2786 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2787 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2789 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2790 gdouble zoom = pow (2, active-2 );
2792 gdouble width_min, width_max, height_min, height_max;
2795 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2796 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2798 /* TODO: support for xzoom and yzoom values */
2799 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2800 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2802 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2803 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2805 gtk_spin_button_set_value ( width_spin, width );
2806 gtk_spin_button_set_value ( height_spin, height );
2809 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2811 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2813 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2814 gdouble zoom = pow (2, active-2 );
2818 w = gtk_spin_button_get_value(width_spin) * zoom;
2819 h = gtk_spin_button_get_value(height_spin) * zoom;
2820 if (pass_along[4]) /* save many images; find TOTAL area covered */
2822 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2823 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2825 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2826 switch (dist_units) {
2827 case VIK_UNITS_DISTANCE_KILOMETRES:
2828 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2830 case VIK_UNITS_DISTANCE_MILES:
2831 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2834 label_text = g_strdup_printf ("Just to keep the compiler happy");
2835 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2838 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2839 g_free ( label_text );
2843 * Get an allocated filename (or directory as specified)
2845 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
2848 if ( one_image_only )
2851 if (!vw->save_img_dia) {
2852 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2854 GTK_FILE_CHOOSER_ACTION_SAVE,
2855 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2856 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2859 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
2861 GtkFileFilter *filter;
2862 filter = gtk_file_filter_new ();
2863 gtk_file_filter_set_name ( filter, _("All") );
2864 gtk_file_filter_add_pattern ( filter, "*" );
2865 gtk_file_chooser_add_filter ( chooser, filter );
2867 filter = gtk_file_filter_new ();
2868 gtk_file_filter_set_name ( filter, _("JPG") );
2869 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2870 gtk_file_chooser_add_filter ( chooser, filter );
2872 filter = gtk_file_filter_new ();
2873 gtk_file_filter_set_name ( filter, _("PNG") );
2874 gtk_file_filter_add_mime_type ( filter, "image/png");
2875 gtk_file_chooser_add_filter ( chooser, filter );
2878 gtk_file_chooser_set_filter ( chooser, filter );
2880 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2881 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2884 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
2885 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2886 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
2887 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 ) ) )
2890 gtk_widget_hide ( vw->save_img_dia );
2894 // For some reason this method is only written to work in UTM...
2895 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2896 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2900 if (!vw->save_img_dir_dia) {
2901 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2903 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2904 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2905 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2907 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2908 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2911 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
2912 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2914 gtk_widget_hide ( vw->save_img_dir_dia );
2919 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
2921 /* todo: default for answers inside VikWindow or static (thruout instance) */
2922 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2923 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2925 GTK_RESPONSE_REJECT,
2927 GTK_RESPONSE_ACCEPT,
2929 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2930 GtkWidget *png_radio, *jpeg_radio;
2931 GtkWidget *current_window_button;
2932 gpointer current_window_pass_along[7];
2933 GtkWidget *zoom_label, *zoom_combo;
2934 GtkWidget *total_size_label;
2936 /* only used if (!one_image_only) */
2937 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2939 width_label = gtk_label_new ( _("Width (pixels):") );
2940 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
2941 height_label = gtk_label_new ( _("Height (pixels):") );
2942 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
2944 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
2946 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2947 /* TODO: separate xzoom and yzoom factors */
2948 zoom_combo = create_zoom_combo_all_levels();
2950 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
2951 gint active = 2 + round ( log (mpp) / log (2) );
2953 // Can we not hard code size here?
2956 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
2958 total_size_label = gtk_label_new ( NULL );
2960 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2961 current_window_pass_along [0] = vw;
2962 current_window_pass_along [1] = width_spin;
2963 current_window_pass_along [2] = height_spin;
2964 current_window_pass_along [3] = zoom_combo;
2965 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
2966 current_window_pass_along [5] = NULL;
2967 current_window_pass_along [6] = total_size_label;
2968 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
2970 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
2971 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
2973 if ( ! vw->draw_image_save_as_png )
2974 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
2976 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
2977 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
2978 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
2979 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
2981 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), win_warning_label, FALSE, FALSE, 0);
2983 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
2984 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
2985 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
2986 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
2987 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_combo, FALSE, FALSE, 0);
2989 if ( ! one_image_only )
2991 GtkWidget *tiles_width_label, *tiles_height_label;
2993 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
2994 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2995 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
2996 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2997 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
2998 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
2999 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
3000 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
3002 current_window_pass_along [4] = tiles_width_spin;
3003 current_window_pass_along [5] = tiles_height_spin;
3004 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3005 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3007 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
3008 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3009 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3010 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3012 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3014 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3016 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
3018 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3020 gtk_widget_hide ( GTK_WIDGET(dialog) );
3022 gchar *fn = draw_image_filename ( vw, one_image_only );
3026 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3027 gdouble zoom = pow (2, active-2 );
3029 if ( one_image_only )
3030 save_image_file ( vw, fn,
3031 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3032 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3034 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3036 // NB is in UTM mode ATM
3037 save_image_dir ( vw, fn,
3038 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3039 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3041 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3042 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3043 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3048 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3052 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3054 draw_to_image_file ( vw, TRUE );
3057 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3059 draw_to_image_file ( vw, FALSE );
3062 #if GTK_CHECK_VERSION(2,10,0)
3063 static void print_cb ( GtkAction *a, VikWindow *vw )
3065 a_print(vw, vw->viking_vvp);
3069 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3070 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3072 VikViewportDrawMode drawmode;
3073 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3074 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3076 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3077 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3079 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3080 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3082 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3083 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3086 g_critical("Houston, we've had a problem.");
3090 if ( !vw->only_updating_coord_mode_ui )
3092 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3093 if ( olddrawmode != drawmode )
3095 /* this takes care of coord mode too */
3096 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3097 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3098 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3099 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3100 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3107 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3109 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3110 g_assert(check_box);
3111 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3112 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3116 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3118 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3119 g_assert(check_box);
3120 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3121 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3125 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3127 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3128 g_assert(check_box);
3129 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3130 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3134 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3136 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3137 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3138 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3139 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3140 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3142 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3143 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3147 gtk_widget_destroy ( colorsd );
3150 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3152 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3153 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3154 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3155 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3156 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3158 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3159 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3163 gtk_widget_destroy ( colorsd );
3168 /***********************************************************************************************
3170 ***********************************************************************************************/
3172 static GtkActionEntry entries[] = {
3173 { "File", NULL, N_("_File"), 0, 0, 0 },
3174 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3175 { "View", NULL, N_("_View"), 0, 0, 0 },
3176 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3177 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3178 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3179 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3180 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3181 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3182 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3184 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3185 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3186 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3187 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3188 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3189 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3190 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3191 #ifdef VIK_CONFIG_GOOGLE
3192 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
3194 #ifdef VIK_CONFIG_OPENSTREETMAP
3195 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3196 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3198 #ifdef VIK_CONFIG_GEOCACHES
3199 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3201 #ifdef VIK_CONFIG_GEOTAG
3202 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3204 #ifdef VIK_CONFIG_GEONAMES
3205 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3207 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3208 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3209 { "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 },
3210 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3212 #if GTK_CHECK_VERSION(2,10,0)
3213 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3216 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3217 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3219 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3220 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3221 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3222 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3223 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3224 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3225 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3226 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3227 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3228 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3229 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3230 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3231 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3232 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3233 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3235 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3236 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3237 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3238 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3239 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3240 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3241 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3242 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3243 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3244 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3246 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3247 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3251 /* FIXME use VIEWPORT_DRAWMODE values */
3252 static GtkRadioActionEntry mode_entries[] = {
3253 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3254 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3255 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3256 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3259 static GtkToggleActionEntry toggle_entries[] = {
3260 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3261 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3262 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3263 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3264 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3265 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3266 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3267 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3270 #include "menu.xml.h"
3271 static void window_create_ui( VikWindow *window )
3274 GtkActionGroup *action_group;
3275 GtkAccelGroup *accel_group;
3278 GtkIconFactory *icon_factory;
3279 GtkIconSet *icon_set;
3280 GtkRadioActionEntry *tools = NULL, *radio;
3283 uim = gtk_ui_manager_new ();
3286 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3287 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3288 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3289 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3292 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3293 g_error_free (error);
3297 action_group = gtk_action_group_new ("MenuActions");
3298 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3299 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3300 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3301 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3303 icon_factory = gtk_icon_factory_new ();
3304 gtk_icon_factory_add_default (icon_factory);
3306 register_vik_icons(icon_factory);
3308 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3309 // so that it can be applied to the UI in one action group add function call below
3311 for (i=0; i<window->vt->n_tools; i++) {
3312 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3313 radio = &tools[ntools];
3315 *radio = window->vt->tools[i].ti.radioActionEntry;
3316 radio->value = ntools;
3319 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3320 GtkActionEntry action;
3321 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3322 vik_layer_get_interface(i)->name,
3323 vik_layer_get_interface(i)->name,
3324 GTK_UI_MANAGER_MENUITEM, FALSE);
3326 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3327 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3328 gtk_icon_set_unref (icon_set);
3330 action.name = vik_layer_get_interface(i)->name;
3331 action.stock_id = vik_layer_get_interface(i)->name;
3332 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3333 action.accelerator = vik_layer_get_interface(i)->accelerator;
3334 action.tooltip = NULL;
3335 action.callback = (GCallback)menu_addlayer_cb;
3336 gtk_action_group_add_actions(action_group, &action, 1, window);
3338 if ( vik_layer_get_interface(i)->tools_count ) {
3339 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3340 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3343 // Further tool copying for to apply to the UI, also apply menu UI setup
3344 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3345 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3346 radio = &tools[ntools];
3349 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3350 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3351 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3352 GTK_UI_MANAGER_MENUITEM, FALSE);
3353 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3354 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3355 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3356 GTK_UI_MANAGER_TOOLITEM, FALSE);
3358 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3360 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3361 // Overwrite with actual number to use
3362 radio->value = ntools;
3365 GtkActionEntry action_dl;
3366 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
3367 vik_layer_get_interface(i)->name,
3368 g_strdup_printf("Layer%s", vik_layer_get_interface(i)->fixed_layer_name),
3369 GTK_UI_MANAGER_MENUITEM, FALSE);
3371 // For default layers use action names of the form 'Layer<LayerName>'
3372 // This is to avoid clashing with just the layer name used above for the tool actions
3373 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
3374 action_dl.stock_id = NULL;
3375 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
3376 action_dl.accelerator = NULL;
3377 action_dl.tooltip = NULL;
3378 action_dl.callback = (GCallback)layer_defaults_cb;
3379 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
3381 g_object_unref (icon_factory);
3383 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3386 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3388 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3389 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3390 GtkAction *action = gtk_action_group_get_action(action_group,
3391 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3392 g_object_set(action, "sensitive", FALSE, NULL);
3396 // This is done last so we don't need to track the value of mid anymore
3397 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
3399 window->action_group = action_group;
3401 accel_group = gtk_ui_manager_get_accel_group (uim);
3402 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3403 gtk_ui_manager_ensure_update (uim);
3405 setup_recent_files(window);
3409 // TODO - add method to add tool icons defined from outside this file
3410 // and remove the reverse dependency on icon definition from this file
3412 const GdkPixdata *data;
3415 { &mover_22_pixbuf, "vik-icon-pan" },
3416 { &zoom_18_pixbuf, "vik-icon-zoom" },
3417 { &ruler_18_pixbuf, "vik-icon-ruler" },
3418 { &select_18_pixbuf, "vik-icon-select" },
3419 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3420 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3421 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3422 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3423 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3424 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3425 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3426 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3427 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3428 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3429 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3432 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3435 register_vik_icons (GtkIconFactory *icon_factory)
3437 GtkIconSet *icon_set;
3440 for (i = 0; i < n_stock_icons; i++) {
3441 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3442 stock_icons[i].data, FALSE, NULL ));
3443 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3444 gtk_icon_set_unref (icon_set);
3448 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3450 return vw->selected_vtl;
3453 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3455 vw->selected_vtl = vtl;
3456 vw->containing_vtl = vtl;
3458 vw->selected_track = NULL;
3459 vw->selected_tracks = NULL;
3460 vw->selected_waypoint = NULL;
3461 vw->selected_waypoints = NULL;
3462 // Set highlight thickness
3463 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3466 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
3468 return vw->selected_tracks;
3471 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3473 vw->selected_tracks = ght;
3474 vw->containing_vtl = vtl;
3476 vw->selected_vtl = NULL;
3477 vw->selected_track = NULL;
3478 vw->selected_waypoint = NULL;
3479 vw->selected_waypoints = NULL;
3480 // Set highlight thickness
3481 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3484 gpointer vik_window_get_selected_track ( VikWindow *vw )
3486 return vw->selected_track;
3489 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
3491 vw->selected_track = vt;
3492 vw->containing_vtl = vtl;
3494 vw->selected_vtl = NULL;
3495 vw->selected_tracks = NULL;
3496 vw->selected_waypoint = NULL;
3497 vw->selected_waypoints = NULL;
3498 // Set highlight thickness
3499 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3502 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
3504 return vw->selected_waypoints;
3507 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3509 vw->selected_waypoints = ght;
3510 vw->containing_vtl = vtl;
3512 vw->selected_vtl = NULL;
3513 vw->selected_track = NULL;
3514 vw->selected_tracks = NULL;
3515 vw->selected_waypoint = NULL;
3518 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3520 return vw->selected_waypoint;
3523 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
3525 vw->selected_waypoint = vwp;
3526 vw->containing_vtl = vtl;
3528 vw->selected_vtl = NULL;
3529 vw->selected_track = NULL;
3530 vw->selected_tracks = NULL;
3531 vw->selected_waypoints = NULL;
3534 gboolean vik_window_clear_highlight ( VikWindow *vw )
3536 gboolean need_redraw = FALSE;
3537 if ( vw->selected_vtl != NULL ) {
3538 vw->selected_vtl = NULL;
3541 if ( vw->selected_track != NULL ) {
3542 vw->selected_track = NULL;
3545 if ( vw->selected_tracks != NULL ) {
3546 vw->selected_tracks = NULL;
3549 if ( vw->selected_waypoint != NULL ) {
3550 vw->selected_waypoint = NULL;
3553 if ( vw->selected_waypoints != NULL ) {
3554 vw->selected_waypoints = NULL;
3560 GThread *vik_window_get_thread ( VikWindow *vw )