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_set_filename ( VikWindow *vw, const gchar *filename );
75 static const gchar *window_get_filename ( VikWindow *vw );
77 static VikWindow *window_new ();
79 static void draw_update ( VikWindow *vw );
81 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
84 static void open_window ( VikWindow *vw, GSList *files );
85 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type );
86 static void destroy_window ( GtkWidget *widget,
91 static gboolean delete_event( VikWindow *vw );
93 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
95 static void window_configure_event ( VikWindow *vw );
96 static void draw_sync ( VikWindow *vw );
97 static void draw_redraw ( VikWindow *vw );
98 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
99 static void draw_click ( VikWindow *vw, GdkEventButton *event );
100 static void draw_release ( VikWindow *vw, GdkEventButton *event );
101 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
102 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
103 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
104 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
106 static void draw_status ( VikWindow *vw );
108 /* End Drawing Functions */
110 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
111 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
112 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
114 /* tool management */
120 #define TOOL_LAYER_TYPE_NONE -1
125 toolbox_tool_t *tools;
129 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
130 static toolbox_tools_t* toolbox_create(VikWindow *vw);
131 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
132 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
133 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
134 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
135 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
136 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
137 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
141 static void window_create_ui( VikWindow *window );
142 static void register_vik_icons (GtkIconFactory *icon_factory);
145 static void load_file ( GtkAction *a, VikWindow *vw );
146 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
147 static gboolean save_file ( GtkAction *a, VikWindow *vw );
148 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
149 static gboolean window_save ( VikWindow *vw );
153 VikViewport *viking_vvp;
154 VikLayersPanel *viking_vlp;
155 VikStatusbar *viking_vs;
158 GtkComboBox *tb_zoom_combo;
160 GtkItemFactory *item_factory;
162 /* tool management state */
165 guint16 tool_layer_id;
166 guint16 tool_tool_id;
168 GtkActionGroup *action_group;
173 guint draw_image_width, draw_image_height;
174 gboolean draw_image_save_as_png;
179 GtkWidget *open_dia, *save_dia;
180 GtkWidget *save_img_dia, *save_img_dir_dia;
182 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
185 /* half-drawn update */
187 VikCoord trigger_center;
189 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
190 /* Only one of these items can be selected at the same time */
191 gpointer selected_vtl; /* notionally VikTrwLayer */
192 GHashTable *selected_tracks;
193 gpointer selected_track; /* notionally VikTrack */
194 GHashTable *selected_waypoints;
195 gpointer selected_waypoint; /* notionally VikWaypoint */
196 /* only use for individual track or waypoint */
197 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
198 gpointer containing_vtl; /* notionally VikTrwLayer */
212 VW_OPENWINDOW_SIGNAL,
213 VW_STATUSBAR_UPDATE_SIGNAL,
217 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
219 // TODO get rid of this as this is unnecessary duplication...
220 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
222 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
224 VikViewport * vik_window_viewport(VikWindow *vw)
226 return(vw->viking_vvp);
229 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
231 return(vw->viking_vlp);
235 * Returns the statusbar for the window
237 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
239 return vw->viking_vs;
243 * For signalling the update from a background thread
245 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
247 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message, vs_type );
251 * For the actual statusbar update!
253 static gboolean statusbar_idle_update ( gpointer indata )
255 gpointer *data = indata;
256 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
261 * Update statusbar in the main thread
263 static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
265 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
266 static gpointer data[3];
267 data[0] = vw->viking_vs;
268 data[1] = (gchar*) message;
269 data[2] = GINT_TO_POINTER(vs_type);
270 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
273 // Actual signal handlers
274 static void destroy_window ( GtkWidget *widget,
277 if ( ! --window_count )
281 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type )
283 window_statusbar_update ( vw, message, vs_type );
286 VikWindow *vik_window_new_window ()
288 if ( window_count < MAX_WINDOWS )
290 VikWindow *vw = window_new ();
292 g_signal_connect (G_OBJECT (vw), "destroy",
293 G_CALLBACK (destroy_window), NULL);
294 g_signal_connect (G_OBJECT (vw), "newwindow",
295 G_CALLBACK (vik_window_new_window), NULL);
296 g_signal_connect (G_OBJECT (vw), "openwindow",
297 G_CALLBACK (open_window), NULL);
298 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
299 G_CALLBACK (statusbar_update), vw);
301 gtk_widget_show_all ( GTK_WIDGET(vw) );
310 static void open_window ( VikWindow *vw, GSList *files )
312 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
313 GSList *cur_file = files;
315 // Only open a new window if a viking file
316 gchar *file_name = cur_file->data;
317 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
318 VikWindow *newvw = vik_window_new_window ();
320 vik_window_open_file ( newvw, file_name, TRUE );
323 vik_window_open_file ( vw, file_name, change_fn );
326 cur_file = g_slist_next (cur_file);
328 g_slist_free (files);
332 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
334 int i, j, tool_count;
335 VikLayerInterface *layer_interface;
337 if (!vw->action_group) return;
339 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
341 layer_interface = vik_layer_get_interface(i);
342 tool_count = layer_interface->tools_count;
344 for (j = 0; j < tool_count; j++) {
345 action = gtk_action_group_get_action(vw->action_group,
346 layer_interface->tools[j].radioActionEntry.name);
347 g_object_set(action, "sensitive", i == vl->type, NULL);
352 static void window_finalize ( GObject *gob )
354 VikWindow *vw = VIK_WINDOW(gob);
355 g_return_if_fail ( vw != NULL );
357 a_background_remove_window ( vw );
359 G_OBJECT_CLASS(parent_class)->finalize(gob);
363 static void vik_window_class_init ( VikWindowClass *klass )
366 GObjectClass *object_class;
368 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);
369 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);
370 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);
372 object_class = G_OBJECT_CLASS (klass);
374 object_class->finalize = window_finalize;
376 parent_class = g_type_class_peek_parent (klass);
380 static void set_toolbar_zoom ( VikWindow *vw, gdouble mpp )
382 gint active = 2 + round ( log (mpp) / log (2) );
383 // Can we not hard code size here?
386 gtk_combo_box_set_active ( vw->tb_zoom_combo, active );
389 static void zoom_changed ( GtkComboBox *combo, VikWindow *vw )
391 gint active = gtk_combo_box_get_active ( combo );
393 // But has it really changed?
394 // Unfortunately this function gets invoked even on manual setting of the combo value
395 gdouble zoom_request = pow (2, active-2 );
396 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
397 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
398 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
399 // Force drawing update
404 static GtkWidget *create_zoom_combo_all_levels ()
406 GtkWidget *zoom_combo = gtk_combo_box_new_text();
407 GtkComboBox *combo = GTK_COMBO_BOX ( zoom_combo );
408 gtk_combo_box_append_text ( combo, "0.25");
409 gtk_combo_box_append_text ( combo, "0.5");
410 gtk_combo_box_append_text ( combo, "1");
411 gtk_combo_box_append_text ( combo, "2");
412 gtk_combo_box_append_text ( combo, "4");
413 gtk_combo_box_append_text ( combo, "8");
414 gtk_combo_box_append_text ( combo, "16");
415 gtk_combo_box_append_text ( combo, "32");
416 gtk_combo_box_append_text ( combo, "64");
417 gtk_combo_box_append_text ( combo, "128");
418 gtk_combo_box_append_text ( combo, "256");
419 gtk_combo_box_append_text ( combo, "512");
420 gtk_combo_box_append_text ( combo, "1024");
421 gtk_combo_box_append_text ( combo, "2048");
422 gtk_combo_box_append_text ( combo, "4096");
423 gtk_combo_box_append_text ( combo, "8192");
424 gtk_combo_box_append_text ( combo, "16384");
425 gtk_combo_box_append_text ( combo, "32768");
427 gtk_widget_set_tooltip_text (GTK_WIDGET (combo), _("Select zoom level"));
431 static void vik_window_init ( VikWindow *vw )
433 GtkWidget *main_vbox;
436 vw->action_group = NULL;
438 vw->viking_vvp = vik_viewport_new();
439 vw->viking_vlp = vik_layers_panel_new();
440 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
441 vw->viking_vs = vik_statusbar_new();
443 vw->vt = toolbox_create(vw);
444 window_create_ui(vw);
445 window_set_filename (vw, NULL);
446 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
448 // Set the default tool
449 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
452 vw->item_factory = NULL;
454 vw->modified = FALSE;
455 vw->only_updating_coord_mode_ui = FALSE;
457 vw->pan_move = FALSE;
458 vw->pan_x = vw->pan_y = -1;
459 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
460 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
461 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
463 main_vbox = gtk_vbox_new(FALSE, 1);
464 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
466 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
467 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
468 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
469 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
471 vik_ext_tools_add_menu_items ( vw, vw->uim );
473 vw->tb_zoom_combo = GTK_COMBO_BOX(create_zoom_combo_all_levels());
475 g_signal_connect ( G_OBJECT(vw->tb_zoom_combo), "changed", G_CALLBACK(zoom_changed), vw );
477 // Add the zoom combo to the toolbar at the end
478 GtkToolItem *tooli = gtk_tool_item_new ();
479 gtk_container_add ( GTK_CONTAINER(tooli), GTK_WIDGET (vw->tb_zoom_combo) );
480 gtk_toolbar_insert ( vw->toolbar, tooli, gtk_toolbar_get_n_items (vw->toolbar) );
482 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
484 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
485 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
486 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 );
487 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
488 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
489 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
490 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
491 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
493 // Allow key presses to be processed anywhere
494 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
496 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
498 hpaned = gtk_hpaned_new ();
499 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
500 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
502 /* This packs the button into the window (a gtk container). */
503 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
505 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
507 a_background_add_window ( vw );
511 vw->save_img_dia = NULL;
512 vw->save_img_dir_dia = NULL;
515 static VikWindow *window_new ()
517 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
521 * Update the displayed map
522 * Only update the top most visible map layer
523 * ATM this assumes (as per defaults) the top most map has full alpha setting
524 * such that other other maps even though they may be active will not be seen
525 * It's more complicated to work out which maps are actually visible due to alpha settings
526 * and overkill for this simple refresh method.
528 static void simple_map_update ( VikWindow *vw, gboolean only_new )
530 // Find the most relevent single map layer to operate on
531 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
533 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
537 * This is the global key press handler
538 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
540 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
542 // The keys handled here are not in the menuing system for a couple of reasons:
543 // . Keeps the menu size compact (alebit at expense of discoverably)
544 // . Allows differing key bindings to perform the same actions
546 // First decide if key events are related to the maps layer
547 gboolean map_download = FALSE;
548 gboolean map_download_only_new = TRUE; // Only new or reload
550 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
552 // Standard 'Refresh' keys: F5 or Ctrl+r
553 // Note 'F5' is actually handled via draw_refresh_cb() later on
554 // (not 'R' it's 'r' notice the case difference!!)
555 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
557 map_download_only_new = TRUE;
559 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
560 // Note the use of uppercase R here since shift key has been pressed
561 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
562 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
564 map_download_only_new = FALSE;
567 if ( map_download ) {
568 simple_map_update ( vw, map_download_only_new );
571 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
572 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
573 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
574 if ( vl && ltype == vl->type )
575 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
578 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
579 if ( vw->current_tool < TOOL_LAYER ) {
580 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
581 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
582 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
586 /* Restore Main Menu via Escape key if the user has hidden it */
587 /* This key is more likely to be used as they may not remember the function key */
588 if ( event->keyval == GDK_Escape ) {
589 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
591 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
593 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
594 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
595 return TRUE; /* handled keypress */
600 return FALSE; /* don't handle the keypress */
603 static gboolean delete_event( VikWindow *vw )
605 #ifdef VIKING_PROMPT_IF_MODIFIED
612 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
613 _("Do you want to save the changes you made to the document \"%s\"?\n"
615 "Your changes will be lost if you don't save them."),
616 window_get_filename ( vw ) ) );
617 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
618 switch ( gtk_dialog_run ( dia ) )
620 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
621 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
622 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
629 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
631 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
634 static void draw_update ( VikWindow *vw )
640 static void draw_sync ( VikWindow *vw )
642 vik_viewport_sync(vw->viking_vvp);
647 * Split the status update, as sometimes only need to update the tool part
648 * also on initialization the zoom related stuff is not ready to be used
650 static void draw_status_tool ( VikWindow *vw )
652 if ( vw->current_tool == TOOL_LAYER )
653 // Use tooltip rather than the internal name as the tooltip is i8n
654 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 );
656 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
659 static void draw_status ( VikWindow *vw )
661 static gchar zoom_level[22];
662 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
663 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
664 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
666 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
668 if ( (int)xmpp - xmpp < 0.0 )
669 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
671 /* xmpp should be a whole number so don't show useless .000 bit */
672 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
674 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
675 // OK maybe not quite in the statusbar - but we have the zoom level so use it
676 set_toolbar_zoom ( vw, xmpp ); // But it's a status of some kind!
678 draw_status_tool ( vw );
681 void vik_window_set_redraw_trigger(VikLayer *vl)
683 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
688 static void window_configure_event ( VikWindow *vw )
690 static int first = 1;
693 // This is a hack to set the cursor corresponding to the first tool
694 // FIXME find the correct way to initialize both tool and its cursor
695 const GdkCursor *cursor = NULL;
697 cursor = toolbox_get_cursor(vw->vt, "Pan");
698 /* We set cursor, even if it is NULL: it resets to default */
699 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
703 static void draw_redraw ( VikWindow *vw )
705 VikCoord old_center = vw->trigger_center;
706 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
707 VikLayer *new_trigger = vw->trigger;
709 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
712 ; /* do nothing -- have to redraw everything. */
713 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
714 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
716 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
719 vik_viewport_clear ( vw->viking_vvp);
720 vik_layers_panel_draw_all ( vw->viking_vlp );
721 vik_viewport_draw_scale ( vw->viking_vvp );
722 vik_viewport_draw_copyright ( vw->viking_vvp );
723 vik_viewport_draw_centermark ( vw->viking_vvp );
724 vik_viewport_draw_logo ( vw->viking_vvp );
726 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
729 gboolean draw_buf_done = TRUE;
731 static gboolean draw_buf(gpointer data)
733 gpointer *pass_along = data;
735 gdk_draw_drawable (pass_along[0], pass_along[1],
736 pass_along[2], 0, 0, 0, 0, -1, -1);
737 draw_buf_done = TRUE;
743 /* Mouse event handlers ************************************************************************/
745 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
747 /* set panning origin */
748 vw->pan_move = FALSE;
749 vw->pan_x = (gint) event->x;
750 vw->pan_y = (gint) event->y;
753 static void draw_click (VikWindow *vw, GdkEventButton *event)
755 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
757 /* middle button pressed. we reserve all middle button and scroll events
758 * for panning and zooming; tools only get left/right/movement
760 if ( event->button == 2) {
761 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
762 // Tool still may need to do something (such as disable something)
763 toolbox_click(vw->vt, event);
764 vik_window_pan_click ( vw, event );
767 toolbox_click(vw->vt, event);
771 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
773 if ( vw->pan_x != -1 ) {
774 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
775 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
777 vw->pan_x = event->x;
778 vw->pan_y = event->y;
783 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
785 static VikCoord coord;
786 static struct UTM utm;
787 static struct LatLon ll;
788 #define BUFFER_SIZE 50
789 static char pointer_buf[BUFFER_SIZE];
790 gchar *lat = NULL, *lon = NULL;
793 VikDemInterpol interpol_method;
795 /* This is a hack, but work far the best, at least for single pointer systems.
796 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
798 gdk_window_get_pointer (event->window, &x, &y, NULL);
802 toolbox_move(vw->vt, event);
804 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
805 vik_coord_to_utm ( &coord, &utm );
807 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
808 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
809 // ZONE[N|S] EASTING NORTHING
810 lat = g_malloc(4*sizeof(gchar));
811 // NB zone is stored in a char but is an actual number
812 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
813 lon = g_malloc(16*sizeof(gchar));
814 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
817 a_coords_utm_to_latlon ( &utm, &ll );
818 a_coords_latlon_to_string ( &ll, &lat, &lon );
821 /* Change interpolate method according to scale */
822 zoom = vik_viewport_get_zoom(vw->viking_vvp);
824 interpol_method = VIK_DEM_INTERPOL_NONE;
825 else if (zoom >= 1.0)
826 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
828 interpol_method = VIK_DEM_INTERPOL_BEST;
829 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
830 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
831 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
833 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
836 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
841 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
843 vik_window_pan_move ( vw, event );
845 /* This is recommended by the GTK+ documentation, but does not work properly.
846 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
847 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
849 /* gdk_event_request_motions ( event ); */
852 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
854 if ( vw->pan_move == FALSE )
855 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
857 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
858 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
859 vw->pan_move = FALSE;
860 vw->pan_x = vw->pan_y = -1;
864 static void draw_release ( VikWindow *vw, GdkEventButton *event )
866 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
868 if ( event->button == 2 ) { /* move / pan */
869 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
870 // Tool still may need to do something (such as reenable something)
871 toolbox_release(vw->vt, event);
872 vik_window_pan_release ( vw, event );
875 toolbox_release(vw->vt, event);
879 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
881 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
882 if ( modifiers == GDK_CONTROL_MASK ) {
883 /* control == pan up & down */
884 if ( event->direction == GDK_SCROLL_UP )
885 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
887 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 );
888 } else if ( modifiers == GDK_SHIFT_MASK ) {
889 /* shift == pan left & right */
890 if ( event->direction == GDK_SCROLL_UP )
891 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
893 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 );
894 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
895 // This zoom is on the center position
896 if ( event->direction == GDK_SCROLL_UP )
897 vik_viewport_zoom_in (vw->viking_vvp);
899 vik_viewport_zoom_out (vw->viking_vvp);
901 /* make sure mouse is still over the same point on the map when we zoom */
904 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
905 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
906 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
907 if ( event->direction == GDK_SCROLL_UP )
908 vik_viewport_zoom_in (vw->viking_vvp);
910 vik_viewport_zoom_out(vw->viking_vvp);
911 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
912 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
913 center_y + (y - event->y) );
921 /********************************************************************************
923 ********************************************************************************/
924 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
928 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
929 GdkGC *thickgc = gdk_gc_new(d);
931 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
932 gdouble dx = (x2-x1)/len*10;
933 gdouble dy = (y2-y1)/len*10;
934 gdouble c = cos(15.0 * M_PI/180.0);
935 gdouble s = sin(15.0 * M_PI/180.0);
937 gdouble baseangle = 0;
940 /* draw line with arrow ends */
942 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
943 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
944 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
947 a_viewport_clip_line(&x1, &y1, &x2, &y2);
948 gdk_draw_line(d, gc, x1, y1, x2, y2);
950 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
951 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
952 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
953 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
954 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
955 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
961 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
965 gdk_gc_copy(thickgc, gc);
966 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
967 gdk_color_parse("#2255cc", &color);
968 gdk_gc_set_rgb_fg_color(thickgc, &color);
970 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);
973 gdk_gc_copy(thickgc, gc);
974 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
975 for (i=0; i<180; i++) {
976 c = cos(i*M_PI/90.0 + baseangle);
977 s = sin(i*M_PI/90.0 + baseangle);
980 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
982 gdouble ticksize = 2*CW;
983 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
987 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
988 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
989 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
990 c = (CR+CW*2)*cos(baseangle);
991 s = (CR+CW*2)*sin(baseangle);
992 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
993 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
996 #define LABEL(x, y, w, h) { \
997 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
998 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
999 gdk_draw_layout(d, gc, (x), (y), pl); }
1001 gint wd, hd, xd, yd;
1002 gint wb, hb, xb, yb;
1004 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1005 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
1006 pango_layout_set_text(pl, "N", -1);
1007 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1009 /* draw label with distance */
1010 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1011 switch (dist_units) {
1012 case VIK_UNITS_DISTANCE_KILOMETRES:
1013 if (distance >= 1000 && distance < 100000) {
1014 g_sprintf(str, "%3.2f km", distance/1000.0);
1015 } else if (distance < 1000) {
1016 g_sprintf(str, "%d m", (int)distance);
1018 g_sprintf(str, "%d km", (int)distance/1000);
1021 case VIK_UNITS_DISTANCE_MILES:
1022 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1023 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1024 } else if (distance < VIK_MILES_TO_METERS(1)) {
1025 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1027 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1031 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1034 pango_layout_set_text(pl, str, -1);
1036 pango_layout_get_pixel_size ( pl, &wd, &hd );
1038 xd = (x1+x2)/2 + dy;
1039 yd = (y1+y2)/2 - hd/2 - dx;
1041 xd = (x1+x2)/2 - dy;
1042 yd = (y1+y2)/2 - hd/2 + dx;
1045 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1050 LABEL(xd, yd, wd, hd);
1052 /* draw label with bearing */
1053 g_sprintf(str, "%3.1f°", angle*180.0/M_PI);
1054 pango_layout_set_text(pl, str, -1);
1055 pango_layout_get_pixel_size ( pl, &wb, &hb );
1056 xb = x1 + CR*cos(angle-M_PI_2);
1057 yb = y1 + CR*sin(angle-M_PI_2);
1059 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1065 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1066 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1070 LABEL(xb, yb, wb, hb);
1074 g_object_unref ( G_OBJECT ( pl ) );
1075 g_object_unref ( G_OBJECT ( labgc ) );
1076 g_object_unref ( G_OBJECT ( thickgc ) );
1082 gboolean has_oldcoord;
1084 } ruler_tool_state_t;
1086 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1088 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1091 s->has_oldcoord = FALSE;
1095 static void ruler_destroy (ruler_tool_state_t *s)
1100 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1105 if ( event->button == 1 ) {
1106 gchar *lat=NULL, *lon=NULL;
1107 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1108 vik_coord_to_latlon ( &coord, &ll );
1109 a_coords_latlon_to_string ( &ll, &lat, &lon );
1110 if ( s->has_oldcoord ) {
1111 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1112 switch (dist_units) {
1113 case VIK_UNITS_DISTANCE_KILOMETRES:
1114 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1116 case VIK_UNITS_DISTANCE_MILES:
1117 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1120 temp = g_strdup_printf ("Just to keep the compiler happy");
1121 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1124 s->has_oldcoord = FALSE;
1127 temp = g_strdup_printf ( "%s %s", lat, lon );
1128 s->has_oldcoord = TRUE;
1131 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1134 s->oldcoord = coord;
1137 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1138 draw_update ( s->vw );
1140 return VIK_LAYER_TOOL_ACK;
1143 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1145 VikViewport *vvp = s->vvp;
1146 VikWindow *vw = s->vw;
1152 if ( s->has_oldcoord ) {
1153 int oldx, oldy, w1, h1, w2, h2;
1154 static GdkPixmap *buf = NULL;
1155 gchar *lat=NULL, *lon=NULL;
1156 w1 = vik_viewport_get_width(vvp);
1157 h1 = vik_viewport_get_height(vvp);
1159 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1161 gdk_drawable_get_size(buf, &w2, &h2);
1162 if (w1 != w2 || h1 != h2) {
1163 g_object_unref ( G_OBJECT ( buf ) );
1164 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1167 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1168 vik_coord_to_latlon ( &coord, &ll );
1169 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1171 gdk_draw_drawable (buf, GTK_WIDGET(vvp)->style->black_gc,
1172 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1173 draw_ruler(vvp, buf, GTK_WIDGET(vvp)->style->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1174 if (draw_buf_done) {
1175 static gpointer pass_along[3];
1176 pass_along[0] = GTK_WIDGET(vvp)->window;
1177 pass_along[1] = GTK_WIDGET(vvp)->style->black_gc;
1178 pass_along[2] = buf;
1179 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1180 draw_buf_done = FALSE;
1182 a_coords_latlon_to_string(&ll, &lat, &lon);
1183 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1184 switch (dist_units) {
1185 case VIK_UNITS_DISTANCE_KILOMETRES:
1186 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1188 case VIK_UNITS_DISTANCE_MILES:
1189 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1192 temp = g_strdup_printf ("Just to keep the compiler happy");
1193 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1195 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1198 return VIK_LAYER_TOOL_ACK;
1201 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1203 return VIK_LAYER_TOOL_ACK;
1206 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1208 draw_update ( s->vw );
1211 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1213 if (event->keyval == GDK_Escape) {
1214 s->has_oldcoord = FALSE;
1215 ruler_deactivate ( vl, s );
1218 // Regardless of whether we used it, return false so other GTK things may use it
1222 static VikToolInterface ruler_tool =
1223 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1224 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1225 (VikToolConstructorFunc) ruler_create,
1226 (VikToolDestructorFunc) ruler_destroy,
1227 (VikToolActivationFunc) NULL,
1228 (VikToolActivationFunc) ruler_deactivate,
1229 (VikToolMouseFunc) ruler_click,
1230 (VikToolMouseMoveFunc) ruler_move,
1231 (VikToolMouseFunc) ruler_release,
1232 (VikToolKeyFunc) ruler_key_press,
1234 GDK_CURSOR_IS_PIXMAP,
1235 &cursor_ruler_pixbuf };
1236 /*** end ruler code ********************************************************/
1240 /********************************************************************************
1242 ********************************************************************************/
1247 // Track zoom bounds for zoom tool with shift modifier:
1248 gboolean bounds_active;
1251 } zoom_tool_state_t;
1254 * In case the screen size has changed
1256 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1260 // Allocate a drawing area the size of the viewport
1261 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1262 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1264 if ( !zts->pixmap ) {
1266 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1269 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1271 if ( w1 != w2 || h1 != h2 ) {
1272 // Has changed - delete and recreate with new values
1273 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1274 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1278 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1280 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1285 zts->bounds_active = FALSE;
1289 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1292 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1296 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1298 zts->vw->modified = TRUE;
1299 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1303 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1304 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1306 gboolean skip_update = FALSE;
1308 zts->bounds_active = FALSE;
1310 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1311 // This zoom is on the center position
1312 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1313 if ( event->button == 1 )
1314 vik_viewport_zoom_in (zts->vw->viking_vvp);
1315 else if ( event->button == 3 )
1316 vik_viewport_zoom_out (zts->vw->viking_vvp);
1318 else if ( modifiers == GDK_CONTROL_MASK ) {
1319 // This zoom is to recenter on the mouse position
1320 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1321 if ( event->button == 1 )
1322 vik_viewport_zoom_in (zts->vw->viking_vvp);
1323 else if ( event->button == 3 )
1324 vik_viewport_zoom_out (zts->vw->viking_vvp);
1326 else if ( modifiers == GDK_SHIFT_MASK ) {
1327 // Get start of new zoom bounds
1328 if ( event->button == 1 ) {
1329 zts->bounds_active = TRUE;
1330 zts->start_x = (gint) event->x;
1331 zts->start_y = (gint) event->y;
1336 /* make sure mouse is still over the same point on the map when we zoom */
1337 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1338 if ( event->button == 1 )
1339 vik_viewport_zoom_in (zts->vw->viking_vvp);
1340 else if ( event->button == 3 )
1341 vik_viewport_zoom_out(zts->vw->viking_vvp);
1342 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1343 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1344 center_x + (x - event->x),
1345 center_y + (y - event->y) );
1349 draw_update ( zts->vw );
1351 return VIK_LAYER_TOOL_ACK;
1354 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1356 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1358 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1359 zoomtool_resize_pixmap ( zts );
1361 // Blank out currently drawn area
1362 gdk_draw_drawable ( zts->pixmap,
1363 GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc,
1364 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1365 0, 0, 0, 0, -1, -1);
1367 // Calculate new box starting point & size in pixels
1368 int xx, yy, width, height;
1369 if ( event->y > zts->start_y ) {
1371 height = event->y-zts->start_y;
1375 height = zts->start_y-event->y;
1377 if ( event->x > zts->start_x ) {
1379 width = event->x-zts->start_x;
1383 width = zts->start_x-event->x;
1387 gdk_draw_rectangle (zts->pixmap, GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, FALSE, xx, yy, width, height);
1389 // Only actually draw when there's time to do so
1390 if (draw_buf_done) {
1391 static gpointer pass_along[3];
1392 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1393 pass_along[1] = GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc;
1394 pass_along[2] = zts->pixmap;
1395 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1396 draw_buf_done = FALSE;
1399 return VIK_LAYER_TOOL_ACK;
1402 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1404 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1406 zts->bounds_active = FALSE;
1408 // Ensure haven't just released on the exact same position
1409 // i.e. probably haven't moved the mouse at all
1410 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1412 VikCoord coord1, coord2;
1413 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1414 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1416 // From the extend of the bounds pick the best zoom level
1417 // c.f. trw_layer_zoom_to_show_latlons()
1418 // Maybe refactor...
1419 struct LatLon ll1, ll2;
1420 vik_coord_to_latlon(&coord1, &ll1);
1421 vik_coord_to_latlon(&coord2, &ll2);
1422 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1423 (ll1.lon+ll2.lon)/2 };
1425 VikCoord new_center;
1426 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1427 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1429 /* Convert into definite 'smallest' and 'largest' positions */
1430 struct LatLon minmin;
1431 if ( ll1.lat < ll2.lat )
1432 minmin.lat = ll1.lat;
1434 minmin.lat = ll2.lat;
1436 struct LatLon maxmax;
1437 if ( ll1.lon > ll2.lon )
1438 maxmax.lon = ll1.lon;
1440 maxmax.lon = ll2.lon;
1442 /* Always recalculate the 'best' zoom level */
1443 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1444 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1446 gdouble min_lat, max_lat, min_lon, max_lon;
1447 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1448 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1449 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1450 /* NB I think the logic used in this test to determine if the bounds is within view
1451 fails if track goes across 180 degrees longitude.
1452 Hopefully that situation is not too common...
1453 Mind you viking doesn't really do edge locations to well anyway */
1454 if ( min_lat < minmin.lat &&
1455 max_lat > minmin.lat &&
1456 min_lon < maxmax.lon &&
1457 max_lon > maxmax.lon )
1458 /* Found within zoom level */
1463 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1466 draw_update ( zts->vw );
1468 return VIK_LAYER_TOOL_ACK;
1471 static VikToolInterface zoom_tool =
1472 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1473 (VikToolConstructorFunc) zoomtool_create,
1474 (VikToolDestructorFunc) zoomtool_destroy,
1475 (VikToolActivationFunc) NULL,
1476 (VikToolActivationFunc) NULL,
1477 (VikToolMouseFunc) zoomtool_click,
1478 (VikToolMouseMoveFunc) zoomtool_move,
1479 (VikToolMouseFunc) zoomtool_release,
1482 GDK_CURSOR_IS_PIXMAP,
1483 &cursor_zoom_pixbuf };
1484 /*** end zoom code ********************************************************/
1486 /********************************************************************************
1488 ********************************************************************************/
1489 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1494 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1496 vw->modified = TRUE;
1497 if ( event->button == 1 )
1498 vik_window_pan_click ( vw, event );
1500 return VIK_LAYER_TOOL_ACK;
1503 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1505 vik_window_pan_move ( vw, event );
1506 return VIK_LAYER_TOOL_ACK;
1509 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1511 if ( event->button == 1 )
1512 vik_window_pan_release ( vw, event );
1513 return VIK_LAYER_TOOL_ACK;
1516 static VikToolInterface pan_tool =
1517 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1518 (VikToolConstructorFunc) pantool_create,
1519 (VikToolDestructorFunc) NULL,
1520 (VikToolActivationFunc) NULL,
1521 (VikToolActivationFunc) NULL,
1522 (VikToolMouseFunc) pantool_click,
1523 (VikToolMouseMoveFunc) pantool_move,
1524 (VikToolMouseFunc) pantool_release,
1528 /*** end pan code ********************************************************/
1530 /********************************************************************************
1532 ********************************************************************************/
1533 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1535 tool_ed_t *t = g_new(tool_ed_t, 1);
1539 t->is_waypoint = FALSE;
1543 static void selecttool_destroy (tool_ed_t *t)
1551 GdkEventButton *event;
1552 tool_ed_t *tool_edit;
1555 static void click_layer_selected (VikLayer *vl, clicker *ck)
1557 /* Do nothing when function call returns true; */
1558 /* i.e. stop on first found item */
1561 if ( vik_layer_get_interface(vl->type)->select_click )
1562 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1565 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1567 /* Only allow selection on primary button */
1568 if ( event->button == 1 ) {
1569 /* Enable click to apply callback to potentially all track/waypoint layers */
1570 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1571 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1574 ck.vvp = t->vw->viking_vvp;
1577 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1580 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1583 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1585 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1586 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1587 gint type = vik_treeview_item_get_type ( vtv, &iter );
1588 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1589 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1591 vik_treeview_item_unselect ( vtv, &iter );
1592 if ( vik_window_clear_highlight ( t->vw ) )
1593 draw_update ( t->vw );
1598 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1600 /* Act on currently selected item to show menu */
1601 if ( t->vw->selected_track || t->vw->selected_waypoint )
1602 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1603 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1606 return VIK_LAYER_TOOL_ACK;
1609 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1611 /* Only allow selection on primary button */
1612 if ( event->button == 1 ) {
1613 // Don't care about vl here
1615 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1616 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1618 return VIK_LAYER_TOOL_ACK;
1621 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1623 /* Only allow selection on primary button */
1624 if ( event->button == 1 ) {
1625 // Don't care about vl here
1627 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1628 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1630 return VIK_LAYER_TOOL_ACK;
1633 static VikToolInterface select_tool =
1634 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1635 (VikToolConstructorFunc) selecttool_create,
1636 (VikToolDestructorFunc) selecttool_destroy,
1637 (VikToolActivationFunc) NULL,
1638 (VikToolActivationFunc) NULL,
1639 (VikToolMouseFunc) selecttool_click,
1640 (VikToolMouseMoveFunc) selecttool_move,
1641 (VikToolMouseFunc) selecttool_release,
1642 (VikToolKeyFunc) NULL,
1647 /*** end select tool code ********************************************************/
1649 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1651 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1652 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1653 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1654 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1655 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1656 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1657 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1658 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1663 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1665 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1666 g_assert(check_box);
1667 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1669 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1671 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1674 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1678 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1681 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1684 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1687 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1691 gchar *s = (gchar *)gtk_action_get_name(a);
1697 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1698 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1699 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1700 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1701 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1706 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1708 VikCoord new_center;
1710 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1711 struct LatLon ll, llold;
1712 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1713 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1714 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1718 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1719 struct UTM utm, utmold;
1720 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1721 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1722 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1727 g_critical("Houston, we've had a problem.");
1731 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1736 * Refresh maps displayed
1738 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1740 // Only get 'new' maps
1741 simple_map_update ( vw, TRUE );
1744 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1747 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1748 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1749 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1751 vw->modified = TRUE;
1757 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1759 a_clipboard_copy_selected ( vw->viking_vlp );
1762 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1764 vik_layers_panel_cut_selected ( vw->viking_vlp );
1765 vw->modified = TRUE;
1768 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1770 if ( a_clipboard_paste ( vw->viking_vlp ) )
1772 vw->modified = TRUE;
1776 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1778 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1779 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1782 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1785 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1787 #if GTK_CHECK_VERSION (2, 14, 0)
1789 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1790 GError *error = NULL;
1791 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1792 if ( !show && !error )
1793 // No error to show, so unlikely this will get called
1794 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1797 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 );
1798 g_error_free ( error );
1802 a_dialog_error_msg ( GTK_WINDOW(vw), "Help is not available in this build." ); // Unlikely to happen so not going to bother with I8N
1804 #endif /* WINDOWS */
1807 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1809 a_dialog_about(GTK_WINDOW(vw));
1812 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1814 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1816 vik_layers_panel_delete_selected ( vw->viking_vlp );
1817 vw->modified = TRUE;
1820 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1823 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1825 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1826 g_assert(check_box);
1827 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1829 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1831 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1834 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1836 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1839 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1841 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1843 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1846 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1848 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1851 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1853 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1855 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1858 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1860 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1863 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1865 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1867 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1870 /***************************************
1871 ** tool management routines
1873 ***************************************/
1875 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1877 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1880 vt->active_tool = -1;
1885 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1887 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1888 vt->tools[vt->n_tools].ti = *vti;
1889 vt->tools[vt->n_tools].layer_type = layer_type;
1891 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1894 vt->tools[vt->n_tools].state = NULL;
1899 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1902 for (i=0; i<vt->n_tools; i++) {
1903 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1910 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1912 int tool = toolbox_get_tool(vt, tool_name);
1913 toolbox_tool_t *t = &vt->tools[tool];
1914 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1916 if (tool == vt->n_tools) {
1917 g_critical("trying to activate a non-existent tool...");
1920 /* is the tool already active? */
1921 if (vt->active_tool == tool) {
1925 if (vt->active_tool != -1) {
1926 if (vt->tools[vt->active_tool].ti.deactivate) {
1927 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1930 if (t->ti.activate) {
1931 t->ti.activate(vl, t->state);
1933 vt->active_tool = tool;
1936 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1938 int tool = toolbox_get_tool(vt, tool_name);
1939 toolbox_tool_t *t = &vt->tools[tool];
1940 if (t->ti.cursor == NULL) {
1941 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1942 GError *cursor_load_err = NULL;
1943 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1944 /* TODO: settable offeset */
1945 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1946 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1948 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1951 return t->ti.cursor;
1954 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1956 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1957 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1958 gint ltype = vt->tools[vt->active_tool].layer_type;
1959 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1960 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
1964 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
1966 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1967 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
1968 gint ltype = vt->tools[vt->active_tool].layer_type;
1969 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1970 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
1971 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
1975 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
1977 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1978 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
1979 gint ltype = vt->tools[vt->active_tool].layer_type;
1980 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1981 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
1984 /** End tool management ************************************/
1986 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
1988 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
1991 /* this function gets called whenever a toolbar tool is clicked */
1992 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
1994 /* White Magic, my friends ... White Magic... */
1995 int layer_id, tool_id;
1996 const GdkCursor *cursor = NULL;
1998 toolbox_activate(vw->vt, gtk_action_get_name(a));
2000 cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2002 if ( GTK_WIDGET(vw->viking_vvp)->window )
2003 /* We set cursor, even if it is NULL: it resets to default */
2004 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
2006 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2007 vw->current_tool = TOOL_PAN;
2009 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2010 vw->current_tool = TOOL_ZOOM;
2012 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2013 vw->current_tool = TOOL_RULER;
2015 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2016 vw->current_tool = TOOL_SELECT;
2019 /* TODO: only enable tools from active layer */
2020 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2021 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2022 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2023 vw->current_tool = TOOL_LAYER;
2024 vw->tool_layer_id = layer_id;
2025 vw->tool_tool_id = tool_id;
2030 draw_status_tool ( vw );
2033 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2038 g_free ( vw->filename );
2039 if ( filename == NULL )
2041 vw->filename = NULL;
2045 vw->filename = g_strdup(filename);
2048 /* Refresh window's title */
2049 file = window_get_filename ( vw );
2050 title = g_strdup_printf( "%s - Viking", file );
2051 gtk_window_set_title ( GTK_WINDOW(vw), title );
2055 static const gchar *window_get_filename ( VikWindow *vw )
2057 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2060 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2062 GtkWidget *mode_button;
2065 #ifdef VIK_CONFIG_EXPEDIA
2066 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2068 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2069 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2070 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2072 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2073 g_assert ( mode_button );
2078 * vik_window_get_pan_move:
2079 * @vw: some VikWindow
2081 * Retrieves @vw's pan_move.
2083 * Should be removed as soon as possible.
2085 * Returns: @vw's pan_move
2089 gboolean vik_window_get_pan_move ( VikWindow *vw )
2091 return vw->pan_move;
2094 static void on_activate_recent_item (GtkRecentChooser *chooser,
2099 filename = gtk_recent_chooser_get_current_uri (chooser);
2100 if (filename != NULL)
2102 GFile *file = g_file_new_for_uri ( filename );
2103 gchar *path = g_file_get_path ( file );
2104 g_object_unref ( file );
2105 if ( self->filename )
2107 GSList *filenames = NULL;
2108 filenames = g_slist_append ( filenames, path );
2109 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2110 // NB: GSList & contents are freed by main.open_window
2113 vik_window_open_file ( self, path, TRUE );
2121 static void setup_recent_files (VikWindow *self)
2123 GtkRecentManager *manager;
2124 GtkRecentFilter *filter;
2125 GtkWidget *menu, *menu_item;
2127 filter = gtk_recent_filter_new ();
2128 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2129 gtk_recent_filter_add_group(filter, "viking");
2131 manager = gtk_recent_manager_get_default ();
2132 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2133 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2134 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2136 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2137 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2139 g_signal_connect (G_OBJECT (menu), "item-activated",
2140 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2143 static void update_recently_used_document(const gchar *filename)
2145 /* Update Recently Used Document framework */
2146 GtkRecentManager *manager = gtk_recent_manager_get_default();
2147 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2148 gchar *groups[] = {"viking", NULL};
2149 GFile *file = g_file_new_for_commandline_arg(filename);
2150 gchar *uri = g_file_get_uri(file);
2151 gchar *basename = g_path_get_basename(filename);
2152 g_object_unref(file);
2155 recent_data->display_name = basename;
2156 recent_data->description = NULL;
2157 recent_data->mime_type = "text/x-gps-data";
2158 recent_data->app_name = (gchar *) g_get_application_name ();
2159 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2160 recent_data->groups = groups;
2161 recent_data->is_private = FALSE;
2162 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2164 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2169 g_free (recent_data->app_exec);
2170 g_slice_free (GtkRecentData, recent_data);
2173 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2175 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2177 case LOAD_TYPE_READ_FAILURE:
2178 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2180 case LOAD_TYPE_GPSBABEL_FAILURE:
2181 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2183 case LOAD_TYPE_GPX_FAILURE:
2184 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2186 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2187 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2189 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2191 // Since we can process .vik files with issues just show a warning in the status bar
2192 // Not that a user can do much about it... or tells them what this issue is yet...
2193 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2194 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2197 // No break, carry on to show any data
2198 case LOAD_TYPE_VIK_SUCCESS:
2200 GtkWidget *mode_button;
2202 if ( change_filename )
2203 window_set_filename ( vw, filename );
2204 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2205 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. */
2206 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2207 vw->only_updating_coord_mode_ui = FALSE;
2209 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2211 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2212 g_assert ( mode_button );
2213 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2215 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2216 g_assert ( mode_button );
2217 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2219 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2220 g_assert ( mode_button );
2221 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2223 //case LOAD_TYPE_OTHER_SUCCESS:
2225 update_recently_used_document(filename);
2230 static void load_file ( GtkAction *a, VikWindow *vw )
2232 GSList *files = NULL;
2233 GSList *cur_file = NULL;
2235 if (!strcmp(gtk_action_get_name(a), "Open")) {
2238 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2242 g_critical("Houston, we've had a problem.");
2246 if ( ! vw->open_dia )
2248 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2250 GTK_FILE_CHOOSER_ACTION_OPEN,
2251 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2252 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2254 GtkFileFilter *filter;
2255 // NB file filters are listed this way for alphabetical ordering
2256 #ifdef VIK_CONFIG_GEOCACHES
2257 filter = gtk_file_filter_new ();
2258 gtk_file_filter_set_name( filter, _("Geocaching") );
2259 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2260 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2263 filter = gtk_file_filter_new ();
2264 gtk_file_filter_set_name( filter, _("Google Earth") );
2265 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2266 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2268 filter = gtk_file_filter_new ();
2269 gtk_file_filter_set_name( filter, _("GPX") );
2270 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2271 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2273 filter = gtk_file_filter_new ();
2274 gtk_file_filter_set_name( filter, _("Viking") );
2275 gtk_file_filter_add_pattern ( filter, "*.vik" );
2276 gtk_file_filter_add_pattern ( filter, "*.viking" );
2277 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2279 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2280 // However assume this are barely used and thus not worthy of inclusion
2281 // as they'll just make the options too many and have no clear file pattern
2282 // one can always use the all option
2283 filter = gtk_file_filter_new ();
2284 gtk_file_filter_set_name( filter, _("All") );
2285 gtk_file_filter_add_pattern ( filter, "*" );
2286 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2287 // Default to any file - same as before open filters were added
2288 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2290 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2291 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2292 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2294 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2296 gtk_widget_hide ( vw->open_dia );
2297 #ifdef VIKING_PROMPT_IF_MODIFIED
2298 if ( (vw->modified || vw->filename) && newwindow )
2300 if ( vw->filename && newwindow )
2302 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2304 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2305 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2306 gboolean first_vik_file = TRUE;
2308 while ( cur_file ) {
2310 gchar *file_name = cur_file->data;
2311 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2312 // Load first of many .vik files in current window
2313 if ( first_vik_file ) {
2314 vik_window_open_file ( vw, file_name, TRUE );
2315 first_vik_file = FALSE;
2318 // Load each subsequent .vik file in a separate window
2319 VikWindow *newvw = vik_window_new_window ();
2321 vik_window_open_file ( newvw, file_name, TRUE );
2326 vik_window_open_file ( vw, file_name, change_fn );
2329 cur_file = g_slist_next (cur_file);
2331 g_slist_free (files);
2335 gtk_widget_hide ( vw->open_dia );
2338 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2340 gboolean rv = FALSE;
2342 if ( ! vw->save_dia )
2344 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2346 GTK_FILE_CHOOSER_ACTION_SAVE,
2347 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2348 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2350 GtkFileFilter *filter;
2351 filter = gtk_file_filter_new ();
2352 gtk_file_filter_set_name( filter, _("All") );
2353 gtk_file_filter_add_pattern ( filter, "*" );
2354 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2356 filter = gtk_file_filter_new ();
2357 gtk_file_filter_set_name( filter, _("Viking") );
2358 gtk_file_filter_add_pattern ( filter, "*.vik" );
2359 gtk_file_filter_add_pattern ( filter, "*.viking" );
2360 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2361 // Default to a Viking file
2362 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2364 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2365 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2367 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2368 gchar* auto_save_name = strdup ( window_get_filename ( vw ) );
2369 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2370 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2372 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2374 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2376 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2377 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 ) ) )
2379 window_set_filename ( vw, fn );
2380 rv = window_save ( vw );
2381 vw->modified = FALSE;
2385 g_free ( auto_save_name );
2386 gtk_widget_hide ( vw->save_dia );
2390 static gboolean window_save ( VikWindow *vw )
2392 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2394 update_recently_used_document ( vw->filename );
2399 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2404 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2406 if ( ! vw->filename )
2407 return save_file_as ( NULL, vw );
2410 vw->modified = FALSE;
2411 return window_save ( vw );
2415 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2417 // Via the file menu, acquiring from a GPS makes a new layer
2418 // this has always been the way (not entirely sure if this was the real intention!)
2419 // thus maintain the behaviour ATM.
2420 // Hence explicit setting here (as the value may be changed elsewhere)
2421 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2422 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2425 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2427 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2430 #ifdef VIK_CONFIG_GOOGLE
2431 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2433 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2437 #ifdef VIK_CONFIG_OPENSTREETMAP
2438 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2440 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2444 #ifdef VIK_CONFIG_GEOCACHES
2445 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2447 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2451 #ifdef VIK_CONFIG_GEOTAG
2452 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2454 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2455 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2459 static void goto_default_location( GtkAction *a, VikWindow *vw)
2462 ll.lat = a_vik_get_default_lat();
2463 ll.lon = a_vik_get_default_long();
2464 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2465 vik_layers_panel_emit_update(vw->viking_vlp);
2469 static void goto_address( GtkAction *a, VikWindow *vw)
2471 a_vik_goto ( vw, vw->viking_vvp );
2472 vik_layers_panel_emit_update ( vw->viking_vlp );
2475 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2480 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2482 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2484 a_preferences_show_window ( GTK_WINDOW(vw) );
2486 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2487 if (wp_icon_size != a_vik_get_use_large_waypoint_icons())
2488 clear_garmin_icon_syms ();
2493 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2495 /* Simplistic repeat of preference setting
2496 Only the name & type are important for setting the preference via this 'external' way */
2497 VikLayerParam pref_lat[] = {
2498 { VIKING_PREFERENCES_NAMESPACE "default_latitude",
2499 VIK_LAYER_PARAM_DOUBLE,
2502 VIK_LAYER_WIDGET_SPINBUTTON,
2507 VikLayerParam pref_lon[] = {
2508 { VIKING_PREFERENCES_NAMESPACE "default_longitude",
2509 VIK_LAYER_PARAM_DOUBLE,
2512 VIK_LAYER_WIDGET_SPINBUTTON,
2518 /* Get current center */
2520 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2522 /* Apply to preferences */
2523 VikLayerParamData vlp_data;
2524 vlp_data.d = ll.lat;
2525 a_preferences_run_setparam (vlp_data, pref_lat);
2526 vlp_data.d = ll.lon;
2527 a_preferences_run_setparam (vlp_data, pref_lon);
2528 /* Remember to save */
2529 a_preferences_save_to_file();
2532 static void clear_cb ( GtkAction *a, VikWindow *vw )
2534 vik_layers_panel_clear ( vw->viking_vlp );
2535 window_set_filename ( vw, NULL );
2539 static void window_close ( GtkAction *a, VikWindow *vw )
2541 if ( ! delete_event ( vw ) )
2542 gtk_widget_destroy ( GTK_WIDGET(vw) );
2545 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2547 if (save_file( NULL, vw)) {
2548 window_close( NULL, vw);
2555 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2557 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2558 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2560 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2561 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2566 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2568 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2569 GdkPixbuf *pixbuf_to_save;
2570 gdouble old_xmpp, old_ympp;
2571 GError *error = NULL;
2573 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2574 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2577 _("Generating image file...") );
2579 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2580 // Ensure dialog shown
2581 gtk_widget_show_all ( msgbox );
2583 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2584 while ( gtk_events_pending() )
2585 gtk_main_iteration ();
2586 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2587 // At least the empty box can give a clue something's going on + the statusbar msg...
2588 // Windows version under Wine OK!
2590 /* backup old zoom & set new */
2591 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2592 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2593 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2595 /* reset width and height: */
2596 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2598 /* draw all layers */
2601 /* save buffer as file. */
2602 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);
2603 if ( !pixbuf_to_save ) {
2604 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2605 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
2609 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2612 g_warning("Unable to write to file %s: %s", fn, error->message );
2613 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
2614 g_error_free (error);
2618 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2620 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2623 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2624 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2625 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2627 /* pretend like nothing happened ;) */
2628 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2629 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2630 vik_viewport_configure ( vw->viking_vvp );
2634 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 )
2636 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2637 gchar *name_of_file = g_malloc ( size );
2639 struct UTM utm_orig, utm;
2641 /* *** copied from above *** */
2642 GdkPixbuf *pixbuf_to_save;
2643 gdouble old_xmpp, old_ympp;
2644 GError *error = NULL;
2646 /* backup old zoom & set new */
2647 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2648 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2649 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2651 /* reset width and height: do this only once for all images (same size) */
2652 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2653 /* *** end copy from above *** */
2655 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2659 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2661 for ( y = 1; y <= tiles_h; y++ )
2663 for ( x = 1; x <= tiles_w; x++ )
2665 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2667 if ( tiles_w & 0x1 )
2668 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2670 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2671 if ( tiles_h & 0x1 ) /* odd */
2672 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2674 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2676 /* move to correct place. */
2677 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2681 /* save buffer as file. */
2682 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);
2683 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2686 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2687 g_error_free (error);
2690 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2694 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2695 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2696 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2697 vik_viewport_configure ( vw->viking_vvp );
2700 g_free ( name_of_file );
2703 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2705 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2706 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2708 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2709 gdouble zoom = pow (2, active-2 );
2711 gdouble width_min, width_max, height_min, height_max;
2714 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2715 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2717 /* TODO: support for xzoom and yzoom values */
2718 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2719 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2721 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2722 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2724 gtk_spin_button_set_value ( width_spin, width );
2725 gtk_spin_button_set_value ( height_spin, height );
2728 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2730 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2732 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2733 gdouble zoom = pow (2, active-2 );
2737 w = gtk_spin_button_get_value(width_spin) * zoom;
2738 h = gtk_spin_button_get_value(height_spin) * zoom;
2739 if (pass_along[4]) /* save many images; find TOTAL area covered */
2741 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2742 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2744 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2745 switch (dist_units) {
2746 case VIK_UNITS_DISTANCE_KILOMETRES:
2747 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2749 case VIK_UNITS_DISTANCE_MILES:
2750 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2753 label_text = g_strdup_printf ("Just to keep the compiler happy");
2754 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2757 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2758 g_free ( label_text );
2762 * Get an allocated filename (or directory as specified)
2764 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
2767 if ( one_image_only )
2770 if (!vw->save_img_dia) {
2771 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2773 GTK_FILE_CHOOSER_ACTION_SAVE,
2774 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2775 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2778 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
2780 GtkFileFilter *filter;
2781 filter = gtk_file_filter_new ();
2782 gtk_file_filter_set_name ( filter, _("All") );
2783 gtk_file_filter_add_pattern ( filter, "*" );
2784 gtk_file_chooser_add_filter ( chooser, filter );
2786 filter = gtk_file_filter_new ();
2787 gtk_file_filter_set_name ( filter, _("JPG") );
2788 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2789 gtk_file_chooser_add_filter ( chooser, filter );
2791 filter = gtk_file_filter_new ();
2792 gtk_file_filter_set_name ( filter, _("PNG") );
2793 gtk_file_filter_add_mime_type ( filter, "image/png");
2794 gtk_file_chooser_add_filter ( chooser, filter );
2797 gtk_file_chooser_set_filter ( chooser, filter );
2799 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2800 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2803 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
2804 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2805 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
2806 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 ) ) )
2809 gtk_widget_hide ( vw->save_img_dia );
2813 // For some reason this method is only written to work in UTM...
2814 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2815 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2819 if (!vw->save_img_dir_dia) {
2820 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2822 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2823 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2824 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2826 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2827 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2830 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
2831 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2833 gtk_widget_hide ( vw->save_img_dir_dia );
2838 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
2840 /* todo: default for answers inside VikWindow or static (thruout instance) */
2841 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2842 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2844 GTK_RESPONSE_REJECT,
2846 GTK_RESPONSE_ACCEPT,
2848 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2849 GtkWidget *png_radio, *jpeg_radio;
2850 GtkWidget *current_window_button;
2851 gpointer current_window_pass_along[7];
2852 GtkWidget *zoom_label, *zoom_combo;
2853 GtkWidget *total_size_label;
2855 /* only used if (!one_image_only) */
2856 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2858 width_label = gtk_label_new ( _("Width (pixels):") );
2859 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
2860 height_label = gtk_label_new ( _("Height (pixels):") );
2861 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
2863 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
2865 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2866 /* TODO: separate xzoom and yzoom factors */
2867 zoom_combo = create_zoom_combo_all_levels();
2869 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
2870 gint active = 2 + round ( log (mpp) / log (2) );
2872 // Can we not hard code size here?
2875 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
2877 total_size_label = gtk_label_new ( NULL );
2879 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2880 current_window_pass_along [0] = vw;
2881 current_window_pass_along [1] = width_spin;
2882 current_window_pass_along [2] = height_spin;
2883 current_window_pass_along [3] = zoom_combo;
2884 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
2885 current_window_pass_along [5] = NULL;
2886 current_window_pass_along [6] = total_size_label;
2887 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
2889 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
2890 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
2892 if ( ! vw->draw_image_save_as_png )
2893 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
2895 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
2896 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
2897 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
2898 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
2900 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), win_warning_label, FALSE, FALSE, 0);
2902 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
2903 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
2904 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
2905 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
2906 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_combo, FALSE, FALSE, 0);
2908 if ( ! one_image_only )
2910 GtkWidget *tiles_width_label, *tiles_height_label;
2912 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
2913 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2914 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
2915 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2916 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
2917 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
2918 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
2919 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
2921 current_window_pass_along [4] = tiles_width_spin;
2922 current_window_pass_along [5] = tiles_height_spin;
2923 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2924 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2926 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
2927 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2928 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2929 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2931 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
2933 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2935 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
2937 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
2939 gtk_widget_hide ( GTK_WIDGET(dialog) );
2941 gchar *fn = draw_image_filename ( vw, one_image_only );
2945 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
2946 gdouble zoom = pow (2, active-2 );
2948 if ( one_image_only )
2949 save_image_file ( vw, fn,
2950 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2951 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2953 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
2955 // NB is in UTM mode ATM
2956 save_image_dir ( vw, fn,
2957 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2958 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2960 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
2961 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
2962 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
2967 gtk_widget_destroy ( GTK_WIDGET(dialog) );
2971 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
2973 draw_to_image_file ( vw, TRUE );
2976 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
2978 draw_to_image_file ( vw, FALSE );
2981 #if GTK_CHECK_VERSION(2,10,0)
2982 static void print_cb ( GtkAction *a, VikWindow *vw )
2984 a_print(vw, vw->viking_vvp);
2988 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
2989 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
2991 VikViewportDrawMode drawmode;
2992 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
2993 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
2995 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
2996 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
2998 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
2999 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3001 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3002 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3005 g_critical("Houston, we've had a problem.");
3009 if ( !vw->only_updating_coord_mode_ui )
3011 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3012 if ( olddrawmode != drawmode )
3014 /* this takes care of coord mode too */
3015 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3016 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3017 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3018 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3019 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3026 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3028 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3029 g_assert(check_box);
3030 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3031 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3035 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3037 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3038 g_assert(check_box);
3039 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3040 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3044 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3046 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3047 g_assert(check_box);
3048 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3049 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3053 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3055 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3056 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3057 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3058 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3059 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3061 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3062 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3066 gtk_widget_destroy ( colorsd );
3069 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3071 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3072 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3073 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3074 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3075 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3077 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3078 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3082 gtk_widget_destroy ( colorsd );
3087 /***********************************************************************************************
3089 ***********************************************************************************************/
3091 static GtkActionEntry entries[] = {
3092 { "File", NULL, N_("_File"), 0, 0, 0 },
3093 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3094 { "View", NULL, N_("_View"), 0, 0, 0 },
3095 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3096 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3097 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3098 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3099 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3100 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3101 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3103 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3104 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3105 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3106 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3107 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3108 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3109 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3110 #ifdef VIK_CONFIG_GOOGLE
3111 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
3113 #ifdef VIK_CONFIG_OPENSTREETMAP
3114 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3116 #ifdef VIK_CONFIG_GEOCACHES
3117 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3119 #ifdef VIK_CONFIG_GEOTAG
3120 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3122 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3123 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3124 { "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 },
3125 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3127 #if GTK_CHECK_VERSION(2,10,0)
3128 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3131 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3132 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3134 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3135 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3136 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3137 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3138 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3139 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3140 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3141 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3142 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3143 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3144 { "Zoom0.25", NULL, N_("0.25"), NULL, NULL, (GCallback)draw_zoom_cb },
3145 { "Zoom0.5", NULL, N_("0.5"), NULL, NULL, (GCallback)draw_zoom_cb },
3146 { "Zoom1", NULL, N_("1"), NULL, NULL, (GCallback)draw_zoom_cb },
3147 { "Zoom2", NULL, N_("2"), NULL, NULL, (GCallback)draw_zoom_cb },
3148 { "Zoom4", NULL, N_("4"), NULL, NULL, (GCallback)draw_zoom_cb },
3149 { "Zoom8", NULL, N_("8"), NULL, NULL, (GCallback)draw_zoom_cb },
3150 { "Zoom16", NULL, N_("16"), NULL, NULL, (GCallback)draw_zoom_cb },
3151 { "Zoom32", NULL, N_("32"), NULL, NULL, (GCallback)draw_zoom_cb },
3152 { "Zoom64", NULL, N_("64"), NULL, NULL, (GCallback)draw_zoom_cb },
3153 { "Zoom128", NULL, N_("128"), NULL, NULL, (GCallback)draw_zoom_cb },
3154 { "Zoom256", NULL, N_("256"), NULL, NULL, (GCallback)draw_zoom_cb },
3155 { "Zoom512", NULL, N_("512"), NULL, NULL, (GCallback)draw_zoom_cb },
3156 { "Zoom1024", NULL, N_("1024"), NULL, NULL, (GCallback)draw_zoom_cb },
3157 { "Zoom2048", NULL, N_("2048"), NULL, NULL, (GCallback)draw_zoom_cb },
3158 { "Zoom4096", NULL, N_("4096"), NULL, NULL, (GCallback)draw_zoom_cb },
3159 { "Zoom8192", NULL, N_("8192"), NULL, NULL, (GCallback)draw_zoom_cb },
3160 { "Zoom16384", NULL, N_("16384"), NULL, NULL, (GCallback)draw_zoom_cb },
3161 { "Zoom32768", NULL, N_("32768"), NULL, NULL, (GCallback)draw_zoom_cb },
3162 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3163 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3164 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3165 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3166 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3168 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3169 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3170 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3171 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3172 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3173 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3174 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3175 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3176 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3178 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3179 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3183 /* FIXME use VIEWPORT_DRAWMODE values */
3184 static GtkRadioActionEntry mode_entries[] = {
3185 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3186 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3187 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3188 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3191 static GtkToggleActionEntry toggle_entries[] = {
3192 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3193 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3194 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3195 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3196 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3197 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3198 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3199 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3202 #include "menu.xml.h"
3203 static void window_create_ui( VikWindow *window )
3206 GtkActionGroup *action_group;
3207 GtkAccelGroup *accel_group;
3210 GtkIconFactory *icon_factory;
3211 GtkIconSet *icon_set;
3212 GtkRadioActionEntry *tools = NULL, *radio;
3215 uim = gtk_ui_manager_new ();
3218 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3219 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3220 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3221 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3224 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3225 g_error_free (error);
3229 action_group = gtk_action_group_new ("MenuActions");
3230 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3231 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3232 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3233 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3235 icon_factory = gtk_icon_factory_new ();
3236 gtk_icon_factory_add_default (icon_factory);
3238 register_vik_icons(icon_factory);
3240 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3241 // so that it can be applied to the UI in one action group add function call below
3243 for (i=0; i<window->vt->n_tools; i++) {
3244 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3245 radio = &tools[ntools];
3247 *radio = window->vt->tools[i].ti.radioActionEntry;
3248 radio->value = ntools;
3251 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3252 GtkActionEntry action;
3253 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3254 vik_layer_get_interface(i)->name,
3255 vik_layer_get_interface(i)->name,
3256 GTK_UI_MANAGER_MENUITEM, FALSE);
3258 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3259 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3260 gtk_icon_set_unref (icon_set);
3262 action.name = vik_layer_get_interface(i)->name;
3263 action.stock_id = vik_layer_get_interface(i)->name;
3264 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3265 action.accelerator = vik_layer_get_interface(i)->accelerator;
3266 action.tooltip = NULL;
3267 action.callback = (GCallback)menu_addlayer_cb;
3268 gtk_action_group_add_actions(action_group, &action, 1, window);
3270 if ( vik_layer_get_interface(i)->tools_count ) {
3271 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3272 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3275 // Further tool copying for to apply to the UI, also apply menu UI setup
3276 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3277 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3278 radio = &tools[ntools];
3281 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3282 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3283 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3284 GTK_UI_MANAGER_MENUITEM, FALSE);
3285 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3286 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3287 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3288 GTK_UI_MANAGER_TOOLITEM, FALSE);
3290 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3292 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3293 // Overwrite with actual number to use
3294 radio->value = ntools;
3297 g_object_unref (icon_factory);
3299 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3302 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3304 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3305 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3306 GtkAction *action = gtk_action_group_get_action(action_group,
3307 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3308 g_object_set(action, "sensitive", FALSE, NULL);
3311 window->action_group = action_group;
3313 accel_group = gtk_ui_manager_get_accel_group (uim);
3314 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3315 gtk_ui_manager_ensure_update (uim);
3317 setup_recent_files(window);
3321 // TODO - add method to add tool icons defined from outside this file
3322 // and remove the reverse dependency on icon definition from this file
3324 const GdkPixdata *data;
3327 { &mover_22_pixbuf, "vik-icon-pan" },
3328 { &zoom_18_pixbuf, "vik-icon-zoom" },
3329 { &ruler_18_pixbuf, "vik-icon-ruler" },
3330 { &select_18_pixbuf, "vik-icon-select" },
3331 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3332 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3333 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3334 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3335 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3336 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3337 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3338 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3339 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3340 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3341 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3344 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3347 register_vik_icons (GtkIconFactory *icon_factory)
3349 GtkIconSet *icon_set;
3352 for (i = 0; i < n_stock_icons; i++) {
3353 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3354 stock_icons[i].data, FALSE, NULL ));
3355 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3356 gtk_icon_set_unref (icon_set);
3360 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3362 return vw->selected_vtl;
3365 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3367 vw->selected_vtl = vtl;
3368 vw->containing_vtl = vtl;
3370 vw->selected_track = NULL;
3371 vw->selected_tracks = NULL;
3372 vw->selected_waypoint = NULL;
3373 vw->selected_waypoints = NULL;
3374 // Set highlight thickness
3375 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3378 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
3380 return vw->selected_tracks;
3383 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3385 vw->selected_tracks = ght;
3386 vw->containing_vtl = vtl;
3388 vw->selected_vtl = NULL;
3389 vw->selected_track = NULL;
3390 vw->selected_waypoint = NULL;
3391 vw->selected_waypoints = NULL;
3392 // Set highlight thickness
3393 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3396 gpointer vik_window_get_selected_track ( VikWindow *vw )
3398 return vw->selected_track;
3401 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
3403 vw->selected_track = vt;
3404 vw->containing_vtl = vtl;
3406 vw->selected_vtl = NULL;
3407 vw->selected_tracks = NULL;
3408 vw->selected_waypoint = NULL;
3409 vw->selected_waypoints = NULL;
3410 // Set highlight thickness
3411 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3414 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
3416 return vw->selected_waypoints;
3419 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3421 vw->selected_waypoints = ght;
3422 vw->containing_vtl = vtl;
3424 vw->selected_vtl = NULL;
3425 vw->selected_track = NULL;
3426 vw->selected_tracks = NULL;
3427 vw->selected_waypoint = NULL;
3430 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3432 return vw->selected_waypoint;
3435 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
3437 vw->selected_waypoint = vwp;
3438 vw->containing_vtl = vtl;
3440 vw->selected_vtl = NULL;
3441 vw->selected_track = NULL;
3442 vw->selected_tracks = NULL;
3443 vw->selected_waypoints = NULL;
3446 gboolean vik_window_clear_highlight ( VikWindow *vw )
3448 gboolean need_redraw = FALSE;
3449 if ( vw->selected_vtl != NULL ) {
3450 vw->selected_vtl = NULL;
3453 if ( vw->selected_track != NULL ) {
3454 vw->selected_track = NULL;
3457 if ( vw->selected_tracks != NULL ) {
3458 vw->selected_tracks = NULL;
3461 if ( vw->selected_waypoint != NULL ) {
3462 vw->selected_waypoint = NULL;
3465 if ( vw->selected_waypoints != NULL ) {
3466 vw->selected_waypoints = NULL;