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