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 );
76 static VikWindow *window_new ();
78 static void draw_update ( VikWindow *vw );
80 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
83 static void open_window ( VikWindow *vw, GSList *files );
84 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type );
85 static void destroy_window ( GtkWidget *widget,
90 static gboolean delete_event( VikWindow *vw );
92 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
94 static void window_configure_event ( VikWindow *vw );
95 static void draw_sync ( VikWindow *vw );
96 static void draw_redraw ( VikWindow *vw );
97 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
98 static void draw_click ( VikWindow *vw, GdkEventButton *event );
99 static void draw_release ( VikWindow *vw, GdkEventButton *event );
100 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
101 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
102 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
103 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
105 static void draw_status ( VikWindow *vw );
107 /* End Drawing Functions */
109 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
110 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
111 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
113 /* tool management */
119 #define TOOL_LAYER_TYPE_NONE -1
124 toolbox_tool_t *tools;
128 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
129 static toolbox_tools_t* toolbox_create(VikWindow *vw);
130 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
131 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
132 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
133 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
134 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
135 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
136 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
140 static void window_create_ui( VikWindow *window );
141 static void register_vik_icons (GtkIconFactory *icon_factory);
144 static void load_file ( GtkAction *a, VikWindow *vw );
145 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
146 static gboolean save_file ( GtkAction *a, VikWindow *vw );
147 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
148 static gboolean window_save ( VikWindow *vw );
152 VikViewport *viking_vvp;
153 VikLayersPanel *viking_vlp;
154 VikStatusbar *viking_vs;
157 GtkComboBox *tb_zoom_combo;
159 GtkItemFactory *item_factory;
161 /* tool management state */
164 guint16 tool_layer_id;
165 guint16 tool_tool_id;
167 GtkActionGroup *action_group;
172 guint draw_image_width, draw_image_height;
173 gboolean draw_image_save_as_png;
178 GtkWidget *open_dia, *save_dia;
179 GtkWidget *save_img_dia, *save_img_dir_dia;
181 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
184 /* half-drawn update */
186 VikCoord trigger_center;
188 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
189 /* Only one of these items can be selected at the same time */
190 gpointer selected_vtl; /* notionally VikTrwLayer */
191 gpointer selected_tracks; /* notionally GList */
192 gpointer selected_track; /* notionally VikTrack */
193 gpointer selected_waypoints; /* notionally GList */
194 gpointer selected_waypoint; /* notionally VikWaypoint */
195 /* only use for individual track or waypoint */
196 ////// NEED TO THINK ABOUT VALIDITY OF THESE //////
197 ////// i.e. what happens when stuff is deleted elsewhere //////
198 ////// Generally seems alright as can not access them //////
199 ////// containing_vtl now seems unecessary //////
200 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
201 gpointer containing_vtl; /* notionally VikTrwLayer */
215 VW_OPENWINDOW_SIGNAL,
216 VW_STATUSBAR_UPDATE_SIGNAL,
220 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
222 // TODO get rid of this as this is unnecessary duplication...
223 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
225 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
227 VikViewport * vik_window_viewport(VikWindow *vw)
229 return(vw->viking_vvp);
232 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
234 return(vw->viking_vlp);
238 * Returns the statusbar for the window
240 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
242 return vw->viking_vs;
246 * For signalling the update from a background thread
248 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
250 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message, vs_type );
254 * For the actual statusbar update!
256 static gboolean statusbar_idle_update ( gpointer indata )
258 gpointer *data = indata;
259 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
264 * Update statusbar in the main thread
266 static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
268 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
269 static gpointer data[3];
270 data[0] = vw->viking_vs;
271 data[1] = (gchar*) message;
272 data[2] = GINT_TO_POINTER(vs_type);
273 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
276 // Actual signal handlers
277 static void destroy_window ( GtkWidget *widget,
280 if ( ! --window_count )
284 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type )
286 window_statusbar_update ( vw, message, vs_type );
289 VikWindow *vik_window_new_window ()
291 if ( window_count < MAX_WINDOWS )
293 VikWindow *vw = window_new ();
295 g_signal_connect (G_OBJECT (vw), "destroy",
296 G_CALLBACK (destroy_window), NULL);
297 g_signal_connect (G_OBJECT (vw), "newwindow",
298 G_CALLBACK (vik_window_new_window), NULL);
299 g_signal_connect (G_OBJECT (vw), "openwindow",
300 G_CALLBACK (open_window), NULL);
301 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
302 G_CALLBACK (statusbar_update), vw);
304 gtk_widget_show_all ( GTK_WIDGET(vw) );
313 static void open_window ( VikWindow *vw, GSList *files )
315 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
316 GSList *cur_file = files;
318 // Only open a new window if a viking file
319 gchar *file_name = cur_file->data;
320 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
321 VikWindow *newvw = vik_window_new_window ();
323 vik_window_open_file ( newvw, file_name, TRUE );
326 vik_window_open_file ( vw, file_name, change_fn );
329 cur_file = g_slist_next (cur_file);
331 g_slist_free (files);
335 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
337 int i, j, tool_count;
338 VikLayerInterface *layer_interface;
340 if (!vw->action_group) return;
342 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
344 layer_interface = vik_layer_get_interface(i);
345 tool_count = layer_interface->tools_count;
347 for (j = 0; j < tool_count; j++) {
348 action = gtk_action_group_get_action(vw->action_group,
349 layer_interface->tools[j].radioActionEntry.name);
350 g_object_set(action, "sensitive", i == vl->type, NULL);
355 static void window_finalize ( GObject *gob )
357 VikWindow *vw = VIK_WINDOW(gob);
358 g_return_if_fail ( vw != NULL );
360 a_background_remove_window ( vw );
362 G_OBJECT_CLASS(parent_class)->finalize(gob);
366 static void vik_window_class_init ( VikWindowClass *klass )
369 GObjectClass *object_class;
371 window_signals[VW_NEWWINDOW_SIGNAL] = g_signal_new ( "newwindow", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, newwindow), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
372 window_signals[VW_OPENWINDOW_SIGNAL] = g_signal_new ( "openwindow", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, openwindow), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
373 window_signals[VW_STATUSBAR_UPDATE_SIGNAL] = g_signal_new ( "statusbarupdate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, statusbarupdate), NULL, NULL, gtk_marshal_VOID__POINTER_UINT, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
375 object_class = G_OBJECT_CLASS (klass);
377 object_class->finalize = window_finalize;
379 parent_class = g_type_class_peek_parent (klass);
383 static void set_toolbar_zoom ( VikWindow *vw, gdouble mpp )
385 gint active = 2 + round ( log (mpp) / log (2) );
386 // Can we not hard code size here?
389 gtk_combo_box_set_active ( vw->tb_zoom_combo, active );
392 static void zoom_changed ( GtkComboBox *combo, VikWindow *vw )
394 gint active = gtk_combo_box_get_active ( combo );
396 // But has it really changed?
397 // Unfortunately this function gets invoked even on manual setting of the combo value
398 gdouble zoom_request = pow (2, active-2 );
399 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
400 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
401 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
402 // Force drawing update
407 static GtkWidget *create_zoom_combo_all_levels ()
409 GtkWidget *zoom_combo = gtk_combo_box_new_text();
410 GtkComboBox *combo = GTK_COMBO_BOX ( zoom_combo );
411 gtk_combo_box_append_text ( combo, "0.25");
412 gtk_combo_box_append_text ( combo, "0.5");
413 gtk_combo_box_append_text ( combo, "1");
414 gtk_combo_box_append_text ( combo, "2");
415 gtk_combo_box_append_text ( combo, "4");
416 gtk_combo_box_append_text ( combo, "8");
417 gtk_combo_box_append_text ( combo, "16");
418 gtk_combo_box_append_text ( combo, "32");
419 gtk_combo_box_append_text ( combo, "64");
420 gtk_combo_box_append_text ( combo, "128");
421 gtk_combo_box_append_text ( combo, "256");
422 gtk_combo_box_append_text ( combo, "512");
423 gtk_combo_box_append_text ( combo, "1024");
424 gtk_combo_box_append_text ( combo, "2048");
425 gtk_combo_box_append_text ( combo, "4096");
426 gtk_combo_box_append_text ( combo, "8192");
427 gtk_combo_box_append_text ( combo, "16384");
428 gtk_combo_box_append_text ( combo, "32768");
430 gtk_widget_set_tooltip_text (GTK_WIDGET (combo), _("Select zoom level"));
434 static void vik_window_init ( VikWindow *vw )
436 GtkWidget *main_vbox;
439 vw->action_group = NULL;
441 vw->viking_vvp = vik_viewport_new();
442 vw->viking_vlp = vik_layers_panel_new();
443 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
444 vw->viking_vs = vik_statusbar_new();
446 vw->vt = toolbox_create(vw);
447 window_create_ui(vw);
448 window_set_filename (vw, NULL);
449 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
451 // Set the default tool
452 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
455 vw->item_factory = NULL;
457 vw->modified = FALSE;
458 vw->only_updating_coord_mode_ui = FALSE;
460 vw->pan_move = FALSE;
461 vw->pan_x = vw->pan_y = -1;
462 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
463 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
464 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
466 main_vbox = gtk_vbox_new(FALSE, 1);
467 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
469 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
470 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
471 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
472 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
474 vik_ext_tools_add_menu_items ( vw, vw->uim );
476 vw->tb_zoom_combo = GTK_COMBO_BOX(create_zoom_combo_all_levels());
478 g_signal_connect ( G_OBJECT(vw->tb_zoom_combo), "changed", G_CALLBACK(zoom_changed), vw );
480 // Add the zoom combo to the toolbar at the end
481 GtkToolItem *tooli = gtk_tool_item_new ();
482 gtk_container_add ( GTK_CONTAINER(tooli), GTK_WIDGET (vw->tb_zoom_combo) );
483 gtk_toolbar_insert ( vw->toolbar, tooli, gtk_toolbar_get_n_items (vw->toolbar) );
485 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
487 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
488 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
489 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 );
490 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
491 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
492 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
493 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
494 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
496 // Allow key presses to be processed anywhere
497 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
499 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
501 hpaned = gtk_hpaned_new ();
502 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
503 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
505 /* This packs the button into the window (a gtk container). */
506 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
508 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
510 a_background_add_window ( vw );
514 vw->save_img_dia = NULL;
515 vw->save_img_dir_dia = NULL;
518 static VikWindow *window_new ()
520 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
524 * Update the displayed map
525 * Only update the top most visible map layer
526 * ATM this assumes (as per defaults) the top most map has full alpha setting
527 * such that other other maps even though they may be active will not be seen
528 * It's more complicated to work out which maps are actually visible due to alpha settings
529 * and overkill for this simple refresh method.
531 static void simple_map_update ( VikWindow *vw, gboolean only_new )
533 // Find the most relevent single map layer to operate on
534 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
536 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
540 * This is the global key press handler
541 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
543 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
545 // The keys handled here are not in the menuing system for a couple of reasons:
546 // . Keeps the menu size compact (alebit at expense of discoverably)
547 // . Allows differing key bindings to perform the same actions
549 // First decide if key events are related to the maps layer
550 gboolean map_download = FALSE;
551 gboolean map_download_only_new = TRUE; // Only new or reload
553 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
555 // Standard 'Refresh' keys: F5 or Ctrl+r
556 // Note 'F5' is actually handled via draw_refresh_cb() later on
557 // (not 'R' it's 'r' notice the case difference!!)
558 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
560 map_download_only_new = TRUE;
562 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
563 // Note the use of uppercase R here since shift key has been pressed
564 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
565 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
567 map_download_only_new = FALSE;
570 if ( map_download ) {
571 simple_map_update ( vw, map_download_only_new );
574 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
575 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
576 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
577 if ( vl && ltype == vl->type )
578 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
581 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
582 if ( vw->current_tool < TOOL_LAYER ) {
583 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
584 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
585 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
589 /* Restore Main Menu via Escape key if the user has hidden it */
590 /* This key is more likely to be used as they may not remember the function key */
591 if ( event->keyval == GDK_Escape ) {
592 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
594 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
596 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
597 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
598 return TRUE; /* handled keypress */
603 return FALSE; /* don't handle the keypress */
606 static gboolean delete_event( VikWindow *vw )
608 #ifdef VIKING_PROMPT_IF_MODIFIED
615 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
616 _("Do you want to save the changes you made to the document \"%s\"?\n"
618 "Your changes will be lost if you don't save them."),
619 vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") ) );
620 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
621 switch ( gtk_dialog_run ( dia ) )
623 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
624 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
625 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
632 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
634 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
637 static void draw_update ( VikWindow *vw )
643 static void draw_sync ( VikWindow *vw )
645 vik_viewport_sync(vw->viking_vvp);
650 * Split the status update, as sometimes only need to update the tool part
651 * also on initialization the zoom related stuff is not ready to be used
653 static void draw_status_tool ( VikWindow *vw )
655 if ( vw->current_tool == TOOL_LAYER )
656 // Use tooltip rather than the internal name as the tooltip is i8n
657 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 );
659 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
662 static void draw_status ( VikWindow *vw )
664 static gchar zoom_level[22];
665 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
666 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
667 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
669 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
671 if ( (int)xmpp - xmpp < 0.0 )
672 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
674 /* xmpp should be a whole number so don't show useless .000 bit */
675 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
677 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
678 // OK maybe not quite in the statusbar - but we have the zoom level so use it
679 set_toolbar_zoom ( vw, xmpp ); // But it's a status of some kind!
681 draw_status_tool ( vw );
684 void vik_window_set_redraw_trigger(VikLayer *vl)
686 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
691 static void window_configure_event ( VikWindow *vw )
693 static int first = 1;
696 // This is a hack to set the cursor corresponding to the first tool
697 // FIXME find the correct way to initialize both tool and its cursor
698 const GdkCursor *cursor = NULL;
700 cursor = toolbox_get_cursor(vw->vt, "Pan");
701 /* We set cursor, even if it is NULL: it resets to default */
702 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
706 static void draw_redraw ( VikWindow *vw )
708 VikCoord old_center = vw->trigger_center;
709 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
710 VikLayer *new_trigger = vw->trigger;
712 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
715 ; /* do nothing -- have to redraw everything. */
716 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
717 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
719 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
722 vik_viewport_clear ( vw->viking_vvp);
723 vik_layers_panel_draw_all ( vw->viking_vlp );
724 vik_viewport_draw_scale ( vw->viking_vvp );
725 vik_viewport_draw_copyright ( vw->viking_vvp );
726 vik_viewport_draw_centermark ( vw->viking_vvp );
727 vik_viewport_draw_logo ( vw->viking_vvp );
729 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
732 gboolean draw_buf_done = TRUE;
734 static gboolean draw_buf(gpointer data)
736 gpointer *pass_along = data;
738 gdk_draw_drawable (pass_along[0], pass_along[1],
739 pass_along[2], 0, 0, 0, 0, -1, -1);
740 draw_buf_done = TRUE;
746 /* Mouse event handlers ************************************************************************/
748 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
750 /* set panning origin */
751 vw->pan_move = FALSE;
752 vw->pan_x = (gint) event->x;
753 vw->pan_y = (gint) event->y;
756 static void draw_click (VikWindow *vw, GdkEventButton *event)
758 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
760 /* middle button pressed. we reserve all middle button and scroll events
761 * for panning and zooming; tools only get left/right/movement
763 if ( event->button == 2) {
764 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
765 // Tool still may need to do something (such as disable something)
766 toolbox_click(vw->vt, event);
767 vik_window_pan_click ( vw, event );
770 toolbox_click(vw->vt, event);
774 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
776 if ( vw->pan_x != -1 ) {
777 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
778 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
780 vw->pan_x = event->x;
781 vw->pan_y = event->y;
786 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
788 static VikCoord coord;
789 static struct UTM utm;
790 static struct LatLon ll;
791 #define BUFFER_SIZE 50
792 static char pointer_buf[BUFFER_SIZE];
793 gchar *lat = NULL, *lon = NULL;
796 VikDemInterpol interpol_method;
798 /* This is a hack, but work far the best, at least for single pointer systems.
799 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
801 gdk_window_get_pointer (event->window, &x, &y, NULL);
805 toolbox_move(vw->vt, event);
807 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
808 vik_coord_to_utm ( &coord, &utm );
810 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
811 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
812 // ZONE[N|S] EASTING NORTHING
813 lat = g_malloc(4*sizeof(gchar));
814 // NB zone is stored in a char but is an actual number
815 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
816 lon = g_malloc(16*sizeof(gchar));
817 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
820 a_coords_utm_to_latlon ( &utm, &ll );
821 a_coords_latlon_to_string ( &ll, &lat, &lon );
824 /* Change interpolate method according to scale */
825 zoom = vik_viewport_get_zoom(vw->viking_vvp);
827 interpol_method = VIK_DEM_INTERPOL_NONE;
828 else if (zoom >= 1.0)
829 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
831 interpol_method = VIK_DEM_INTERPOL_BEST;
832 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
833 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
834 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
836 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
839 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
844 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
846 vik_window_pan_move ( vw, event );
848 /* This is recommended by the GTK+ documentation, but does not work properly.
849 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
850 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
852 /* gdk_event_request_motions ( event ); */
855 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
857 if ( vw->pan_move == FALSE )
858 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
860 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
861 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
862 vw->pan_move = FALSE;
863 vw->pan_x = vw->pan_y = -1;
867 static void draw_release ( VikWindow *vw, GdkEventButton *event )
869 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
871 if ( event->button == 2 ) { /* move / pan */
872 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
873 // Tool still may need to do something (such as reenable something)
874 toolbox_release(vw->vt, event);
875 vik_window_pan_release ( vw, event );
878 toolbox_release(vw->vt, event);
882 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
884 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
885 if ( modifiers == GDK_CONTROL_MASK ) {
886 /* control == pan up & down */
887 if ( event->direction == GDK_SCROLL_UP )
888 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
890 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 );
891 } else if ( modifiers == GDK_SHIFT_MASK ) {
892 /* shift == pan left & right */
893 if ( event->direction == GDK_SCROLL_UP )
894 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
896 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 );
897 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
898 // This zoom is on the center position
899 if ( event->direction == GDK_SCROLL_UP )
900 vik_viewport_zoom_in (vw->viking_vvp);
902 vik_viewport_zoom_out (vw->viking_vvp);
904 /* make sure mouse is still over the same point on the map when we zoom */
907 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
908 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
909 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
910 if ( event->direction == GDK_SCROLL_UP )
911 vik_viewport_zoom_in (vw->viking_vvp);
913 vik_viewport_zoom_out(vw->viking_vvp);
914 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
915 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
916 center_y + (y - event->y) );
924 /********************************************************************************
926 ********************************************************************************/
927 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
931 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
932 GdkGC *thickgc = gdk_gc_new(d);
934 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
935 gdouble dx = (x2-x1)/len*10;
936 gdouble dy = (y2-y1)/len*10;
937 gdouble c = cos(15.0 * M_PI/180.0);
938 gdouble s = sin(15.0 * M_PI/180.0);
940 gdouble baseangle = 0;
943 /* draw line with arrow ends */
945 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
946 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
947 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
950 a_viewport_clip_line(&x1, &y1, &x2, &y2);
951 gdk_draw_line(d, gc, x1, y1, x2, y2);
953 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
954 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
955 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
956 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
957 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
958 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
963 angle = atan2(dy, dx) + M_PI_2;
965 if ( vik_viewport_get_drawmode ( vvp ) == VIK_VIEWPORT_DRAWMODE_UTM) {
971 vik_viewport_screen_to_coord ( vvp, x1, y1, &test );
972 vik_coord_to_latlon ( &test, &ll );
973 ll.lat += vik_viewport_get_ympp ( vvp ) * vik_viewport_get_height ( vvp ) / 11000.0; // about 11km per degree latitude
974 a_coords_latlon_to_utm ( &ll, &u );
975 vik_coord_load_from_utm ( &test, VIK_VIEWPORT_DRAWMODE_UTM, &u );
976 vik_viewport_coord_to_screen ( vvp, &test, &tx, &ty );
978 baseangle = M_PI - atan2(tx-x1, ty-y1);
989 gdk_gc_copy(thickgc, gc);
990 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
991 gdk_color_parse("#2255cc", &color);
992 gdk_gc_set_rgb_fg_color(thickgc, &color);
994 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);
997 gdk_gc_copy(thickgc, gc);
998 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
999 for (i=0; i<180; i++) {
1000 c = cos(i*M_PI/90.0 + baseangle);
1001 s = sin(i*M_PI/90.0 + baseangle);
1004 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1006 gdouble ticksize = 2*CW;
1007 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1011 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1012 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1013 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1014 c = (CR+CW*2)*cos(baseangle);
1015 s = (CR+CW*2)*sin(baseangle);
1016 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1017 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1020 #define LABEL(x, y, w, h) { \
1021 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1022 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1023 gdk_draw_layout(d, gc, (x), (y), pl); }
1025 gint wd, hd, xd, yd;
1026 gint wb, hb, xb, yb;
1028 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1029 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
1030 pango_layout_set_text(pl, "N", -1);
1031 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1033 /* draw label with distance */
1034 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1035 switch (dist_units) {
1036 case VIK_UNITS_DISTANCE_KILOMETRES:
1037 if (distance >= 1000 && distance < 100000) {
1038 g_sprintf(str, "%3.2f km", distance/1000.0);
1039 } else if (distance < 1000) {
1040 g_sprintf(str, "%d m", (int)distance);
1042 g_sprintf(str, "%d km", (int)distance/1000);
1045 case VIK_UNITS_DISTANCE_MILES:
1046 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1047 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1048 } else if (distance < VIK_MILES_TO_METERS(1)) {
1049 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1051 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1055 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1058 pango_layout_set_text(pl, str, -1);
1060 pango_layout_get_pixel_size ( pl, &wd, &hd );
1062 xd = (x1+x2)/2 + dy;
1063 yd = (y1+y2)/2 - hd/2 - dx;
1065 xd = (x1+x2)/2 - dy;
1066 yd = (y1+y2)/2 - hd/2 + dx;
1069 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1074 LABEL(xd, yd, wd, hd);
1076 /* draw label with bearing */
1077 g_sprintf(str, "%3.1f°", angle*180.0/M_PI);
1078 pango_layout_set_text(pl, str, -1);
1079 pango_layout_get_pixel_size ( pl, &wb, &hb );
1080 xb = x1 + CR*cos(angle-M_PI_2);
1081 yb = y1 + CR*sin(angle-M_PI_2);
1083 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1089 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1090 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1094 LABEL(xb, yb, wb, hb);
1098 g_object_unref ( G_OBJECT ( pl ) );
1099 g_object_unref ( G_OBJECT ( labgc ) );
1100 g_object_unref ( G_OBJECT ( thickgc ) );
1106 gboolean has_oldcoord;
1108 } ruler_tool_state_t;
1110 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1112 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1115 s->has_oldcoord = FALSE;
1119 static void ruler_destroy (ruler_tool_state_t *s)
1124 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1129 if ( event->button == 1 ) {
1130 gchar *lat=NULL, *lon=NULL;
1131 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1132 vik_coord_to_latlon ( &coord, &ll );
1133 a_coords_latlon_to_string ( &ll, &lat, &lon );
1134 if ( s->has_oldcoord ) {
1135 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1136 switch (dist_units) {
1137 case VIK_UNITS_DISTANCE_KILOMETRES:
1138 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1140 case VIK_UNITS_DISTANCE_MILES:
1141 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1144 temp = g_strdup_printf ("Just to keep the compiler happy");
1145 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1148 s->has_oldcoord = FALSE;
1151 temp = g_strdup_printf ( "%s %s", lat, lon );
1152 s->has_oldcoord = TRUE;
1155 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1158 s->oldcoord = coord;
1161 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1162 draw_update ( s->vw );
1164 return VIK_LAYER_TOOL_ACK;
1167 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1169 VikViewport *vvp = s->vvp;
1170 VikWindow *vw = s->vw;
1176 if ( s->has_oldcoord ) {
1177 int oldx, oldy, w1, h1, w2, h2;
1178 static GdkPixmap *buf = NULL;
1179 gchar *lat=NULL, *lon=NULL;
1180 w1 = vik_viewport_get_width(vvp);
1181 h1 = vik_viewport_get_height(vvp);
1183 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1185 gdk_drawable_get_size(buf, &w2, &h2);
1186 if (w1 != w2 || h1 != h2) {
1187 g_object_unref ( G_OBJECT ( buf ) );
1188 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1191 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1192 vik_coord_to_latlon ( &coord, &ll );
1193 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1195 gdk_draw_drawable (buf, GTK_WIDGET(vvp)->style->black_gc,
1196 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1197 draw_ruler(vvp, buf, GTK_WIDGET(vvp)->style->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1198 if (draw_buf_done) {
1199 static gpointer pass_along[3];
1200 pass_along[0] = GTK_WIDGET(vvp)->window;
1201 pass_along[1] = GTK_WIDGET(vvp)->style->black_gc;
1202 pass_along[2] = buf;
1203 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1204 draw_buf_done = FALSE;
1206 a_coords_latlon_to_string(&ll, &lat, &lon);
1207 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1208 switch (dist_units) {
1209 case VIK_UNITS_DISTANCE_KILOMETRES:
1210 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1212 case VIK_UNITS_DISTANCE_MILES:
1213 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1216 temp = g_strdup_printf ("Just to keep the compiler happy");
1217 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1219 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1222 return VIK_LAYER_TOOL_ACK;
1225 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1227 return VIK_LAYER_TOOL_ACK;
1230 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1232 draw_update ( s->vw );
1235 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1237 if (event->keyval == GDK_Escape) {
1238 s->has_oldcoord = FALSE;
1239 ruler_deactivate ( vl, s );
1242 // Regardless of whether we used it, return false so other GTK things may use it
1246 static VikToolInterface ruler_tool =
1247 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1248 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1249 (VikToolConstructorFunc) ruler_create,
1250 (VikToolDestructorFunc) ruler_destroy,
1251 (VikToolActivationFunc) NULL,
1252 (VikToolActivationFunc) ruler_deactivate,
1253 (VikToolMouseFunc) ruler_click,
1254 (VikToolMouseMoveFunc) ruler_move,
1255 (VikToolMouseFunc) ruler_release,
1256 (VikToolKeyFunc) ruler_key_press,
1258 GDK_CURSOR_IS_PIXMAP,
1259 &cursor_ruler_pixbuf };
1260 /*** end ruler code ********************************************************/
1264 /********************************************************************************
1266 ********************************************************************************/
1271 // Track zoom bounds for zoom tool with shift modifier:
1272 gboolean bounds_active;
1275 } zoom_tool_state_t;
1278 * In case the screen size has changed
1280 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1284 // Allocate a drawing area the size of the viewport
1285 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1286 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1288 if ( !zts->pixmap ) {
1290 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1293 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1295 if ( w1 != w2 || h1 != h2 ) {
1296 // Has changed - delete and recreate with new values
1297 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1298 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1302 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1304 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1309 zts->bounds_active = FALSE;
1313 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1316 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1320 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1322 zts->vw->modified = TRUE;
1323 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1327 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1328 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1330 gboolean skip_update = FALSE;
1332 zts->bounds_active = FALSE;
1334 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1335 // This zoom is on the center position
1336 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1337 if ( event->button == 1 )
1338 vik_viewport_zoom_in (zts->vw->viking_vvp);
1339 else if ( event->button == 3 )
1340 vik_viewport_zoom_out (zts->vw->viking_vvp);
1342 else if ( modifiers == GDK_CONTROL_MASK ) {
1343 // This zoom is to recenter on the mouse position
1344 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1345 if ( event->button == 1 )
1346 vik_viewport_zoom_in (zts->vw->viking_vvp);
1347 else if ( event->button == 3 )
1348 vik_viewport_zoom_out (zts->vw->viking_vvp);
1350 else if ( modifiers == GDK_SHIFT_MASK ) {
1351 // Get start of new zoom bounds
1352 if ( event->button == 1 ) {
1353 zts->bounds_active = TRUE;
1354 zts->start_x = (gint) event->x;
1355 zts->start_y = (gint) event->y;
1360 /* make sure mouse is still over the same point on the map when we zoom */
1361 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1362 if ( event->button == 1 )
1363 vik_viewport_zoom_in (zts->vw->viking_vvp);
1364 else if ( event->button == 3 )
1365 vik_viewport_zoom_out(zts->vw->viking_vvp);
1366 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1367 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1368 center_x + (x - event->x),
1369 center_y + (y - event->y) );
1373 draw_update ( zts->vw );
1375 return VIK_LAYER_TOOL_ACK;
1378 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1380 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1382 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1383 zoomtool_resize_pixmap ( zts );
1385 // Blank out currently drawn area
1386 gdk_draw_drawable ( zts->pixmap,
1387 GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc,
1388 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1389 0, 0, 0, 0, -1, -1);
1391 // Calculate new box starting point & size in pixels
1392 int xx, yy, width, height;
1393 if ( event->y > zts->start_y ) {
1395 height = event->y-zts->start_y;
1399 height = zts->start_y-event->y;
1401 if ( event->x > zts->start_x ) {
1403 width = event->x-zts->start_x;
1407 width = zts->start_x-event->x;
1411 gdk_draw_rectangle (zts->pixmap, GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, FALSE, xx, yy, width, height);
1413 // Only actually draw when there's time to do so
1414 if (draw_buf_done) {
1415 static gpointer pass_along[3];
1416 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1417 pass_along[1] = GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc;
1418 pass_along[2] = zts->pixmap;
1419 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1420 draw_buf_done = FALSE;
1423 return VIK_LAYER_TOOL_ACK;
1426 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1428 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1430 zts->bounds_active = FALSE;
1432 // Ensure haven't just released on the exact same position
1433 // i.e. probably haven't moved the mouse at all
1434 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1436 VikCoord coord1, coord2;
1437 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1438 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1440 // From the extend of the bounds pick the best zoom level
1441 // c.f. trw_layer_zoom_to_show_latlons()
1442 // Maybe refactor...
1443 struct LatLon ll1, ll2;
1444 vik_coord_to_latlon(&coord1, &ll1);
1445 vik_coord_to_latlon(&coord2, &ll2);
1446 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1447 (ll1.lon+ll2.lon)/2 };
1449 VikCoord new_center;
1450 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1451 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1453 /* Convert into definite 'smallest' and 'largest' positions */
1454 struct LatLon minmin;
1455 if ( ll1.lat < ll2.lat )
1456 minmin.lat = ll1.lat;
1458 minmin.lat = ll2.lat;
1460 struct LatLon maxmax;
1461 if ( ll1.lon > ll2.lon )
1462 maxmax.lon = ll1.lon;
1464 maxmax.lon = ll2.lon;
1466 /* Always recalculate the 'best' zoom level */
1467 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1468 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1470 gdouble min_lat, max_lat, min_lon, max_lon;
1471 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1472 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1473 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1474 /* NB I think the logic used in this test to determine if the bounds is within view
1475 fails if track goes across 180 degrees longitude.
1476 Hopefully that situation is not too common...
1477 Mind you viking doesn't really do edge locations to well anyway */
1478 if ( min_lat < minmin.lat &&
1479 max_lat > minmin.lat &&
1480 min_lon < maxmax.lon &&
1481 max_lon > maxmax.lon )
1482 /* Found within zoom level */
1487 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1490 draw_update ( zts->vw );
1492 return VIK_LAYER_TOOL_ACK;
1495 static VikToolInterface zoom_tool =
1496 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1497 (VikToolConstructorFunc) zoomtool_create,
1498 (VikToolDestructorFunc) zoomtool_destroy,
1499 (VikToolActivationFunc) NULL,
1500 (VikToolActivationFunc) NULL,
1501 (VikToolMouseFunc) zoomtool_click,
1502 (VikToolMouseMoveFunc) zoomtool_move,
1503 (VikToolMouseFunc) zoomtool_release,
1506 GDK_CURSOR_IS_PIXMAP,
1507 &cursor_zoom_pixbuf };
1508 /*** end zoom code ********************************************************/
1510 /********************************************************************************
1512 ********************************************************************************/
1513 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1518 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1520 vw->modified = TRUE;
1521 if ( event->button == 1 )
1522 vik_window_pan_click ( vw, event );
1524 return VIK_LAYER_TOOL_ACK;
1527 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1529 vik_window_pan_move ( vw, event );
1530 return VIK_LAYER_TOOL_ACK;
1533 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1535 if ( event->button == 1 )
1536 vik_window_pan_release ( vw, event );
1537 return VIK_LAYER_TOOL_ACK;
1540 static VikToolInterface pan_tool =
1541 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1542 (VikToolConstructorFunc) pantool_create,
1543 (VikToolDestructorFunc) NULL,
1544 (VikToolActivationFunc) NULL,
1545 (VikToolActivationFunc) NULL,
1546 (VikToolMouseFunc) pantool_click,
1547 (VikToolMouseMoveFunc) pantool_move,
1548 (VikToolMouseFunc) pantool_release,
1552 /*** end pan code ********************************************************/
1554 /********************************************************************************
1556 ********************************************************************************/
1557 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1559 tool_ed_t *t = g_new(tool_ed_t, 1);
1563 t->is_waypoint = FALSE;
1567 static void selecttool_destroy (tool_ed_t *t)
1575 GdkEventButton *event;
1576 tool_ed_t *tool_edit;
1579 static void click_layer_selected (VikLayer *vl, clicker *ck)
1581 /* Do nothing when function call returns true; */
1582 /* i.e. stop on first found item */
1585 if ( vik_layer_get_interface(vl->type)->select_click )
1586 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1589 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1591 /* Only allow selection on primary button */
1592 if ( event->button == 1 ) {
1593 /* Enable click to apply callback to potentially all track/waypoint layers */
1594 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1595 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1598 ck.vvp = t->vw->viking_vvp;
1601 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1604 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1607 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1609 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1610 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1611 gint type = vik_treeview_item_get_type ( vtv, &iter );
1612 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1613 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1615 vik_treeview_item_unselect ( vtv, &iter );
1616 if ( vik_window_clear_highlight ( t->vw ) )
1617 draw_update ( t->vw );
1622 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1624 /* Act on currently selected item to show menu */
1625 if ( t->vw->selected_track || t->vw->selected_waypoint )
1626 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1627 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1630 return VIK_LAYER_TOOL_ACK;
1633 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1635 /* Only allow selection on primary button */
1636 if ( event->button == 1 ) {
1637 // Don't care about vl here
1639 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1640 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1642 return VIK_LAYER_TOOL_ACK;
1645 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1647 /* Only allow selection on primary button */
1648 if ( event->button == 1 ) {
1649 // Don't care about vl here
1651 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1652 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1654 return VIK_LAYER_TOOL_ACK;
1657 static VikToolInterface select_tool =
1658 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1659 (VikToolConstructorFunc) selecttool_create,
1660 (VikToolDestructorFunc) selecttool_destroy,
1661 (VikToolActivationFunc) NULL,
1662 (VikToolActivationFunc) NULL,
1663 (VikToolMouseFunc) selecttool_click,
1664 (VikToolMouseMoveFunc) selecttool_move,
1665 (VikToolMouseFunc) selecttool_release,
1666 (VikToolKeyFunc) NULL,
1671 /*** end select tool code ********************************************************/
1673 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1675 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1676 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1677 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1678 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1679 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1680 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1681 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1682 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1687 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1689 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1690 g_assert(check_box);
1691 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1693 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1695 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1698 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1702 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1705 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1708 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1711 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1715 gchar *s = (gchar *)gtk_action_get_name(a);
1721 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1722 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1723 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1724 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1725 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1730 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1732 VikCoord new_center;
1734 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1735 struct LatLon ll, llold;
1736 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1737 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1738 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1742 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1743 struct UTM utm, utmold;
1744 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1745 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1746 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1751 g_critical("Houston, we've had a problem.");
1755 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1760 * Refresh maps displayed
1762 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1764 // Only get 'new' maps
1765 simple_map_update ( vw, TRUE );
1768 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1771 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1772 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1773 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1775 vw->modified = TRUE;
1781 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1783 a_clipboard_copy_selected ( vw->viking_vlp );
1786 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1788 vik_layers_panel_cut_selected ( vw->viking_vlp );
1789 vw->modified = TRUE;
1792 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1794 if ( a_clipboard_paste ( vw->viking_vlp ) )
1796 vw->modified = TRUE;
1800 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1802 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1803 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1806 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1809 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1811 #if GTK_CHECK_VERSION (2, 14, 0)
1813 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1814 GError *error = NULL;
1815 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1816 if ( !show && !error )
1817 // No error to show, so unlikely this will get called
1818 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1821 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 );
1822 g_error_free ( error );
1826 a_dialog_error_msg ( GTK_WINDOW(vw), "Help is not available in this build." ); // Unlikely to happen so not going to bother with I8N
1828 #endif /* WINDOWS */
1831 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1833 a_dialog_about(GTK_WINDOW(vw));
1836 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1838 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1840 vik_layers_panel_delete_selected ( vw->viking_vlp );
1841 vw->modified = TRUE;
1844 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1847 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1849 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1850 g_assert(check_box);
1851 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1853 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1855 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1858 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1860 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1863 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1865 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1867 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1870 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1872 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1875 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1877 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1879 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1882 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1884 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1887 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1889 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1891 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1894 /***************************************
1895 ** tool management routines
1897 ***************************************/
1899 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1901 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1904 vt->active_tool = -1;
1909 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1911 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1912 vt->tools[vt->n_tools].ti = *vti;
1913 vt->tools[vt->n_tools].layer_type = layer_type;
1915 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1918 vt->tools[vt->n_tools].state = NULL;
1923 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1926 for (i=0; i<vt->n_tools; i++) {
1927 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1934 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1936 int tool = toolbox_get_tool(vt, tool_name);
1937 toolbox_tool_t *t = &vt->tools[tool];
1938 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1940 if (tool == vt->n_tools) {
1941 g_critical("trying to activate a non-existent tool...");
1944 /* is the tool already active? */
1945 if (vt->active_tool == tool) {
1949 if (vt->active_tool != -1) {
1950 if (vt->tools[vt->active_tool].ti.deactivate) {
1951 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1954 if (t->ti.activate) {
1955 t->ti.activate(vl, t->state);
1957 vt->active_tool = tool;
1960 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1962 int tool = toolbox_get_tool(vt, tool_name);
1963 toolbox_tool_t *t = &vt->tools[tool];
1964 if (t->ti.cursor == NULL) {
1965 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1966 GError *cursor_load_err = NULL;
1967 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1968 /* TODO: settable offeset */
1969 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1970 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1972 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1975 return t->ti.cursor;
1978 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1980 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1981 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1982 gint ltype = vt->tools[vt->active_tool].layer_type;
1983 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1984 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
1988 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
1990 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1991 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
1992 gint ltype = vt->tools[vt->active_tool].layer_type;
1993 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1994 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
1995 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
1999 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2001 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2002 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2003 gint ltype = vt->tools[vt->active_tool].layer_type;
2004 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2005 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2008 /** End tool management ************************************/
2010 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2012 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2015 /* this function gets called whenever a toolbar tool is clicked */
2016 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2018 /* White Magic, my friends ... White Magic... */
2019 int layer_id, tool_id;
2020 const GdkCursor *cursor = NULL;
2022 toolbox_activate(vw->vt, gtk_action_get_name(a));
2024 cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2026 if ( GTK_WIDGET(vw->viking_vvp)->window )
2027 /* We set cursor, even if it is NULL: it resets to default */
2028 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor );
2030 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2031 vw->current_tool = TOOL_PAN;
2033 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2034 vw->current_tool = TOOL_ZOOM;
2036 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2037 vw->current_tool = TOOL_RULER;
2039 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2040 vw->current_tool = TOOL_SELECT;
2043 /* TODO: only enable tools from active layer */
2044 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2045 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2046 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2047 vw->current_tool = TOOL_LAYER;
2048 vw->tool_layer_id = layer_id;
2049 vw->tool_tool_id = tool_id;
2054 draw_status_tool ( vw );
2057 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2062 g_free ( vw->filename );
2063 if ( filename == NULL )
2065 vw->filename = NULL;
2066 file = _("Untitled");
2070 vw->filename = g_strdup(filename);
2071 file = a_file_basename ( filename );
2073 title = g_strdup_printf( "%s - Viking", file );
2074 gtk_window_set_title ( GTK_WINDOW(vw), title );
2078 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2080 GtkWidget *mode_button;
2083 #ifdef VIK_CONFIG_EXPEDIA
2084 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2086 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2087 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2088 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2090 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2091 g_assert ( mode_button );
2096 * vik_window_get_pan_move:
2097 * @vw: some VikWindow
2099 * Retrieves @vw's pan_move.
2101 * Should be removed as soon as possible.
2103 * Returns: @vw's pan_move
2107 gboolean vik_window_get_pan_move ( VikWindow *vw )
2109 return vw->pan_move;
2112 static void on_activate_recent_item (GtkRecentChooser *chooser,
2117 filename = gtk_recent_chooser_get_current_uri (chooser);
2118 if (filename != NULL)
2120 GFile *file = g_file_new_for_uri ( filename );
2121 gchar *path = g_file_get_path ( file );
2122 g_object_unref ( file );
2123 if ( self->filename )
2125 GSList *filenames = NULL;
2126 filenames = g_slist_append ( filenames, path );
2127 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2128 // NB: GSList & contents are freed by main.open_window
2131 vik_window_open_file ( self, path, TRUE );
2139 static void setup_recent_files (VikWindow *self)
2141 GtkRecentManager *manager;
2142 GtkRecentFilter *filter;
2143 GtkWidget *menu, *menu_item;
2145 filter = gtk_recent_filter_new ();
2146 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2147 gtk_recent_filter_add_group(filter, "viking");
2149 manager = gtk_recent_manager_get_default ();
2150 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2151 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2152 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2154 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2155 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2157 g_signal_connect (G_OBJECT (menu), "item-activated",
2158 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2161 static void update_recently_used_document(const gchar *filename)
2163 /* Update Recently Used Document framework */
2164 GtkRecentManager *manager = gtk_recent_manager_get_default();
2165 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2166 gchar *groups[] = {"viking", NULL};
2167 GFile *file = g_file_new_for_commandline_arg(filename);
2168 gchar *uri = g_file_get_uri(file);
2169 gchar *basename = g_path_get_basename(filename);
2170 g_object_unref(file);
2173 recent_data->display_name = basename;
2174 recent_data->description = NULL;
2175 recent_data->mime_type = "text/x-gps-data";
2176 recent_data->app_name = (gchar *) g_get_application_name ();
2177 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2178 recent_data->groups = groups;
2179 recent_data->is_private = FALSE;
2180 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2182 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2187 g_free (recent_data->app_exec);
2188 g_slice_free (GtkRecentData, recent_data);
2191 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2193 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2195 case LOAD_TYPE_READ_FAILURE:
2196 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2198 case LOAD_TYPE_GPSBABEL_FAILURE:
2199 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2201 case LOAD_TYPE_GPX_FAILURE:
2202 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2204 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2205 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2207 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2209 // Since we can process .vik files with issues just show a warning in the status bar
2210 // Not that a user can do much about it... or tells them what this issue is yet...
2211 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2212 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2215 // No break, carry on to show any data
2216 case LOAD_TYPE_VIK_SUCCESS:
2218 GtkWidget *mode_button;
2220 if ( change_filename )
2221 window_set_filename ( vw, filename );
2222 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2223 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. */
2224 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2225 vw->only_updating_coord_mode_ui = FALSE;
2227 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2229 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2230 g_assert ( mode_button );
2231 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2233 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2234 g_assert ( mode_button );
2235 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2237 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2238 g_assert ( mode_button );
2239 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2241 //case LOAD_TYPE_OTHER_SUCCESS:
2243 update_recently_used_document(filename);
2248 static void load_file ( GtkAction *a, VikWindow *vw )
2250 GSList *files = NULL;
2251 GSList *cur_file = NULL;
2253 if (!strcmp(gtk_action_get_name(a), "Open")) {
2256 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2260 g_critical("Houston, we've had a problem.");
2264 if ( ! vw->open_dia )
2266 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2268 GTK_FILE_CHOOSER_ACTION_OPEN,
2269 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2270 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2272 GtkFileFilter *filter;
2273 // NB file filters are listed this way for alphabetical ordering
2274 #ifdef VIK_CONFIG_GEOCACHES
2275 filter = gtk_file_filter_new ();
2276 gtk_file_filter_set_name( filter, _("Geocaching") );
2277 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2278 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2281 filter = gtk_file_filter_new ();
2282 gtk_file_filter_set_name( filter, _("Google Earth") );
2283 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2284 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2286 filter = gtk_file_filter_new ();
2287 gtk_file_filter_set_name( filter, _("GPX") );
2288 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2289 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2291 filter = gtk_file_filter_new ();
2292 gtk_file_filter_set_name( filter, _("Viking") );
2293 gtk_file_filter_add_pattern ( filter, "*.vik" );
2294 gtk_file_filter_add_pattern ( filter, "*.viking" );
2295 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2297 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2298 // However assume this are barely used and thus not worthy of inclusion
2299 // as they'll just make the options too many and have no clear file pattern
2300 // one can always use the all option
2301 filter = gtk_file_filter_new ();
2302 gtk_file_filter_set_name( filter, _("All") );
2303 gtk_file_filter_add_pattern ( filter, "*" );
2304 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2305 // Default to any file - same as before open filters were added
2306 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2308 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2309 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2310 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2312 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2314 gtk_widget_hide ( vw->open_dia );
2315 #ifdef VIKING_PROMPT_IF_MODIFIED
2316 if ( (vw->modified || vw->filename) && newwindow )
2318 if ( vw->filename && newwindow )
2320 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2322 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2323 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2324 gboolean first_vik_file = TRUE;
2326 while ( cur_file ) {
2328 gchar *file_name = cur_file->data;
2329 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2330 // Load first of many .vik files in current window
2331 if ( first_vik_file ) {
2332 vik_window_open_file ( vw, file_name, TRUE );
2333 first_vik_file = FALSE;
2336 // Load each subsequent .vik file in a separate window
2337 VikWindow *newvw = vik_window_new_window ();
2339 vik_window_open_file ( newvw, file_name, TRUE );
2344 vik_window_open_file ( vw, file_name, change_fn );
2347 cur_file = g_slist_next (cur_file);
2349 g_slist_free (files);
2353 gtk_widget_hide ( vw->open_dia );
2356 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2358 gboolean rv = FALSE;
2360 if ( ! vw->save_dia )
2362 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2364 GTK_FILE_CHOOSER_ACTION_SAVE,
2365 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2366 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2368 GtkFileFilter *filter;
2369 filter = gtk_file_filter_new ();
2370 gtk_file_filter_set_name( filter, _("All") );
2371 gtk_file_filter_add_pattern ( filter, "*" );
2372 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2374 filter = gtk_file_filter_new ();
2375 gtk_file_filter_set_name( filter, _("Viking") );
2376 gtk_file_filter_add_pattern ( filter, "*.vik" );
2377 gtk_file_filter_add_pattern ( filter, "*.viking" );
2378 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2379 // Default to a Viking file
2380 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2382 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2383 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2385 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2386 gchar* auto_save_name = strdup ( vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") );
2387 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2388 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2390 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2392 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2394 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2395 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 ) ) )
2397 window_set_filename ( vw, fn );
2398 rv = window_save ( vw );
2399 vw->modified = FALSE;
2403 g_free ( auto_save_name );
2404 gtk_widget_hide ( vw->save_dia );
2408 static gboolean window_save ( VikWindow *vw )
2410 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2412 update_recently_used_document ( vw->filename );
2417 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2422 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2424 if ( ! vw->filename )
2425 return save_file_as ( NULL, vw );
2428 vw->modified = FALSE;
2429 return window_save ( vw );
2433 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2435 // Via the file menu, acquiring from a GPS makes a new layer
2436 // this has always been the way (not entirely sure if this was the real intention!)
2437 // thus maintain the behaviour ATM.
2438 // Hence explicit setting here (as the value may be changed elsewhere)
2439 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2440 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2443 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2445 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2448 #ifdef VIK_CONFIG_GOOGLE
2449 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2451 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2455 #ifdef VIK_CONFIG_OPENSTREETMAP
2456 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2458 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2462 #ifdef VIK_CONFIG_GEOCACHES
2463 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2465 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2469 #ifdef VIK_CONFIG_GEOTAG
2470 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2472 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2473 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2477 static void goto_default_location( GtkAction *a, VikWindow *vw)
2480 ll.lat = a_vik_get_default_lat();
2481 ll.lon = a_vik_get_default_long();
2482 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2483 vik_layers_panel_emit_update(vw->viking_vlp);
2487 static void goto_address( GtkAction *a, VikWindow *vw)
2489 a_vik_goto(vw, vw->viking_vlp, vw->viking_vvp);
2492 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2497 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2499 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2501 a_preferences_show_window ( GTK_WINDOW(vw) );
2503 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2504 if (wp_icon_size != a_vik_get_use_large_waypoint_icons())
2505 clear_garmin_icon_syms ();
2510 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2512 /* Simplistic repeat of preference setting
2513 Only the name & type are important for setting the preference via this 'external' way */
2514 VikLayerParam pref_lat[] = {
2515 { VIKING_PREFERENCES_NAMESPACE "default_latitude",
2516 VIK_LAYER_PARAM_DOUBLE,
2519 VIK_LAYER_WIDGET_SPINBUTTON,
2523 VikLayerParam pref_lon[] = {
2524 { VIKING_PREFERENCES_NAMESPACE "default_longitude",
2525 VIK_LAYER_PARAM_DOUBLE,
2528 VIK_LAYER_WIDGET_SPINBUTTON,
2533 /* Get current center */
2535 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2537 /* Apply to preferences */
2538 VikLayerParamData vlp_data;
2539 vlp_data.d = ll.lat;
2540 a_preferences_run_setparam (vlp_data, pref_lat);
2541 vlp_data.d = ll.lon;
2542 a_preferences_run_setparam (vlp_data, pref_lon);
2543 /* Remember to save */
2544 a_preferences_save_to_file();
2547 static void clear_cb ( GtkAction *a, VikWindow *vw )
2549 vik_layers_panel_clear ( vw->viking_vlp );
2550 window_set_filename ( vw, NULL );
2554 static void window_close ( GtkAction *a, VikWindow *vw )
2556 if ( ! delete_event ( vw ) )
2557 gtk_widget_destroy ( GTK_WIDGET(vw) );
2560 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2562 if (save_file( NULL, vw)) {
2563 window_close( NULL, vw);
2570 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2572 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2573 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2575 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2576 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2581 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2583 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2584 GdkPixbuf *pixbuf_to_save;
2585 gdouble old_xmpp, old_ympp;
2586 GError *error = NULL;
2588 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2589 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2592 _("Generating image file...") );
2594 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2595 // Ensure dialog shown
2596 gtk_widget_show_all ( msgbox );
2598 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2599 while ( gtk_events_pending() )
2600 gtk_main_iteration ();
2601 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2602 // At least the empty box can give a clue something's going on + the statusbar msg...
2603 // Windows version under Wine OK!
2605 /* backup old zoom & set new */
2606 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2607 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2608 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2610 /* reset width and height: */
2611 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2613 /* draw all layers */
2616 /* save buffer as file. */
2617 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);
2618 if ( !pixbuf_to_save ) {
2619 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2620 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
2624 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2627 g_warning("Unable to write to file %s: %s", fn, error->message );
2628 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
2629 g_error_free (error);
2633 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2635 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2638 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2639 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2640 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2642 /* pretend like nothing happened ;) */
2643 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2644 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2645 vik_viewport_configure ( vw->viking_vvp );
2649 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 )
2651 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2652 gchar *name_of_file = g_malloc ( size );
2654 struct UTM utm_orig, utm;
2656 /* *** copied from above *** */
2657 GdkPixbuf *pixbuf_to_save;
2658 gdouble old_xmpp, old_ympp;
2659 GError *error = NULL;
2661 /* backup old zoom & set new */
2662 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2663 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2664 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2666 /* reset width and height: do this only once for all images (same size) */
2667 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2668 /* *** end copy from above *** */
2670 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2674 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2676 for ( y = 1; y <= tiles_h; y++ )
2678 for ( x = 1; x <= tiles_w; x++ )
2680 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2682 if ( tiles_w & 0x1 )
2683 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2685 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2686 if ( tiles_h & 0x1 ) /* odd */
2687 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2689 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2691 /* move to correct place. */
2692 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2696 /* save buffer as file. */
2697 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);
2698 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2701 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2702 g_error_free (error);
2705 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2709 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2710 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2711 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2712 vik_viewport_configure ( vw->viking_vvp );
2715 g_free ( name_of_file );
2718 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2720 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2721 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2723 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2724 gdouble zoom = pow (2, active-2 );
2726 gdouble width_min, width_max, height_min, height_max;
2729 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2730 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2732 /* TODO: support for xzoom and yzoom values */
2733 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2734 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2736 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2737 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2739 gtk_spin_button_set_value ( width_spin, width );
2740 gtk_spin_button_set_value ( height_spin, height );
2743 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2745 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2747 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2748 gdouble zoom = pow (2, active-2 );
2752 w = gtk_spin_button_get_value(width_spin) * zoom;
2753 h = gtk_spin_button_get_value(height_spin) * zoom;
2754 if (pass_along[4]) /* save many images; find TOTAL area covered */
2756 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2757 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2759 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2760 switch (dist_units) {
2761 case VIK_UNITS_DISTANCE_KILOMETRES:
2762 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2764 case VIK_UNITS_DISTANCE_MILES:
2765 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2768 label_text = g_strdup_printf ("Just to keep the compiler happy");
2769 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2772 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2773 g_free ( label_text );
2777 * Get an allocated filename (or directory as specified)
2779 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
2782 if ( one_image_only )
2785 if (!vw->save_img_dia) {
2786 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2788 GTK_FILE_CHOOSER_ACTION_SAVE,
2789 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2790 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2793 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
2795 GtkFileFilter *filter;
2796 filter = gtk_file_filter_new ();
2797 gtk_file_filter_set_name ( filter, _("All") );
2798 gtk_file_filter_add_pattern ( filter, "*" );
2799 gtk_file_chooser_add_filter ( chooser, filter );
2801 filter = gtk_file_filter_new ();
2802 gtk_file_filter_set_name ( filter, _("JPG") );
2803 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2804 gtk_file_chooser_add_filter ( chooser, filter );
2806 filter = gtk_file_filter_new ();
2807 gtk_file_filter_set_name ( filter, _("PNG") );
2808 gtk_file_filter_add_mime_type ( filter, "image/png");
2809 gtk_file_chooser_add_filter ( chooser, filter );
2812 gtk_file_chooser_set_filter ( chooser, filter );
2814 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2815 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2818 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
2819 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2820 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
2821 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 ) ) )
2824 gtk_widget_hide ( vw->save_img_dia );
2828 // For some reason this method is only written to work in UTM...
2829 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2830 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2834 if (!vw->save_img_dir_dia) {
2835 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2837 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2838 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2839 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2841 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2842 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2845 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
2846 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2848 gtk_widget_hide ( vw->save_img_dir_dia );
2853 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
2855 /* todo: default for answers inside VikWindow or static (thruout instance) */
2856 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2857 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2859 GTK_RESPONSE_REJECT,
2861 GTK_RESPONSE_ACCEPT,
2863 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2864 GtkWidget *png_radio, *jpeg_radio;
2865 GtkWidget *current_window_button;
2866 gpointer current_window_pass_along[7];
2867 GtkWidget *zoom_label, *zoom_combo;
2868 GtkWidget *total_size_label;
2870 /* only used if (!one_image_only) */
2871 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2873 width_label = gtk_label_new ( _("Width (pixels):") );
2874 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
2875 height_label = gtk_label_new ( _("Height (pixels):") );
2876 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
2878 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
2880 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2881 /* TODO: separate xzoom and yzoom factors */
2882 zoom_combo = create_zoom_combo_all_levels();
2884 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
2885 gint active = 2 + round ( log (mpp) / log (2) );
2887 // Can we not hard code size here?
2890 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
2892 total_size_label = gtk_label_new ( NULL );
2894 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2895 current_window_pass_along [0] = vw;
2896 current_window_pass_along [1] = width_spin;
2897 current_window_pass_along [2] = height_spin;
2898 current_window_pass_along [3] = zoom_combo;
2899 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
2900 current_window_pass_along [5] = NULL;
2901 current_window_pass_along [6] = total_size_label;
2902 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
2904 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
2905 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
2907 if ( ! vw->draw_image_save_as_png )
2908 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
2910 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
2911 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
2912 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
2913 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
2915 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), win_warning_label, FALSE, FALSE, 0);
2917 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
2918 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
2919 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
2920 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
2921 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_combo, FALSE, FALSE, 0);
2923 if ( ! one_image_only )
2925 GtkWidget *tiles_width_label, *tiles_height_label;
2927 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
2928 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2929 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
2930 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
2931 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
2932 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
2933 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
2934 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
2936 current_window_pass_along [4] = tiles_width_spin;
2937 current_window_pass_along [5] = tiles_height_spin;
2938 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2939 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2941 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
2942 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2943 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2944 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
2946 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
2948 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2950 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
2952 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
2954 gtk_widget_hide ( GTK_WIDGET(dialog) );
2956 gchar *fn = draw_image_filename ( vw, one_image_only );
2960 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
2961 gdouble zoom = pow (2, active-2 );
2963 if ( one_image_only )
2964 save_image_file ( vw, fn,
2965 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2966 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2968 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
2970 // NB is in UTM mode ATM
2971 save_image_dir ( vw, fn,
2972 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
2973 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
2975 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
2976 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
2977 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
2982 gtk_widget_destroy ( GTK_WIDGET(dialog) );
2986 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
2988 draw_to_image_file ( vw, TRUE );
2991 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
2993 draw_to_image_file ( vw, FALSE );
2996 #if GTK_CHECK_VERSION(2,10,0)
2997 static void print_cb ( GtkAction *a, VikWindow *vw )
2999 a_print(vw, vw->viking_vvp);
3003 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3004 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3006 VikViewportDrawMode drawmode;
3007 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3008 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3010 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3011 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3013 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3014 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3016 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3017 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3020 g_critical("Houston, we've had a problem.");
3024 if ( !vw->only_updating_coord_mode_ui )
3026 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3027 if ( olddrawmode != drawmode )
3029 /* this takes care of coord mode too */
3030 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3031 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3032 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3033 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3034 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3041 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3043 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3044 g_assert(check_box);
3045 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3046 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3050 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3052 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3053 g_assert(check_box);
3054 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3055 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3059 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3061 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3062 g_assert(check_box);
3063 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3064 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3068 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3070 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3071 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3072 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3073 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3074 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3076 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3077 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3081 gtk_widget_destroy ( colorsd );
3084 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3086 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3087 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3088 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3089 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3090 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3092 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3093 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3097 gtk_widget_destroy ( colorsd );
3102 /***********************************************************************************************
3104 ***********************************************************************************************/
3106 static GtkActionEntry entries[] = {
3107 { "File", NULL, N_("_File"), 0, 0, 0 },
3108 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3109 { "View", NULL, N_("_View"), 0, 0, 0 },
3110 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3111 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3112 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3113 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3114 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3115 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3116 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3118 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3119 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3120 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3121 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3122 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3123 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3124 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3125 #ifdef VIK_CONFIG_GOOGLE
3126 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
3128 #ifdef VIK_CONFIG_OPENSTREETMAP
3129 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3131 #ifdef VIK_CONFIG_GEOCACHES
3132 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3134 #ifdef VIK_CONFIG_GEOTAG
3135 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3137 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3138 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3139 { "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 },
3140 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3142 #if GTK_CHECK_VERSION(2,10,0)
3143 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3146 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3147 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3149 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3150 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3151 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3152 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3153 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3154 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3155 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3156 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3157 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3158 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3159 { "Zoom0.25", NULL, N_("0.25"), NULL, NULL, (GCallback)draw_zoom_cb },
3160 { "Zoom0.5", NULL, N_("0.5"), NULL, NULL, (GCallback)draw_zoom_cb },
3161 { "Zoom1", NULL, N_("1"), NULL, NULL, (GCallback)draw_zoom_cb },
3162 { "Zoom2", NULL, N_("2"), NULL, NULL, (GCallback)draw_zoom_cb },
3163 { "Zoom4", NULL, N_("4"), NULL, NULL, (GCallback)draw_zoom_cb },
3164 { "Zoom8", NULL, N_("8"), NULL, NULL, (GCallback)draw_zoom_cb },
3165 { "Zoom16", NULL, N_("16"), NULL, NULL, (GCallback)draw_zoom_cb },
3166 { "Zoom32", NULL, N_("32"), NULL, NULL, (GCallback)draw_zoom_cb },
3167 { "Zoom64", NULL, N_("64"), NULL, NULL, (GCallback)draw_zoom_cb },
3168 { "Zoom128", NULL, N_("128"), NULL, NULL, (GCallback)draw_zoom_cb },
3169 { "Zoom256", NULL, N_("256"), NULL, NULL, (GCallback)draw_zoom_cb },
3170 { "Zoom512", NULL, N_("512"), NULL, NULL, (GCallback)draw_zoom_cb },
3171 { "Zoom1024", NULL, N_("1024"), NULL, NULL, (GCallback)draw_zoom_cb },
3172 { "Zoom2048", NULL, N_("2048"), NULL, NULL, (GCallback)draw_zoom_cb },
3173 { "Zoom4096", NULL, N_("4096"), NULL, NULL, (GCallback)draw_zoom_cb },
3174 { "Zoom8192", NULL, N_("8192"), NULL, NULL, (GCallback)draw_zoom_cb },
3175 { "Zoom16384", NULL, N_("16384"), NULL, NULL, (GCallback)draw_zoom_cb },
3176 { "Zoom32768", NULL, N_("32768"), NULL, NULL, (GCallback)draw_zoom_cb },
3177 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3178 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3179 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3180 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3181 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3183 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3184 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3185 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3186 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3187 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3188 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3189 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3190 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3191 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3193 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3194 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3198 /* FIXME use VIEWPORT_DRAWMODE values */
3199 static GtkRadioActionEntry mode_entries[] = {
3200 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3201 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3202 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3203 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3206 static GtkToggleActionEntry toggle_entries[] = {
3207 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3208 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3209 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3210 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3211 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3212 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3213 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3214 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3217 #include "menu.xml.h"
3218 static void window_create_ui( VikWindow *window )
3221 GtkActionGroup *action_group;
3222 GtkAccelGroup *accel_group;
3225 GtkIconFactory *icon_factory;
3226 GtkIconSet *icon_set;
3227 GtkRadioActionEntry *tools = NULL, *radio;
3230 uim = gtk_ui_manager_new ();
3233 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3234 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3235 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3236 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3239 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3240 g_error_free (error);
3244 action_group = gtk_action_group_new ("MenuActions");
3245 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3246 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3247 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3248 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3250 icon_factory = gtk_icon_factory_new ();
3251 gtk_icon_factory_add_default (icon_factory);
3253 register_vik_icons(icon_factory);
3255 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3256 // so that it can be applied to the UI in one action group add function call below
3258 for (i=0; i<window->vt->n_tools; i++) {
3259 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3260 radio = &tools[ntools];
3262 *radio = window->vt->tools[i].ti.radioActionEntry;
3263 radio->value = ntools;
3266 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3267 GtkActionEntry action;
3268 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3269 vik_layer_get_interface(i)->name,
3270 vik_layer_get_interface(i)->name,
3271 GTK_UI_MANAGER_MENUITEM, FALSE);
3273 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3274 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3275 gtk_icon_set_unref (icon_set);
3277 action.name = vik_layer_get_interface(i)->name;
3278 action.stock_id = vik_layer_get_interface(i)->name;
3279 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3280 action.accelerator = vik_layer_get_interface(i)->accelerator;
3281 action.tooltip = NULL;
3282 action.callback = (GCallback)menu_addlayer_cb;
3283 gtk_action_group_add_actions(action_group, &action, 1, window);
3285 if ( vik_layer_get_interface(i)->tools_count ) {
3286 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3287 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3290 // Further tool copying for to apply to the UI, also apply menu UI setup
3291 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3292 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3293 radio = &tools[ntools];
3296 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3297 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3298 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3299 GTK_UI_MANAGER_MENUITEM, FALSE);
3300 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3301 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3302 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3303 GTK_UI_MANAGER_TOOLITEM, FALSE);
3305 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3307 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3308 // Overwrite with actual number to use
3309 radio->value = ntools;
3312 g_object_unref (icon_factory);
3314 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3317 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3319 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3320 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3321 GtkAction *action = gtk_action_group_get_action(action_group,
3322 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3323 g_object_set(action, "sensitive", FALSE, NULL);
3326 window->action_group = action_group;
3328 accel_group = gtk_ui_manager_get_accel_group (uim);
3329 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3330 gtk_ui_manager_ensure_update (uim);
3332 setup_recent_files(window);
3336 // TODO - add method to add tool icons defined from outside this file
3337 // and remove the reverse dependency on icon definition from this file
3339 const GdkPixdata *data;
3342 { &mover_22_pixbuf, "vik-icon-pan" },
3343 { &zoom_18_pixbuf, "vik-icon-zoom" },
3344 { &ruler_18_pixbuf, "vik-icon-ruler" },
3345 { &select_18_pixbuf, "vik-icon-select" },
3346 { &begintr_18_pixbuf, "vik-icon-Begin Track" },
3347 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3348 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3349 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3350 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3351 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3352 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3353 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3354 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3355 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3356 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3359 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3362 register_vik_icons (GtkIconFactory *icon_factory)
3364 GtkIconSet *icon_set;
3367 for (i = 0; i < n_stock_icons; i++) {
3368 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3369 stock_icons[i].data, FALSE, NULL ));
3370 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3371 gtk_icon_set_unref (icon_set);
3375 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3377 return vw->selected_vtl;
3380 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3382 vw->selected_vtl = vtl;
3383 vw->containing_vtl = vtl;
3385 vw->selected_track = NULL;
3386 vw->selected_tracks = NULL;
3387 vw->selected_waypoint = NULL;
3388 vw->selected_waypoints = NULL;
3389 // Set highlight thickness
3390 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3393 gpointer vik_window_get_selected_tracks ( VikWindow *vw )
3395 return vw->selected_tracks;
3398 void vik_window_set_selected_tracks ( VikWindow *vw, gpointer gl, gpointer vtl )
3400 vw->selected_tracks = gl;
3401 vw->containing_vtl = vtl;
3403 vw->selected_vtl = NULL;
3404 vw->selected_track = NULL;
3405 vw->selected_waypoint = NULL;
3406 vw->selected_waypoints = NULL;
3407 // Set highlight thickness
3408 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3411 gpointer vik_window_get_selected_track ( VikWindow *vw )
3413 return vw->selected_track;
3416 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
3418 vw->selected_track = vt;
3419 vw->containing_vtl = vtl;
3421 vw->selected_vtl = NULL;
3422 vw->selected_tracks = NULL;
3423 vw->selected_waypoint = NULL;
3424 vw->selected_waypoints = NULL;
3425 // Set highlight thickness
3426 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3429 gpointer vik_window_get_selected_waypoints ( VikWindow *vw )
3431 return vw->selected_waypoints;
3434 void vik_window_set_selected_waypoints ( VikWindow *vw, gpointer gl, gpointer vtl )
3436 vw->selected_waypoints = gl;
3437 vw->containing_vtl = vtl;
3439 vw->selected_vtl = NULL;
3440 vw->selected_track = NULL;
3441 vw->selected_tracks = NULL;
3442 vw->selected_waypoint = NULL;
3445 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3447 return vw->selected_waypoint;
3450 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
3452 vw->selected_waypoint = vwp;
3453 vw->containing_vtl = vtl;
3455 vw->selected_vtl = NULL;
3456 vw->selected_track = NULL;
3457 vw->selected_tracks = NULL;
3458 vw->selected_waypoints = NULL;
3461 gboolean vik_window_clear_highlight ( VikWindow *vw )
3463 gboolean need_redraw = FALSE;
3464 if ( vw->selected_vtl != NULL ) {
3465 vw->selected_vtl = NULL;
3468 if ( vw->selected_track != NULL ) {
3469 vw->selected_track = NULL;
3472 if ( vw->selected_tracks != NULL ) {
3473 vw->selected_tracks = NULL;
3476 if ( vw->selected_waypoint != NULL ) {
3477 vw->selected_waypoint = NULL;
3480 if ( vw->selected_waypoints != NULL ) {
3481 vw->selected_waypoints = NULL;