]> git.street.me.uk Git - andy/viking.git/blame - src/vikwindow.c
Really empty GPS realtime layers.
[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;
941aa6e9
AF
1208 gdk_draw_drawable (pass_along[0], pass_along[1],
1209 pass_along[2], 0, 0, 0, 0, -1, -1);
1210 draw_buf_done = TRUE;
941aa6e9
AF
1211 return FALSE;
1212}
1213
1214
1215/* Mouse event handlers ************************************************************************/
1216
b57126a3
GB
1217static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1218{
1219 /* set panning origin */
b71eff77 1220 vw->pan_move = FALSE;
b57126a3
GB
1221 vw->pan_x = (gint) event->x;
1222 vw->pan_y = (gint) event->y;
1223}
1224
941aa6e9
AF
1225static void draw_click (VikWindow *vw, GdkEventButton *event)
1226{
165d30aa 1227 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
941aa6e9
AF
1228
1229 /* middle button pressed. we reserve all middle button and scroll events
1230 * for panning and zooming; tools only get left/right/movement
1231 */
1232 if ( event->button == 2) {
ef5e8132
RN
1233 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1234 // Tool still may need to do something (such as disable something)
1235 toolbox_click(vw->vt, event);
b57126a3 1236 vik_window_pan_click ( vw, event );
941aa6e9
AF
1237 }
1238 else {
1239 toolbox_click(vw->vt, event);
1240 }
1241}
1242
b57126a3
GB
1243static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1244{
1245 if ( vw->pan_x != -1 ) {
b71eff77
JJ
1246 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1247 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
b71eff77
JJ
1248 vw->pan_move = TRUE;
1249 vw->pan_x = event->x;
1250 vw->pan_y = event->y;
a7abaae5 1251 draw_update ( vw );
b57126a3
GB
1252 }
1253}
1254
7c8bddff
RN
1255/**
1256 * get_location_strings:
1257 *
1258 * Utility function to get positional strings for the given location
1259 * lat and lon strings will get allocated and so need to be freed after use
1260 */
1261static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1262{
1263 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1264 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1265 // ZONE[N|S] EASTING NORTHING
1266 *lat = g_malloc(4*sizeof(gchar));
1267 // NB zone is stored in a char but is an actual number
1268 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1269 *lon = g_malloc(16*sizeof(gchar));
1270 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1271 }
1272 else {
1273 struct LatLon ll;
1274 a_coords_utm_to_latlon ( &utm, &ll );
1275 a_coords_latlon_to_string ( &ll, lat, lon );
1276 }
1277}
1278
941aa6e9
AF
1279static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1280{
1281 static VikCoord coord;
1282 static struct UTM utm;
a58aaed4
GB
1283 #define BUFFER_SIZE 50
1284 static char pointer_buf[BUFFER_SIZE];
1285 gchar *lat = NULL, *lon = NULL;
071da616 1286 gint16 alt;
228213c5
QT
1287 gdouble zoom;
1288 VikDemInterpol interpol_method;
941aa6e9 1289
fe27d6d2
SB
1290 /* This is a hack, but work far the best, at least for single pointer systems.
1291 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1292 gint x, y;
1293 gdk_window_get_pointer (event->window, &x, &y, NULL);
1294 event->x = x;
1295 event->y = y;
1296
576cbd17 1297 toolbox_move(vw->vt, event);
941aa6e9
AF
1298
1299 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1300 vik_coord_to_utm ( &coord, &utm );
1a6e7d70 1301
7c8bddff 1302 get_location_strings ( vw, utm, &lat, &lon );
1a6e7d70 1303
228213c5
QT
1304 /* Change interpolate method according to scale */
1305 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1306 if (zoom > 2.0)
1307 interpol_method = VIK_DEM_INTERPOL_NONE;
1308 else if (zoom >= 1.0)
1309 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1310 else
1311 interpol_method = VIK_DEM_INTERPOL_BEST;
8c5f013a
RN
1312 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1313 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1314 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1315 else
6c20e59a 1316 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
8c5f013a 1317 }
071da616 1318 else
a58aaed4
GB
1319 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1320 g_free (lat);
1321 lat = NULL;
1322 g_free (lon);
1323 lon = NULL;
4efc10ca 1324 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
941aa6e9 1325
b57126a3 1326 vik_window_pan_move ( vw, event );
fe27d6d2
SB
1327
1328 /* This is recommended by the GTK+ documentation, but does not work properly.
1329 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1330 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1331 */
1332 /* gdk_event_request_motions ( event ); */
b57126a3
GB
1333}
1334
beb9d63a
RN
1335/**
1336 * Action the single click after a small timeout
1337 * If a double click has occurred then this will do nothing
1338 */
1339static gboolean vik_window_pan_timeout (VikWindow *vw)
1340{
1341 if ( ! vw->single_click_pending ) {
1342 // Double click happened, so don't do anything
1343 return FALSE;
1344 }
1345
1346 /* set panning origin */
1347 vw->pan_move = FALSE;
1348 vw->single_click_pending = FALSE;
1349 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1350 draw_update ( vw );
1351
1352 // Really turn off the pan moving!!
1353 vw->pan_x = vw->pan_y = -1;
1354 return FALSE;
1355}
1356
b57126a3
GB
1357static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1358{
3a318c69
RN
1359 gboolean do_draw = TRUE;
1360
beb9d63a
RN
1361 if ( vw->pan_move == FALSE ) {
1362 vw->single_click_pending = !vw->single_click_pending;
1363
1364 if ( vw->single_click_pending ) {
1365 // Store offset to use
1366 vw->delayed_pan_x = vw->pan_x;
1367 vw->delayed_pan_y = vw->pan_y;
1368 // Get double click time
1369 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
b9f777e4 1370 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
beb9d63a
RN
1371 g_value_init ( &dct, G_TYPE_INT );
1372 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1373 // Give chance for a double click to occur
1374 gint timer = g_value_get_int ( &dct ) + 50;
1375 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
3a318c69 1376 do_draw = FALSE;
beb9d63a
RN
1377 }
1378 else {
1379 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1380 }
1381 }
1382 else {
b57126a3
GB
1383 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1384 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
beb9d63a
RN
1385 }
1386
a7abaae5 1387 vw->pan_move = FALSE;
b57126a3 1388 vw->pan_x = vw->pan_y = -1;
3a318c69
RN
1389 if ( do_draw )
1390 draw_update ( vw );
941aa6e9
AF
1391}
1392
1393static void draw_release ( VikWindow *vw, GdkEventButton *event )
1394{
165d30aa
EB
1395 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1396
941aa6e9 1397 if ( event->button == 2 ) { /* move / pan */
ef5e8132
RN
1398 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1399 // Tool still may need to do something (such as reenable something)
1400 toolbox_release(vw->vt, event);
1401 vik_window_pan_release ( vw, event );
941aa6e9
AF
1402 }
1403 else {
1404 toolbox_release(vw->vt, event);
1405 }
1406}
1407
1408static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1409{
d1a45556
JN
1410 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1411 if ( modifiers == GDK_CONTROL_MASK ) {
8c721f83
EB
1412 /* control == pan up & down */
1413 if ( event->direction == GDK_SCROLL_UP )
1414 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1415 else
1416 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
1417 } else if ( modifiers == GDK_SHIFT_MASK ) {
1418 /* shift == pan left & right */
8c721f83
EB
1419 if ( event->direction == GDK_SCROLL_UP )
1420 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1421 else
1422 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 1423 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
15ff5402
RN
1424 // This zoom is on the center position
1425 if ( event->direction == GDK_SCROLL_UP )
1426 vik_viewport_zoom_in (vw->viking_vvp);
1427 else
1428 vik_viewport_zoom_out (vw->viking_vvp);
1429 } else {
1430 /* make sure mouse is still over the same point on the map when we zoom */
3c575a4a
JN
1431 VikCoord coord;
1432 gint x, y;
1433 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1434 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1435 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1436 if ( event->direction == GDK_SCROLL_UP )
8c721f83 1437 vik_viewport_zoom_in (vw->viking_vvp);
3c575a4a 1438 else
8c721f83 1439 vik_viewport_zoom_out(vw->viking_vvp);
3c575a4a
JN
1440 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1441 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1442 center_y + (y - event->y) );
8c721f83
EB
1443 }
1444
941aa6e9
AF
1445 draw_update(vw);
1446}
1447
1448
1449
1450/********************************************************************************
1451 ** Ruler tool code
1452 ********************************************************************************/
e4847ce9
AF
1453static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1454{
e4847ce9
AF
1455 PangoLayout *pl;
1456 gchar str[128];
0dff88ea
AF
1457 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1458 GdkGC *thickgc = gdk_gc_new(d);
1459
e4847ce9
AF
1460 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1461 gdouble dx = (x2-x1)/len*10;
1462 gdouble dy = (y2-y1)/len*10;
0da53bd9
GB
1463 gdouble c = cos(DEG2RAD(15.0));
1464 gdouble s = sin(DEG2RAD(15.0));
0dff88ea 1465 gdouble angle;
15614495 1466 gdouble baseangle = 0;
0dff88ea 1467 gint i;
e4847ce9 1468
0dff88ea 1469 /* draw line with arrow ends */
d9ffd267
EB
1470 {
1471 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1472 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1473 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1474 }
1475
15614495
AF
1476 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1477 gdk_draw_line(d, gc, x1, y1, x2, y2);
d9ffd267 1478
e4847ce9
AF
1479 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1480 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1481 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1482 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1483 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1484 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1485
0dff88ea
AF
1486 /* draw compass */
1487#define CR 80
1488#define CW 4
15614495 1489
9a3538f5 1490 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
0dff88ea
AF
1491
1492 {
1493 GdkColor color;
1494 gdk_gc_copy(thickgc, gc);
1495 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1496 gdk_color_parse("#2255cc", &color);
1497 gdk_gc_set_rgb_fg_color(thickgc, &color);
1498 }
0da53bd9 1499 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 1500
e4847ce9 1501
0dff88ea
AF
1502 gdk_gc_copy(thickgc, gc);
1503 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1504 for (i=0; i<180; i++) {
0da53bd9
GB
1505 c = cos(DEG2RAD(i)*2 + baseangle);
1506 s = sin(DEG2RAD(i)*2 + baseangle);
e4847ce9 1507
0dff88ea
AF
1508 if (i%5) {
1509 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1510 } else {
1511 gdouble ticksize = 2*CW;
1512 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1513 }
e4847ce9 1514 }
0dff88ea
AF
1515
1516 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1517 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1518 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
15614495
AF
1519 c = (CR+CW*2)*cos(baseangle);
1520 s = (CR+CW*2)*sin(baseangle);
1521 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1522 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
0dff88ea
AF
1523
1524 /* draw labels */
1525#define LABEL(x, y, w, h) { \
1526 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1527 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1528 gdk_draw_layout(d, gc, (x), (y), pl); }
1529 {
1530 gint wd, hd, xd, yd;
1531 gint wb, hb, xb, yb;
1532
1533 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
ff37db21 1534 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
0dff88ea
AF
1535 pango_layout_set_text(pl, "N", -1);
1536 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1537
1538 /* draw label with distance */
6f9336aa
RN
1539 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1540 switch (dist_units) {
1541 case VIK_UNITS_DISTANCE_KILOMETRES:
1542 if (distance >= 1000 && distance < 100000) {
b22233bd 1543 g_sprintf(str, "%3.2f km", distance/1000.0);
6f9336aa 1544 } else if (distance < 1000) {
b22233bd 1545 g_sprintf(str, "%d m", (int)distance);
6f9336aa 1546 } else {
b22233bd 1547 g_sprintf(str, "%d km", (int)distance/1000);
6f9336aa
RN
1548 }
1549 break;
1550 case VIK_UNITS_DISTANCE_MILES:
433b3f7f 1551 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
b22233bd 1552 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
433b3f7f 1553 } else if (distance < VIK_MILES_TO_METERS(1)) {
b22233bd 1554 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
6f9336aa 1555 } else {
b22233bd
RN
1556 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1557 }
1558 break;
1559 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1560 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1561 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1562 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1563 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1564 } else {
1565 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
6f9336aa
RN
1566 }
1567 break;
1568 default:
1569 g_critical("Houston, we've had a problem. distance=%d", dist_units);
0dff88ea 1570 }
6f9336aa 1571
0dff88ea
AF
1572 pango_layout_set_text(pl, str, -1);
1573
1574 pango_layout_get_pixel_size ( pl, &wd, &hd );
1575 if (dy>0) {
1576 xd = (x1+x2)/2 + dy;
1577 yd = (y1+y2)/2 - hd/2 - dx;
1578 } else {
1579 xd = (x1+x2)/2 - dy;
1580 yd = (y1+y2)/2 - hd/2 + dx;
1581 }
024f32c1
EB
1582
1583 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1584 xd = x2 + 10;
1585 yd = y2 - 5;
1586 }
1587
0dff88ea
AF
1588 LABEL(xd, yd, wd, hd);
1589
1590 /* draw label with bearing */
0da53bd9 1591 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
0dff88ea
AF
1592 pango_layout_set_text(pl, str, -1);
1593 pango_layout_get_pixel_size ( pl, &wb, &hb );
1594 xb = x1 + CR*cos(angle-M_PI_2);
1595 yb = y1 + CR*sin(angle-M_PI_2);
1596
024f32c1
EB
1597 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1598 xb = x2 + 10;
1599 yb = y2 + 10;
1600 }
1601
0dff88ea
AF
1602 {
1603 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1604 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1605 xb = xd + wd + 5;
1606 }
1607 }
1608 LABEL(xb, yb, wb, hb);
e4847ce9 1609 }
03d62e57 1610#undef LABEL
e4847ce9 1611
024f32c1 1612 g_object_unref ( G_OBJECT ( pl ) );
0dff88ea
AF
1613 g_object_unref ( G_OBJECT ( labgc ) );
1614 g_object_unref ( G_OBJECT ( thickgc ) );
e4847ce9
AF
1615}
1616
941aa6e9
AF
1617typedef struct {
1618 VikWindow *vw;
1619 VikViewport *vvp;
1620 gboolean has_oldcoord;
1621 VikCoord oldcoord;
1622} ruler_tool_state_t;
03d62e57 1623
941aa6e9 1624static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
03d62e57 1625{
941aa6e9
AF
1626 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1627 s->vw = vw;
1628 s->vvp = vvp;
1629 s->has_oldcoord = FALSE;
1630 return s;
03d62e57
AF
1631}
1632
941aa6e9 1633static void ruler_destroy (ruler_tool_state_t *s)
50a14534 1634{
941aa6e9
AF
1635 g_free(s);
1636}
50a14534 1637
941aa6e9
AF
1638static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1639{
1640 struct LatLon ll;
1641 VikCoord coord;
1642 gchar *temp;
1643 if ( event->button == 1 ) {
a58aaed4 1644 gchar *lat=NULL, *lon=NULL;
941aa6e9
AF
1645 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1646 vik_coord_to_latlon ( &coord, &ll );
a58aaed4 1647 a_coords_latlon_to_string ( &ll, &lat, &lon );
941aa6e9 1648 if ( s->has_oldcoord ) {
6f9336aa
RN
1649 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1650 switch (dist_units) {
1651 case VIK_UNITS_DISTANCE_KILOMETRES:
b22233bd
RN
1652 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1653 break;
6f9336aa 1654 case VIK_UNITS_DISTANCE_MILES:
b22233bd
RN
1655 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1656 break;
1657 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1658 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1659 break;
6f9336aa 1660 default:
b22233bd
RN
1661 temp = g_strdup_printf ("Just to keep the compiler happy");
1662 g_critical("Houston, we've had a problem. distance=%d", dist_units);
6f9336aa
RN
1663 }
1664
941aa6e9
AF
1665 s->has_oldcoord = FALSE;
1666 }
1667 else {
a58aaed4 1668 temp = g_strdup_printf ( "%s %s", lat, lon );
941aa6e9
AF
1669 s->has_oldcoord = TRUE;
1670 }
50a14534 1671
4efc10ca 1672 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
941aa6e9 1673 g_free ( temp );
e4847ce9 1674
941aa6e9
AF
1675 s->oldcoord = coord;
1676 }
1677 else {
1678 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1679 draw_update ( s->vw );
1680 }
1681 return VIK_LAYER_TOOL_ACK;
1682}
e4847ce9 1683
dc2c040e 1684static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
941aa6e9
AF
1685{
1686 VikViewport *vvp = s->vvp;
1687 VikWindow *vw = s->vw;
1688
1689 struct LatLon ll;
1690 VikCoord coord;
1691 gchar *temp;
1692
1693 if ( s->has_oldcoord ) {
1694 int oldx, oldy, w1, h1, w2, h2;
1695 static GdkPixmap *buf = NULL;
a58aaed4 1696 gchar *lat=NULL, *lon=NULL;
941aa6e9
AF
1697 w1 = vik_viewport_get_width(vvp);
1698 h1 = vik_viewport_get_height(vvp);
1699 if (!buf) {
9b082b39 1700 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
941aa6e9
AF
1701 }
1702 gdk_drawable_get_size(buf, &w2, &h2);
1703 if (w1 != w2 || h1 != h2) {
1704 g_object_unref ( G_OBJECT ( buf ) );
9b082b39 1705 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
e4847ce9 1706 }
e4847ce9 1707
941aa6e9
AF
1708 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1709 vik_coord_to_latlon ( &coord, &ll );
1710 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1711
ff37db21 1712 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
941aa6e9 1713 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
ff37db21 1714 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
1715 if (draw_buf_done) {
1716 static gpointer pass_along[3];
9b082b39 1717 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
ff37db21 1718 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
941aa6e9
AF
1719 pass_along[2] = buf;
1720 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1721 draw_buf_done = FALSE;
1722 }
a58aaed4 1723 a_coords_latlon_to_string(&ll, &lat, &lon);
6f9336aa
RN
1724 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1725 switch (dist_units) {
1726 case VIK_UNITS_DISTANCE_KILOMETRES:
1727 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1728 break;
1729 case VIK_UNITS_DISTANCE_MILES:
433b3f7f 1730 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
6f9336aa 1731 break;
b22233bd
RN
1732 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1733 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1734 break;
6f9336aa
RN
1735 default:
1736 temp = g_strdup_printf ("Just to keep the compiler happy");
1737 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1738 }
4efc10ca 1739 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
941aa6e9 1740 g_free ( temp );
acaf7113 1741 }
941aa6e9
AF
1742 return VIK_LAYER_TOOL_ACK;
1743}
50a14534 1744
941aa6e9
AF
1745static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1746{
1747 return VIK_LAYER_TOOL_ACK;
50a14534
EB
1748}
1749
941aa6e9 1750static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
50a14534 1751{
941aa6e9 1752 draw_update ( s->vw );
50a14534
EB
1753}
1754
0eb26799
RN
1755static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1756{
1757 if (event->keyval == GDK_Escape) {
1758 s->has_oldcoord = FALSE;
1759 ruler_deactivate ( vl, s );
1760 return TRUE;
1761 }
6b59f63d 1762 // Regardless of whether we used it, return false so other GTK things may use it
0eb26799
RN
1763 return FALSE;
1764}
1765
6b59f63d
RN
1766static VikToolInterface ruler_tool =
1767 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1768 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
941aa6e9
AF
1769 (VikToolConstructorFunc) ruler_create,
1770 (VikToolDestructorFunc) ruler_destroy,
1771 (VikToolActivationFunc) NULL,
1772 (VikToolActivationFunc) ruler_deactivate,
1773 (VikToolMouseFunc) ruler_click,
dc2c040e 1774 (VikToolMouseMoveFunc) ruler_move,
f2f2f7bf 1775 (VikToolMouseFunc) ruler_release,
0eb26799 1776 (VikToolKeyFunc) ruler_key_press,
ef5e8132 1777 FALSE,
f2f2f7bf 1778 GDK_CURSOR_IS_PIXMAP,
63959706
RN
1779 &cursor_ruler_pixbuf,
1780 NULL };
941aa6e9
AF
1781/*** end ruler code ********************************************************/
1782
1783
1784
1785/********************************************************************************
1786 ** Zoom tool code
1787 ********************************************************************************/
2eea9a36
RN
1788
1789typedef struct {
1790 VikWindow *vw;
1791 GdkPixmap *pixmap;
1792 // Track zoom bounds for zoom tool with shift modifier:
1793 gboolean bounds_active;
1794 gint start_x;
1795 gint start_y;
1796} zoom_tool_state_t;
1797
1798/*
1799 * In case the screen size has changed
1800 */
1801static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1802{
1803 int w1, h1, w2, h2;
1804
1805 // Allocate a drawing area the size of the viewport
1806 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1807 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1808
1809 if ( !zts->pixmap ) {
1810 // Totally new
9b082b39 1811 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
2eea9a36
RN
1812 }
1813
1814 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1815
1816 if ( w1 != w2 || h1 != h2 ) {
1817 // Has changed - delete and recreate with new values
1818 g_object_unref ( G_OBJECT ( zts->pixmap ) );
9b082b39 1819 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
2eea9a36
RN
1820 }
1821}
1822
941aa6e9 1823static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
50a14534 1824{
2eea9a36
RN
1825 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1826 zts->vw = vw;
1827 zts->pixmap = NULL;
1828 zts->start_x = 0;
1829 zts->start_y = 0;
1830 zts->bounds_active = FALSE;
1831 return zts;
50a14534
EB
1832}
1833
2eea9a36 1834static void zoomtool_destroy ( zoom_tool_state_t *zts)
50a14534 1835{
2eea9a36
RN
1836 if ( zts->pixmap )
1837 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1838 g_free(zts);
1839}
1840
1841static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1842{
1843 zts->vw->modified = TRUE;
15ff5402
RN
1844 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1845
1846 VikCoord coord;
1847 gint x, y;
2eea9a36
RN
1848 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1849 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1850
1851 gboolean skip_update = FALSE;
1852
1853 zts->bounds_active = FALSE;
15ff5402
RN
1854
1855 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1856 // This zoom is on the center position
2eea9a36 1857 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
15ff5402 1858 if ( event->button == 1 )
2eea9a36 1859 vik_viewport_zoom_in (zts->vw->viking_vvp);
15ff5402 1860 else if ( event->button == 3 )
2eea9a36 1861 vik_viewport_zoom_out (zts->vw->viking_vvp);
15ff5402 1862 }
130ac805
RN
1863 else if ( modifiers == GDK_CONTROL_MASK ) {
1864 // This zoom is to recenter on the mouse position
2eea9a36 1865 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
130ac805 1866 if ( event->button == 1 )
2eea9a36 1867 vik_viewport_zoom_in (zts->vw->viking_vvp);
130ac805 1868 else if ( event->button == 3 )
2eea9a36
RN
1869 vik_viewport_zoom_out (zts->vw->viking_vvp);
1870 }
1871 else if ( modifiers == GDK_SHIFT_MASK ) {
1872 // Get start of new zoom bounds
1873 if ( event->button == 1 ) {
1874 zts->bounds_active = TRUE;
1875 zts->start_x = (gint) event->x;
1876 zts->start_y = (gint) event->y;
1877 skip_update = TRUE;
1878 }
130ac805 1879 }
15ff5402
RN
1880 else {
1881 /* make sure mouse is still over the same point on the map when we zoom */
2eea9a36 1882 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
15ff5402 1883 if ( event->button == 1 )
2eea9a36 1884 vik_viewport_zoom_in (zts->vw->viking_vvp);
15ff5402 1885 else if ( event->button == 3 )
2eea9a36
RN
1886 vik_viewport_zoom_out(zts->vw->viking_vvp);
1887 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1888 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
15ff5402
RN
1889 center_x + (x - event->x),
1890 center_y + (y - event->y) );
1891 }
2eea9a36
RN
1892
1893 if ( !skip_update )
1894 draw_update ( zts->vw );
1895
941aa6e9
AF
1896 return VIK_LAYER_TOOL_ACK;
1897}
50a14534 1898
2eea9a36 1899static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
941aa6e9 1900{
2eea9a36
RN
1901 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1902
1903 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1904 zoomtool_resize_pixmap ( zts );
1905
1906 // Blank out currently drawn area
1907 gdk_draw_drawable ( zts->pixmap,
ff37db21 1908 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
2eea9a36
RN
1909 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1910 0, 0, 0, 0, -1, -1);
1911
1912 // Calculate new box starting point & size in pixels
1913 int xx, yy, width, height;
1914 if ( event->y > zts->start_y ) {
1915 yy = zts->start_y;
1916 height = event->y-zts->start_y;
1917 }
1918 else {
1919 yy = event->y;
1920 height = zts->start_y-event->y;
1921 }
1922 if ( event->x > zts->start_x ) {
1923 xx = zts->start_x;
1924 width = event->x-zts->start_x;
1925 }
1926 else {
1927 xx = event->x;
1928 width = zts->start_x-event->x;
1929 }
1930
1931 // Draw the box
ff37db21 1932 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
2eea9a36
RN
1933
1934 // Only actually draw when there's time to do so
1935 if (draw_buf_done) {
1936 static gpointer pass_along[3];
9b082b39 1937 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
ff37db21 1938 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
2eea9a36
RN
1939 pass_along[2] = zts->pixmap;
1940 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1941 draw_buf_done = FALSE;
1942 }
1943 }
2067d6de
RN
1944 else
1945 zts->bounds_active = FALSE;
1946
941aa6e9
AF
1947 return VIK_LAYER_TOOL_ACK;
1948}
50a14534 1949
2eea9a36 1950static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
941aa6e9 1951{
2eea9a36
RN
1952 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1953
2eea9a36
RN
1954 // Ensure haven't just released on the exact same position
1955 // i.e. probably haven't moved the mouse at all
2067d6de
RN
1956 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1957 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1958 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
2eea9a36
RN
1959
1960 VikCoord coord1, coord2;
1961 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1962 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1963
1964 // From the extend of the bounds pick the best zoom level
1965 // c.f. trw_layer_zoom_to_show_latlons()
1966 // Maybe refactor...
1967 struct LatLon ll1, ll2;
1968 vik_coord_to_latlon(&coord1, &ll1);
1969 vik_coord_to_latlon(&coord2, &ll2);
1970 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1971 (ll1.lon+ll2.lon)/2 };
1972
1973 VikCoord new_center;
1974 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
be5554c5 1975 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
2eea9a36
RN
1976
1977 /* Convert into definite 'smallest' and 'largest' positions */
1978 struct LatLon minmin;
1979 if ( ll1.lat < ll2.lat )
1980 minmin.lat = ll1.lat;
1981 else
1982 minmin.lat = ll2.lat;
1983
1984 struct LatLon maxmax;
1985 if ( ll1.lon > ll2.lon )
1986 maxmax.lon = ll1.lon;
1987 else
1988 maxmax.lon = ll2.lon;
1989
1990 /* Always recalculate the 'best' zoom level */
1991 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1992 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1993
1994 gdouble min_lat, max_lat, min_lon, max_lon;
1995 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1996 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1997 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1998 /* NB I think the logic used in this test to determine if the bounds is within view
1999 fails if track goes across 180 degrees longitude.
2000 Hopefully that situation is not too common...
2001 Mind you viking doesn't really do edge locations to well anyway */
2002 if ( min_lat < minmin.lat &&
2003 max_lat > minmin.lat &&
2004 min_lon < maxmax.lon &&
2005 max_lon > maxmax.lon )
2006 /* Found within zoom level */
2007 break;
2008
2009 /* Try next */
2010 zoom = zoom * 2;
2011 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2012 }
2eea9a36 2013 }
2067d6de
RN
2014 else {
2015 // When pressing shift and clicking for zoom, then jump three levels
2016 if ( modifiers == GDK_SHIFT_MASK ) {
2017 // Zoom in/out by three if possible
2018 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2019 if ( event->button == 1 ) {
2020 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2021 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2022 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2023 }
2024 else if ( event->button == 3 ) {
2025 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2026 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2027 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2028 }
2029 }
2030 }
2031
2032 draw_update ( zts->vw );
2033
2034 // Reset
2035 zts->bounds_active = FALSE;
2036
941aa6e9 2037 return VIK_LAYER_TOOL_ACK;
50a14534
EB
2038}
2039
941aa6e9 2040static VikToolInterface zoom_tool =
79dce0cb 2041 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
941aa6e9 2042 (VikToolConstructorFunc) zoomtool_create,
2eea9a36 2043 (VikToolDestructorFunc) zoomtool_destroy,
941aa6e9
AF
2044 (VikToolActivationFunc) NULL,
2045 (VikToolActivationFunc) NULL,
2046 (VikToolMouseFunc) zoomtool_click,
dc2c040e 2047 (VikToolMouseMoveFunc) zoomtool_move,
f2f2f7bf
GB
2048 (VikToolMouseFunc) zoomtool_release,
2049 NULL,
ef5e8132 2050 FALSE,
f2f2f7bf 2051 GDK_CURSOR_IS_PIXMAP,
63959706
RN
2052 &cursor_zoom_pixbuf,
2053 NULL };
463f9d07 2054/*** end zoom code ********************************************************/
941aa6e9 2055
576cbd17
GB
2056/********************************************************************************
2057 ** Pan tool code
2058 ********************************************************************************/
2059static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2060{
2061 return vw;
2062}
2063
beb9d63a 2064// NB Double clicking means this gets called THREE times!!!
576cbd17
GB
2065static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2066{
2067 vw->modified = TRUE;
beb9d63a
RN
2068
2069 if ( event->type == GDK_2BUTTON_PRESS ) {
2070 // Zoom in / out on double click
2071 // No need to change the center as that has already occurred in the first click of a double click occurrence
2072 if ( event->button == 1 ) {
2073 guint modifier = event->state & GDK_SHIFT_MASK;
2074 if ( modifier )
2075 vik_viewport_zoom_out ( vw->viking_vvp );
2076 else
2077 vik_viewport_zoom_in ( vw->viking_vvp );
2078 }
2079 else if ( event->button == 3 )
2080 vik_viewport_zoom_out ( vw->viking_vvp );
2081
2082 draw_update ( vw );
2083 }
2084 else
2085 // Standard pan click
2086 if ( event->button == 1 )
2087 vik_window_pan_click ( vw, event );
2088
576cbd17
GB
2089 return VIK_LAYER_TOOL_ACK;
2090}
2091
dc2c040e 2092static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
576cbd17
GB
2093{
2094 vik_window_pan_move ( vw, event );
2095 return VIK_LAYER_TOOL_ACK;
2096}
2097
2098static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2099{
2100 if ( event->button == 1 )
2101 vik_window_pan_release ( vw, event );
2102 return VIK_LAYER_TOOL_ACK;
2103}
2104
2105static VikToolInterface pan_tool =
79dce0cb 2106 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
576cbd17
GB
2107 (VikToolConstructorFunc) pantool_create,
2108 (VikToolDestructorFunc) NULL,
2109 (VikToolActivationFunc) NULL,
2110 (VikToolActivationFunc) NULL,
2111 (VikToolMouseFunc) pantool_click,
dc2c040e 2112 (VikToolMouseMoveFunc) pantool_move,
f2f2f7bf
GB
2113 (VikToolMouseFunc) pantool_release,
2114 NULL,
ef5e8132 2115 FALSE,
63959706
RN
2116 GDK_FLEUR,
2117 NULL,
2118 NULL };
576cbd17
GB
2119/*** end pan code ********************************************************/
2120
a47bfefa
RN
2121/********************************************************************************
2122 ** Select tool code
2123 ********************************************************************************/
2124static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2125{
08f14055
RN
2126 tool_ed_t *t = g_new(tool_ed_t, 1);
2127 t->vw = vw;
2128 t->vvp = vvp;
2129 t->vtl = NULL;
2130 t->is_waypoint = FALSE;
2131 return t;
2132}
2133
2134static void selecttool_destroy (tool_ed_t *t)
2135{
2136 g_free(t);
a47bfefa
RN
2137}
2138
2139typedef struct {
2140 gboolean cont;
2141 VikViewport *vvp;
2142 GdkEventButton *event;
08f14055 2143 tool_ed_t *tool_edit;
a47bfefa
RN
2144} clicker;
2145
2146static void click_layer_selected (VikLayer *vl, clicker *ck)
2147{
2148 /* Do nothing when function call returns true; */
2149 /* i.e. stop on first found item */
2150 if ( ck->cont )
2151 if ( vl->visible )
08f14055
RN
2152 if ( vik_layer_get_interface(vl->type)->select_click )
2153 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
a47bfefa
RN
2154}
2155
d40e66ae
RN
2156#ifdef WINDOWS
2157// Hopefully Alt keys by default
2158#define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2159#else
2160// Alt+mouse on Linux desktops tend to be used by the desktop manager
2161// Thus use an alternate modifier - you may need to set something into this group
2162#define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2163#endif
2164
08f14055 2165static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
a47bfefa 2166{
c29a3198 2167 t->vw->select_move = FALSE;
a47bfefa
RN
2168 /* Only allow selection on primary button */
2169 if ( event->button == 1 ) {
d40e66ae
RN
2170
2171 if ( event->state & VIK_MOVE_MODIFIER )
2172 vik_window_pan_click ( t->vw, event );
2173 else {
2174 /* Enable click to apply callback to potentially all track/waypoint layers */
2175 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2176 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2177 clicker ck;
2178 ck.cont = TRUE;
2179 ck.vvp = t->vw->viking_vvp;
2180 ck.event = event;
2181 ck.tool_edit = t;
2182 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2183 g_list_free ( gl );
2184
2185 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2186 if ( ck.cont ) {
2187 GtkTreeIter iter;
2188 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2189
2190 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2191 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2192 gint type = vik_treeview_item_get_type ( vtv, &iter );
2193 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2194 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
a47bfefa 2195
d40e66ae
RN
2196 vik_treeview_item_unselect ( vtv, &iter );
2197 if ( vik_window_clear_highlight ( t->vw ) )
2198 draw_update ( t->vw );
2199 }
2200 }
2201 }
2202 else {
2203 // Something found - so enable movement
2204 t->vw->select_move = TRUE;
a47bfefa 2205 }
c29a3198 2206 }
a47bfefa
RN
2207 }
2208 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2209 if ( vl->visible )
2210 /* Act on currently selected item to show menu */
60a69560 2211 if ( t->vw->selected_track || t->vw->selected_waypoint )
a47bfefa 2212 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
08f14055 2213 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
a47bfefa
RN
2214 }
2215
2216 return VIK_LAYER_TOOL_ACK;
2217}
2218
c29a3198 2219static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
08f14055 2220{
c29a3198 2221 if ( t->vw->select_move ) {
08f14055
RN
2222 // Don't care about vl here
2223 if ( t->vtl )
2224 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
c29a3198 2225 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
08f14055 2226 }
d40e66ae
RN
2227 else
2228 // Optional Panning
2229 if ( event->state & VIK_MOVE_MODIFIER )
2230 vik_window_pan_move ( t->vw, event );
2231
08f14055
RN
2232 return VIK_LAYER_TOOL_ACK;
2233}
2234
2235static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2236{
d40e66ae 2237 if ( t->vw->select_move ) {
08f14055
RN
2238 // Don't care about vl here
2239 if ( t->vtl )
2240 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
c29a3198 2241 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
08f14055 2242 }
c29a3198 2243
d40e66ae
RN
2244 if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2245 vik_window_pan_release ( t->vw, event );
2246
2247 // Force pan off incase it was on
2248 t->vw->pan_move = FALSE;
2249 t->vw->pan_x = t->vw->pan_y = -1;
2250
c29a3198
RN
2251 // End of this select movement
2252 t->vw->select_move = FALSE;
2253
08f14055
RN
2254 return VIK_LAYER_TOOL_ACK;
2255}
2256
a47bfefa 2257static VikToolInterface select_tool =
79dce0cb 2258 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
a47bfefa 2259 (VikToolConstructorFunc) selecttool_create,
08f14055 2260 (VikToolDestructorFunc) selecttool_destroy,
a47bfefa
RN
2261 (VikToolActivationFunc) NULL,
2262 (VikToolActivationFunc) NULL,
2263 (VikToolMouseFunc) selecttool_click,
08f14055
RN
2264 (VikToolMouseMoveFunc) selecttool_move,
2265 (VikToolMouseFunc) selecttool_release,
a47bfefa 2266 (VikToolKeyFunc) NULL,
ef5e8132 2267 FALSE,
a47bfefa
RN
2268 GDK_LEFT_PTR,
2269 NULL,
2270 NULL };
2271/*** end select tool code ********************************************************/
2272
8c721f83
EB
2273static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2274{
ee4c7107
RN
2275 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2276 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2277 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2278 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2279 return;
2280
8c721f83
EB
2281 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2282 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2283 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2284 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2285 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2286 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2287 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2288 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2289 }
2290 draw_update ( vw );
2291}
941aa6e9 2292
e4afc73a 2293static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
50a14534 2294{
e4afc73a
EB
2295 guint what = 128;
2296
2297 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2298 what = -3;
2299 }
2300 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2301 what = -4;
2302 }
2303 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2304 what = -2;
2305 }
2306 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2307 what = -1;
2308 }
2309 else {
2310 gchar *s = (gchar *)gtk_action_get_name(a);
2311 what = atoi(s+4);
2312 }
2313
50a14534
EB
2314 switch (what)
2315 {
2316 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2317 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2318 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2319 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2320 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2321 }
2322 draw_update ( vw );
2323}
2324
61a9fb73 2325static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
2326{
2327 VikCoord new_center;
e4afc73a
EB
2328
2329 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
50a14534
EB
2330 struct LatLon ll, llold;
2331 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2332 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2333 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2334 else
2335 return;
2336 }
e4afc73a 2337 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
50a14534
EB
2338 struct UTM utm, utmold;
2339 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2340 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2341 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2342 else
2343 return;
2344 }
e4afc73a 2345 else {
8dc8da82 2346 g_critical("Houston, we've had a problem.");
e4afc73a
EB
2347 return;
2348 }
50a14534 2349
be5554c5 2350 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
50a14534
EB
2351 draw_update ( vw );
2352}
2353
be5554c5
RN
2354/**
2355 * center_changed_cb:
2356 */
2357static void center_changed_cb ( VikWindow *vw )
2358{
2359// ATM Keep back always available, so when we pan - we can jump to the last requested position
2360/*
2361 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2362 if ( action_back ) {
2363 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2364 }
2365*/
2366 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2367 if ( action_forward ) {
2368 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2369 }
75b7457a
RN
2370
2371 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
be5554c5
RN
2372}
2373
2374/**
2375 * draw_goto_back_and_forth:
2376 */
2377static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2378{
2379 gboolean changed = FALSE;
2380 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2381 changed = vik_viewport_go_back ( vw->viking_vvp );
2382 }
2383 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2384 changed = vik_viewport_go_forward ( vw->viking_vvp );
2385 }
2386 else {
2387 return;
2388 }
2389
2390 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2391 // (otherwise we would get stuck in an infinite loop!)
2392 center_changed_cb ( vw );
2393
2394 if ( changed )
2395 draw_update ( vw );
2396}
2397
6b59f63d
RN
2398/**
2399 * Refresh maps displayed
2400 */
2401static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2402{
2403 // Only get 'new' maps
2404 simple_map_update ( vw, TRUE );
2405}
2406
e4afc73a 2407static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
50a14534 2408{
b5926b35 2409 VikLayerTypeEnum type;
e4afc73a
EB
2410 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2411 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2412 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
b5926b35
RN
2413 draw_update ( vw );
2414 vw->modified = TRUE;
e4afc73a
EB
2415 }
2416 }
50a14534
EB
2417 }
2418}
2419
e4afc73a 2420static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
50a14534 2421{
2cebc318 2422 a_clipboard_copy_selected ( vw->viking_vlp );
50a14534
EB
2423}
2424
e4afc73a 2425static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
50a14534 2426{
169acf64 2427 vik_layers_panel_cut_selected ( vw->viking_vlp );
169acf64 2428 vw->modified = TRUE;
50a14534
EB
2429}
2430
e4afc73a 2431static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
50a14534 2432{
ee4c7107 2433 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
50a14534 2434 {
50a14534
EB
2435 vw->modified = TRUE;
2436 }
2437}
2438
e4afc73a 2439static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
2440{
2441 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
4c77d5e0 2442 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
50a14534
EB
2443}
2444
5ff75d1e
GB
2445static void help_help_cb ( GtkAction *a, VikWindow *vw )
2446{
6ace3182
MA
2447#ifdef WINDOWS
2448 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2449#else /* WINDOWS */
5ff75d1e
GB
2450 gchar *uri;
2451 uri = g_strdup_printf("ghelp:%s", PACKAGE);
9080b461
RN
2452 GError *error = NULL;
2453 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2454 if ( !show && !error )
2455 // No error to show, so unlikely this will get called
2456 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2457 else if ( error ) {
2458 // Main error path
2459 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 );
2460 g_error_free ( error );
2461 }
5ff75d1e 2462 g_free(uri);
6ace3182 2463#endif /* WINDOWS */
5ff75d1e
GB
2464}
2465
75b7457a
RN
2466static void toggle_side_panel ( VikWindow *vw )
2467{
2468 vw->show_side_panel = !vw->show_side_panel;
2469 if ( vw->show_side_panel )
2470 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2471 else
2472 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2473}
2474
2475static void toggle_full_screen ( VikWindow *vw )
2476{
2477 vw->show_full_screen = !vw->show_full_screen;
2478 if ( vw->show_full_screen )
2479 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2480 else
2481 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2482}
2483
2484static void toggle_statusbar ( VikWindow *vw )
2485{
2486 vw->show_statusbar = !vw->show_statusbar;
2487 if ( vw->show_statusbar )
2488 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2489 else
2490 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2491}
2492
2493static void toggle_toolbar ( VikWindow *vw )
2494{
2495 vw->show_toolbar = !vw->show_toolbar;
2496 if ( vw->show_toolbar )
2497 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2498 else
2499 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2500}
2501
2502static void toggle_main_menu ( VikWindow *vw )
2503{
2504 vw->show_main_menu = !vw->show_main_menu;
2505 if ( vw->show_main_menu )
2506 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2507 else
2508 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2509}
2510
2511// Only for 'view' toggle menu widgets ATM.
2512GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2513{
2514 g_return_val_if_fail(name != NULL, NULL);
2515
2516 // ATM only FullScreen is *not* in SetShow path
2517 gchar *path;
2518 if ( g_strcmp0 ("FullScreen", name ) )
2519 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2520 else
2521 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2522
2523 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2524 g_free(path);
2525
2526 return widget;
2527}
2528
2529static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2530{
2531 gboolean next_state = !vw->show_side_panel;
2532 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2533 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2534 if ( next_state != menu_state )
2535 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2536 else
2537 toggle_side_panel ( vw );
2538}
2539
2540static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2541{
2542 gboolean next_state = !vw->show_full_screen;
2543 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2544 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2545 if ( next_state != menu_state )
2546 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2547 else
2548 toggle_full_screen ( vw );
2549}
2550
2551static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2552{
2553 gboolean next_state = !vw->show_statusbar;
2554 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2555 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2556 if ( next_state != menu_state )
2557 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2558 else
2559 toggle_statusbar ( vw );
2560}
2561
2562static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2563{
2564 gboolean next_state = !vw->show_toolbar;
2565 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2566 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2567 if ( next_state != menu_state )
2568 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2569 else
2570 toggle_toolbar ( vw );
2571}
2572
2573static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2574{
2575 gboolean next_state = !vw->show_main_menu;
2576 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2577 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2578 if ( next_state != menu_state )
2579 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2580 else
2581 toggle_main_menu ( vw );
2582}
2583
2584static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2585{
2586 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2587 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2588 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2589 if ( next_state != menu_state )
2590 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2591 else {
2592 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2593 draw_update ( vw );
2594 }
2595}
2596
2597static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2598{
2599 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2600 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2601 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2602 if ( next_state != menu_state )
2603 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2604 else {
2605 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2606 draw_update ( vw );
2607 }
2608}
2609
2610static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2611{
2612 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2613 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2614 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2615 if ( next_state != menu_state )
2616 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2617 else {
2618 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2619 draw_update ( vw );
2620 }
2621}
2622
d0a5f320
AF
2623static void help_about_cb ( GtkAction *a, VikWindow *vw )
2624{
2625 a_dialog_about(GTK_WINDOW(vw));
2626}
2627
a3040a49
RN
2628static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2629{
2630 // NB: No i18n as this is just for debug
c86a3725
RN
2631 gint byte_size = a_mapcache_get_size();
2632 gchar *msg_sz = NULL;
2633 gchar *msg = NULL;
2634#if GLIB_CHECK_VERSION(2,30,0)
2635 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2636#else
2637 msg_sz = g_format_size_for_display ( byte_size );
2638#endif
2639 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2640 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2641 g_free ( msg_sz );
2642 g_free ( msg );
a3040a49
RN
2643}
2644
fca1f3ad
RN
2645static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2646{
2647 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2648}
2649
e4afc73a 2650static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
2651{
2652 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2653 {
2654 vik_layers_panel_delete_selected ( vw->viking_vlp );
2655 vw->modified = TRUE;
2656 }
2657 else
4c77d5e0 2658 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
50a14534
EB
2659}
2660
75b7457a
RN
2661static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2662{
2663 gboolean next_state = !vw->show_full_screen;
2664 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2665 if ( tbutton ) {
2666 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2667 if ( next_state != tb_state )
2668 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2669 else
2670 toggle_full_screen ( vw );
2671 }
2672 else
2673 toggle_full_screen ( vw );
2674}
2675
181f5d0c
MA
2676static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2677{
75b7457a
RN
2678 gboolean next_state = !vw->show_side_panel;
2679 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2680 if ( tbutton ) {
2681 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2682 if ( next_state != tb_state )
2683 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2684 else
2685 toggle_side_panel ( vw );
2686 }
181f5d0c 2687 else
75b7457a 2688 toggle_side_panel ( vw );
181f5d0c
MA
2689}
2690
a459ee10
RN
2691static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2692{
75b7457a
RN
2693 gboolean next_state = !vw->show_statusbar;
2694 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2695 if ( tbutton ) {
2696 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2697 if ( next_state != tb_state )
2698 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2699 else
2700 toggle_statusbar ( vw );
2701 }
a459ee10 2702 else
75b7457a 2703 toggle_statusbar ( vw );
a459ee10
RN
2704}
2705
e7591765
RN
2706static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2707{
75b7457a
RN
2708 gboolean next_state = !vw->show_toolbar;
2709 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2710 if ( tbutton ) {
2711 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2712 if ( next_state != tb_state )
2713 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2714 else
2715 toggle_toolbar ( vw );
2716 }
e7591765 2717 else
75b7457a 2718 toggle_toolbar ( vw );
e7591765
RN
2719}
2720
7622022f
RN
2721static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2722{
75b7457a
RN
2723 gboolean next_state = !vw->show_main_menu;
2724 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2725 if ( tbutton ) {
2726 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2727 if ( next_state != tb_state )
2728 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2729 else
2730 toggle_main_menu ( vw );
2731 }
7622022f 2732 else
8b95da3d 2733 toggle_main_menu ( vw );
7622022f
RN
2734}
2735
941aa6e9
AF
2736/***************************************
2737 ** tool management routines
2738 **
2739 ***************************************/
2740
2741static toolbox_tools_t* toolbox_create(VikWindow *vw)
2742{
2743 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2744 vt->tools = NULL;
2745 vt->n_tools = 0;
2746 vt->active_tool = -1;
2747 vt->vw = vw;
941aa6e9
AF
2748 return vt;
2749}
2750
9593a4c9 2751static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
941aa6e9
AF
2752{
2753 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2754 vt->tools[vt->n_tools].ti = *vti;
9593a4c9 2755 vt->tools[vt->n_tools].layer_type = layer_type;
941aa6e9
AF
2756 if (vti->create) {
2757 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2758 }
2759 else {
2760 vt->tools[vt->n_tools].state = NULL;
2761 }
2762 vt->n_tools++;
2763}
2764
2765static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2766{
2767 int i;
2768 for (i=0; i<vt->n_tools; i++) {
79dce0cb 2769 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
941aa6e9
AF
2770 break;
2771 }
2772 }
2773 return i;
2774}
2775
2776static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2777{
2778 int tool = toolbox_get_tool(vt, tool_name);
2779 toolbox_tool_t *t = &vt->tools[tool];
2780 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2781
2782 if (tool == vt->n_tools) {
7742da66 2783 g_critical("trying to activate a non-existent tool...");
d84ade77 2784 return;
941aa6e9
AF
2785 }
2786 /* is the tool already active? */
2787 if (vt->active_tool == tool) {
2788 return;
2789 }
2790
2791 if (vt->active_tool != -1) {
2792 if (vt->tools[vt->active_tool].ti.deactivate) {
2793 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2794 }
2795 }
2796 if (t->ti.activate) {
2797 t->ti.activate(vl, t->state);
2798 }
2799 vt->active_tool = tool;
2800}
2801
f2f2f7bf
GB
2802static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2803{
2804 int tool = toolbox_get_tool(vt, tool_name);
2805 toolbox_tool_t *t = &vt->tools[tool];
2806 if (t->ti.cursor == NULL) {
2807 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
f246725d 2808 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, NULL);
f2f2f7bf
GB
2809 /* TODO: settable offeset */
2810 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2811 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2812 } else {
2813 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2814 }
2815 }
2816 return t->ti.cursor;
2817}
2818
941aa6e9
AF
2819static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2820{
2821 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2822 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
9593a4c9
EB
2823 gint ltype = vt->tools[vt->active_tool].layer_type;
2824 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2825 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
941aa6e9
AF
2826 }
2827}
2828
dc2c040e 2829static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
941aa6e9
AF
2830{
2831 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2832 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
9593a4c9
EB
2833 gint ltype = vt->tools[vt->active_tool].layer_type;
2834 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
165d30aa
EB
2835 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2836 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
941aa6e9
AF
2837 }
2838}
2839
2840static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2841{
2842 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
9593a4c9
EB
2843 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2844 gint ltype = vt->tools[vt->active_tool].layer_type;
2845 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2846 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
941aa6e9
AF
2847 }
2848}
2849/** End tool management ************************************/
2850
8fb71d6c
EB
2851void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2852{
79dce0cb 2853 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
8fb71d6c 2854}
941aa6e9 2855
75b7457a
RN
2856// Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2857// DON'T Use this from menu callback with toggle toolbar items!!
2858static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2859{
2860 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2861 if ( tbutton ) {
2862 // Causes toggle signal action to be raised.
2863 gtk_toggle_tool_button_set_active ( tbutton, state );
2864 }
2865}
2866
2867/* this function gets called whenever a menu is clicked */
2868// Note old is not used
2869static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
50a14534 2870{
75b7457a
RN
2871 // Ensure Toolbar kept in sync
2872 const gchar *name = gtk_action_get_name(a);
2873 toolbar_sync ( vw, name, TRUE );
2874
50a14534 2875 /* White Magic, my friends ... White Magic... */
b5926b35 2876 gint tool_id;
75b7457a 2877 toolbox_activate(vw->vt, name);
941aa6e9 2878
75b7457a 2879 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
33bc7c1b 2880
9b082b39 2881 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
33bc7c1b 2882 /* We set cursor, even if it is NULL: it resets to default */
9b082b39 2883 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
f2f2f7bf 2884
75b7457a 2885 if (!g_strcmp0(name, "Pan")) {
576cbd17 2886 vw->current_tool = TOOL_PAN;
576cbd17 2887 }
75b7457a 2888 else if (!g_strcmp0(name, "Zoom")) {
e4afc73a
EB
2889 vw->current_tool = TOOL_ZOOM;
2890 }
75b7457a 2891 else if (!g_strcmp0(name, "Ruler")) {
e4afc73a
EB
2892 vw->current_tool = TOOL_RULER;
2893 }
75b7457a 2894 else if (!g_strcmp0(name, "Select")) {
a47bfefa
RN
2895 vw->current_tool = TOOL_SELECT;
2896 }
e4afc73a 2897 else {
b5926b35 2898 VikLayerTypeEnum layer_id;
8fb71d6c
EB
2899 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2900 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
75b7457a 2901 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
8fb71d6c
EB
2902 vw->current_tool = TOOL_LAYER;
2903 vw->tool_layer_id = layer_id;
2904 vw->tool_tool_id = tool_id;
efb21731 2905 }
e4afc73a
EB
2906 }
2907 }
2908 }
ac4478f4 2909 draw_status_tool ( vw );
50a14534
EB
2910}
2911
2912static void window_set_filename ( VikWindow *vw, const gchar *filename )
2913{
2914 gchar *title;
4c77d5e0 2915 const gchar *file;
50a14534
EB
2916 if ( vw->filename )
2917 g_free ( vw->filename );
2918 if ( filename == NULL )
2919 {
2920 vw->filename = NULL;
50a14534
EB
2921 }
2922 else
2923 {
2924 vw->filename = g_strdup(filename);
50a14534 2925 }
4522c4ff
GB
2926
2927 /* Refresh window's title */
2928 file = window_get_filename ( vw );
4c77d5e0
GB
2929 title = g_strdup_printf( "%s - Viking", file );
2930 gtk_window_set_title ( GTK_WINDOW(vw), title );
2931 g_free ( title );
50a14534
EB
2932}
2933
4522c4ff
GB
2934static const gchar *window_get_filename ( VikWindow *vw )
2935{
2936 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2937}
2938
7bc965c0
GB
2939GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2940{
2941 GtkWidget *mode_button;
2942 gchar *buttonname;
2943 switch ( mode ) {
794c8f18 2944#ifdef VIK_CONFIG_EXPEDIA
7bc965c0 2945 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
794c8f18 2946#endif
7bc965c0 2947 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
d587678a 2948 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
794c8f18 2949 default: buttonname = "/ui/MainMenu/View/ModeUTM";
7bc965c0
GB
2950 }
2951 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2952 g_assert ( mode_button );
2953 return mode_button;
2954}
2955
01da6b4d
GB
2956/**
2957 * vik_window_get_pan_move:
2958 * @vw: some VikWindow
2959 *
2960 * Retrieves @vw's pan_move.
2961 *
2962 * Should be removed as soon as possible.
2963 *
2964 * Returns: @vw's pan_move
2965 *
2966 * Since: 0.9.96
2967 **/
1c6a6010
GB
2968gboolean vik_window_get_pan_move ( VikWindow *vw )
2969{
2970 return vw->pan_move;
2971}
2972
e6e2fc0b
RN
2973/**
2974 * Remove the potentially auto added map
2975 */
2976static void remove_default_map_layer ( VikWindow *vw )
2977{
2978 if ( a_vik_get_add_default_map_layer () ) {
2979 VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
2980 if ( vik_aggregate_layer_count(top) == 1 ) {
2981 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (top, VIK_LAYER_MAPS);
2982 // Could try to compare name vs _("Default Map") but this might have i8n issues if not translated
2983 if ( vl )
2984 vik_aggregate_layer_delete_layer ( top, vl );
2985 }
2986 }
2987}
2988
13505702
GB
2989static void on_activate_recent_item (GtkRecentChooser *chooser,
2990 VikWindow *self)
2991{
2992 gchar *filename;
2993
2994 filename = gtk_recent_chooser_get_current_uri (chooser);
2995 if (filename != NULL)
2996 {
2997 GFile *file = g_file_new_for_uri ( filename );
2998 gchar *path = g_file_get_path ( file );
2999 g_object_unref ( file );
3000 if ( self->filename )
3001 {
d8ff1421
RN
3002 GSList *filenames = NULL;
3003 filenames = g_slist_append ( filenames, path );
13505702 3004 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
d8ff1421 3005 // NB: GSList & contents are freed by main.open_window
13505702 3006 }
d8ff1421 3007 else {
e6e2fc0b 3008 remove_default_map_layer ( self );
756d53f5 3009 vik_window_open_file ( self, path, TRUE );
d8ff1421
RN
3010 g_free ( path );
3011 }
13505702
GB
3012 }
3013
3014 g_free (filename);
3015}
3016
3017static void setup_recent_files (VikWindow *self)
3018{
3019 GtkRecentManager *manager;
3020 GtkRecentFilter *filter;
3021 GtkWidget *menu, *menu_item;
3022
3023 filter = gtk_recent_filter_new ();
3024 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3025 gtk_recent_filter_add_group(filter, "viking");
3026
3027 manager = gtk_recent_manager_get_default ();
3028 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3029 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3030 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
50843c96 3031 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
13505702
GB
3032
3033 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3034 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3035
3036 g_signal_connect (G_OBJECT (menu), "item-activated",
3037 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3038}
3039
d17be106
RN
3040/*
3041 *
3042 */
3043static void update_recently_used_document (VikWindow *vw, const gchar *filename)
13505702
GB
3044{
3045 /* Update Recently Used Document framework */
3046 GtkRecentManager *manager = gtk_recent_manager_get_default();
3047 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3048 gchar *groups[] = {"viking", NULL};
3049 GFile *file = g_file_new_for_commandline_arg(filename);
3050 gchar *uri = g_file_get_uri(file);
3051 gchar *basename = g_path_get_basename(filename);
3052 g_object_unref(file);
3053 file = NULL;
3054
3055 recent_data->display_name = basename;
3056 recent_data->description = NULL;
3057 recent_data->mime_type = "text/x-gps-data";
3058 recent_data->app_name = (gchar *) g_get_application_name ();
3059 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3060 recent_data->groups = groups;
3061 recent_data->is_private = FALSE;
3062 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3063 {
d17be106
RN
3064 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3065 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3066 g_free ( msg );
13505702
GB
3067 }
3068
3069 g_free (uri);
3070 g_free (basename);
3071 g_free (recent_data->app_exec);
3072 g_slice_free (GtkRecentData, recent_data);
3073}
3074
55d3a53c
RN
3075/**
3076 * Call this before doing things that may take a long time and otherwise not show any other feedback
3077 * such as loading and saving files
3078 */
3079void vik_window_set_busy_cursor ( VikWindow *vw )
3080{
3081 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3082 // Viewport has a separate cursor
fafd0b95 3083 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
55d3a53c
RN
3084 // Ensure cursor updated before doing stuff
3085 while( gtk_events_pending() )
3086 gtk_main_iteration();
3087}
3088
3089void vik_window_clear_busy_cursor ( VikWindow *vw )
3090{
3091 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3092 // Restore viewport cursor
fafd0b95 3093 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
55d3a53c
RN
3094}
3095
50a14534
EB
3096void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3097{
55d3a53c 3098 vik_window_set_busy_cursor ( vw );
1b14d0d2
RN
3099
3100 // Enable the *new* filename to be accessible by the Layers codez
3101 gchar *original_filename = g_strdup ( vw->filename );
3102 g_free ( vw->filename );
3103 vw->filename = g_strdup ( filename );
3104 gboolean success = FALSE;
3105 gboolean restore_original_filename = FALSE;
718696d6 3106 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, vw->containing_vtl, filename );
a14f46cf 3107 switch ( vw->loaded_type )
50a14534 3108 {
ba9d0a00 3109 case LOAD_TYPE_READ_FAILURE:
4c77d5e0 3110 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
50a14534 3111 break;
ba9d0a00
RN
3112 case LOAD_TYPE_GPSBABEL_FAILURE:
3113 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3114 break;
54b84792
RN
3115 case LOAD_TYPE_GPX_FAILURE:
3116 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3117 break;
cb5ec7a8
RN
3118 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3119 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3120 break;
327a2533
RN
3121 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3122 {
3123 // Since we can process .vik files with issues just show a warning in the status bar
3124 // Not that a user can do much about it... or tells them what this issue is yet...
3125 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3126 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3127 g_free ( msg );
3128 }
3129 // No break, carry on to show any data
ba9d0a00 3130 case LOAD_TYPE_VIK_SUCCESS:
50a14534 3131 {
1b14d0d2 3132 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
50a14534 3133 GtkWidget *mode_button;
13505702 3134 /* Update UI */
50a14534
EB
3135 if ( change_filename )
3136 window_set_filename ( vw, filename );
7bc965c0 3137 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
50a14534
EB
3138 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. */
3139 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3140 vw->only_updating_coord_mode_ui = FALSE;
3141
3142 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
1657065a 3143
87d052f9
RN
3144 // Slightly long winded methods to align loaded viewport settings with the UI
3145 // Since the rewrite for toolbar + menu actions
3146 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3147 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3148 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3149 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3150 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3151 if ( vp_state_scale != ui_state_scale ) {
3152 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3153 toggle_draw_scale ( NULL, vw );
3154 }
3155 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3156 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3157 if ( vp_state_centermark != ui_state_centermark ) {
3158 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3159 toggle_draw_centermark ( NULL, vw );
3160 }
3161 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3162 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3163 if ( vp_state_highlight != ui_state_highlight ) {
3164 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3165 toggle_draw_highlight ( NULL, vw );
3166 }
50a14534 3167 }
1b14d0d2 3168 // NB No break, carry on to redraw
ba9d0a00 3169 //case LOAD_TYPE_OTHER_SUCCESS:
13505702 3170 default:
1b14d0d2
RN
3171 success = TRUE;
3172 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3173 restore_original_filename = ! restore_original_filename;
d17be106 3174 update_recently_used_document (vw, filename);
13505702 3175 draw_update ( vw );
ba9d0a00 3176 break;
50a14534 3177 }
55d3a53c 3178
1b14d0d2
RN
3179 if ( ! success || restore_original_filename )
3180 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3181 window_set_filename ( vw, original_filename );
3182 g_free ( original_filename );
3183
55d3a53c 3184 vik_window_clear_busy_cursor ( vw );
50a14534 3185}
55d3a53c 3186
e4afc73a 3187static void load_file ( GtkAction *a, VikWindow *vw )
50a14534 3188{
6e4a49aa
MA
3189 GSList *files = NULL;
3190 GSList *cur_file = NULL;
e4afc73a
EB
3191 gboolean newwindow;
3192 if (!strcmp(gtk_action_get_name(a), "Open")) {
3193 newwindow = TRUE;
3194 }
3195 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3196 newwindow = FALSE;
3197 }
3198 else {
8dc8da82 3199 g_critical("Houston, we've had a problem.");
e4afc73a
EB
3200 return;
3201 }
31729835 3202
3705c908
RN
3203 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3204 GTK_WINDOW(vw),
3205 GTK_FILE_CHOOSER_ACTION_OPEN,
3206 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3207 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3208 NULL);
3209 if ( last_folder_files_uri )
3210 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3211
3212 GtkFileFilter *filter;
3213 // NB file filters are listed this way for alphabetical ordering
269fe4da 3214#ifdef VIK_CONFIG_GEOCACHES
3705c908
RN
3215 filter = gtk_file_filter_new ();
3216 gtk_file_filter_set_name( filter, _("Geocaching") );
3217 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3218 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
269fe4da
RN
3219#endif
3220
3705c908
RN
3221 filter = gtk_file_filter_new ();
3222 gtk_file_filter_set_name( filter, _("Google Earth") );
3223 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3224 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3225
3226 filter = gtk_file_filter_new ();
3227 gtk_file_filter_set_name( filter, _("GPX") );
3228 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3229 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3230
3231 filter = gtk_file_filter_new ();
3232 gtk_file_filter_set_name ( filter, _("JPG") );
3233 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3234 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3235
3236 filter = gtk_file_filter_new ();
3237 gtk_file_filter_set_name( filter, _("Viking") );
3238 gtk_file_filter_add_pattern ( filter, "*.vik" );
3239 gtk_file_filter_add_pattern ( filter, "*.viking" );
3240 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3241
3242 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3243 // However assume this are barely used and thus not worthy of inclusion
3244 // as they'll just make the options too many and have no clear file pattern
3245 // one can always use the all option
3246 filter = gtk_file_filter_new ();
3247 gtk_file_filter_set_name( filter, _("All") );
3248 gtk_file_filter_add_pattern ( filter, "*" );
3249 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3250 // Default to any file - same as before open filters were added
3251 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3252
3253 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(dialog), TRUE );
3254 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3255 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
269fe4da 3256
3705c908 3257 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
50a14534 3258 {
4850161a 3259 g_free ( last_folder_files_uri );
3705c908 3260 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
4850161a 3261
a5fd2196 3262#ifdef VIKING_PROMPT_IF_MODIFIED
50a14534 3263 if ( (vw->modified || vw->filename) && newwindow )
a5fd2196
QT
3264#else
3265 if ( vw->filename && newwindow )
3266#endif
3705c908 3267 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) ) );
50a14534 3268 else {
4850161a 3269
3705c908 3270 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) );
6e4a49aa 3271 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
d4a8b54d 3272 gboolean first_vik_file = TRUE;
6e4a49aa
MA
3273 cur_file = files;
3274 while ( cur_file ) {
d4a8b54d 3275
6e4a49aa 3276 gchar *file_name = cur_file->data;
d4a8b54d
RN
3277 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3278 // Load first of many .vik files in current window
3279 if ( first_vik_file ) {
e6e2fc0b 3280 remove_default_map_layer ( vw );
d4a8b54d
RN
3281 vik_window_open_file ( vw, file_name, TRUE );
3282 first_vik_file = FALSE;
3283 }
3284 else {
3285 // Load each subsequent .vik file in a separate window
3286 VikWindow *newvw = vik_window_new_window ();
3287 if (newvw)
3288 vik_window_open_file ( newvw, file_name, TRUE );
3289 }
3290 }
3291 else
3292 // Other file types
3293 vik_window_open_file ( vw, file_name, change_fn );
3294
6e4a49aa
MA
3295 g_free (file_name);
3296 cur_file = g_slist_next (cur_file);
50a14534 3297 }
6e4a49aa 3298 g_slist_free (files);
50a14534
EB
3299 }
3300 }
3705c908 3301 gtk_widget_destroy ( dialog );
50a14534
EB
3302}
3303
e4afc73a 3304static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
50a14534
EB
3305{
3306 gboolean rv = FALSE;
3307 const gchar *fn;
31729835 3308
3705c908
RN
3309 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3310 GTK_WINDOW(vw),
3311 GTK_FILE_CHOOSER_ACTION_SAVE,
3312 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3313 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3314 NULL);
3315 if ( last_folder_files_uri )
3316 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3317
3318 GtkFileFilter *filter;
3319 filter = gtk_file_filter_new ();
3320 gtk_file_filter_set_name( filter, _("All") );
3321 gtk_file_filter_add_pattern ( filter, "*" );
3322 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3323
3324 filter = gtk_file_filter_new ();
3325 gtk_file_filter_set_name( filter, _("Viking") );
3326 gtk_file_filter_add_pattern ( filter, "*.vik" );
3327 gtk_file_filter_add_pattern ( filter, "*.viking" );
3328 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3329 // Default to a Viking file
3330 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3331
3332 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3333 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
471d418c 3334
c38eb451 3335 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
8f75f616 3336 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
140d0d67 3337 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
c38eb451
RN
3338 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3339
3705c908 3340 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), auto_save_name);
50a14534 3341
3705c908 3342 while ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
50a14534 3343 {
3705c908
RN
3344 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
3345 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
3346 {
3347 window_set_filename ( vw, fn );
3348 rv = window_save ( vw );
4850161a
RN
3349 if ( rv ) {
3350 vw->modified = FALSE;
3351 g_free ( last_folder_files_uri );
3705c908 3352 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
4850161a 3353 }
50a14534
EB
3354 break;
3355 }
3356 }
c38eb451 3357 g_free ( auto_save_name );
3705c908 3358 gtk_widget_destroy ( dialog );
50a14534
EB
3359 return rv;
3360}
3361
3362static gboolean window_save ( VikWindow *vw )
3363{
55d3a53c
RN
3364 vik_window_set_busy_cursor ( vw );
3365 gboolean success = TRUE;
3366
50a14534 3367 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
13505702 3368 {
d17be106 3369 update_recently_used_document ( vw, vw->filename );
13505702 3370 }
50a14534
EB
3371 else
3372 {
4c77d5e0 3373 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
55d3a53c 3374 success = FALSE;
50a14534 3375 }
55d3a53c
RN
3376 vik_window_clear_busy_cursor ( vw );
3377 return success;
50a14534
EB
3378}
3379
e4afc73a 3380static gboolean save_file ( GtkAction *a, VikWindow *vw )
50a14534
EB
3381{
3382 if ( ! vw->filename )
e4afc73a 3383 return save_file_as ( NULL, vw );
50a14534
EB
3384 else
3385 {
3386 vw->modified = FALSE;
3387 return window_save ( vw );
3388 }
3389}
3390
0bb36e78
RN
3391/**
3392 * export_to:
3393 *
3394 * Export all TRW Layers in the list to individual files in the specified directory
3395 *
3396 * Returns: %TRUE on success
3397 */
3398static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3399{
3400 gboolean success = TRUE;
3401
3402 gint export_count = 0;
3403
3404 vik_window_set_busy_cursor ( vw );
3405
3406 while ( gl ) {
3407
3408 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3409
3410 // Some protection in attempting to write too many same named files
3411 // As this will get horribly slow...
3412 gboolean safe = FALSE;
3413 gint ii = 2;
3414 while ( ii < 5000 ) {
3415 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3416 // Try rename
3417 g_free ( fn );
3418 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3419 }
3420 else {
3421 safe = TRUE;
3422 break;
3423 }
3424 ii++;
3425 }
3426 if ( ii == 5000 )
3427 success = FALSE;
3428
3429 // NB: We allow exporting empty layers
3430 if ( safe ) {
3431 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3432
3433 // Show some progress
3434 if ( this_success ) {
3435 export_count++;
6e4bf640 3436 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
0bb36e78
RN
3437 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3438 while ( gtk_events_pending() )
3439 gtk_main_iteration ();
3440 g_free ( message );
3441 }
3442
3443 success = success && this_success;
3444 }
3445
3446 g_free ( fn );
3447 gl = g_list_next ( gl );
3448 }
3449
3450 vik_window_clear_busy_cursor ( vw );
3451
3452 // Confirm what happened.
3453 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3454 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3455 g_free ( message );
3456
3457 return success;
3458}
3459
3460static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3461{
3462 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3463
3464 if ( !gl ) {
3465 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3466 return;
3467 }
3468
dc453cfd 3469 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
0bb36e78 3470 GTK_WINDOW(vw),
dc453cfd 3471 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
0bb36e78
RN
3472 GTK_STOCK_CANCEL,
3473 GTK_RESPONSE_REJECT,
3474 GTK_STOCK_OK,
3475 GTK_RESPONSE_ACCEPT,
3476 NULL );
dc453cfd
GB
3477 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3478 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3479 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
0bb36e78
RN
3480
3481 gtk_widget_show_all ( dialog );
3482
3483 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
dc453cfd 3484 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
0bb36e78
RN
3485 gtk_widget_destroy ( dialog );
3486 if ( dir ) {
3487 if ( !export_to ( vw, gl, vft, dir, extension ) )
3488 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3489 g_free ( dir );
3490 }
3491 }
3492 else
3493 gtk_widget_destroy ( dialog );
3494
3495 g_list_free ( gl );
3496}
3497
3498static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3499{
3500 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3501}
3502
3503static void export_to_kml ( GtkAction *a, VikWindow *vw )
3504{
3505 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3506}
3507
3f46fe76
RN
3508static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3509{
3510 gchar *message = NULL;
3511 if ( vw->filename ) {
3512 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3513 // Get some timestamp information of the file
3514 GStatBuf stat_buf;
3515 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3516 gchar time_buf[64];
3517 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
c86a3725
RN
3518 gchar *size = NULL;
3519 gint byte_size = stat_buf.st_size;
3520#if GLIB_CHECK_VERSION(2,30,0)
3521 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3522#else
3523 size = g_format_size_for_display ( byte_size );
3524#endif
3525 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3526 g_free (size);
3f46fe76
RN
3527 }
3528 }
3529 else
3530 message = g_strdup ( _("File not accessible") );
3531 }
3532 else
3533 message = g_strdup ( _("No Viking File") );
3534
3535 // Show the info
3536 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3537 g_free ( message );
3538}
3539
9cc13848
RN
3540static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3541{
3542 vik_datasource_mode_t mode = datasource->mode;
3543 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3544 mode = VIK_DATASOURCE_CREATENEWLAYER;
3545 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3546}
3547
1d1bc3c1
EB
3548static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3549{
9cc13848 3550 my_acquire ( vw, &vik_datasource_gps_interface );
7b3479e3
EB
3551}
3552
31349009
GB
3553static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3554{
9cc13848 3555 my_acquire ( vw, &vik_datasource_file_interface );
31349009
GB
3556}
3557
c0c5893f
RN
3558static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3559{
3560 my_acquire ( vw, &vik_datasource_geojson_interface );
3561}
3562
7f95fd54 3563static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
7b3479e3 3564{
9cc13848 3565 my_acquire ( vw, &vik_datasource_routing_interface );
1d1bc3c1
EB
3566}
3567
9c4555df
GB
3568#ifdef VIK_CONFIG_OPENSTREETMAP
3569static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3570{
9cc13848 3571 my_acquire ( vw, &vik_datasource_osm_interface );
9c4555df 3572}
3cc57413
RN
3573
3574static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3575{
9cc13848 3576 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3cc57413 3577}
9c4555df
GB
3578#endif
3579
1ef9e637 3580#ifdef VIK_CONFIG_GEOCACHES
3333c069
EB
3581static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3582{
9cc13848 3583 my_acquire ( vw, &vik_datasource_gc_interface );
3333c069 3584}
1ef9e637 3585#endif
3333c069 3586
f75d0233
RN
3587#ifdef VIK_CONFIG_GEOTAG
3588static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3589{
9cc13848 3590 my_acquire ( vw, &vik_datasource_geotag_interface );
f75d0233
RN
3591}
3592#endif
3593
3c29a566
RN
3594#ifdef VIK_CONFIG_GEONAMES
3595static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3596{
9cc13848 3597 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3c29a566
RN
3598}
3599#endif
3600
c6acf18d
RN
3601static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3602{
9cc13848 3603 my_acquire ( vw, &vik_datasource_url_interface );
c6acf18d
RN
3604}
3605
5210c3d3
GB
3606static void goto_default_location( GtkAction *a, VikWindow *vw)
3607{
3608 struct LatLon ll;
3609 ll.lat = a_vik_get_default_lat();
3610 ll.lon = a_vik_get_default_long();
be5554c5 3611 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
5210c3d3
GB
3612 vik_layers_panel_emit_update(vw->viking_vlp);
3613}
3614
3615
369126f3
QT
3616static void goto_address( GtkAction *a, VikWindow *vw)
3617{
c36a079a
RN
3618 a_vik_goto ( vw, vw->viking_vvp );
3619 vik_layers_panel_emit_update ( vw->viking_vlp );
369126f3
QT
3620}
3621
7c259702
JJ
3622static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3623{
3624 a_mapcache_flush();
3625}
3626
7c8bddff
RN
3627static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3628{
3629 const VikCoord* coord;
3630 struct UTM utm;
3631 gchar *lat = NULL, *lon = NULL;
3632
3633 coord = vik_viewport_get_center ( vw->viking_vvp );
3634 vik_coord_to_utm ( coord, &utm );
3635
3636 gboolean full_format = FALSE;
f5db2d67 3637 (void)a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
7c8bddff
RN
3638
3639 if ( full_format )
3640 // Bells & Whistles - may include degrees, minutes and second symbols
3641 get_location_strings ( vw, utm, &lat, &lon );
3642 else {
3643 // Simple x.xx y.yy format
3644 struct LatLon ll;
3645 a_coords_utm_to_latlon ( &utm, &ll );
3646 lat = g_strdup_printf ( "%.6f", ll.lat );
3647 lon = g_strdup_printf ( "%.6f", ll.lon );
3648 }
3649
3650 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3651 g_free (lat);
3652 g_free (lon);
3653
3654 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3655
3656 g_free ( msg );
3657}
3658
a7023a1b
RN
3659static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3660{
3661 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3662
3663 if ( !texts[1] )
3664 return; // Internally broken :(
3665
3666 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3667 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3668 // NB no update needed
3669
3670 g_strfreev ( texts );
3671}
3672
98acb9a1
RN
3673static void preferences_change_update ( VikWindow *vw, gpointer data )
3674{
3675 // Want to update all TrackWaypoint layers
3676 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3677
7fe46df4
RN
3678 if ( !layers )
3679 return;
3680
3681 while ( layers ) {
98acb9a1 3682 // Reset the individual waypoints themselves due to the preferences change
7fe46df4 3683 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
98acb9a1 3684 vik_trw_layer_reset_waypoints ( vtl );
7fe46df4 3685 layers = g_list_next ( layers );
98acb9a1
RN
3686 }
3687
3688 g_list_free ( layers );
3689
3690 draw_update ( vw );
3691}
3692
17a1f8f9
EB
3693static void preferences_cb ( GtkAction *a, VikWindow *vw )
3694{
9be0449f
RN
3695 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3696
17a1f8f9 3697 a_preferences_show_window ( GTK_WINDOW(vw) );
9be0449f 3698
98acb9a1
RN
3699 // Has the waypoint size setting changed?
3700 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3701 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
9be0449f
RN
3702 clear_garmin_icon_syms ();
3703
98acb9a1
RN
3704 // Update all windows
3705 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3706 }
74562734
RN
3707
3708 // Ensure TZ Lookup initialized
3709 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3710 vu_setup_lat_lon_tz_lookup();
75b7457a
RN
3711
3712 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
17a1f8f9
EB
3713}
3714
5210c3d3
GB
3715static void default_location_cb ( GtkAction *a, VikWindow *vw )
3716{
3717 /* Simplistic repeat of preference setting
3718 Only the name & type are important for setting the preference via this 'external' way */
3719 VikLayerParam pref_lat[] = {
a7023a1b
RN
3720 { VIK_LAYER_NUM_TYPES,
3721 VIKING_PREFERENCES_NAMESPACE "default_latitude",
5210c3d3 3722 VIK_LAYER_PARAM_DOUBLE,
e0528707 3723 VIK_LAYER_GROUP_NONE,
5210c3d3
GB
3724 NULL,
3725 VIK_LAYER_WIDGET_SPINBUTTON,
3726 NULL,
e6994d8d 3727 NULL,
a87f8fa1
RN
3728 NULL,
3729 NULL,
3730 NULL,
63959706 3731 NULL,
a87f8fa1 3732 },
5210c3d3
GB
3733 };
3734 VikLayerParam pref_lon[] = {
a7023a1b
RN
3735 { VIK_LAYER_NUM_TYPES,
3736 VIKING_PREFERENCES_NAMESPACE "default_longitude",
5210c3d3 3737 VIK_LAYER_PARAM_DOUBLE,
e0528707 3738 VIK_LAYER_GROUP_NONE,
5210c3d3
GB
3739 NULL,
3740 VIK_LAYER_WIDGET_SPINBUTTON,
3741 NULL,
e6994d8d 3742 NULL,
a87f8fa1
RN
3743 NULL,
3744 NULL,
3745 NULL,
63959706 3746 NULL,
a87f8fa1 3747 },
5210c3d3
GB
3748 };
3749
3750 /* Get current center */
3751 struct LatLon ll;
3752 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3753
3754 /* Apply to preferences */
3755 VikLayerParamData vlp_data;
3756 vlp_data.d = ll.lat;
3757 a_preferences_run_setparam (vlp_data, pref_lat);
3758 vlp_data.d = ll.lon;
3759 a_preferences_run_setparam (vlp_data, pref_lon);
3760 /* Remember to save */
3761 a_preferences_save_to_file();
3762}
3763
bf490ae7
RN
3764/**
3765 * Delete All
3766 */
e4afc73a 3767static void clear_cb ( GtkAction *a, VikWindow *vw )
50a14534 3768{
bf490ae7
RN
3769 // Do nothing if empty
3770 VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
3771 if ( ! vik_aggregate_layer_is_empty(top) ) {
3772 if ( a_dialog_yes_or_no ( GTK_WINDOW(vw), _("Are you sure you wish to delete all layers?"), NULL ) ) {
3773 vik_layers_panel_clear ( vw->viking_vlp );
3774 window_set_filename ( vw, NULL );
3775 draw_update ( vw );
3776 }
3777 }
50a14534
EB
3778}
3779
e4afc73a 3780static void window_close ( GtkAction *a, VikWindow *vw )
50a14534
EB
3781{
3782 if ( ! delete_event ( vw ) )
3783 gtk_widget_destroy ( GTK_WIDGET(vw) );
3784}
3785
2bf7cadd
QT
3786static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3787{
7bb60307 3788 if (save_file( NULL, vw)) {
2bf7cadd 3789 window_close( NULL, vw);
7bb60307
QT
3790 return(TRUE);
3791 }
2bf7cadd
QT
3792 else
3793 return(FALSE);
3794}
3795
e4afc73a 3796static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
3797{
3798 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3799 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3800 {
3801 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3802 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3803 draw_update ( vw );
3804 }
3805}
3806
49d43d14 3807static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png, gboolean save_kmz )
50a14534
EB
3808{
3809 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3810 GdkPixbuf *pixbuf_to_save;
3811 gdouble old_xmpp, old_ympp;
3812 GError *error = NULL;
3813
d78751d4
RN
3814 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3815 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3816 GTK_MESSAGE_INFO,
3817 GTK_BUTTONS_NONE,
3818 _("Generating image file...") );
3819
3820 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3821 // Ensure dialog shown
3822 gtk_widget_show_all ( msgbox );
3823 // Try harder...
3824 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3825 while ( gtk_events_pending() )
3826 gtk_main_iteration ();
3827 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3828 // At least the empty box can give a clue something's going on + the statusbar msg...
3829 // Windows version under Wine OK!
3830
50a14534
EB
3831 /* backup old zoom & set new */
3832 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3833 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3834 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3835
3836 /* reset width and height: */
3837 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3838
3839 /* draw all layers */
3840 draw_redraw ( vw );
3841
3842 /* save buffer as file. */
3843 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 3844 if ( !pixbuf_to_save ) {
d78751d4
RN
3845 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3846 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
5cd09d57
RN
3847 goto cleanup;
3848 }
3849
49d43d14
RN
3850 int ans = 0; // Default to success
3851
3852 if ( save_kmz ) {
3853 gdouble north, east, south, west;
3854 vik_viewport_get_min_max_lat_lon ( vw->viking_vvp, &south, &north, &west, &east );
3855 ans = kmz_save_file ( pixbuf_to_save, fn, north, east, south, west );
50a14534 3856 }
d78751d4 3857 else {
49d43d14
RN
3858 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3859 if (error) {
3860 g_warning("Unable to write to file %s: %s", fn, error->message );
3861 g_error_free (error);
3862 ans = 42;
3863 }
d78751d4 3864 }
49d43d14
RN
3865
3866 if ( ans == 0 )
3867 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3868 else
3869 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3870
50a14534
EB
3871 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3872
5cd09d57 3873 cleanup:
d78751d4
RN
3874 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3875 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3876 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3877
50a14534
EB
3878 /* pretend like nothing happened ;) */
3879 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3880 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3881 vik_viewport_configure ( vw->viking_vvp );
3882 draw_update ( vw );
3883}
3884
3885static 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 )
3886{
3887 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3888 gchar *name_of_file = g_malloc ( size );
3889 guint x = 1, y = 1;
3890 struct UTM utm_orig, utm;
3891
3892 /* *** copied from above *** */
3893 GdkPixbuf *pixbuf_to_save;
3894 gdouble old_xmpp, old_ympp;
3895 GError *error = NULL;
3896
3897 /* backup old zoom & set new */
3898 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3899 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3900 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3901
3902 /* reset width and height: do this only once for all images (same size) */
3903 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3904 /* *** end copy from above *** */
3905
3906 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3907
fd437981
RN
3908 if ( g_mkdir(fn,0777) != 0 )
3909 g_warning ( "%s: Failed to create directory %s", __FUNCTION__, fn );
50a14534
EB
3910
3911 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3912
3913 for ( y = 1; y <= tiles_h; y++ )
3914 {
3915 for ( x = 1; x <= tiles_w; x++ )
3916 {
3d9454e6 3917 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
3918 utm = utm_orig;
3919 if ( tiles_w & 0x1 )
3920 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3921 else
3922 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3923 if ( tiles_h & 0x1 ) /* odd */
3924 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3925 else /* even */
3926 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3927
3928 /* move to correct place. */
be5554c5 3929 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
50a14534
EB
3930
3931 draw_redraw ( vw );
3932
3933 /* save buffer as file. */
3934 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);
3935 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3936 if (error)
3937 {
d17be106
RN
3938 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3939 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3940 g_free ( msg );
50a14534
EB
3941 g_error_free (error);
3942 }
3943
3944 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3945 }
3946 }
3947
be5554c5 3948 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
50a14534
EB
3949 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3950 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3951 vik_viewport_configure ( vw->viking_vvp );
3952 draw_update ( vw );
3953
3954 g_free ( name_of_file );
3955}
3956
3957static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3958{
3959 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3960 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
267e3ce7
RN
3961
3962 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3963 gdouble zoom = pow (2, active-2 );
3964
50a14534
EB
3965 gdouble width_min, width_max, height_min, height_max;
3966 gint width, height;
3967
3968 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3969 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3970
3971 /* TODO: support for xzoom and yzoom values */
267e3ce7
RN
3972 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3973 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
50a14534
EB
3974
3975 if ( width > width_max || width < width_min || height > height_max || height < height_min )
4c77d5e0 3976 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
50a14534
EB
3977
3978 gtk_spin_button_set_value ( width_spin, width );
3979 gtk_spin_button_set_value ( height_spin, height );
3980}
3981
3982static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3983{
3984 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
267e3ce7
RN
3985
3986 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3987 gdouble zoom = pow (2, active-2 );
3988
50a14534
EB
3989 gchar *label_text;
3990 gdouble w, h;
267e3ce7
RN
3991 w = gtk_spin_button_get_value(width_spin) * zoom;
3992 h = gtk_spin_button_get_value(height_spin) * zoom;
50a14534
EB
3993 if (pass_along[4]) /* save many images; find TOTAL area covered */
3994 {
3995 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3996 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3997 }
6f9336aa
RN
3998 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3999 switch (dist_units) {
4000 case VIK_UNITS_DISTANCE_KILOMETRES:
4001 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
4002 break;
4003 case VIK_UNITS_DISTANCE_MILES:
4004 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
4005 break;
b22233bd
RN
4006 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
4007 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
4008 break;
6f9336aa
RN
4009 default:
4010 label_text = g_strdup_printf ("Just to keep the compiler happy");
4011 g_critical("Houston, we've had a problem. distance=%d", dist_units);
4012 }
4013
50a14534
EB
4014 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
4015 g_free ( label_text );
4016}
4017
49d43d14
RN
4018typedef enum {
4019 VW_GEN_SINGLE_IMAGE,
4020 VW_GEN_DIRECTORY_OF_IMAGES,
4021 VW_GEN_KMZ_FILE,
4022} img_generation_t;
4023
310cbaf7
RN
4024/*
4025 * Get an allocated filename (or directory as specified)
4026 */
49d43d14 4027static gchar* draw_image_filename ( VikWindow *vw, img_generation_t img_gen )
310cbaf7
RN
4028{
4029 gchar *fn = NULL;
49d43d14 4030 if ( img_gen != VW_GEN_DIRECTORY_OF_IMAGES )
310cbaf7
RN
4031 {
4032 // Single file
3705c908
RN
4033 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save Image"),
4034 GTK_WINDOW(vw),
4035 GTK_FILE_CHOOSER_ACTION_SAVE,
4036 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4037 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4038 NULL);
4039 if ( last_folder_images_uri )
4040 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_images_uri );
4041
4042 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( dialog );
4043 /* Add filters */
4044 GtkFileFilter *filter;
4045 filter = gtk_file_filter_new ();
4046 gtk_file_filter_set_name ( filter, _("All") );
4047 gtk_file_filter_add_pattern ( filter, "*" );
4048 gtk_file_chooser_add_filter ( chooser, filter );
4049
49d43d14
RN
4050 if ( img_gen == VW_GEN_KMZ_FILE ) {
4051 filter = gtk_file_filter_new ();
4052 gtk_file_filter_set_name ( filter, _("KMZ") );
4053 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4054 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4055 gtk_file_chooser_add_filter ( chooser, filter );
3705c908 4056 gtk_file_chooser_set_filter ( chooser, filter );
49d43d14
RN
4057 }
4058 else {
4059 filter = gtk_file_filter_new ();
4060 gtk_file_filter_set_name ( filter, _("JPG") );
4061 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4062 gtk_file_chooser_add_filter ( chooser, filter );
3705c908 4063
49d43d14
RN
4064 if ( !vw->draw_image_save_as_png )
4065 gtk_file_chooser_set_filter ( chooser, filter );
3705c908 4066
49d43d14
RN
4067 filter = gtk_file_filter_new ();
4068 gtk_file_filter_set_name ( filter, _("PNG") );
4069 gtk_file_filter_add_mime_type ( filter, "image/png");
4070 gtk_file_chooser_add_filter ( chooser, filter );
4071
4072 if ( vw->draw_image_save_as_png )
4073 gtk_file_chooser_set_filter ( chooser, filter );
4074 }
310cbaf7 4075
3705c908
RN
4076 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4077 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4078
4079 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4080 g_free ( last_folder_images_uri );
4081 last_folder_images_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
4082
4083 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
310cbaf7 4084 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3705c908
RN
4085 if ( ! a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
4086 fn = NULL;
310cbaf7 4087 }
3705c908 4088 gtk_widget_destroy ( dialog );
310cbaf7
RN
4089 }
4090 else {
4091 // A directory
4092 // For some reason this method is only written to work in UTM...
4093 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4094 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4095 return fn;
4096 }
4097
3705c908
RN
4098 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4099 GTK_WINDOW(vw),
4100 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4101 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4102 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4103 NULL);
4104 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4105 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
310cbaf7 4106
3705c908
RN
4107 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4108 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
310cbaf7 4109 }
3705c908 4110 gtk_widget_destroy ( dialog );
310cbaf7
RN
4111 }
4112 return fn;
4113}
4114
49d43d14 4115static void draw_to_image_file ( VikWindow *vw, img_generation_t img_gen )
50a14534
EB
4116{
4117 /* todo: default for answers inside VikWindow or static (thruout instance) */
4c77d5e0 4118 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
50a14534
EB
4119 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4120 GTK_STOCK_CANCEL,
4121 GTK_RESPONSE_REJECT,
4122 GTK_STOCK_OK,
4123 GTK_RESPONSE_ACCEPT,
10888930 4124 NULL );
50a14534 4125 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
50a14534
EB
4126 GtkWidget *current_window_button;
4127 gpointer current_window_pass_along[7];
267e3ce7 4128 GtkWidget *zoom_label, *zoom_combo;
50a14534
EB
4129 GtkWidget *total_size_label;
4130
49d43d14 4131 // only used for VW_GEN_DIRECTORY_OF_IMAGES
886031df 4132 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
50a14534 4133
4c77d5e0 4134 width_label = gtk_label_new ( _("Width (pixels):") );
5cd09d57 4135 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4c77d5e0 4136 height_label = gtk_label_new ( _("Height (pixels):") );
5cd09d57
RN
4137 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4138#ifdef WINDOWS
4139 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4140#endif
4c77d5e0 4141 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
50a14534 4142 /* TODO: separate xzoom and yzoom factors */
267e3ce7
RN
4143 zoom_combo = create_zoom_combo_all_levels();
4144
4145 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4db16a36 4146 gint active = 2 + round ( log (mpp) / log (2) );
267e3ce7
RN
4147
4148 // Can we not hard code size here?
4149 if ( active > 17 )
4150 active = 17;
06716e69
RN
4151 if ( active < 0 )
4152 active = 0;
267e3ce7 4153 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
50a14534
EB
4154
4155 total_size_label = gtk_label_new ( NULL );
4156
4c77d5e0 4157 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
50a14534
EB
4158 current_window_pass_along [0] = vw;
4159 current_window_pass_along [1] = width_spin;
4160 current_window_pass_along [2] = height_spin;
267e3ce7 4161 current_window_pass_along [3] = zoom_combo;
49d43d14
RN
4162 current_window_pass_along [4] = NULL; // Only for directory of tiles: width
4163 current_window_pass_along [5] = NULL; // Only for directory of tiles: height
50a14534
EB
4164 current_window_pass_along [6] = total_size_label;
4165 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4166
49d43d14
RN
4167 GtkWidget *png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4168 GtkWidget *jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
50a14534 4169
49d43d14
RN
4170 if ( img_gen == VW_GEN_KMZ_FILE ) {
4171 // Don't show image type selection if creating a KMZ (always JPG internally)
4172 // Start with viewable area by default
4173 draw_to_image_file_current_window_cb ( current_window_button, NULL, current_window_pass_along );
4174 } else {
4175 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4176 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4177 }
59d5fb31 4178
50a14534
EB
4179 if ( ! vw->draw_image_save_as_png )
4180 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4181
9b082b39
RN
4182 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4183 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4184 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4185 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
5cd09d57 4186#ifdef WINDOWS
9b082b39 4187 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
5cd09d57 4188#endif
9b082b39 4189 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
9b082b39
RN
4190 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4191 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
50a14534 4192
49d43d14 4193 if ( img_gen == VW_GEN_DIRECTORY_OF_IMAGES )
50a14534
EB
4194 {
4195 GtkWidget *tiles_width_label, *tiles_height_label;
4196
4c77d5e0 4197 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
50a14534 4198 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4c77d5e0 4199 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
50a14534 4200 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
9b082b39
RN
4201 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4202 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4203 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4204 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
50a14534
EB
4205
4206 current_window_pass_along [4] = tiles_width_spin;
4207 current_window_pass_along [5] = tiles_height_spin;
4208 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4209 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4210 }
9b082b39 4211 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
50a14534
EB
4212 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4213 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
267e3ce7 4214 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
50a14534
EB
4215
4216 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4217
1fb999d3
RN
4218 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4219
9b082b39 4220 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
50a14534
EB
4221
4222 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4223 {
4224 gtk_widget_hide ( GTK_WIDGET(dialog) );
310cbaf7 4225
49d43d14 4226 gchar *fn = draw_image_filename ( vw, img_gen );
310cbaf7
RN
4227 if ( !fn )
4228 return;
4229
c60f82df
RN
4230 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4231 gdouble zoom = pow (2, active_z-2 );
267e3ce7 4232
49d43d14 4233 if ( img_gen == VW_GEN_SINGLE_IMAGE )
50a14534
EB
4234 save_image_file ( vw, fn,
4235 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4236 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
267e3ce7 4237 zoom,
49d43d14
RN
4238 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4239 FALSE );
4240 else if ( img_gen == VW_GEN_KMZ_FILE ) {
4241 // Remove some viewport overlays as these aren't useful in KMZ file.
4242 gboolean restore_xhair = vik_viewport_get_draw_centermark ( vw->viking_vvp );
4243 if ( restore_xhair )
4244 vik_viewport_set_draw_centermark ( vw->viking_vvp, FALSE );
4245 gboolean restore_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
4246 if ( restore_scale )
4247 vik_viewport_set_draw_scale ( vw->viking_vvp, FALSE );
4248
4249 save_image_file ( vw,
4250 fn,
4251 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4252 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4253 zoom,
4254 FALSE, // JPG
4255 TRUE );
4256
4257 if ( restore_xhair )
4258 vik_viewport_set_draw_centermark ( vw->viking_vvp, TRUE );
4259 if ( restore_scale )
4260 vik_viewport_set_draw_scale ( vw->viking_vvp, TRUE );
4261 if ( restore_xhair || restore_scale )
4262 draw_update ( vw );
4263 }
50a14534 4264 else {
e5657444
RN
4265 // NB is in UTM mode ATM
4266 save_image_dir ( vw, fn,
50a14534
EB
4267 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4268 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
267e3ce7 4269 zoom,
50a14534
EB
4270 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4271 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4272 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
50a14534 4273 }
310cbaf7
RN
4274
4275 g_free ( fn );
50a14534
EB
4276 }
4277 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4278}
4279
49d43d14
RN
4280static void draw_to_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4281{
4282 if ( vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ) {
4283 a_dialog_error_msg ( GTK_WINDOW(vw), _("This feature is not available in UTM mode") );
4284 return;
4285 }
4286 // 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]
4287 // (it does *not* include a full KML dump of every track, waypoint etc...)
4288 draw_to_image_file ( vw, VW_GEN_KMZ_FILE );
4289}
50a14534 4290
e4afc73a 4291static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
50a14534 4292{
49d43d14 4293 draw_to_image_file ( vw, VW_GEN_SINGLE_IMAGE );
50a14534
EB
4294}
4295
e4afc73a 4296static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
50a14534 4297{
49d43d14 4298 draw_to_image_file ( vw, VW_GEN_DIRECTORY_OF_IMAGES );
50a14534
EB
4299}
4300
ab922442
RN
4301/**
4302 *
4303 */
4304static void import_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4305{
4306 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Open File"),
4307 GTK_WINDOW(vw),
4308 GTK_FILE_CHOOSER_ACTION_OPEN,
4309 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4310 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
4311 NULL);
4312
4313 GtkFileFilter *filter;
4314 filter = gtk_file_filter_new ();
4315 gtk_file_filter_set_name ( filter, _("KMZ") );
4316 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4317 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4318 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4319 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4320
4321 filter = gtk_file_filter_new ();
4322 gtk_file_filter_set_name( filter, _("All") );
4323 gtk_file_filter_add_pattern ( filter, "*" );
4324 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4325 // Default to any file - same as before open filters were added
4326 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4327
4328 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4329 gchar *fn = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
4330 // TODO convert ans value into readable explaination of failure...
4331 int ans = kmz_open_file ( fn, vw->viking_vvp, vw->viking_vlp );
4332 if ( ans )
4333 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to import %s."), fn );
4334
4335 draw_update ( vw );
4336 }
4337 gtk_widget_destroy ( dialog );
4338}
4339
42f34743
QT
4340static void print_cb ( GtkAction *a, VikWindow *vw )
4341{
4342 a_print(vw, vw->viking_vvp);
4343}
4344
50a14534 4345/* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
e4afc73a 4346static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
50a14534 4347{
75b7457a
RN
4348 const gchar *name = gtk_action_get_name(a);
4349 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4350 if ( tbutton )
4351 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4352
e4afc73a 4353 VikViewportDrawMode drawmode;
75b7457a 4354 if (!g_strcmp0(name, "ModeUTM")) {
e4afc73a
EB
4355 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4356 }
75b7457a 4357 else if (!g_strcmp0(name, "ModeLatLon")) {
d587678a
GB
4358 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4359 }
75b7457a 4360 else if (!g_strcmp0(name, "ModeExpedia")) {
e4afc73a
EB
4361 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4362 }
75b7457a 4363 else if (!g_strcmp0(name, "ModeMercator")) {
e4afc73a
EB
4364 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4365 }
4366 else {
8dc8da82 4367 g_critical("Houston, we've had a problem.");
e4afc73a
EB
4368 return;
4369 }
4370
50a14534
EB
4371 if ( !vw->only_updating_coord_mode_ui )
4372 {
4373 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4374 if ( olddrawmode != drawmode )
4375 {
4376 /* this takes care of coord mode too */
4377 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4378 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4379 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4380 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4381 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4382 }
4383 draw_update ( vw );
4384 }
4385 }
4386}
4387
87d052f9 4388static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
35c7c0ba 4389{
75b7457a 4390 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
48df6aa3 4391 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
75b7457a
RN
4392 if ( !check_box )
4393 return;
4394 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
1657065a 4395 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
35c7c0ba
EB
4396 draw_update ( vw );
4397}
4398
87d052f9 4399static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
c933487f 4400{
75b7457a 4401 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
48df6aa3 4402 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
75b7457a
RN
4403 if ( !check_box )
4404 return;
4405 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
1657065a 4406 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
c933487f
QT
4407 draw_update ( vw );
4408}
4409
87d052f9 4410static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
2afcef36 4411{
75b7457a 4412 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2afcef36 4413 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
75b7457a
RN
4414 if ( !check_box )
4415 return;
4416 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
87d052f9
RN
4417 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4418 draw_update ( vw );
2afcef36
RN
4419}
4420
e4afc73a 4421static void set_bg_color ( GtkAction *a, VikWindow *vw )
50a14534 4422{
4c77d5e0 4423 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
50a14534 4424 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
9b082b39
RN
4425 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4426 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
50a14534
EB
4427 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4428 {
9b082b39 4429 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
50a14534
EB
4430 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4431 draw_update ( vw );
4432 }
4433 g_free ( color );
4434 gtk_widget_destroy ( colorsd );
4435}
4436
480fb7e1
RN
4437static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4438{
4439 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4440 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
9b082b39
RN
4441 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4442 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
480fb7e1
RN
4443 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4444 {
9b082b39 4445 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
480fb7e1
RN
4446 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4447 draw_update ( vw );
4448 }
4449 g_free ( color );
4450 gtk_widget_destroy ( colorsd );
4451}
4452
941aa6e9 4453
941aa6e9
AF
4454/***********************************************************************************************
4455 ** GUI Creation
4456 ***********************************************************************************************/
4457
e4afc73a 4458static GtkActionEntry entries[] = {
5515e2d3
JJ
4459 { "File", NULL, N_("_File"), 0, 0, 0 },
4460 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4461 { "View", NULL, N_("_View"), 0, 0, 0 },
48df6aa3 4462 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
5515e2d3
JJ
4463 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4464 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4465 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4466 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
92806042 4467 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
5515e2d3
JJ
4468 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4469
4470 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
06526f88
RN
4471 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4472 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4473 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
0bb36e78
RN
4474 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4475 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
d6de71f9 4476 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
7f95fd54 4477 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
9c4555df
GB
4478#ifdef VIK_CONFIG_OPENSTREETMAP
4479 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3cc57413 4480 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
9c4555df 4481#endif
1ef9e637 4482#ifdef VIK_CONFIG_GEOCACHES
06526f88 4483 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
f75d0233
RN
4484#endif
4485#ifdef VIK_CONFIG_GEOTAG
4486 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3c29a566 4487#endif
c6acf18d 4488 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
3c29a566
RN
4489#ifdef VIK_CONFIG_GEONAMES
4490 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
1ef9e637 4491#endif
5515e2d3 4492 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
06526f88 4493 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3f46fe76 4494 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
49d43d14 4495#ifdef HAVE_ZIP_H
ab922442 4496 { "ImportKMZ", GTK_STOCK_CONVERT, N_("Import KMZ _Map File..."), NULL, N_("Import a KMZ file"), (GCallback)import_kmz_file_cb },
49d43d14
RN
4497 { "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 },
4498#endif
56571470 4499 { "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 4500 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
5515e2d3 4501 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
5515e2d3
JJ
4502 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4503 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4504
be5554c5
RN
4505 { "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 },
4506 { "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 4507 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
cbd293bb 4508 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
402fee6c 4509 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
150618fe 4510 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
6b59f63d 4511 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
22f536ea
RN
4512 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4513 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4514 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4515 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4516 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
cbd293bb
RN
4517 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4518 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4519 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4520 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
22f536ea 4521 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
5515e2d3 4522
22f536ea
RN
4523 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4524 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4525 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4526 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
5515e2d3 4527 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
7c8bddff 4528 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
06526f88 4529 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
5210c3d3 4530 { "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 4531 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
6d61ba92 4532 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
22f536ea 4533 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
5515e2d3 4534
22f536ea
RN
4535 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4536 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
e4afc73a
EB
4537};
4538
a3040a49
RN
4539static GtkActionEntry debug_entries[] = {
4540 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
fca1f3ad 4541 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
a3040a49
RN
4542};
4543
0bb36e78
RN
4544static GtkActionEntry entries_gpsbabel[] = {
4545 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
88ca6420
RN
4546 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4547 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
0bb36e78
RN
4548};
4549
c0c5893f
RN
4550static GtkActionEntry entries_geojson[] = {
4551 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4552};
4553
e4afc73a
EB
4554/* Radio items */
4555static GtkRadioActionEntry mode_entries[] = {
123fff51
RN
4556 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4557 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4558 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4559 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
50a14534
EB
4560};
4561
35c7c0ba 4562static GtkToggleActionEntry toggle_entries[] = {
87d052f9
RN
4563 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4564 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4565 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
cbd293bb 4566 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
48df6aa3 4567 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
a459ee10 4568 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
48df6aa3
RN
4569 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4570 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
35c7c0ba
EB
4571};
4572
75b7457a
RN
4573// This must match the toggle entries order above
4574static gpointer toggle_entries_toolbar_cb[] = {
4575 (GCallback)tb_set_draw_scale,
4576 (GCallback)tb_set_draw_centermark,
4577 (GCallback)tb_set_draw_highlight,
4578 (GCallback)tb_full_screen_cb,
4579 (GCallback)tb_view_side_panel_cb,
4580 (GCallback)tb_view_statusbar_cb,
4581 (GCallback)tb_view_toolbar_cb,
4582 (GCallback)tb_view_main_menu_cb,
4583};
4584
a527cfff 4585#include "menu.xml.h"
e4afc73a 4586static void window_create_ui( VikWindow *window )
50a14534 4587{
e4afc73a
EB
4588 GtkUIManager *uim;
4589 GtkActionGroup *action_group;
50a14534 4590 GtkAccelGroup *accel_group;
e4afc73a 4591 GError *error;
e4afc73a
EB
4592 guint i, j, mid;
4593 GtkIconFactory *icon_factory;
4594 GtkIconSet *icon_set;
e4afc73a
EB
4595 GtkRadioActionEntry *tools = NULL, *radio;
4596 guint ntools;
4597
4598 uim = gtk_ui_manager_new ();
4599 window->uim = uim;
4600
9593a4c9
EB
4601 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4602 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
576cbd17 4603 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
a47bfefa 4604 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
941aa6e9 4605
75b7457a
RN
4606 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4607 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4608 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4609 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4610
e4afc73a 4611 error = NULL;
a527cfff 4612 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
e4afc73a
EB
4613 g_error_free (error);
4614 exit (1);
4615 }
4616
4617 action_group = gtk_action_group_new ("MenuActions");
5515e2d3 4618 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
e4afc73a 4619 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
35c7c0ba 4620 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
6a9ff0ee 4621 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
4622 if ( vik_debug ) {
4623 if ( gtk_ui_manager_add_ui_from_string ( uim,
fca1f3ad
RN
4624 "<ui><menubar name='MainMenu'><menu action='Help'>"
4625 "<menuitem action='MapCacheInfo'/>"
4626 "<menuitem action='BackForwardInfo'/>"
4627 "</menu></menubar></ui>",
a3040a49
RN
4628 -1, NULL ) ) {
4629 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4630 }
4631 }
4632
75b7457a
RN
4633 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4634 if ( entries[i].callback )
4635 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4636 }
4637
4638 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4639 g_print ( "Broken entries definitions\n" );
4640 exit (1);
4641 }
4642 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4643 if ( toggle_entries_toolbar_cb[i] )
4644 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4645 }
4646
4647 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4648 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4649 }
e4afc73a 4650
0bb36e78 4651 // Use this to see if GPSBabel is available:
430a37a9 4652 if ( a_babel_available () ) {
88ca6420
RN
4653 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4654 if ( gtk_ui_manager_add_ui_from_string ( uim,
4655 "<ui>" \
4656 "<menubar name='MainMenu'>" \
4657 "<menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu>" \
4658 "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPS'/></menu></menu>" \
4659 "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPSBabel'/></menu></menu>" \
4660 "</menubar>" \
4661 "</ui>",
4662 -1, &error ) )
0bb36e78
RN
4663 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4664 }
4665
c0c5893f
RN
4666 // GeoJSON import capability
4667 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4668 if ( gtk_ui_manager_add_ui_from_string ( uim,
4669 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4670 -1, &error ) )
4671 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4672 }
4673
e4afc73a 4674 icon_factory = gtk_icon_factory_new ();
ee36ac4f 4675 gtk_icon_factory_add_default (icon_factory);
e4afc73a
EB
4676
4677 register_vik_icons(icon_factory);
4678
79dce0cb
RN
4679 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4680 // so that it can be applied to the UI in one action group add function call below
e4afc73a 4681 ntools = 0;
79dce0cb 4682 for (i=0; i<window->vt->n_tools; i++) {
e4afc73a
EB
4683 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4684 radio = &tools[ntools];
4685 ntools++;
79dce0cb 4686 *radio = window->vt->tools[i].ti.radioActionEntry;
e4afc73a 4687 radio->value = ntools;
79dce0cb 4688 }
e4afc73a
EB
4689
4690 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4691 GtkActionEntry action;
4692 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4693 vik_layer_get_interface(i)->name,
4694 vik_layer_get_interface(i)->name,
4695 GTK_UI_MANAGER_MENUITEM, FALSE);
4696
4697 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4698 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4699 gtk_icon_set_unref (icon_set);
4700
4701 action.name = vik_layer_get_interface(i)->name;
4702 action.stock_id = vik_layer_get_interface(i)->name;
1ac01167 4703 action.label = g_strdup_printf( _("New _%s Layer"), _(vik_layer_get_interface(i)->name));
75078768 4704 action.accelerator = vik_layer_get_interface(i)->accelerator;
e4afc73a
EB
4705 action.tooltip = NULL;
4706 action.callback = (GCallback)menu_addlayer_cb;
4707 gtk_action_group_add_actions(action_group, &action, 1, window);
4708
644eea0e
RN
4709 g_free ( (gchar*)action.label );
4710
e4afc73a
EB
4711 if ( vik_layer_get_interface(i)->tools_count ) {
4712 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
e4afc73a
EB
4713 }
4714
79dce0cb 4715 // Further tool copying for to apply to the UI, also apply menu UI setup
e4afc73a
EB
4716 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4717 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4718 radio = &tools[ntools];
4719 ntools++;
4720
e4afc73a 4721 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
79dce0cb
RN
4722 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4723 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
e4afc73a 4724 GTK_UI_MANAGER_MENUITEM, FALSE);
e4afc73a 4725
9593a4c9 4726 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
75b7457a 4727 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
941aa6e9 4728
79dce0cb
RN
4729 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4730 // Overwrite with actual number to use
e4afc73a
EB
4731 radio->value = ntools;
4732 }
a7023a1b
RN
4733
4734 GtkActionEntry action_dl;
644eea0e 4735 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
a7023a1b
RN
4736 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4737 vik_layer_get_interface(i)->name,
644eea0e 4738 layername,
a7023a1b 4739 GTK_UI_MANAGER_MENUITEM, FALSE);
644eea0e 4740 g_free (layername);
a7023a1b
RN
4741
4742 // For default layers use action names of the form 'Layer<LayerName>'
4743 // This is to avoid clashing with just the layer name used above for the tool actions
4744 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4745 action_dl.stock_id = NULL;
1ac01167 4746 action_dl.label = g_strconcat(_(vik_layer_get_interface(i)->name), _("..."), NULL);
a7023a1b
RN
4747 action_dl.accelerator = NULL;
4748 action_dl.tooltip = NULL;
4749 action_dl.callback = (GCallback)layer_defaults_cb;
4750 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
644eea0e
RN
4751 g_free ( (gchar*)action_dl.name );
4752 g_free ( (gchar*)action_dl.label );
e4afc73a 4753 }
ee36ac4f 4754 g_object_unref (icon_factory);
50a14534 4755
75b7457a 4756 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
e4afc73a 4757 g_free(tools);
50a14534 4758
e4afc73a 4759 gtk_ui_manager_insert_action_group (uim, action_group, 0);
50a14534 4760
79845167
QT
4761 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4762 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4763 GtkAction *action = gtk_action_group_get_action(action_group,
79dce0cb 4764 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
79845167
QT
4765 g_object_set(action, "sensitive", FALSE, NULL);
4766 }
4767 }
a147baa7
RN
4768
4769 // This is done last so we don't need to track the value of mid anymore
4770 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4771
79845167
QT
4772 window->action_group = action_group;
4773
e4afc73a
EB
4774 accel_group = gtk_ui_manager_get_accel_group (uim);
4775 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4776 gtk_ui_manager_ensure_update (uim);
13505702
GB
4777
4778 setup_recent_files(window);
4d7d2041
RN
4779
4780 if ( vik_routing_number_of_engines(VIK_ROUTING_METHOD_DIRECTIONS) == 0 ) {
4781 GtkWidget *widget = gtk_ui_manager_get_widget ( uim, "/ui/MainMenu/File/Acquire/AcquireRouting" );
4782 if ( widget ) {
4783 g_object_set ( widget, "sensitive", FALSE, NULL );
4784 }
4785 g_warning ( "No direction routing engines available" );
4786 }
e4afc73a 4787}
50a14534 4788
50a14534 4789
79dce0cb
RN
4790// TODO - add method to add tool icons defined from outside this file
4791// and remove the reverse dependency on icon definition from this file
e4afc73a 4792static struct {
4bd45256 4793 const GdkPixdata *data;
e4afc73a
EB
4794 gchar *stock_id;
4795} stock_icons[] = {
79dce0cb 4796 { &mover_22_pixbuf, "vik-icon-pan" },
5bfafde9
GB
4797 { &zoom_18_pixbuf, "vik-icon-zoom" },
4798 { &ruler_18_pixbuf, "vik-icon-ruler" },
a47bfefa 4799 { &select_18_pixbuf, "vik-icon-select" },
e37b2a6d 4800 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
79dce0cb 4801 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
06a6af5b 4802 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
79dce0cb
RN
4803 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4804 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4805 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4806 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4807 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4808 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4809 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4810 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
e4afc73a
EB
4811};
4812
e4afc73a
EB
4813static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4814
4815static void
4816register_vik_icons (GtkIconFactory *icon_factory)
4817{
4818 GtkIconSet *icon_set;
e4afc73a 4819 gint i;
e4afc73a
EB
4820
4821 for (i = 0; i < n_stock_icons; i++) {
4bd45256
EB
4822 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4823 stock_icons[i].data, FALSE, NULL ));
e4afc73a
EB
4824 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4825 gtk_icon_set_unref (icon_set);
4826 }
50a14534 4827}
bce3a7b0 4828
9d7c24ed
RN
4829gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4830{
4831 return vw->selected_vtl;
4832}
4833
4834void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4835{
113c74f6
RN
4836 vw->selected_vtl = vtl;
4837 vw->containing_vtl = vtl;
9d7c24ed
RN
4838 /* Clear others */
4839 vw->selected_track = NULL;
4840 vw->selected_tracks = NULL;
4841 vw->selected_waypoint = NULL;
4842 vw->selected_waypoints = NULL;
04f36d92
RN
4843 // Set highlight thickness
4844 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
9d7c24ed
RN
4845}
4846
1c70a947 4847GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
9d7c24ed
RN
4848{
4849 return vw->selected_tracks;
4850}
4851
1c70a947 4852void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
9d7c24ed 4853{
1c70a947 4854 vw->selected_tracks = ght;
113c74f6 4855 vw->containing_vtl = vtl;
9d7c24ed
RN
4856 /* Clear others */
4857 vw->selected_vtl = NULL;
4858 vw->selected_track = NULL;
4859 vw->selected_waypoint = NULL;
4860 vw->selected_waypoints = NULL;
04f36d92
RN
4861 // Set highlight thickness
4862 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
9d7c24ed
RN
4863}
4864
4865gpointer vik_window_get_selected_track ( VikWindow *vw )
4866{
4867 return vw->selected_track;
4868}
4869
b16effab 4870void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
9d7c24ed
RN
4871{
4872 vw->selected_track = vt;
113c74f6 4873 vw->containing_vtl = vtl;
9d7c24ed
RN
4874 /* Clear others */
4875 vw->selected_vtl = NULL;
4876 vw->selected_tracks = NULL;
4877 vw->selected_waypoint = NULL;
4878 vw->selected_waypoints = NULL;
04f36d92
RN
4879 // Set highlight thickness
4880 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
9d7c24ed 4881}
04f36d92 4882
1c70a947 4883GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
9d7c24ed
RN
4884{
4885 return vw->selected_waypoints;
4886}
4887
1c70a947 4888void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
9d7c24ed 4889{
1c70a947 4890 vw->selected_waypoints = ght;
113c74f6 4891 vw->containing_vtl = vtl;
9d7c24ed
RN
4892 /* Clear others */
4893 vw->selected_vtl = NULL;
4894 vw->selected_track = NULL;
4895 vw->selected_tracks = NULL;
4896 vw->selected_waypoint = NULL;
4897}
4898
4899gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4900{
4901 return vw->selected_waypoint;
4902}
4903
b16effab 4904void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
9d7c24ed
RN
4905{
4906 vw->selected_waypoint = vwp;
113c74f6 4907 vw->containing_vtl = vtl;
9d7c24ed
RN
4908 /* Clear others */
4909 vw->selected_vtl = NULL;
4910 vw->selected_track = NULL;
4911 vw->selected_tracks = NULL;
4912 vw->selected_waypoints = NULL;
4913}
4914
4915gboolean vik_window_clear_highlight ( VikWindow *vw )
4916{
4917 gboolean need_redraw = FALSE;
b08ee5b7 4918 vw->containing_vtl = NULL;
9d7c24ed
RN
4919 if ( vw->selected_vtl != NULL ) {
4920 vw->selected_vtl = NULL;
4921 need_redraw = TRUE;
4922 }
4923 if ( vw->selected_track != NULL ) {
4924 vw->selected_track = NULL;
4925 need_redraw = TRUE;
4926 }
4927 if ( vw->selected_tracks != NULL ) {
4928 vw->selected_tracks = NULL;
4929 need_redraw = TRUE;
4930 }
4931 if ( vw->selected_waypoint != NULL ) {
4932 vw->selected_waypoint = NULL;
4933 need_redraw = TRUE;
4934 }
4935 if ( vw->selected_waypoints != NULL ) {
4936 vw->selected_waypoints = NULL;
4937 need_redraw = TRUE;
4938 }
4939 return need_redraw;
4940}
fa51adec 4941
86642fdf
RN
4942/**
4943 * May return NULL if the window no longer exists
4944 */
fa51adec
RN
4945GThread *vik_window_get_thread ( VikWindow *vw )
4946{
86642fdf
RN
4947 if ( vw )
4948 return vw->thread;
4949 return NULL;
fa51adec 4950}