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 "viklayer_defaults.h"
38 #include "icons/icons.h"
39 #include "vikexttools.h"
40 #include "garminsymbols.h"
41 #include "vikmapslayer.h"
42 #include "geonamessearch.h"
55 #include <glib/gstdio.h>
56 #include <glib/gprintf.h>
57 #include <glib/gi18n.h>
59 #include <gdk/gdkkeysyms.h>
61 // This seems rather arbitary, quite large and pointless
62 // I mean, if you have a thousand windows open;
63 // why not be allowed to open a thousand more...
64 #define MAX_WINDOWS 1024
65 static guint window_count = 0;
66 static GSList *window_list = NULL;
68 #define VIKING_WINDOW_WIDTH 1000
69 #define VIKING_WINDOW_HEIGHT 800
70 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
71 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
72 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
74 static void window_finalize ( GObject *gob );
75 static GObjectClass *parent_class;
77 static void window_set_filename ( VikWindow *vw, const gchar *filename );
78 static const gchar *window_get_filename ( VikWindow *vw );
80 static VikWindow *window_new ();
82 static void draw_update ( VikWindow *vw );
84 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
87 static void open_window ( VikWindow *vw, GSList *files );
88 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type );
89 static void destroy_window ( GtkWidget *widget,
94 static gboolean delete_event( VikWindow *vw );
96 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
98 static void window_configure_event ( VikWindow *vw );
99 static void draw_sync ( VikWindow *vw );
100 static void draw_redraw ( VikWindow *vw );
101 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
102 static void draw_click ( VikWindow *vw, GdkEventButton *event );
103 static void draw_release ( VikWindow *vw, GdkEventButton *event );
104 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
105 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
106 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
107 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
109 static void draw_status ( VikWindow *vw );
111 /* End Drawing Functions */
113 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
114 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
115 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
117 /* tool management */
123 #define TOOL_LAYER_TYPE_NONE -1
128 toolbox_tool_t *tools;
132 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
133 static toolbox_tools_t* toolbox_create(VikWindow *vw);
134 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
135 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
136 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
137 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
138 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
139 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
140 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
144 static void window_create_ui( VikWindow *window );
145 static void register_vik_icons (GtkIconFactory *icon_factory);
148 static void load_file ( GtkAction *a, VikWindow *vw );
149 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
150 static gboolean save_file ( GtkAction *a, VikWindow *vw );
151 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
152 static gboolean window_save ( VikWindow *vw );
156 VikViewport *viking_vvp;
157 VikLayersPanel *viking_vlp;
158 VikStatusbar *viking_vs;
162 GdkCursor *busy_cursor;
163 GdkCursor *viewport_cursor; // only a reference
165 /* tool management state */
168 guint16 tool_layer_id;
169 guint16 tool_tool_id;
171 GtkActionGroup *action_group;
176 guint draw_image_width, draw_image_height;
177 gboolean draw_image_save_as_png;
182 GtkWidget *open_dia, *save_dia;
183 GtkWidget *save_img_dia, *save_img_dir_dia;
185 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
189 /* half-drawn update */
191 VikCoord trigger_center;
193 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
194 /* Only one of these items can be selected at the same time */
195 gpointer selected_vtl; /* notionally VikTrwLayer */
196 GHashTable *selected_tracks;
197 gpointer selected_track; /* notionally VikTrack */
198 GHashTable *selected_waypoints;
199 gpointer selected_waypoint; /* notionally VikWaypoint */
200 /* only use for individual track or waypoint */
201 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
202 gpointer containing_vtl; /* notionally VikTrwLayer */
216 VW_OPENWINDOW_SIGNAL,
217 VW_STATUSBAR_UPDATE_SIGNAL,
221 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
223 // TODO get rid of this as this is unnecessary duplication...
224 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
226 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
228 VikViewport * vik_window_viewport(VikWindow *vw)
230 return(vw->viking_vvp);
233 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
235 return(vw->viking_vlp);
239 * Returns the statusbar for the window
241 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
243 return vw->viking_vs;
247 * For signalling the update from a background thread
249 void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
251 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message, vs_type );
255 * For the actual statusbar update!
257 static gboolean statusbar_idle_update ( gpointer indata )
259 gpointer *data = indata;
260 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
265 * Update statusbar in the main thread
267 static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
269 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
270 static gpointer data[3];
271 data[0] = vw->viking_vs;
272 data[1] = (gchar*) message;
273 data[2] = GINT_TO_POINTER(vs_type);
274 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
277 // Actual signal handlers
278 static void destroy_window ( GtkWidget *widget,
281 if ( ! --window_count )
285 static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type )
287 window_statusbar_update ( vw, message, vs_type );
290 VikWindow *vik_window_new_window ()
292 if ( window_count < MAX_WINDOWS )
294 VikWindow *vw = window_new ();
296 g_signal_connect (G_OBJECT (vw), "destroy",
297 G_CALLBACK (destroy_window), NULL);
298 g_signal_connect (G_OBJECT (vw), "newwindow",
299 G_CALLBACK (vik_window_new_window), NULL);
300 g_signal_connect (G_OBJECT (vw), "openwindow",
301 G_CALLBACK (open_window), NULL);
302 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
303 G_CALLBACK (statusbar_update), vw);
305 gtk_widget_show_all ( GTK_WIDGET(vw) );
314 static void open_window ( VikWindow *vw, GSList *files )
316 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
317 GSList *cur_file = files;
319 // Only open a new window if a viking file
320 gchar *file_name = cur_file->data;
321 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
322 VikWindow *newvw = vik_window_new_window ();
324 vik_window_open_file ( newvw, file_name, TRUE );
327 vik_window_open_file ( vw, file_name, change_fn );
330 cur_file = g_slist_next (cur_file);
332 g_slist_free (files);
336 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
338 int i, j, tool_count;
339 VikLayerInterface *layer_interface;
341 if (!vw->action_group) return;
343 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
345 layer_interface = vik_layer_get_interface(i);
346 tool_count = layer_interface->tools_count;
348 for (j = 0; j < tool_count; j++) {
349 action = gtk_action_group_get_action(vw->action_group,
350 layer_interface->tools[j].radioActionEntry.name);
351 g_object_set(action, "sensitive", i == vl->type, NULL);
356 static void window_finalize ( GObject *gob )
358 VikWindow *vw = VIK_WINDOW(gob);
359 g_return_if_fail ( vw != NULL );
361 a_background_remove_window ( vw );
363 window_list = g_slist_remove ( window_list, vw );
365 gdk_cursor_unref ( vw->busy_cursor );
367 G_OBJECT_CLASS(parent_class)->finalize(gob);
371 static void vik_window_class_init ( VikWindowClass *klass )
374 GObjectClass *object_class;
376 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);
377 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);
378 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);
380 object_class = G_OBJECT_CLASS (klass);
382 object_class->finalize = window_finalize;
384 parent_class = g_type_class_peek_parent (klass);
388 static void zoom_changed (GtkMenuShell *menushell,
391 VikWindow *vw = VIK_WINDOW (user_data);
393 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
394 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
396 gdouble zoom_request = pow (2, active-2 );
398 // But has it really changed?
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_menu_all_levels ()
409 GtkWidget *menu = gtk_menu_new ();
410 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768", NULL };
413 for (i = 0 ; itemLabels[i] != NULL ; i++)
415 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
416 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
417 gtk_widget_show (item);
418 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
424 static GtkWidget *create_zoom_combo_all_levels ()
426 GtkWidget *zoom_combo = gtk_combo_box_new_text();
427 GtkComboBox *combo = GTK_COMBO_BOX ( zoom_combo );
428 gtk_combo_box_append_text ( combo, "0.25");
429 gtk_combo_box_append_text ( combo, "0.5");
430 gtk_combo_box_append_text ( combo, "1");
431 gtk_combo_box_append_text ( combo, "2");
432 gtk_combo_box_append_text ( combo, "4");
433 gtk_combo_box_append_text ( combo, "8");
434 gtk_combo_box_append_text ( combo, "16");
435 gtk_combo_box_append_text ( combo, "32");
436 gtk_combo_box_append_text ( combo, "64");
437 gtk_combo_box_append_text ( combo, "128");
438 gtk_combo_box_append_text ( combo, "256");
439 gtk_combo_box_append_text ( combo, "512");
440 gtk_combo_box_append_text ( combo, "1024");
441 gtk_combo_box_append_text ( combo, "2048");
442 gtk_combo_box_append_text ( combo, "4096");
443 gtk_combo_box_append_text ( combo, "8192");
444 gtk_combo_box_append_text ( combo, "16384");
445 gtk_combo_box_append_text ( combo, "32768");
447 gtk_widget_set_tooltip_text (GTK_WIDGET (combo), _("Select zoom level"));
451 static gint zoom_popup_handler (GtkWidget *widget)
455 g_return_val_if_fail (widget != NULL, FALSE);
456 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
458 /* The "widget" is the menu that was supplied when
459 * g_signal_connect_swapped() was called.
461 menu = GTK_MENU (widget);
463 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
464 1, gtk_get_current_event_time());
468 static void vik_window_init ( VikWindow *vw )
470 GtkWidget *main_vbox;
473 vw->action_group = NULL;
475 vw->viking_vvp = vik_viewport_new();
476 vw->viking_vlp = vik_layers_panel_new();
477 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
478 vw->viking_vs = vik_statusbar_new();
480 vw->vt = toolbox_create(vw);
481 window_create_ui(vw);
482 window_set_filename (vw, NULL);
483 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
485 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
487 // Set the default tool
488 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
492 vw->modified = FALSE;
493 vw->only_updating_coord_mode_ui = FALSE;
495 vw->pan_move = FALSE;
496 vw->pan_x = vw->pan_y = -1;
497 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
498 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
499 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
501 main_vbox = gtk_vbox_new(FALSE, 1);
502 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
504 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
505 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
506 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
507 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
509 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
510 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ();
511 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
512 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
513 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
515 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
517 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
518 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
519 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 );
520 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
521 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
522 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
523 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
524 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
526 // Allow key presses to be processed anywhere
527 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
529 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
531 hpaned = gtk_hpaned_new ();
532 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
533 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
535 /* This packs the button into the window (a gtk container). */
536 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
538 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
540 a_background_add_window ( vw );
542 window_list = g_slist_prepend ( window_list, vw);
546 vw->save_img_dia = NULL;
547 vw->save_img_dir_dia = NULL;
549 // Store the thread value so comparisons can be made to determine the gdk update method
550 // Hopefully we are storing the main thread value here :)
551 // [ATM any window initialization is always be performed by the main thread]
552 vw->thread = g_thread_self();
555 static VikWindow *window_new ()
557 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
561 * Update the displayed map
562 * Only update the top most visible map layer
563 * ATM this assumes (as per defaults) the top most map has full alpha setting
564 * such that other other maps even though they may be active will not be seen
565 * It's more complicated to work out which maps are actually visible due to alpha settings
566 * and overkill for this simple refresh method.
568 static void simple_map_update ( VikWindow *vw, gboolean only_new )
570 // Find the most relevent single map layer to operate on
571 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
573 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
577 * This is the global key press handler
578 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
580 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
582 // The keys handled here are not in the menuing system for a couple of reasons:
583 // . Keeps the menu size compact (alebit at expense of discoverably)
584 // . Allows differing key bindings to perform the same actions
586 // First decide if key events are related to the maps layer
587 gboolean map_download = FALSE;
588 gboolean map_download_only_new = TRUE; // Only new or reload
590 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
592 // Standard 'Refresh' keys: F5 or Ctrl+r
593 // Note 'F5' is actually handled via draw_refresh_cb() later on
594 // (not 'R' it's 'r' notice the case difference!!)
595 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
597 map_download_only_new = TRUE;
599 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
600 // Note the use of uppercase R here since shift key has been pressed
601 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
602 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
604 map_download_only_new = FALSE;
607 if ( map_download ) {
608 simple_map_update ( vw, map_download_only_new );
611 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
612 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
613 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
614 if ( vl && ltype == vl->type )
615 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
618 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
619 if ( vw->current_tool < TOOL_LAYER ) {
620 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
621 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
622 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
626 /* Restore Main Menu via Escape key if the user has hidden it */
627 /* This key is more likely to be used as they may not remember the function key */
628 if ( event->keyval == GDK_Escape ) {
629 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
631 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
633 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
634 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
635 return TRUE; /* handled keypress */
640 return FALSE; /* don't handle the keypress */
643 static gboolean delete_event( VikWindow *vw )
645 #ifdef VIKING_PROMPT_IF_MODIFIED
652 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
653 _("Do you want to save the changes you made to the document \"%s\"?\n"
655 "Your changes will be lost if you don't save them."),
656 window_get_filename ( vw ) ) );
657 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
658 switch ( gtk_dialog_run ( dia ) )
660 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
661 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
662 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
669 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
671 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
674 static void draw_update ( VikWindow *vw )
680 static void draw_sync ( VikWindow *vw )
682 vik_viewport_sync(vw->viking_vvp);
687 * Split the status update, as sometimes only need to update the tool part
688 * also on initialization the zoom related stuff is not ready to be used
690 static void draw_status_tool ( VikWindow *vw )
692 if ( vw->current_tool == TOOL_LAYER )
693 // Use tooltip rather than the internal name as the tooltip is i8n
694 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 );
696 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
699 static void draw_status ( VikWindow *vw )
701 static gchar zoom_level[22];
702 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
703 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
704 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
706 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
708 if ( (int)xmpp - xmpp < 0.0 )
709 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
711 /* xmpp should be a whole number so don't show useless .000 bit */
712 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
714 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
716 draw_status_tool ( vw );
719 void vik_window_set_redraw_trigger(VikLayer *vl)
721 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
726 static void window_configure_event ( VikWindow *vw )
728 static int first = 1;
731 // This is a hack to set the cursor corresponding to the first tool
732 // FIXME find the correct way to initialize both tool and its cursor
734 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
735 /* We set cursor, even if it is NULL: it resets to default */
736 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->viewport_cursor );
740 static void draw_redraw ( VikWindow *vw )
742 VikCoord old_center = vw->trigger_center;
743 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
744 VikLayer *new_trigger = vw->trigger;
746 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
749 ; /* do nothing -- have to redraw everything. */
750 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
751 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
753 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
756 vik_viewport_clear ( vw->viking_vvp);
757 vik_layers_panel_draw_all ( vw->viking_vlp );
758 vik_viewport_draw_scale ( vw->viking_vvp );
759 vik_viewport_draw_copyright ( vw->viking_vvp );
760 vik_viewport_draw_centermark ( vw->viking_vvp );
761 vik_viewport_draw_logo ( vw->viking_vvp );
763 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
766 gboolean draw_buf_done = TRUE;
768 static gboolean draw_buf(gpointer data)
770 gpointer *pass_along = data;
772 gdk_draw_drawable (pass_along[0], pass_along[1],
773 pass_along[2], 0, 0, 0, 0, -1, -1);
774 draw_buf_done = TRUE;
780 /* Mouse event handlers ************************************************************************/
782 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
784 /* set panning origin */
785 vw->pan_move = FALSE;
786 vw->pan_x = (gint) event->x;
787 vw->pan_y = (gint) event->y;
790 static void draw_click (VikWindow *vw, GdkEventButton *event)
792 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
794 /* middle button pressed. we reserve all middle button and scroll events
795 * for panning and zooming; tools only get left/right/movement
797 if ( event->button == 2) {
798 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
799 // Tool still may need to do something (such as disable something)
800 toolbox_click(vw->vt, event);
801 vik_window_pan_click ( vw, event );
804 toolbox_click(vw->vt, event);
808 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
810 if ( vw->pan_x != -1 ) {
811 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
812 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
814 vw->pan_x = event->x;
815 vw->pan_y = event->y;
820 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
822 static VikCoord coord;
823 static struct UTM utm;
824 static struct LatLon ll;
825 #define BUFFER_SIZE 50
826 static char pointer_buf[BUFFER_SIZE];
827 gchar *lat = NULL, *lon = NULL;
830 VikDemInterpol interpol_method;
832 /* This is a hack, but work far the best, at least for single pointer systems.
833 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
835 gdk_window_get_pointer (event->window, &x, &y, NULL);
839 toolbox_move(vw->vt, event);
841 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
842 vik_coord_to_utm ( &coord, &utm );
844 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
845 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
846 // ZONE[N|S] EASTING NORTHING
847 lat = g_malloc(4*sizeof(gchar));
848 // NB zone is stored in a char but is an actual number
849 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
850 lon = g_malloc(16*sizeof(gchar));
851 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
854 a_coords_utm_to_latlon ( &utm, &ll );
855 a_coords_latlon_to_string ( &ll, &lat, &lon );
858 /* Change interpolate method according to scale */
859 zoom = vik_viewport_get_zoom(vw->viking_vvp);
861 interpol_method = VIK_DEM_INTERPOL_NONE;
862 else if (zoom >= 1.0)
863 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
865 interpol_method = VIK_DEM_INTERPOL_BEST;
866 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
867 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
868 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
870 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
873 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
878 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
880 vik_window_pan_move ( vw, event );
882 /* This is recommended by the GTK+ documentation, but does not work properly.
883 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
884 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
886 /* gdk_event_request_motions ( event ); */
889 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
891 if ( vw->pan_move == FALSE )
892 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
894 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
895 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
896 vw->pan_move = FALSE;
897 vw->pan_x = vw->pan_y = -1;
901 static void draw_release ( VikWindow *vw, GdkEventButton *event )
903 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
905 if ( event->button == 2 ) { /* move / pan */
906 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
907 // Tool still may need to do something (such as reenable something)
908 toolbox_release(vw->vt, event);
909 vik_window_pan_release ( vw, event );
912 toolbox_release(vw->vt, event);
916 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
918 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
919 if ( modifiers == GDK_CONTROL_MASK ) {
920 /* control == pan up & down */
921 if ( event->direction == GDK_SCROLL_UP )
922 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
924 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 );
925 } else if ( modifiers == GDK_SHIFT_MASK ) {
926 /* shift == pan left & right */
927 if ( event->direction == GDK_SCROLL_UP )
928 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
930 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 );
931 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
932 // This zoom is on the center position
933 if ( event->direction == GDK_SCROLL_UP )
934 vik_viewport_zoom_in (vw->viking_vvp);
936 vik_viewport_zoom_out (vw->viking_vvp);
938 /* make sure mouse is still over the same point on the map when we zoom */
941 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
942 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
943 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
944 if ( event->direction == GDK_SCROLL_UP )
945 vik_viewport_zoom_in (vw->viking_vvp);
947 vik_viewport_zoom_out(vw->viking_vvp);
948 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
949 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
950 center_y + (y - event->y) );
958 /********************************************************************************
960 ********************************************************************************/
961 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
965 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
966 GdkGC *thickgc = gdk_gc_new(d);
968 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
969 gdouble dx = (x2-x1)/len*10;
970 gdouble dy = (y2-y1)/len*10;
971 gdouble c = cos(DEG2RAD(15.0));
972 gdouble s = sin(DEG2RAD(15.0));
974 gdouble baseangle = 0;
977 /* draw line with arrow ends */
979 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
980 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
981 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
984 a_viewport_clip_line(&x1, &y1, &x2, &y2);
985 gdk_draw_line(d, gc, x1, y1, x2, y2);
987 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
988 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
989 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
990 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
991 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
992 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
998 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1002 gdk_gc_copy(thickgc, gc);
1003 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1004 gdk_color_parse("#2255cc", &color);
1005 gdk_gc_set_rgb_fg_color(thickgc, &color);
1007 gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - RAD2DEG(baseangle))*64, -RAD2DEG(angle)*64);
1010 gdk_gc_copy(thickgc, gc);
1011 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1012 for (i=0; i<180; i++) {
1013 c = cos(DEG2RAD(i)*2 + baseangle);
1014 s = sin(DEG2RAD(i)*2 + baseangle);
1017 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1019 gdouble ticksize = 2*CW;
1020 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1024 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1025 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1026 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1027 c = (CR+CW*2)*cos(baseangle);
1028 s = (CR+CW*2)*sin(baseangle);
1029 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1030 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1033 #define LABEL(x, y, w, h) { \
1034 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1035 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1036 gdk_draw_layout(d, gc, (x), (y), pl); }
1038 gint wd, hd, xd, yd;
1039 gint wb, hb, xb, yb;
1041 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1042 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1043 pango_layout_set_text(pl, "N", -1);
1044 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1046 /* draw label with distance */
1047 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1048 switch (dist_units) {
1049 case VIK_UNITS_DISTANCE_KILOMETRES:
1050 if (distance >= 1000 && distance < 100000) {
1051 g_sprintf(str, "%3.2f km", distance/1000.0);
1052 } else if (distance < 1000) {
1053 g_sprintf(str, "%d m", (int)distance);
1055 g_sprintf(str, "%d km", (int)distance/1000);
1058 case VIK_UNITS_DISTANCE_MILES:
1059 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1060 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1061 } else if (distance < VIK_MILES_TO_METERS(1)) {
1062 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1064 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1068 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1071 pango_layout_set_text(pl, str, -1);
1073 pango_layout_get_pixel_size ( pl, &wd, &hd );
1075 xd = (x1+x2)/2 + dy;
1076 yd = (y1+y2)/2 - hd/2 - dx;
1078 xd = (x1+x2)/2 - dy;
1079 yd = (y1+y2)/2 - hd/2 + dx;
1082 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1087 LABEL(xd, yd, wd, hd);
1089 /* draw label with bearing */
1090 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1091 pango_layout_set_text(pl, str, -1);
1092 pango_layout_get_pixel_size ( pl, &wb, &hb );
1093 xb = x1 + CR*cos(angle-M_PI_2);
1094 yb = y1 + CR*sin(angle-M_PI_2);
1096 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1102 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1103 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1107 LABEL(xb, yb, wb, hb);
1111 g_object_unref ( G_OBJECT ( pl ) );
1112 g_object_unref ( G_OBJECT ( labgc ) );
1113 g_object_unref ( G_OBJECT ( thickgc ) );
1119 gboolean has_oldcoord;
1121 } ruler_tool_state_t;
1123 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1125 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1128 s->has_oldcoord = FALSE;
1132 static void ruler_destroy (ruler_tool_state_t *s)
1137 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1142 if ( event->button == 1 ) {
1143 gchar *lat=NULL, *lon=NULL;
1144 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1145 vik_coord_to_latlon ( &coord, &ll );
1146 a_coords_latlon_to_string ( &ll, &lat, &lon );
1147 if ( s->has_oldcoord ) {
1148 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1149 switch (dist_units) {
1150 case VIK_UNITS_DISTANCE_KILOMETRES:
1151 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1153 case VIK_UNITS_DISTANCE_MILES:
1154 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1157 temp = g_strdup_printf ("Just to keep the compiler happy");
1158 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1161 s->has_oldcoord = FALSE;
1164 temp = g_strdup_printf ( "%s %s", lat, lon );
1165 s->has_oldcoord = TRUE;
1168 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1171 s->oldcoord = coord;
1174 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1175 draw_update ( s->vw );
1177 return VIK_LAYER_TOOL_ACK;
1180 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1182 VikViewport *vvp = s->vvp;
1183 VikWindow *vw = s->vw;
1189 if ( s->has_oldcoord ) {
1190 int oldx, oldy, w1, h1, w2, h2;
1191 static GdkPixmap *buf = NULL;
1192 gchar *lat=NULL, *lon=NULL;
1193 w1 = vik_viewport_get_width(vvp);
1194 h1 = vik_viewport_get_height(vvp);
1196 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1198 gdk_drawable_get_size(buf, &w2, &h2);
1199 if (w1 != w2 || h1 != h2) {
1200 g_object_unref ( G_OBJECT ( buf ) );
1201 buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
1204 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1205 vik_coord_to_latlon ( &coord, &ll );
1206 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1208 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1209 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1210 draw_ruler(vvp, buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1211 if (draw_buf_done) {
1212 static gpointer pass_along[3];
1213 pass_along[0] = GTK_WIDGET(vvp)->window;
1214 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1215 pass_along[2] = buf;
1216 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1217 draw_buf_done = FALSE;
1219 a_coords_latlon_to_string(&ll, &lat, &lon);
1220 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1221 switch (dist_units) {
1222 case VIK_UNITS_DISTANCE_KILOMETRES:
1223 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1225 case VIK_UNITS_DISTANCE_MILES:
1226 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1229 temp = g_strdup_printf ("Just to keep the compiler happy");
1230 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1232 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1235 return VIK_LAYER_TOOL_ACK;
1238 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1240 return VIK_LAYER_TOOL_ACK;
1243 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1245 draw_update ( s->vw );
1248 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1250 if (event->keyval == GDK_Escape) {
1251 s->has_oldcoord = FALSE;
1252 ruler_deactivate ( vl, s );
1255 // Regardless of whether we used it, return false so other GTK things may use it
1259 static VikToolInterface ruler_tool =
1260 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1261 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1262 (VikToolConstructorFunc) ruler_create,
1263 (VikToolDestructorFunc) ruler_destroy,
1264 (VikToolActivationFunc) NULL,
1265 (VikToolActivationFunc) ruler_deactivate,
1266 (VikToolMouseFunc) ruler_click,
1267 (VikToolMouseMoveFunc) ruler_move,
1268 (VikToolMouseFunc) ruler_release,
1269 (VikToolKeyFunc) ruler_key_press,
1271 GDK_CURSOR_IS_PIXMAP,
1272 &cursor_ruler_pixbuf };
1273 /*** end ruler code ********************************************************/
1277 /********************************************************************************
1279 ********************************************************************************/
1284 // Track zoom bounds for zoom tool with shift modifier:
1285 gboolean bounds_active;
1288 } zoom_tool_state_t;
1291 * In case the screen size has changed
1293 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1297 // Allocate a drawing area the size of the viewport
1298 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1299 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1301 if ( !zts->pixmap ) {
1303 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1306 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1308 if ( w1 != w2 || h1 != h2 ) {
1309 // Has changed - delete and recreate with new values
1310 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1311 zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 );
1315 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1317 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1322 zts->bounds_active = FALSE;
1326 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1329 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1333 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1335 zts->vw->modified = TRUE;
1336 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1340 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1341 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1343 gboolean skip_update = FALSE;
1345 zts->bounds_active = FALSE;
1347 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1348 // This zoom is on the center position
1349 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1350 if ( event->button == 1 )
1351 vik_viewport_zoom_in (zts->vw->viking_vvp);
1352 else if ( event->button == 3 )
1353 vik_viewport_zoom_out (zts->vw->viking_vvp);
1355 else if ( modifiers == GDK_CONTROL_MASK ) {
1356 // This zoom is to recenter on the mouse position
1357 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1358 if ( event->button == 1 )
1359 vik_viewport_zoom_in (zts->vw->viking_vvp);
1360 else if ( event->button == 3 )
1361 vik_viewport_zoom_out (zts->vw->viking_vvp);
1363 else if ( modifiers == GDK_SHIFT_MASK ) {
1364 // Get start of new zoom bounds
1365 if ( event->button == 1 ) {
1366 zts->bounds_active = TRUE;
1367 zts->start_x = (gint) event->x;
1368 zts->start_y = (gint) event->y;
1373 /* make sure mouse is still over the same point on the map when we zoom */
1374 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1375 if ( event->button == 1 )
1376 vik_viewport_zoom_in (zts->vw->viking_vvp);
1377 else if ( event->button == 3 )
1378 vik_viewport_zoom_out(zts->vw->viking_vvp);
1379 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1380 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1381 center_x + (x - event->x),
1382 center_y + (y - event->y) );
1386 draw_update ( zts->vw );
1388 return VIK_LAYER_TOOL_ACK;
1391 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1393 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1395 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1396 zoomtool_resize_pixmap ( zts );
1398 // Blank out currently drawn area
1399 gdk_draw_drawable ( zts->pixmap,
1400 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1401 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1402 0, 0, 0, 0, -1, -1);
1404 // Calculate new box starting point & size in pixels
1405 int xx, yy, width, height;
1406 if ( event->y > zts->start_y ) {
1408 height = event->y-zts->start_y;
1412 height = zts->start_y-event->y;
1414 if ( event->x > zts->start_x ) {
1416 width = event->x-zts->start_x;
1420 width = zts->start_x-event->x;
1424 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1426 // Only actually draw when there's time to do so
1427 if (draw_buf_done) {
1428 static gpointer pass_along[3];
1429 pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window;
1430 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1431 pass_along[2] = zts->pixmap;
1432 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1433 draw_buf_done = FALSE;
1436 return VIK_LAYER_TOOL_ACK;
1439 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1441 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1443 zts->bounds_active = FALSE;
1445 // Ensure haven't just released on the exact same position
1446 // i.e. probably haven't moved the mouse at all
1447 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1449 VikCoord coord1, coord2;
1450 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1451 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1453 // From the extend of the bounds pick the best zoom level
1454 // c.f. trw_layer_zoom_to_show_latlons()
1455 // Maybe refactor...
1456 struct LatLon ll1, ll2;
1457 vik_coord_to_latlon(&coord1, &ll1);
1458 vik_coord_to_latlon(&coord2, &ll2);
1459 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1460 (ll1.lon+ll2.lon)/2 };
1462 VikCoord new_center;
1463 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1464 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1466 /* Convert into definite 'smallest' and 'largest' positions */
1467 struct LatLon minmin;
1468 if ( ll1.lat < ll2.lat )
1469 minmin.lat = ll1.lat;
1471 minmin.lat = ll2.lat;
1473 struct LatLon maxmax;
1474 if ( ll1.lon > ll2.lon )
1475 maxmax.lon = ll1.lon;
1477 maxmax.lon = ll2.lon;
1479 /* Always recalculate the 'best' zoom level */
1480 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1481 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1483 gdouble min_lat, max_lat, min_lon, max_lon;
1484 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1485 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1486 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1487 /* NB I think the logic used in this test to determine if the bounds is within view
1488 fails if track goes across 180 degrees longitude.
1489 Hopefully that situation is not too common...
1490 Mind you viking doesn't really do edge locations to well anyway */
1491 if ( min_lat < minmin.lat &&
1492 max_lat > minmin.lat &&
1493 min_lon < maxmax.lon &&
1494 max_lon > maxmax.lon )
1495 /* Found within zoom level */
1500 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1503 draw_update ( zts->vw );
1505 return VIK_LAYER_TOOL_ACK;
1508 static VikToolInterface zoom_tool =
1509 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1510 (VikToolConstructorFunc) zoomtool_create,
1511 (VikToolDestructorFunc) zoomtool_destroy,
1512 (VikToolActivationFunc) NULL,
1513 (VikToolActivationFunc) NULL,
1514 (VikToolMouseFunc) zoomtool_click,
1515 (VikToolMouseMoveFunc) zoomtool_move,
1516 (VikToolMouseFunc) zoomtool_release,
1519 GDK_CURSOR_IS_PIXMAP,
1520 &cursor_zoom_pixbuf };
1521 /*** end zoom code ********************************************************/
1523 /********************************************************************************
1525 ********************************************************************************/
1526 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1531 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1533 vw->modified = TRUE;
1534 if ( event->button == 1 )
1535 vik_window_pan_click ( vw, event );
1537 return VIK_LAYER_TOOL_ACK;
1540 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1542 vik_window_pan_move ( vw, event );
1543 return VIK_LAYER_TOOL_ACK;
1546 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1548 if ( event->button == 1 )
1549 vik_window_pan_release ( vw, event );
1550 return VIK_LAYER_TOOL_ACK;
1553 static VikToolInterface pan_tool =
1554 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1555 (VikToolConstructorFunc) pantool_create,
1556 (VikToolDestructorFunc) NULL,
1557 (VikToolActivationFunc) NULL,
1558 (VikToolActivationFunc) NULL,
1559 (VikToolMouseFunc) pantool_click,
1560 (VikToolMouseMoveFunc) pantool_move,
1561 (VikToolMouseFunc) pantool_release,
1565 /*** end pan code ********************************************************/
1567 /********************************************************************************
1569 ********************************************************************************/
1570 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1572 tool_ed_t *t = g_new(tool_ed_t, 1);
1576 t->is_waypoint = FALSE;
1580 static void selecttool_destroy (tool_ed_t *t)
1588 GdkEventButton *event;
1589 tool_ed_t *tool_edit;
1592 static void click_layer_selected (VikLayer *vl, clicker *ck)
1594 /* Do nothing when function call returns true; */
1595 /* i.e. stop on first found item */
1598 if ( vik_layer_get_interface(vl->type)->select_click )
1599 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1602 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1604 /* Only allow selection on primary button */
1605 if ( event->button == 1 ) {
1606 /* Enable click to apply callback to potentially all track/waypoint layers */
1607 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1608 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1611 ck.vvp = t->vw->viking_vvp;
1614 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1617 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1620 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1622 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1623 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1624 gint type = vik_treeview_item_get_type ( vtv, &iter );
1625 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1626 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1628 vik_treeview_item_unselect ( vtv, &iter );
1629 if ( vik_window_clear_highlight ( t->vw ) )
1630 draw_update ( t->vw );
1635 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1637 /* Act on currently selected item to show menu */
1638 if ( t->vw->selected_track || t->vw->selected_waypoint )
1639 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1640 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1643 return VIK_LAYER_TOOL_ACK;
1646 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1648 /* Only allow selection on primary button */
1649 if ( event->button == 1 ) {
1650 // Don't care about vl here
1652 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1653 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1655 return VIK_LAYER_TOOL_ACK;
1658 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1660 /* Only allow selection on primary button */
1661 if ( event->button == 1 ) {
1662 // Don't care about vl here
1664 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1665 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1667 return VIK_LAYER_TOOL_ACK;
1670 static VikToolInterface select_tool =
1671 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1672 (VikToolConstructorFunc) selecttool_create,
1673 (VikToolDestructorFunc) selecttool_destroy,
1674 (VikToolActivationFunc) NULL,
1675 (VikToolActivationFunc) NULL,
1676 (VikToolMouseFunc) selecttool_click,
1677 (VikToolMouseMoveFunc) selecttool_move,
1678 (VikToolMouseFunc) selecttool_release,
1679 (VikToolKeyFunc) NULL,
1684 /*** end select tool code ********************************************************/
1686 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1688 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1689 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1690 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1691 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1692 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1693 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1694 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1695 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1700 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1702 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1703 g_assert(check_box);
1704 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1706 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1708 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1711 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1715 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1718 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1721 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1724 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1728 gchar *s = (gchar *)gtk_action_get_name(a);
1734 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1735 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1736 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1737 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1738 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1743 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1745 VikCoord new_center;
1747 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1748 struct LatLon ll, llold;
1749 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1750 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1751 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1755 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1756 struct UTM utm, utmold;
1757 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1758 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1759 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1764 g_critical("Houston, we've had a problem.");
1768 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1773 * Refresh maps displayed
1775 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1777 // Only get 'new' maps
1778 simple_map_update ( vw, TRUE );
1781 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1783 VikLayerTypeEnum type;
1784 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1785 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1786 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1788 vw->modified = TRUE;
1794 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1796 a_clipboard_copy_selected ( vw->viking_vlp );
1799 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1801 vik_layers_panel_cut_selected ( vw->viking_vlp );
1802 vw->modified = TRUE;
1805 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1807 if ( a_clipboard_paste ( vw->viking_vlp ) )
1809 vw->modified = TRUE;
1813 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1815 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1816 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1819 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1822 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1824 #if GTK_CHECK_VERSION (2, 14, 0)
1826 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1827 GError *error = NULL;
1828 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1829 if ( !show && !error )
1830 // No error to show, so unlikely this will get called
1831 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1834 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 );
1835 g_error_free ( error );
1839 a_dialog_error_msg ( GTK_WINDOW(vw), "Help is not available in this build." ); // Unlikely to happen so not going to bother with I8N
1841 #endif /* WINDOWS */
1844 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1846 a_dialog_about(GTK_WINDOW(vw));
1849 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1851 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1853 vik_layers_panel_delete_selected ( vw->viking_vlp );
1854 vw->modified = TRUE;
1857 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1860 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1862 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1863 g_assert(check_box);
1864 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1866 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1868 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1871 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1873 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1876 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1878 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1880 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1883 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1885 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1888 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1890 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1892 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1895 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1897 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1900 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1902 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1904 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1907 /***************************************
1908 ** tool management routines
1910 ***************************************/
1912 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1914 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1917 vt->active_tool = -1;
1922 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1924 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1925 vt->tools[vt->n_tools].ti = *vti;
1926 vt->tools[vt->n_tools].layer_type = layer_type;
1928 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1931 vt->tools[vt->n_tools].state = NULL;
1936 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1939 for (i=0; i<vt->n_tools; i++) {
1940 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1947 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1949 int tool = toolbox_get_tool(vt, tool_name);
1950 toolbox_tool_t *t = &vt->tools[tool];
1951 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1953 if (tool == vt->n_tools) {
1954 g_critical("trying to activate a non-existent tool...");
1957 /* is the tool already active? */
1958 if (vt->active_tool == tool) {
1962 if (vt->active_tool != -1) {
1963 if (vt->tools[vt->active_tool].ti.deactivate) {
1964 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1967 if (t->ti.activate) {
1968 t->ti.activate(vl, t->state);
1970 vt->active_tool = tool;
1973 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1975 int tool = toolbox_get_tool(vt, tool_name);
1976 toolbox_tool_t *t = &vt->tools[tool];
1977 if (t->ti.cursor == NULL) {
1978 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1979 GError *cursor_load_err = NULL;
1980 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1981 /* TODO: settable offeset */
1982 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1983 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1985 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1988 return t->ti.cursor;
1991 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1993 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1994 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
1995 gint ltype = vt->tools[vt->active_tool].layer_type;
1996 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1997 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2001 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2003 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2004 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2005 gint ltype = vt->tools[vt->active_tool].layer_type;
2006 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2007 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2008 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2012 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2014 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2015 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2016 gint ltype = vt->tools[vt->active_tool].layer_type;
2017 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2018 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2021 /** End tool management ************************************/
2023 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2025 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2028 /* this function gets called whenever a toolbar tool is clicked */
2029 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2031 /* White Magic, my friends ... White Magic... */
2033 toolbox_activate(vw->vt, gtk_action_get_name(a));
2035 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2037 if ( GTK_WIDGET(vw->viking_vvp)->window )
2038 /* We set cursor, even if it is NULL: it resets to default */
2039 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->viewport_cursor );
2041 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2042 vw->current_tool = TOOL_PAN;
2044 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2045 vw->current_tool = TOOL_ZOOM;
2047 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2048 vw->current_tool = TOOL_RULER;
2050 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2051 vw->current_tool = TOOL_SELECT;
2054 /* TODO: only enable tools from active layer */
2055 VikLayerTypeEnum layer_id;
2056 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2057 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2058 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2059 vw->current_tool = TOOL_LAYER;
2060 vw->tool_layer_id = layer_id;
2061 vw->tool_tool_id = tool_id;
2066 draw_status_tool ( vw );
2069 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2074 g_free ( vw->filename );
2075 if ( filename == NULL )
2077 vw->filename = NULL;
2081 vw->filename = g_strdup(filename);
2084 /* Refresh window's title */
2085 file = window_get_filename ( vw );
2086 title = g_strdup_printf( "%s - Viking", file );
2087 gtk_window_set_title ( GTK_WINDOW(vw), title );
2091 static const gchar *window_get_filename ( VikWindow *vw )
2093 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2096 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2098 GtkWidget *mode_button;
2101 #ifdef VIK_CONFIG_EXPEDIA
2102 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2104 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2105 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2106 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2108 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2109 g_assert ( mode_button );
2114 * vik_window_get_pan_move:
2115 * @vw: some VikWindow
2117 * Retrieves @vw's pan_move.
2119 * Should be removed as soon as possible.
2121 * Returns: @vw's pan_move
2125 gboolean vik_window_get_pan_move ( VikWindow *vw )
2127 return vw->pan_move;
2130 static void on_activate_recent_item (GtkRecentChooser *chooser,
2135 filename = gtk_recent_chooser_get_current_uri (chooser);
2136 if (filename != NULL)
2138 GFile *file = g_file_new_for_uri ( filename );
2139 gchar *path = g_file_get_path ( file );
2140 g_object_unref ( file );
2141 if ( self->filename )
2143 GSList *filenames = NULL;
2144 filenames = g_slist_append ( filenames, path );
2145 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2146 // NB: GSList & contents are freed by main.open_window
2149 vik_window_open_file ( self, path, TRUE );
2157 static void setup_recent_files (VikWindow *self)
2159 GtkRecentManager *manager;
2160 GtkRecentFilter *filter;
2161 GtkWidget *menu, *menu_item;
2163 filter = gtk_recent_filter_new ();
2164 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2165 gtk_recent_filter_add_group(filter, "viking");
2167 manager = gtk_recent_manager_get_default ();
2168 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2169 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2170 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2172 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2173 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2175 g_signal_connect (G_OBJECT (menu), "item-activated",
2176 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2179 static void update_recently_used_document(const gchar *filename)
2181 /* Update Recently Used Document framework */
2182 GtkRecentManager *manager = gtk_recent_manager_get_default();
2183 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2184 gchar *groups[] = {"viking", NULL};
2185 GFile *file = g_file_new_for_commandline_arg(filename);
2186 gchar *uri = g_file_get_uri(file);
2187 gchar *basename = g_path_get_basename(filename);
2188 g_object_unref(file);
2191 recent_data->display_name = basename;
2192 recent_data->description = NULL;
2193 recent_data->mime_type = "text/x-gps-data";
2194 recent_data->app_name = (gchar *) g_get_application_name ();
2195 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2196 recent_data->groups = groups;
2197 recent_data->is_private = FALSE;
2198 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2200 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2205 g_free (recent_data->app_exec);
2206 g_slice_free (GtkRecentData, recent_data);
2210 * Call this before doing things that may take a long time and otherwise not show any other feedback
2211 * such as loading and saving files
2213 void vik_window_set_busy_cursor ( VikWindow *vw )
2215 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2216 // Viewport has a separate cursor
2217 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->busy_cursor );
2218 // Ensure cursor updated before doing stuff
2219 while( gtk_events_pending() )
2220 gtk_main_iteration();
2223 void vik_window_clear_busy_cursor ( VikWindow *vw )
2225 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2226 // Restore viewport cursor
2227 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->viewport_cursor );
2230 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2232 vik_window_set_busy_cursor ( vw );
2234 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2236 case LOAD_TYPE_READ_FAILURE:
2237 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2239 case LOAD_TYPE_GPSBABEL_FAILURE:
2240 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2242 case LOAD_TYPE_GPX_FAILURE:
2243 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2245 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2246 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2248 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2250 // Since we can process .vik files with issues just show a warning in the status bar
2251 // Not that a user can do much about it... or tells them what this issue is yet...
2252 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2253 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2256 // No break, carry on to show any data
2257 case LOAD_TYPE_VIK_SUCCESS:
2259 GtkWidget *mode_button;
2261 if ( change_filename )
2262 window_set_filename ( vw, filename );
2263 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2264 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. */
2265 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2266 vw->only_updating_coord_mode_ui = FALSE;
2268 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2270 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2271 g_assert ( mode_button );
2272 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2274 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2275 g_assert ( mode_button );
2276 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2278 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2279 g_assert ( mode_button );
2280 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2282 //case LOAD_TYPE_OTHER_SUCCESS:
2284 update_recently_used_document(filename);
2289 vik_window_clear_busy_cursor ( vw );
2292 static void load_file ( GtkAction *a, VikWindow *vw )
2294 GSList *files = NULL;
2295 GSList *cur_file = NULL;
2297 if (!strcmp(gtk_action_get_name(a), "Open")) {
2300 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2304 g_critical("Houston, we've had a problem.");
2308 if ( ! vw->open_dia )
2310 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2312 GTK_FILE_CHOOSER_ACTION_OPEN,
2313 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2314 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2316 GtkFileFilter *filter;
2317 // NB file filters are listed this way for alphabetical ordering
2318 #ifdef VIK_CONFIG_GEOCACHES
2319 filter = gtk_file_filter_new ();
2320 gtk_file_filter_set_name( filter, _("Geocaching") );
2321 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2322 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2325 filter = gtk_file_filter_new ();
2326 gtk_file_filter_set_name( filter, _("Google Earth") );
2327 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2328 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2330 filter = gtk_file_filter_new ();
2331 gtk_file_filter_set_name( filter, _("GPX") );
2332 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2333 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2335 filter = gtk_file_filter_new ();
2336 gtk_file_filter_set_name( filter, _("Viking") );
2337 gtk_file_filter_add_pattern ( filter, "*.vik" );
2338 gtk_file_filter_add_pattern ( filter, "*.viking" );
2339 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2341 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2342 // However assume this are barely used and thus not worthy of inclusion
2343 // as they'll just make the options too many and have no clear file pattern
2344 // one can always use the all option
2345 filter = gtk_file_filter_new ();
2346 gtk_file_filter_set_name( filter, _("All") );
2347 gtk_file_filter_add_pattern ( filter, "*" );
2348 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2349 // Default to any file - same as before open filters were added
2350 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2352 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2353 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2354 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2356 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2358 gtk_widget_hide ( vw->open_dia );
2359 #ifdef VIKING_PROMPT_IF_MODIFIED
2360 if ( (vw->modified || vw->filename) && newwindow )
2362 if ( vw->filename && newwindow )
2364 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2366 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2367 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2368 gboolean first_vik_file = TRUE;
2370 while ( cur_file ) {
2372 gchar *file_name = cur_file->data;
2373 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2374 // Load first of many .vik files in current window
2375 if ( first_vik_file ) {
2376 vik_window_open_file ( vw, file_name, TRUE );
2377 first_vik_file = FALSE;
2380 // Load each subsequent .vik file in a separate window
2381 VikWindow *newvw = vik_window_new_window ();
2383 vik_window_open_file ( newvw, file_name, TRUE );
2388 vik_window_open_file ( vw, file_name, change_fn );
2391 cur_file = g_slist_next (cur_file);
2393 g_slist_free (files);
2397 gtk_widget_hide ( vw->open_dia );
2400 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2402 gboolean rv = FALSE;
2404 if ( ! vw->save_dia )
2406 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2408 GTK_FILE_CHOOSER_ACTION_SAVE,
2409 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2410 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2412 GtkFileFilter *filter;
2413 filter = gtk_file_filter_new ();
2414 gtk_file_filter_set_name( filter, _("All") );
2415 gtk_file_filter_add_pattern ( filter, "*" );
2416 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2418 filter = gtk_file_filter_new ();
2419 gtk_file_filter_set_name( filter, _("Viking") );
2420 gtk_file_filter_add_pattern ( filter, "*.vik" );
2421 gtk_file_filter_add_pattern ( filter, "*.viking" );
2422 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2423 // Default to a Viking file
2424 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2426 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2427 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2429 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2430 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2431 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2432 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2434 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2436 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2438 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2439 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 ) ) )
2441 window_set_filename ( vw, fn );
2442 rv = window_save ( vw );
2443 vw->modified = FALSE;
2447 g_free ( auto_save_name );
2448 gtk_widget_hide ( vw->save_dia );
2452 static gboolean window_save ( VikWindow *vw )
2454 vik_window_set_busy_cursor ( vw );
2455 gboolean success = TRUE;
2457 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2459 update_recently_used_document ( vw->filename );
2463 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2466 vik_window_clear_busy_cursor ( vw );
2470 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2472 if ( ! vw->filename )
2473 return save_file_as ( NULL, vw );
2476 vw->modified = FALSE;
2477 return window_save ( vw );
2481 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2483 // Via the file menu, acquiring from a GPS makes a new layer
2484 // this has always been the way (not entirely sure if this was the real intention!)
2485 // thus maintain the behaviour ATM.
2486 // Hence explicit setting here (as the value may be changed elsewhere)
2487 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2488 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2491 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2493 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2496 #ifdef VIK_CONFIG_GOOGLE
2497 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2499 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
2503 #ifdef VIK_CONFIG_OPENSTREETMAP
2504 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2506 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2509 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
2511 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface );
2515 #ifdef VIK_CONFIG_GEOCACHES
2516 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2518 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2522 #ifdef VIK_CONFIG_GEOTAG
2523 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2525 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2526 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2530 #ifdef VIK_CONFIG_GEONAMES
2531 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
2533 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface );
2537 static void goto_default_location( GtkAction *a, VikWindow *vw)
2540 ll.lat = a_vik_get_default_lat();
2541 ll.lon = a_vik_get_default_long();
2542 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2543 vik_layers_panel_emit_update(vw->viking_vlp);
2547 static void goto_address( GtkAction *a, VikWindow *vw)
2549 a_vik_goto ( vw, vw->viking_vvp );
2550 vik_layers_panel_emit_update ( vw->viking_vlp );
2553 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2558 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
2560 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
2563 return; // Internally broken :(
2565 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
2566 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
2567 // NB no update needed
2569 g_strfreev ( texts );
2572 static void preferences_change_update ( VikWindow *vw, gpointer data )
2574 // Want to update all TrackWaypoint layers
2575 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2577 GList *iter = g_list_first ( layers );
2579 // Reset the individual waypoints themselves due to the preferences change
2580 VikTrwLayer *vtl = VIK_TRW_LAYER(VIK_LAYER(layers->data));
2581 vik_trw_layer_reset_waypoints ( vtl );
2582 iter = g_list_next ( iter );
2585 g_list_free ( layers );
2590 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2592 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2594 a_preferences_show_window ( GTK_WINDOW(vw) );
2596 // Has the waypoint size setting changed?
2597 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
2598 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2599 clear_garmin_icon_syms ();
2601 // Update all windows
2602 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
2606 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2608 /* Simplistic repeat of preference setting
2609 Only the name & type are important for setting the preference via this 'external' way */
2610 VikLayerParam pref_lat[] = {
2611 { VIK_LAYER_NUM_TYPES,
2612 VIKING_PREFERENCES_NAMESPACE "default_latitude",
2613 VIK_LAYER_PARAM_DOUBLE,
2616 VIK_LAYER_WIDGET_SPINBUTTON,
2621 VikLayerParam pref_lon[] = {
2622 { VIK_LAYER_NUM_TYPES,
2623 VIKING_PREFERENCES_NAMESPACE "default_longitude",
2624 VIK_LAYER_PARAM_DOUBLE,
2627 VIK_LAYER_WIDGET_SPINBUTTON,
2633 /* Get current center */
2635 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2637 /* Apply to preferences */
2638 VikLayerParamData vlp_data;
2639 vlp_data.d = ll.lat;
2640 a_preferences_run_setparam (vlp_data, pref_lat);
2641 vlp_data.d = ll.lon;
2642 a_preferences_run_setparam (vlp_data, pref_lon);
2643 /* Remember to save */
2644 a_preferences_save_to_file();
2647 static void clear_cb ( GtkAction *a, VikWindow *vw )
2649 vik_layers_panel_clear ( vw->viking_vlp );
2650 window_set_filename ( vw, NULL );
2654 static void window_close ( GtkAction *a, VikWindow *vw )
2656 if ( ! delete_event ( vw ) )
2657 gtk_widget_destroy ( GTK_WIDGET(vw) );
2660 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2662 if (save_file( NULL, vw)) {
2663 window_close( NULL, vw);
2670 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2672 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2673 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2675 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2676 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2681 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2683 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2684 GdkPixbuf *pixbuf_to_save;
2685 gdouble old_xmpp, old_ympp;
2686 GError *error = NULL;
2688 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2689 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2692 _("Generating image file...") );
2694 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2695 // Ensure dialog shown
2696 gtk_widget_show_all ( msgbox );
2698 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2699 while ( gtk_events_pending() )
2700 gtk_main_iteration ();
2701 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2702 // At least the empty box can give a clue something's going on + the statusbar msg...
2703 // Windows version under Wine OK!
2705 /* backup old zoom & set new */
2706 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2707 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2708 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2710 /* reset width and height: */
2711 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2713 /* draw all layers */
2716 /* save buffer as file. */
2717 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);
2718 if ( !pixbuf_to_save ) {
2719 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2720 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
2724 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2727 g_warning("Unable to write to file %s: %s", fn, error->message );
2728 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
2729 g_error_free (error);
2733 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2735 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2738 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2739 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2740 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2742 /* pretend like nothing happened ;) */
2743 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2744 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2745 vik_viewport_configure ( vw->viking_vvp );
2749 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 )
2751 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2752 gchar *name_of_file = g_malloc ( size );
2754 struct UTM utm_orig, utm;
2756 /* *** copied from above *** */
2757 GdkPixbuf *pixbuf_to_save;
2758 gdouble old_xmpp, old_ympp;
2759 GError *error = NULL;
2761 /* backup old zoom & set new */
2762 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2763 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2764 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2766 /* reset width and height: do this only once for all images (same size) */
2767 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2768 /* *** end copy from above *** */
2770 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2774 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2776 for ( y = 1; y <= tiles_h; y++ )
2778 for ( x = 1; x <= tiles_w; x++ )
2780 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2782 if ( tiles_w & 0x1 )
2783 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2785 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2786 if ( tiles_h & 0x1 ) /* odd */
2787 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2789 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2791 /* move to correct place. */
2792 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2796 /* save buffer as file. */
2797 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);
2798 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2801 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2802 g_error_free (error);
2805 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2809 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2810 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2811 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2812 vik_viewport_configure ( vw->viking_vvp );
2815 g_free ( name_of_file );
2818 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2820 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2821 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2823 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2824 gdouble zoom = pow (2, active-2 );
2826 gdouble width_min, width_max, height_min, height_max;
2829 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2830 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2832 /* TODO: support for xzoom and yzoom values */
2833 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2834 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2836 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2837 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2839 gtk_spin_button_set_value ( width_spin, width );
2840 gtk_spin_button_set_value ( height_spin, height );
2843 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2845 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2847 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2848 gdouble zoom = pow (2, active-2 );
2852 w = gtk_spin_button_get_value(width_spin) * zoom;
2853 h = gtk_spin_button_get_value(height_spin) * zoom;
2854 if (pass_along[4]) /* save many images; find TOTAL area covered */
2856 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2857 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2859 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2860 switch (dist_units) {
2861 case VIK_UNITS_DISTANCE_KILOMETRES:
2862 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2864 case VIK_UNITS_DISTANCE_MILES:
2865 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2868 label_text = g_strdup_printf ("Just to keep the compiler happy");
2869 g_critical("Houston, we've had a problem. distance=%d", dist_units);
2872 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
2873 g_free ( label_text );
2877 * Get an allocated filename (or directory as specified)
2879 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
2882 if ( one_image_only )
2885 if (!vw->save_img_dia) {
2886 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
2888 GTK_FILE_CHOOSER_ACTION_SAVE,
2889 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2890 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2893 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
2895 GtkFileFilter *filter;
2896 filter = gtk_file_filter_new ();
2897 gtk_file_filter_set_name ( filter, _("All") );
2898 gtk_file_filter_add_pattern ( filter, "*" );
2899 gtk_file_chooser_add_filter ( chooser, filter );
2901 filter = gtk_file_filter_new ();
2902 gtk_file_filter_set_name ( filter, _("JPG") );
2903 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2904 gtk_file_chooser_add_filter ( chooser, filter );
2906 filter = gtk_file_filter_new ();
2907 gtk_file_filter_set_name ( filter, _("PNG") );
2908 gtk_file_filter_add_mime_type ( filter, "image/png");
2909 gtk_file_chooser_add_filter ( chooser, filter );
2912 gtk_file_chooser_set_filter ( chooser, filter );
2914 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
2915 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
2918 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
2919 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
2920 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
2921 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 ) ) )
2924 gtk_widget_hide ( vw->save_img_dia );
2928 // For some reason this method is only written to work in UTM...
2929 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
2930 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
2934 if (!vw->save_img_dir_dia) {
2935 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
2937 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2938 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2939 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2941 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
2942 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
2945 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
2946 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
2948 gtk_widget_hide ( vw->save_img_dir_dia );
2953 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
2955 /* todo: default for answers inside VikWindow or static (thruout instance) */
2956 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
2957 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2959 GTK_RESPONSE_REJECT,
2961 GTK_RESPONSE_ACCEPT,
2963 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
2964 GtkWidget *png_radio, *jpeg_radio;
2965 GtkWidget *current_window_button;
2966 gpointer current_window_pass_along[7];
2967 GtkWidget *zoom_label, *zoom_combo;
2968 GtkWidget *total_size_label;
2970 /* only used if (!one_image_only) */
2971 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
2973 width_label = gtk_label_new ( _("Width (pixels):") );
2974 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
2975 height_label = gtk_label_new ( _("Height (pixels):") );
2976 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
2978 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
2980 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
2981 /* TODO: separate xzoom and yzoom factors */
2982 zoom_combo = create_zoom_combo_all_levels();
2984 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
2985 gint active = 2 + round ( log (mpp) / log (2) );
2987 // Can we not hard code size here?
2990 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
2992 total_size_label = gtk_label_new ( NULL );
2994 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
2995 current_window_pass_along [0] = vw;
2996 current_window_pass_along [1] = width_spin;
2997 current_window_pass_along [2] = height_spin;
2998 current_window_pass_along [3] = zoom_combo;
2999 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3000 current_window_pass_along [5] = NULL;
3001 current_window_pass_along [6] = total_size_label;
3002 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3004 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3005 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3007 if ( ! vw->draw_image_save_as_png )
3008 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3010 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0);
3011 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0);
3012 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0);
3013 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0);
3015 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), win_warning_label, FALSE, FALSE, 0);
3017 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0);
3018 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0);
3019 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0);
3020 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0);
3021 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_combo, FALSE, FALSE, 0);
3023 if ( ! one_image_only )
3025 GtkWidget *tiles_width_label, *tiles_height_label;
3027 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3028 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3029 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3030 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3031 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0);
3032 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0);
3033 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0);
3034 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0);
3036 current_window_pass_along [4] = tiles_width_spin;
3037 current_window_pass_along [5] = tiles_height_spin;
3038 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3039 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3041 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0);
3042 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3043 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3044 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3046 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3048 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3050 gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
3052 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3054 gtk_widget_hide ( GTK_WIDGET(dialog) );
3056 gchar *fn = draw_image_filename ( vw, one_image_only );
3060 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3061 gdouble zoom = pow (2, active-2 );
3063 if ( one_image_only )
3064 save_image_file ( vw, fn,
3065 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3066 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3068 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3070 // NB is in UTM mode ATM
3071 save_image_dir ( vw, fn,
3072 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3073 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3075 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3076 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3077 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3082 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3086 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3088 draw_to_image_file ( vw, TRUE );
3091 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3093 draw_to_image_file ( vw, FALSE );
3096 #if GTK_CHECK_VERSION(2,10,0)
3097 static void print_cb ( GtkAction *a, VikWindow *vw )
3099 a_print(vw, vw->viking_vvp);
3103 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3104 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3106 VikViewportDrawMode drawmode;
3107 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3108 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3110 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3111 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3113 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3114 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3116 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3117 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3120 g_critical("Houston, we've had a problem.");
3124 if ( !vw->only_updating_coord_mode_ui )
3126 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3127 if ( olddrawmode != drawmode )
3129 /* this takes care of coord mode too */
3130 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3131 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3132 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3133 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3134 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3141 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3143 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3144 g_assert(check_box);
3145 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3146 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3150 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3152 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3153 g_assert(check_box);
3154 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3155 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3159 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3161 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3162 g_assert(check_box);
3163 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3164 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3168 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3170 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3171 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3172 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3173 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3174 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3176 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3177 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3181 gtk_widget_destroy ( colorsd );
3184 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3186 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3187 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3188 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3189 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3190 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3192 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color );
3193 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3197 gtk_widget_destroy ( colorsd );
3202 /***********************************************************************************************
3204 ***********************************************************************************************/
3206 static GtkActionEntry entries[] = {
3207 { "File", NULL, N_("_File"), 0, 0, 0 },
3208 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3209 { "View", NULL, N_("_View"), 0, 0, 0 },
3210 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3211 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3212 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3213 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3214 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3215 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3216 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3218 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3219 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3220 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3221 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3222 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3223 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3224 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3225 #ifdef VIK_CONFIG_GOOGLE
3226 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
3228 #ifdef VIK_CONFIG_OPENSTREETMAP
3229 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3230 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3232 #ifdef VIK_CONFIG_GEOCACHES
3233 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3235 #ifdef VIK_CONFIG_GEOTAG
3236 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3238 #ifdef VIK_CONFIG_GEONAMES
3239 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3241 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3242 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3243 { "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 },
3244 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3246 #if GTK_CHECK_VERSION(2,10,0)
3247 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3250 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3251 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3253 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3254 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3255 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3256 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3257 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3258 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3259 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3260 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3261 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3262 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3263 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3264 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3265 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3266 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3267 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3269 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3270 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3271 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3272 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3273 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3274 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3275 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3276 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3277 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3278 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3280 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3281 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3285 /* FIXME use VIEWPORT_DRAWMODE values */
3286 static GtkRadioActionEntry mode_entries[] = {
3287 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3288 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3289 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3290 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3293 static GtkToggleActionEntry toggle_entries[] = {
3294 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3295 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3296 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3297 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3298 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3299 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3300 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3301 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3304 #include "menu.xml.h"
3305 static void window_create_ui( VikWindow *window )
3308 GtkActionGroup *action_group;
3309 GtkAccelGroup *accel_group;
3312 GtkIconFactory *icon_factory;
3313 GtkIconSet *icon_set;
3314 GtkRadioActionEntry *tools = NULL, *radio;
3317 uim = gtk_ui_manager_new ();
3320 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3321 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3322 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3323 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3326 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3327 g_error_free (error);
3331 action_group = gtk_action_group_new ("MenuActions");
3332 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3333 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3334 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3335 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3337 icon_factory = gtk_icon_factory_new ();
3338 gtk_icon_factory_add_default (icon_factory);
3340 register_vik_icons(icon_factory);
3342 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3343 // so that it can be applied to the UI in one action group add function call below
3345 for (i=0; i<window->vt->n_tools; i++) {
3346 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3347 radio = &tools[ntools];
3349 *radio = window->vt->tools[i].ti.radioActionEntry;
3350 radio->value = ntools;
3353 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3354 GtkActionEntry action;
3355 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3356 vik_layer_get_interface(i)->name,
3357 vik_layer_get_interface(i)->name,
3358 GTK_UI_MANAGER_MENUITEM, FALSE);
3360 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3361 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3362 gtk_icon_set_unref (icon_set);
3364 action.name = vik_layer_get_interface(i)->name;
3365 action.stock_id = vik_layer_get_interface(i)->name;
3366 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3367 action.accelerator = vik_layer_get_interface(i)->accelerator;
3368 action.tooltip = NULL;
3369 action.callback = (GCallback)menu_addlayer_cb;
3370 gtk_action_group_add_actions(action_group, &action, 1, window);
3372 if ( vik_layer_get_interface(i)->tools_count ) {
3373 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3374 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3377 // Further tool copying for to apply to the UI, also apply menu UI setup
3378 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3379 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3380 radio = &tools[ntools];
3383 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3384 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3385 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3386 GTK_UI_MANAGER_MENUITEM, FALSE);
3387 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3388 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3389 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3390 GTK_UI_MANAGER_TOOLITEM, FALSE);
3392 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3394 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3395 // Overwrite with actual number to use
3396 radio->value = ntools;
3399 GtkActionEntry action_dl;
3400 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
3401 vik_layer_get_interface(i)->name,
3402 g_strdup_printf("Layer%s", vik_layer_get_interface(i)->fixed_layer_name),
3403 GTK_UI_MANAGER_MENUITEM, FALSE);
3405 // For default layers use action names of the form 'Layer<LayerName>'
3406 // This is to avoid clashing with just the layer name used above for the tool actions
3407 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
3408 action_dl.stock_id = NULL;
3409 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
3410 action_dl.accelerator = NULL;
3411 action_dl.tooltip = NULL;
3412 action_dl.callback = (GCallback)layer_defaults_cb;
3413 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
3415 g_object_unref (icon_factory);
3417 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3420 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3422 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3423 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3424 GtkAction *action = gtk_action_group_get_action(action_group,
3425 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3426 g_object_set(action, "sensitive", FALSE, NULL);
3430 // This is done last so we don't need to track the value of mid anymore
3431 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
3433 window->action_group = action_group;
3435 accel_group = gtk_ui_manager_get_accel_group (uim);
3436 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3437 gtk_ui_manager_ensure_update (uim);
3439 setup_recent_files(window);
3443 // TODO - add method to add tool icons defined from outside this file
3444 // and remove the reverse dependency on icon definition from this file
3446 const GdkPixdata *data;
3449 { &mover_22_pixbuf, "vik-icon-pan" },
3450 { &zoom_18_pixbuf, "vik-icon-zoom" },
3451 { &ruler_18_pixbuf, "vik-icon-ruler" },
3452 { &select_18_pixbuf, "vik-icon-select" },
3453 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3454 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3455 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3456 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3457 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3458 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3459 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3460 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3461 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3462 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3463 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3466 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3469 register_vik_icons (GtkIconFactory *icon_factory)
3471 GtkIconSet *icon_set;
3474 for (i = 0; i < n_stock_icons; i++) {
3475 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3476 stock_icons[i].data, FALSE, NULL ));
3477 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3478 gtk_icon_set_unref (icon_set);
3482 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3484 return vw->selected_vtl;
3487 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3489 vw->selected_vtl = vtl;
3490 vw->containing_vtl = vtl;
3492 vw->selected_track = NULL;
3493 vw->selected_tracks = NULL;
3494 vw->selected_waypoint = NULL;
3495 vw->selected_waypoints = NULL;
3496 // Set highlight thickness
3497 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3500 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
3502 return vw->selected_tracks;
3505 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3507 vw->selected_tracks = ght;
3508 vw->containing_vtl = vtl;
3510 vw->selected_vtl = NULL;
3511 vw->selected_track = NULL;
3512 vw->selected_waypoint = NULL;
3513 vw->selected_waypoints = NULL;
3514 // Set highlight thickness
3515 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3518 gpointer vik_window_get_selected_track ( VikWindow *vw )
3520 return vw->selected_track;
3523 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
3525 vw->selected_track = vt;
3526 vw->containing_vtl = vtl;
3528 vw->selected_vtl = NULL;
3529 vw->selected_tracks = NULL;
3530 vw->selected_waypoint = NULL;
3531 vw->selected_waypoints = NULL;
3532 // Set highlight thickness
3533 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3536 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
3538 return vw->selected_waypoints;
3541 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3543 vw->selected_waypoints = ght;
3544 vw->containing_vtl = vtl;
3546 vw->selected_vtl = NULL;
3547 vw->selected_track = NULL;
3548 vw->selected_tracks = NULL;
3549 vw->selected_waypoint = NULL;
3552 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3554 return vw->selected_waypoint;
3557 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
3559 vw->selected_waypoint = vwp;
3560 vw->containing_vtl = vtl;
3562 vw->selected_vtl = NULL;
3563 vw->selected_track = NULL;
3564 vw->selected_tracks = NULL;
3565 vw->selected_waypoints = NULL;
3568 gboolean vik_window_clear_highlight ( VikWindow *vw )
3570 gboolean need_redraw = FALSE;
3571 if ( vw->selected_vtl != NULL ) {
3572 vw->selected_vtl = NULL;
3575 if ( vw->selected_track != NULL ) {
3576 vw->selected_track = NULL;
3579 if ( vw->selected_tracks != NULL ) {
3580 vw->selected_tracks = NULL;
3583 if ( vw->selected_waypoint != NULL ) {
3584 vw->selected_waypoint = NULL;
3587 if ( vw->selected_waypoints != NULL ) {
3588 vw->selected_waypoints = NULL;
3594 GThread *vik_window_get_thread ( VikWindow *vw )