]> git.street.me.uk Git - andy/viking.git/blame_incremental - src/vikwindow.c
Fix crash if a route requested by the route finder is empty.
[andy/viking.git] / src / vikwindow.c
... / ...
CommitLineData
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2006, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2012-2015, Rob Norris <rw_norris@hotmail.com>
7 *
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.
12 *
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.
17 *
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
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "viking.h"
29#include "background.h"
30#include "acquire.h"
31#include "datasources.h"
32#include "geojson.h"
33#include "vikgoto.h"
34#include "dems.h"
35#include "mapcache.h"
36#include "print.h"
37#include "preferences.h"
38#include "toolbar.h"
39#include "viklayer_defaults.h"
40#include "icons/icons.h"
41#include "vikexttools.h"
42#include "vikexttool_datasources.h"
43#include "garminsymbols.h"
44#include "vikmapslayer.h"
45#include "geonamessearch.h"
46#include "vikutils.h"
47#include "dir.h"
48#include "kmz.h"
49
50#ifdef HAVE_STDLIB_H
51#include <stdlib.h>
52#endif
53#ifdef HAVE_MATH_H
54#include <math.h>
55#endif
56#ifdef HAVE_STRING_H
57#include <string.h>
58#endif
59#include <ctype.h>
60#include <glib.h>
61#include <glib/gstdio.h>
62#include <glib/gprintf.h>
63#include <glib/gi18n.h>
64#include <gio/gio.h>
65#include <gdk/gdkkeysyms.h>
66
67// This seems rather arbitary, quite large and pointless
68// I mean, if you have a thousand windows open;
69// why not be allowed to open a thousand more...
70#define MAX_WINDOWS 1024
71static guint window_count = 0;
72static GSList *window_list = NULL;
73
74#define VIKING_WINDOW_WIDTH 1000
75#define VIKING_WINDOW_HEIGHT 800
76#define DRAW_IMAGE_DEFAULT_WIDTH 1280
77#define DRAW_IMAGE_DEFAULT_HEIGHT 1024
78#define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
79
80// The last used directories
81static gchar *last_folder_files_uri = NULL;
82static gchar *last_folder_images_uri = NULL;
83
84static void window_finalize ( GObject *gob );
85static GObjectClass *parent_class;
86
87static void window_set_filename ( VikWindow *vw, const gchar *filename );
88static const gchar *window_get_filename ( VikWindow *vw );
89
90static VikWindow *window_new ();
91
92static void draw_update ( VikWindow *vw );
93
94static void newwindow_cb ( GtkAction *a, VikWindow *vw );
95
96// Signals
97static void open_window ( VikWindow *vw, GSList *files );
98static void destroy_window ( GtkWidget *widget,
99 gpointer data );
100
101/* Drawing & stuff */
102
103static gboolean delete_event( VikWindow *vw );
104
105static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
106
107static void center_changed_cb ( VikWindow *vw );
108static void window_configure_event ( VikWindow *vw );
109static void draw_sync ( VikWindow *vw );
110static void draw_redraw ( VikWindow *vw );
111static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
112static void draw_click ( VikWindow *vw, GdkEventButton *event );
113static void draw_release ( VikWindow *vw, GdkEventButton *event );
114static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
115static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
116static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
117static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
118
119static void draw_status ( VikWindow *vw );
120
121/* End Drawing Functions */
122
123static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
124static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
125static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
126
127static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
128static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
129static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
130
131/* tool management */
132typedef struct {
133 VikToolInterface ti;
134 gpointer state;
135 gint layer_type;
136} toolbox_tool_t;
137#define TOOL_LAYER_TYPE_NONE -1
138
139typedef struct {
140 int active_tool;
141 int n_tools;
142 toolbox_tool_t *tools;
143 VikWindow *vw;
144} toolbox_tools_t;
145
146static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
147static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
148static toolbox_tools_t* toolbox_create(VikWindow *vw);
149static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
150static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
151static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
152static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
153static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
154static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
155static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
156
157
158/* ui creation */
159static void window_create_ui( VikWindow *window );
160static void register_vik_icons (GtkIconFactory *icon_factory);
161
162/* i/o */
163static void load_file ( GtkAction *a, VikWindow *vw );
164static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
165static gboolean save_file ( GtkAction *a, VikWindow *vw );
166static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
167static gboolean window_save ( VikWindow *vw );
168
169struct _VikWindow {
170 GtkWindow gtkwindow;
171 GtkWidget *hpaned;
172 VikViewport *viking_vvp;
173 VikLayersPanel *viking_vlp;
174 VikStatusbar *viking_vs;
175 VikToolbar *viking_vtb;
176
177 GtkWidget *main_vbox;
178 GtkWidget *menu_hbox;
179
180 GdkCursor *busy_cursor;
181 GdkCursor *viewport_cursor; // only a reference
182
183 /* tool management state */
184 guint current_tool;
185 toolbox_tools_t *vt;
186 guint16 tool_layer_id;
187 guint16 tool_tool_id;
188
189 GtkActionGroup *action_group;
190
191 // Display controls
192 // NB scale, centermark and highlight are in viewport.
193 gboolean show_full_screen;
194 gboolean show_side_panel;
195 gboolean show_statusbar;
196 gboolean show_toolbar;
197 gboolean show_main_menu;
198
199 gboolean select_move;
200 gboolean pan_move;
201 gint pan_x, pan_y;
202 gint delayed_pan_x, delayed_pan_y; // Temporary storage
203 gboolean single_click_pending;
204
205 guint draw_image_width, draw_image_height;
206 gboolean draw_image_save_as_png;
207
208 gchar *filename;
209 gboolean modified;
210 VikLoadType_t loaded_type;
211
212 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
213 GtkUIManager *uim;
214
215 GThread *thread;
216 /* half-drawn update */
217 VikLayer *trigger;
218 VikCoord trigger_center;
219
220 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
221 /* Only one of these items can be selected at the same time */
222 gpointer selected_vtl; /* notionally VikTrwLayer */
223 GHashTable *selected_tracks;
224 gpointer selected_track; /* notionally VikTrack */
225 GHashTable *selected_waypoints;
226 gpointer selected_waypoint; /* notionally VikWaypoint */
227 /* only use for individual track or waypoint */
228 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
229 gpointer containing_vtl; /* notionally VikTrwLayer */
230};
231
232enum {
233 TOOL_PAN = 0,
234 TOOL_ZOOM,
235 TOOL_RULER,
236 TOOL_SELECT,
237 TOOL_LAYER,
238 NUMBER_OF_TOOLS
239};
240
241enum {
242 VW_NEWWINDOW_SIGNAL,
243 VW_OPENWINDOW_SIGNAL,
244 VW_LAST_SIGNAL
245};
246
247static guint window_signals[VW_LAST_SIGNAL] = { 0 };
248
249// TODO get rid of this as this is unnecessary duplication...
250static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
251
252G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
253
254VikViewport * vik_window_viewport(VikWindow *vw)
255{
256 return(vw->viking_vvp);
257}
258
259VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
260{
261 return(vw->viking_vlp);
262}
263
264/**
265 * Returns the statusbar for the window
266 */
267VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
268{
269 return vw->viking_vs;
270}
271
272/**
273 * Returns the 'project' filename
274 */
275const gchar *vik_window_get_filename (VikWindow *vw)
276{
277 return vw->filename;
278}
279
280typedef struct {
281 VikStatusbar *vs;
282 vik_statusbar_type_t vs_type;
283 gchar* message; // Always make a copy of this data
284} statusbar_idle_data;
285
286/**
287 * For the actual statusbar update!
288 */
289static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
290{
291 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
292 g_free ( sid->message );
293 g_free ( sid );
294 return FALSE;
295}
296
297/**
298 * vik_window_statusbar_update:
299 * @vw: The main window in which the statusbar will be updated.
300 * @message: The string to be displayed. This is copied.
301 * @vs_type: The part of the statusbar to be updated.
302 *
303 * This updates any part of the statusbar with the new string.
304 * It handles calling from the main thread or any background thread
305 * ATM this mostly used from background threads - as from the main thread
306 * one may use the vik_statusbar_set_message() directly.
307 */
308void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
309{
310 GThread *thread = vik_window_get_thread ( vw );
311 if ( !thread )
312 // Do nothing
313 return;
314
315 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
316 sid->vs = vw->viking_vs;
317 sid->vs_type = vs_type;
318 sid->message = g_strdup ( message );
319
320 if ( g_thread_self() == thread ) {
321 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
322 }
323 else {
324 // From a background thread
325 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
326 }
327}
328
329// Actual signal handlers
330static void destroy_window ( GtkWidget *widget,
331 gpointer data )
332{
333 if ( ! --window_count ) {
334 g_free ( last_folder_files_uri );
335 g_free ( last_folder_images_uri );
336 gtk_main_quit ();
337 }
338}
339
340#define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
341#define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
342#define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
343// Menubar setting to off is never auto saved in case it's accidentally turned off
344// It's not so obvious so to recover the menu visibility.
345// Thus this value is for setting manually via editting the settings file directly
346#define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
347
348VikWindow *vik_window_new_window ()
349{
350 if ( window_count < MAX_WINDOWS )
351 {
352 VikWindow *vw = window_new ();
353
354 g_signal_connect (G_OBJECT (vw), "destroy",
355 G_CALLBACK (destroy_window), NULL);
356 g_signal_connect (G_OBJECT (vw), "newwindow",
357 G_CALLBACK (vik_window_new_window), NULL);
358 g_signal_connect (G_OBJECT (vw), "openwindow",
359 G_CALLBACK (open_window), NULL);
360
361 gtk_widget_show_all ( GTK_WIDGET(vw) );
362
363 if ( a_vik_get_restore_window_state() ) {
364 // These settings are applied after the show all as these options hide widgets
365 gboolean sidepanel;
366 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
367 if ( ! sidepanel ) {
368 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
369 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
370 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
371 }
372
373 gboolean statusbar;
374 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
375 if ( ! statusbar ) {
376 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
377 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
378 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
379 }
380
381 gboolean toolbar;
382 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
383 if ( ! toolbar ) {
384 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
385 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
386 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
387 }
388
389 gboolean menubar;
390 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
391 if ( ! menubar ) {
392 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
393 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
394 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
395 }
396 }
397 window_count++;
398
399 return vw;
400 }
401 return NULL;
402}
403
404/**
405 * determine_location_thread:
406 * @vw: The window that will get updated
407 * @threaddata: Data used by our background thread mechanism
408 *
409 * Use the features in vikgoto to determine where we are
410 * Then set up the viewport:
411 * 1. To goto the location
412 * 2. Set an appropriate level zoom for the location type
413 * 3. Some statusbar message feedback
414 */
415static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
416{
417 struct LatLon ll;
418 gchar *name = NULL;
419 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
420
421 int result = a_background_thread_progress ( threaddata, 1.0 );
422 if ( result != 0 ) {
423 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
424 return -1; /* Abort thread */
425 }
426
427 if ( ans ) {
428 // Zoom out a little
429 gdouble zoom = 16.0;
430
431 if ( ans == 2 ) {
432 // Position found with city precision - so zoom out more
433 zoom = 128.0;
434 }
435 else if ( ans == 3 ) {
436 // Position found via country name search - so zoom wayyyy out
437 zoom = 2048.0;
438 }
439
440 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
441 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
442
443 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
444 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
445 g_free ( name );
446 g_free ( message );
447
448 // Signal to redraw from the background
449 vik_layers_panel_emit_update ( vw->viking_vlp );
450 }
451 else
452 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
453
454 return 0;
455}
456
457/**
458 * Steps to be taken once initial loading has completed
459 */
460void vik_window_new_window_finish ( VikWindow *vw )
461{
462 // Don't add a map if we've loaded a Viking file already
463 if ( vw->filename )
464 return;
465
466 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
467 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
468 if ( vw->filename )
469 return;
470 }
471
472 // Maybe add a default map layer
473 if ( a_vik_get_add_default_map_layer () ) {
474 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
475 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
476 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
477
478 draw_update ( vw );
479 }
480
481 // If not loaded any file, maybe try the location lookup
482 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
483 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
484
485 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
486
487 a_background_thread ( BACKGROUND_POOL_REMOTE,
488 GTK_WINDOW(vw),
489 _("Determining location"),
490 (vik_thr_func) determine_location_thread,
491 vw,
492 NULL,
493 NULL,
494 1 );
495 }
496 }
497}
498
499static void open_window ( VikWindow *vw, GSList *files )
500{
501 if ( !vw )
502 return;
503 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
504 GSList *cur_file = files;
505 while ( cur_file ) {
506 // Only open a new window if a viking file
507 gchar *file_name = cur_file->data;
508 if (vw->filename && check_file_magic_vik ( file_name ) ) {
509 VikWindow *newvw = vik_window_new_window ();
510 if (newvw)
511 vik_window_open_file ( newvw, file_name, TRUE );
512 }
513 else {
514 vik_window_open_file ( vw, file_name, change_fn );
515 }
516 g_free (file_name);
517 cur_file = g_slist_next (cur_file);
518 }
519 g_slist_free (files);
520}
521// End signals
522
523void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
524{
525 int i, j, tool_count;
526 VikLayerInterface *layer_interface;
527
528 if (!vw->action_group) return;
529
530 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
531 GtkAction *action;
532 layer_interface = vik_layer_get_interface(i);
533 tool_count = layer_interface->tools_count;
534
535 for (j = 0; j < tool_count; j++) {
536 action = gtk_action_group_get_action(vw->action_group,
537 layer_interface->tools[j].radioActionEntry.name);
538 g_object_set(action, "sensitive", i == vl->type, NULL);
539 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
540 }
541 }
542}
543
544static void window_finalize ( GObject *gob )
545{
546 VikWindow *vw = VIK_WINDOW(gob);
547 g_return_if_fail ( vw != NULL );
548
549 a_background_remove_window ( vw );
550
551 window_list = g_slist_remove ( window_list, vw );
552
553 gdk_cursor_unref ( vw->busy_cursor );
554 int tt;
555 for (tt = 0; tt < vw->vt->n_tools; tt++ )
556 if ( vw->vt->tools[tt].ti.destroy )
557 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
558 g_free ( vw->vt->tools );
559 g_free ( vw->vt );
560
561 vik_toolbar_finalize ( vw->viking_vtb );
562
563 G_OBJECT_CLASS(parent_class)->finalize(gob);
564}
565
566
567static void vik_window_class_init ( VikWindowClass *klass )
568{
569 /* destructor */
570 GObjectClass *object_class;
571
572 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);
573 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);
574
575 object_class = G_OBJECT_CLASS (klass);
576
577 object_class->finalize = window_finalize;
578
579 parent_class = g_type_class_peek_parent (klass);
580
581}
582
583static void zoom_changed (GtkMenuShell *menushell,
584 gpointer user_data)
585{
586 VikWindow *vw = VIK_WINDOW (user_data);
587
588 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
589 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
590
591 gdouble zoom_request = pow (2, active-5 );
592
593 // But has it really changed?
594 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
595 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
596 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
597 // Force drawing update
598 draw_update ( vw );
599 }
600}
601
602/**
603 * @mpp: The initial zoom level
604 */
605static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
606{
607 GtkWidget *menu = gtk_menu_new ();
608 char *itemLabels[] = { "0.031", "0.063", "0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
609
610 int i;
611 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
612 {
613 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
614 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
615 gtk_widget_show (item);
616 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
617 }
618
619 gint active = 5 + round ( log (mpp) / log (2) );
620 // Ensure value derived from mpp is in bounds of the menu
621 if ( active >= G_N_ELEMENTS(itemLabels) )
622 active = G_N_ELEMENTS(itemLabels) - 1;
623 if ( active < 0 )
624 active = 0;
625 gtk_menu_set_active ( GTK_MENU(menu), active );
626
627 return menu;
628}
629
630static GtkWidget *create_zoom_combo_all_levels ()
631{
632 GtkWidget *combo = vik_combo_box_text_new();
633 vik_combo_box_text_append ( combo, "0.25");
634 vik_combo_box_text_append ( combo, "0.5");
635 vik_combo_box_text_append ( combo, "1");
636 vik_combo_box_text_append ( combo, "2");
637 vik_combo_box_text_append ( combo, "4");
638 vik_combo_box_text_append ( combo, "8");
639 vik_combo_box_text_append ( combo, "16");
640 vik_combo_box_text_append ( combo, "32");
641 vik_combo_box_text_append ( combo, "64");
642 vik_combo_box_text_append ( combo, "128");
643 vik_combo_box_text_append ( combo, "256");
644 vik_combo_box_text_append ( combo, "512");
645 vik_combo_box_text_append ( combo, "1024");
646 vik_combo_box_text_append ( combo, "2048");
647 vik_combo_box_text_append ( combo, "4096");
648 vik_combo_box_text_append ( combo, "8192");
649 vik_combo_box_text_append ( combo, "16384");
650 vik_combo_box_text_append ( combo, "32768");
651 /* Create tooltip */
652 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
653 return combo;
654}
655
656static gint zoom_popup_handler (GtkWidget *widget)
657{
658 GtkMenu *menu;
659
660 g_return_val_if_fail (widget != NULL, FALSE);
661 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
662
663 /* The "widget" is the menu that was supplied when
664 * g_signal_connect_swapped() was called.
665 */
666 menu = GTK_MENU (widget);
667
668 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
669 1, gtk_get_current_event_time());
670 return TRUE;
671}
672
673enum {
674 TARGET_URIS,
675};
676
677static void drag_data_received_cb ( GtkWidget *widget,
678 GdkDragContext *context,
679 gint x,
680 gint y,
681 GtkSelectionData *selection_data,
682 guint target_type,
683 guint time,
684 gpointer data )
685{
686 gboolean success = FALSE;
687
688 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
689 switch (target_type) {
690 case TARGET_URIS: {
691 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
692 g_debug ("drag received string:%s \n", str);
693
694 // Convert string into GSList of individual entries for use with our open signal
695 gchar **entries = g_strsplit(str, "\r\n", 0);
696 GSList *filenames = NULL;
697 gint entry_runner = 0;
698 gchar *entry = entries[entry_runner];
699 while (entry) {
700 if ( g_strcmp0 ( entry, "" ) ) {
701 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
702 // thus need to convert the text into a plain string
703 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
704 if ( filename )
705 filenames = g_slist_append ( filenames, filename );
706 }
707 entry_runner++;
708 entry = entries[entry_runner];
709 }
710
711 if ( filenames )
712 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
713 // NB: GSList & contents are freed by main.open_window
714
715 success = TRUE;
716 break;
717 }
718 default: break;
719 }
720 }
721
722 gtk_drag_finish ( context, success, FALSE, time );
723}
724
725static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
726{
727 VikWindow *vw = (VikWindow*)gp;
728 GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
729 if ( action )
730 gtk_action_activate ( action );
731}
732
733static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
734{
735 VikWindow *vw = (VikWindow*)gp;
736 center_changed_cb ( vw );
737}
738
739#define VIK_SETTINGS_WIN_MAX "window_maximized"
740#define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
741#define VIK_SETTINGS_WIN_WIDTH "window_width"
742#define VIK_SETTINGS_WIN_HEIGHT "window_height"
743#define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
744#define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
745#define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
746#define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
747#define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
748
749#define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
750
751static void vik_window_init ( VikWindow *vw )
752{
753 vw->action_group = NULL;
754
755 vw->viking_vvp = vik_viewport_new();
756 vw->viking_vlp = vik_layers_panel_new();
757 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
758 vw->viking_vs = vik_statusbar_new();
759
760 vw->vt = toolbox_create(vw);
761 vw->viking_vtb = vik_toolbar_new ();
762 window_create_ui(vw);
763 window_set_filename (vw, NULL);
764
765 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
766
767 vw->filename = NULL;
768 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
769 vw->modified = FALSE;
770 vw->only_updating_coord_mode_ui = FALSE;
771
772 vw->select_move = FALSE;
773 vw->pan_move = FALSE;
774 vw->pan_x = vw->pan_y = -1;
775 vw->single_click_pending = FALSE;
776
777 gint draw_image_width;
778 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
779 vw->draw_image_width = draw_image_width;
780 else
781 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
782 gint draw_image_height;
783 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
784 vw->draw_image_height = draw_image_height;
785 else
786 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
787 gboolean draw_image_save_as_png;
788 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
789 vw->draw_image_save_as_png = draw_image_save_as_png;
790 else
791 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
792
793 vw->main_vbox = gtk_vbox_new(FALSE, 1);
794 gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
795 vw->menu_hbox = gtk_hbox_new(FALSE, 1);
796 GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
797 gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
798 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
799
800 toolbar_init(vw->viking_vtb,
801 &vw->gtkwindow,
802 vw->main_vbox,
803 vw->menu_hbox,
804 toolbar_tool_cb,
805 toolbar_reload_cb,
806 (gpointer)vw); // This auto packs toolbar into the vbox
807 // Must be performed post toolbar init
808 gint i,j;
809 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
810 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
811 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
812 }
813 }
814
815 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
816
817 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
818 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
819 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
820 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
821 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
822
823 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
824
825 // Own signals
826 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
827 // Signals from GTK
828 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
829 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
830 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 );
831 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
832 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
833 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
834 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
835
836 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
837 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
838
839 // Allow key presses to be processed anywhere
840 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
841
842 // Set initial button sensitivity
843 center_changed_cb ( vw );
844
845 vw->hpaned = gtk_hpaned_new ();
846 gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, TRUE );
847 gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
848
849 /* This packs the button into the window (a gtk container). */
850 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
851
852 gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
853
854 a_background_add_window ( vw );
855
856 window_list = g_slist_prepend ( window_list, vw);
857
858 gint height = VIKING_WINDOW_HEIGHT;
859 gint width = VIKING_WINDOW_WIDTH;
860
861 if ( a_vik_get_restore_window_state() ) {
862 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
863 // Enforce a basic minimum size
864 if ( height < 160 )
865 height = 160;
866 }
867 else
868 // No setting - so use default
869 height = VIKING_WINDOW_HEIGHT;
870
871 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
872 // Enforce a basic minimum size
873 if ( width < 320 )
874 width = 320;
875 }
876 else
877 // No setting - so use default
878 width = VIKING_WINDOW_WIDTH;
879
880 gboolean maxed;
881 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
882 if ( maxed )
883 gtk_window_maximize ( GTK_WINDOW(vw) );
884
885 gboolean full;
886 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
887 if ( full ) {
888 vw->show_full_screen = TRUE;
889 gtk_window_fullscreen ( GTK_WINDOW(vw) );
890 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
891 if ( check_box )
892 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
893 }
894 }
895
896 gint position = -1; // Let GTK determine default positioning
897 if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
898 position = -1;
899 }
900 gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
901 }
902
903 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
904
905 vw->show_side_panel = TRUE;
906 vw->show_statusbar = TRUE;
907 vw->show_toolbar = TRUE;
908 vw->show_main_menu = TRUE;
909
910 // Only accept Drag and Drop of files onto the viewport
911 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
912 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
913 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
914
915 // Store the thread value so comparisons can be made to determine the gdk update method
916 // Hopefully we are storing the main thread value here :)
917 // [ATM any window initialization is always be performed by the main thread]
918 vw->thread = g_thread_self();
919
920 // Set the default tool + mode
921 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
922 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
923
924 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
925 gtk_accel_map_load ( accel_file_name );
926 g_free ( accel_file_name );
927}
928
929static VikWindow *window_new ()
930{
931 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
932}
933
934/**
935 * Update the displayed map
936 * Only update the top most visible map layer
937 * ATM this assumes (as per defaults) the top most map has full alpha setting
938 * such that other other maps even though they may be active will not be seen
939 * It's more complicated to work out which maps are actually visible due to alpha settings
940 * and overkill for this simple refresh method.
941 */
942static void simple_map_update ( VikWindow *vw, gboolean only_new )
943{
944 // Find the most relevent single map layer to operate on
945 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
946 if ( vl )
947 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
948}
949
950/**
951 * This is the global key press handler
952 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
953 */
954static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
955{
956 // The keys handled here are not in the menuing system for a couple of reasons:
957 // . Keeps the menu size compact (alebit at expense of discoverably)
958 // . Allows differing key bindings to perform the same actions
959
960 // First decide if key events are related to the maps layer
961 gboolean map_download = FALSE;
962 gboolean map_download_only_new = TRUE; // Only new or reload
963
964 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
965
966 // Standard 'Refresh' keys: F5 or Ctrl+r
967 // Note 'F5' is actually handled via draw_refresh_cb() later on
968 // (not 'R' it's 'r' notice the case difference!!)
969 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
970 map_download = TRUE;
971 map_download_only_new = TRUE;
972 }
973 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
974 // Note the use of uppercase R here since shift key has been pressed
975 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
976 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
977 map_download = TRUE;
978 map_download_only_new = FALSE;
979 }
980 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
981 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
982 vik_viewport_zoom_in ( vw->viking_vvp );
983 draw_update(vw);
984 return TRUE; // handled keypress
985 }
986 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
987 vik_viewport_zoom_out ( vw->viking_vvp );
988 draw_update(vw);
989 return TRUE; // handled keypress
990 }
991
992 if ( map_download ) {
993 simple_map_update ( vw, map_download_only_new );
994 return TRUE; // handled keypress
995 }
996
997 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
998 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
999 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
1000 if ( vl && ltype == vl->type )
1001 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
1002 }
1003
1004 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
1005 if ( vw->current_tool < TOOL_LAYER ) {
1006 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
1007 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1008 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1009 }
1010 }
1011
1012 /* Restore Main Menu via Escape key if the user has hidden it */
1013 /* This key is more likely to be used as they may not remember the function key */
1014 if ( event->keyval == GDK_Escape ) {
1015 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1016 if ( check_box ) {
1017 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1018 if ( !state ) {
1019 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1020 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1021 return TRUE; /* handled keypress */
1022 }
1023 }
1024 }
1025
1026 return FALSE; /* don't handle the keypress */
1027}
1028
1029static gboolean delete_event( VikWindow *vw )
1030{
1031#ifdef VIKING_PROMPT_IF_MODIFIED
1032 if ( vw->modified )
1033#else
1034 if (0)
1035#endif
1036 {
1037 GtkDialog *dia;
1038 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1039 _("Do you want to save the changes you made to the document \"%s\"?\n"
1040 "\n"
1041 "Your changes will be lost if you don't save them."),
1042 window_get_filename ( vw ) ) );
1043 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1044 switch ( gtk_dialog_run ( dia ) )
1045 {
1046 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1047 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1048 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1049 }
1050 }
1051
1052 if ( window_count == 1 ) {
1053 // On the final window close - save latest state - if it's wanted...
1054 if ( a_vik_get_restore_window_state() ) {
1055 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1056 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1057 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1058
1059 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1060 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1061
1062 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1063
1064 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1065
1066 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1067
1068 // If supersized - no need to save the enlarged width+height values
1069 if ( ! (state_fullscreen || state_max) ) {
1070 gint width, height;
1071 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1072 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1073 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1074 }
1075
1076 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1077 }
1078
1079 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1080 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1081 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1082
1083 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1084 gtk_accel_map_save ( accel_file_name );
1085 g_free ( accel_file_name );
1086 }
1087
1088 return FALSE;
1089}
1090
1091/* Drawing stuff */
1092static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1093{
1094 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1095}
1096
1097static void draw_update ( VikWindow *vw )
1098{
1099 draw_redraw (vw);
1100 draw_sync (vw);
1101}
1102
1103static void draw_sync ( VikWindow *vw )
1104{
1105 vik_viewport_sync(vw->viking_vvp);
1106 draw_status ( vw );
1107}
1108
1109/*
1110 * Split the status update, as sometimes only need to update the tool part
1111 * also on initialization the zoom related stuff is not ready to be used
1112 */
1113static void draw_status_tool ( VikWindow *vw )
1114{
1115 if ( vw->current_tool == TOOL_LAYER )
1116 // Use tooltip rather than the internal name as the tooltip is i8n
1117 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 );
1118 else
1119 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1120}
1121
1122static void draw_status ( VikWindow *vw )
1123{
1124 static gchar zoom_level[22];
1125 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1126 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1127 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1128 if (xmpp != ympp)
1129 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1130 else
1131 if ( (int)xmpp - xmpp < 0.0 )
1132 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1133 else
1134 /* xmpp should be a whole number so don't show useless .000 bit */
1135 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1136
1137 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1138
1139 draw_status_tool ( vw );
1140}
1141
1142void vik_window_set_redraw_trigger(VikLayer *vl)
1143{
1144 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1145 if (NULL != vw)
1146 vw->trigger = vl;
1147}
1148
1149static void window_configure_event ( VikWindow *vw )
1150{
1151 static int first = 1;
1152 draw_redraw ( vw );
1153 if (first) {
1154 // This is a hack to set the cursor corresponding to the first tool
1155 // FIXME find the correct way to initialize both tool and its cursor
1156 first = 0;
1157 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1158 /* We set cursor, even if it is NULL: it resets to default */
1159 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1160 }
1161}
1162
1163static void draw_redraw ( VikWindow *vw )
1164{
1165 VikCoord old_center = vw->trigger_center;
1166 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1167 VikLayer *new_trigger = vw->trigger;
1168 vw->trigger = NULL;
1169 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1170
1171 if ( ! new_trigger )
1172 ; /* do nothing -- have to redraw everything. */
1173 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1174 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1175 else
1176 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1177
1178 /* actually draw */
1179 vik_viewport_clear ( vw->viking_vvp);
1180 // Main layer drawing
1181 vik_layers_panel_draw_all ( vw->viking_vlp );
1182 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1183 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1184 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1185 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1186 }
1187 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1188 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1189 }
1190 else if ( vw->selected_vtl ) {
1191 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1192 }
1193 }
1194 // Other viewport decoration items on top if they are enabled/in use
1195 vik_viewport_draw_scale ( vw->viking_vvp );
1196 vik_viewport_draw_copyright ( vw->viking_vvp );
1197 vik_viewport_draw_centermark ( vw->viking_vvp );
1198 vik_viewport_draw_logo ( vw->viking_vvp );
1199
1200 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1201}
1202
1203gboolean draw_buf_done = TRUE;
1204
1205static gboolean draw_buf(gpointer data)
1206{
1207 gpointer *pass_along = data;
1208 gdk_threads_enter();
1209 gdk_draw_drawable (pass_along[0], pass_along[1],
1210 pass_along[2], 0, 0, 0, 0, -1, -1);
1211 draw_buf_done = TRUE;
1212 gdk_threads_leave();
1213 return FALSE;
1214}
1215
1216
1217/* Mouse event handlers ************************************************************************/
1218
1219static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1220{
1221 /* set panning origin */
1222 vw->pan_move = FALSE;
1223 vw->pan_x = (gint) event->x;
1224 vw->pan_y = (gint) event->y;
1225}
1226
1227static void draw_click (VikWindow *vw, GdkEventButton *event)
1228{
1229 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1230
1231 /* middle button pressed. we reserve all middle button and scroll events
1232 * for panning and zooming; tools only get left/right/movement
1233 */
1234 if ( event->button == 2) {
1235 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1236 // Tool still may need to do something (such as disable something)
1237 toolbox_click(vw->vt, event);
1238 vik_window_pan_click ( vw, event );
1239 }
1240 else {
1241 toolbox_click(vw->vt, event);
1242 }
1243}
1244
1245static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1246{
1247 if ( vw->pan_x != -1 ) {
1248 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1249 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1250 vw->pan_move = TRUE;
1251 vw->pan_x = event->x;
1252 vw->pan_y = event->y;
1253 draw_update ( vw );
1254 }
1255}
1256
1257/**
1258 * get_location_strings:
1259 *
1260 * Utility function to get positional strings for the given location
1261 * lat and lon strings will get allocated and so need to be freed after use
1262 */
1263static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1264{
1265 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1266 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1267 // ZONE[N|S] EASTING NORTHING
1268 *lat = g_malloc(4*sizeof(gchar));
1269 // NB zone is stored in a char but is an actual number
1270 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1271 *lon = g_malloc(16*sizeof(gchar));
1272 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1273 }
1274 else {
1275 struct LatLon ll;
1276 a_coords_utm_to_latlon ( &utm, &ll );
1277 a_coords_latlon_to_string ( &ll, lat, lon );
1278 }
1279}
1280
1281static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1282{
1283 static VikCoord coord;
1284 static struct UTM utm;
1285 #define BUFFER_SIZE 50
1286 static char pointer_buf[BUFFER_SIZE];
1287 gchar *lat = NULL, *lon = NULL;
1288 gint16 alt;
1289 gdouble zoom;
1290 VikDemInterpol interpol_method;
1291
1292 /* This is a hack, but work far the best, at least for single pointer systems.
1293 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1294 gint x, y;
1295 gdk_window_get_pointer (event->window, &x, &y, NULL);
1296 event->x = x;
1297 event->y = y;
1298
1299 toolbox_move(vw->vt, event);
1300
1301 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1302 vik_coord_to_utm ( &coord, &utm );
1303
1304 get_location_strings ( vw, utm, &lat, &lon );
1305
1306 /* Change interpolate method according to scale */
1307 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1308 if (zoom > 2.0)
1309 interpol_method = VIK_DEM_INTERPOL_NONE;
1310 else if (zoom >= 1.0)
1311 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1312 else
1313 interpol_method = VIK_DEM_INTERPOL_BEST;
1314 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1315 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1316 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1317 else
1318 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1319 }
1320 else
1321 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1322 g_free (lat);
1323 lat = NULL;
1324 g_free (lon);
1325 lon = NULL;
1326 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1327
1328 vik_window_pan_move ( vw, event );
1329
1330 /* This is recommended by the GTK+ documentation, but does not work properly.
1331 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1332 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1333 */
1334 /* gdk_event_request_motions ( event ); */
1335}
1336
1337/**
1338 * Action the single click after a small timeout
1339 * If a double click has occurred then this will do nothing
1340 */
1341static gboolean vik_window_pan_timeout (VikWindow *vw)
1342{
1343 if ( ! vw->single_click_pending ) {
1344 // Double click happened, so don't do anything
1345 return FALSE;
1346 }
1347
1348 /* set panning origin */
1349 vw->pan_move = FALSE;
1350 vw->single_click_pending = FALSE;
1351 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1352 draw_update ( vw );
1353
1354 // Really turn off the pan moving!!
1355 vw->pan_x = vw->pan_y = -1;
1356 return FALSE;
1357}
1358
1359static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1360{
1361 gboolean do_draw = TRUE;
1362
1363 if ( vw->pan_move == FALSE ) {
1364 vw->single_click_pending = !vw->single_click_pending;
1365
1366 if ( vw->single_click_pending ) {
1367 // Store offset to use
1368 vw->delayed_pan_x = vw->pan_x;
1369 vw->delayed_pan_y = vw->pan_y;
1370 // Get double click time
1371 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1372 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1373 g_value_init ( &dct, G_TYPE_INT );
1374 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1375 // Give chance for a double click to occur
1376 gint timer = g_value_get_int ( &dct ) + 50;
1377 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1378 do_draw = FALSE;
1379 }
1380 else {
1381 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1382 }
1383 }
1384 else {
1385 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1386 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1387 }
1388
1389 vw->pan_move = FALSE;
1390 vw->pan_x = vw->pan_y = -1;
1391 if ( do_draw )
1392 draw_update ( vw );
1393}
1394
1395static void draw_release ( VikWindow *vw, GdkEventButton *event )
1396{
1397 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1398
1399 if ( event->button == 2 ) { /* move / pan */
1400 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1401 // Tool still may need to do something (such as reenable something)
1402 toolbox_release(vw->vt, event);
1403 vik_window_pan_release ( vw, event );
1404 }
1405 else {
1406 toolbox_release(vw->vt, event);
1407 }
1408}
1409
1410static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1411{
1412 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1413 if ( modifiers == GDK_CONTROL_MASK ) {
1414 /* control == pan up & down */
1415 if ( event->direction == GDK_SCROLL_UP )
1416 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1417 else
1418 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 );
1419 } else if ( modifiers == GDK_SHIFT_MASK ) {
1420 /* shift == pan left & right */
1421 if ( event->direction == GDK_SCROLL_UP )
1422 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1423 else
1424 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 );
1425 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1426 // This zoom is on the center position
1427 if ( event->direction == GDK_SCROLL_UP )
1428 vik_viewport_zoom_in (vw->viking_vvp);
1429 else
1430 vik_viewport_zoom_out (vw->viking_vvp);
1431 } else {
1432 /* make sure mouse is still over the same point on the map when we zoom */
1433 VikCoord coord;
1434 gint x, y;
1435 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1436 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1437 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1438 if ( event->direction == GDK_SCROLL_UP )
1439 vik_viewport_zoom_in (vw->viking_vvp);
1440 else
1441 vik_viewport_zoom_out(vw->viking_vvp);
1442 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1443 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1444 center_y + (y - event->y) );
1445 }
1446
1447 draw_update(vw);
1448}
1449
1450
1451
1452/********************************************************************************
1453 ** Ruler tool code
1454 ********************************************************************************/
1455static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1456{
1457 PangoLayout *pl;
1458 gchar str[128];
1459 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1460 GdkGC *thickgc = gdk_gc_new(d);
1461
1462 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1463 gdouble dx = (x2-x1)/len*10;
1464 gdouble dy = (y2-y1)/len*10;
1465 gdouble c = cos(DEG2RAD(15.0));
1466 gdouble s = sin(DEG2RAD(15.0));
1467 gdouble angle;
1468 gdouble baseangle = 0;
1469 gint i;
1470
1471 /* draw line with arrow ends */
1472 {
1473 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1474 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1475 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1476 }
1477
1478 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1479 gdk_draw_line(d, gc, x1, y1, x2, y2);
1480
1481 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1482 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1483 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1484 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1485 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1486 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1487
1488 /* draw compass */
1489#define CR 80
1490#define CW 4
1491
1492 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1493
1494 {
1495 GdkColor color;
1496 gdk_gc_copy(thickgc, gc);
1497 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1498 gdk_color_parse("#2255cc", &color);
1499 gdk_gc_set_rgb_fg_color(thickgc, &color);
1500 }
1501 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);
1502
1503
1504 gdk_gc_copy(thickgc, gc);
1505 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1506 for (i=0; i<180; i++) {
1507 c = cos(DEG2RAD(i)*2 + baseangle);
1508 s = sin(DEG2RAD(i)*2 + baseangle);
1509
1510 if (i%5) {
1511 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1512 } else {
1513 gdouble ticksize = 2*CW;
1514 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1515 }
1516 }
1517
1518 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1519 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1520 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1521 c = (CR+CW*2)*cos(baseangle);
1522 s = (CR+CW*2)*sin(baseangle);
1523 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1524 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1525
1526 /* draw labels */
1527#define LABEL(x, y, w, h) { \
1528 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1529 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1530 gdk_draw_layout(d, gc, (x), (y), pl); }
1531 {
1532 gint wd, hd, xd, yd;
1533 gint wb, hb, xb, yb;
1534
1535 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1536 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1537 pango_layout_set_text(pl, "N", -1);
1538 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1539
1540 /* draw label with distance */
1541 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1542 switch (dist_units) {
1543 case VIK_UNITS_DISTANCE_KILOMETRES:
1544 if (distance >= 1000 && distance < 100000) {
1545 g_sprintf(str, "%3.2f km", distance/1000.0);
1546 } else if (distance < 1000) {
1547 g_sprintf(str, "%d m", (int)distance);
1548 } else {
1549 g_sprintf(str, "%d km", (int)distance/1000);
1550 }
1551 break;
1552 case VIK_UNITS_DISTANCE_MILES:
1553 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1554 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1555 } else if (distance < VIK_MILES_TO_METERS(1)) {
1556 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1557 } else {
1558 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1559 }
1560 break;
1561 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1562 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1563 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1564 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1565 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1566 } else {
1567 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1568 }
1569 break;
1570 default:
1571 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1572 }
1573
1574 pango_layout_set_text(pl, str, -1);
1575
1576 pango_layout_get_pixel_size ( pl, &wd, &hd );
1577 if (dy>0) {
1578 xd = (x1+x2)/2 + dy;
1579 yd = (y1+y2)/2 - hd/2 - dx;
1580 } else {
1581 xd = (x1+x2)/2 - dy;
1582 yd = (y1+y2)/2 - hd/2 + dx;
1583 }
1584
1585 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1586 xd = x2 + 10;
1587 yd = y2 - 5;
1588 }
1589
1590 LABEL(xd, yd, wd, hd);
1591
1592 /* draw label with bearing */
1593 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1594 pango_layout_set_text(pl, str, -1);
1595 pango_layout_get_pixel_size ( pl, &wb, &hb );
1596 xb = x1 + CR*cos(angle-M_PI_2);
1597 yb = y1 + CR*sin(angle-M_PI_2);
1598
1599 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1600 xb = x2 + 10;
1601 yb = y2 + 10;
1602 }
1603
1604 {
1605 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1606 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1607 xb = xd + wd + 5;
1608 }
1609 }
1610 LABEL(xb, yb, wb, hb);
1611 }
1612#undef LABEL
1613
1614 g_object_unref ( G_OBJECT ( pl ) );
1615 g_object_unref ( G_OBJECT ( labgc ) );
1616 g_object_unref ( G_OBJECT ( thickgc ) );
1617}
1618
1619typedef struct {
1620 VikWindow *vw;
1621 VikViewport *vvp;
1622 gboolean has_oldcoord;
1623 VikCoord oldcoord;
1624} ruler_tool_state_t;
1625
1626static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1627{
1628 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1629 s->vw = vw;
1630 s->vvp = vvp;
1631 s->has_oldcoord = FALSE;
1632 return s;
1633}
1634
1635static void ruler_destroy (ruler_tool_state_t *s)
1636{
1637 g_free(s);
1638}
1639
1640static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1641{
1642 struct LatLon ll;
1643 VikCoord coord;
1644 gchar *temp;
1645 if ( event->button == 1 ) {
1646 gchar *lat=NULL, *lon=NULL;
1647 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1648 vik_coord_to_latlon ( &coord, &ll );
1649 a_coords_latlon_to_string ( &ll, &lat, &lon );
1650 if ( s->has_oldcoord ) {
1651 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1652 switch (dist_units) {
1653 case VIK_UNITS_DISTANCE_KILOMETRES:
1654 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1655 break;
1656 case VIK_UNITS_DISTANCE_MILES:
1657 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1658 break;
1659 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1660 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1661 break;
1662 default:
1663 temp = g_strdup_printf ("Just to keep the compiler happy");
1664 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1665 }
1666
1667 s->has_oldcoord = FALSE;
1668 }
1669 else {
1670 temp = g_strdup_printf ( "%s %s", lat, lon );
1671 s->has_oldcoord = TRUE;
1672 }
1673
1674 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1675 g_free ( temp );
1676
1677 s->oldcoord = coord;
1678 }
1679 else {
1680 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1681 draw_update ( s->vw );
1682 }
1683 return VIK_LAYER_TOOL_ACK;
1684}
1685
1686static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1687{
1688 VikViewport *vvp = s->vvp;
1689 VikWindow *vw = s->vw;
1690
1691 struct LatLon ll;
1692 VikCoord coord;
1693 gchar *temp;
1694
1695 if ( s->has_oldcoord ) {
1696 int oldx, oldy, w1, h1, w2, h2;
1697 static GdkPixmap *buf = NULL;
1698 gchar *lat=NULL, *lon=NULL;
1699 w1 = vik_viewport_get_width(vvp);
1700 h1 = vik_viewport_get_height(vvp);
1701 if (!buf) {
1702 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1703 }
1704 gdk_drawable_get_size(buf, &w2, &h2);
1705 if (w1 != w2 || h1 != h2) {
1706 g_object_unref ( G_OBJECT ( buf ) );
1707 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1708 }
1709
1710 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1711 vik_coord_to_latlon ( &coord, &ll );
1712 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1713
1714 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1715 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1716 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)) );
1717 if (draw_buf_done) {
1718 static gpointer pass_along[3];
1719 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1720 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1721 pass_along[2] = buf;
1722 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1723 draw_buf_done = FALSE;
1724 }
1725 a_coords_latlon_to_string(&ll, &lat, &lon);
1726 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1727 switch (dist_units) {
1728 case VIK_UNITS_DISTANCE_KILOMETRES:
1729 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1730 break;
1731 case VIK_UNITS_DISTANCE_MILES:
1732 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1733 break;
1734 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1735 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1736 break;
1737 default:
1738 temp = g_strdup_printf ("Just to keep the compiler happy");
1739 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1740 }
1741 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1742 g_free ( temp );
1743 }
1744 return VIK_LAYER_TOOL_ACK;
1745}
1746
1747static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1748{
1749 return VIK_LAYER_TOOL_ACK;
1750}
1751
1752static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1753{
1754 draw_update ( s->vw );
1755}
1756
1757static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1758{
1759 if (event->keyval == GDK_Escape) {
1760 s->has_oldcoord = FALSE;
1761 ruler_deactivate ( vl, s );
1762 return TRUE;
1763 }
1764 // Regardless of whether we used it, return false so other GTK things may use it
1765 return FALSE;
1766}
1767
1768static VikToolInterface ruler_tool =
1769 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1770 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1771 (VikToolConstructorFunc) ruler_create,
1772 (VikToolDestructorFunc) ruler_destroy,
1773 (VikToolActivationFunc) NULL,
1774 (VikToolActivationFunc) ruler_deactivate,
1775 (VikToolMouseFunc) ruler_click,
1776 (VikToolMouseMoveFunc) ruler_move,
1777 (VikToolMouseFunc) ruler_release,
1778 (VikToolKeyFunc) ruler_key_press,
1779 FALSE,
1780 GDK_CURSOR_IS_PIXMAP,
1781 &cursor_ruler_pixbuf,
1782 NULL };
1783/*** end ruler code ********************************************************/
1784
1785
1786
1787/********************************************************************************
1788 ** Zoom tool code
1789 ********************************************************************************/
1790
1791typedef struct {
1792 VikWindow *vw;
1793 GdkPixmap *pixmap;
1794 // Track zoom bounds for zoom tool with shift modifier:
1795 gboolean bounds_active;
1796 gint start_x;
1797 gint start_y;
1798} zoom_tool_state_t;
1799
1800/*
1801 * In case the screen size has changed
1802 */
1803static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1804{
1805 int w1, h1, w2, h2;
1806
1807 // Allocate a drawing area the size of the viewport
1808 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1809 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1810
1811 if ( !zts->pixmap ) {
1812 // Totally new
1813 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1814 }
1815
1816 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1817
1818 if ( w1 != w2 || h1 != h2 ) {
1819 // Has changed - delete and recreate with new values
1820 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1821 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1822 }
1823}
1824
1825static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1826{
1827 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1828 zts->vw = vw;
1829 zts->pixmap = NULL;
1830 zts->start_x = 0;
1831 zts->start_y = 0;
1832 zts->bounds_active = FALSE;
1833 return zts;
1834}
1835
1836static void zoomtool_destroy ( zoom_tool_state_t *zts)
1837{
1838 if ( zts->pixmap )
1839 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1840 g_free(zts);
1841}
1842
1843static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1844{
1845 zts->vw->modified = TRUE;
1846 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1847
1848 VikCoord coord;
1849 gint x, y;
1850 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1851 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1852
1853 gboolean skip_update = FALSE;
1854
1855 zts->bounds_active = FALSE;
1856
1857 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1858 // This zoom is on the center position
1859 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1860 if ( event->button == 1 )
1861 vik_viewport_zoom_in (zts->vw->viking_vvp);
1862 else if ( event->button == 3 )
1863 vik_viewport_zoom_out (zts->vw->viking_vvp);
1864 }
1865 else if ( modifiers == GDK_CONTROL_MASK ) {
1866 // This zoom is to recenter on the mouse position
1867 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1868 if ( event->button == 1 )
1869 vik_viewport_zoom_in (zts->vw->viking_vvp);
1870 else if ( event->button == 3 )
1871 vik_viewport_zoom_out (zts->vw->viking_vvp);
1872 }
1873 else if ( modifiers == GDK_SHIFT_MASK ) {
1874 // Get start of new zoom bounds
1875 if ( event->button == 1 ) {
1876 zts->bounds_active = TRUE;
1877 zts->start_x = (gint) event->x;
1878 zts->start_y = (gint) event->y;
1879 skip_update = TRUE;
1880 }
1881 }
1882 else {
1883 /* make sure mouse is still over the same point on the map when we zoom */
1884 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1885 if ( event->button == 1 )
1886 vik_viewport_zoom_in (zts->vw->viking_vvp);
1887 else if ( event->button == 3 )
1888 vik_viewport_zoom_out(zts->vw->viking_vvp);
1889 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1890 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1891 center_x + (x - event->x),
1892 center_y + (y - event->y) );
1893 }
1894
1895 if ( !skip_update )
1896 draw_update ( zts->vw );
1897
1898 return VIK_LAYER_TOOL_ACK;
1899}
1900
1901static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1902{
1903 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1904
1905 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1906 zoomtool_resize_pixmap ( zts );
1907
1908 // Blank out currently drawn area
1909 gdk_draw_drawable ( zts->pixmap,
1910 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1911 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1912 0, 0, 0, 0, -1, -1);
1913
1914 // Calculate new box starting point & size in pixels
1915 int xx, yy, width, height;
1916 if ( event->y > zts->start_y ) {
1917 yy = zts->start_y;
1918 height = event->y-zts->start_y;
1919 }
1920 else {
1921 yy = event->y;
1922 height = zts->start_y-event->y;
1923 }
1924 if ( event->x > zts->start_x ) {
1925 xx = zts->start_x;
1926 width = event->x-zts->start_x;
1927 }
1928 else {
1929 xx = event->x;
1930 width = zts->start_x-event->x;
1931 }
1932
1933 // Draw the box
1934 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1935
1936 // Only actually draw when there's time to do so
1937 if (draw_buf_done) {
1938 static gpointer pass_along[3];
1939 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1940 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1941 pass_along[2] = zts->pixmap;
1942 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1943 draw_buf_done = FALSE;
1944 }
1945 }
1946 else
1947 zts->bounds_active = FALSE;
1948
1949 return VIK_LAYER_TOOL_ACK;
1950}
1951
1952static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1953{
1954 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1955
1956 // Ensure haven't just released on the exact same position
1957 // i.e. probably haven't moved the mouse at all
1958 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1959 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1960 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1961
1962 VikCoord coord1, coord2;
1963 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1964 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1965
1966 // From the extend of the bounds pick the best zoom level
1967 // c.f. trw_layer_zoom_to_show_latlons()
1968 // Maybe refactor...
1969 struct LatLon ll1, ll2;
1970 vik_coord_to_latlon(&coord1, &ll1);
1971 vik_coord_to_latlon(&coord2, &ll2);
1972 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1973 (ll1.lon+ll2.lon)/2 };
1974
1975 VikCoord new_center;
1976 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1977 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1978
1979 /* Convert into definite 'smallest' and 'largest' positions */
1980 struct LatLon minmin;
1981 if ( ll1.lat < ll2.lat )
1982 minmin.lat = ll1.lat;
1983 else
1984 minmin.lat = ll2.lat;
1985
1986 struct LatLon maxmax;
1987 if ( ll1.lon > ll2.lon )
1988 maxmax.lon = ll1.lon;
1989 else
1990 maxmax.lon = ll2.lon;
1991
1992 /* Always recalculate the 'best' zoom level */
1993 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1994 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1995
1996 gdouble min_lat, max_lat, min_lon, max_lon;
1997 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1998 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1999 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2000 /* NB I think the logic used in this test to determine if the bounds is within view
2001 fails if track goes across 180 degrees longitude.
2002 Hopefully that situation is not too common...
2003 Mind you viking doesn't really do edge locations to well anyway */
2004 if ( min_lat < minmin.lat &&
2005 max_lat > minmin.lat &&
2006 min_lon < maxmax.lon &&
2007 max_lon > maxmax.lon )
2008 /* Found within zoom level */
2009 break;
2010
2011 /* Try next */
2012 zoom = zoom * 2;
2013 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2014 }
2015 }
2016 else {
2017 // When pressing shift and clicking for zoom, then jump three levels
2018 if ( modifiers == GDK_SHIFT_MASK ) {
2019 // Zoom in/out by three if possible
2020 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2021 if ( event->button == 1 ) {
2022 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2023 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2024 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2025 }
2026 else if ( event->button == 3 ) {
2027 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2028 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2029 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2030 }
2031 }
2032 }
2033
2034 draw_update ( zts->vw );
2035
2036 // Reset
2037 zts->bounds_active = FALSE;
2038
2039 return VIK_LAYER_TOOL_ACK;
2040}
2041
2042static VikToolInterface zoom_tool =
2043 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2044 (VikToolConstructorFunc) zoomtool_create,
2045 (VikToolDestructorFunc) zoomtool_destroy,
2046 (VikToolActivationFunc) NULL,
2047 (VikToolActivationFunc) NULL,
2048 (VikToolMouseFunc) zoomtool_click,
2049 (VikToolMouseMoveFunc) zoomtool_move,
2050 (VikToolMouseFunc) zoomtool_release,
2051 NULL,
2052 FALSE,
2053 GDK_CURSOR_IS_PIXMAP,
2054 &cursor_zoom_pixbuf,
2055 NULL };
2056/*** end zoom code ********************************************************/
2057
2058/********************************************************************************
2059 ** Pan tool code
2060 ********************************************************************************/
2061static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2062{
2063 return vw;
2064}
2065
2066// NB Double clicking means this gets called THREE times!!!
2067static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2068{
2069 vw->modified = TRUE;
2070
2071 if ( event->type == GDK_2BUTTON_PRESS ) {
2072 // Zoom in / out on double click
2073 // No need to change the center as that has already occurred in the first click of a double click occurrence
2074 if ( event->button == 1 ) {
2075 guint modifier = event->state & GDK_SHIFT_MASK;
2076 if ( modifier )
2077 vik_viewport_zoom_out ( vw->viking_vvp );
2078 else
2079 vik_viewport_zoom_in ( vw->viking_vvp );
2080 }
2081 else if ( event->button == 3 )
2082 vik_viewport_zoom_out ( vw->viking_vvp );
2083
2084 draw_update ( vw );
2085 }
2086 else
2087 // Standard pan click
2088 if ( event->button == 1 )
2089 vik_window_pan_click ( vw, event );
2090
2091 return VIK_LAYER_TOOL_ACK;
2092}
2093
2094static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2095{
2096 vik_window_pan_move ( vw, event );
2097 return VIK_LAYER_TOOL_ACK;
2098}
2099
2100static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2101{
2102 if ( event->button == 1 )
2103 vik_window_pan_release ( vw, event );
2104 return VIK_LAYER_TOOL_ACK;
2105}
2106
2107static VikToolInterface pan_tool =
2108 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2109 (VikToolConstructorFunc) pantool_create,
2110 (VikToolDestructorFunc) NULL,
2111 (VikToolActivationFunc) NULL,
2112 (VikToolActivationFunc) NULL,
2113 (VikToolMouseFunc) pantool_click,
2114 (VikToolMouseMoveFunc) pantool_move,
2115 (VikToolMouseFunc) pantool_release,
2116 NULL,
2117 FALSE,
2118 GDK_FLEUR,
2119 NULL,
2120 NULL };
2121/*** end pan code ********************************************************/
2122
2123/********************************************************************************
2124 ** Select tool code
2125 ********************************************************************************/
2126static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2127{
2128 tool_ed_t *t = g_new(tool_ed_t, 1);
2129 t->vw = vw;
2130 t->vvp = vvp;
2131 t->vtl = NULL;
2132 t->is_waypoint = FALSE;
2133 return t;
2134}
2135
2136static void selecttool_destroy (tool_ed_t *t)
2137{
2138 g_free(t);
2139}
2140
2141typedef struct {
2142 gboolean cont;
2143 VikViewport *vvp;
2144 GdkEventButton *event;
2145 tool_ed_t *tool_edit;
2146} clicker;
2147
2148static void click_layer_selected (VikLayer *vl, clicker *ck)
2149{
2150 /* Do nothing when function call returns true; */
2151 /* i.e. stop on first found item */
2152 if ( ck->cont )
2153 if ( vl->visible )
2154 if ( vik_layer_get_interface(vl->type)->select_click )
2155 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2156}
2157
2158#ifdef WINDOWS
2159// Hopefully Alt keys by default
2160#define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2161#else
2162// Alt+mouse on Linux desktops tend to be used by the desktop manager
2163// Thus use an alternate modifier - you may need to set something into this group
2164#define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2165#endif
2166
2167static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2168{
2169 t->vw->select_move = FALSE;
2170 /* Only allow selection on primary button */
2171 if ( event->button == 1 ) {
2172
2173 if ( event->state & VIK_MOVE_MODIFIER )
2174 vik_window_pan_click ( t->vw, event );
2175 else {
2176 /* Enable click to apply callback to potentially all track/waypoint layers */
2177 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2178 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2179 clicker ck;
2180 ck.cont = TRUE;
2181 ck.vvp = t->vw->viking_vvp;
2182 ck.event = event;
2183 ck.tool_edit = t;
2184 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2185 g_list_free ( gl );
2186
2187 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2188 if ( ck.cont ) {
2189 GtkTreeIter iter;
2190 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2191
2192 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2193 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2194 gint type = vik_treeview_item_get_type ( vtv, &iter );
2195 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2196 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2197
2198 vik_treeview_item_unselect ( vtv, &iter );
2199 if ( vik_window_clear_highlight ( t->vw ) )
2200 draw_update ( t->vw );
2201 }
2202 }
2203 }
2204 else {
2205 // Something found - so enable movement
2206 t->vw->select_move = TRUE;
2207 }
2208 }
2209 }
2210 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2211 if ( vl->visible )
2212 /* Act on currently selected item to show menu */
2213 if ( t->vw->selected_track || t->vw->selected_waypoint )
2214 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2215 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2216 }
2217
2218 return VIK_LAYER_TOOL_ACK;
2219}
2220
2221static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
2222{
2223 if ( t->vw->select_move ) {
2224 // Don't care about vl here
2225 if ( t->vtl )
2226 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2227 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2228 }
2229 else
2230 // Optional Panning
2231 if ( event->state & VIK_MOVE_MODIFIER )
2232 vik_window_pan_move ( t->vw, event );
2233
2234 return VIK_LAYER_TOOL_ACK;
2235}
2236
2237static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2238{
2239 if ( t->vw->select_move ) {
2240 // Don't care about vl here
2241 if ( t->vtl )
2242 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2243 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2244 }
2245
2246 if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2247 vik_window_pan_release ( t->vw, event );
2248
2249 // Force pan off incase it was on
2250 t->vw->pan_move = FALSE;
2251 t->vw->pan_x = t->vw->pan_y = -1;
2252
2253 // End of this select movement
2254 t->vw->select_move = FALSE;
2255
2256 return VIK_LAYER_TOOL_ACK;
2257}
2258
2259static VikToolInterface select_tool =
2260 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2261 (VikToolConstructorFunc) selecttool_create,
2262 (VikToolDestructorFunc) selecttool_destroy,
2263 (VikToolActivationFunc) NULL,
2264 (VikToolActivationFunc) NULL,
2265 (VikToolMouseFunc) selecttool_click,
2266 (VikToolMouseMoveFunc) selecttool_move,
2267 (VikToolMouseFunc) selecttool_release,
2268 (VikToolKeyFunc) NULL,
2269 FALSE,
2270 GDK_LEFT_PTR,
2271 NULL,
2272 NULL };
2273/*** end select tool code ********************************************************/
2274
2275static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2276{
2277 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2278 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2279 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2280 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2281 return;
2282
2283 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2284 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2285 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2286 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2287 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2288 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2289 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2290 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2291 }
2292 draw_update ( vw );
2293}
2294
2295static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2296{
2297 guint what = 128;
2298
2299 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2300 what = -3;
2301 }
2302 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2303 what = -4;
2304 }
2305 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2306 what = -2;
2307 }
2308 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2309 what = -1;
2310 }
2311 else {
2312 gchar *s = (gchar *)gtk_action_get_name(a);
2313 what = atoi(s+4);
2314 }
2315
2316 switch (what)
2317 {
2318 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2319 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2320 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2321 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2322 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2323 }
2324 draw_update ( vw );
2325}
2326
2327static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2328{
2329 VikCoord new_center;
2330
2331 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2332 struct LatLon ll, llold;
2333 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2334 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2335 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2336 else
2337 return;
2338 }
2339 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2340 struct UTM utm, utmold;
2341 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2342 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2343 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2344 else
2345 return;
2346 }
2347 else {
2348 g_critical("Houston, we've had a problem.");
2349 return;
2350 }
2351
2352 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2353 draw_update ( vw );
2354}
2355
2356/**
2357 * center_changed_cb:
2358 */
2359static void center_changed_cb ( VikWindow *vw )
2360{
2361// ATM Keep back always available, so when we pan - we can jump to the last requested position
2362/*
2363 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2364 if ( action_back ) {
2365 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2366 }
2367*/
2368 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2369 if ( action_forward ) {
2370 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2371 }
2372
2373 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2374}
2375
2376/**
2377 * draw_goto_back_and_forth:
2378 */
2379static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2380{
2381 gboolean changed = FALSE;
2382 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2383 changed = vik_viewport_go_back ( vw->viking_vvp );
2384 }
2385 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2386 changed = vik_viewport_go_forward ( vw->viking_vvp );
2387 }
2388 else {
2389 return;
2390 }
2391
2392 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2393 // (otherwise we would get stuck in an infinite loop!)
2394 center_changed_cb ( vw );
2395
2396 if ( changed )
2397 draw_update ( vw );
2398}
2399
2400/**
2401 * Refresh maps displayed
2402 */
2403static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2404{
2405 // Only get 'new' maps
2406 simple_map_update ( vw, TRUE );
2407}
2408
2409static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2410{
2411 VikLayerTypeEnum type;
2412 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2413 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2414 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2415 draw_update ( vw );
2416 vw->modified = TRUE;
2417 }
2418 }
2419 }
2420}
2421
2422static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2423{
2424 a_clipboard_copy_selected ( vw->viking_vlp );
2425}
2426
2427static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2428{
2429 vik_layers_panel_cut_selected ( vw->viking_vlp );
2430 vw->modified = TRUE;
2431}
2432
2433static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2434{
2435 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2436 {
2437 vw->modified = TRUE;
2438 }
2439}
2440
2441static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2442{
2443 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2444 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2445}
2446
2447static void help_help_cb ( GtkAction *a, VikWindow *vw )
2448{
2449#ifdef WINDOWS
2450 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2451#else /* WINDOWS */
2452 gchar *uri;
2453 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2454 GError *error = NULL;
2455 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2456 if ( !show && !error )
2457 // No error to show, so unlikely this will get called
2458 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2459 else if ( error ) {
2460 // Main error path
2461 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 );
2462 g_error_free ( error );
2463 }
2464 g_free(uri);
2465#endif /* WINDOWS */
2466}
2467
2468static void toggle_side_panel ( VikWindow *vw )
2469{
2470 vw->show_side_panel = !vw->show_side_panel;
2471 if ( vw->show_side_panel )
2472 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2473 else
2474 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2475}
2476
2477static void toggle_full_screen ( VikWindow *vw )
2478{
2479 vw->show_full_screen = !vw->show_full_screen;
2480 if ( vw->show_full_screen )
2481 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2482 else
2483 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2484}
2485
2486static void toggle_statusbar ( VikWindow *vw )
2487{
2488 vw->show_statusbar = !vw->show_statusbar;
2489 if ( vw->show_statusbar )
2490 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2491 else
2492 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2493}
2494
2495static void toggle_toolbar ( VikWindow *vw )
2496{
2497 vw->show_toolbar = !vw->show_toolbar;
2498 if ( vw->show_toolbar )
2499 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2500 else
2501 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2502}
2503
2504static void toggle_main_menu ( VikWindow *vw )
2505{
2506 vw->show_main_menu = !vw->show_main_menu;
2507 if ( vw->show_main_menu )
2508 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2509 else
2510 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2511}
2512
2513// Only for 'view' toggle menu widgets ATM.
2514GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2515{
2516 g_return_val_if_fail(name != NULL, NULL);
2517
2518 // ATM only FullScreen is *not* in SetShow path
2519 gchar *path;
2520 if ( g_strcmp0 ("FullScreen", name ) )
2521 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2522 else
2523 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2524
2525 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2526 g_free(path);
2527
2528 return widget;
2529}
2530
2531static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2532{
2533 gboolean next_state = !vw->show_side_panel;
2534 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2535 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2536 if ( next_state != menu_state )
2537 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2538 else
2539 toggle_side_panel ( vw );
2540}
2541
2542static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2543{
2544 gboolean next_state = !vw->show_full_screen;
2545 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2546 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2547 if ( next_state != menu_state )
2548 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2549 else
2550 toggle_full_screen ( vw );
2551}
2552
2553static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2554{
2555 gboolean next_state = !vw->show_statusbar;
2556 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2557 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2558 if ( next_state != menu_state )
2559 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2560 else
2561 toggle_statusbar ( vw );
2562}
2563
2564static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2565{
2566 gboolean next_state = !vw->show_toolbar;
2567 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2568 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2569 if ( next_state != menu_state )
2570 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2571 else
2572 toggle_toolbar ( vw );
2573}
2574
2575static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2576{
2577 gboolean next_state = !vw->show_main_menu;
2578 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2579 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2580 if ( next_state != menu_state )
2581 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2582 else
2583 toggle_main_menu ( vw );
2584}
2585
2586static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2587{
2588 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2589 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2590 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2591 if ( next_state != menu_state )
2592 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2593 else {
2594 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2595 draw_update ( vw );
2596 }
2597}
2598
2599static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2600{
2601 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2602 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2603 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2604 if ( next_state != menu_state )
2605 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2606 else {
2607 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2608 draw_update ( vw );
2609 }
2610}
2611
2612static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2613{
2614 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2615 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2616 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2617 if ( next_state != menu_state )
2618 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2619 else {
2620 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2621 draw_update ( vw );
2622 }
2623}
2624
2625static void help_about_cb ( GtkAction *a, VikWindow *vw )
2626{
2627 a_dialog_about(GTK_WINDOW(vw));
2628}
2629
2630static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2631{
2632 // NB: No i18n as this is just for debug
2633 gint byte_size = a_mapcache_get_size();
2634 gchar *msg_sz = NULL;
2635 gchar *msg = NULL;
2636#if GLIB_CHECK_VERSION(2,30,0)
2637 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2638#else
2639 msg_sz = g_format_size_for_display ( byte_size );
2640#endif
2641 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2642 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2643 g_free ( msg_sz );
2644 g_free ( msg );
2645}
2646
2647static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2648{
2649 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2650}
2651
2652static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2653{
2654 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2655 {
2656 vik_layers_panel_delete_selected ( vw->viking_vlp );
2657 vw->modified = TRUE;
2658 }
2659 else
2660 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2661}
2662
2663static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2664{
2665 gboolean next_state = !vw->show_full_screen;
2666 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2667 if ( tbutton ) {
2668 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2669 if ( next_state != tb_state )
2670 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2671 else
2672 toggle_full_screen ( vw );
2673 }
2674 else
2675 toggle_full_screen ( vw );
2676}
2677
2678static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2679{
2680 gboolean next_state = !vw->show_side_panel;
2681 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2682 if ( tbutton ) {
2683 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2684 if ( next_state != tb_state )
2685 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2686 else
2687 toggle_side_panel ( vw );
2688 }
2689 else
2690 toggle_side_panel ( vw );
2691}
2692
2693static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2694{
2695 gboolean next_state = !vw->show_statusbar;
2696 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2697 if ( tbutton ) {
2698 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2699 if ( next_state != tb_state )
2700 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2701 else
2702 toggle_statusbar ( vw );
2703 }
2704 else
2705 toggle_statusbar ( vw );
2706}
2707
2708static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2709{
2710 gboolean next_state = !vw->show_toolbar;
2711 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2712 if ( tbutton ) {
2713 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2714 if ( next_state != tb_state )
2715 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2716 else
2717 toggle_toolbar ( vw );
2718 }
2719 else
2720 toggle_toolbar ( vw );
2721}
2722
2723static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2724{
2725 gboolean next_state = !vw->show_main_menu;
2726 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2727 if ( tbutton ) {
2728 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2729 if ( next_state != tb_state )
2730 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2731 else
2732 toggle_main_menu ( vw );
2733 }
2734 else
2735 toggle_toolbar ( vw );
2736}
2737
2738/***************************************
2739 ** tool management routines
2740 **
2741 ***************************************/
2742
2743static toolbox_tools_t* toolbox_create(VikWindow *vw)
2744{
2745 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2746 vt->tools = NULL;
2747 vt->n_tools = 0;
2748 vt->active_tool = -1;
2749 vt->vw = vw;
2750 return vt;
2751}
2752
2753static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2754{
2755 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2756 vt->tools[vt->n_tools].ti = *vti;
2757 vt->tools[vt->n_tools].layer_type = layer_type;
2758 if (vti->create) {
2759 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2760 }
2761 else {
2762 vt->tools[vt->n_tools].state = NULL;
2763 }
2764 vt->n_tools++;
2765}
2766
2767static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2768{
2769 int i;
2770 for (i=0; i<vt->n_tools; i++) {
2771 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2772 break;
2773 }
2774 }
2775 return i;
2776}
2777
2778static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2779{
2780 int tool = toolbox_get_tool(vt, tool_name);
2781 toolbox_tool_t *t = &vt->tools[tool];
2782 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2783
2784 if (tool == vt->n_tools) {
2785 g_critical("trying to activate a non-existent tool...");
2786 return;
2787 }
2788 /* is the tool already active? */
2789 if (vt->active_tool == tool) {
2790 return;
2791 }
2792
2793 if (vt->active_tool != -1) {
2794 if (vt->tools[vt->active_tool].ti.deactivate) {
2795 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2796 }
2797 }
2798 if (t->ti.activate) {
2799 t->ti.activate(vl, t->state);
2800 }
2801 vt->active_tool = tool;
2802}
2803
2804static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2805{
2806 int tool = toolbox_get_tool(vt, tool_name);
2807 toolbox_tool_t *t = &vt->tools[tool];
2808 if (t->ti.cursor == NULL) {
2809 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2810 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, NULL);
2811 /* TODO: settable offeset */
2812 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2813 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2814 } else {
2815 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2816 }
2817 }
2818 return t->ti.cursor;
2819}
2820
2821static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2822{
2823 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2824 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2825 gint ltype = vt->tools[vt->active_tool].layer_type;
2826 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2827 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2828 }
2829}
2830
2831static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2832{
2833 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2834 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2835 gint ltype = vt->tools[vt->active_tool].layer_type;
2836 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2837 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2838 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2839 }
2840}
2841
2842static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2843{
2844 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2845 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2846 gint ltype = vt->tools[vt->active_tool].layer_type;
2847 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2848 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2849 }
2850}
2851/** End tool management ************************************/
2852
2853void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2854{
2855 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2856}
2857
2858// Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2859// DON'T Use this from menu callback with toggle toolbar items!!
2860static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2861{
2862 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2863 if ( tbutton ) {
2864 // Causes toggle signal action to be raised.
2865 gtk_toggle_tool_button_set_active ( tbutton, state );
2866 }
2867}
2868
2869/* this function gets called whenever a menu is clicked */
2870// Note old is not used
2871static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2872{
2873 // Ensure Toolbar kept in sync
2874 const gchar *name = gtk_action_get_name(a);
2875 toolbar_sync ( vw, name, TRUE );
2876
2877 /* White Magic, my friends ... White Magic... */
2878 gint tool_id;
2879 toolbox_activate(vw->vt, name);
2880
2881 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2882
2883 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2884 /* We set cursor, even if it is NULL: it resets to default */
2885 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2886
2887 if (!g_strcmp0(name, "Pan")) {
2888 vw->current_tool = TOOL_PAN;
2889 }
2890 else if (!g_strcmp0(name, "Zoom")) {
2891 vw->current_tool = TOOL_ZOOM;
2892 }
2893 else if (!g_strcmp0(name, "Ruler")) {
2894 vw->current_tool = TOOL_RULER;
2895 }
2896 else if (!g_strcmp0(name, "Select")) {
2897 vw->current_tool = TOOL_SELECT;
2898 }
2899 else {
2900 VikLayerTypeEnum layer_id;
2901 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2902 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2903 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2904 vw->current_tool = TOOL_LAYER;
2905 vw->tool_layer_id = layer_id;
2906 vw->tool_tool_id = tool_id;
2907 }
2908 }
2909 }
2910 }
2911 draw_status_tool ( vw );
2912}
2913
2914static void window_set_filename ( VikWindow *vw, const gchar *filename )
2915{
2916 gchar *title;
2917 const gchar *file;
2918 if ( vw->filename )
2919 g_free ( vw->filename );
2920 if ( filename == NULL )
2921 {
2922 vw->filename = NULL;
2923 }
2924 else
2925 {
2926 vw->filename = g_strdup(filename);
2927 }
2928
2929 /* Refresh window's title */
2930 file = window_get_filename ( vw );
2931 title = g_strdup_printf( "%s - Viking", file );
2932 gtk_window_set_title ( GTK_WINDOW(vw), title );
2933 g_free ( title );
2934}
2935
2936static const gchar *window_get_filename ( VikWindow *vw )
2937{
2938 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2939}
2940
2941GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2942{
2943 GtkWidget *mode_button;
2944 gchar *buttonname;
2945 switch ( mode ) {
2946#ifdef VIK_CONFIG_EXPEDIA
2947 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2948#endif
2949 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2950 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2951 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2952 }
2953 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2954 g_assert ( mode_button );
2955 return mode_button;
2956}
2957
2958/**
2959 * vik_window_get_pan_move:
2960 * @vw: some VikWindow
2961 *
2962 * Retrieves @vw's pan_move.
2963 *
2964 * Should be removed as soon as possible.
2965 *
2966 * Returns: @vw's pan_move
2967 *
2968 * Since: 0.9.96
2969 **/
2970gboolean vik_window_get_pan_move ( VikWindow *vw )
2971{
2972 return vw->pan_move;
2973}
2974
2975static void on_activate_recent_item (GtkRecentChooser *chooser,
2976 VikWindow *self)
2977{
2978 gchar *filename;
2979
2980 filename = gtk_recent_chooser_get_current_uri (chooser);
2981 if (filename != NULL)
2982 {
2983 GFile *file = g_file_new_for_uri ( filename );
2984 gchar *path = g_file_get_path ( file );
2985 g_object_unref ( file );
2986 if ( self->filename )
2987 {
2988 GSList *filenames = NULL;
2989 filenames = g_slist_append ( filenames, path );
2990 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2991 // NB: GSList & contents are freed by main.open_window
2992 }
2993 else {
2994 vik_window_open_file ( self, path, TRUE );
2995 g_free ( path );
2996 }
2997 }
2998
2999 g_free (filename);
3000}
3001
3002static void setup_recent_files (VikWindow *self)
3003{
3004 GtkRecentManager *manager;
3005 GtkRecentFilter *filter;
3006 GtkWidget *menu, *menu_item;
3007
3008 filter = gtk_recent_filter_new ();
3009 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3010 gtk_recent_filter_add_group(filter, "viking");
3011
3012 manager = gtk_recent_manager_get_default ();
3013 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3014 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3015 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
3016 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
3017
3018 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3019 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3020
3021 g_signal_connect (G_OBJECT (menu), "item-activated",
3022 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3023}
3024
3025/*
3026 *
3027 */
3028static void update_recently_used_document (VikWindow *vw, const gchar *filename)
3029{
3030 /* Update Recently Used Document framework */
3031 GtkRecentManager *manager = gtk_recent_manager_get_default();
3032 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3033 gchar *groups[] = {"viking", NULL};
3034 GFile *file = g_file_new_for_commandline_arg(filename);
3035 gchar *uri = g_file_get_uri(file);
3036 gchar *basename = g_path_get_basename(filename);
3037 g_object_unref(file);
3038 file = NULL;
3039
3040 recent_data->display_name = basename;
3041 recent_data->description = NULL;
3042 recent_data->mime_type = "text/x-gps-data";
3043 recent_data->app_name = (gchar *) g_get_application_name ();
3044 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3045 recent_data->groups = groups;
3046 recent_data->is_private = FALSE;
3047 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3048 {
3049 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3050 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3051 g_free ( msg );
3052 }
3053
3054 g_free (uri);
3055 g_free (basename);
3056 g_free (recent_data->app_exec);
3057 g_slice_free (GtkRecentData, recent_data);
3058}
3059
3060/**
3061 * Call this before doing things that may take a long time and otherwise not show any other feedback
3062 * such as loading and saving files
3063 */
3064void vik_window_set_busy_cursor ( VikWindow *vw )
3065{
3066 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3067 // Viewport has a separate cursor
3068 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3069 // Ensure cursor updated before doing stuff
3070 while( gtk_events_pending() )
3071 gtk_main_iteration();
3072}
3073
3074void vik_window_clear_busy_cursor ( VikWindow *vw )
3075{
3076 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3077 // Restore viewport cursor
3078 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3079}
3080
3081void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3082{
3083 vik_window_set_busy_cursor ( vw );
3084
3085 // Enable the *new* filename to be accessible by the Layers codez
3086 gchar *original_filename = g_strdup ( vw->filename );
3087 g_free ( vw->filename );
3088 vw->filename = g_strdup ( filename );
3089 gboolean success = FALSE;
3090 gboolean restore_original_filename = FALSE;
3091
3092 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
3093 switch ( vw->loaded_type )
3094 {
3095 case LOAD_TYPE_READ_FAILURE:
3096 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3097 break;
3098 case LOAD_TYPE_GPSBABEL_FAILURE:
3099 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3100 break;
3101 case LOAD_TYPE_GPX_FAILURE:
3102 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3103 break;
3104 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3105 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3106 break;
3107 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3108 {
3109 // Since we can process .vik files with issues just show a warning in the status bar
3110 // Not that a user can do much about it... or tells them what this issue is yet...
3111 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3112 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3113 g_free ( msg );
3114 }
3115 // No break, carry on to show any data
3116 case LOAD_TYPE_VIK_SUCCESS:
3117 {
3118 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3119 GtkWidget *mode_button;
3120 /* Update UI */
3121 if ( change_filename )
3122 window_set_filename ( vw, filename );
3123 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3124 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. */
3125 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3126 vw->only_updating_coord_mode_ui = FALSE;
3127
3128 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3129
3130 // Slightly long winded methods to align loaded viewport settings with the UI
3131 // Since the rewrite for toolbar + menu actions
3132 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3133 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3134 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3135 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3136 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3137 if ( vp_state_scale != ui_state_scale ) {
3138 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3139 toggle_draw_scale ( NULL, vw );
3140 }
3141 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3142 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3143 if ( vp_state_centermark != ui_state_centermark ) {
3144 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3145 toggle_draw_centermark ( NULL, vw );
3146 }
3147 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3148 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3149 if ( vp_state_highlight != ui_state_highlight ) {
3150 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3151 toggle_draw_highlight ( NULL, vw );
3152 }
3153 }
3154 // NB No break, carry on to redraw
3155 //case LOAD_TYPE_OTHER_SUCCESS:
3156 default:
3157 success = TRUE;
3158 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3159 restore_original_filename = ! restore_original_filename;
3160 update_recently_used_document (vw, filename);
3161 draw_update ( vw );
3162 break;
3163 }
3164
3165 if ( ! success || restore_original_filename )
3166 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3167 window_set_filename ( vw, original_filename );
3168 g_free ( original_filename );
3169
3170 vik_window_clear_busy_cursor ( vw );
3171}
3172
3173static void load_file ( GtkAction *a, VikWindow *vw )
3174{
3175 GSList *files = NULL;
3176 GSList *cur_file = NULL;
3177 gboolean newwindow;
3178 if (!strcmp(gtk_action_get_name(a), "Open")) {
3179 newwindow = TRUE;
3180 }
3181 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3182 newwindow = FALSE;
3183 }
3184 else {
3185 g_critical("Houston, we've had a problem.");
3186 return;
3187 }
3188
3189 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3190 GTK_WINDOW(vw),
3191 GTK_FILE_CHOOSER_ACTION_OPEN,
3192 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3193 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3194 NULL);
3195 if ( last_folder_files_uri )
3196 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3197
3198 GtkFileFilter *filter;
3199 // NB file filters are listed this way for alphabetical ordering
3200#ifdef VIK_CONFIG_GEOCACHES
3201 filter = gtk_file_filter_new ();
3202 gtk_file_filter_set_name( filter, _("Geocaching") );
3203 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3204 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3205#endif
3206
3207 filter = gtk_file_filter_new ();
3208 gtk_file_filter_set_name( filter, _("Google Earth") );
3209 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3210 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3211
3212 filter = gtk_file_filter_new ();
3213 gtk_file_filter_set_name( filter, _("GPX") );
3214 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3215 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3216
3217 filter = gtk_file_filter_new ();
3218 gtk_file_filter_set_name ( filter, _("JPG") );
3219 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3220 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3221
3222 filter = gtk_file_filter_new ();
3223 gtk_file_filter_set_name( filter, _("Viking") );
3224 gtk_file_filter_add_pattern ( filter, "*.vik" );
3225 gtk_file_filter_add_pattern ( filter, "*.viking" );
3226 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3227
3228 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3229 // However assume this are barely used and thus not worthy of inclusion
3230 // as they'll just make the options too many and have no clear file pattern
3231 // one can always use the all option
3232 filter = gtk_file_filter_new ();
3233 gtk_file_filter_set_name( filter, _("All") );
3234 gtk_file_filter_add_pattern ( filter, "*" );
3235 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3236 // Default to any file - same as before open filters were added
3237 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3238
3239 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(dialog), TRUE );
3240 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3241 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3242
3243 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3244 {
3245 g_free ( last_folder_files_uri );
3246 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3247
3248#ifdef VIKING_PROMPT_IF_MODIFIED
3249 if ( (vw->modified || vw->filename) && newwindow )
3250#else
3251 if ( vw->filename && newwindow )
3252#endif
3253 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) ) );
3254 else {
3255
3256 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) );
3257 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3258 gboolean first_vik_file = TRUE;
3259 cur_file = files;
3260 while ( cur_file ) {
3261
3262 gchar *file_name = cur_file->data;
3263 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3264 // Load first of many .vik files in current window
3265 if ( first_vik_file ) {
3266 vik_window_open_file ( vw, file_name, TRUE );
3267 first_vik_file = FALSE;
3268 }
3269 else {
3270 // Load each subsequent .vik file in a separate window
3271 VikWindow *newvw = vik_window_new_window ();
3272 if (newvw)
3273 vik_window_open_file ( newvw, file_name, TRUE );
3274 }
3275 }
3276 else
3277 // Other file types
3278 vik_window_open_file ( vw, file_name, change_fn );
3279
3280 g_free (file_name);
3281 cur_file = g_slist_next (cur_file);
3282 }
3283 g_slist_free (files);
3284 }
3285 }
3286 gtk_widget_destroy ( dialog );
3287}
3288
3289static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3290{
3291 gboolean rv = FALSE;
3292 const gchar *fn;
3293
3294 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3295 GTK_WINDOW(vw),
3296 GTK_FILE_CHOOSER_ACTION_SAVE,
3297 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3298 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3299 NULL);
3300 if ( last_folder_files_uri )
3301 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3302
3303 GtkFileFilter *filter;
3304 filter = gtk_file_filter_new ();
3305 gtk_file_filter_set_name( filter, _("All") );
3306 gtk_file_filter_add_pattern ( filter, "*" );
3307 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3308
3309 filter = gtk_file_filter_new ();
3310 gtk_file_filter_set_name( filter, _("Viking") );
3311 gtk_file_filter_add_pattern ( filter, "*.vik" );
3312 gtk_file_filter_add_pattern ( filter, "*.viking" );
3313 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3314 // Default to a Viking file
3315 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3316
3317 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3318 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3319
3320 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3321 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3322 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3323 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3324
3325 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), auto_save_name);
3326
3327 while ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3328 {
3329 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
3330 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3331 {
3332 window_set_filename ( vw, fn );
3333 rv = window_save ( vw );
3334 if ( rv ) {
3335 vw->modified = FALSE;
3336 g_free ( last_folder_files_uri );
3337 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3338 }
3339 break;
3340 }
3341 }
3342 g_free ( auto_save_name );
3343 gtk_widget_destroy ( dialog );
3344 return rv;
3345}
3346
3347static gboolean window_save ( VikWindow *vw )
3348{
3349 vik_window_set_busy_cursor ( vw );
3350 gboolean success = TRUE;
3351
3352 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3353 {
3354 update_recently_used_document ( vw, vw->filename );
3355 }
3356 else
3357 {
3358 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3359 success = FALSE;
3360 }
3361 vik_window_clear_busy_cursor ( vw );
3362 return success;
3363}
3364
3365static gboolean save_file ( GtkAction *a, VikWindow *vw )
3366{
3367 if ( ! vw->filename )
3368 return save_file_as ( NULL, vw );
3369 else
3370 {
3371 vw->modified = FALSE;
3372 return window_save ( vw );
3373 }
3374}
3375
3376/**
3377 * export_to:
3378 *
3379 * Export all TRW Layers in the list to individual files in the specified directory
3380 *
3381 * Returns: %TRUE on success
3382 */
3383static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3384{
3385 gboolean success = TRUE;
3386
3387 gint export_count = 0;
3388
3389 vik_window_set_busy_cursor ( vw );
3390
3391 while ( gl ) {
3392
3393 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3394
3395 // Some protection in attempting to write too many same named files
3396 // As this will get horribly slow...
3397 gboolean safe = FALSE;
3398 gint ii = 2;
3399 while ( ii < 5000 ) {
3400 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3401 // Try rename
3402 g_free ( fn );
3403 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3404 }
3405 else {
3406 safe = TRUE;
3407 break;
3408 }
3409 ii++;
3410 }
3411 if ( ii == 5000 )
3412 success = FALSE;
3413
3414 // NB: We allow exporting empty layers
3415 if ( safe ) {
3416 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3417
3418 // Show some progress
3419 if ( this_success ) {
3420 export_count++;
3421 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3422 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3423 while ( gtk_events_pending() )
3424 gtk_main_iteration ();
3425 g_free ( message );
3426 }
3427
3428 success = success && this_success;
3429 }
3430
3431 g_free ( fn );
3432 gl = g_list_next ( gl );
3433 }
3434
3435 vik_window_clear_busy_cursor ( vw );
3436
3437 // Confirm what happened.
3438 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3439 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3440 g_free ( message );
3441
3442 return success;
3443}
3444
3445static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3446{
3447 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3448
3449 if ( !gl ) {
3450 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3451 return;
3452 }
3453
3454 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3455 GTK_WINDOW(vw),
3456 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3457 GTK_STOCK_CANCEL,
3458 GTK_RESPONSE_REJECT,
3459 GTK_STOCK_OK,
3460 GTK_RESPONSE_ACCEPT,
3461 NULL );
3462 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3463 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3464 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3465
3466 gtk_widget_show_all ( dialog );
3467
3468 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3469 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3470 gtk_widget_destroy ( dialog );
3471 if ( dir ) {
3472 if ( !export_to ( vw, gl, vft, dir, extension ) )
3473 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3474 g_free ( dir );
3475 }
3476 }
3477 else
3478 gtk_widget_destroy ( dialog );
3479
3480 g_list_free ( gl );
3481}
3482
3483static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3484{
3485 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3486}
3487
3488static void export_to_kml ( GtkAction *a, VikWindow *vw )
3489{
3490 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3491}
3492
3493#if !GLIB_CHECK_VERSION(2,26,0)
3494typedef struct stat GStatBuf;
3495#endif
3496
3497static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3498{
3499 gchar *message = NULL;
3500 if ( vw->filename ) {
3501 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3502 // Get some timestamp information of the file
3503 GStatBuf stat_buf;
3504 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3505 gchar time_buf[64];
3506 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3507 gchar *size = NULL;
3508 gint byte_size = stat_buf.st_size;
3509#if GLIB_CHECK_VERSION(2,30,0)
3510 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3511#else
3512 size = g_format_size_for_display ( byte_size );
3513#endif
3514 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3515 g_free (size);
3516 }
3517 }
3518 else
3519 message = g_strdup ( _("File not accessible") );
3520 }
3521 else
3522 message = g_strdup ( _("No Viking File") );
3523
3524 // Show the info
3525 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3526 g_free ( message );
3527}
3528
3529static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3530{
3531 vik_datasource_mode_t mode = datasource->mode;
3532 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3533 mode = VIK_DATASOURCE_CREATENEWLAYER;
3534 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3535}
3536
3537static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3538{
3539 my_acquire ( vw, &vik_datasource_gps_interface );
3540}
3541
3542static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3543{
3544 my_acquire ( vw, &vik_datasource_file_interface );
3545}
3546
3547static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3548{
3549 my_acquire ( vw, &vik_datasource_geojson_interface );
3550}
3551
3552static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3553{
3554 my_acquire ( vw, &vik_datasource_routing_interface );
3555}
3556
3557#ifdef VIK_CONFIG_OPENSTREETMAP
3558static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3559{
3560 my_acquire ( vw, &vik_datasource_osm_interface );
3561}
3562
3563static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3564{
3565 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3566}
3567#endif
3568
3569#ifdef VIK_CONFIG_GEOCACHES
3570static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3571{
3572 my_acquire ( vw, &vik_datasource_gc_interface );
3573}
3574#endif
3575
3576#ifdef VIK_CONFIG_GEOTAG
3577static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3578{
3579 my_acquire ( vw, &vik_datasource_geotag_interface );
3580}
3581#endif
3582
3583#ifdef VIK_CONFIG_GEONAMES
3584static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3585{
3586 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3587}
3588#endif
3589
3590static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3591{
3592 my_acquire ( vw, &vik_datasource_url_interface );
3593}
3594
3595static void goto_default_location( GtkAction *a, VikWindow *vw)
3596{
3597 struct LatLon ll;
3598 ll.lat = a_vik_get_default_lat();
3599 ll.lon = a_vik_get_default_long();
3600 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3601 vik_layers_panel_emit_update(vw->viking_vlp);
3602}
3603
3604
3605static void goto_address( GtkAction *a, VikWindow *vw)
3606{
3607 a_vik_goto ( vw, vw->viking_vvp );
3608 vik_layers_panel_emit_update ( vw->viking_vlp );
3609}
3610
3611static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3612{
3613 a_mapcache_flush();
3614}
3615
3616static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3617{
3618 const VikCoord* coord;
3619 struct UTM utm;
3620 gchar *lat = NULL, *lon = NULL;
3621
3622 coord = vik_viewport_get_center ( vw->viking_vvp );
3623 vik_coord_to_utm ( coord, &utm );
3624
3625 gboolean full_format = FALSE;
3626 (void)a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3627
3628 if ( full_format )
3629 // Bells & Whistles - may include degrees, minutes and second symbols
3630 get_location_strings ( vw, utm, &lat, &lon );
3631 else {
3632 // Simple x.xx y.yy format
3633 struct LatLon ll;
3634 a_coords_utm_to_latlon ( &utm, &ll );
3635 lat = g_strdup_printf ( "%.6f", ll.lat );
3636 lon = g_strdup_printf ( "%.6f", ll.lon );
3637 }
3638
3639 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3640 g_free (lat);
3641 g_free (lon);
3642
3643 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3644
3645 g_free ( msg );
3646}
3647
3648static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3649{
3650 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3651
3652 if ( !texts[1] )
3653 return; // Internally broken :(
3654
3655 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3656 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3657 // NB no update needed
3658
3659 g_strfreev ( texts );
3660}
3661
3662static void preferences_change_update ( VikWindow *vw, gpointer data )
3663{
3664 // Want to update all TrackWaypoint layers
3665 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3666
3667 if ( !layers )
3668 return;
3669
3670 while ( layers ) {
3671 // Reset the individual waypoints themselves due to the preferences change
3672 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3673 vik_trw_layer_reset_waypoints ( vtl );
3674 layers = g_list_next ( layers );
3675 }
3676
3677 g_list_free ( layers );
3678
3679 draw_update ( vw );
3680}
3681
3682static void preferences_cb ( GtkAction *a, VikWindow *vw )
3683{
3684 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3685
3686 a_preferences_show_window ( GTK_WINDOW(vw) );
3687
3688 // Has the waypoint size setting changed?
3689 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3690 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3691 clear_garmin_icon_syms ();
3692
3693 // Update all windows
3694 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3695 }
3696
3697 // Ensure TZ Lookup initialized
3698 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3699 vu_setup_lat_lon_tz_lookup();
3700
3701 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
3702}
3703
3704static void default_location_cb ( GtkAction *a, VikWindow *vw )
3705{
3706 /* Simplistic repeat of preference setting
3707 Only the name & type are important for setting the preference via this 'external' way */
3708 VikLayerParam pref_lat[] = {
3709 { VIK_LAYER_NUM_TYPES,
3710 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3711 VIK_LAYER_PARAM_DOUBLE,
3712 VIK_LAYER_GROUP_NONE,
3713 NULL,
3714 VIK_LAYER_WIDGET_SPINBUTTON,
3715 NULL,
3716 NULL,
3717 NULL,
3718 NULL,
3719 NULL,
3720 NULL,
3721 },
3722 };
3723 VikLayerParam pref_lon[] = {
3724 { VIK_LAYER_NUM_TYPES,
3725 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3726 VIK_LAYER_PARAM_DOUBLE,
3727 VIK_LAYER_GROUP_NONE,
3728 NULL,
3729 VIK_LAYER_WIDGET_SPINBUTTON,
3730 NULL,
3731 NULL,
3732 NULL,
3733 NULL,
3734 NULL,
3735 NULL,
3736 },
3737 };
3738
3739 /* Get current center */
3740 struct LatLon ll;
3741 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3742
3743 /* Apply to preferences */
3744 VikLayerParamData vlp_data;
3745 vlp_data.d = ll.lat;
3746 a_preferences_run_setparam (vlp_data, pref_lat);
3747 vlp_data.d = ll.lon;
3748 a_preferences_run_setparam (vlp_data, pref_lon);
3749 /* Remember to save */
3750 a_preferences_save_to_file();
3751}
3752
3753/**
3754 * Delete All
3755 */
3756static void clear_cb ( GtkAction *a, VikWindow *vw )
3757{
3758 // Do nothing if empty
3759 VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
3760 if ( ! vik_aggregate_layer_is_empty(top) ) {
3761 if ( a_dialog_yes_or_no ( GTK_WINDOW(vw), _("Are you sure you wish to delete all layers?"), NULL ) ) {
3762 vik_layers_panel_clear ( vw->viking_vlp );
3763 window_set_filename ( vw, NULL );
3764 draw_update ( vw );
3765 }
3766 }
3767}
3768
3769static void window_close ( GtkAction *a, VikWindow *vw )
3770{
3771 if ( ! delete_event ( vw ) )
3772 gtk_widget_destroy ( GTK_WIDGET(vw) );
3773}
3774
3775static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3776{
3777 if (save_file( NULL, vw)) {
3778 window_close( NULL, vw);
3779 return(TRUE);
3780 }
3781 else
3782 return(FALSE);
3783}
3784
3785static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3786{
3787 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3788 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3789 {
3790 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3791 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3792 draw_update ( vw );
3793 }
3794}
3795
3796static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png, gboolean save_kmz )
3797{
3798 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3799 GdkPixbuf *pixbuf_to_save;
3800 gdouble old_xmpp, old_ympp;
3801 GError *error = NULL;
3802
3803 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3804 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3805 GTK_MESSAGE_INFO,
3806 GTK_BUTTONS_NONE,
3807 _("Generating image file...") );
3808
3809 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3810 // Ensure dialog shown
3811 gtk_widget_show_all ( msgbox );
3812 // Try harder...
3813 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3814 while ( gtk_events_pending() )
3815 gtk_main_iteration ();
3816 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3817 // At least the empty box can give a clue something's going on + the statusbar msg...
3818 // Windows version under Wine OK!
3819
3820 /* backup old zoom & set new */
3821 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3822 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3823 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3824
3825 /* reset width and height: */
3826 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3827
3828 /* draw all layers */
3829 draw_redraw ( vw );
3830
3831 /* save buffer as file. */
3832 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);
3833 if ( !pixbuf_to_save ) {
3834 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3835 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3836 goto cleanup;
3837 }
3838
3839 int ans = 0; // Default to success
3840
3841 if ( save_kmz ) {
3842 gdouble north, east, south, west;
3843 vik_viewport_get_min_max_lat_lon ( vw->viking_vvp, &south, &north, &west, &east );
3844 ans = kmz_save_file ( pixbuf_to_save, fn, north, east, south, west );
3845 }
3846 else {
3847 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3848 if (error) {
3849 g_warning("Unable to write to file %s: %s", fn, error->message );
3850 g_error_free (error);
3851 ans = 42;
3852 }
3853 }
3854
3855 if ( ans == 0 )
3856 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3857 else
3858 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3859
3860 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3861
3862 cleanup:
3863 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3864 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3865 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3866
3867 /* pretend like nothing happened ;) */
3868 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3869 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3870 vik_viewport_configure ( vw->viking_vvp );
3871 draw_update ( vw );
3872}
3873
3874static 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 )
3875{
3876 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3877 gchar *name_of_file = g_malloc ( size );
3878 guint x = 1, y = 1;
3879 struct UTM utm_orig, utm;
3880
3881 /* *** copied from above *** */
3882 GdkPixbuf *pixbuf_to_save;
3883 gdouble old_xmpp, old_ympp;
3884 GError *error = NULL;
3885
3886 /* backup old zoom & set new */
3887 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3888 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3889 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3890
3891 /* reset width and height: do this only once for all images (same size) */
3892 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3893 /* *** end copy from above *** */
3894
3895 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3896
3897 if ( g_mkdir(fn,0777) != 0 )
3898 g_warning ( "%s: Failed to create directory %s", __FUNCTION__, fn );
3899
3900 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3901
3902 for ( y = 1; y <= tiles_h; y++ )
3903 {
3904 for ( x = 1; x <= tiles_w; x++ )
3905 {
3906 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3907 utm = utm_orig;
3908 if ( tiles_w & 0x1 )
3909 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3910 else
3911 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3912 if ( tiles_h & 0x1 ) /* odd */
3913 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3914 else /* even */
3915 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3916
3917 /* move to correct place. */
3918 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3919
3920 draw_redraw ( vw );
3921
3922 /* save buffer as file. */
3923 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);
3924 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3925 if (error)
3926 {
3927 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3928 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3929 g_free ( msg );
3930 g_error_free (error);
3931 }
3932
3933 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3934 }
3935 }
3936
3937 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3938 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3939 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3940 vik_viewport_configure ( vw->viking_vvp );
3941 draw_update ( vw );
3942
3943 g_free ( name_of_file );
3944}
3945
3946static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3947{
3948 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3949 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3950
3951 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3952 gdouble zoom = pow (2, active-2 );
3953
3954 gdouble width_min, width_max, height_min, height_max;
3955 gint width, height;
3956
3957 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3958 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3959
3960 /* TODO: support for xzoom and yzoom values */
3961 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3962 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3963
3964 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3965 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3966
3967 gtk_spin_button_set_value ( width_spin, width );
3968 gtk_spin_button_set_value ( height_spin, height );
3969}
3970
3971static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3972{
3973 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3974
3975 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3976 gdouble zoom = pow (2, active-2 );
3977
3978 gchar *label_text;
3979 gdouble w, h;
3980 w = gtk_spin_button_get_value(width_spin) * zoom;
3981 h = gtk_spin_button_get_value(height_spin) * zoom;
3982 if (pass_along[4]) /* save many images; find TOTAL area covered */
3983 {
3984 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3985 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3986 }
3987 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3988 switch (dist_units) {
3989 case VIK_UNITS_DISTANCE_KILOMETRES:
3990 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3991 break;
3992 case VIK_UNITS_DISTANCE_MILES:
3993 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3994 break;
3995 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3996 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
3997 break;
3998 default:
3999 label_text = g_strdup_printf ("Just to keep the compiler happy");
4000 g_critical("Houston, we've had a problem. distance=%d", dist_units);
4001 }
4002
4003 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
4004 g_free ( label_text );
4005}
4006
4007typedef enum {
4008 VW_GEN_SINGLE_IMAGE,
4009 VW_GEN_DIRECTORY_OF_IMAGES,
4010 VW_GEN_KMZ_FILE,
4011} img_generation_t;
4012
4013/*
4014 * Get an allocated filename (or directory as specified)
4015 */
4016static gchar* draw_image_filename ( VikWindow *vw, img_generation_t img_gen )
4017{
4018 gchar *fn = NULL;
4019 if ( img_gen != VW_GEN_DIRECTORY_OF_IMAGES )
4020 {
4021 // Single file
4022 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save Image"),
4023 GTK_WINDOW(vw),
4024 GTK_FILE_CHOOSER_ACTION_SAVE,
4025 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4026 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4027 NULL);
4028 if ( last_folder_images_uri )
4029 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_images_uri );
4030
4031 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( dialog );
4032 /* Add filters */
4033 GtkFileFilter *filter;
4034 filter = gtk_file_filter_new ();
4035 gtk_file_filter_set_name ( filter, _("All") );
4036 gtk_file_filter_add_pattern ( filter, "*" );
4037 gtk_file_chooser_add_filter ( chooser, filter );
4038
4039 if ( img_gen == VW_GEN_KMZ_FILE ) {
4040 filter = gtk_file_filter_new ();
4041 gtk_file_filter_set_name ( filter, _("KMZ") );
4042 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4043 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4044 gtk_file_chooser_add_filter ( chooser, filter );
4045 gtk_file_chooser_set_filter ( chooser, filter );
4046 }
4047 else {
4048 filter = gtk_file_filter_new ();
4049 gtk_file_filter_set_name ( filter, _("JPG") );
4050 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4051 gtk_file_chooser_add_filter ( chooser, filter );
4052
4053 if ( !vw->draw_image_save_as_png )
4054 gtk_file_chooser_set_filter ( chooser, filter );
4055
4056 filter = gtk_file_filter_new ();
4057 gtk_file_filter_set_name ( filter, _("PNG") );
4058 gtk_file_filter_add_mime_type ( filter, "image/png");
4059 gtk_file_chooser_add_filter ( chooser, filter );
4060
4061 if ( vw->draw_image_save_as_png )
4062 gtk_file_chooser_set_filter ( chooser, filter );
4063 }
4064
4065 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4066 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4067
4068 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4069 g_free ( last_folder_images_uri );
4070 last_folder_images_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
4071
4072 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4073 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4074 if ( ! a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
4075 fn = NULL;
4076 }
4077 gtk_widget_destroy ( dialog );
4078 }
4079 else {
4080 // A directory
4081 // For some reason this method is only written to work in UTM...
4082 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4083 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4084 return fn;
4085 }
4086
4087 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4088 GTK_WINDOW(vw),
4089 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4090 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4091 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4092 NULL);
4093 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4094 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4095
4096 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4097 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4098 }
4099 gtk_widget_destroy ( dialog );
4100 }
4101 return fn;
4102}
4103
4104static void draw_to_image_file ( VikWindow *vw, img_generation_t img_gen )
4105{
4106 /* todo: default for answers inside VikWindow or static (thruout instance) */
4107 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4108 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4109 GTK_STOCK_CANCEL,
4110 GTK_RESPONSE_REJECT,
4111 GTK_STOCK_OK,
4112 GTK_RESPONSE_ACCEPT,
4113 NULL );
4114 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4115 GtkWidget *current_window_button;
4116 gpointer current_window_pass_along[7];
4117 GtkWidget *zoom_label, *zoom_combo;
4118 GtkWidget *total_size_label;
4119
4120 // only used for VW_GEN_DIRECTORY_OF_IMAGES
4121 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4122
4123 width_label = gtk_label_new ( _("Width (pixels):") );
4124 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4125 height_label = gtk_label_new ( _("Height (pixels):") );
4126 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4127#ifdef WINDOWS
4128 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4129#endif
4130 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4131 /* TODO: separate xzoom and yzoom factors */
4132 zoom_combo = create_zoom_combo_all_levels();
4133
4134 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4135 gint active = 2 + round ( log (mpp) / log (2) );
4136
4137 // Can we not hard code size here?
4138 if ( active > 17 )
4139 active = 17;
4140 if ( active < 0 )
4141 active = 0;
4142 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4143
4144 total_size_label = gtk_label_new ( NULL );
4145
4146 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4147 current_window_pass_along [0] = vw;
4148 current_window_pass_along [1] = width_spin;
4149 current_window_pass_along [2] = height_spin;
4150 current_window_pass_along [3] = zoom_combo;
4151 current_window_pass_along [4] = NULL; // Only for directory of tiles: width
4152 current_window_pass_along [5] = NULL; // Only for directory of tiles: height
4153 current_window_pass_along [6] = total_size_label;
4154 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4155
4156 GtkWidget *png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4157 GtkWidget *jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4158
4159 if ( img_gen == VW_GEN_KMZ_FILE ) {
4160 // Don't show image type selection if creating a KMZ (always JPG internally)
4161 // Start with viewable area by default
4162 draw_to_image_file_current_window_cb ( current_window_button, NULL, current_window_pass_along );
4163 } else {
4164 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4165 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4166 }
4167
4168 if ( ! vw->draw_image_save_as_png )
4169 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4170
4171 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4172 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4173 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4174 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4175#ifdef WINDOWS
4176 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4177#endif
4178 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4179 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4180 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4181
4182 if ( img_gen == VW_GEN_DIRECTORY_OF_IMAGES )
4183 {
4184 GtkWidget *tiles_width_label, *tiles_height_label;
4185
4186 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4187 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4188 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4189 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4190 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4191 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4192 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4193 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4194
4195 current_window_pass_along [4] = tiles_width_spin;
4196 current_window_pass_along [5] = tiles_height_spin;
4197 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4198 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4199 }
4200 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4201 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4202 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4203 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4204
4205 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4206
4207 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4208
4209 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4210
4211 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4212 {
4213 gtk_widget_hide ( GTK_WIDGET(dialog) );
4214
4215 gchar *fn = draw_image_filename ( vw, img_gen );
4216 if ( !fn )
4217 return;
4218
4219 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4220 gdouble zoom = pow (2, active_z-2 );
4221
4222 if ( img_gen == VW_GEN_SINGLE_IMAGE )
4223 save_image_file ( vw, fn,
4224 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4225 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4226 zoom,
4227 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4228 FALSE );
4229 else if ( img_gen == VW_GEN_KMZ_FILE ) {
4230 // Remove some viewport overlays as these aren't useful in KMZ file.
4231 gboolean restore_xhair = vik_viewport_get_draw_centermark ( vw->viking_vvp );
4232 if ( restore_xhair )
4233 vik_viewport_set_draw_centermark ( vw->viking_vvp, FALSE );
4234 gboolean restore_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
4235 if ( restore_scale )
4236 vik_viewport_set_draw_scale ( vw->viking_vvp, FALSE );
4237
4238 save_image_file ( vw,
4239 fn,
4240 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4241 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4242 zoom,
4243 FALSE, // JPG
4244 TRUE );
4245
4246 if ( restore_xhair )
4247 vik_viewport_set_draw_centermark ( vw->viking_vvp, TRUE );
4248 if ( restore_scale )
4249 vik_viewport_set_draw_scale ( vw->viking_vvp, TRUE );
4250 if ( restore_xhair || restore_scale )
4251 draw_update ( vw );
4252 }
4253 else {
4254 // NB is in UTM mode ATM
4255 save_image_dir ( vw, fn,
4256 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4257 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4258 zoom,
4259 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4260 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4261 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4262 }
4263
4264 g_free ( fn );
4265 }
4266 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4267}
4268
4269static void draw_to_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4270{
4271 if ( vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ) {
4272 a_dialog_error_msg ( GTK_WINDOW(vw), _("This feature is not available in UTM mode") );
4273 return;
4274 }
4275 // NB ATM This only generates a KMZ file with the current viewport image - intended mostly for map images [but will include any lines/icons from track & waypoints that are drawn]
4276 // (it does *not* include a full KML dump of every track, waypoint etc...)
4277 draw_to_image_file ( vw, VW_GEN_KMZ_FILE );
4278}
4279
4280static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4281{
4282 draw_to_image_file ( vw, VW_GEN_SINGLE_IMAGE );
4283}
4284
4285static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4286{
4287 draw_to_image_file ( vw, VW_GEN_DIRECTORY_OF_IMAGES );
4288}
4289
4290/**
4291 *
4292 */
4293static void import_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4294{
4295 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Open File"),
4296 GTK_WINDOW(vw),
4297 GTK_FILE_CHOOSER_ACTION_OPEN,
4298 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4299 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
4300 NULL);
4301
4302 GtkFileFilter *filter;
4303 filter = gtk_file_filter_new ();
4304 gtk_file_filter_set_name ( filter, _("KMZ") );
4305 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4306 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4307 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4308 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4309
4310 filter = gtk_file_filter_new ();
4311 gtk_file_filter_set_name( filter, _("All") );
4312 gtk_file_filter_add_pattern ( filter, "*" );
4313 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4314 // Default to any file - same as before open filters were added
4315 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4316
4317 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4318 gchar *fn = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
4319 // TODO convert ans value into readable explaination of failure...
4320 int ans = kmz_open_file ( fn, vw->viking_vvp, vw->viking_vlp );
4321 if ( ans )
4322 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to import %s."), fn );
4323
4324 draw_update ( vw );
4325 }
4326 gtk_widget_destroy ( dialog );
4327}
4328
4329static void print_cb ( GtkAction *a, VikWindow *vw )
4330{
4331 a_print(vw, vw->viking_vvp);
4332}
4333
4334/* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4335static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4336{
4337 const gchar *name = gtk_action_get_name(a);
4338 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4339 if ( tbutton )
4340 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4341
4342 VikViewportDrawMode drawmode;
4343 if (!g_strcmp0(name, "ModeUTM")) {
4344 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4345 }
4346 else if (!g_strcmp0(name, "ModeLatLon")) {
4347 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4348 }
4349 else if (!g_strcmp0(name, "ModeExpedia")) {
4350 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4351 }
4352 else if (!g_strcmp0(name, "ModeMercator")) {
4353 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4354 }
4355 else {
4356 g_critical("Houston, we've had a problem.");
4357 return;
4358 }
4359
4360 if ( !vw->only_updating_coord_mode_ui )
4361 {
4362 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4363 if ( olddrawmode != drawmode )
4364 {
4365 /* this takes care of coord mode too */
4366 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4367 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4368 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4369 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4370 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4371 }
4372 draw_update ( vw );
4373 }
4374 }
4375}
4376
4377static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4378{
4379 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4380 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4381 if ( !check_box )
4382 return;
4383 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4384 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4385 draw_update ( vw );
4386}
4387
4388static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4389{
4390 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4391 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4392 if ( !check_box )
4393 return;
4394 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4395 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4396 draw_update ( vw );
4397}
4398
4399static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4400{
4401 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4402 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4403 if ( !check_box )
4404 return;
4405 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4406 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4407 draw_update ( vw );
4408}
4409
4410static void set_bg_color ( GtkAction *a, VikWindow *vw )
4411{
4412 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4413 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4414 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4415 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4416 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4417 {
4418 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4419 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4420 draw_update ( vw );
4421 }
4422 g_free ( color );
4423 gtk_widget_destroy ( colorsd );
4424}
4425
4426static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4427{
4428 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4429 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4430 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4431 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4432 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4433 {
4434 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4435 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4436 draw_update ( vw );
4437 }
4438 g_free ( color );
4439 gtk_widget_destroy ( colorsd );
4440}
4441
4442
4443/***********************************************************************************************
4444 ** GUI Creation
4445 ***********************************************************************************************/
4446
4447static GtkActionEntry entries[] = {
4448 { "File", NULL, N_("_File"), 0, 0, 0 },
4449 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4450 { "View", NULL, N_("_View"), 0, 0, 0 },
4451 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4452 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4453 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4454 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4455 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4456 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4457 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4458
4459 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
4460 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4461 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4462 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
4463 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4464 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
4465 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
4466 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4467 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
4468 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
4469#ifdef VIK_CONFIG_OPENSTREETMAP
4470 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
4471 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
4472#endif
4473#ifdef VIK_CONFIG_GEOCACHES
4474 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
4475#endif
4476#ifdef VIK_CONFIG_GEOTAG
4477 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
4478#endif
4479 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
4480#ifdef VIK_CONFIG_GEONAMES
4481 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4482#endif
4483 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
4484 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
4485 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
4486#ifdef HAVE_ZIP_H
4487 { "ImportKMZ", GTK_STOCK_CONVERT, N_("Import KMZ _Map File..."), NULL, N_("Import a KMZ file"), (GCallback)import_kmz_file_cb },
4488 { "GenKMZ", GTK_STOCK_DND, N_("Generate _KMZ Map File..."), NULL, N_("Generate a KMZ file with an overlay of the current view"), (GCallback)draw_to_kmz_file_cb },
4489#endif
4490 { "GenImg", GTK_STOCK_FILE, N_("_Generate Image File..."), NULL, N_("Save a snapshot of the workspace into a file"), (GCallback)draw_to_image_file_cb },
4491 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
4492 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
4493 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4494 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4495
4496 { "GoBack", GTK_STOCK_GO_BACK, N_("Go to the Pre_vious Location"), NULL, N_("Go to the previous location"), (GCallback)draw_goto_back_and_forth },
4497 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
4498 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
4499 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
4500 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
4501 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
4502 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
4503 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4504 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4505 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4506 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4507 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
4508 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4509 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4510 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4511 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
4512 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
4513
4514 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4515 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4516 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4517 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
4518 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
4519 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
4520 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
4521 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4522 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
4523 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
4524 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
4525
4526 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4527 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
4528};
4529
4530static GtkActionEntry debug_entries[] = {
4531 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
4532 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
4533};
4534
4535static GtkActionEntry entries_gpsbabel[] = {
4536 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4537};
4538
4539static GtkActionEntry entries_geojson[] = {
4540 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4541};
4542
4543/* Radio items */
4544static GtkRadioActionEntry mode_entries[] = {
4545 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4546 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4547 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4548 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4549};
4550
4551static GtkToggleActionEntry toggle_entries[] = {
4552 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4553 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4554 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
4555 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4556 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4557 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4558 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4559 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4560};
4561
4562// This must match the toggle entries order above
4563static gpointer toggle_entries_toolbar_cb[] = {
4564 (GCallback)tb_set_draw_scale,
4565 (GCallback)tb_set_draw_centermark,
4566 (GCallback)tb_set_draw_highlight,
4567 (GCallback)tb_full_screen_cb,
4568 (GCallback)tb_view_side_panel_cb,
4569 (GCallback)tb_view_statusbar_cb,
4570 (GCallback)tb_view_toolbar_cb,
4571 (GCallback)tb_view_main_menu_cb,
4572};
4573
4574#include "menu.xml.h"
4575static void window_create_ui( VikWindow *window )
4576{
4577 GtkUIManager *uim;
4578 GtkActionGroup *action_group;
4579 GtkAccelGroup *accel_group;
4580 GError *error;
4581 guint i, j, mid;
4582 GtkIconFactory *icon_factory;
4583 GtkIconSet *icon_set;
4584 GtkRadioActionEntry *tools = NULL, *radio;
4585 guint ntools;
4586
4587 uim = gtk_ui_manager_new ();
4588 window->uim = uim;
4589
4590 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4591 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4592 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4593 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4594
4595 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4596 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4597 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4598 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4599
4600 error = NULL;
4601 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4602 g_error_free (error);
4603 exit (1);
4604 }
4605
4606 action_group = gtk_action_group_new ("MenuActions");
4607 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4608 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4609 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4610 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4611 if ( vik_debug ) {
4612 if ( gtk_ui_manager_add_ui_from_string ( uim,
4613 "<ui><menubar name='MainMenu'><menu action='Help'>"
4614 "<menuitem action='MapCacheInfo'/>"
4615 "<menuitem action='BackForwardInfo'/>"
4616 "</menu></menubar></ui>",
4617 -1, NULL ) ) {
4618 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4619 }
4620 }
4621
4622 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4623 if ( entries[i].callback )
4624 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4625 }
4626
4627 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4628 g_print ( "Broken entries definitions\n" );
4629 exit (1);
4630 }
4631 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4632 if ( toggle_entries_toolbar_cb[i] )
4633 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4634 }
4635
4636 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4637 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4638 }
4639
4640 // Use this to see if GPSBabel is available:
4641 if ( a_babel_available () ) {
4642 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4643 if ( gtk_ui_manager_add_ui_from_string ( uim,
4644 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4645 -1, &error ) )
4646 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4647 }
4648
4649 // GeoJSON import capability
4650 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4651 if ( gtk_ui_manager_add_ui_from_string ( uim,
4652 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4653 -1, &error ) )
4654 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4655 }
4656
4657 icon_factory = gtk_icon_factory_new ();
4658 gtk_icon_factory_add_default (icon_factory);
4659
4660 register_vik_icons(icon_factory);
4661
4662 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4663 // so that it can be applied to the UI in one action group add function call below
4664 ntools = 0;
4665 for (i=0; i<window->vt->n_tools; i++) {
4666 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4667 radio = &tools[ntools];
4668 ntools++;
4669 *radio = window->vt->tools[i].ti.radioActionEntry;
4670 radio->value = ntools;
4671 }
4672
4673 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4674 GtkActionEntry action;
4675 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4676 vik_layer_get_interface(i)->name,
4677 vik_layer_get_interface(i)->name,
4678 GTK_UI_MANAGER_MENUITEM, FALSE);
4679
4680 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4681 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4682 gtk_icon_set_unref (icon_set);
4683
4684 action.name = vik_layer_get_interface(i)->name;
4685 action.stock_id = vik_layer_get_interface(i)->name;
4686 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4687 action.accelerator = vik_layer_get_interface(i)->accelerator;
4688 action.tooltip = NULL;
4689 action.callback = (GCallback)menu_addlayer_cb;
4690 gtk_action_group_add_actions(action_group, &action, 1, window);
4691
4692 g_free ( (gchar*)action.label );
4693
4694 if ( vik_layer_get_interface(i)->tools_count ) {
4695 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4696 }
4697
4698 // Further tool copying for to apply to the UI, also apply menu UI setup
4699 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4700 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4701 radio = &tools[ntools];
4702 ntools++;
4703
4704 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4705 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4706 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4707 GTK_UI_MANAGER_MENUITEM, FALSE);
4708
4709 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4710 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4711
4712 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4713 // Overwrite with actual number to use
4714 radio->value = ntools;
4715 }
4716
4717 GtkActionEntry action_dl;
4718 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4719 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4720 vik_layer_get_interface(i)->name,
4721 layername,
4722 GTK_UI_MANAGER_MENUITEM, FALSE);
4723 g_free (layername);
4724
4725 // For default layers use action names of the form 'Layer<LayerName>'
4726 // This is to avoid clashing with just the layer name used above for the tool actions
4727 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4728 action_dl.stock_id = NULL;
4729 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4730 action_dl.accelerator = NULL;
4731 action_dl.tooltip = NULL;
4732 action_dl.callback = (GCallback)layer_defaults_cb;
4733 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4734 g_free ( (gchar*)action_dl.name );
4735 g_free ( (gchar*)action_dl.label );
4736 }
4737 g_object_unref (icon_factory);
4738
4739 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4740 g_free(tools);
4741
4742 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4743
4744 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4745 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4746 GtkAction *action = gtk_action_group_get_action(action_group,
4747 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4748 g_object_set(action, "sensitive", FALSE, NULL);
4749 }
4750 }
4751
4752 // This is done last so we don't need to track the value of mid anymore
4753 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4754
4755 window->action_group = action_group;
4756
4757 accel_group = gtk_ui_manager_get_accel_group (uim);
4758 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4759 gtk_ui_manager_ensure_update (uim);
4760
4761 setup_recent_files(window);
4762}
4763
4764
4765// TODO - add method to add tool icons defined from outside this file
4766// and remove the reverse dependency on icon definition from this file
4767static struct {
4768 const GdkPixdata *data;
4769 gchar *stock_id;
4770} stock_icons[] = {
4771 { &mover_22_pixbuf, "vik-icon-pan" },
4772 { &zoom_18_pixbuf, "vik-icon-zoom" },
4773 { &ruler_18_pixbuf, "vik-icon-ruler" },
4774 { &select_18_pixbuf, "vik-icon-select" },
4775 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4776 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4777 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4778 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4779 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4780 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4781 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4782 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4783 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4784 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4785 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4786};
4787
4788static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4789
4790static void
4791register_vik_icons (GtkIconFactory *icon_factory)
4792{
4793 GtkIconSet *icon_set;
4794 gint i;
4795
4796 for (i = 0; i < n_stock_icons; i++) {
4797 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4798 stock_icons[i].data, FALSE, NULL ));
4799 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4800 gtk_icon_set_unref (icon_set);
4801 }
4802}
4803
4804gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4805{
4806 return vw->selected_vtl;
4807}
4808
4809void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4810{
4811 vw->selected_vtl = vtl;
4812 vw->containing_vtl = vtl;
4813 /* Clear others */
4814 vw->selected_track = NULL;
4815 vw->selected_tracks = NULL;
4816 vw->selected_waypoint = NULL;
4817 vw->selected_waypoints = NULL;
4818 // Set highlight thickness
4819 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4820}
4821
4822GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4823{
4824 return vw->selected_tracks;
4825}
4826
4827void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4828{
4829 vw->selected_tracks = ght;
4830 vw->containing_vtl = vtl;
4831 /* Clear others */
4832 vw->selected_vtl = NULL;
4833 vw->selected_track = NULL;
4834 vw->selected_waypoint = NULL;
4835 vw->selected_waypoints = NULL;
4836 // Set highlight thickness
4837 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4838}
4839
4840gpointer vik_window_get_selected_track ( VikWindow *vw )
4841{
4842 return vw->selected_track;
4843}
4844
4845void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4846{
4847 vw->selected_track = vt;
4848 vw->containing_vtl = vtl;
4849 /* Clear others */
4850 vw->selected_vtl = NULL;
4851 vw->selected_tracks = NULL;
4852 vw->selected_waypoint = NULL;
4853 vw->selected_waypoints = NULL;
4854 // Set highlight thickness
4855 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4856}
4857
4858GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4859{
4860 return vw->selected_waypoints;
4861}
4862
4863void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4864{
4865 vw->selected_waypoints = ght;
4866 vw->containing_vtl = vtl;
4867 /* Clear others */
4868 vw->selected_vtl = NULL;
4869 vw->selected_track = NULL;
4870 vw->selected_tracks = NULL;
4871 vw->selected_waypoint = NULL;
4872}
4873
4874gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4875{
4876 return vw->selected_waypoint;
4877}
4878
4879void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4880{
4881 vw->selected_waypoint = vwp;
4882 vw->containing_vtl = vtl;
4883 /* Clear others */
4884 vw->selected_vtl = NULL;
4885 vw->selected_track = NULL;
4886 vw->selected_tracks = NULL;
4887 vw->selected_waypoints = NULL;
4888}
4889
4890gboolean vik_window_clear_highlight ( VikWindow *vw )
4891{
4892 gboolean need_redraw = FALSE;
4893 if ( vw->selected_vtl != NULL ) {
4894 vw->selected_vtl = NULL;
4895 need_redraw = TRUE;
4896 }
4897 if ( vw->selected_track != NULL ) {
4898 vw->selected_track = NULL;
4899 need_redraw = TRUE;
4900 }
4901 if ( vw->selected_tracks != NULL ) {
4902 vw->selected_tracks = NULL;
4903 need_redraw = TRUE;
4904 }
4905 if ( vw->selected_waypoint != NULL ) {
4906 vw->selected_waypoint = NULL;
4907 need_redraw = TRUE;
4908 }
4909 if ( vw->selected_waypoints != NULL ) {
4910 vw->selected_waypoints = NULL;
4911 need_redraw = TRUE;
4912 }
4913 return need_redraw;
4914}
4915
4916/**
4917 * May return NULL if the window no longer exists
4918 */
4919GThread *vik_window_get_thread ( VikWindow *vw )
4920{
4921 if ( vw )
4922 return vw->thread;
4923 return NULL;
4924}