2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2006, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2012, Rob Norris <rw_norris@hotmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "background.h"
31 #include "datasources.h"
36 #include "preferences.h"
37 #include "icons/icons.h"
38 #include "vikexttools.h"
39 #include "garminsymbols.h"
40 #include "vikmapslayer.h"
41 #include "geonamessearch.h"
54 #include <glib/gstdio.h>
55 #include <glib/gprintf.h>
56 #include <glib/gi18n.h>
58 #include <gdk/gdkkeysyms.h>
60 // This seems rather arbitary, quite large and pointless
61 // I mean, if you have a thousand windows open;
62 // why not be allowed to open a thousand more...
63 #define MAX_WINDOWS 1024
64 static guint window_count = 0;
66 #define VIKING_WINDOW_WIDTH 1000
67 #define VIKING_WINDOW_HEIGHT 800
68 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
69 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
70 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
72 static void window_finalize ( GObject *gob );
73 static GObjectClass *parent_class;
75 static void window_set_filename ( VikWindow *vw, const gchar *filename );
76 static const gchar *window_get_filename ( VikWindow *vw );
78 static VikWindow *window_new ();
80 static void draw_update ( VikWindow *vw );
82 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
85 static void open_window ( VikWindow *vw, GSList *files );
86 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type );
87 static void destroy_window ( GtkWidget *widget,
92 static gboolean delete_event( VikWindow *vw );
94 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
96 static void window_configure_event ( VikWindow *vw );
97 static void draw_sync ( VikWindow *vw );
98 static void draw_redraw ( VikWindow *vw );
99 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
100 static void draw_click ( VikWindow *vw, GdkEventButton *event );
101 static void draw_release ( VikWindow *vw, GdkEventButton *event );
102 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
103 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
104 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
105 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
107 static void draw_status ( VikWindow *vw );
109 /* End Drawing Functions */
111 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
112 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
113 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
115 /* tool management */
121 #define TOOL_LAYER_TYPE_NONE -1
126 toolbox_tool_t *tools;
130 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
131 static toolbox_tools_t* toolbox_create(VikWindow *vw);
132 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
133 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
134 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
135 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
136 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
137 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
138 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
142 static void window_create_ui( VikWindow *window );
143 static void register_vik_icons (GtkIconFactory *icon_factory);
146 static void load_file ( GtkAction *a, VikWindow *vw );
147 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
148 static gboolean save_file ( GtkAction *a, VikWindow *vw );
149 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
150 static gboolean window_save ( VikWindow *vw );
154 VikViewport *viking_vvp;
155 VikLayersPanel *viking_vlp;
156 VikStatusbar *viking_vs;
159 GtkComboBox *tb_zoom_combo;
161 GtkItemFactory *item_factory;
163 /* tool management state */
166 guint16 tool_layer_id;
167 guint16 tool_tool_id;
169 GtkActionGroup *action_group;
174 guint draw_image_width, draw_image_height;
175 gboolean draw_image_save_as_png;
180 GtkWidget *open_dia, *save_dia;
181 GtkWidget *save_img_dia, *save_img_dir_dia;
183 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
187 /* half-drawn update */
189 VikCoord trigger_center;
191 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
192 /* Only one of these items can be selected at the same time */
193 gpointer selected_vtl; /* notionally VikTrwLayer */
194 GHashTable *selected_tracks;
195 gpointer selected_track; /* notionally VikTrack */
196 GHashTable *selected_waypoints;
197 gpointer selected_waypoint; /* notionally VikWaypoint */
198 /* only use for individual track or waypoint */
199 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
200 gpointer containing_vtl; /* notionally VikTrwLayer */
214 VW_OPENWINDOW_SIGNAL,
215 VW_STATUSBAR_UPDATE_SIGNAL,
219 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
221 // TODO get rid of this as this is unnecessary duplication...
222 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
224 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
226 VikViewport * vik_window_viewport(VikWindow *vw)
228 return(vw->viking_vvp);
231 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
233 return(vw->viking_vlp);
237 * Returns the statusbar for the window
239 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
241 return vw->viking_vs;
245 * For signalling the update from a background thread
247 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
249 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message, vs_type );
253 * For the actual statusbar update!
255 static gboolean statusbar_idle_update ( gpointer indata )
257 gpointer *data = indata;
258 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
263 * Update statusbar in the main thread
265 static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
267 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
268 static gpointer data[3];
269 data[0] = vw->viking_vs;
270 data[1] = (gchar*) message;
271 data[2] = GINT_TO_POINTER(vs_type);
272 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
275 // Actual signal handlers
276 static void destroy_window ( GtkWidget *widget,
279 if ( ! --window_count )
283 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type )
285 window_statusbar_update ( vw, message, vs_type );
288 VikWindow *vik_window_new_window ()
290 if ( window_count < MAX_WINDOWS )
292 VikWindow *vw = window_new ();
294 g_signal_connect (G_OBJECT (vw), "destroy",
295 G_CALLBACK (destroy_window), NULL);
296 g_signal_connect (G_OBJECT (vw), "newwindow",
297 G_CALLBACK (vik_window_new_window), NULL);
298 g_signal_connect (G_OBJECT (vw), "openwindow",
299 G_CALLBACK (open_window), NULL);
300 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
301 G_CALLBACK (statusbar_update), vw);
303 gtk_widget_show_all ( GTK_WIDGET(vw) );
312 static void open_window ( VikWindow *vw, GSList *files )
314 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
315 GSList *cur_file = files;
317 // Only open a new window if a viking file
318 gchar *file_name = cur_file->data;
319 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
320 VikWindow *newvw = vik_window_new_window ();
322 vik_window_open_file ( newvw, file_name, TRUE );
325 vik_window_open_file ( vw, file_name, change_fn );
328 cur_file = g_slist_next (cur_file);
330 g_slist_free (files);
334 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
336 int i, j, tool_count;
337 VikLayerInterface *layer_interface;
339 if (!vw->action_group) return;
341 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
343 layer_interface = vik_layer_get_interface(i);
344 tool_count = layer_interface->tools_count;
346 for (j = 0; j < tool_count; j++) {
347 action = gtk_action_group_get_action(vw->action_group,
348 layer_interface->tools[j].radioActionEntry.name);
349 g_object_set(action, "sensitive", i == vl->type, NULL);
354 static void window_finalize ( GObject *gob )
356 VikWindow *vw = VIK_WINDOW(gob);
357 g_return_if_fail ( vw != NULL );
359 a_background_remove_window ( vw );
361 G_OBJECT_CLASS(parent_class)->finalize(gob);
365 static void vik_window_class_init ( VikWindowClass *klass )
368 GObjectClass *object_class;
370 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);
371 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);
372 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);
374 object_class = G_OBJECT_CLASS (klass);
376 object_class->finalize = window_finalize;
378 parent_class = g_type_class_peek_parent (klass);
382 static void set_toolbar_zoom ( VikWindow *vw, gdouble mpp )
384 gint active = 2 + round ( log (mpp) / log (2) );
385 // Can we not hard code size here?
388 gtk_combo_box_set_active ( vw->tb_zoom_combo, active );
391 static void zoom_changed ( GtkComboBox *combo, VikWindow *vw )
393 gint active = gtk_combo_box_get_active ( combo );
395 // But has it really changed?
396 // Unfortunately this function gets invoked even on manual setting of the combo value
397 gdouble zoom_request = pow (2, active-2 );
398 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
399 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
400 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
401 // Force drawing update
406 static GtkWidget *create_zoom_combo_all_levels ()
408 GtkWidget *zoom_combo = gtk_combo_box_new_text();
409 GtkComboBox *combo = GTK_COMBO_BOX ( zoom_combo );
410 gtk_combo_box_append_text ( combo, "0.25");
411 gtk_combo_box_append_text ( combo, "0.5");
412 gtk_combo_box_append_text ( combo, "1");
413 gtk_combo_box_append_text ( combo, "2");
414 gtk_combo_box_append_text ( combo, "4");
415 gtk_combo_box_append_text ( combo, "8");
416 gtk_combo_box_append_text ( combo, "16");
417 gtk_combo_box_append_text ( combo, "32");
418 gtk_combo_box_append_text ( combo, "64");
419 gtk_combo_box_append_text ( combo, "128");
420 gtk_combo_box_append_text ( combo, "256");
421 gtk_combo_box_append_text ( combo, "512");
422 gtk_combo_box_append_text ( combo, "1024");
423 gtk_combo_box_append_text ( combo, "2048");
424 gtk_combo_box_append_text ( combo, "4096");
425 gtk_combo_box_append_text ( combo, "8192");
426 gtk_combo_box_append_text ( combo, "16384");
427 gtk_combo_box_append_text ( combo, "32768");
429 gtk_widget_set_tooltip_text (GTK_WIDGET (combo), _("Select zoom level"));
433 static void vik_window_init ( VikWindow *vw )
435 GtkWidget *main_vbox;
438 vw->action_group = NULL;
440 vw->viking_vvp = vik_viewport_new();
441 vw->viking_vlp = vik_layers_panel_new();
442 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
443 vw->viking_vs = vik_statusbar_new();
445 vw->vt = toolbox_create(vw);
446 window_create_ui(vw);
447 window_set_filename (vw, NULL);
448 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
450 // Set the default tool
451 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
454 vw->item_factory = NULL;
456 vw->modified = FALSE;
457 vw->only_updating_coord_mode_ui = FALSE;
459 vw->pan_move = FALSE;
460 vw->pan_x = vw->pan_y = -1;
461 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
462 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
463 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
465 main_vbox = gtk_vbox_new(FALSE, 1);
466 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
468 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
469 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
470 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
471 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
473 vik_ext_tools_add_menu_items ( vw, vw->uim );
475 vw->tb_zoom_combo = GTK_COMBO_BOX(create_zoom_combo_all_levels());
477 g_signal_connect ( G_OBJECT(vw->tb_zoom_combo), "changed", G_CALLBACK(zoom_changed), vw );
479 // Add the zoom combo to the toolbar at the end
480 GtkToolItem *tooli = gtk_tool_item_new ();
481 gtk_container_add ( GTK_CONTAINER(tooli), GTK_WIDGET (vw->tb_zoom_combo) );
482 gtk_toolbar_insert ( vw->toolbar, tooli, gtk_toolbar_get_n_items (vw->toolbar) );
484 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
486 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
487 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
488 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 );
489 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
490 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
491 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
492 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
493 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
495 // Allow key presses to be processed anywhere
496 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
498 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
500 hpaned = gtk_hpaned_new ();
501 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
502 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
504 /* This packs the button into the window (a gtk container). */
505 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
507 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
509 a_background_add_window ( vw );
513 vw->save_img_dia = NULL;
514 vw->save_img_dir_dia = NULL;
516 // Store the thread value so comparisons can be made to determine the gdk update method
517 // Hopefully we are storing the main thread value here :)
518 // [ATM any window initialization is always be performed by the main thread]
519 vw->thread = g_thread_self();
522 static VikWindow *window_new ()
524 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
528 * Update the displayed map
529 * Only update the top most visible map layer
530 * ATM this assumes (as per defaults) the top most map has full alpha setting
531 * such that other other maps even though they may be active will not be seen
532 * It's more complicated to work out which maps are actually visible due to alpha settings
533 * and overkill for this simple refresh method.
535 static void simple_map_update ( VikWindow *vw, gboolean only_new )
537 // Find the most relevent single map layer to operate on
538 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
540 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
544 * This is the global key press handler
545 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
547 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
549 // The keys handled here are not in the menuing system for a couple of reasons:
550 // . Keeps the menu size compact (alebit at expense of discoverably)
551 // . Allows differing key bindings to perform the same actions
553 // First decide if key events are related to the maps layer
554 gboolean map_download = FALSE;
555 gboolean map_download_only_new = TRUE; // Only new or reload
557 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
559 // Standard 'Refresh' keys: F5 or Ctrl+r
560 // Note 'F5' is actually handled via draw_refresh_cb() later on
561 // (not 'R' it's 'r' notice the case difference!!)
562 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
564 map_download_only_new = TRUE;
566 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
567 // Note the use of uppercase R here since shift key has been pressed
568 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
569 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
571 map_download_only_new = FALSE;
574 if ( map_download ) {
575 simple_map_update ( vw, map_download_only_new );
578 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
579 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
580 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
581 if ( vl && ltype == vl->type )
582 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
585 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
586 if ( vw->current_tool < TOOL_LAYER ) {
587 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
588 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
589 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
593 /* Restore Main Menu via Escape key if the user has hidden it */
594 /* This key is more likely to be used as they may not remember the function key */
595 if ( event->keyval == GDK_Escape ) {
596 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
598 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
600 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
601 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
602 return TRUE; /* handled keypress */
607 return FALSE; /* don't handle the keypress */
610 static gboolean delete_event( VikWindow *vw )
612 #ifdef VIKING_PROMPT_IF_MODIFIED
619 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
620 _("Do you want to save the changes you made to the document \"%s\"?\n"
622 "Your changes will be lost if you don't save them."),
623 window_get_filename ( vw ) ) );
624 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
625 switch ( gtk_dialog_run ( dia ) )
627 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
628 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
629 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
636 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
638 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
641 static void draw_update ( VikWindow *vw )
647 static void draw_sync ( VikWindow *vw )
649 vik_viewport_sync(vw->viking_vvp);
654 * Split the status update, as sometimes only need to update the tool part
655 * also on initialization the zoom related stuff is not ready to be used
657 static void draw_status_tool ( VikWindow *vw )
659 if ( vw->current_tool == TOOL_LAYER )
660 // Use tooltip rather than the internal name as the tooltip is i8n
661 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 );
663 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
666 static void draw_status ( VikWindow *vw )
668 static gchar zoom_level[22];
669 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
670 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
671 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
673 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
675 if ( (int)xmpp - xmpp < 0.0 )
676 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
678 /* xmpp should be a whole number so don't show useless .000 bit */
679 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
681 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
682 // OK maybe not quite in the statusbar - but we have the zoom level so use it
683 set_toolbar_zoom ( vw, xmpp ); // But it's a status of some kind!
685 draw_status_tool ( vw );
688 void vik_window_set_redraw_trigger(VikLayer *vl)
690 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
695 static void window_configure_event ( VikWindow *vw )
697 static int first = 1;
700 // This is a hack to set the cursor corresponding to the first tool
701 // FIXME find the correct way to initialize both tool and its cursor
702 const GdkCursor *cursor = NULL;
704 cursor = toolbox_get_cursor(vw->vt, "Pan");
705 /* We set cursor, even if it is NULL: it resets to default */
706 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
710 static void draw_redraw ( VikWindow *vw )
712 VikCoord old_center = vw->trigger_center;
713 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
714 VikLayer *new_trigger = vw->trigger;
716 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
719 ; /* do nothing -- have to redraw everything. */
720 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
721 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
723 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
726 vik_viewport_clear ( vw->viking_vvp);
727 vik_layers_panel_draw_all ( vw->viking_vlp );
728 vik_viewport_draw_scale ( vw->viking_vvp );
729 vik_viewport_draw_copyright ( vw->viking_vvp );
730 vik_viewport_draw_centermark ( vw->viking_vvp );
731 vik_viewport_draw_logo ( vw->viking_vvp );
733 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
736 gboolean draw_buf_done = TRUE;
738 static gboolean draw_buf(gpointer data)
740 gpointer *pass_along = data;
742 gdk_draw_drawable (pass_along[0], pass_along[1],
743 pass_along[2], 0, 0, 0, 0, -1, -1);
744 draw_buf_done = TRUE;
750 /* Mouse event handlers ************************************************************************/
752 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
754 /* set panning origin */
755 vw->pan_move = FALSE;
756 vw->pan_x = (gint) event->x;
757 vw->pan_y = (gint) event->y;
760 static void draw_click (VikWindow *vw, GdkEventButton *event)
762 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
764 /* middle button pressed. we reserve all middle button and scroll events
765 * for panning and zooming; tools only get left/right/movement
767 if ( event->button == 2) {
768 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
769 // Tool still may need to do something (such as disable something)
770 toolbox_click(vw->vt, event);
771 vik_window_pan_click ( vw, event );
774 toolbox_click(vw->vt, event);
778 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
780 if ( vw->pan_x != -1 ) {
781 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
782 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
784 vw->pan_x = event->x;
785 vw->pan_y = event->y;
790 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
792 static VikCoord coord;
793 static struct UTM utm;
794 static struct LatLon ll;
795 #define BUFFER_SIZE 50
796 static char pointer_buf[BUFFER_SIZE];
797 gchar *lat = NULL, *lon = NULL;
800 VikDemInterpol interpol_method;
802 /* This is a hack, but work far the best, at least for single pointer systems.
803 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
805 gdk_window_get_pointer (event->window, &x, &y, NULL);
809 toolbox_move(vw->vt, event);
811 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
812 vik_coord_to_utm ( &coord, &utm );
814 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
815 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
816 // ZONE[N|S] EASTING NORTHING
817 lat = g_malloc(4*sizeof(gchar));
818 // NB zone is stored in a char but is an actual number
819 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
820 lon = g_malloc(16*sizeof(gchar));
821 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
824 a_coords_utm_to_latlon ( &utm, &ll );
825 a_coords_latlon_to_string ( &ll, &lat, &lon );
828 /* Change interpolate method according to scale */
829 zoom = vik_viewport_get_zoom(vw->viking_vvp);
831 interpol_method = VIK_DEM_INTERPOL_NONE;
832 else if (zoom >= 1.0)
833 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
835 interpol_method = VIK_DEM_INTERPOL_BEST;
836 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
837 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
838 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
840 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
843 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
848 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
850 vik_window_pan_move ( vw, event );
852 /* This is recommended by the GTK+ documentation, but does not work properly.
853 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
854 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
856 /* gdk_event_request_motions ( event ); */
859 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
861 if ( vw->pan_move == FALSE )
862 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
864 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
865 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
866 vw->pan_move = FALSE;
867 vw->pan_x = vw->pan_y = -1;
871 static void draw_release ( VikWindow *vw, GdkEventButton *event )
873 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
875 if ( event->button == 2 ) { /* move / pan */
876 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
877 // Tool still may need to do something (such as reenable something)
878 toolbox_release(vw->vt, event);
879 vik_window_pan_release ( vw, event );
882 toolbox_release(vw->vt, event);
886 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
888 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
889 if ( modifiers == GDK_CONTROL_MASK ) {
890 /* control == pan up & down */
891 if ( event->direction == GDK_SCROLL_UP )
892 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
894 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 );
895 } else if ( modifiers == GDK_SHIFT_MASK ) {
896 /* shift == pan left & right */
897 if ( event->direction == GDK_SCROLL_UP )
898 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
900 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 );
901 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
902 // This zoom is on the center position
903 if ( event->direction == GDK_SCROLL_UP )
904 vik_viewport_zoom_in (vw->viking_vvp);
906 vik_viewport_zoom_out (vw->viking_vvp);
908 /* make sure mouse is still over the same point on the map when we zoom */
911 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
912 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
913 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
914 if ( event->direction == GDK_SCROLL_UP )
915 vik_viewport_zoom_in (vw->viking_vvp);
917 vik_viewport_zoom_out(vw->viking_vvp);
918 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
919 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
920 center_y + (y - event->y) );
928 /********************************************************************************
930 ********************************************************************************/
931 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
935 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
936 GdkGC *thickgc = gdk_gc_new(d);
938 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
939 gdouble dx = (x2-x1)/len*10;
940 gdouble dy = (y2-y1)/len*10;
941 gdouble c = cos(15.0 * M_PI/180.0);
942 gdouble s = sin(15.0 * M_PI/180.0);
944 gdouble baseangle = 0;
947 /* draw line with arrow ends */
949 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
950 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
951 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
954 a_viewport_clip_line(&x1, &y1, &x2, &y2);
955 gdk_draw_line(d, gc, x1, y1, x2, y2);
957 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
958 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
959 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
960 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
961 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
962 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
968 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
972 gdk_gc_copy(thickgc, gc);
973 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
974 gdk_color_parse("#2255cc", &color);
975 gdk_gc_set_rgb_fg_color(thickgc, &color);
977 gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - baseangle*180/M_PI)*64, -angle*180/M_PI*64);
980 gdk_gc_copy(thickgc, gc);
981 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
982 for (i=0; i<180; i++) {
983 c = cos(i*M_PI/90.0 + baseangle);
984 s = sin(i*M_PI/90.0 + baseangle);
987 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
989 gdouble ticksize = 2*CW;
990 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
994 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
995 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
996 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
997 c = (CR+CW*2)*cos(baseangle);
998 s = (CR+CW*2)*sin(baseangle);
999 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1000 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1003 #define LABEL(x, y, w, h) { \
1004 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1005 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1006 gdk_draw_layout(d, gc, (x), (y), pl); }
1008 gint wd, hd, xd, yd;
1009 gint wb, hb, xb, yb;
1011 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1012 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
1013 pango_layout_set_text(pl, "N", -1);
1014 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1016 /* draw label with distance */
1017 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1018 switch (dist_units) {
1019 case VIK_UNITS_DISTANCE_KILOMETRES:
1020 if (distance >= 1000 && distance < 100000) {
1021 g_sprintf(str, "%3.2f km", distance/1000.0);
1022 } else if (distance < 1000) {
1023 g_sprintf(str, "%d m", (int)distance);
1025 g_sprintf(str, "%d km", (int)distance/1000);
1028 case VIK_UNITS_DISTANCE_MILES:
1029 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1030 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1031 } else if (distance < VIK_MILES_TO_METERS(1)) {
1032 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1034 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1038 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1041 pango_layout_set_text(pl, str, -1);
1043 pango_layout_get_pixel_size ( pl, &wd, &hd );
1045 xd = (x1+x2)/2 + dy;
1046 yd = (y1+y2)/2 - hd/2 - dx;
1048 xd = (x1+x2)/2 - dy;
1049 yd = (y1+y2)/2 - hd/2 + dx;
1052 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1057 LABEL(xd, yd, wd, hd);
1059 /* draw label with bearing */
1060 g_sprintf(str, "%3.1f°", angle*180.0/M_PI);
1061 pango_layout_set_text(pl, str, -1);
1062 pango_layout_get_pixel_size ( pl, &wb, &hb );
1063 xb = x1 + CR*cos(angle-M_PI_2);
1064 yb = y1 + CR*sin(angle-M_PI_2);
1066 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1072 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1073 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1077 LABEL(xb, yb, wb, hb);
1081 g_object_unref ( G_OBJECT ( pl ) );
1082 g_object_unref ( G_OBJECT ( labgc ) );
1083 g_object_unref ( G_OBJECT ( thickgc ) );
1089 gboolean has_oldcoord;
1091 } ruler_tool_state_t;
1093 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1095 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1098 s->has_oldcoord = FALSE;
1102 static void ruler_destroy (ruler_tool_state_t *s)
1107 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1112 if ( event->button == 1 ) {
1113 gchar *lat=NULL, *lon=NULL;
1114 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1115 vik_coord_to_latlon ( &coord, &ll );
1116 a_coords_latlon_to_string ( &ll, &lat, &lon );
1117 if ( s->has_oldcoord ) {
1118 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1119 switch (dist_units) {
1120 case VIK_UNITS_DISTANCE_KILOMETRES:
1121 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1123 case VIK_UNITS_DISTANCE_MILES:
1124 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1127 temp = g_strdup_printf ("Just to keep the compiler happy");
1128 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1131 s->has_oldcoord = FALSE;
1134 temp = g_strdup_printf ( "%s %s", lat, lon );
1135 s->has_oldcoord = TRUE;
1138 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1141 s->oldcoord = coord;
1144 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1145 draw_update ( s->vw );
1147 return VIK_LAYER_TOOL_ACK;
1150 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1152 VikViewport *vvp = s->vvp;
1153 VikWindow *vw = s->vw;
1159 if ( s->has_oldcoord ) {
1160 int oldx, oldy, w1, h1, w2, h2;
1161 static GdkPixmap *buf = NULL;
1162 gchar *lat=NULL, *lon=NULL;
1163 w1 = vik_viewport_get_width(vvp);
1164 h1 = vik_viewport_get_height(vvp);
1166 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1168 gdk_drawable_get_size(buf, &w2, &h2);
1169 if (w1 != w2 || h1 != h2) {
1170 g_object_unref ( G_OBJECT ( buf ) );
1171 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1174 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1175 vik_coord_to_latlon ( &coord, &ll );
1176 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1178 gdk_draw_drawable (buf, GTK_WIDGET(vvp)->style->black_gc,
1179 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1180 draw_ruler(vvp, buf, GTK_WIDGET(vvp)->style->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1181 if (draw_buf_done) {
1182 static gpointer pass_along[3];
1183 pass_along[0] = GTK_WIDGET(vvp)->window;
1184 pass_along[1] = GTK_WIDGET(vvp)->style->black_gc;
1185 pass_along[2] = buf;
1186 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1187 draw_buf_done = FALSE;
1189 a_coords_latlon_to_string(&ll, &lat, &lon);
1190 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1191 switch (dist_units) {
1192 case VIK_UNITS_DISTANCE_KILOMETRES:
1193 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1195 case VIK_UNITS_DISTANCE_MILES:
1196 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1199 temp = g_strdup_printf ("Just to keep the compiler happy");
1200 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1202 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1205 return VIK_LAYER_TOOL_ACK;
1208 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1210 return VIK_LAYER_TOOL_ACK;
1213 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1215 draw_update ( s->vw );
1218 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1220 if (event->keyval == GDK_Escape) {
1221 s->has_oldcoord = FALSE;
1222 ruler_deactivate ( vl, s );
1225 // Regardless of whether we used it, return false so other GTK things may use it
1229 static VikToolInterface ruler_tool =
1230 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1231 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1232 (VikToolConstructorFunc) ruler_create,
1233 (VikToolDestructorFunc) ruler_destroy,
1234 (VikToolActivationFunc) NULL,
1235 (VikToolActivationFunc) ruler_deactivate,
1236 (VikToolMouseFunc) ruler_click,
1237 (VikToolMouseMoveFunc) ruler_move,
1238 (VikToolMouseFunc) ruler_release,
1239 (VikToolKeyFunc) ruler_key_press,
1241 GDK_CURSOR_IS_PIXMAP,
1242 &cursor_ruler_pixbuf };
1243 /*** end ruler code ********************************************************/
1247 /********************************************************************************
1249 ********************************************************************************/
1254 // Track zoom bounds for zoom tool with shift modifier:
1255 gboolean bounds_active;
1258 } zoom_tool_state_t;
1261 * In case the screen size has changed
1263 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1267 // Allocate a drawing area the size of the viewport
1268 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1269 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1271 if ( !zts->pixmap ) {
1273 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1276 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1278 if ( w1 != w2 || h1 != h2 ) {
1279 // Has changed - delete and recreate with new values
1280 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1281 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1285 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1287 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1292 zts->bounds_active = FALSE;
1296 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1299 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1303 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1305 zts->vw->modified = TRUE;
1306 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1310 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1311 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1313 gboolean skip_update = FALSE;
1315 zts->bounds_active = FALSE;
1317 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1318 // This zoom is on the center position
1319 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1320 if ( event->button == 1 )
1321 vik_viewport_zoom_in (zts->vw->viking_vvp);
1322 else if ( event->button == 3 )
1323 vik_viewport_zoom_out (zts->vw->viking_vvp);
1325 else if ( modifiers == GDK_CONTROL_MASK ) {
1326 // This zoom is to recenter on the mouse position
1327 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1328 if ( event->button == 1 )
1329 vik_viewport_zoom_in (zts->vw->viking_vvp);
1330 else if ( event->button == 3 )
1331 vik_viewport_zoom_out (zts->vw->viking_vvp);
1333 else if ( modifiers == GDK_SHIFT_MASK ) {
1334 // Get start of new zoom bounds
1335 if ( event->button == 1 ) {
1336 zts->bounds_active = TRUE;
1337 zts->start_x = (gint) event->x;
1338 zts->start_y = (gint) event->y;
1343 /* make sure mouse is still over the same point on the map when we zoom */
1344 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1345 if ( event->button == 1 )
1346 vik_viewport_zoom_in (zts->vw->viking_vvp);
1347 else if ( event->button == 3 )
1348 vik_viewport_zoom_out(zts->vw->viking_vvp);
1349 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1350 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1351 center_x + (x - event->x),
1352 center_y + (y - event->y) );
1356 draw_update ( zts->vw );
1358 return VIK_LAYER_TOOL_ACK;
1361 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1363 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1365 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1366 zoomtool_resize_pixmap ( zts );
1368 // Blank out currently drawn area
1369 gdk_draw_drawable ( zts->pixmap,
1370 GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc,
1371 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1372 0, 0, 0, 0, -1, -1);
1374 // Calculate new box starting point & size in pixels
1375 int xx, yy, width, height;
1376 if ( event->y > zts->start_y ) {
1378 height = event->y-zts->start_y;
1382 height = zts->start_y-event->y;
1384 if ( event->x > zts->start_x ) {
1386 width = event->x-zts->start_x;
1390 width = zts->start_x-event->x;
1394 gdk_draw_rectangle (zts->pixmap, GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, FALSE, xx, yy, width, height);
1396 // Only actually draw when there's time to do so
1397 if (draw_buf_done) {
1398 static gpointer pass_along[3];
1399 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1400 pass_along[1] = GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc;
1401 pass_along[2] = zts->pixmap;
1402 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1403 draw_buf_done = FALSE;
1406 return VIK_LAYER_TOOL_ACK;
1409 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1411 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1413 zts->bounds_active = FALSE;
1415 // Ensure haven't just released on the exact same position
1416 // i.e. probably haven't moved the mouse at all
1417 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1419 VikCoord coord1, coord2;
1420 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1421 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1423 // From the extend of the bounds pick the best zoom level
1424 // c.f. trw_layer_zoom_to_show_latlons()
1425 // Maybe refactor...
1426 struct LatLon ll1, ll2;
1427 vik_coord_to_latlon(&coord1, &ll1);
1428 vik_coord_to_latlon(&coord2, &ll2);
1429 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1430 (ll1.lon+ll2.lon)/2 };
1432 VikCoord new_center;
1433 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1434 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1436 /* Convert into definite 'smallest' and 'largest' positions */
1437 struct LatLon minmin;
1438 if ( ll1.lat < ll2.lat )
1439 minmin.lat = ll1.lat;
1441 minmin.lat = ll2.lat;
1443 struct LatLon maxmax;
1444 if ( ll1.lon > ll2.lon )
1445 maxmax.lon = ll1.lon;
1447 maxmax.lon = ll2.lon;
1449 /* Always recalculate the 'best' zoom level */
1450 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1451 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1453 gdouble min_lat, max_lat, min_lon, max_lon;
1454 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1455 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1456 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1457 /* NB I think the logic used in this test to determine if the bounds is within view
1458 fails if track goes across 180 degrees longitude.
1459 Hopefully that situation is not too common...
1460 Mind you viking doesn't really do edge locations to well anyway */
1461 if ( min_lat < minmin.lat &&
1462 max_lat > minmin.lat &&
1463 min_lon < maxmax.lon &&
1464 max_lon > maxmax.lon )
1465 /* Found within zoom level */
1470 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1473 draw_update ( zts->vw );
1475 return VIK_LAYER_TOOL_ACK;
1478 static VikToolInterface zoom_tool =
1479 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1480 (VikToolConstructorFunc) zoomtool_create,
1481 (VikToolDestructorFunc) zoomtool_destroy,
1482 (VikToolActivationFunc) NULL,
1483 (VikToolActivationFunc) NULL,
1484 (VikToolMouseFunc) zoomtool_click,
1485 (VikToolMouseMoveFunc) zoomtool_move,
1486 (VikToolMouseFunc) zoomtool_release,
1489 GDK_CURSOR_IS_PIXMAP,
1490 &cursor_zoom_pixbuf };
1491 /*** end zoom code ********************************************************/
1493 /********************************************************************************
1495 ********************************************************************************/
1496 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1501 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1503 vw->modified = TRUE;
1504 if ( event->button == 1 )
1505 vik_window_pan_click ( vw, event );
1507 return VIK_LAYER_TOOL_ACK;
1510 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1512 vik_window_pan_move ( vw, event );
1513 return VIK_LAYER_TOOL_ACK;
1516 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1518 if ( event->button == 1 )
1519 vik_window_pan_release ( vw, event );
1520 return VIK_LAYER_TOOL_ACK;
1523 static VikToolInterface pan_tool =
1524 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1525 (VikToolConstructorFunc) pantool_create,
1526 (VikToolDestructorFunc) NULL,
1527 (VikToolActivationFunc) NULL,
1528 (VikToolActivationFunc) NULL,
1529 (VikToolMouseFunc) pantool_click,
1530 (VikToolMouseMoveFunc) pantool_move,
1531 (VikToolMouseFunc) pantool_release,
1535 /*** end pan code ********************************************************/
1537 /********************************************************************************
1539 ********************************************************************************/
1540 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1542 tool_ed_t *t = g_new(tool_ed_t, 1);
1546 t->is_waypoint = FALSE;
1550 static void selecttool_destroy (tool_ed_t *t)
1558 GdkEventButton *event;
1559 tool_ed_t *tool_edit;
1562 static void click_layer_selected (VikLayer *vl, clicker *ck)
1564 /* Do nothing when function call returns true; */
1565 /* i.e. stop on first found item */
1568 if ( vik_layer_get_interface(vl->type)->select_click )
1569 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1572 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1574 /* Only allow selection on primary button */
1575 if ( event->button == 1 ) {
1576 /* Enable click to apply callback to potentially all track/waypoint layers */
1577 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1578 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1581 ck.vvp = t->vw->viking_vvp;
1584 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1587 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1590 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1592 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1593 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1594 gint type = vik_treeview_item_get_type ( vtv, &iter );
1595 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1596 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1598 vik_treeview_item_unselect ( vtv, &iter );
1599 if ( vik_window_clear_highlight ( t->vw ) )
1600 draw_update ( t->vw );
1605 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1607 /* Act on currently selected item to show menu */
1608 if ( t->vw->selected_track || t->vw->selected_waypoint )
1609 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1610 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1613 return VIK_LAYER_TOOL_ACK;
1616 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1618 /* Only allow selection on primary button */
1619 if ( event->button == 1 ) {
1620 // Don't care about vl here
1622 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1623 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1625 return VIK_LAYER_TOOL_ACK;
1628 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1630 /* Only allow selection on primary button */
1631 if ( event->button == 1 ) {
1632 // Don't care about vl here
1634 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1635 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1637 return VIK_LAYER_TOOL_ACK;
1640 static VikToolInterface select_tool =
1641 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1642 (VikToolConstructorFunc) selecttool_create,
1643 (VikToolDestructorFunc) selecttool_destroy,
1644 (VikToolActivationFunc) NULL,
1645 (VikToolActivationFunc) NULL,
1646 (VikToolMouseFunc) selecttool_click,
1647 (VikToolMouseMoveFunc) selecttool_move,
1648 (VikToolMouseFunc) selecttool_release,
1649 (VikToolKeyFunc) NULL,
1654 /*** end select tool code ********************************************************/
1656 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1658 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1659 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1660 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1661 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1662 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1663 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1664 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1665 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1670 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1672 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1673 g_assert(check_box);
1674 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1676 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1678 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1681 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1685 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1688 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1691 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1694 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1698 gchar *s = (gchar *)gtk_action_get_name(a);
1704 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1705 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1706 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1707 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1708 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1713 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1715 VikCoord new_center;
1717 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1718 struct LatLon ll, llold;
1719 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1720 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1721 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1725 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1726 struct UTM utm, utmold;
1727 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1728 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1729 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1734 g_critical("Houston, we've had a problem.");
1738 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1743 * Refresh maps displayed
1745 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1747 // Only get 'new' maps
1748 simple_map_update ( vw, TRUE );
1751 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1754 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1755 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1756 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1758 vw->modified = TRUE;
1764 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1766 a_clipboard_copy_selected ( vw->viking_vlp );
1769 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1771 vik_layers_panel_cut_selected ( vw->viking_vlp );
1772 vw->modified = TRUE;
1775 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1777 if ( a_clipboard_paste ( vw->viking_vlp ) )
1779 vw->modified = TRUE;
1783 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1785 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1786 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1789 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1792 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1794 #if GTK_CHECK_VERSION (2, 14, 0)
1796 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1797 GError *error = NULL;
1798 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1799 if ( !show && !error )
1800 // No error to show, so unlikely this will get called
1801 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1804 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 );
1805 g_error_free ( error );
1809 a_dialog_error_msg ( GTK_WINDOW(vw), "Help is not available in this build." ); // Unlikely to happen so not going to bother with I8N
1811 #endif /* WINDOWS */
1814 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1816 a_dialog_about(GTK_WINDOW(vw));
1819 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1821 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1823 vik_layers_panel_delete_selected ( vw->viking_vlp );
1824 vw->modified = TRUE;
1827 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1830 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1832 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1833 g_assert(check_box);
1834 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1836 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1838 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1841 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1843 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1846 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1848 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1850 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1853 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1855 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1858 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1860 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1862 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1865 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1867 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1870 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1872 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1874 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1877 /***************************************
1878 ** tool management routines
1880 ***************************************/
1882 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1884 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1887 vt->active_tool = -1;
1892 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1894 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1895 vt->tools[vt->n_tools].ti = *vti;
1896 vt->tools[vt->n_tools].layer_type = layer_type;
1898 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1901 vt->tools[vt->n_tools].state = NULL;
1906 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1909 for (i=0; i<vt->n_tools; i++) {
1910 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1917 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1919 int tool = toolbox_get_tool(vt, tool_name);
1920 toolbox_tool_t *t = &vt->tools[tool];
1921 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1923 if (tool == vt->n_tools) {
1924 g_critical("trying to activate a non-existent tool...");
1927 /* is the tool already active? */
1928 if (vt->active_tool == tool) {
1932 if (vt->active_tool != -1) {
1933 if (vt->tools[vt->active_tool].ti.deactivate) {
1934 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1937 if (t->ti.activate) {
1938 t->ti.activate(vl, t->state);
1940 vt->active_tool = tool;
1943 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1945 int tool = toolbox_get_tool(vt, tool_name);
1946 toolbox_tool_t *t = &vt->tools[tool];
1947 if (t->ti.cursor == NULL) {
1948 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1949 GError *cursor_load_err = NULL;
1950 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1951 /* TODO: settable offeset */
1952 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1953 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1955 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1958 return t->ti.cursor;
1961 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1963 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1964 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1965 gint ltype = vt->tools[vt->active_tool].layer_type;
1966 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1967 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
1971 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
1973 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1974 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
1975 gint ltype = vt->tools[vt->active_tool].layer_type;
1976 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1977 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
1978 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
1982 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
1984 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1985 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
1986 gint ltype = vt->tools[vt->active_tool].layer_type;
1987 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1988 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
1991 /** End tool management ************************************/
1993 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
1995 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
1998 /* this function gets called whenever a toolbar tool is clicked */
1999 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2001 /* White Magic, my friends ... White Magic... */
2002 int layer_id, tool_id;
2003 const GdkCursor *cursor = NULL;
2005 toolbox_activate(vw->vt, gtk_action_get_name(a));
2007 cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2009 if ( GTK_WIDGET(vw->viking_vvp)->window )
2010 /* We set cursor, even if it is NULL: it resets to default */
2011 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
2013 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2014 vw->current_tool = TOOL_PAN;
2016 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2017 vw->current_tool = TOOL_ZOOM;
2019 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2020 vw->current_tool = TOOL_RULER;
2022 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2023 vw->current_tool = TOOL_SELECT;
2026 /* TODO: only enable tools from active layer */
2027 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2028 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2029 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2030 vw->current_tool = TOOL_LAYER;
2031 vw->tool_layer_id = layer_id;
2032 vw->tool_tool_id = tool_id;
2037 draw_status_tool ( vw );
2040 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2045 g_free ( vw->filename );
2046 if ( filename == NULL )
2048 vw->filename = NULL;
2052 vw->filename = g_strdup(filename);
2055 /* Refresh window's title */
2056 file = window_get_filename ( vw );
2057 title = g_strdup_printf( "%s - Viking", file );
2058 gtk_window_set_title ( GTK_WINDOW(vw), title );
2062 static const gchar *window_get_filename ( VikWindow *vw )
2064 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2067 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2069 GtkWidget *mode_button;
2072 #ifdef VIK_CONFIG_EXPEDIA
2073 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2075 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2076 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2077 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2079 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2080 g_assert ( mode_button );
2085 * vik_window_get_pan_move:
2086 * @vw: some VikWindow
2088 * Retrieves @vw's pan_move.
2090 * Should be removed as soon as possible.
2092 * Returns: @vw's pan_move
2096 gboolean vik_window_get_pan_move ( VikWindow *vw )
2098 return vw->pan_move;
2101 static void on_activate_recent_item (GtkRecentChooser *chooser,
2106 filename = gtk_recent_chooser_get_current_uri (chooser);
2107 if (filename != NULL)
2109 GFile *file = g_file_new_for_uri ( filename );
2110 gchar *path = g_file_get_path ( file );
2111 g_object_unref ( file );
2112 if ( self->filename )
2114 GSList *filenames = NULL;
2115 filenames = g_slist_append ( filenames, path );
2116 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2117 // NB: GSList & contents are freed by main.open_window
2120 vik_window_open_file ( self, path, TRUE );
2128 static void setup_recent_files (VikWindow *self)
2130 GtkRecentManager *manager;
2131 GtkRecentFilter *filter;
2132 GtkWidget *menu, *menu_item;
2134 filter = gtk_recent_filter_new ();
2135 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2136 gtk_recent_filter_add_group(filter, "viking");
2138 manager = gtk_recent_manager_get_default ();
2139 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2140 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2141 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2143 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2144 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2146 g_signal_connect (G_OBJECT (menu), "item-activated",
2147 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2150 static void update_recently_used_document(const gchar *filename)
2152 /* Update Recently Used Document framework */
2153 GtkRecentManager *manager = gtk_recent_manager_get_default();
2154 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2155 gchar *groups[] = {"viking", NULL};
2156 GFile *file = g_file_new_for_commandline_arg(filename);
2157 gchar *uri = g_file_get_uri(file);
2158 gchar *basename = g_path_get_basename(filename);
2159 g_object_unref(file);
2162 recent_data->display_name = basename;
2163 recent_data->description = NULL;
2164 recent_data->mime_type = "text/x-gps-data";
2165 recent_data->app_name = (gchar *) g_get_application_name ();
2166 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2167 recent_data->groups = groups;
2168 recent_data->is_private = FALSE;
2169 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2171 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2176 g_free (recent_data->app_exec);
2177 g_slice_free (GtkRecentData, recent_data);
2180 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2182 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2184 case LOAD_TYPE_READ_FAILURE:
2185 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2187 case LOAD_TYPE_GPSBABEL_FAILURE:
2188 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2190 case LOAD_TYPE_GPX_FAILURE:
2191 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2193 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2194 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2196 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2198 // Since we can process .vik files with issues just show a warning in the status bar
2199 // Not that a user can do much about it... or tells them what this issue is yet...
2200 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2201 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2204 // No break, carry on to show any data
2205 case LOAD_TYPE_VIK_SUCCESS:
2207 GtkWidget *mode_button;
2209 if ( change_filename )
2210 window_set_filename ( vw, filename );
2211 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2212 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. */
2213 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2214 vw->only_updating_coord_mode_ui = FALSE;
2216 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2218 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2219 g_assert ( mode_button );
2220 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2222 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2223 g_assert ( mode_button );
2224 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2226 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2227 g_assert ( mode_button );
2228 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2230 //case LOAD_TYPE_OTHER_SUCCESS:
2232 update_recently_used_document(filename);
2237 static void load_file ( GtkAction *a, VikWindow *vw )
2239 GSList *files = NULL;
2240 GSList *cur_file = NULL;
2242 if (!strcmp(gtk_action_get_name(a), "Open")) {
2245 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2249 g_critical("Houston, we've had a problem.");
2253 if ( ! vw->open_dia )
2255 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2257 GTK_FILE_CHOOSER_ACTION_OPEN,
2258 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2259 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2261 GtkFileFilter *filter;
2262 // NB file filters are listed this way for alphabetical ordering
2263 #ifdef VIK_CONFIG_GEOCACHES
2264 filter = gtk_file_filter_new ();
2265 gtk_file_filter_set_name( filter, _("Geocaching") );
2266 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2267 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2270 filter = gtk_file_filter_new ();
2271 gtk_file_filter_set_name( filter, _("Google Earth") );
2272 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2273 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2275 filter = gtk_file_filter_new ();
2276 gtk_file_filter_set_name( filter, _("GPX") );
2277 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2278 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2280 filter = gtk_file_filter_new ();
2281 gtk_file_filter_set_name( filter, _("Viking") );
2282 gtk_file_filter_add_pattern ( filter, "*.vik" );
2283 gtk_file_filter_add_pattern ( filter, "*.viking" );
2284 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2286 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2287 // However assume this are barely used and thus not worthy of inclusion
2288 // as they'll just make the options too many and have no clear file pattern
2289 // one can always use the all option
2290 filter = gtk_file_filter_new ();
2291 gtk_file_filter_set_name( filter, _("All") );
2292 gtk_file_filter_add_pattern ( filter, "*" );
2293 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2294 // Default to any file - same as before open filters were added
2295 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2297 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2298 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2299 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2301 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2303 gtk_widget_hide ( vw->open_dia );
2304 #ifdef VIKING_PROMPT_IF_MODIFIED
2305 if ( (vw->modified || vw->filename) && newwindow )
2307 if ( vw->filename && newwindow )
2309 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2311 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2312 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2313 gboolean first_vik_file = TRUE;
2315 while ( cur_file ) {
2317 gchar *file_name = cur_file->data;
2318 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2319 // Load first of many .vik files in current window
2320 if ( first_vik_file ) {
2321 vik_window_open_file ( vw, file_name, TRUE );
2322 first_vik_file = FALSE;
2325 // Load each subsequent .vik file in a separate window
2326 VikWindow *newvw = vik_window_new_window ();
2328 vik_window_open_file ( newvw, file_name, TRUE );
2333 vik_window_open_file ( vw, file_name, change_fn );
2336 cur_file = g_slist_next (cur_file);
2338 g_slist_free (files);
2342 gtk_widget_hide ( vw->open_dia );
2345 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2347 gboolean rv = FALSE;
2349 if ( ! vw->save_dia )
2351 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2353 GTK_FILE_CHOOSER_ACTION_SAVE,
2354 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2355 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2357 GtkFileFilter *filter;
2358 filter = gtk_file_filter_new ();
2359 gtk_file_filter_set_name( filter, _("All") );
2360 gtk_file_filter_add_pattern ( filter, "*" );
2361 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2363 filter = gtk_file_filter_new ();
2364 gtk_file_filter_set_name( filter, _("Viking") );
2365 gtk_file_filter_add_pattern ( filter, "*.vik" );
2366 gtk_file_filter_add_pattern ( filter, "*.viking" );
2367 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2368 // Default to a Viking file
2369 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2371 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2372 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2374 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2375 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2376 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2377 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2379 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2381 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2383 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2384 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 ) ) )
2386 window_set_filename ( vw, fn );
2387 rv = window_save ( vw );
2388 vw->modified = FALSE;
2392 g_free ( auto_save_name );
2393 gtk_widget_hide ( vw->save_dia );
2397 static gboolean window_save ( VikWindow *vw )
2399 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2401 update_recently_used_document ( vw->filename );
2406 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2411 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2413 if ( ! vw->filename )
2414 return save_file_as ( NULL, vw );
2417 vw->modified = FALSE;
2418 return window_save ( vw );
2422 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2424 // Via the file menu, acquiring from a GPS makes a new layer
2425 // this has always been the way (not entirely sure if this was the real intention!)
2426 // thus maintain the behaviour ATM.
2427 // Hence explicit setting here (as the value may be changed elsewhere)
2428 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2429 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2432 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2434 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2437 #ifdef VIK_CONFIG_GOOGLE
2438 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2440 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2444 #ifdef VIK_CONFIG_OPENSTREETMAP
2445 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2447 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2451 #ifdef VIK_CONFIG_GEOCACHES
2452 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2454 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2458 #ifdef VIK_CONFIG_GEOTAG
2459 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2461 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2462 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2466 #ifdef VIK_CONFIG_GEONAMES
2467 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
2469 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface );
2473 static void goto_default_location( GtkAction *a, VikWindow *vw)
2476 ll.lat = a_vik_get_default_lat();
2477 ll.lon = a_vik_get_default_long();
2478 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2479 vik_layers_panel_emit_update(vw->viking_vlp);
2483 static void goto_address( GtkAction *a, VikWindow *vw)
2485 a_vik_goto ( vw, vw->viking_vvp );
2486 vik_layers_panel_emit_update ( vw->viking_vlp );
2489 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2494 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2496 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2498 a_preferences_show_window ( GTK_WINDOW(vw) );
2500 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2501 if (wp_icon_size != a_vik_get_use_large_waypoint_icons())
2502 clear_garmin_icon_syms ();
2507 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2509 /* Simplistic repeat of preference setting
2510 Only the name & type are important for setting the preference via this 'external' way */
2511 VikLayerParam pref_lat[] = {
2512 { VIKING_PREFERENCES_NAMESPACE "default_latitude",
2513 VIK_LAYER_PARAM_DOUBLE,
2516 VIK_LAYER_WIDGET_SPINBUTTON,
2521 VikLayerParam pref_lon[] = {
2522 { VIKING_PREFERENCES_NAMESPACE "default_longitude",
2523 VIK_LAYER_PARAM_DOUBLE,
2526 VIK_LAYER_WIDGET_SPINBUTTON,
2532 /* Get current center */
2534 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2536 /* Apply to preferences */
2537 VikLayerParamData vlp_data;
2538 vlp_data.d = ll.lat;
2539 a_preferences_run_setparam (vlp_data, pref_lat);
2540 vlp_data.d = ll.lon;
2541 a_preferences_run_setparam (vlp_data, pref_lon);
2542 /* Remember to save */
2543 a_preferences_save_to_file();
2546 static void clear_cb ( GtkAction *a, VikWindow *vw )
2548 vik_layers_panel_clear ( vw->viking_vlp );
2549 window_set_filename ( vw, NULL );
2553 static void window_close ( GtkAction *a, VikWindow *vw )
2555 if ( ! delete_event ( vw ) )
2556 gtk_widget_destroy ( GTK_WIDGET(vw) );
2559 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2561 if (save_file( NULL, vw)) {
2562 window_close( NULL, vw);
2569 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2571 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2572 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2574 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2575 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2580 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2582 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2583 GdkPixbuf *pixbuf_to_save;
2584 gdouble old_xmpp, old_ympp;
2585 GError *error = NULL;
2587 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2588 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2591 _("Generating image file...") );
2593 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2594 // Ensure dialog shown
2595 gtk_widget_show_all ( msgbox );
2597 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2598 while ( gtk_events_pending() )
2599 gtk_main_iteration ();
2600 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2601 // At least the empty box can give a clue something's going on + the statusbar msg...
2602 // Windows version under Wine OK!
2604 /* backup old zoom & set new */
2605 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2606 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2607 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2609 /* reset width and height: */
2610 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2612 /* draw all layers */
2615 /* save buffer as file. */
2616 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);
2617 if ( !pixbuf_to_save ) {
2618 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2619 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
2623 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2626 g_warning("Unable to write to file %s: %s", fn, error->message );
2627 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
2628 g_error_free (error);
2632 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2634 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2637 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2638 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2639 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2641 /* pretend like nothing happened ;) */
2642 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2643 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2644 vik_viewport_configure ( vw->viking_vvp );
2648 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 )
2650 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2651 gchar *name_of_file = g_malloc ( size );
2653 struct UTM utm_orig, utm;
2655 /* *** copied from above *** */
2656 GdkPixbuf *pixbuf_to_save;
2657 gdouble old_xmpp, old_ympp;
2658 GError *error = NULL;
2660 /* backup old zoom & set new */
2661 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2662 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2663 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2665 /* reset width and height: do this only once for all images (same size) */
2666 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2667 /* *** end copy from above *** */
2669 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2673 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2675 for ( y = 1; y <= tiles_h; y++ )
2677 for ( x = 1; x <= tiles_w; x++ )
2679 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2681 if ( tiles_w & 0x1 )
2682 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2684 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2685 if ( tiles_h & 0x1 ) /* odd */
2686 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2688 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2690 /* move to correct place. */
2691 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2695 /* save buffer as file. */
2696 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);
2697 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2700 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2701 g_error_free (error);
2704 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2708 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
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 );
2714 g_free ( name_of_file );
2717 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2719 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2720 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2722 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2723 gdouble zoom = pow (2, active-2 );
2725 gdouble width_min, width_max, height_min, height_max;
2728 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2729 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2731 /* TODO: support for xzoom and yzoom values */
2732 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2733 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2735 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2736 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2738 gtk_spin_button_set_value ( width_spin, width );
2739 gtk_spin_button_set_value ( height_spin, height );
2742 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2744 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2746 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2747 gdouble zoom = pow (2, active-2 );
2751 w = gtk_spin_button_get_value(width_spin) * zoom;
2752 h = gtk_spin_button_get_value(height_spin) * zoom;
2753 if (pass_along[4]) /* save many images; find TOTAL area covered */
2755 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2756 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2758 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2759 switch (dist_units) {
2760 case VIK_UNITS_DISTANCE_KILOMETRES:
2761 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2763 case VIK_UNITS_DISTANCE_MILES:
2764 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2767 label_text = g_strdup_printf ("Just to keep the compiler happy");
2768 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2771 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2772 g_free ( label_text );
2776 * Get an allocated filename (or directory as specified)
2778 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
2781 if ( one_image_only )
2784 if (!vw->save_img_dia) {
2785 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2787 GTK_FILE_CHOOSER_ACTION_SAVE,
2788 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2789 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2792 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
2794 GtkFileFilter *filter;
2795 filter = gtk_file_filter_new ();
2796 gtk_file_filter_set_name ( filter, _("All") );
2797 gtk_file_filter_add_pattern ( filter, "*" );
2798 gtk_file_chooser_add_filter ( chooser, filter );
2800 filter = gtk_file_filter_new ();
2801 gtk_file_filter_set_name ( filter, _("JPG") );
2802 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2803 gtk_file_chooser_add_filter ( chooser, filter );
2805 filter = gtk_file_filter_new ();
2806 gtk_file_filter_set_name ( filter, _("PNG") );
2807 gtk_file_filter_add_mime_type ( filter, "image/png");
2808 gtk_file_chooser_add_filter ( chooser, filter );
2811 gtk_file_chooser_set_filter ( chooser, filter );
2813 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2814 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2817 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
2818 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2819 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
2820 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 ) ) )
2823 gtk_widget_hide ( vw->save_img_dia );
2827 // For some reason this method is only written to work in UTM...
2828 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2829 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2833 if (!vw->save_img_dir_dia) {
2834 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2836 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2837 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2838 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2840 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2841 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2844 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
2845 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2847 gtk_widget_hide ( vw->save_img_dir_dia );
2852 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
2854 /* todo: default for answers inside VikWindow or static (thruout instance) */
2855 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2856 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2858 GTK_RESPONSE_REJECT,
2860 GTK_RESPONSE_ACCEPT,
2862 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2863 GtkWidget *png_radio, *jpeg_radio;
2864 GtkWidget *current_window_button;
2865 gpointer current_window_pass_along[7];
2866 GtkWidget *zoom_label, *zoom_combo;
2867 GtkWidget *total_size_label;
2869 /* only used if (!one_image_only) */
2870 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2872 width_label = gtk_label_new ( _("Width (pixels):") );
2873 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
2874 height_label = gtk_label_new ( _("Height (pixels):") );
2875 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
2877 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
2879 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2880 /* TODO: separate xzoom and yzoom factors */
2881 zoom_combo = create_zoom_combo_all_levels();
2883 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
2884 gint active = 2 + round ( log (mpp) / log (2) );
2886 // Can we not hard code size here?
2889 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
2891 total_size_label = gtk_label_new ( NULL );
2893 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2894 current_window_pass_along [0] = vw;
2895 current_window_pass_along [1] = width_spin;
2896 current_window_pass_along [2] = height_spin;
2897 current_window_pass_along [3] = zoom_combo;
2898 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
2899 current_window_pass_along [5] = NULL;
2900 current_window_pass_along [6] = total_size_label;
2901 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
2903 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
2904 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
2906 if ( ! vw->draw_image_save_as_png )
2907 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
2909 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
2910 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
2911 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
2912 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
2914 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), win_warning_label, FALSE, FALSE, 0);
2916 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
2917 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
2918 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
2919 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
2920 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_combo, FALSE, FALSE, 0);
2922 if ( ! one_image_only )
2924 GtkWidget *tiles_width_label, *tiles_height_label;
2926 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
2927 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2928 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
2929 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2930 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
2931 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
2932 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
2933 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
2935 current_window_pass_along [4] = tiles_width_spin;
2936 current_window_pass_along [5] = tiles_height_spin;
2937 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2938 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2940 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
2941 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2942 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2943 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2945 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
2947 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2949 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
2951 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
2953 gtk_widget_hide ( GTK_WIDGET(dialog) );
2955 gchar *fn = draw_image_filename ( vw, one_image_only );
2959 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
2960 gdouble zoom = pow (2, active-2 );
2962 if ( one_image_only )
2963 save_image_file ( vw, fn,
2964 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2965 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2967 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
2969 // NB is in UTM mode ATM
2970 save_image_dir ( vw, fn,
2971 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2972 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2974 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
2975 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
2976 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
2981 gtk_widget_destroy ( GTK_WIDGET(dialog) );
2985 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
2987 draw_to_image_file ( vw, TRUE );
2990 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
2992 draw_to_image_file ( vw, FALSE );
2995 #if GTK_CHECK_VERSION(2,10,0)
2996 static void print_cb ( GtkAction *a, VikWindow *vw )
2998 a_print(vw, vw->viking_vvp);
3002 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3003 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3005 VikViewportDrawMode drawmode;
3006 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3007 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3009 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3010 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3012 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3013 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3015 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3016 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3019 g_critical("Houston, we've had a problem.");
3023 if ( !vw->only_updating_coord_mode_ui )
3025 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3026 if ( olddrawmode != drawmode )
3028 /* this takes care of coord mode too */
3029 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3030 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3031 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3032 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3033 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3040 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3042 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3043 g_assert(check_box);
3044 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3045 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3049 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3051 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3052 g_assert(check_box);
3053 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3054 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3058 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3060 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3061 g_assert(check_box);
3062 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3063 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3067 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3069 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3070 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3071 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3072 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3073 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3075 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3076 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3080 gtk_widget_destroy ( colorsd );
3083 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3085 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3086 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3087 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3088 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3089 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3091 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3092 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3096 gtk_widget_destroy ( colorsd );
3101 /***********************************************************************************************
3103 ***********************************************************************************************/
3105 static GtkActionEntry entries[] = {
3106 { "File", NULL, N_("_File"), 0, 0, 0 },
3107 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3108 { "View", NULL, N_("_View"), 0, 0, 0 },
3109 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3110 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3111 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3112 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3113 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3114 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3115 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3117 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3118 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3119 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3120 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3121 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3122 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3123 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3124 #ifdef VIK_CONFIG_GOOGLE
3125 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
3127 #ifdef VIK_CONFIG_OPENSTREETMAP
3128 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3130 #ifdef VIK_CONFIG_GEOCACHES
3131 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3133 #ifdef VIK_CONFIG_GEOTAG
3134 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3136 #ifdef VIK_CONFIG_GEONAMES
3137 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3139 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3140 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3141 { "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 },
3142 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3144 #if GTK_CHECK_VERSION(2,10,0)
3145 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3148 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3149 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3151 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3152 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3153 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3154 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3155 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3156 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3157 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3158 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3159 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3160 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3161 { "Zoom0.25", NULL, N_("0.25"), NULL, NULL, (GCallback)draw_zoom_cb },
3162 { "Zoom0.5", NULL, N_("0.5"), NULL, NULL, (GCallback)draw_zoom_cb },
3163 { "Zoom1", NULL, N_("1"), NULL, NULL, (GCallback)draw_zoom_cb },
3164 { "Zoom2", NULL, N_("2"), NULL, NULL, (GCallback)draw_zoom_cb },
3165 { "Zoom4", NULL, N_("4"), NULL, NULL, (GCallback)draw_zoom_cb },
3166 { "Zoom8", NULL, N_("8"), NULL, NULL, (GCallback)draw_zoom_cb },
3167 { "Zoom16", NULL, N_("16"), NULL, NULL, (GCallback)draw_zoom_cb },
3168 { "Zoom32", NULL, N_("32"), NULL, NULL, (GCallback)draw_zoom_cb },
3169 { "Zoom64", NULL, N_("64"), NULL, NULL, (GCallback)draw_zoom_cb },
3170 { "Zoom128", NULL, N_("128"), NULL, NULL, (GCallback)draw_zoom_cb },
3171 { "Zoom256", NULL, N_("256"), NULL, NULL, (GCallback)draw_zoom_cb },
3172 { "Zoom512", NULL, N_("512"), NULL, NULL, (GCallback)draw_zoom_cb },
3173 { "Zoom1024", NULL, N_("1024"), NULL, NULL, (GCallback)draw_zoom_cb },
3174 { "Zoom2048", NULL, N_("2048"), NULL, NULL, (GCallback)draw_zoom_cb },
3175 { "Zoom4096", NULL, N_("4096"), NULL, NULL, (GCallback)draw_zoom_cb },
3176 { "Zoom8192", NULL, N_("8192"), NULL, NULL, (GCallback)draw_zoom_cb },
3177 { "Zoom16384", NULL, N_("16384"), NULL, NULL, (GCallback)draw_zoom_cb },
3178 { "Zoom32768", NULL, N_("32768"), NULL, NULL, (GCallback)draw_zoom_cb },
3179 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3180 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3181 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3182 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3183 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3185 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3186 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3187 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3188 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3189 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3190 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3191 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3192 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3193 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3195 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3196 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3200 /* FIXME use VIEWPORT_DRAWMODE values */
3201 static GtkRadioActionEntry mode_entries[] = {
3202 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3203 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3204 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3205 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3208 static GtkToggleActionEntry toggle_entries[] = {
3209 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3210 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3211 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3212 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3213 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3214 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3215 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3216 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3219 #include "menu.xml.h"
3220 static void window_create_ui( VikWindow *window )
3223 GtkActionGroup *action_group;
3224 GtkAccelGroup *accel_group;
3227 GtkIconFactory *icon_factory;
3228 GtkIconSet *icon_set;
3229 GtkRadioActionEntry *tools = NULL, *radio;
3232 uim = gtk_ui_manager_new ();
3235 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3236 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3237 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3238 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3241 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3242 g_error_free (error);
3246 action_group = gtk_action_group_new ("MenuActions");
3247 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3248 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3249 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3250 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3252 icon_factory = gtk_icon_factory_new ();
3253 gtk_icon_factory_add_default (icon_factory);
3255 register_vik_icons(icon_factory);
3257 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3258 // so that it can be applied to the UI in one action group add function call below
3260 for (i=0; i<window->vt->n_tools; i++) {
3261 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3262 radio = &tools[ntools];
3264 *radio = window->vt->tools[i].ti.radioActionEntry;
3265 radio->value = ntools;
3268 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3269 GtkActionEntry action;
3270 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3271 vik_layer_get_interface(i)->name,
3272 vik_layer_get_interface(i)->name,
3273 GTK_UI_MANAGER_MENUITEM, FALSE);
3275 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3276 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3277 gtk_icon_set_unref (icon_set);
3279 action.name = vik_layer_get_interface(i)->name;
3280 action.stock_id = vik_layer_get_interface(i)->name;
3281 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3282 action.accelerator = vik_layer_get_interface(i)->accelerator;
3283 action.tooltip = NULL;
3284 action.callback = (GCallback)menu_addlayer_cb;
3285 gtk_action_group_add_actions(action_group, &action, 1, window);
3287 if ( vik_layer_get_interface(i)->tools_count ) {
3288 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3289 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3292 // Further tool copying for to apply to the UI, also apply menu UI setup
3293 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3294 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3295 radio = &tools[ntools];
3298 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3299 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3300 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3301 GTK_UI_MANAGER_MENUITEM, FALSE);
3302 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3303 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3304 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3305 GTK_UI_MANAGER_TOOLITEM, FALSE);
3307 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3309 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3310 // Overwrite with actual number to use
3311 radio->value = ntools;
3314 g_object_unref (icon_factory);
3316 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3319 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3321 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3322 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3323 GtkAction *action = gtk_action_group_get_action(action_group,
3324 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3325 g_object_set(action, "sensitive", FALSE, NULL);
3328 window->action_group = action_group;
3330 accel_group = gtk_ui_manager_get_accel_group (uim);
3331 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3332 gtk_ui_manager_ensure_update (uim);
3334 setup_recent_files(window);
3338 // TODO - add method to add tool icons defined from outside this file
3339 // and remove the reverse dependency on icon definition from this file
3341 const GdkPixdata *data;
3344 { &mover_22_pixbuf, "vik-icon-pan" },
3345 { &zoom_18_pixbuf, "vik-icon-zoom" },
3346 { &ruler_18_pixbuf, "vik-icon-ruler" },
3347 { &select_18_pixbuf, "vik-icon-select" },
3348 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3349 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3350 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3351 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3352 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3353 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3354 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3355 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3356 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3357 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3358 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3361 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3364 register_vik_icons (GtkIconFactory *icon_factory)
3366 GtkIconSet *icon_set;
3369 for (i = 0; i < n_stock_icons; i++) {
3370 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3371 stock_icons[i].data, FALSE, NULL ));
3372 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3373 gtk_icon_set_unref (icon_set);
3377 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3379 return vw->selected_vtl;
3382 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3384 vw->selected_vtl = vtl;
3385 vw->containing_vtl = vtl;
3387 vw->selected_track = NULL;
3388 vw->selected_tracks = NULL;
3389 vw->selected_waypoint = NULL;
3390 vw->selected_waypoints = NULL;
3391 // Set highlight thickness
3392 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3395 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
3397 return vw->selected_tracks;
3400 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3402 vw->selected_tracks = ght;
3403 vw->containing_vtl = vtl;
3405 vw->selected_vtl = NULL;
3406 vw->selected_track = NULL;
3407 vw->selected_waypoint = NULL;
3408 vw->selected_waypoints = NULL;
3409 // Set highlight thickness
3410 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3413 gpointer vik_window_get_selected_track ( VikWindow *vw )
3415 return vw->selected_track;
3418 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
3420 vw->selected_track = vt;
3421 vw->containing_vtl = vtl;
3423 vw->selected_vtl = NULL;
3424 vw->selected_tracks = NULL;
3425 vw->selected_waypoint = NULL;
3426 vw->selected_waypoints = NULL;
3427 // Set highlight thickness
3428 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3431 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
3433 return vw->selected_waypoints;
3436 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3438 vw->selected_waypoints = ght;
3439 vw->containing_vtl = vtl;
3441 vw->selected_vtl = NULL;
3442 vw->selected_track = NULL;
3443 vw->selected_tracks = NULL;
3444 vw->selected_waypoint = NULL;
3447 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3449 return vw->selected_waypoint;
3452 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
3454 vw->selected_waypoint = vwp;
3455 vw->containing_vtl = vtl;
3457 vw->selected_vtl = NULL;
3458 vw->selected_track = NULL;
3459 vw->selected_tracks = NULL;
3460 vw->selected_waypoints = NULL;
3463 gboolean vik_window_clear_highlight ( VikWindow *vw )
3465 gboolean need_redraw = FALSE;
3466 if ( vw->selected_vtl != NULL ) {
3467 vw->selected_vtl = NULL;
3470 if ( vw->selected_track != NULL ) {
3471 vw->selected_track = NULL;
3474 if ( vw->selected_tracks != NULL ) {
3475 vw->selected_tracks = NULL;
3478 if ( vw->selected_waypoint != NULL ) {
3479 vw->selected_waypoint = NULL;
3482 if ( vw->selected_waypoints != NULL ) {
3483 vw->selected_waypoints = NULL;
3489 GThread *vik_window_get_thread ( VikWindow *vw )