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"
53 #include <glib/gstdio.h>
54 #include <glib/gprintf.h>
55 #include <glib/gi18n.h>
57 #include <gdk/gdkkeysyms.h>
59 // This seems rather arbitary, quite large and pointless
60 // I mean, if you have a thousand windows open;
61 // why not be allowed to open a thousand more...
62 #define MAX_WINDOWS 1024
63 static guint window_count = 0;
65 #define VIKING_WINDOW_WIDTH 1000
66 #define VIKING_WINDOW_HEIGHT 800
67 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
68 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
69 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
71 static void window_finalize ( GObject *gob );
72 static GObjectClass *parent_class;
74 static void window_init ( VikWindow *vw );
75 static void window_class_init ( VikWindowClass *klass );
76 static void window_set_filename ( VikWindow *vw, const gchar *filename );
78 static VikWindow *window_new ();
80 static void draw_update ( VikWindow *vw );
82 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
85 static void open_window ( VikWindow *vw, GSList *files );
86 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type );
87 static void destroy_window ( GtkWidget *widget,
92 static gboolean delete_event( VikWindow *vw );
94 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
96 static void window_configure_event ( VikWindow *vw );
97 static void draw_sync ( VikWindow *vw );
98 static void draw_redraw ( VikWindow *vw );
99 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
100 static void draw_click ( VikWindow *vw, GdkEventButton *event );
101 static void draw_release ( VikWindow *vw, GdkEventButton *event );
102 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
103 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
104 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
105 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
107 static void draw_status ( VikWindow *vw );
109 /* End Drawing Functions */
111 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
112 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
113 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
115 /* tool management */
121 #define TOOL_LAYER_TYPE_NONE -1
126 toolbox_tool_t *tools;
130 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
131 static toolbox_tools_t* toolbox_create(VikWindow *vw);
132 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
133 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
134 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
135 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
136 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
137 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
138 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
142 static void window_create_ui( VikWindow *window );
143 static void register_vik_icons (GtkIconFactory *icon_factory);
146 static void load_file ( GtkAction *a, VikWindow *vw );
147 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
148 static gboolean save_file ( GtkAction *a, VikWindow *vw );
149 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
150 static gboolean window_save ( VikWindow *vw );
154 VikViewport *viking_vvp;
155 VikLayersPanel *viking_vlp;
156 VikStatusbar *viking_vs;
159 GtkComboBox *tb_zoom_combo;
161 GtkItemFactory *item_factory;
163 /* tool management state */
166 guint16 tool_layer_id;
167 guint16 tool_tool_id;
169 GtkActionGroup *action_group;
174 guint draw_image_width, draw_image_height;
175 gboolean draw_image_save_as_png;
180 GtkWidget *open_dia, *save_dia;
181 GtkWidget *save_img_dia, *save_img_dir_dia;
183 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
186 /* half-drawn update */
188 VikCoord trigger_center;
190 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
191 /* Only one of these items can be selected at the same time */
192 gpointer selected_vtl; /* notionally VikTrwLayer */
193 gpointer selected_tracks; /* notionally GList */
194 gpointer selected_track; /* notionally VikTrack */
195 gpointer selected_waypoints; /* notionally GList */
196 gpointer selected_waypoint; /* notionally VikWaypoint */
197 /* only use for individual track or waypoint */
198 ////// NEED TO THINK ABOUT VALIDITY OF THESE //////
199 ////// i.e. what happens when stuff is deleted elsewhere //////
200 ////// Generally seems alright as can not access them //////
201 ////// containing_vtl now seems unecessary //////
202 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
203 gpointer containing_vtl; /* notionally VikTrwLayer */
217 VW_OPENWINDOW_SIGNAL,
218 VW_STATUSBAR_UPDATE_SIGNAL,
222 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
224 // TODO get rid of this as this is unnecessary duplication...
225 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
227 GType vik_window_get_type (void)
229 static GType vw_type = 0;
233 static const GTypeInfo vw_info =
235 sizeof (VikWindowClass),
236 NULL, /* base_init */
237 NULL, /* base_finalize */
238 (GClassInitFunc) window_class_init, /* class_init */
239 NULL, /* class_finalize */
240 NULL, /* class_data */
243 (GInstanceInitFunc) window_init,
245 vw_type = g_type_register_static ( GTK_TYPE_WINDOW, "VikWindow", &vw_info, 0 );
251 VikViewport * vik_window_viewport(VikWindow *vw)
253 return(vw->viking_vvp);
256 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
258 return(vw->viking_vlp);
262 * Returns the statusbar for the window
264 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
266 return vw->viking_vs;
270 * For signalling the update from a background thread
272 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
274 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message, vs_type );
278 * For the actual statusbar update!
280 static gboolean statusbar_idle_update ( gpointer indata )
282 gpointer *data = indata;
283 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
288 * Update statusbar in the main thread
290 static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
292 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
293 static gpointer data[3];
294 data[0] = vw->viking_vs;
295 data[1] = (gchar*) message;
296 data[2] = GINT_TO_POINTER(vs_type);
297 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
300 // Actual signal handlers
301 static void destroy_window ( GtkWidget *widget,
304 if ( ! --window_count )
308 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type )
310 window_statusbar_update ( vw, message, vs_type );
313 VikWindow *vik_window_new_window ()
315 if ( window_count < MAX_WINDOWS )
317 VikWindow *vw = window_new ();
319 g_signal_connect (G_OBJECT (vw), "destroy",
320 G_CALLBACK (destroy_window), NULL);
321 g_signal_connect (G_OBJECT (vw), "newwindow",
322 G_CALLBACK (vik_window_new_window), NULL);
323 g_signal_connect (G_OBJECT (vw), "openwindow",
324 G_CALLBACK (open_window), NULL);
325 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
326 G_CALLBACK (statusbar_update), vw);
328 gtk_widget_show_all ( GTK_WIDGET(vw) );
337 static void open_window ( VikWindow *vw, GSList *files )
339 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
340 GSList *cur_file = files;
342 // Only open a new window if a viking file
343 gchar *file_name = cur_file->data;
344 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
345 VikWindow *newvw = vik_window_new_window ();
347 vik_window_open_file ( newvw, file_name, TRUE );
350 vik_window_open_file ( vw, file_name, change_fn );
353 cur_file = g_slist_next (cur_file);
355 g_slist_free (files);
359 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
361 int i, j, tool_count;
362 VikLayerInterface *layer_interface;
364 if (!vw->action_group) return;
366 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
368 layer_interface = vik_layer_get_interface(i);
369 tool_count = layer_interface->tools_count;
371 for (j = 0; j < tool_count; j++) {
372 action = gtk_action_group_get_action(vw->action_group,
373 layer_interface->tools[j].radioActionEntry.name);
374 g_object_set(action, "sensitive", i == vl->type, NULL);
379 static void window_finalize ( GObject *gob )
381 VikWindow *vw = VIK_WINDOW(gob);
382 g_return_if_fail ( vw != NULL );
384 a_background_remove_window ( vw );
386 G_OBJECT_CLASS(parent_class)->finalize(gob);
390 static void window_class_init ( VikWindowClass *klass )
393 GObjectClass *object_class;
395 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);
396 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);
397 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);
399 object_class = G_OBJECT_CLASS (klass);
401 object_class->finalize = window_finalize;
403 parent_class = g_type_class_peek_parent (klass);
407 static void set_toolbar_zoom ( VikWindow *vw, gdouble mpp )
409 gint active = 2 + ( log (mpp) / log (2) );
410 // Can we not hard code size here?
413 gtk_combo_box_set_active ( vw->tb_zoom_combo, active );
416 static void zoom_changed ( GtkComboBox *combo, VikWindow *vw )
418 gint active = gtk_combo_box_get_active ( combo );
420 // But has it really changed?
421 // Unfortunately this function gets invoked even on manual setting of the combo value
422 gdouble zoom_request = pow (2, active-2 );
423 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
424 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
425 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
426 // Force drawing update
431 static GtkWidget *create_zoom_combo_all_levels ()
433 GtkWidget *zoom_combo = gtk_combo_box_new_text();
434 GtkComboBox *combo = GTK_COMBO_BOX ( zoom_combo );
435 gtk_combo_box_append_text ( combo, "0.25");
436 gtk_combo_box_append_text ( combo, "0.5");
437 gtk_combo_box_append_text ( combo, "1");
438 gtk_combo_box_append_text ( combo, "2");
439 gtk_combo_box_append_text ( combo, "4");
440 gtk_combo_box_append_text ( combo, "8");
441 gtk_combo_box_append_text ( combo, "16");
442 gtk_combo_box_append_text ( combo, "32");
443 gtk_combo_box_append_text ( combo, "64");
444 gtk_combo_box_append_text ( combo, "128");
445 gtk_combo_box_append_text ( combo, "256");
446 gtk_combo_box_append_text ( combo, "512");
447 gtk_combo_box_append_text ( combo, "1024");
448 gtk_combo_box_append_text ( combo, "2048");
449 gtk_combo_box_append_text ( combo, "4096");
450 gtk_combo_box_append_text ( combo, "8192");
451 gtk_combo_box_append_text ( combo, "16384");
452 gtk_combo_box_append_text ( combo, "32768");
454 GtkTooltips *tooltips = gtk_tooltips_new ();
455 gtk_tooltips_set_tip ( tooltips, GTK_WIDGET (combo), _("Select zoom level"), NULL);
459 static void 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" ) );
480 vw->item_factory = NULL;
482 vw->modified = FALSE;
483 vw->only_updating_coord_mode_ui = FALSE;
485 vw->pan_move = FALSE;
486 vw->pan_x = vw->pan_y = -1;
487 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
488 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
489 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
491 main_vbox = gtk_vbox_new(FALSE, 1);
492 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
494 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
495 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
496 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
497 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
499 vik_ext_tools_add_menu_items ( vw, vw->uim );
501 vw->tb_zoom_combo = GTK_COMBO_BOX(create_zoom_combo_all_levels());
503 g_signal_connect ( G_OBJECT(vw->tb_zoom_combo), "changed", G_CALLBACK(zoom_changed), vw );
505 // Add the zoom combo to the toolbar at the end
506 GtkToolItem *tooli = gtk_tool_item_new ();
507 gtk_container_add ( GTK_CONTAINER(tooli), GTK_WIDGET (vw->tb_zoom_combo) );
508 gtk_toolbar_insert ( vw->toolbar, tooli, gtk_toolbar_get_n_items (vw->toolbar) );
510 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
512 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
513 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
514 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 );
515 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
516 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
517 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
518 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
519 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
521 // Allow key presses to be processed anywhere
522 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
524 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
526 hpaned = gtk_hpaned_new ();
527 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
528 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
530 /* This packs the button into the window (a gtk container). */
531 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
533 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
535 a_background_add_window ( vw );
539 vw->save_img_dia = NULL;
540 vw->save_img_dir_dia = NULL;
543 static VikWindow *window_new ()
545 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
549 * Update the displayed map
550 * Only update the top most visible map layer
551 * ATM this assumes (as per defaults) the top most map has full alpha setting
552 * such that other other maps even though they may be active will not be seen
553 * It's more complicated to work out which maps are actually visible due to alpha settings
554 * and overkill for this simple refresh method.
556 static void simple_map_update ( VikWindow *vw, gboolean only_new )
558 // Find the most relevent single map layer to operate on
559 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
561 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
565 * This is the global key press handler
566 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
568 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
570 // The keys handled here are not in the menuing system for a couple of reasons:
571 // . Keeps the menu size compact (alebit at expense of discoverably)
572 // . Allows differing key bindings to perform the same actions
574 // First decide if key events are related to the maps layer
575 gboolean map_download = FALSE;
576 gboolean map_download_only_new = TRUE; // Only new or reload
578 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
580 // Standard 'Refresh' keys: F5 or Ctrl+r
581 // Note 'F5' is actually handled via draw_refresh_cb() later on
582 // (not 'R' it's 'r' notice the case difference!!)
583 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
585 map_download_only_new = TRUE;
587 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
588 // Note the use of uppercase R here since shift key has been pressed
589 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
590 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
592 map_download_only_new = FALSE;
595 if ( map_download ) {
596 simple_map_update ( vw, map_download_only_new );
599 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
600 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
601 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
602 if ( vl && ltype == vl->type )
603 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
606 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
607 if ( vw->current_tool < TOOL_LAYER ) {
608 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
609 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
610 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
614 /* Restore Main Menu via Escape key if the user has hidden it */
615 /* This key is more likely to be used as they may not remember the function key */
616 if ( event->keyval == GDK_Escape ) {
617 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
619 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
621 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
622 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
623 return TRUE; /* handled keypress */
628 return FALSE; /* don't handle the keypress */
631 static gboolean delete_event( VikWindow *vw )
633 #ifdef VIKING_PROMPT_IF_MODIFIED
640 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
641 _("Do you want to save the changes you made to the document \"%s\"?\n"
643 "Your changes will be lost if you don't save them."),
644 vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") ) );
645 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
646 switch ( gtk_dialog_run ( dia ) )
648 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
649 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
650 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
657 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
659 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
662 static void draw_update ( VikWindow *vw )
668 static void draw_sync ( VikWindow *vw )
670 vik_viewport_sync(vw->viking_vvp);
675 * Split the status update, as sometimes only need to update the tool part
676 * also on initialization the zoom related stuff is not ready to be used
678 static void draw_status_tool ( VikWindow *vw )
680 if ( vw->current_tool == TOOL_LAYER )
681 // Use tooltip rather than the internal name as the tooltip is i8n
682 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 );
684 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
687 static void draw_status ( VikWindow *vw )
689 static gchar zoom_level[22];
690 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
691 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
692 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
694 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
696 if ( (int)xmpp - xmpp < 0.0 )
697 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
699 /* xmpp should be a whole number so don't show useless .000 bit */
700 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
702 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
703 // OK maybe not quite in the statusbar - but we have the zoom level so use it
704 set_toolbar_zoom ( vw, xmpp ); // But it's a status of some kind!
706 draw_status_tool ( vw );
709 void vik_window_set_redraw_trigger(VikLayer *vl)
711 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
716 static void window_configure_event ( VikWindow *vw )
718 static int first = 1;
721 // This is a hack to set the cursor corresponding to the first tool
722 // FIXME find the correct way to initialize both tool and its cursor
723 const GdkCursor *cursor = NULL;
725 cursor = toolbox_get_cursor(vw->vt, "Pan");
726 /* We set cursor, even if it is NULL: it resets to default */
727 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
731 static void draw_redraw ( VikWindow *vw )
733 VikCoord old_center = vw->trigger_center;
734 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
735 VikLayer *new_trigger = vw->trigger;
737 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
740 ; /* do nothing -- have to redraw everything. */
741 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
742 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
744 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
747 vik_viewport_clear ( vw->viking_vvp);
748 vik_layers_panel_draw_all ( vw->viking_vlp );
749 vik_viewport_draw_scale ( vw->viking_vvp );
750 vik_viewport_draw_copyright ( vw->viking_vvp );
751 vik_viewport_draw_centermark ( vw->viking_vvp );
752 vik_viewport_draw_logo ( vw->viking_vvp );
754 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
757 gboolean draw_buf_done = TRUE;
759 static gboolean draw_buf(gpointer data)
761 gpointer *pass_along = data;
763 gdk_draw_drawable (pass_along[0], pass_along[1],
764 pass_along[2], 0, 0, 0, 0, -1, -1);
765 draw_buf_done = TRUE;
771 /* Mouse event handlers ************************************************************************/
773 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
775 /* set panning origin */
776 vw->pan_move = FALSE;
777 vw->pan_x = (gint) event->x;
778 vw->pan_y = (gint) event->y;
781 static void draw_click (VikWindow *vw, GdkEventButton *event)
783 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
785 /* middle button pressed. we reserve all middle button and scroll events
786 * for panning and zooming; tools only get left/right/movement
788 if ( event->button == 2) {
789 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
790 // Tool still may need to do something (such as disable something)
791 toolbox_click(vw->vt, event);
792 vik_window_pan_click ( vw, event );
795 toolbox_click(vw->vt, event);
799 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
801 if ( vw->pan_x != -1 ) {
802 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
803 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
805 vw->pan_x = event->x;
806 vw->pan_y = event->y;
811 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
813 static VikCoord coord;
814 static struct UTM utm;
815 static struct LatLon ll;
816 #define BUFFER_SIZE 50
817 static char pointer_buf[BUFFER_SIZE];
818 gchar *lat = NULL, *lon = NULL;
821 VikDemInterpol interpol_method;
823 /* This is a hack, but work far the best, at least for single pointer systems.
824 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
826 gdk_window_get_pointer (event->window, &x, &y, NULL);
830 toolbox_move(vw->vt, event);
832 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
833 vik_coord_to_utm ( &coord, &utm );
835 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
836 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
837 // ZONE[N|S] EASTING NORTHING
838 lat = g_malloc(4*sizeof(gchar));
839 // NB zone is stored in a char but is an actual number
840 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
841 lon = g_malloc(16*sizeof(gchar));
842 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
845 a_coords_utm_to_latlon ( &utm, &ll );
846 a_coords_latlon_to_string ( &ll, &lat, &lon );
849 /* Change interpolate method according to scale */
850 zoom = vik_viewport_get_zoom(vw->viking_vvp);
852 interpol_method = VIK_DEM_INTERPOL_NONE;
853 else if (zoom >= 1.0)
854 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
856 interpol_method = VIK_DEM_INTERPOL_BEST;
857 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
858 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
859 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
861 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
864 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
869 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
871 vik_window_pan_move ( vw, event );
873 /* This is recommended by the GTK+ documentation, but does not work properly.
874 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
875 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
877 /* gdk_event_request_motions ( event ); */
880 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
882 if ( vw->pan_move == FALSE )
883 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
885 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
886 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
887 vw->pan_move = FALSE;
888 vw->pan_x = vw->pan_y = -1;
892 static void draw_release ( VikWindow *vw, GdkEventButton *event )
894 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
896 if ( event->button == 2 ) { /* move / pan */
897 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
898 // Tool still may need to do something (such as reenable something)
899 toolbox_release(vw->vt, event);
900 vik_window_pan_release ( vw, event );
903 toolbox_release(vw->vt, event);
907 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
909 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
910 if ( modifiers == GDK_CONTROL_MASK ) {
911 /* control == pan up & down */
912 if ( event->direction == GDK_SCROLL_UP )
913 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
915 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 );
916 } else if ( modifiers == GDK_SHIFT_MASK ) {
917 /* shift == pan left & right */
918 if ( event->direction == GDK_SCROLL_UP )
919 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
921 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 );
922 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
923 // This zoom is on the center position
924 if ( event->direction == GDK_SCROLL_UP )
925 vik_viewport_zoom_in (vw->viking_vvp);
927 vik_viewport_zoom_out (vw->viking_vvp);
929 /* make sure mouse is still over the same point on the map when we zoom */
932 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
933 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
934 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
935 if ( event->direction == GDK_SCROLL_UP )
936 vik_viewport_zoom_in (vw->viking_vvp);
938 vik_viewport_zoom_out(vw->viking_vvp);
939 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
940 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
941 center_y + (y - event->y) );
949 /********************************************************************************
951 ********************************************************************************/
952 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
956 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
957 GdkGC *thickgc = gdk_gc_new(d);
959 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
960 gdouble dx = (x2-x1)/len*10;
961 gdouble dy = (y2-y1)/len*10;
962 gdouble c = cos(15.0 * M_PI/180.0);
963 gdouble s = sin(15.0 * M_PI/180.0);
965 gdouble baseangle = 0;
968 /* draw line with arrow ends */
970 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
971 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
972 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
975 a_viewport_clip_line(&x1, &y1, &x2, &y2);
976 gdk_draw_line(d, gc, x1, y1, x2, y2);
978 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
979 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
980 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
981 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
982 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
983 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
988 angle = atan2(dy, dx) + M_PI_2;
990 if ( vik_viewport_get_drawmode ( vvp ) == VIK_VIEWPORT_DRAWMODE_UTM) {
996 vik_viewport_screen_to_coord ( vvp, x1, y1, &test );
997 vik_coord_to_latlon ( &test, &ll );
998 ll.lat += vik_viewport_get_ympp ( vvp ) * vik_viewport_get_height ( vvp ) / 11000.0; // about 11km per degree latitude
999 a_coords_latlon_to_utm ( &ll, &u );
1000 vik_coord_load_from_utm ( &test, VIK_VIEWPORT_DRAWMODE_UTM, &u );
1001 vik_viewport_coord_to_screen ( vvp, &test, &tx, &ty );
1003 baseangle = M_PI - atan2(tx-x1, ty-y1);
1014 gdk_gc_copy(thickgc, gc);
1015 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1016 gdk_color_parse("#2255cc", &color);
1017 gdk_gc_set_rgb_fg_color(thickgc, &color);
1019 gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - baseangle*180/M_PI)*64, -angle*180/M_PI*64);
1022 gdk_gc_copy(thickgc, gc);
1023 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1024 for (i=0; i<180; i++) {
1025 c = cos(i*M_PI/90.0 + baseangle);
1026 s = sin(i*M_PI/90.0 + baseangle);
1029 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1031 gdouble ticksize = 2*CW;
1032 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1036 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1037 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1038 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1039 c = (CR+CW*2)*cos(baseangle);
1040 s = (CR+CW*2)*sin(baseangle);
1041 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1042 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1045 #define LABEL(x, y, w, h) { \
1046 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1047 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1048 gdk_draw_layout(d, gc, (x), (y), pl); }
1050 gint wd, hd, xd, yd;
1051 gint wb, hb, xb, yb;
1053 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1054 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
1055 pango_layout_set_text(pl, "N", -1);
1056 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1058 /* draw label with distance */
1059 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1060 switch (dist_units) {
1061 case VIK_UNITS_DISTANCE_KILOMETRES:
1062 if (distance >= 1000 && distance < 100000) {
1063 g_sprintf(str, "%3.2f km", distance/1000.0);
1064 } else if (distance < 1000) {
1065 g_sprintf(str, "%d m", (int)distance);
1067 g_sprintf(str, "%d km", (int)distance/1000);
1070 case VIK_UNITS_DISTANCE_MILES:
1071 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1072 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1073 } else if (distance < VIK_MILES_TO_METERS(1)) {
1074 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1076 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1080 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1083 pango_layout_set_text(pl, str, -1);
1085 pango_layout_get_pixel_size ( pl, &wd, &hd );
1087 xd = (x1+x2)/2 + dy;
1088 yd = (y1+y2)/2 - hd/2 - dx;
1090 xd = (x1+x2)/2 - dy;
1091 yd = (y1+y2)/2 - hd/2 + dx;
1094 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1099 LABEL(xd, yd, wd, hd);
1101 /* draw label with bearing */
1102 g_sprintf(str, "%3.1f°", angle*180.0/M_PI);
1103 pango_layout_set_text(pl, str, -1);
1104 pango_layout_get_pixel_size ( pl, &wb, &hb );
1105 xb = x1 + CR*cos(angle-M_PI_2);
1106 yb = y1 + CR*sin(angle-M_PI_2);
1108 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1114 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1115 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1119 LABEL(xb, yb, wb, hb);
1123 g_object_unref ( G_OBJECT ( pl ) );
1124 g_object_unref ( G_OBJECT ( labgc ) );
1125 g_object_unref ( G_OBJECT ( thickgc ) );
1131 gboolean has_oldcoord;
1133 } ruler_tool_state_t;
1135 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1137 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1140 s->has_oldcoord = FALSE;
1144 static void ruler_destroy (ruler_tool_state_t *s)
1149 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1154 if ( event->button == 1 ) {
1155 gchar *lat=NULL, *lon=NULL;
1156 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1157 vik_coord_to_latlon ( &coord, &ll );
1158 a_coords_latlon_to_string ( &ll, &lat, &lon );
1159 if ( s->has_oldcoord ) {
1160 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1161 switch (dist_units) {
1162 case VIK_UNITS_DISTANCE_KILOMETRES:
1163 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1165 case VIK_UNITS_DISTANCE_MILES:
1166 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1169 temp = g_strdup_printf ("Just to keep the compiler happy");
1170 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1173 s->has_oldcoord = FALSE;
1176 temp = g_strdup_printf ( "%s %s", lat, lon );
1177 s->has_oldcoord = TRUE;
1180 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1183 s->oldcoord = coord;
1186 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1187 draw_update ( s->vw );
1189 return VIK_LAYER_TOOL_ACK;
1192 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1194 VikViewport *vvp = s->vvp;
1195 VikWindow *vw = s->vw;
1201 if ( s->has_oldcoord ) {
1202 int oldx, oldy, w1, h1, w2, h2;
1203 static GdkPixmap *buf = NULL;
1204 gchar *lat=NULL, *lon=NULL;
1205 w1 = vik_viewport_get_width(vvp);
1206 h1 = vik_viewport_get_height(vvp);
1208 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1210 gdk_drawable_get_size(buf, &w2, &h2);
1211 if (w1 != w2 || h1 != h2) {
1212 g_object_unref ( G_OBJECT ( buf ) );
1213 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1216 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1217 vik_coord_to_latlon ( &coord, &ll );
1218 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1220 gdk_draw_drawable (buf, GTK_WIDGET(vvp)->style->black_gc,
1221 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1222 draw_ruler(vvp, buf, GTK_WIDGET(vvp)->style->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1223 if (draw_buf_done) {
1224 static gpointer pass_along[3];
1225 pass_along[0] = GTK_WIDGET(vvp)->window;
1226 pass_along[1] = GTK_WIDGET(vvp)->style->black_gc;
1227 pass_along[2] = buf;
1228 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1229 draw_buf_done = FALSE;
1231 a_coords_latlon_to_string(&ll, &lat, &lon);
1232 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1233 switch (dist_units) {
1234 case VIK_UNITS_DISTANCE_KILOMETRES:
1235 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1237 case VIK_UNITS_DISTANCE_MILES:
1238 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1241 temp = g_strdup_printf ("Just to keep the compiler happy");
1242 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1244 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1247 return VIK_LAYER_TOOL_ACK;
1250 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1252 return VIK_LAYER_TOOL_ACK;
1255 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1257 draw_update ( s->vw );
1260 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1262 if (event->keyval == GDK_Escape) {
1263 s->has_oldcoord = FALSE;
1264 ruler_deactivate ( vl, s );
1267 // Regardless of whether we used it, return false so other GTK things may use it
1271 static VikToolInterface ruler_tool =
1272 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1273 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1274 (VikToolConstructorFunc) ruler_create,
1275 (VikToolDestructorFunc) ruler_destroy,
1276 (VikToolActivationFunc) NULL,
1277 (VikToolActivationFunc) ruler_deactivate,
1278 (VikToolMouseFunc) ruler_click,
1279 (VikToolMouseMoveFunc) ruler_move,
1280 (VikToolMouseFunc) ruler_release,
1281 (VikToolKeyFunc) ruler_key_press,
1283 GDK_CURSOR_IS_PIXMAP,
1284 &cursor_ruler_pixbuf };
1285 /*** end ruler code ********************************************************/
1289 /********************************************************************************
1291 ********************************************************************************/
1296 // Track zoom bounds for zoom tool with shift modifier:
1297 gboolean bounds_active;
1300 } zoom_tool_state_t;
1303 * In case the screen size has changed
1305 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1309 // Allocate a drawing area the size of the viewport
1310 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1311 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1313 if ( !zts->pixmap ) {
1315 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1318 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1320 if ( w1 != w2 || h1 != h2 ) {
1321 // Has changed - delete and recreate with new values
1322 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1323 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1327 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1329 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1334 zts->bounds_active = FALSE;
1338 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1341 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1345 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1347 zts->vw->modified = TRUE;
1348 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1352 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1353 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1355 gboolean skip_update = FALSE;
1357 zts->bounds_active = FALSE;
1359 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1360 // This zoom is on the center position
1361 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1362 if ( event->button == 1 )
1363 vik_viewport_zoom_in (zts->vw->viking_vvp);
1364 else if ( event->button == 3 )
1365 vik_viewport_zoom_out (zts->vw->viking_vvp);
1367 else if ( modifiers == GDK_CONTROL_MASK ) {
1368 // This zoom is to recenter on the mouse position
1369 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1370 if ( event->button == 1 )
1371 vik_viewport_zoom_in (zts->vw->viking_vvp);
1372 else if ( event->button == 3 )
1373 vik_viewport_zoom_out (zts->vw->viking_vvp);
1375 else if ( modifiers == GDK_SHIFT_MASK ) {
1376 // Get start of new zoom bounds
1377 if ( event->button == 1 ) {
1378 zts->bounds_active = TRUE;
1379 zts->start_x = (gint) event->x;
1380 zts->start_y = (gint) event->y;
1385 /* make sure mouse is still over the same point on the map when we zoom */
1386 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1387 if ( event->button == 1 )
1388 vik_viewport_zoom_in (zts->vw->viking_vvp);
1389 else if ( event->button == 3 )
1390 vik_viewport_zoom_out(zts->vw->viking_vvp);
1391 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1392 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1393 center_x + (x - event->x),
1394 center_y + (y - event->y) );
1398 draw_update ( zts->vw );
1400 return VIK_LAYER_TOOL_ACK;
1403 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1405 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1407 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1408 zoomtool_resize_pixmap ( zts );
1410 // Blank out currently drawn area
1411 gdk_draw_drawable ( zts->pixmap,
1412 GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc,
1413 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1414 0, 0, 0, 0, -1, -1);
1416 // Calculate new box starting point & size in pixels
1417 int xx, yy, width, height;
1418 if ( event->y > zts->start_y ) {
1420 height = event->y-zts->start_y;
1424 height = zts->start_y-event->y;
1426 if ( event->x > zts->start_x ) {
1428 width = event->x-zts->start_x;
1432 width = zts->start_x-event->x;
1436 gdk_draw_rectangle (zts->pixmap, GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, FALSE, xx, yy, width, height);
1438 // Only actually draw when there's time to do so
1439 if (draw_buf_done) {
1440 static gpointer pass_along[3];
1441 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1442 pass_along[1] = GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc;
1443 pass_along[2] = zts->pixmap;
1444 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1445 draw_buf_done = FALSE;
1448 return VIK_LAYER_TOOL_ACK;
1451 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1453 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1455 zts->bounds_active = FALSE;
1457 // Ensure haven't just released on the exact same position
1458 // i.e. probably haven't moved the mouse at all
1459 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1461 VikCoord coord1, coord2;
1462 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1463 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1465 // From the extend of the bounds pick the best zoom level
1466 // c.f. trw_layer_zoom_to_show_latlons()
1467 // Maybe refactor...
1468 struct LatLon ll1, ll2;
1469 vik_coord_to_latlon(&coord1, &ll1);
1470 vik_coord_to_latlon(&coord2, &ll2);
1471 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1472 (ll1.lon+ll2.lon)/2 };
1474 VikCoord new_center;
1475 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1476 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1478 /* Convert into definite 'smallest' and 'largest' positions */
1479 struct LatLon minmin;
1480 if ( ll1.lat < ll2.lat )
1481 minmin.lat = ll1.lat;
1483 minmin.lat = ll2.lat;
1485 struct LatLon maxmax;
1486 if ( ll1.lon > ll2.lon )
1487 maxmax.lon = ll1.lon;
1489 maxmax.lon = ll2.lon;
1491 /* Always recalculate the 'best' zoom level */
1492 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1493 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1495 gdouble min_lat, max_lat, min_lon, max_lon;
1496 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1497 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1498 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1499 /* NB I think the logic used in this test to determine if the bounds is within view
1500 fails if track goes across 180 degrees longitude.
1501 Hopefully that situation is not too common...
1502 Mind you viking doesn't really do edge locations to well anyway */
1503 if ( min_lat < minmin.lat &&
1504 max_lat > minmin.lat &&
1505 min_lon < maxmax.lon &&
1506 max_lon > maxmax.lon )
1507 /* Found within zoom level */
1512 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1515 draw_update ( zts->vw );
1517 return VIK_LAYER_TOOL_ACK;
1520 static VikToolInterface zoom_tool =
1521 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1522 (VikToolConstructorFunc) zoomtool_create,
1523 (VikToolDestructorFunc) zoomtool_destroy,
1524 (VikToolActivationFunc) NULL,
1525 (VikToolActivationFunc) NULL,
1526 (VikToolMouseFunc) zoomtool_click,
1527 (VikToolMouseMoveFunc) zoomtool_move,
1528 (VikToolMouseFunc) zoomtool_release,
1531 GDK_CURSOR_IS_PIXMAP,
1532 &cursor_zoom_pixbuf };
1533 /*** end zoom code ********************************************************/
1535 /********************************************************************************
1537 ********************************************************************************/
1538 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1543 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1545 vw->modified = TRUE;
1546 if ( event->button == 1 )
1547 vik_window_pan_click ( vw, event );
1549 return VIK_LAYER_TOOL_ACK;
1552 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1554 vik_window_pan_move ( vw, event );
1555 return VIK_LAYER_TOOL_ACK;
1558 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1560 if ( event->button == 1 )
1561 vik_window_pan_release ( vw, event );
1562 return VIK_LAYER_TOOL_ACK;
1565 static VikToolInterface pan_tool =
1566 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1567 (VikToolConstructorFunc) pantool_create,
1568 (VikToolDestructorFunc) NULL,
1569 (VikToolActivationFunc) NULL,
1570 (VikToolActivationFunc) NULL,
1571 (VikToolMouseFunc) pantool_click,
1572 (VikToolMouseMoveFunc) pantool_move,
1573 (VikToolMouseFunc) pantool_release,
1577 /*** end pan code ********************************************************/
1579 /********************************************************************************
1581 ********************************************************************************/
1582 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1584 tool_ed_t *t = g_new(tool_ed_t, 1);
1588 t->is_waypoint = FALSE;
1592 static void selecttool_destroy (tool_ed_t *t)
1600 GdkEventButton *event;
1601 tool_ed_t *tool_edit;
1604 static void click_layer_selected (VikLayer *vl, clicker *ck)
1606 /* Do nothing when function call returns true; */
1607 /* i.e. stop on first found item */
1610 if ( vik_layer_get_interface(vl->type)->select_click )
1611 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1614 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1616 /* Only allow selection on primary button */
1617 if ( event->button == 1 ) {
1618 /* Enable click to apply callback to potentially all track/waypoint layers */
1619 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1620 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1623 ck.vvp = t->vw->viking_vvp;
1626 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1629 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1632 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1634 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1635 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1636 gint type = vik_treeview_item_get_type ( vtv, &iter );
1637 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1638 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1640 vik_treeview_item_unselect ( vtv, &iter );
1641 if ( vik_window_clear_highlight ( t->vw ) )
1642 draw_update ( t->vw );
1647 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1649 /* Act on currently selected item to show menu */
1650 if ( t->vw->selected_track || t->vw->selected_waypoint )
1651 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1652 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1655 return VIK_LAYER_TOOL_ACK;
1658 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1660 /* Only allow selection on primary button */
1661 if ( event->button == 1 ) {
1662 // Don't care about vl here
1664 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1665 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1667 return VIK_LAYER_TOOL_ACK;
1670 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1672 /* Only allow selection on primary button */
1673 if ( event->button == 1 ) {
1674 // Don't care about vl here
1676 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1677 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1679 return VIK_LAYER_TOOL_ACK;
1682 static VikToolInterface select_tool =
1683 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1684 (VikToolConstructorFunc) selecttool_create,
1685 (VikToolDestructorFunc) selecttool_destroy,
1686 (VikToolActivationFunc) NULL,
1687 (VikToolActivationFunc) NULL,
1688 (VikToolMouseFunc) selecttool_click,
1689 (VikToolMouseMoveFunc) selecttool_move,
1690 (VikToolMouseFunc) selecttool_release,
1691 (VikToolKeyFunc) NULL,
1696 /*** end select tool code ********************************************************/
1698 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1700 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1701 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1702 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1703 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1704 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1705 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1706 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1707 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1712 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1714 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1715 g_assert(check_box);
1716 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1718 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1720 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1723 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1727 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1730 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1733 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1736 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1740 gchar *s = (gchar *)gtk_action_get_name(a);
1746 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1747 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1748 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1749 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1750 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1755 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1757 VikCoord new_center;
1759 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1760 struct LatLon ll, llold;
1761 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1762 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1763 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1767 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1768 struct UTM utm, utmold;
1769 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1770 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1771 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1776 g_critical("Houston, we've had a problem.");
1780 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1785 * Refresh maps displayed
1787 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1789 // Only get 'new' maps
1790 simple_map_update ( vw, TRUE );
1793 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1796 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1797 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1798 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1800 vw->modified = TRUE;
1806 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1808 a_clipboard_copy_selected ( vw->viking_vlp );
1811 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1813 vik_layers_panel_cut_selected ( vw->viking_vlp );
1814 vw->modified = TRUE;
1817 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1819 if ( a_clipboard_paste ( vw->viking_vlp ) )
1821 vw->modified = TRUE;
1825 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1827 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1828 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1831 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1834 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1836 #if GTK_CHECK_VERSION (2, 14, 0)
1838 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1839 GError *error = NULL;
1840 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1841 if ( !show && !error )
1842 // No error to show, so unlikely this will get called
1843 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1846 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 );
1847 g_error_free ( error );
1851 a_dialog_error_msg ( GTK_WINDOW(vw), "Help is not available in this build." ); // Unlikely to happen so not going to bother with I8N
1853 #endif /* WINDOWS */
1856 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1858 a_dialog_about(GTK_WINDOW(vw));
1861 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1863 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1865 vik_layers_panel_delete_selected ( vw->viking_vlp );
1866 vw->modified = TRUE;
1869 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1872 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1874 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1875 g_assert(check_box);
1876 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1878 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1880 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1883 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1885 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1888 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1890 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1892 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1895 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1897 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1900 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1902 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1904 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1907 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1909 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1912 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1914 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1916 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1919 /***************************************
1920 ** tool management routines
1922 ***************************************/
1924 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1926 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1929 vt->active_tool = -1;
1934 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1936 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1937 vt->tools[vt->n_tools].ti = *vti;
1938 vt->tools[vt->n_tools].layer_type = layer_type;
1940 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1943 vt->tools[vt->n_tools].state = NULL;
1948 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1951 for (i=0; i<vt->n_tools; i++) {
1952 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1959 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1961 int tool = toolbox_get_tool(vt, tool_name);
1962 toolbox_tool_t *t = &vt->tools[tool];
1963 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1965 if (tool == vt->n_tools) {
1966 g_critical("trying to activate a non-existent tool...");
1969 /* is the tool already active? */
1970 if (vt->active_tool == tool) {
1974 if (vt->active_tool != -1) {
1975 if (vt->tools[vt->active_tool].ti.deactivate) {
1976 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1979 if (t->ti.activate) {
1980 t->ti.activate(vl, t->state);
1982 vt->active_tool = tool;
1985 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1987 int tool = toolbox_get_tool(vt, tool_name);
1988 toolbox_tool_t *t = &vt->tools[tool];
1989 if (t->ti.cursor == NULL) {
1990 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1991 GError *cursor_load_err = NULL;
1992 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1993 /* TODO: settable offeset */
1994 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1995 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1997 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2000 return t->ti.cursor;
2003 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2005 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2006 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2007 gint ltype = vt->tools[vt->active_tool].layer_type;
2008 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2009 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2013 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2015 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2016 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2017 gint ltype = vt->tools[vt->active_tool].layer_type;
2018 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2019 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2020 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2024 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2026 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2027 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2028 gint ltype = vt->tools[vt->active_tool].layer_type;
2029 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2030 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2033 /** End tool management ************************************/
2035 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2037 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2040 /* this function gets called whenever a toolbar tool is clicked */
2041 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2043 /* White Magic, my friends ... White Magic... */
2044 int layer_id, tool_id;
2045 const GdkCursor *cursor = NULL;
2047 toolbox_activate(vw->vt, gtk_action_get_name(a));
2049 cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2051 if ( GTK_WIDGET(vw->viking_vvp)->window )
2052 /* We set cursor, even if it is NULL: it resets to default */
2053 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
2055 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2056 vw->current_tool = TOOL_PAN;
2058 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2059 vw->current_tool = TOOL_ZOOM;
2061 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2062 vw->current_tool = TOOL_RULER;
2064 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2065 vw->current_tool = TOOL_SELECT;
2068 /* TODO: only enable tools from active layer */
2069 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2070 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2071 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2072 vw->current_tool = TOOL_LAYER;
2073 vw->tool_layer_id = layer_id;
2074 vw->tool_tool_id = tool_id;
2079 draw_status_tool ( vw );
2082 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2087 g_free ( vw->filename );
2088 if ( filename == NULL )
2090 vw->filename = NULL;
2091 file = _("Untitled");
2095 vw->filename = g_strdup(filename);
2096 file = a_file_basename ( filename );
2098 title = g_strdup_printf( "%s - Viking", file );
2099 gtk_window_set_title ( GTK_WINDOW(vw), title );
2103 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2105 GtkWidget *mode_button;
2108 #ifdef VIK_CONFIG_EXPEDIA
2109 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2111 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2112 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2113 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2115 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2116 g_assert ( mode_button );
2121 * vik_window_get_pan_move:
2122 * @vw: some VikWindow
2124 * Retrieves @vw's pan_move.
2126 * Should be removed as soon as possible.
2128 * Returns: @vw's pan_move
2132 gboolean vik_window_get_pan_move ( VikWindow *vw )
2134 return vw->pan_move;
2137 static void on_activate_recent_item (GtkRecentChooser *chooser,
2142 filename = gtk_recent_chooser_get_current_uri (chooser);
2143 if (filename != NULL)
2145 GFile *file = g_file_new_for_uri ( filename );
2146 gchar *path = g_file_get_path ( file );
2147 g_object_unref ( file );
2148 if ( self->filename )
2150 GSList *filenames = NULL;
2151 filenames = g_slist_append ( filenames, path );
2152 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2153 // NB: GSList & contents are freed by main.open_window
2156 vik_window_open_file ( self, path, TRUE );
2164 static void setup_recent_files (VikWindow *self)
2166 GtkRecentManager *manager;
2167 GtkRecentFilter *filter;
2168 GtkWidget *menu, *menu_item;
2170 filter = gtk_recent_filter_new ();
2171 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2172 gtk_recent_filter_add_group(filter, "viking");
2174 manager = gtk_recent_manager_get_default ();
2175 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2176 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2177 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2179 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2180 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2182 g_signal_connect (G_OBJECT (menu), "item-activated",
2183 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2186 static void update_recently_used_document(const gchar *filename)
2188 /* Update Recently Used Document framework */
2189 GtkRecentManager *manager = gtk_recent_manager_get_default();
2190 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2191 gchar *groups[] = {"viking", NULL};
2192 GFile *file = g_file_new_for_commandline_arg(filename);
2193 gchar *uri = g_file_get_uri(file);
2194 gchar *basename = g_path_get_basename(filename);
2195 g_object_unref(file);
2198 recent_data->display_name = basename;
2199 recent_data->description = NULL;
2200 recent_data->mime_type = "text/x-gps-data";
2201 recent_data->app_name = (gchar *) g_get_application_name ();
2202 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2203 recent_data->groups = groups;
2204 recent_data->is_private = FALSE;
2205 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2207 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2212 g_free (recent_data->app_exec);
2213 g_slice_free (GtkRecentData, recent_data);
2216 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2218 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2220 case LOAD_TYPE_READ_FAILURE:
2221 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2223 case LOAD_TYPE_GPSBABEL_FAILURE:
2224 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2226 case LOAD_TYPE_GPX_FAILURE:
2227 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2229 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2230 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2232 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2234 // Since we can process .vik files with issues just show a warning in the status bar
2235 // Not that a user can do much about it... or tells them what this issue is yet...
2236 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2237 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2240 // No break, carry on to show any data
2241 case LOAD_TYPE_VIK_SUCCESS:
2243 GtkWidget *mode_button;
2245 if ( change_filename )
2246 window_set_filename ( vw, filename );
2247 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2248 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. */
2249 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2250 vw->only_updating_coord_mode_ui = FALSE;
2252 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2254 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2255 g_assert ( mode_button );
2256 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2258 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2259 g_assert ( mode_button );
2260 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2262 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2263 g_assert ( mode_button );
2264 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2266 //case LOAD_TYPE_OTHER_SUCCESS:
2268 update_recently_used_document(filename);
2273 static void load_file ( GtkAction *a, VikWindow *vw )
2275 GSList *files = NULL;
2276 GSList *cur_file = NULL;
2278 if (!strcmp(gtk_action_get_name(a), "Open")) {
2281 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2285 g_critical("Houston, we've had a problem.");
2289 if ( ! vw->open_dia )
2291 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2293 GTK_FILE_CHOOSER_ACTION_OPEN,
2294 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2295 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2297 GtkFileFilter *filter;
2298 // NB file filters are listed this way for alphabetical ordering
2299 #ifdef VIK_CONFIG_GEOCACHES
2300 filter = gtk_file_filter_new ();
2301 gtk_file_filter_set_name( filter, _("Geocaching") );
2302 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2303 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2306 filter = gtk_file_filter_new ();
2307 gtk_file_filter_set_name( filter, _("Google Earth") );
2308 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2309 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2311 filter = gtk_file_filter_new ();
2312 gtk_file_filter_set_name( filter, _("GPX") );
2313 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2314 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2316 filter = gtk_file_filter_new ();
2317 gtk_file_filter_set_name( filter, _("Viking") );
2318 gtk_file_filter_add_pattern ( filter, "*.vik" );
2319 gtk_file_filter_add_pattern ( filter, "*.viking" );
2320 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2322 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2323 // However assume this are barely used and thus not worthy of inclusion
2324 // as they'll just make the options too many and have no clear file pattern
2325 // one can always use the all option
2326 filter = gtk_file_filter_new ();
2327 gtk_file_filter_set_name( filter, _("All") );
2328 gtk_file_filter_add_pattern ( filter, "*" );
2329 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2330 // Default to any file - same as before open filters were added
2331 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2333 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2334 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2335 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2337 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2339 gtk_widget_hide ( vw->open_dia );
2340 #ifdef VIKING_PROMPT_IF_MODIFIED
2341 if ( (vw->modified || vw->filename) && newwindow )
2343 if ( vw->filename && newwindow )
2345 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2347 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2348 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2349 gboolean first_vik_file = TRUE;
2351 while ( cur_file ) {
2353 gchar *file_name = cur_file->data;
2354 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2355 // Load first of many .vik files in current window
2356 if ( first_vik_file ) {
2357 vik_window_open_file ( vw, file_name, TRUE );
2358 first_vik_file = FALSE;
2361 // Load each subsequent .vik file in a separate window
2362 VikWindow *newvw = vik_window_new_window ();
2364 vik_window_open_file ( newvw, file_name, TRUE );
2369 vik_window_open_file ( vw, file_name, change_fn );
2372 cur_file = g_slist_next (cur_file);
2374 g_slist_free (files);
2378 gtk_widget_hide ( vw->open_dia );
2381 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2383 gboolean rv = FALSE;
2385 if ( ! vw->save_dia )
2387 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2389 GTK_FILE_CHOOSER_ACTION_SAVE,
2390 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2391 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2393 GtkFileFilter *filter;
2394 filter = gtk_file_filter_new ();
2395 gtk_file_filter_set_name( filter, _("All") );
2396 gtk_file_filter_add_pattern ( filter, "*" );
2397 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2399 filter = gtk_file_filter_new ();
2400 gtk_file_filter_set_name( filter, _("Viking") );
2401 gtk_file_filter_add_pattern ( filter, "*.vik" );
2402 gtk_file_filter_add_pattern ( filter, "*.viking" );
2403 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2404 // Default to a Viking file
2405 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2407 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2408 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2410 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2411 gchar* auto_save_name = strdup ( vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") );
2412 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2413 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2415 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2417 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2419 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2420 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 ) ) )
2422 window_set_filename ( vw, fn );
2423 rv = window_save ( vw );
2424 vw->modified = FALSE;
2428 g_free ( auto_save_name );
2429 gtk_widget_hide ( vw->save_dia );
2433 static gboolean window_save ( VikWindow *vw )
2435 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2437 update_recently_used_document ( vw->filename );
2442 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2447 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2449 if ( ! vw->filename )
2450 return save_file_as ( NULL, vw );
2453 vw->modified = FALSE;
2454 return window_save ( vw );
2458 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2460 // Via the file menu, acquiring from a GPS makes a new layer
2461 // this has always been the way (not entirely sure if this was the real intention!)
2462 // thus maintain the behaviour ATM.
2463 // Hence explicit setting here (as the value may be changed elsewhere)
2464 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2465 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2468 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2470 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2473 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2474 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2476 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2480 #ifdef VIK_CONFIG_OPENSTREETMAP
2481 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2483 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2487 #ifdef VIK_CONFIG_GEOCACHES
2488 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2490 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2494 #ifdef VIK_CONFIG_GEOTAG
2495 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2497 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2498 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2502 static void goto_default_location( GtkAction *a, VikWindow *vw)
2505 ll.lat = a_vik_get_default_lat();
2506 ll.lon = a_vik_get_default_long();
2507 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2508 vik_layers_panel_emit_update(vw->viking_vlp);
2512 static void goto_address( GtkAction *a, VikWindow *vw)
2514 a_vik_goto(vw, vw->viking_vlp, vw->viking_vvp);
2517 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2522 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2524 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2526 a_preferences_show_window ( GTK_WINDOW(vw) );
2528 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2529 if (wp_icon_size != a_vik_get_use_large_waypoint_icons())
2530 clear_garmin_icon_syms ();
2535 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2537 /* Simplistic repeat of preference setting
2538 Only the name & type are important for setting the preference via this 'external' way */
2539 VikLayerParam pref_lat[] = {
2540 { VIKING_PREFERENCES_NAMESPACE "default_latitude",
2541 VIK_LAYER_PARAM_DOUBLE,
2544 VIK_LAYER_WIDGET_SPINBUTTON,
2548 VikLayerParam pref_lon[] = {
2549 { VIKING_PREFERENCES_NAMESPACE "default_longitude",
2550 VIK_LAYER_PARAM_DOUBLE,
2553 VIK_LAYER_WIDGET_SPINBUTTON,
2558 /* Get current center */
2560 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2562 /* Apply to preferences */
2563 VikLayerParamData vlp_data;
2564 vlp_data.d = ll.lat;
2565 a_preferences_run_setparam (vlp_data, pref_lat);
2566 vlp_data.d = ll.lon;
2567 a_preferences_run_setparam (vlp_data, pref_lon);
2568 /* Remember to save */
2569 a_preferences_save_to_file();
2572 static void clear_cb ( GtkAction *a, VikWindow *vw )
2574 vik_layers_panel_clear ( vw->viking_vlp );
2575 window_set_filename ( vw, NULL );
2579 static void window_close ( GtkAction *a, VikWindow *vw )
2581 if ( ! delete_event ( vw ) )
2582 gtk_widget_destroy ( GTK_WIDGET(vw) );
2585 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2587 if (save_file( NULL, vw)) {
2588 window_close( NULL, vw);
2595 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2597 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2598 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2600 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2601 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2606 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2608 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2609 GdkPixbuf *pixbuf_to_save;
2610 gdouble old_xmpp, old_ympp;
2611 GError *error = NULL;
2613 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2614 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2617 _("Generating image file...") );
2619 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2620 // Ensure dialog shown
2621 gtk_widget_show_all ( msgbox );
2623 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2624 while ( gtk_events_pending() )
2625 gtk_main_iteration ();
2626 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2627 // At least the empty box can give a clue something's going on + the statusbar msg...
2628 // Windows version under Wine OK!
2630 /* backup old zoom & set new */
2631 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2632 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2633 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2635 /* reset width and height: */
2636 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2638 /* draw all layers */
2641 /* save buffer as file. */
2642 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);
2643 if ( !pixbuf_to_save ) {
2644 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2645 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
2649 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2652 g_warning("Unable to write to file %s: %s", fn, error->message );
2653 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
2654 g_error_free (error);
2658 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2660 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2663 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2664 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2665 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2667 /* pretend like nothing happened ;) */
2668 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2669 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2670 vik_viewport_configure ( vw->viking_vvp );
2674 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 )
2676 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2677 gchar *name_of_file = g_malloc ( size );
2679 struct UTM utm_orig, utm;
2681 /* *** copied from above *** */
2682 GdkPixbuf *pixbuf_to_save;
2683 gdouble old_xmpp, old_ympp;
2684 GError *error = NULL;
2686 /* backup old zoom & set new */
2687 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2688 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2689 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2691 /* reset width and height: do this only once for all images (same size) */
2692 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2693 /* *** end copy from above *** */
2695 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2699 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2701 for ( y = 1; y <= tiles_h; y++ )
2703 for ( x = 1; x <= tiles_w; x++ )
2705 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2707 if ( tiles_w & 0x1 )
2708 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2710 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2711 if ( tiles_h & 0x1 ) /* odd */
2712 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2714 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2716 /* move to correct place. */
2717 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2721 /* save buffer as file. */
2722 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);
2723 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2726 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2727 g_error_free (error);
2730 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2734 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2735 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2736 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2737 vik_viewport_configure ( vw->viking_vvp );
2740 g_free ( name_of_file );
2743 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2745 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2746 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2748 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2749 gdouble zoom = pow (2, active-2 );
2751 gdouble width_min, width_max, height_min, height_max;
2754 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2755 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2757 /* TODO: support for xzoom and yzoom values */
2758 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2759 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2761 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2762 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2764 gtk_spin_button_set_value ( width_spin, width );
2765 gtk_spin_button_set_value ( height_spin, height );
2768 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2770 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2772 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2773 gdouble zoom = pow (2, active-2 );
2777 w = gtk_spin_button_get_value(width_spin) * zoom;
2778 h = gtk_spin_button_get_value(height_spin) * zoom;
2779 if (pass_along[4]) /* save many images; find TOTAL area covered */
2781 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2782 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2784 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2785 switch (dist_units) {
2786 case VIK_UNITS_DISTANCE_KILOMETRES:
2787 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2789 case VIK_UNITS_DISTANCE_MILES:
2790 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2793 label_text = g_strdup_printf ("Just to keep the compiler happy");
2794 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2797 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2798 g_free ( label_text );
2802 * Get an allocated filename (or directory as specified)
2804 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
2807 if ( one_image_only )
2810 if (!vw->save_img_dia) {
2811 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2813 GTK_FILE_CHOOSER_ACTION_SAVE,
2814 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2815 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2818 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
2820 GtkFileFilter *filter;
2821 filter = gtk_file_filter_new ();
2822 gtk_file_filter_set_name ( filter, _("All") );
2823 gtk_file_filter_add_pattern ( filter, "*" );
2824 gtk_file_chooser_add_filter ( chooser, filter );
2826 filter = gtk_file_filter_new ();
2827 gtk_file_filter_set_name ( filter, _("JPG") );
2828 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2829 gtk_file_chooser_add_filter ( chooser, filter );
2831 filter = gtk_file_filter_new ();
2832 gtk_file_filter_set_name ( filter, _("PNG") );
2833 gtk_file_filter_add_mime_type ( filter, "image/png");
2834 gtk_file_chooser_add_filter ( chooser, filter );
2837 gtk_file_chooser_set_filter ( chooser, filter );
2839 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2840 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2843 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
2844 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2845 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
2846 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 ) ) )
2849 gtk_widget_hide ( vw->save_img_dia );
2853 // For some reason this method is only written to work in UTM...
2854 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2855 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2859 if (!vw->save_img_dir_dia) {
2860 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2862 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2863 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2864 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2866 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2867 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2870 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
2871 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2873 gtk_widget_hide ( vw->save_img_dir_dia );
2878 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
2880 /* todo: default for answers inside VikWindow or static (thruout instance) */
2881 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2882 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2884 GTK_RESPONSE_REJECT,
2886 GTK_RESPONSE_ACCEPT,
2888 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2889 GtkWidget *png_radio, *jpeg_radio;
2890 GtkWidget *current_window_button;
2891 gpointer current_window_pass_along[7];
2892 GtkWidget *zoom_label, *zoom_combo;
2893 GtkWidget *total_size_label;
2895 /* only used if (!one_image_only) */
2896 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2898 width_label = gtk_label_new ( _("Width (pixels):") );
2899 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
2900 height_label = gtk_label_new ( _("Height (pixels):") );
2901 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
2903 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
2905 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2906 /* TODO: separate xzoom and yzoom factors */
2907 zoom_combo = create_zoom_combo_all_levels();
2909 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
2910 gint active = 2 + ( log (mpp) / log (2) );
2912 // Can we not hard code size here?
2915 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
2917 total_size_label = gtk_label_new ( NULL );
2919 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2920 current_window_pass_along [0] = vw;
2921 current_window_pass_along [1] = width_spin;
2922 current_window_pass_along [2] = height_spin;
2923 current_window_pass_along [3] = zoom_combo;
2924 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
2925 current_window_pass_along [5] = NULL;
2926 current_window_pass_along [6] = total_size_label;
2927 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
2929 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
2930 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
2932 if ( ! vw->draw_image_save_as_png )
2933 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
2935 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
2936 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
2937 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
2938 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
2940 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), win_warning_label, FALSE, FALSE, 0);
2942 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
2943 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
2944 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
2945 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
2946 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_combo, FALSE, FALSE, 0);
2948 if ( ! one_image_only )
2950 GtkWidget *tiles_width_label, *tiles_height_label;
2952 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
2953 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2954 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
2955 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2956 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
2957 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
2958 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
2959 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
2961 current_window_pass_along [4] = tiles_width_spin;
2962 current_window_pass_along [5] = tiles_height_spin;
2963 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2964 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2966 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
2967 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2968 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2969 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2971 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
2973 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2975 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
2977 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
2979 gtk_widget_hide ( GTK_WIDGET(dialog) );
2981 gchar *fn = draw_image_filename ( vw, one_image_only );
2985 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
2986 gdouble zoom = pow (2, active-2 );
2988 if ( one_image_only )
2989 save_image_file ( vw, fn,
2990 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2991 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2993 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
2995 // NB is in UTM mode ATM
2996 save_image_dir ( vw, fn,
2997 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2998 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3000 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3001 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3002 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3007 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3011 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3013 draw_to_image_file ( vw, TRUE );
3016 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3018 draw_to_image_file ( vw, FALSE );
3021 #if GTK_CHECK_VERSION(2,10,0)
3022 static void print_cb ( GtkAction *a, VikWindow *vw )
3024 a_print(vw, vw->viking_vvp);
3028 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3029 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3031 VikViewportDrawMode drawmode;
3032 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3033 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3035 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3036 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3038 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3039 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3041 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3042 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3045 g_critical("Houston, we've had a problem.");
3049 if ( !vw->only_updating_coord_mode_ui )
3051 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3052 if ( olddrawmode != drawmode )
3054 /* this takes care of coord mode too */
3055 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3056 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3057 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3058 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3059 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3066 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3068 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3069 g_assert(check_box);
3070 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3071 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3075 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3077 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3078 g_assert(check_box);
3079 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3080 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3084 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3086 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3087 g_assert(check_box);
3088 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3089 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3093 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3095 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3096 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3097 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3098 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3099 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3101 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3102 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3106 gtk_widget_destroy ( colorsd );
3109 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3111 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3112 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3113 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3114 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3115 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3117 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3118 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3122 gtk_widget_destroy ( colorsd );
3127 /***********************************************************************************************
3129 ***********************************************************************************************/
3131 static GtkActionEntry entries[] = {
3132 { "File", NULL, N_("_File"), 0, 0, 0 },
3133 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3134 { "View", NULL, N_("_View"), 0, 0, 0 },
3135 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3136 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3137 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3138 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3139 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3140 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3141 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3143 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3144 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3145 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3146 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3147 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3148 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3149 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3150 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
3151 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
3153 #ifdef VIK_CONFIG_OPENSTREETMAP
3154 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_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 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3163 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3164 { "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 },
3165 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3167 #if GTK_CHECK_VERSION(2,10,0)
3168 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3171 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3172 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3174 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3175 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3176 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3177 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3178 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3179 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3180 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3181 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3182 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3183 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3184 { "Zoom0.25", NULL, N_("0.25"), NULL, NULL, (GCallback)draw_zoom_cb },
3185 { "Zoom0.5", NULL, N_("0.5"), NULL, NULL, (GCallback)draw_zoom_cb },
3186 { "Zoom1", NULL, N_("1"), NULL, NULL, (GCallback)draw_zoom_cb },
3187 { "Zoom2", NULL, N_("2"), NULL, NULL, (GCallback)draw_zoom_cb },
3188 { "Zoom4", NULL, N_("4"), NULL, NULL, (GCallback)draw_zoom_cb },
3189 { "Zoom8", NULL, N_("8"), NULL, NULL, (GCallback)draw_zoom_cb },
3190 { "Zoom16", NULL, N_("16"), NULL, NULL, (GCallback)draw_zoom_cb },
3191 { "Zoom32", NULL, N_("32"), NULL, NULL, (GCallback)draw_zoom_cb },
3192 { "Zoom64", NULL, N_("64"), NULL, NULL, (GCallback)draw_zoom_cb },
3193 { "Zoom128", NULL, N_("128"), NULL, NULL, (GCallback)draw_zoom_cb },
3194 { "Zoom256", NULL, N_("256"), NULL, NULL, (GCallback)draw_zoom_cb },
3195 { "Zoom512", NULL, N_("512"), NULL, NULL, (GCallback)draw_zoom_cb },
3196 { "Zoom1024", NULL, N_("1024"), NULL, NULL, (GCallback)draw_zoom_cb },
3197 { "Zoom2048", NULL, N_("2048"), NULL, NULL, (GCallback)draw_zoom_cb },
3198 { "Zoom4096", NULL, N_("4096"), NULL, NULL, (GCallback)draw_zoom_cb },
3199 { "Zoom8192", NULL, N_("8192"), NULL, NULL, (GCallback)draw_zoom_cb },
3200 { "Zoom16384", NULL, N_("16384"), NULL, NULL, (GCallback)draw_zoom_cb },
3201 { "Zoom32768", NULL, N_("32768"), NULL, NULL, (GCallback)draw_zoom_cb },
3202 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3203 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3204 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3205 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3206 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3208 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3209 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3210 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3211 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3212 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3213 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3214 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3215 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3216 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3218 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3219 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3223 /* FIXME use VIEWPORT_DRAWMODE values */
3224 static GtkRadioActionEntry mode_entries[] = {
3225 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3226 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3227 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3228 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3231 static GtkToggleActionEntry toggle_entries[] = {
3232 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3233 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3234 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3235 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3236 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3237 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3238 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3239 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3242 #include "menu.xml.h"
3243 static void window_create_ui( VikWindow *window )
3246 GtkActionGroup *action_group;
3247 GtkAccelGroup *accel_group;
3250 GtkIconFactory *icon_factory;
3251 GtkIconSet *icon_set;
3252 GtkRadioActionEntry *tools = NULL, *radio;
3255 uim = gtk_ui_manager_new ();
3258 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3259 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3260 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3261 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3264 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3265 g_error_free (error);
3269 action_group = gtk_action_group_new ("MenuActions");
3270 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3271 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3272 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3273 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3275 icon_factory = gtk_icon_factory_new ();
3276 gtk_icon_factory_add_default (icon_factory);
3278 register_vik_icons(icon_factory);
3280 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3281 // so that it can be applied to the UI in one action group add function call below
3283 for (i=0; i<window->vt->n_tools; i++) {
3284 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3285 radio = &tools[ntools];
3287 *radio = window->vt->tools[i].ti.radioActionEntry;
3288 radio->value = ntools;
3291 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3292 GtkActionEntry action;
3293 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3294 vik_layer_get_interface(i)->name,
3295 vik_layer_get_interface(i)->name,
3296 GTK_UI_MANAGER_MENUITEM, FALSE);
3298 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3299 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3300 gtk_icon_set_unref (icon_set);
3302 action.name = vik_layer_get_interface(i)->name;
3303 action.stock_id = vik_layer_get_interface(i)->name;
3304 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3305 action.accelerator = vik_layer_get_interface(i)->accelerator;
3306 action.tooltip = NULL;
3307 action.callback = (GCallback)menu_addlayer_cb;
3308 gtk_action_group_add_actions(action_group, &action, 1, window);
3310 if ( vik_layer_get_interface(i)->tools_count ) {
3311 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3312 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3315 // Further tool copying for to apply to the UI, also apply menu UI setup
3316 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3317 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3318 radio = &tools[ntools];
3321 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3322 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3323 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3324 GTK_UI_MANAGER_MENUITEM, FALSE);
3325 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3326 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3327 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3328 GTK_UI_MANAGER_TOOLITEM, FALSE);
3330 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3332 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3333 // Overwrite with actual number to use
3334 radio->value = ntools;
3337 g_object_unref (icon_factory);
3339 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3342 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3344 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3345 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3346 GtkAction *action = gtk_action_group_get_action(action_group,
3347 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3348 g_object_set(action, "sensitive", FALSE, NULL);
3351 window->action_group = action_group;
3353 accel_group = gtk_ui_manager_get_accel_group (uim);
3354 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3355 gtk_ui_manager_ensure_update (uim);
3357 setup_recent_files(window);
3361 // TODO - add method to add tool icons defined from outside this file
3362 // and remove the reverse dependency on icon definition from this file
3364 const GdkPixdata *data;
3367 { &mover_22_pixbuf, "vik-icon-pan" },
3368 { &zoom_18_pixbuf, "vik-icon-zoom" },
3369 { &ruler_18_pixbuf, "vik-icon-ruler" },
3370 { &select_18_pixbuf, "vik-icon-select" },
3371 { &begintr_18_pixbuf, "vik-icon-Begin Track" },
3372 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3373 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3374 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3375 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3376 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3377 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3378 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3379 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3380 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3381 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3384 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3387 register_vik_icons (GtkIconFactory *icon_factory)
3389 GtkIconSet *icon_set;
3392 for (i = 0; i < n_stock_icons; i++) {
3393 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3394 stock_icons[i].data, FALSE, NULL ));
3395 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3396 gtk_icon_set_unref (icon_set);
3400 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3402 return vw->selected_vtl;
3405 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3407 vw->selected_vtl = vtl;
3408 vw->containing_vtl = vtl;
3410 vw->selected_track = NULL;
3411 vw->selected_tracks = NULL;
3412 vw->selected_waypoint = NULL;
3413 vw->selected_waypoints = NULL;
3414 // Set highlight thickness
3415 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3418 gpointer vik_window_get_selected_tracks ( VikWindow *vw )
3420 return vw->selected_tracks;
3423 void vik_window_set_selected_tracks ( VikWindow *vw, gpointer gl, gpointer vtl )
3425 vw->selected_tracks = gl;
3426 vw->containing_vtl = vtl;
3428 vw->selected_vtl = NULL;
3429 vw->selected_track = NULL;
3430 vw->selected_waypoint = NULL;
3431 vw->selected_waypoints = NULL;
3432 // Set highlight thickness
3433 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3436 gpointer vik_window_get_selected_track ( VikWindow *vw )
3438 return vw->selected_track;
3441 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
3443 vw->selected_track = vt;
3444 vw->containing_vtl = vtl;
3446 vw->selected_vtl = NULL;
3447 vw->selected_tracks = NULL;
3448 vw->selected_waypoint = NULL;
3449 vw->selected_waypoints = NULL;
3450 // Set highlight thickness
3451 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3454 gpointer vik_window_get_selected_waypoints ( VikWindow *vw )
3456 return vw->selected_waypoints;
3459 void vik_window_set_selected_waypoints ( VikWindow *vw, gpointer gl, gpointer vtl )
3461 vw->selected_waypoints = gl;
3462 vw->containing_vtl = vtl;
3464 vw->selected_vtl = NULL;
3465 vw->selected_track = NULL;
3466 vw->selected_tracks = NULL;
3467 vw->selected_waypoint = NULL;
3470 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3472 return vw->selected_waypoint;
3475 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
3477 vw->selected_waypoint = vwp;
3478 vw->containing_vtl = vtl;
3480 vw->selected_vtl = NULL;
3481 vw->selected_track = NULL;
3482 vw->selected_tracks = NULL;
3483 vw->selected_waypoints = NULL;
3486 gboolean vik_window_clear_highlight ( VikWindow *vw )
3488 gboolean need_redraw = FALSE;
3489 if ( vw->selected_vtl != NULL ) {
3490 vw->selected_vtl = NULL;
3493 if ( vw->selected_track != NULL ) {
3494 vw->selected_track = NULL;
3497 if ( vw->selected_tracks != NULL ) {
3498 vw->selected_tracks = NULL;
3501 if ( vw->selected_waypoint != NULL ) {
3502 vw->selected_waypoint = NULL;
3505 if ( vw->selected_waypoints != NULL ) {
3506 vw->selected_waypoints = NULL;