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