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;
160 /* tool management state */
163 guint16 tool_layer_id;
164 guint16 tool_tool_id;
166 GtkActionGroup *action_group;
171 guint draw_image_width, draw_image_height;
172 gboolean draw_image_save_as_png;
177 GtkWidget *open_dia, *save_dia;
178 GtkWidget *save_img_dia, *save_img_dir_dia;
180 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
184 /* half-drawn update */
186 VikCoord trigger_center;
188 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
189 /* Only one of these items can be selected at the same time */
190 gpointer selected_vtl; /* notionally VikTrwLayer */
191 GHashTable *selected_tracks;
192 gpointer selected_track; /* notionally VikTrack */
193 GHashTable *selected_waypoints;
194 gpointer selected_waypoint; /* notionally VikWaypoint */
195 /* only use for individual track or waypoint */
196 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
197 gpointer containing_vtl; /* notionally VikTrwLayer */
211 VW_OPENWINDOW_SIGNAL,
212 VW_STATUSBAR_UPDATE_SIGNAL,
216 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
218 // TODO get rid of this as this is unnecessary duplication...
219 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
221 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
223 VikViewport * vik_window_viewport(VikWindow *vw)
225 return(vw->viking_vvp);
228 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
230 return(vw->viking_vlp);
234 * Returns the statusbar for the window
236 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
238 return vw->viking_vs;
242 * For signalling the update from a background thread
244 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
246 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message, vs_type );
250 * For the actual statusbar update!
252 static gboolean statusbar_idle_update ( gpointer indata )
254 gpointer *data = indata;
255 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
260 * Update statusbar in the main thread
262 static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
264 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
265 static gpointer data[3];
266 data[0] = vw->viking_vs;
267 data[1] = (gchar*) message;
268 data[2] = GINT_TO_POINTER(vs_type);
269 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
272 // Actual signal handlers
273 static void destroy_window ( GtkWidget *widget,
276 if ( ! --window_count )
280 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type )
282 window_statusbar_update ( vw, message, vs_type );
285 VikWindow *vik_window_new_window ()
287 if ( window_count < MAX_WINDOWS )
289 VikWindow *vw = window_new ();
291 g_signal_connect (G_OBJECT (vw), "destroy",
292 G_CALLBACK (destroy_window), NULL);
293 g_signal_connect (G_OBJECT (vw), "newwindow",
294 G_CALLBACK (vik_window_new_window), NULL);
295 g_signal_connect (G_OBJECT (vw), "openwindow",
296 G_CALLBACK (open_window), NULL);
297 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
298 G_CALLBACK (statusbar_update), vw);
300 gtk_widget_show_all ( GTK_WIDGET(vw) );
309 static void open_window ( VikWindow *vw, GSList *files )
311 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
312 GSList *cur_file = files;
314 // Only open a new window if a viking file
315 gchar *file_name = cur_file->data;
316 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
317 VikWindow *newvw = vik_window_new_window ();
319 vik_window_open_file ( newvw, file_name, TRUE );
322 vik_window_open_file ( vw, file_name, change_fn );
325 cur_file = g_slist_next (cur_file);
327 g_slist_free (files);
331 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
333 int i, j, tool_count;
334 VikLayerInterface *layer_interface;
336 if (!vw->action_group) return;
338 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
340 layer_interface = vik_layer_get_interface(i);
341 tool_count = layer_interface->tools_count;
343 for (j = 0; j < tool_count; j++) {
344 action = gtk_action_group_get_action(vw->action_group,
345 layer_interface->tools[j].radioActionEntry.name);
346 g_object_set(action, "sensitive", i == vl->type, NULL);
351 static void window_finalize ( GObject *gob )
353 VikWindow *vw = VIK_WINDOW(gob);
354 g_return_if_fail ( vw != NULL );
356 a_background_remove_window ( vw );
358 G_OBJECT_CLASS(parent_class)->finalize(gob);
362 static void vik_window_class_init ( VikWindowClass *klass )
365 GObjectClass *object_class;
367 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);
368 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);
369 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);
371 object_class = G_OBJECT_CLASS (klass);
373 object_class->finalize = window_finalize;
375 parent_class = g_type_class_peek_parent (klass);
379 static void zoom_changed (GtkMenuShell *menushell,
382 VikWindow *vw = VIK_WINDOW (user_data);
384 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
385 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
387 gdouble zoom_request = pow (2, active-2 );
389 // But has it really changed?
390 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
391 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
392 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
393 // Force drawing update
398 static GtkWidget * create_zoom_menu_all_levels ()
400 GtkWidget *menu = gtk_menu_new ();
401 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768", NULL };
404 for (i = 0 ; itemLabels[i] != NULL ; i++)
406 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
407 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
408 gtk_widget_show (item);
409 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
415 static GtkWidget *create_zoom_combo_all_levels ()
417 GtkWidget *zoom_combo = gtk_combo_box_new_text();
418 GtkComboBox *combo = GTK_COMBO_BOX ( zoom_combo );
419 gtk_combo_box_append_text ( combo, "0.25");
420 gtk_combo_box_append_text ( combo, "0.5");
421 gtk_combo_box_append_text ( combo, "1");
422 gtk_combo_box_append_text ( combo, "2");
423 gtk_combo_box_append_text ( combo, "4");
424 gtk_combo_box_append_text ( combo, "8");
425 gtk_combo_box_append_text ( combo, "16");
426 gtk_combo_box_append_text ( combo, "32");
427 gtk_combo_box_append_text ( combo, "64");
428 gtk_combo_box_append_text ( combo, "128");
429 gtk_combo_box_append_text ( combo, "256");
430 gtk_combo_box_append_text ( combo, "512");
431 gtk_combo_box_append_text ( combo, "1024");
432 gtk_combo_box_append_text ( combo, "2048");
433 gtk_combo_box_append_text ( combo, "4096");
434 gtk_combo_box_append_text ( combo, "8192");
435 gtk_combo_box_append_text ( combo, "16384");
436 gtk_combo_box_append_text ( combo, "32768");
438 gtk_widget_set_tooltip_text (GTK_WIDGET (combo), _("Select zoom level"));
442 static gint zoom_popup_handler (GtkWidget *widget)
446 g_return_val_if_fail (widget != NULL, FALSE);
447 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
449 /* The "widget" is the menu that was supplied when
450 * g_signal_connect_swapped() was called.
452 menu = GTK_MENU (widget);
454 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
455 1, gtk_get_current_event_time());
459 static void vik_window_init ( VikWindow *vw )
461 GtkWidget *main_vbox;
464 vw->action_group = NULL;
466 vw->viking_vvp = vik_viewport_new();
467 vw->viking_vlp = vik_layers_panel_new();
468 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
469 vw->viking_vs = vik_statusbar_new();
471 vw->vt = toolbox_create(vw);
472 window_create_ui(vw);
473 window_set_filename (vw, NULL);
474 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
476 // Set the default tool
477 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
481 vw->modified = FALSE;
482 vw->only_updating_coord_mode_ui = FALSE;
484 vw->pan_move = FALSE;
485 vw->pan_x = vw->pan_y = -1;
486 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
487 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
488 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
490 main_vbox = gtk_vbox_new(FALSE, 1);
491 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
493 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
494 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
495 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
496 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
498 vik_ext_tools_add_menu_items ( vw, vw->uim );
500 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
501 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ();
502 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
503 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
504 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
506 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
508 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
509 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
510 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 );
511 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
512 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
513 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
514 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
515 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
517 // Allow key presses to be processed anywhere
518 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
520 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
522 hpaned = gtk_hpaned_new ();
523 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
524 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
526 /* This packs the button into the window (a gtk container). */
527 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
529 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
531 a_background_add_window ( vw );
535 vw->save_img_dia = NULL;
536 vw->save_img_dir_dia = NULL;
538 // Store the thread value so comparisons can be made to determine the gdk update method
539 // Hopefully we are storing the main thread value here :)
540 // [ATM any window initialization is always be performed by the main thread]
541 vw->thread = g_thread_self();
544 static VikWindow *window_new ()
546 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
550 * Update the displayed map
551 * Only update the top most visible map layer
552 * ATM this assumes (as per defaults) the top most map has full alpha setting
553 * such that other other maps even though they may be active will not be seen
554 * It's more complicated to work out which maps are actually visible due to alpha settings
555 * and overkill for this simple refresh method.
557 static void simple_map_update ( VikWindow *vw, gboolean only_new )
559 // Find the most relevent single map layer to operate on
560 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
562 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
566 * This is the global key press handler
567 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
569 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
571 // The keys handled here are not in the menuing system for a couple of reasons:
572 // . Keeps the menu size compact (alebit at expense of discoverably)
573 // . Allows differing key bindings to perform the same actions
575 // First decide if key events are related to the maps layer
576 gboolean map_download = FALSE;
577 gboolean map_download_only_new = TRUE; // Only new or reload
579 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
581 // Standard 'Refresh' keys: F5 or Ctrl+r
582 // Note 'F5' is actually handled via draw_refresh_cb() later on
583 // (not 'R' it's 'r' notice the case difference!!)
584 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
586 map_download_only_new = TRUE;
588 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
589 // Note the use of uppercase R here since shift key has been pressed
590 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
591 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
593 map_download_only_new = FALSE;
596 if ( map_download ) {
597 simple_map_update ( vw, map_download_only_new );
600 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
601 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
602 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
603 if ( vl && ltype == vl->type )
604 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
607 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
608 if ( vw->current_tool < TOOL_LAYER ) {
609 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
610 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
611 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
615 /* Restore Main Menu via Escape key if the user has hidden it */
616 /* This key is more likely to be used as they may not remember the function key */
617 if ( event->keyval == GDK_Escape ) {
618 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
620 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
622 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
623 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
624 return TRUE; /* handled keypress */
629 return FALSE; /* don't handle the keypress */
632 static gboolean delete_event( VikWindow *vw )
634 #ifdef VIKING_PROMPT_IF_MODIFIED
641 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
642 _("Do you want to save the changes you made to the document \"%s\"?\n"
644 "Your changes will be lost if you don't save them."),
645 window_get_filename ( vw ) ) );
646 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
647 switch ( gtk_dialog_run ( dia ) )
649 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
650 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
651 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
658 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
660 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
663 static void draw_update ( VikWindow *vw )
669 static void draw_sync ( VikWindow *vw )
671 vik_viewport_sync(vw->viking_vvp);
676 * Split the status update, as sometimes only need to update the tool part
677 * also on initialization the zoom related stuff is not ready to be used
679 static void draw_status_tool ( VikWindow *vw )
681 if ( vw->current_tool == TOOL_LAYER )
682 // Use tooltip rather than the internal name as the tooltip is i8n
683 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 );
685 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
688 static void draw_status ( VikWindow *vw )
690 static gchar zoom_level[22];
691 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
692 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
693 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
695 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
697 if ( (int)xmpp - xmpp < 0.0 )
698 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
700 /* xmpp should be a whole number so don't show useless .000 bit */
701 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
703 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
705 draw_status_tool ( vw );
708 void vik_window_set_redraw_trigger(VikLayer *vl)
710 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
715 static void window_configure_event ( VikWindow *vw )
717 static int first = 1;
720 // This is a hack to set the cursor corresponding to the first tool
721 // FIXME find the correct way to initialize both tool and its cursor
722 const GdkCursor *cursor = NULL;
724 cursor = toolbox_get_cursor(vw->vt, "Pan");
725 /* We set cursor, even if it is NULL: it resets to default */
726 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
730 static void draw_redraw ( VikWindow *vw )
732 VikCoord old_center = vw->trigger_center;
733 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
734 VikLayer *new_trigger = vw->trigger;
736 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
739 ; /* do nothing -- have to redraw everything. */
740 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
741 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
743 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
746 vik_viewport_clear ( vw->viking_vvp);
747 vik_layers_panel_draw_all ( vw->viking_vlp );
748 vik_viewport_draw_scale ( vw->viking_vvp );
749 vik_viewport_draw_copyright ( vw->viking_vvp );
750 vik_viewport_draw_centermark ( vw->viking_vvp );
751 vik_viewport_draw_logo ( vw->viking_vvp );
753 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
756 gboolean draw_buf_done = TRUE;
758 static gboolean draw_buf(gpointer data)
760 gpointer *pass_along = data;
762 gdk_draw_drawable (pass_along[0], pass_along[1],
763 pass_along[2], 0, 0, 0, 0, -1, -1);
764 draw_buf_done = TRUE;
770 /* Mouse event handlers ************************************************************************/
772 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
774 /* set panning origin */
775 vw->pan_move = FALSE;
776 vw->pan_x = (gint) event->x;
777 vw->pan_y = (gint) event->y;
780 static void draw_click (VikWindow *vw, GdkEventButton *event)
782 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
784 /* middle button pressed. we reserve all middle button and scroll events
785 * for panning and zooming; tools only get left/right/movement
787 if ( event->button == 2) {
788 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
789 // Tool still may need to do something (such as disable something)
790 toolbox_click(vw->vt, event);
791 vik_window_pan_click ( vw, event );
794 toolbox_click(vw->vt, event);
798 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
800 if ( vw->pan_x != -1 ) {
801 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
802 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
804 vw->pan_x = event->x;
805 vw->pan_y = event->y;
810 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
812 static VikCoord coord;
813 static struct UTM utm;
814 static struct LatLon ll;
815 #define BUFFER_SIZE 50
816 static char pointer_buf[BUFFER_SIZE];
817 gchar *lat = NULL, *lon = NULL;
820 VikDemInterpol interpol_method;
822 /* This is a hack, but work far the best, at least for single pointer systems.
823 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
825 gdk_window_get_pointer (event->window, &x, &y, NULL);
829 toolbox_move(vw->vt, event);
831 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
832 vik_coord_to_utm ( &coord, &utm );
834 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
835 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
836 // ZONE[N|S] EASTING NORTHING
837 lat = g_malloc(4*sizeof(gchar));
838 // NB zone is stored in a char but is an actual number
839 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
840 lon = g_malloc(16*sizeof(gchar));
841 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
844 a_coords_utm_to_latlon ( &utm, &ll );
845 a_coords_latlon_to_string ( &ll, &lat, &lon );
848 /* Change interpolate method according to scale */
849 zoom = vik_viewport_get_zoom(vw->viking_vvp);
851 interpol_method = VIK_DEM_INTERPOL_NONE;
852 else if (zoom >= 1.0)
853 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
855 interpol_method = VIK_DEM_INTERPOL_BEST;
856 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
857 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
858 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
860 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
863 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
868 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
870 vik_window_pan_move ( vw, event );
872 /* This is recommended by the GTK+ documentation, but does not work properly.
873 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
874 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
876 /* gdk_event_request_motions ( event ); */
879 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
881 if ( vw->pan_move == FALSE )
882 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
884 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
885 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
886 vw->pan_move = FALSE;
887 vw->pan_x = vw->pan_y = -1;
891 static void draw_release ( VikWindow *vw, GdkEventButton *event )
893 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
895 if ( event->button == 2 ) { /* move / pan */
896 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
897 // Tool still may need to do something (such as reenable something)
898 toolbox_release(vw->vt, event);
899 vik_window_pan_release ( vw, event );
902 toolbox_release(vw->vt, event);
906 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
908 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
909 if ( modifiers == GDK_CONTROL_MASK ) {
910 /* control == pan up & down */
911 if ( event->direction == GDK_SCROLL_UP )
912 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
914 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 );
915 } else if ( modifiers == GDK_SHIFT_MASK ) {
916 /* shift == pan left & right */
917 if ( event->direction == GDK_SCROLL_UP )
918 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
920 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 );
921 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
922 // This zoom is on the center position
923 if ( event->direction == GDK_SCROLL_UP )
924 vik_viewport_zoom_in (vw->viking_vvp);
926 vik_viewport_zoom_out (vw->viking_vvp);
928 /* make sure mouse is still over the same point on the map when we zoom */
931 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
932 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
933 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
934 if ( event->direction == GDK_SCROLL_UP )
935 vik_viewport_zoom_in (vw->viking_vvp);
937 vik_viewport_zoom_out(vw->viking_vvp);
938 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
939 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
940 center_y + (y - event->y) );
948 /********************************************************************************
950 ********************************************************************************/
951 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
955 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
956 GdkGC *thickgc = gdk_gc_new(d);
958 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
959 gdouble dx = (x2-x1)/len*10;
960 gdouble dy = (y2-y1)/len*10;
961 gdouble c = cos(DEG2RAD(15.0));
962 gdouble s = sin(DEG2RAD(15.0));
964 gdouble baseangle = 0;
967 /* draw line with arrow ends */
969 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
970 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
971 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
974 a_viewport_clip_line(&x1, &y1, &x2, &y2);
975 gdk_draw_line(d, gc, x1, y1, x2, y2);
977 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
978 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
979 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
980 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
981 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
982 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
988 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
992 gdk_gc_copy(thickgc, gc);
993 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
994 gdk_color_parse("#2255cc", &color);
995 gdk_gc_set_rgb_fg_color(thickgc, &color);
997 gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - RAD2DEG(baseangle))*64, -RAD2DEG(angle)*64);
1000 gdk_gc_copy(thickgc, gc);
1001 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1002 for (i=0; i<180; i++) {
1003 c = cos(DEG2RAD(i)*2 + baseangle);
1004 s = sin(DEG2RAD(i)*2 + baseangle);
1007 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1009 gdouble ticksize = 2*CW;
1010 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1014 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1015 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1016 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1017 c = (CR+CW*2)*cos(baseangle);
1018 s = (CR+CW*2)*sin(baseangle);
1019 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1020 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1023 #define LABEL(x, y, w, h) { \
1024 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1025 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1026 gdk_draw_layout(d, gc, (x), (y), pl); }
1028 gint wd, hd, xd, yd;
1029 gint wb, hb, xb, yb;
1031 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1032 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
1033 pango_layout_set_text(pl, "N", -1);
1034 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1036 /* draw label with distance */
1037 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1038 switch (dist_units) {
1039 case VIK_UNITS_DISTANCE_KILOMETRES:
1040 if (distance >= 1000 && distance < 100000) {
1041 g_sprintf(str, "%3.2f km", distance/1000.0);
1042 } else if (distance < 1000) {
1043 g_sprintf(str, "%d m", (int)distance);
1045 g_sprintf(str, "%d km", (int)distance/1000);
1048 case VIK_UNITS_DISTANCE_MILES:
1049 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1050 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1051 } else if (distance < VIK_MILES_TO_METERS(1)) {
1052 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1054 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1058 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1061 pango_layout_set_text(pl, str, -1);
1063 pango_layout_get_pixel_size ( pl, &wd, &hd );
1065 xd = (x1+x2)/2 + dy;
1066 yd = (y1+y2)/2 - hd/2 - dx;
1068 xd = (x1+x2)/2 - dy;
1069 yd = (y1+y2)/2 - hd/2 + dx;
1072 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1077 LABEL(xd, yd, wd, hd);
1079 /* draw label with bearing */
1080 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1081 pango_layout_set_text(pl, str, -1);
1082 pango_layout_get_pixel_size ( pl, &wb, &hb );
1083 xb = x1 + CR*cos(angle-M_PI_2);
1084 yb = y1 + CR*sin(angle-M_PI_2);
1086 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1092 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1093 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1097 LABEL(xb, yb, wb, hb);
1101 g_object_unref ( G_OBJECT ( pl ) );
1102 g_object_unref ( G_OBJECT ( labgc ) );
1103 g_object_unref ( G_OBJECT ( thickgc ) );
1109 gboolean has_oldcoord;
1111 } ruler_tool_state_t;
1113 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1115 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1118 s->has_oldcoord = FALSE;
1122 static void ruler_destroy (ruler_tool_state_t *s)
1127 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1132 if ( event->button == 1 ) {
1133 gchar *lat=NULL, *lon=NULL;
1134 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1135 vik_coord_to_latlon ( &coord, &ll );
1136 a_coords_latlon_to_string ( &ll, &lat, &lon );
1137 if ( s->has_oldcoord ) {
1138 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1139 switch (dist_units) {
1140 case VIK_UNITS_DISTANCE_KILOMETRES:
1141 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1143 case VIK_UNITS_DISTANCE_MILES:
1144 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1147 temp = g_strdup_printf ("Just to keep the compiler happy");
1148 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1151 s->has_oldcoord = FALSE;
1154 temp = g_strdup_printf ( "%s %s", lat, lon );
1155 s->has_oldcoord = TRUE;
1158 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1161 s->oldcoord = coord;
1164 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1165 draw_update ( s->vw );
1167 return VIK_LAYER_TOOL_ACK;
1170 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1172 VikViewport *vvp = s->vvp;
1173 VikWindow *vw = s->vw;
1179 if ( s->has_oldcoord ) {
1180 int oldx, oldy, w1, h1, w2, h2;
1181 static GdkPixmap *buf = NULL;
1182 gchar *lat=NULL, *lon=NULL;
1183 w1 = vik_viewport_get_width(vvp);
1184 h1 = vik_viewport_get_height(vvp);
1186 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1188 gdk_drawable_get_size(buf, &w2, &h2);
1189 if (w1 != w2 || h1 != h2) {
1190 g_object_unref ( G_OBJECT ( buf ) );
1191 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1194 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1195 vik_coord_to_latlon ( &coord, &ll );
1196 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1198 gdk_draw_drawable (buf, GTK_WIDGET(vvp)->style->black_gc,
1199 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1200 draw_ruler(vvp, buf, GTK_WIDGET(vvp)->style->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1201 if (draw_buf_done) {
1202 static gpointer pass_along[3];
1203 pass_along[0] = GTK_WIDGET(vvp)->window;
1204 pass_along[1] = GTK_WIDGET(vvp)->style->black_gc;
1205 pass_along[2] = buf;
1206 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1207 draw_buf_done = FALSE;
1209 a_coords_latlon_to_string(&ll, &lat, &lon);
1210 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1211 switch (dist_units) {
1212 case VIK_UNITS_DISTANCE_KILOMETRES:
1213 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1215 case VIK_UNITS_DISTANCE_MILES:
1216 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1219 temp = g_strdup_printf ("Just to keep the compiler happy");
1220 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1222 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1225 return VIK_LAYER_TOOL_ACK;
1228 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1230 return VIK_LAYER_TOOL_ACK;
1233 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1235 draw_update ( s->vw );
1238 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1240 if (event->keyval == GDK_Escape) {
1241 s->has_oldcoord = FALSE;
1242 ruler_deactivate ( vl, s );
1245 // Regardless of whether we used it, return false so other GTK things may use it
1249 static VikToolInterface ruler_tool =
1250 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1251 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1252 (VikToolConstructorFunc) ruler_create,
1253 (VikToolDestructorFunc) ruler_destroy,
1254 (VikToolActivationFunc) NULL,
1255 (VikToolActivationFunc) ruler_deactivate,
1256 (VikToolMouseFunc) ruler_click,
1257 (VikToolMouseMoveFunc) ruler_move,
1258 (VikToolMouseFunc) ruler_release,
1259 (VikToolKeyFunc) ruler_key_press,
1261 GDK_CURSOR_IS_PIXMAP,
1262 &cursor_ruler_pixbuf };
1263 /*** end ruler code ********************************************************/
1267 /********************************************************************************
1269 ********************************************************************************/
1274 // Track zoom bounds for zoom tool with shift modifier:
1275 gboolean bounds_active;
1278 } zoom_tool_state_t;
1281 * In case the screen size has changed
1283 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1287 // Allocate a drawing area the size of the viewport
1288 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1289 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1291 if ( !zts->pixmap ) {
1293 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1296 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1298 if ( w1 != w2 || h1 != h2 ) {
1299 // Has changed - delete and recreate with new values
1300 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1301 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1305 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1307 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1312 zts->bounds_active = FALSE;
1316 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1319 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1323 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1325 zts->vw->modified = TRUE;
1326 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1330 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1331 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1333 gboolean skip_update = FALSE;
1335 zts->bounds_active = FALSE;
1337 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1338 // This zoom is on the center position
1339 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1340 if ( event->button == 1 )
1341 vik_viewport_zoom_in (zts->vw->viking_vvp);
1342 else if ( event->button == 3 )
1343 vik_viewport_zoom_out (zts->vw->viking_vvp);
1345 else if ( modifiers == GDK_CONTROL_MASK ) {
1346 // This zoom is to recenter on the mouse position
1347 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1348 if ( event->button == 1 )
1349 vik_viewport_zoom_in (zts->vw->viking_vvp);
1350 else if ( event->button == 3 )
1351 vik_viewport_zoom_out (zts->vw->viking_vvp);
1353 else if ( modifiers == GDK_SHIFT_MASK ) {
1354 // Get start of new zoom bounds
1355 if ( event->button == 1 ) {
1356 zts->bounds_active = TRUE;
1357 zts->start_x = (gint) event->x;
1358 zts->start_y = (gint) event->y;
1363 /* make sure mouse is still over the same point on the map when we zoom */
1364 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1365 if ( event->button == 1 )
1366 vik_viewport_zoom_in (zts->vw->viking_vvp);
1367 else if ( event->button == 3 )
1368 vik_viewport_zoom_out(zts->vw->viking_vvp);
1369 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1370 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1371 center_x + (x - event->x),
1372 center_y + (y - event->y) );
1376 draw_update ( zts->vw );
1378 return VIK_LAYER_TOOL_ACK;
1381 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1383 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1385 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1386 zoomtool_resize_pixmap ( zts );
1388 // Blank out currently drawn area
1389 gdk_draw_drawable ( zts->pixmap,
1390 GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc,
1391 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1392 0, 0, 0, 0, -1, -1);
1394 // Calculate new box starting point & size in pixels
1395 int xx, yy, width, height;
1396 if ( event->y > zts->start_y ) {
1398 height = event->y-zts->start_y;
1402 height = zts->start_y-event->y;
1404 if ( event->x > zts->start_x ) {
1406 width = event->x-zts->start_x;
1410 width = zts->start_x-event->x;
1414 gdk_draw_rectangle (zts->pixmap, GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, FALSE, xx, yy, width, height);
1416 // Only actually draw when there's time to do so
1417 if (draw_buf_done) {
1418 static gpointer pass_along[3];
1419 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1420 pass_along[1] = GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc;
1421 pass_along[2] = zts->pixmap;
1422 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1423 draw_buf_done = FALSE;
1426 return VIK_LAYER_TOOL_ACK;
1429 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1431 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1433 zts->bounds_active = FALSE;
1435 // Ensure haven't just released on the exact same position
1436 // i.e. probably haven't moved the mouse at all
1437 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1439 VikCoord coord1, coord2;
1440 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1441 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1443 // From the extend of the bounds pick the best zoom level
1444 // c.f. trw_layer_zoom_to_show_latlons()
1445 // Maybe refactor...
1446 struct LatLon ll1, ll2;
1447 vik_coord_to_latlon(&coord1, &ll1);
1448 vik_coord_to_latlon(&coord2, &ll2);
1449 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1450 (ll1.lon+ll2.lon)/2 };
1452 VikCoord new_center;
1453 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1454 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1456 /* Convert into definite 'smallest' and 'largest' positions */
1457 struct LatLon minmin;
1458 if ( ll1.lat < ll2.lat )
1459 minmin.lat = ll1.lat;
1461 minmin.lat = ll2.lat;
1463 struct LatLon maxmax;
1464 if ( ll1.lon > ll2.lon )
1465 maxmax.lon = ll1.lon;
1467 maxmax.lon = ll2.lon;
1469 /* Always recalculate the 'best' zoom level */
1470 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1471 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1473 gdouble min_lat, max_lat, min_lon, max_lon;
1474 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1475 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1476 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1477 /* NB I think the logic used in this test to determine if the bounds is within view
1478 fails if track goes across 180 degrees longitude.
1479 Hopefully that situation is not too common...
1480 Mind you viking doesn't really do edge locations to well anyway */
1481 if ( min_lat < minmin.lat &&
1482 max_lat > minmin.lat &&
1483 min_lon < maxmax.lon &&
1484 max_lon > maxmax.lon )
1485 /* Found within zoom level */
1490 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1493 draw_update ( zts->vw );
1495 return VIK_LAYER_TOOL_ACK;
1498 static VikToolInterface zoom_tool =
1499 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1500 (VikToolConstructorFunc) zoomtool_create,
1501 (VikToolDestructorFunc) zoomtool_destroy,
1502 (VikToolActivationFunc) NULL,
1503 (VikToolActivationFunc) NULL,
1504 (VikToolMouseFunc) zoomtool_click,
1505 (VikToolMouseMoveFunc) zoomtool_move,
1506 (VikToolMouseFunc) zoomtool_release,
1509 GDK_CURSOR_IS_PIXMAP,
1510 &cursor_zoom_pixbuf };
1511 /*** end zoom code ********************************************************/
1513 /********************************************************************************
1515 ********************************************************************************/
1516 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1521 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1523 vw->modified = TRUE;
1524 if ( event->button == 1 )
1525 vik_window_pan_click ( vw, event );
1527 return VIK_LAYER_TOOL_ACK;
1530 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1532 vik_window_pan_move ( vw, event );
1533 return VIK_LAYER_TOOL_ACK;
1536 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1538 if ( event->button == 1 )
1539 vik_window_pan_release ( vw, event );
1540 return VIK_LAYER_TOOL_ACK;
1543 static VikToolInterface pan_tool =
1544 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1545 (VikToolConstructorFunc) pantool_create,
1546 (VikToolDestructorFunc) NULL,
1547 (VikToolActivationFunc) NULL,
1548 (VikToolActivationFunc) NULL,
1549 (VikToolMouseFunc) pantool_click,
1550 (VikToolMouseMoveFunc) pantool_move,
1551 (VikToolMouseFunc) pantool_release,
1555 /*** end pan code ********************************************************/
1557 /********************************************************************************
1559 ********************************************************************************/
1560 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1562 tool_ed_t *t = g_new(tool_ed_t, 1);
1566 t->is_waypoint = FALSE;
1570 static void selecttool_destroy (tool_ed_t *t)
1578 GdkEventButton *event;
1579 tool_ed_t *tool_edit;
1582 static void click_layer_selected (VikLayer *vl, clicker *ck)
1584 /* Do nothing when function call returns true; */
1585 /* i.e. stop on first found item */
1588 if ( vik_layer_get_interface(vl->type)->select_click )
1589 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1592 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1594 /* Only allow selection on primary button */
1595 if ( event->button == 1 ) {
1596 /* Enable click to apply callback to potentially all track/waypoint layers */
1597 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1598 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1601 ck.vvp = t->vw->viking_vvp;
1604 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1607 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1610 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1612 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1613 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1614 gint type = vik_treeview_item_get_type ( vtv, &iter );
1615 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1616 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1618 vik_treeview_item_unselect ( vtv, &iter );
1619 if ( vik_window_clear_highlight ( t->vw ) )
1620 draw_update ( t->vw );
1625 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1627 /* Act on currently selected item to show menu */
1628 if ( t->vw->selected_track || t->vw->selected_waypoint )
1629 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1630 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1633 return VIK_LAYER_TOOL_ACK;
1636 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1638 /* Only allow selection on primary button */
1639 if ( event->button == 1 ) {
1640 // Don't care about vl here
1642 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1643 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1645 return VIK_LAYER_TOOL_ACK;
1648 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1650 /* Only allow selection on primary button */
1651 if ( event->button == 1 ) {
1652 // Don't care about vl here
1654 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1655 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1657 return VIK_LAYER_TOOL_ACK;
1660 static VikToolInterface select_tool =
1661 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1662 (VikToolConstructorFunc) selecttool_create,
1663 (VikToolDestructorFunc) selecttool_destroy,
1664 (VikToolActivationFunc) NULL,
1665 (VikToolActivationFunc) NULL,
1666 (VikToolMouseFunc) selecttool_click,
1667 (VikToolMouseMoveFunc) selecttool_move,
1668 (VikToolMouseFunc) selecttool_release,
1669 (VikToolKeyFunc) NULL,
1674 /*** end select tool code ********************************************************/
1676 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1678 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1679 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1680 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1681 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1682 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1683 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1684 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1685 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1690 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1692 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1693 g_assert(check_box);
1694 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1696 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1698 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1701 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1705 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1708 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1711 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1714 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1718 gchar *s = (gchar *)gtk_action_get_name(a);
1724 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1725 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1726 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1727 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1728 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1733 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1735 VikCoord new_center;
1737 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1738 struct LatLon ll, llold;
1739 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1740 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1741 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1745 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1746 struct UTM utm, utmold;
1747 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1748 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1749 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1754 g_critical("Houston, we've had a problem.");
1758 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1763 * Refresh maps displayed
1765 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1767 // Only get 'new' maps
1768 simple_map_update ( vw, TRUE );
1771 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1774 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1775 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1776 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1778 vw->modified = TRUE;
1784 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1786 a_clipboard_copy_selected ( vw->viking_vlp );
1789 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1791 vik_layers_panel_cut_selected ( vw->viking_vlp );
1792 vw->modified = TRUE;
1795 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1797 if ( a_clipboard_paste ( vw->viking_vlp ) )
1799 vw->modified = TRUE;
1803 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1805 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1806 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1809 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1812 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1814 #if GTK_CHECK_VERSION (2, 14, 0)
1816 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1817 GError *error = NULL;
1818 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1819 if ( !show && !error )
1820 // No error to show, so unlikely this will get called
1821 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1824 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 );
1825 g_error_free ( error );
1829 a_dialog_error_msg ( GTK_WINDOW(vw), "Help is not available in this build." ); // Unlikely to happen so not going to bother with I8N
1831 #endif /* WINDOWS */
1834 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1836 a_dialog_about(GTK_WINDOW(vw));
1839 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1841 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1843 vik_layers_panel_delete_selected ( vw->viking_vlp );
1844 vw->modified = TRUE;
1847 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1850 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1852 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1853 g_assert(check_box);
1854 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1856 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1858 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1861 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1863 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1866 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1868 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1870 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1873 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1875 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1878 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1880 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1882 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1885 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1887 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1890 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1892 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1894 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1897 /***************************************
1898 ** tool management routines
1900 ***************************************/
1902 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1904 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1907 vt->active_tool = -1;
1912 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1914 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1915 vt->tools[vt->n_tools].ti = *vti;
1916 vt->tools[vt->n_tools].layer_type = layer_type;
1918 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1921 vt->tools[vt->n_tools].state = NULL;
1926 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1929 for (i=0; i<vt->n_tools; i++) {
1930 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1937 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1939 int tool = toolbox_get_tool(vt, tool_name);
1940 toolbox_tool_t *t = &vt->tools[tool];
1941 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1943 if (tool == vt->n_tools) {
1944 g_critical("trying to activate a non-existent tool...");
1947 /* is the tool already active? */
1948 if (vt->active_tool == tool) {
1952 if (vt->active_tool != -1) {
1953 if (vt->tools[vt->active_tool].ti.deactivate) {
1954 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1957 if (t->ti.activate) {
1958 t->ti.activate(vl, t->state);
1960 vt->active_tool = tool;
1963 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1965 int tool = toolbox_get_tool(vt, tool_name);
1966 toolbox_tool_t *t = &vt->tools[tool];
1967 if (t->ti.cursor == NULL) {
1968 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1969 GError *cursor_load_err = NULL;
1970 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1971 /* TODO: settable offeset */
1972 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1973 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1975 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1978 return t->ti.cursor;
1981 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1983 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1984 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1985 gint ltype = vt->tools[vt->active_tool].layer_type;
1986 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1987 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
1991 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
1993 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1994 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
1995 gint ltype = vt->tools[vt->active_tool].layer_type;
1996 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1997 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
1998 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2002 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2004 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2005 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2006 gint ltype = vt->tools[vt->active_tool].layer_type;
2007 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2008 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2011 /** End tool management ************************************/
2013 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2015 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2018 /* this function gets called whenever a toolbar tool is clicked */
2019 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2021 /* White Magic, my friends ... White Magic... */
2022 int layer_id, tool_id;
2023 const GdkCursor *cursor = NULL;
2025 toolbox_activate(vw->vt, gtk_action_get_name(a));
2027 cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2029 if ( GTK_WIDGET(vw->viking_vvp)->window )
2030 /* We set cursor, even if it is NULL: it resets to default */
2031 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
2033 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2034 vw->current_tool = TOOL_PAN;
2036 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2037 vw->current_tool = TOOL_ZOOM;
2039 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2040 vw->current_tool = TOOL_RULER;
2042 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2043 vw->current_tool = TOOL_SELECT;
2046 /* TODO: only enable tools from active layer */
2047 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2048 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2049 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2050 vw->current_tool = TOOL_LAYER;
2051 vw->tool_layer_id = layer_id;
2052 vw->tool_tool_id = tool_id;
2057 draw_status_tool ( vw );
2060 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2065 g_free ( vw->filename );
2066 if ( filename == NULL )
2068 vw->filename = NULL;
2072 vw->filename = g_strdup(filename);
2075 /* Refresh window's title */
2076 file = window_get_filename ( vw );
2077 title = g_strdup_printf( "%s - Viking", file );
2078 gtk_window_set_title ( GTK_WINDOW(vw), title );
2082 static const gchar *window_get_filename ( VikWindow *vw )
2084 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2087 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2089 GtkWidget *mode_button;
2092 #ifdef VIK_CONFIG_EXPEDIA
2093 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2095 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2096 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2097 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2099 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2100 g_assert ( mode_button );
2105 * vik_window_get_pan_move:
2106 * @vw: some VikWindow
2108 * Retrieves @vw's pan_move.
2110 * Should be removed as soon as possible.
2112 * Returns: @vw's pan_move
2116 gboolean vik_window_get_pan_move ( VikWindow *vw )
2118 return vw->pan_move;
2121 static void on_activate_recent_item (GtkRecentChooser *chooser,
2126 filename = gtk_recent_chooser_get_current_uri (chooser);
2127 if (filename != NULL)
2129 GFile *file = g_file_new_for_uri ( filename );
2130 gchar *path = g_file_get_path ( file );
2131 g_object_unref ( file );
2132 if ( self->filename )
2134 GSList *filenames = NULL;
2135 filenames = g_slist_append ( filenames, path );
2136 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2137 // NB: GSList & contents are freed by main.open_window
2140 vik_window_open_file ( self, path, TRUE );
2148 static void setup_recent_files (VikWindow *self)
2150 GtkRecentManager *manager;
2151 GtkRecentFilter *filter;
2152 GtkWidget *menu, *menu_item;
2154 filter = gtk_recent_filter_new ();
2155 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2156 gtk_recent_filter_add_group(filter, "viking");
2158 manager = gtk_recent_manager_get_default ();
2159 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2160 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2161 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2163 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2164 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2166 g_signal_connect (G_OBJECT (menu), "item-activated",
2167 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2170 static void update_recently_used_document(const gchar *filename)
2172 /* Update Recently Used Document framework */
2173 GtkRecentManager *manager = gtk_recent_manager_get_default();
2174 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2175 gchar *groups[] = {"viking", NULL};
2176 GFile *file = g_file_new_for_commandline_arg(filename);
2177 gchar *uri = g_file_get_uri(file);
2178 gchar *basename = g_path_get_basename(filename);
2179 g_object_unref(file);
2182 recent_data->display_name = basename;
2183 recent_data->description = NULL;
2184 recent_data->mime_type = "text/x-gps-data";
2185 recent_data->app_name = (gchar *) g_get_application_name ();
2186 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2187 recent_data->groups = groups;
2188 recent_data->is_private = FALSE;
2189 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2191 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2196 g_free (recent_data->app_exec);
2197 g_slice_free (GtkRecentData, recent_data);
2200 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2202 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2204 case LOAD_TYPE_READ_FAILURE:
2205 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2207 case LOAD_TYPE_GPSBABEL_FAILURE:
2208 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2210 case LOAD_TYPE_GPX_FAILURE:
2211 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2213 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2214 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2216 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2218 // Since we can process .vik files with issues just show a warning in the status bar
2219 // Not that a user can do much about it... or tells them what this issue is yet...
2220 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2221 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2224 // No break, carry on to show any data
2225 case LOAD_TYPE_VIK_SUCCESS:
2227 GtkWidget *mode_button;
2229 if ( change_filename )
2230 window_set_filename ( vw, filename );
2231 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2232 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. */
2233 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2234 vw->only_updating_coord_mode_ui = FALSE;
2236 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2238 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2239 g_assert ( mode_button );
2240 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2242 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2243 g_assert ( mode_button );
2244 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2246 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2247 g_assert ( mode_button );
2248 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2250 //case LOAD_TYPE_OTHER_SUCCESS:
2252 update_recently_used_document(filename);
2257 static void load_file ( GtkAction *a, VikWindow *vw )
2259 GSList *files = NULL;
2260 GSList *cur_file = NULL;
2262 if (!strcmp(gtk_action_get_name(a), "Open")) {
2265 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2269 g_critical("Houston, we've had a problem.");
2273 if ( ! vw->open_dia )
2275 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2277 GTK_FILE_CHOOSER_ACTION_OPEN,
2278 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2279 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2281 GtkFileFilter *filter;
2282 // NB file filters are listed this way for alphabetical ordering
2283 #ifdef VIK_CONFIG_GEOCACHES
2284 filter = gtk_file_filter_new ();
2285 gtk_file_filter_set_name( filter, _("Geocaching") );
2286 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2287 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2290 filter = gtk_file_filter_new ();
2291 gtk_file_filter_set_name( filter, _("Google Earth") );
2292 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2293 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2295 filter = gtk_file_filter_new ();
2296 gtk_file_filter_set_name( filter, _("GPX") );
2297 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2298 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2300 filter = gtk_file_filter_new ();
2301 gtk_file_filter_set_name( filter, _("Viking") );
2302 gtk_file_filter_add_pattern ( filter, "*.vik" );
2303 gtk_file_filter_add_pattern ( filter, "*.viking" );
2304 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2306 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2307 // However assume this are barely used and thus not worthy of inclusion
2308 // as they'll just make the options too many and have no clear file pattern
2309 // one can always use the all option
2310 filter = gtk_file_filter_new ();
2311 gtk_file_filter_set_name( filter, _("All") );
2312 gtk_file_filter_add_pattern ( filter, "*" );
2313 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2314 // Default to any file - same as before open filters were added
2315 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2317 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2318 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2319 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2321 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2323 gtk_widget_hide ( vw->open_dia );
2324 #ifdef VIKING_PROMPT_IF_MODIFIED
2325 if ( (vw->modified || vw->filename) && newwindow )
2327 if ( vw->filename && newwindow )
2329 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2331 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2332 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2333 gboolean first_vik_file = TRUE;
2335 while ( cur_file ) {
2337 gchar *file_name = cur_file->data;
2338 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2339 // Load first of many .vik files in current window
2340 if ( first_vik_file ) {
2341 vik_window_open_file ( vw, file_name, TRUE );
2342 first_vik_file = FALSE;
2345 // Load each subsequent .vik file in a separate window
2346 VikWindow *newvw = vik_window_new_window ();
2348 vik_window_open_file ( newvw, file_name, TRUE );
2353 vik_window_open_file ( vw, file_name, change_fn );
2356 cur_file = g_slist_next (cur_file);
2358 g_slist_free (files);
2362 gtk_widget_hide ( vw->open_dia );
2365 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2367 gboolean rv = FALSE;
2369 if ( ! vw->save_dia )
2371 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2373 GTK_FILE_CHOOSER_ACTION_SAVE,
2374 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2375 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2377 GtkFileFilter *filter;
2378 filter = gtk_file_filter_new ();
2379 gtk_file_filter_set_name( filter, _("All") );
2380 gtk_file_filter_add_pattern ( filter, "*" );
2381 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2383 filter = gtk_file_filter_new ();
2384 gtk_file_filter_set_name( filter, _("Viking") );
2385 gtk_file_filter_add_pattern ( filter, "*.vik" );
2386 gtk_file_filter_add_pattern ( filter, "*.viking" );
2387 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2388 // Default to a Viking file
2389 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2391 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2392 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2394 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2395 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2396 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2397 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2399 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2401 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2403 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2404 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 ) ) )
2406 window_set_filename ( vw, fn );
2407 rv = window_save ( vw );
2408 vw->modified = FALSE;
2412 g_free ( auto_save_name );
2413 gtk_widget_hide ( vw->save_dia );
2417 static gboolean window_save ( VikWindow *vw )
2419 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2421 update_recently_used_document ( vw->filename );
2426 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2431 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2433 if ( ! vw->filename )
2434 return save_file_as ( NULL, vw );
2437 vw->modified = FALSE;
2438 return window_save ( vw );
2442 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2444 // Via the file menu, acquiring from a GPS makes a new layer
2445 // this has always been the way (not entirely sure if this was the real intention!)
2446 // thus maintain the behaviour ATM.
2447 // Hence explicit setting here (as the value may be changed elsewhere)
2448 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2449 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2452 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2454 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2457 #ifdef VIK_CONFIG_GOOGLE
2458 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2460 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2464 #ifdef VIK_CONFIG_OPENSTREETMAP
2465 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2467 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2470 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
2472 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface );
2476 #ifdef VIK_CONFIG_GEOCACHES
2477 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2479 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2483 #ifdef VIK_CONFIG_GEOTAG
2484 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2486 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2487 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2491 #ifdef VIK_CONFIG_GEONAMES
2492 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
2494 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface );
2498 static void goto_default_location( GtkAction *a, VikWindow *vw)
2501 ll.lat = a_vik_get_default_lat();
2502 ll.lon = a_vik_get_default_long();
2503 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2504 vik_layers_panel_emit_update(vw->viking_vlp);
2508 static void goto_address( GtkAction *a, VikWindow *vw)
2510 a_vik_goto ( vw, vw->viking_vvp );
2511 vik_layers_panel_emit_update ( vw->viking_vlp );
2514 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2519 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2521 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2523 a_preferences_show_window ( GTK_WINDOW(vw) );
2525 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2526 if (wp_icon_size != a_vik_get_use_large_waypoint_icons())
2527 clear_garmin_icon_syms ();
2532 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2534 /* Simplistic repeat of preference setting
2535 Only the name & type are important for setting the preference via this 'external' way */
2536 VikLayerParam pref_lat[] = {
2537 { VIKING_PREFERENCES_NAMESPACE "default_latitude",
2538 VIK_LAYER_PARAM_DOUBLE,
2541 VIK_LAYER_WIDGET_SPINBUTTON,
2546 VikLayerParam pref_lon[] = {
2547 { VIKING_PREFERENCES_NAMESPACE "default_longitude",
2548 VIK_LAYER_PARAM_DOUBLE,
2551 VIK_LAYER_WIDGET_SPINBUTTON,
2557 /* Get current center */
2559 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2561 /* Apply to preferences */
2562 VikLayerParamData vlp_data;
2563 vlp_data.d = ll.lat;
2564 a_preferences_run_setparam (vlp_data, pref_lat);
2565 vlp_data.d = ll.lon;
2566 a_preferences_run_setparam (vlp_data, pref_lon);
2567 /* Remember to save */
2568 a_preferences_save_to_file();
2571 static void clear_cb ( GtkAction *a, VikWindow *vw )
2573 vik_layers_panel_clear ( vw->viking_vlp );
2574 window_set_filename ( vw, NULL );
2578 static void window_close ( GtkAction *a, VikWindow *vw )
2580 if ( ! delete_event ( vw ) )
2581 gtk_widget_destroy ( GTK_WIDGET(vw) );
2584 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2586 if (save_file( NULL, vw)) {
2587 window_close( NULL, vw);
2594 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2596 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2597 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2599 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2600 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2605 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2607 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2608 GdkPixbuf *pixbuf_to_save;
2609 gdouble old_xmpp, old_ympp;
2610 GError *error = NULL;
2612 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2613 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2616 _("Generating image file...") );
2618 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2619 // Ensure dialog shown
2620 gtk_widget_show_all ( msgbox );
2622 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2623 while ( gtk_events_pending() )
2624 gtk_main_iteration ();
2625 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2626 // At least the empty box can give a clue something's going on + the statusbar msg...
2627 // Windows version under Wine OK!
2629 /* backup old zoom & set new */
2630 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2631 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2632 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2634 /* reset width and height: */
2635 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2637 /* draw all layers */
2640 /* save buffer as file. */
2641 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);
2642 if ( !pixbuf_to_save ) {
2643 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2644 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
2648 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2651 g_warning("Unable to write to file %s: %s", fn, error->message );
2652 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
2653 g_error_free (error);
2657 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2659 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2662 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2663 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2664 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2666 /* pretend like nothing happened ;) */
2667 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2668 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2669 vik_viewport_configure ( vw->viking_vvp );
2673 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 )
2675 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2676 gchar *name_of_file = g_malloc ( size );
2678 struct UTM utm_orig, utm;
2680 /* *** copied from above *** */
2681 GdkPixbuf *pixbuf_to_save;
2682 gdouble old_xmpp, old_ympp;
2683 GError *error = NULL;
2685 /* backup old zoom & set new */
2686 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2687 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2688 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2690 /* reset width and height: do this only once for all images (same size) */
2691 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2692 /* *** end copy from above *** */
2694 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2698 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2700 for ( y = 1; y <= tiles_h; y++ )
2702 for ( x = 1; x <= tiles_w; x++ )
2704 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2706 if ( tiles_w & 0x1 )
2707 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2709 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2710 if ( tiles_h & 0x1 ) /* odd */
2711 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2713 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2715 /* move to correct place. */
2716 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2720 /* save buffer as file. */
2721 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);
2722 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2725 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2726 g_error_free (error);
2729 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2733 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2734 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2735 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2736 vik_viewport_configure ( vw->viking_vvp );
2739 g_free ( name_of_file );
2742 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2744 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2745 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2747 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2748 gdouble zoom = pow (2, active-2 );
2750 gdouble width_min, width_max, height_min, height_max;
2753 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2754 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2756 /* TODO: support for xzoom and yzoom values */
2757 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2758 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2760 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2761 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2763 gtk_spin_button_set_value ( width_spin, width );
2764 gtk_spin_button_set_value ( height_spin, height );
2767 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2769 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2771 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2772 gdouble zoom = pow (2, active-2 );
2776 w = gtk_spin_button_get_value(width_spin) * zoom;
2777 h = gtk_spin_button_get_value(height_spin) * zoom;
2778 if (pass_along[4]) /* save many images; find TOTAL area covered */
2780 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2781 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2783 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2784 switch (dist_units) {
2785 case VIK_UNITS_DISTANCE_KILOMETRES:
2786 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2788 case VIK_UNITS_DISTANCE_MILES:
2789 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2792 label_text = g_strdup_printf ("Just to keep the compiler happy");
2793 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2796 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2797 g_free ( label_text );
2801 * Get an allocated filename (or directory as specified)
2803 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
2806 if ( one_image_only )
2809 if (!vw->save_img_dia) {
2810 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2812 GTK_FILE_CHOOSER_ACTION_SAVE,
2813 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2814 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2817 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
2819 GtkFileFilter *filter;
2820 filter = gtk_file_filter_new ();
2821 gtk_file_filter_set_name ( filter, _("All") );
2822 gtk_file_filter_add_pattern ( filter, "*" );
2823 gtk_file_chooser_add_filter ( chooser, filter );
2825 filter = gtk_file_filter_new ();
2826 gtk_file_filter_set_name ( filter, _("JPG") );
2827 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2828 gtk_file_chooser_add_filter ( chooser, filter );
2830 filter = gtk_file_filter_new ();
2831 gtk_file_filter_set_name ( filter, _("PNG") );
2832 gtk_file_filter_add_mime_type ( filter, "image/png");
2833 gtk_file_chooser_add_filter ( chooser, filter );
2836 gtk_file_chooser_set_filter ( chooser, filter );
2838 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2839 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2842 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
2843 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2844 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
2845 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 ) ) )
2848 gtk_widget_hide ( vw->save_img_dia );
2852 // For some reason this method is only written to work in UTM...
2853 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2854 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2858 if (!vw->save_img_dir_dia) {
2859 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2861 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2862 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2863 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2865 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2866 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2869 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
2870 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2872 gtk_widget_hide ( vw->save_img_dir_dia );
2877 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
2879 /* todo: default for answers inside VikWindow or static (thruout instance) */
2880 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2881 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2883 GTK_RESPONSE_REJECT,
2885 GTK_RESPONSE_ACCEPT,
2887 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2888 GtkWidget *png_radio, *jpeg_radio;
2889 GtkWidget *current_window_button;
2890 gpointer current_window_pass_along[7];
2891 GtkWidget *zoom_label, *zoom_combo;
2892 GtkWidget *total_size_label;
2894 /* only used if (!one_image_only) */
2895 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2897 width_label = gtk_label_new ( _("Width (pixels):") );
2898 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
2899 height_label = gtk_label_new ( _("Height (pixels):") );
2900 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
2902 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
2904 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2905 /* TODO: separate xzoom and yzoom factors */
2906 zoom_combo = create_zoom_combo_all_levels();
2908 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
2909 gint active = 2 + round ( log (mpp) / log (2) );
2911 // Can we not hard code size here?
2914 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
2916 total_size_label = gtk_label_new ( NULL );
2918 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2919 current_window_pass_along [0] = vw;
2920 current_window_pass_along [1] = width_spin;
2921 current_window_pass_along [2] = height_spin;
2922 current_window_pass_along [3] = zoom_combo;
2923 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
2924 current_window_pass_along [5] = NULL;
2925 current_window_pass_along [6] = total_size_label;
2926 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
2928 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
2929 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
2931 if ( ! vw->draw_image_save_as_png )
2932 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
2934 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
2935 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
2936 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
2937 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
2939 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), win_warning_label, FALSE, FALSE, 0);
2941 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
2942 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
2943 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
2944 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
2945 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_combo, FALSE, FALSE, 0);
2947 if ( ! one_image_only )
2949 GtkWidget *tiles_width_label, *tiles_height_label;
2951 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
2952 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2953 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
2954 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2955 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
2956 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
2957 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
2958 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
2960 current_window_pass_along [4] = tiles_width_spin;
2961 current_window_pass_along [5] = tiles_height_spin;
2962 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2963 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2965 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
2966 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2967 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2968 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2970 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
2972 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2974 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
2976 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
2978 gtk_widget_hide ( GTK_WIDGET(dialog) );
2980 gchar *fn = draw_image_filename ( vw, one_image_only );
2984 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
2985 gdouble zoom = pow (2, active-2 );
2987 if ( one_image_only )
2988 save_image_file ( vw, fn,
2989 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2990 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2992 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
2994 // NB is in UTM mode ATM
2995 save_image_dir ( vw, fn,
2996 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2997 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2999 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3000 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3001 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3006 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3010 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3012 draw_to_image_file ( vw, TRUE );
3015 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3017 draw_to_image_file ( vw, FALSE );
3020 #if GTK_CHECK_VERSION(2,10,0)
3021 static void print_cb ( GtkAction *a, VikWindow *vw )
3023 a_print(vw, vw->viking_vvp);
3027 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3028 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3030 VikViewportDrawMode drawmode;
3031 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3032 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3034 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3035 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3037 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3038 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3040 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3041 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3044 g_critical("Houston, we've had a problem.");
3048 if ( !vw->only_updating_coord_mode_ui )
3050 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3051 if ( olddrawmode != drawmode )
3053 /* this takes care of coord mode too */
3054 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3055 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3056 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3057 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3058 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3065 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3067 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3068 g_assert(check_box);
3069 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3070 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3074 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3076 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3077 g_assert(check_box);
3078 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3079 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3083 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3085 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3086 g_assert(check_box);
3087 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3088 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3092 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3094 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3095 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3096 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3097 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3098 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3100 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3101 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3105 gtk_widget_destroy ( colorsd );
3108 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3110 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3111 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3112 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3113 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3114 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3116 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3117 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3121 gtk_widget_destroy ( colorsd );
3126 /***********************************************************************************************
3128 ***********************************************************************************************/
3130 static GtkActionEntry entries[] = {
3131 { "File", NULL, N_("_File"), 0, 0, 0 },
3132 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3133 { "View", NULL, N_("_View"), 0, 0, 0 },
3134 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3135 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3136 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3137 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3138 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3139 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3140 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3142 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3143 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3144 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3145 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3146 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3147 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3148 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3149 #ifdef VIK_CONFIG_GOOGLE
3150 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
3152 #ifdef VIK_CONFIG_OPENSTREETMAP
3153 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3154 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3156 #ifdef VIK_CONFIG_GEOCACHES
3157 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3159 #ifdef VIK_CONFIG_GEOTAG
3160 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3162 #ifdef VIK_CONFIG_GEONAMES
3163 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3165 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3166 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3167 { "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 },
3168 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3170 #if GTK_CHECK_VERSION(2,10,0)
3171 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3174 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3175 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3177 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3178 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3179 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3180 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3181 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3182 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3183 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3184 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3185 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3186 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3187 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3188 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3189 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3190 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3191 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3193 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3194 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3195 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3196 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3197 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3198 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3199 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3200 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3201 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3203 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3204 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3208 /* FIXME use VIEWPORT_DRAWMODE values */
3209 static GtkRadioActionEntry mode_entries[] = {
3210 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3211 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3212 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3213 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3216 static GtkToggleActionEntry toggle_entries[] = {
3217 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3218 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3219 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3220 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3221 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3222 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3223 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3224 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3227 #include "menu.xml.h"
3228 static void window_create_ui( VikWindow *window )
3231 GtkActionGroup *action_group;
3232 GtkAccelGroup *accel_group;
3235 GtkIconFactory *icon_factory;
3236 GtkIconSet *icon_set;
3237 GtkRadioActionEntry *tools = NULL, *radio;
3240 uim = gtk_ui_manager_new ();
3243 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3244 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3245 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3246 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3249 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3250 g_error_free (error);
3254 action_group = gtk_action_group_new ("MenuActions");
3255 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3256 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3257 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3258 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3260 icon_factory = gtk_icon_factory_new ();
3261 gtk_icon_factory_add_default (icon_factory);
3263 register_vik_icons(icon_factory);
3265 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3266 // so that it can be applied to the UI in one action group add function call below
3268 for (i=0; i<window->vt->n_tools; i++) {
3269 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3270 radio = &tools[ntools];
3272 *radio = window->vt->tools[i].ti.radioActionEntry;
3273 radio->value = ntools;
3276 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3277 GtkActionEntry action;
3278 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3279 vik_layer_get_interface(i)->name,
3280 vik_layer_get_interface(i)->name,
3281 GTK_UI_MANAGER_MENUITEM, FALSE);
3283 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3284 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3285 gtk_icon_set_unref (icon_set);
3287 action.name = vik_layer_get_interface(i)->name;
3288 action.stock_id = vik_layer_get_interface(i)->name;
3289 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3290 action.accelerator = vik_layer_get_interface(i)->accelerator;
3291 action.tooltip = NULL;
3292 action.callback = (GCallback)menu_addlayer_cb;
3293 gtk_action_group_add_actions(action_group, &action, 1, window);
3295 if ( vik_layer_get_interface(i)->tools_count ) {
3296 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3297 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3300 // Further tool copying for to apply to the UI, also apply menu UI setup
3301 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3302 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3303 radio = &tools[ntools];
3306 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3307 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3308 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3309 GTK_UI_MANAGER_MENUITEM, FALSE);
3310 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3311 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3312 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3313 GTK_UI_MANAGER_TOOLITEM, FALSE);
3315 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3317 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3318 // Overwrite with actual number to use
3319 radio->value = ntools;
3322 g_object_unref (icon_factory);
3324 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3327 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3329 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3330 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3331 GtkAction *action = gtk_action_group_get_action(action_group,
3332 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3333 g_object_set(action, "sensitive", FALSE, NULL);
3336 window->action_group = action_group;
3338 accel_group = gtk_ui_manager_get_accel_group (uim);
3339 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3340 gtk_ui_manager_ensure_update (uim);
3342 setup_recent_files(window);
3346 // TODO - add method to add tool icons defined from outside this file
3347 // and remove the reverse dependency on icon definition from this file
3349 const GdkPixdata *data;
3352 { &mover_22_pixbuf, "vik-icon-pan" },
3353 { &zoom_18_pixbuf, "vik-icon-zoom" },
3354 { &ruler_18_pixbuf, "vik-icon-ruler" },
3355 { &select_18_pixbuf, "vik-icon-select" },
3356 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3357 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3358 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3359 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3360 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3361 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3362 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3363 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3364 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3365 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3366 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3369 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3372 register_vik_icons (GtkIconFactory *icon_factory)
3374 GtkIconSet *icon_set;
3377 for (i = 0; i < n_stock_icons; i++) {
3378 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3379 stock_icons[i].data, FALSE, NULL ));
3380 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3381 gtk_icon_set_unref (icon_set);
3385 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3387 return vw->selected_vtl;
3390 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3392 vw->selected_vtl = vtl;
3393 vw->containing_vtl = vtl;
3395 vw->selected_track = NULL;
3396 vw->selected_tracks = NULL;
3397 vw->selected_waypoint = NULL;
3398 vw->selected_waypoints = NULL;
3399 // Set highlight thickness
3400 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3403 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
3405 return vw->selected_tracks;
3408 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3410 vw->selected_tracks = ght;
3411 vw->containing_vtl = vtl;
3413 vw->selected_vtl = NULL;
3414 vw->selected_track = NULL;
3415 vw->selected_waypoint = NULL;
3416 vw->selected_waypoints = NULL;
3417 // Set highlight thickness
3418 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3421 gpointer vik_window_get_selected_track ( VikWindow *vw )
3423 return vw->selected_track;
3426 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
3428 vw->selected_track = vt;
3429 vw->containing_vtl = vtl;
3431 vw->selected_vtl = NULL;
3432 vw->selected_tracks = NULL;
3433 vw->selected_waypoint = NULL;
3434 vw->selected_waypoints = NULL;
3435 // Set highlight thickness
3436 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3439 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
3441 return vw->selected_waypoints;
3444 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3446 vw->selected_waypoints = ght;
3447 vw->containing_vtl = vtl;
3449 vw->selected_vtl = NULL;
3450 vw->selected_track = NULL;
3451 vw->selected_tracks = NULL;
3452 vw->selected_waypoint = NULL;
3455 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3457 return vw->selected_waypoint;
3460 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
3462 vw->selected_waypoint = vwp;
3463 vw->containing_vtl = vtl;
3465 vw->selected_vtl = NULL;
3466 vw->selected_track = NULL;
3467 vw->selected_tracks = NULL;
3468 vw->selected_waypoints = NULL;
3471 gboolean vik_window_clear_highlight ( VikWindow *vw )
3473 gboolean need_redraw = FALSE;
3474 if ( vw->selected_vtl != NULL ) {
3475 vw->selected_vtl = NULL;
3478 if ( vw->selected_track != NULL ) {
3479 vw->selected_track = NULL;
3482 if ( vw->selected_tracks != NULL ) {
3483 vw->selected_tracks = NULL;
3486 if ( vw->selected_waypoint != NULL ) {
3487 vw->selected_waypoint = NULL;
3490 if ( vw->selected_waypoints != NULL ) {
3491 vw->selected_waypoints = NULL;
3497 GThread *vik_window_get_thread ( VikWindow *vw )