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