]> git.street.me.uk Git - andy/viking.git/blame - src/vikwindow.c
Split background threads into two pools.
[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>
75b7457a 6 * Copyright (C) 2012-2014, Rob Norris <rw_norris@hotmail.com>
50a14534
EB
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
4c77d5e0
GB
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
50a14534
EB
28#include "viking.h"
29#include "background.h"
1d1bc3c1 30#include "acquire.h"
7b3479e3 31#include "datasources.h"
c0c5893f 32#include "geojson.h"
34e71b99 33#include "vikgoto.h"
071da616 34#include "dems.h"
7c259702 35#include "mapcache.h"
42f34743 36#include "print.h"
17a1f8f9 37#include "preferences.h"
75b7457a 38#include "toolbar.h"
a7023a1b 39#include "viklayer_defaults.h"
f2f2f7bf 40#include "icons/icons.h"
92806042 41#include "vikexttools.h"
82993cc7 42#include "vikexttool_datasources.h"
9be0449f 43#include "garminsymbols.h"
6b59f63d 44#include "vikmapslayer.h"
3c29a566 45#include "geonamessearch.h"
74562734 46#include "vikutils.h"
7b97b14c 47#include "dir.h"
50a14534 48
8c00358d 49#ifdef HAVE_STDLIB_H
e4afc73a 50#include <stdlib.h>
8c00358d
GB
51#endif
52#ifdef HAVE_MATH_H
50a14534 53#include <math.h>
8c00358d
GB
54#endif
55#ifdef HAVE_STRING_H
50a14534 56#include <string.h>
8c00358d 57#endif
50a14534 58#include <ctype.h>
f83131b9
MA
59#include <glib.h>
60#include <glib/gstdio.h>
1d1bc3c1 61#include <glib/gprintf.h>
4c77d5e0 62#include <glib/gi18n.h>
314084b8 63#include <gio/gio.h>
7622022f 64#include <gdk/gdkkeysyms.h>
50a14534 65
8a13dbd2
RN
66// This seems rather arbitary, quite large and pointless
67// I mean, if you have a thousand windows open;
68// why not be allowed to open a thousand more...
69#define MAX_WINDOWS 1024
70static guint window_count = 0;
98acb9a1 71static GSList *window_list = NULL;
8a13dbd2 72
3570ad57
QT
73#define VIKING_WINDOW_WIDTH 1000
74#define VIKING_WINDOW_HEIGHT 800
50a14534
EB
75#define DRAW_IMAGE_DEFAULT_WIDTH 1280
76#define DRAW_IMAGE_DEFAULT_HEIGHT 1024
77#define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
78
79static void window_finalize ( GObject *gob );
80static GObjectClass *parent_class;
81
4c77d5e0 82static void window_set_filename ( VikWindow *vw, const gchar *filename );
4522c4ff 83static const gchar *window_get_filename ( VikWindow *vw );
50a14534 84
8a13dbd2
RN
85static VikWindow *window_new ();
86
50a14534
EB
87static void draw_update ( VikWindow *vw );
88
e4afc73a 89static void newwindow_cb ( GtkAction *a, VikWindow *vw );
50a14534 90
8a13dbd2
RN
91// Signals
92static void open_window ( VikWindow *vw, GSList *files );
8a13dbd2
RN
93static void destroy_window ( GtkWidget *widget,
94 gpointer data );
95
50a14534
EB
96/* Drawing & stuff */
97
98static gboolean delete_event( VikWindow *vw );
99
777e2d4d
EB
100static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
101
be5554c5 102static void center_changed_cb ( VikWindow *vw );
bce3a7b0 103static void window_configure_event ( VikWindow *vw );
50a14534
EB
104static void draw_sync ( VikWindow *vw );
105static void draw_redraw ( VikWindow *vw );
941aa6e9 106static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
50a14534
EB
107static void draw_click ( VikWindow *vw, GdkEventButton *event );
108static void draw_release ( VikWindow *vw, GdkEventButton *event );
109static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
e4afc73a
EB
110static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
111static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
6b59f63d 112static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
50a14534 113
c8430548 114static void draw_status ( VikWindow *vw );
50a14534
EB
115
116/* End Drawing Functions */
117
87d052f9
RN
118static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
119static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
120static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
121
e4afc73a
EB
122static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
123static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
124static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
941aa6e9
AF
125
126/* tool management */
127typedef struct {
128 VikToolInterface ti;
129 gpointer state;
9593a4c9 130 gint layer_type;
941aa6e9 131} toolbox_tool_t;
9593a4c9 132#define TOOL_LAYER_TYPE_NONE -1
941aa6e9
AF
133
134typedef struct {
135 int active_tool;
136 int n_tools;
137 toolbox_tool_t *tools;
138 VikWindow *vw;
139} toolbox_tools_t;
140
75b7457a
RN
141static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
142static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
941aa6e9 143static toolbox_tools_t* toolbox_create(VikWindow *vw);
9593a4c9 144static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
941aa6e9
AF
145static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
146static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
f2f2f7bf 147static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
941aa6e9 148static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
dc2c040e 149static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
941aa6e9
AF
150static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
151
50a14534 152
941aa6e9 153/* ui creation */
e4afc73a 154static void window_create_ui( VikWindow *window );
941aa6e9 155static void register_vik_icons (GtkIconFactory *icon_factory);
50a14534 156
941aa6e9 157/* i/o */
e4afc73a
EB
158static void load_file ( GtkAction *a, VikWindow *vw );
159static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
160static gboolean save_file ( GtkAction *a, VikWindow *vw );
2bf7cadd 161static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
50a14534
EB
162static gboolean window_save ( VikWindow *vw );
163
164struct _VikWindow {
165 GtkWindow gtkwindow;
67130220 166 GtkWidget *hpaned;
50a14534
EB
167 VikViewport *viking_vvp;
168 VikLayersPanel *viking_vlp;
169 VikStatusbar *viking_vs;
75b7457a 170 VikToolbar *viking_vtb;
50a14534 171
75b7457a
RN
172 GtkWidget *main_vbox;
173 GtkWidget *menu_hbox;
e4afc73a 174
55d3a53c
RN
175 GdkCursor *busy_cursor;
176 GdkCursor *viewport_cursor; // only a reference
177
941aa6e9 178 /* tool management state */
50a14534 179 guint current_tool;
941aa6e9 180 toolbox_tools_t *vt;
50a14534
EB
181 guint16 tool_layer_id;
182 guint16 tool_tool_id;
183
79845167
QT
184 GtkActionGroup *action_group;
185
75b7457a
RN
186 // Display controls
187 // NB scale, centermark and highlight are in viewport.
188 gboolean show_full_screen;
189 gboolean show_side_panel;
190 gboolean show_statusbar;
191 gboolean show_toolbar;
192 gboolean show_main_menu;
193
c29a3198 194 gboolean select_move;
b71eff77 195 gboolean pan_move;
50a14534 196 gint pan_x, pan_y;
beb9d63a
RN
197 gint delayed_pan_x, delayed_pan_y; // Temporary storage
198 gboolean single_click_pending;
50a14534
EB
199
200 guint draw_image_width, draw_image_height;
201 gboolean draw_image_save_as_png;
202
203 gchar *filename;
204 gboolean modified;
a14f46cf 205 VikLoadType_t loaded_type;
50a14534
EB
206
207 GtkWidget *open_dia, *save_dia;
f2a1ca71 208 GtkWidget *save_img_dia, *save_img_dir_dia;
50a14534
EB
209
210 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
e4afc73a 211 GtkUIManager *uim;
c9177aae 212
fa51adec 213 GThread *thread;
c9177aae
QT
214 /* half-drawn update */
215 VikLayer *trigger;
216 VikCoord trigger_center;
9d7c24ed
RN
217
218 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
219 /* Only one of these items can be selected at the same time */
220 gpointer selected_vtl; /* notionally VikTrwLayer */
1c70a947 221 GHashTable *selected_tracks;
9d7c24ed 222 gpointer selected_track; /* notionally VikTrack */
1c70a947 223 GHashTable *selected_waypoints;
9d7c24ed 224 gpointer selected_waypoint; /* notionally VikWaypoint */
43f2e1da 225 /* only use for individual track or waypoint */
113c74f6
RN
226 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
227 gpointer containing_vtl; /* notionally VikTrwLayer */
50a14534
EB
228};
229
230enum {
576cbd17
GB
231 TOOL_PAN = 0,
232 TOOL_ZOOM,
50a14534 233 TOOL_RULER,
a47bfefa 234 TOOL_SELECT,
50a14534
EB
235 TOOL_LAYER,
236 NUMBER_OF_TOOLS
237};
238
239enum {
240 VW_NEWWINDOW_SIGNAL,
241 VW_OPENWINDOW_SIGNAL,
242 VW_LAST_SIGNAL
243};
244
245static guint window_signals[VW_LAST_SIGNAL] = { 0 };
246
79dce0cb 247// TODO get rid of this as this is unnecessary duplication...
a47bfefa 248static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
50a14534 249
f3c798e9 250G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
50a14534 251
014128f6
QT
252VikViewport * vik_window_viewport(VikWindow *vw)
253{
254 return(vw->viking_vvp);
255}
256
55477ce6
RN
257VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
258{
259 return(vw->viking_vlp);
260}
261
c06a63ad
RN
262/**
263 * Returns the statusbar for the window
264 */
265VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
266{
267 return vw->viking_vs;
268}
269
1b14d0d2
RN
270/**
271 * Returns the 'project' filename
272 */
273const gchar *vik_window_get_filename (VikWindow *vw)
274{
275 return vw->filename;
276}
277
39ae014f
RN
278typedef struct {
279 VikStatusbar *vs;
280 vik_statusbar_type_t vs_type;
281 gchar* message; // Always make a copy of this data
282} statusbar_idle_data;
90142302
RN
283
284/**
285 * For the actual statusbar update!
286 */
39ae014f 287static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
90142302 288{
39ae014f
RN
289 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
290 g_free ( sid->message );
291 g_free ( sid );
3ceb0792 292 return FALSE;
90142302
RN
293}
294
295/**
39ae014f
RN
296 * vik_window_statusbar_update:
297 * @vw: The main window in which the statusbar will be updated.
298 * @message: The string to be displayed. This is copied.
299 * @vs_type: The part of the statusbar to be updated.
300 *
301 * This updates any part of the statusbar with the new string.
302 * It handles calling from the main thread or any background thread
303 * ATM this mostly used from background threads - as from the main thread
304 * one may use the vik_statusbar_set_message() directly.
90142302 305 */
39ae014f 306void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
90142302 307{
86642fdf
RN
308 GThread *thread = vik_window_get_thread ( vw );
309 if ( !thread )
310 // Do nothing
311 return;
312
39ae014f
RN
313 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
314 sid->vs = vw->viking_vs;
315 sid->vs_type = vs_type;
316 sid->message = g_strdup ( message );
317
86642fdf 318 if ( g_thread_self() == thread ) {
39ae014f
RN
319 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
320 }
321 else {
322 // From a background thread
323 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
324 }
90142302
RN
325}
326
8a13dbd2
RN
327// Actual signal handlers
328static void destroy_window ( GtkWidget *widget,
329 gpointer data )
330{
331 if ( ! --window_count )
332 gtk_main_quit ();
333}
334
f700bbd9
RN
335#define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
336#define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
337#define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
338// Menubar setting to off is never auto saved in case it's accidentally turned off
339// It's not so obvious so to recover the menu visibility.
340// Thus this value is for setting manually via editting the settings file directly
341#define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
342
8a13dbd2
RN
343VikWindow *vik_window_new_window ()
344{
345 if ( window_count < MAX_WINDOWS )
346 {
347 VikWindow *vw = window_new ();
348
349 g_signal_connect (G_OBJECT (vw), "destroy",
350 G_CALLBACK (destroy_window), NULL);
351 g_signal_connect (G_OBJECT (vw), "newwindow",
352 G_CALLBACK (vik_window_new_window), NULL);
353 g_signal_connect (G_OBJECT (vw), "openwindow",
354 G_CALLBACK (open_window), NULL);
8a13dbd2
RN
355
356 gtk_widget_show_all ( GTK_WIDGET(vw) );
357
1219fd23
RN
358 if ( a_vik_get_restore_window_state() ) {
359 // These settings are applied after the show all as these options hide widgets
360 gboolean sidepanel;
361 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
362 if ( ! sidepanel ) {
363 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
364 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
365 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
366 }
367
368 gboolean statusbar;
369 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
370 if ( ! statusbar ) {
371 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
372 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
373 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
374 }
375
376 gboolean toolbar;
377 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
378 if ( ! toolbar ) {
75b7457a 379 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
1219fd23
RN
380 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
381 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
382 }
383
384 gboolean menubar;
385 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
386 if ( ! menubar ) {
387 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
388 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
389 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
390 }
391 }
8a13dbd2
RN
392 window_count++;
393
394 return vw;
395 }
396 return NULL;
397}
398
a14f46cf
RN
399/**
400 * determine_location_thread:
401 * @vw: The window that will get updated
402 * @threaddata: Data used by our background thread mechanism
403 *
404 * Use the features in vikgoto to determine where we are
405 * Then set up the viewport:
406 * 1. To goto the location
407 * 2. Set an appropriate level zoom for the location type
408 * 3. Some statusbar message feedback
409 */
410static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
411{
412 struct LatLon ll;
413 gchar *name = NULL;
414 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
415
416 int result = a_background_thread_progress ( threaddata, 1.0 );
417 if ( result != 0 ) {
418 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
419 return -1; /* Abort thread */
420 }
421
422 if ( ans ) {
423 // Zoom out a little
424 gdouble zoom = 16.0;
425
426 if ( ans == 2 ) {
427 // Position found with city precision - so zoom out more
428 zoom = 128.0;
429 }
430 else if ( ans == 3 ) {
431 // Position found via country name search - so zoom wayyyy out
432 zoom = 2048.0;
433 }
434
435 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
be5554c5 436 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
a14f46cf
RN
437
438 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
439 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
440 g_free ( name );
441 g_free ( message );
442
443 // Signal to redraw from the background
444 vik_layers_panel_emit_update ( vw->viking_vlp );
445 }
446 else
447 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
448
449 return 0;
450}
451
8fa25c61
RN
452/**
453 * Steps to be taken once initial loading has completed
454 */
455void vik_window_new_window_finish ( VikWindow *vw )
456{
457 // Don't add a map if we've loaded a Viking file already
458 if ( vw->filename )
459 return;
460
7143d1e4
RN
461 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
462 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
463 if ( vw->filename )
464 return;
465 }
466
8fa25c61
RN
467 // Maybe add a default map layer
468 if ( a_vik_get_add_default_map_layer () ) {
0ab35525 469 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
8fa25c61
RN
470 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
471 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
472
473 draw_update ( vw );
474 }
a14f46cf
RN
475
476 // If not loaded any file, maybe try the location lookup
477 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
478 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
479
480 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
481
c75da936
RN
482 a_background_thread ( BACKGROUND_POOL_REMOTE,
483 GTK_WINDOW(vw),
a14f46cf
RN
484 _("Determining location"),
485 (vik_thr_func) determine_location_thread,
486 vw,
487 NULL,
488 NULL,
489 1 );
490 }
491 }
8fa25c61
RN
492}
493
8a13dbd2
RN
494static void open_window ( VikWindow *vw, GSList *files )
495{
496 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
497 GSList *cur_file = files;
498 while ( cur_file ) {
499 // Only open a new window if a viking file
500 gchar *file_name = cur_file->data;
245f17eb 501 if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
8a13dbd2
RN
502 VikWindow *newvw = vik_window_new_window ();
503 if (newvw)
d4a8b54d 504 vik_window_open_file ( newvw, file_name, TRUE );
8a13dbd2
RN
505 }
506 else {
507 vik_window_open_file ( vw, file_name, change_fn );
508 }
509 g_free (file_name);
510 cur_file = g_slist_next (cur_file);
511 }
512 g_slist_free (files);
513}
514// End signals
515
79845167
QT
516void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
517{
518 int i, j, tool_count;
519 VikLayerInterface *layer_interface;
520
521 if (!vw->action_group) return;
522
523 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
524 GtkAction *action;
525 layer_interface = vik_layer_get_interface(i);
526 tool_count = layer_interface->tools_count;
527
528 for (j = 0; j < tool_count; j++) {
529 action = gtk_action_group_get_action(vw->action_group,
79dce0cb 530 layer_interface->tools[j].radioActionEntry.name);
79845167 531 g_object_set(action, "sensitive", i == vl->type, NULL);
75b7457a 532 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
79845167
QT
533 }
534 }
535}
536
50a14534
EB
537static void window_finalize ( GObject *gob )
538{
539 VikWindow *vw = VIK_WINDOW(gob);
540 g_return_if_fail ( vw != NULL );
541
90142302 542 a_background_remove_window ( vw );
50a14534 543
98acb9a1
RN
544 window_list = g_slist_remove ( window_list, vw );
545
55d3a53c 546 gdk_cursor_unref ( vw->busy_cursor );
919ed63e
RN
547 int tt;
548 for (tt = 0; tt < vw->vt->n_tools; tt++ )
549 if ( vw->vt->tools[tt].ti.destroy )
550 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
551 g_free ( vw->vt->tools );
552 g_free ( vw->vt );
55d3a53c 553
75b7457a
RN
554 vik_toolbar_finalize ( vw->viking_vtb );
555
50a14534
EB
556 G_OBJECT_CLASS(parent_class)->finalize(gob);
557}
558
bce3a7b0 559
f3c798e9 560static void vik_window_class_init ( VikWindowClass *klass )
50a14534
EB
561{
562 /* destructor */
563 GObjectClass *object_class;
564
565 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 566 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
567
568 object_class = G_OBJECT_CLASS (klass);
569
570 object_class->finalize = window_finalize;
571
572 parent_class = g_type_class_peek_parent (klass);
573
574}
575
5c0bf50b
GB
576static void zoom_changed (GtkMenuShell *menushell,
577 gpointer user_data)
ac4478f4 578{
5c0bf50b
GB
579 VikWindow *vw = VIK_WINDOW (user_data);
580
581 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
74dd7f07 582 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
5c0bf50b 583
5a210d10 584 gdouble zoom_request = pow (2, active-5 );
ac4478f4 585
c4b6a67d
GB
586 // But has it really changed?
587 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
588 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
589 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
590 // Force drawing update
591 draw_update ( vw );
592 }
c4b6a67d
GB
593}
594
ddd7bb88
RN
595/**
596 * @mpp: The initial zoom level
597 */
598static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
ac4478f4 599{
5c0bf50b 600 GtkWidget *menu = gtk_menu_new ();
5a210d10 601 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 602
5c0bf50b 603 int i;
d7e62ed8 604 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
5c0bf50b
GB
605 {
606 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
607 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
608 gtk_widget_show (item);
74dd7f07 609 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
5c0bf50b
GB
610 }
611
5a210d10 612 gint active = 5 + round ( log (mpp) / log (2) );
ddd7bb88
RN
613 // Ensure value derived from mpp is in bounds of the menu
614 if ( active >= G_N_ELEMENTS(itemLabels) )
615 active = G_N_ELEMENTS(itemLabels) - 1;
616 if ( active < 0 )
617 active = 0;
618 gtk_menu_set_active ( GTK_MENU(menu), active );
619
5c0bf50b 620 return menu;
ac4478f4
RN
621}
622
623static GtkWidget *create_zoom_combo_all_levels ()
624{
1bc1c05b
RN
625 GtkWidget *combo = vik_combo_box_text_new();
626 vik_combo_box_text_append ( combo, "0.25");
627 vik_combo_box_text_append ( combo, "0.5");
628 vik_combo_box_text_append ( combo, "1");
629 vik_combo_box_text_append ( combo, "2");
630 vik_combo_box_text_append ( combo, "4");
631 vik_combo_box_text_append ( combo, "8");
632 vik_combo_box_text_append ( combo, "16");
633 vik_combo_box_text_append ( combo, "32");
634 vik_combo_box_text_append ( combo, "64");
635 vik_combo_box_text_append ( combo, "128");
636 vik_combo_box_text_append ( combo, "256");
637 vik_combo_box_text_append ( combo, "512");
638 vik_combo_box_text_append ( combo, "1024");
639 vik_combo_box_text_append ( combo, "2048");
640 vik_combo_box_text_append ( combo, "4096");
641 vik_combo_box_text_append ( combo, "8192");
642 vik_combo_box_text_append ( combo, "16384");
643 vik_combo_box_text_append ( combo, "32768");
d0bf2f3a 644 /* Create tooltip */
1bc1c05b
RN
645 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
646 return combo;
ac4478f4
RN
647}
648
5c0bf50b
GB
649static gint zoom_popup_handler (GtkWidget *widget)
650{
651 GtkMenu *menu;
652
653 g_return_val_if_fail (widget != NULL, FALSE);
654 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
655
656 /* The "widget" is the menu that was supplied when
657 * g_signal_connect_swapped() was called.
658 */
659 menu = GTK_MENU (widget);
660
661 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
662 1, gtk_get_current_event_time());
663 return TRUE;
664}
665
1e6bae18
RN
666enum {
667 TARGET_URIS,
668};
669
670static void drag_data_received_cb ( GtkWidget *widget,
671 GdkDragContext *context,
672 gint x,
673 gint y,
674 GtkSelectionData *selection_data,
675 guint target_type,
676 guint time,
677 gpointer data )
678{
679 gboolean success = FALSE;
680
681 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
682 switch (target_type) {
683 case TARGET_URIS: {
684 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
685 g_debug ("drag received string:%s \n", str);
686
687 // Convert string into GSList of individual entries for use with our open signal
688 gchar **entries = g_strsplit(str, "\r\n", 0);
689 GSList *filenames = NULL;
690 gint entry_runner = 0;
691 gchar *entry = entries[entry_runner];
692 while (entry) {
07c78458
RN
693 if ( g_strcmp0 ( entry, "" ) ) {
694 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
695 // thus need to convert the text into a plain string
696 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
697 if ( filename )
698 filenames = g_slist_append ( filenames, filename );
699 }
1e6bae18
RN
700 entry_runner++;
701 entry = entries[entry_runner];
702 }
703
704 if ( filenames )
705 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
706 // NB: GSList & contents are freed by main.open_window
707
708 success = TRUE;
709 break;
710 }
711 default: break;
712 }
713 }
714
715 gtk_drag_finish ( context, success, FALSE, time );
716}
717
75b7457a
RN
718static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
719{
720 VikWindow *vw = (VikWindow*)gp;
721 GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
722 if ( action )
723 gtk_action_activate ( action );
724}
725
726static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
727{
728 VikWindow *vw = (VikWindow*)gp;
729 center_changed_cb ( vw );
730}
731
f700bbd9
RN
732#define VIK_SETTINGS_WIN_MAX "window_maximized"
733#define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
734#define VIK_SETTINGS_WIN_WIDTH "window_width"
735#define VIK_SETTINGS_WIN_HEIGHT "window_height"
67130220 736#define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
e7344085
RN
737#define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
738#define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
739#define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
7c8bddff 740#define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
f700bbd9 741
7b97b14c
RN
742#define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
743
f3c798e9 744static void vik_window_init ( VikWindow *vw )
50a14534 745{
79845167 746 vw->action_group = NULL;
50a14534 747
24277274 748 vw->viking_vvp = vik_viewport_new();
50a14534
EB
749 vw->viking_vlp = vik_layers_panel_new();
750 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
751 vw->viking_vs = vik_statusbar_new();
752
941aa6e9 753 vw->vt = toolbox_create(vw);
75b7457a 754 vw->viking_vtb = vik_toolbar_new ();
941aa6e9 755 window_create_ui(vw);
4c77d5e0 756 window_set_filename (vw, NULL);
64e3d6c9 757
55d3a53c
RN
758 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
759
50a14534 760 vw->filename = NULL;
a14f46cf 761 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
50a14534
EB
762 vw->modified = FALSE;
763 vw->only_updating_coord_mode_ui = FALSE;
c29a3198
RN
764
765 vw->select_move = FALSE;
b71eff77 766 vw->pan_move = FALSE;
50a14534 767 vw->pan_x = vw->pan_y = -1;
beb9d63a 768 vw->single_click_pending = FALSE;
e7344085
RN
769
770 gint draw_image_width;
771 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
772 vw->draw_image_width = draw_image_width;
773 else
774 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
775 gint draw_image_height;
776 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
777 vw->draw_image_height = draw_image_height;
778 else
779 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
780 gboolean draw_image_save_as_png;
781 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
782 vw->draw_image_save_as_png = draw_image_save_as_png;
783 else
784 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
50a14534 785
75b7457a
RN
786 vw->main_vbox = gtk_vbox_new(FALSE, 1);
787 gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
788 vw->menu_hbox = gtk_hbox_new(FALSE, 1);
789 GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
790 gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
791 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
792
793 toolbar_init(vw->viking_vtb,
794 &vw->gtkwindow,
795 vw->main_vbox,
796 vw->menu_hbox,
797 toolbar_tool_cb,
798 toolbar_reload_cb,
799 (gpointer)vw); // This auto packs toolbar into the vbox
800 // Must be performed post toolbar init
801 gint i,j;
802 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
803 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
804 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
805 }
806 }
50a14534 807
82993cc7
RN
808 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
809
5c0bf50b 810 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
ddd7bb88 811 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
5c0bf50b
GB
812 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
813 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
814 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
ac4478f4 815
50a14534
EB
816 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
817
be5554c5
RN
818 // Own signals
819 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
820 // Signals from GTK
50a14534 821 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
bce3a7b0 822 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
fe27d6d2 823 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
824 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
825 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
826 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
827 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
bc07590a 828
50a14534 829 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
bc07590a 830 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
50a14534 831
6b59f63d
RN
832 // Allow key presses to be processed anywhere
833 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
777e2d4d 834
be5554c5
RN
835 // Set initial button sensitivity
836 center_changed_cb ( vw );
837
67130220
RN
838 vw->hpaned = gtk_hpaned_new ();
839 gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
840 gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
50a14534
EB
841
842 /* This packs the button into the window (a gtk container). */
75b7457a 843 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
50a14534 844
75b7457a 845 gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
50a14534 846
90142302 847 a_background_add_window ( vw );
50a14534 848
98acb9a1
RN
849 window_list = g_slist_prepend ( window_list, vw);
850
1219fd23
RN
851 gint height = VIKING_WINDOW_HEIGHT;
852 gint width = VIKING_WINDOW_WIDTH;
853
854 if ( a_vik_get_restore_window_state() ) {
855 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
856 // Enforce a basic minimum size
857 if ( height < 160 )
858 height = 160;
859 }
860 else
861 // No setting - so use default
862 height = VIKING_WINDOW_HEIGHT;
863
864 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
865 // Enforce a basic minimum size
866 if ( width < 320 )
867 width = 320;
868 }
869 else
870 // No setting - so use default
871 width = VIKING_WINDOW_WIDTH;
872
873 gboolean maxed;
874 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
875 if ( maxed )
876 gtk_window_maximize ( GTK_WINDOW(vw) );
877
878 gboolean full;
879 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
880 if ( full ) {
75b7457a 881 vw->show_full_screen = TRUE;
1219fd23
RN
882 gtk_window_fullscreen ( GTK_WINDOW(vw) );
883 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
75b7457a
RN
884 if ( check_box )
885 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1219fd23 886 }
f700bbd9 887 }
67130220
RN
888
889 gint position = -1; // Let GTK determine default positioning
890 if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
891 position = -1;
892 }
893 gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
f700bbd9
RN
894 }
895
1219fd23
RN
896 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
897
50a14534
EB
898 vw->open_dia = NULL;
899 vw->save_dia = NULL;
f2a1ca71
QT
900 vw->save_img_dia = NULL;
901 vw->save_img_dir_dia = NULL;
fa51adec 902
75b7457a
RN
903 vw->show_side_panel = TRUE;
904 vw->show_statusbar = TRUE;
905 vw->show_toolbar = TRUE;
906 vw->show_main_menu = TRUE;
907
1e6bae18
RN
908 // Only accept Drag and Drop of files onto the viewport
909 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
910 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
911 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
912
fa51adec
RN
913 // Store the thread value so comparisons can be made to determine the gdk update method
914 // Hopefully we are storing the main thread value here :)
915 // [ATM any window initialization is always be performed by the main thread]
916 vw->thread = g_thread_self();
75b7457a
RN
917
918 // Set the default tool + mode
919 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
920 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
7b97b14c
RN
921
922 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
923 gtk_accel_map_load ( accel_file_name );
924 g_free ( accel_file_name );
50a14534
EB
925}
926
8a13dbd2 927static VikWindow *window_new ()
50a14534
EB
928{
929 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
930}
931
6b59f63d
RN
932/**
933 * Update the displayed map
934 * Only update the top most visible map layer
935 * ATM this assumes (as per defaults) the top most map has full alpha setting
936 * such that other other maps even though they may be active will not be seen
937 * It's more complicated to work out which maps are actually visible due to alpha settings
938 * and overkill for this simple refresh method.
939 */
940static void simple_map_update ( VikWindow *vw, gboolean only_new )
941{
942 // Find the most relevent single map layer to operate on
943 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
944 if ( vl )
945 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
946}
947
948/**
949 * This is the global key press handler
950 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
951 */
777e2d4d
EB
952static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
953{
6b59f63d
RN
954 // The keys handled here are not in the menuing system for a couple of reasons:
955 // . Keeps the menu size compact (alebit at expense of discoverably)
956 // . Allows differing key bindings to perform the same actions
957
958 // First decide if key events are related to the maps layer
959 gboolean map_download = FALSE;
960 gboolean map_download_only_new = TRUE; // Only new or reload
961
962 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
963
964 // Standard 'Refresh' keys: F5 or Ctrl+r
965 // Note 'F5' is actually handled via draw_refresh_cb() later on
966 // (not 'R' it's 'r' notice the case difference!!)
967 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
968 map_download = TRUE;
969 map_download_only_new = TRUE;
970 }
971 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
972 // Note the use of uppercase R here since shift key has been pressed
973 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
974 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
975 map_download = TRUE;
976 map_download_only_new = FALSE;
977 }
f41c804a
RN
978 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
979 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
980 vik_viewport_zoom_in ( vw->viking_vvp );
981 draw_update(vw);
982 return TRUE; // handled keypress
983 }
984 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
985 vik_viewport_zoom_out ( vw->viking_vvp );
986 draw_update(vw);
987 return TRUE; // handled keypress
988 }
6b59f63d
RN
989
990 if ( map_download ) {
991 simple_map_update ( vw, map_download_only_new );
80866bd5 992 return TRUE; // handled keypress
6b59f63d
RN
993 }
994
777e2d4d
EB
995 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
996 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
997 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
998 if ( vl && ltype == vl->type )
999 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
1000 }
7622022f 1001
c68b3c06
RN
1002 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
1003 if ( vw->current_tool < TOOL_LAYER ) {
1004 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
1005 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1006 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1007 }
a7114521
RN
1008 }
1009
7622022f
RN
1010 /* Restore Main Menu via Escape key if the user has hidden it */
1011 /* This key is more likely to be used as they may not remember the function key */
1012 if ( event->keyval == GDK_Escape ) {
48df6aa3 1013 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
7622022f
RN
1014 if ( check_box ) {
1015 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1016 if ( !state ) {
1017 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1018 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1019 return TRUE; /* handled keypress */
1020 }
1021 }
1022 }
1023
777e2d4d
EB
1024 return FALSE; /* don't handle the keypress */
1025}
1026
50a14534
EB
1027static gboolean delete_event( VikWindow *vw )
1028{
a5fd2196 1029#ifdef VIKING_PROMPT_IF_MODIFIED
50a14534 1030 if ( vw->modified )
a5fd2196
QT
1031#else
1032 if (0)
1033#endif
50a14534
EB
1034 {
1035 GtkDialog *dia;
1036 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
4c77d5e0
GB
1037 _("Do you want to save the changes you made to the document \"%s\"?\n"
1038 "\n"
1039 "Your changes will be lost if you don't save them."),
4522c4ff 1040 window_get_filename ( vw ) ) );
4c77d5e0 1041 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
1042 switch ( gtk_dialog_run ( dia ) )
1043 {
1044 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1045 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
e4afc73a 1046 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
50a14534
EB
1047 }
1048 }
e7344085
RN
1049
1050 if ( window_count == 1 ) {
1219fd23
RN
1051 // On the final window close - save latest state - if it's wanted...
1052 if ( a_vik_get_restore_window_state() ) {
1053 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1054 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1055 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1056
1057 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1058 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1059
1060 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1061
1062 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1063
75b7457a 1064 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1219fd23
RN
1065
1066 // If supersized - no need to save the enlarged width+height values
1067 if ( ! (state_fullscreen || state_max) ) {
1068 gint width, height;
1069 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1070 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1071 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1072 }
67130220
RN
1073
1074 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1219fd23
RN
1075 }
1076
e7344085
RN
1077 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1078 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1079 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
7b97b14c
RN
1080
1081 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1082 gtk_accel_map_save ( accel_file_name );
1083 g_free ( accel_file_name );
e7344085
RN
1084 }
1085
50a14534
EB
1086 return FALSE;
1087}
1088
1089/* Drawing stuff */
e4afc73a 1090static void newwindow_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
1091{
1092 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1093}
1094
1095static void draw_update ( VikWindow *vw )
1096{
1097 draw_redraw (vw);
1098 draw_sync (vw);
1099}
1100
1101static void draw_sync ( VikWindow *vw )
1102{
1103 vik_viewport_sync(vw->viking_vvp);
1104 draw_status ( vw );
ac4478f4
RN
1105}
1106
1107/*
1108 * Split the status update, as sometimes only need to update the tool part
1109 * also on initialization the zoom related stuff is not ready to be used
1110 */
1111static void draw_status_tool ( VikWindow *vw )
1112{
1113 if ( vw->current_tool == TOOL_LAYER )
1114 // Use tooltip rather than the internal name as the tooltip is i8n
1115 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 );
1116 else
1117 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
50a14534
EB
1118}
1119
1120static void draw_status ( VikWindow *vw )
1121{
1122 static gchar zoom_level[22];
82940cf6
GB
1123 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1124 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1125 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1126 if (xmpp != ympp)
1127 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1128 else
fb5e99bb
RN
1129 if ( (int)xmpp - xmpp < 0.0 )
1130 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1131 else
1132 /* xmpp should be a whole number so don't show useless .000 bit */
1133 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
50a14534 1134
4efc10ca 1135 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
ac4478f4
RN
1136
1137 draw_status_tool ( vw );
50a14534
EB
1138}
1139
c9177aae
QT
1140void vik_window_set_redraw_trigger(VikLayer *vl)
1141{
730a38c1 1142 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
be3b5803
GB
1143 if (NULL != vw)
1144 vw->trigger = vl;
c9177aae
QT
1145}
1146
bce3a7b0
EB
1147static void window_configure_event ( VikWindow *vw )
1148{
f2f2f7bf 1149 static int first = 1;
bce3a7b0 1150 draw_redraw ( vw );
372132a6
GB
1151 if (first) {
1152 // This is a hack to set the cursor corresponding to the first tool
1153 // FIXME find the correct way to initialize both tool and its cursor
1154 first = 0;
55d3a53c 1155 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
f2f2f7bf 1156 /* We set cursor, even if it is NULL: it resets to default */
9b082b39 1157 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
372132a6 1158 }
bce3a7b0
EB
1159}
1160
50a14534
EB
1161static void draw_redraw ( VikWindow *vw )
1162{
c9177aae
QT
1163 VikCoord old_center = vw->trigger_center;
1164 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1165 VikLayer *new_trigger = vw->trigger;
1166 vw->trigger = NULL;
0df66d57
EB
1167 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1168
1169 if ( ! new_trigger )
1170 ; /* do nothing -- have to redraw everything. */
07c9d0bf 1171 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
0df66d57
EB
1172 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1173 else
1174 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1175
1176 /* actually draw */
50a14534 1177 vik_viewport_clear ( vw->viking_vvp);
bc07590a 1178 // Main layer drawing
50a14534 1179 vik_layers_panel_draw_all ( vw->viking_vlp );
bc07590a
RN
1180 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1181 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1182 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1183 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1184 }
1185 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1186 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1187 }
1188 else if ( vw->selected_vtl ) {
1189 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1190 }
1191 }
1192 // Other viewport decoration items on top if they are enabled/in use
acaf7113 1193 vik_viewport_draw_scale ( vw->viking_vvp );
82aa018d 1194 vik_viewport_draw_copyright ( vw->viking_vvp );
c933487f 1195 vik_viewport_draw_centermark ( vw->viking_vvp );
26336cf0 1196 vik_viewport_draw_logo ( vw->viking_vvp );
0df66d57
EB
1197
1198 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
50a14534
EB
1199}
1200
941aa6e9
AF
1201gboolean draw_buf_done = TRUE;
1202
1203static gboolean draw_buf(gpointer data)
1204{
1205 gpointer *pass_along = data;
1206 gdk_threads_enter();
1207 gdk_draw_drawable (pass_along[0], pass_along[1],
1208 pass_along[2], 0, 0, 0, 0, -1, -1);
1209 draw_buf_done = TRUE;
1210 gdk_threads_leave();
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
75b7457a 2733 toggle_toolbar ( 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) {
2808 GError *cursor_load_err = NULL;
2809 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2810 /* TODO: settable offeset */
2811 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2812 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2813 } else {
2814 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2815 }
2816 }
2817 return t->ti.cursor;
2818}
2819
941aa6e9
AF
2820static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2821{
2822 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2823 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
9593a4c9
EB
2824 gint ltype = vt->tools[vt->active_tool].layer_type;
2825 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2826 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
941aa6e9
AF
2827 }
2828}
2829
dc2c040e 2830static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
941aa6e9
AF
2831{
2832 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2833 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
9593a4c9
EB
2834 gint ltype = vt->tools[vt->active_tool].layer_type;
2835 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
165d30aa
EB
2836 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2837 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
941aa6e9
AF
2838 }
2839}
2840
2841static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2842{
2843 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
9593a4c9
EB
2844 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2845 gint ltype = vt->tools[vt->active_tool].layer_type;
2846 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2847 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
941aa6e9
AF
2848 }
2849}
2850/** End tool management ************************************/
2851
8fb71d6c
EB
2852void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2853{
79dce0cb 2854 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
8fb71d6c 2855}
941aa6e9 2856
75b7457a
RN
2857// Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2858// DON'T Use this from menu callback with toggle toolbar items!!
2859static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2860{
2861 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2862 if ( tbutton ) {
2863 // Causes toggle signal action to be raised.
2864 gtk_toggle_tool_button_set_active ( tbutton, state );
2865 }
2866}
2867
2868/* this function gets called whenever a menu is clicked */
2869// Note old is not used
2870static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
50a14534 2871{
75b7457a
RN
2872 // Ensure Toolbar kept in sync
2873 const gchar *name = gtk_action_get_name(a);
2874 toolbar_sync ( vw, name, TRUE );
2875
50a14534 2876 /* White Magic, my friends ... White Magic... */
b5926b35 2877 gint tool_id;
75b7457a 2878 toolbox_activate(vw->vt, name);
941aa6e9 2879
75b7457a 2880 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
33bc7c1b 2881
9b082b39 2882 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
33bc7c1b 2883 /* We set cursor, even if it is NULL: it resets to default */
9b082b39 2884 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
f2f2f7bf 2885
75b7457a 2886 if (!g_strcmp0(name, "Pan")) {
576cbd17 2887 vw->current_tool = TOOL_PAN;
576cbd17 2888 }
75b7457a 2889 else if (!g_strcmp0(name, "Zoom")) {
e4afc73a
EB
2890 vw->current_tool = TOOL_ZOOM;
2891 }
75b7457a 2892 else if (!g_strcmp0(name, "Ruler")) {
e4afc73a
EB
2893 vw->current_tool = TOOL_RULER;
2894 }
75b7457a 2895 else if (!g_strcmp0(name, "Select")) {
a47bfefa
RN
2896 vw->current_tool = TOOL_SELECT;
2897 }
e4afc73a 2898 else {
b5926b35 2899 VikLayerTypeEnum layer_id;
8fb71d6c
EB
2900 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2901 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
75b7457a 2902 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
8fb71d6c
EB
2903 vw->current_tool = TOOL_LAYER;
2904 vw->tool_layer_id = layer_id;
2905 vw->tool_tool_id = tool_id;
efb21731 2906 }
e4afc73a
EB
2907 }
2908 }
2909 }
ac4478f4 2910 draw_status_tool ( vw );
50a14534
EB
2911}
2912
2913static void window_set_filename ( VikWindow *vw, const gchar *filename )
2914{
2915 gchar *title;
4c77d5e0 2916 const gchar *file;
50a14534
EB
2917 if ( vw->filename )
2918 g_free ( vw->filename );
2919 if ( filename == NULL )
2920 {
2921 vw->filename = NULL;
50a14534
EB
2922 }
2923 else
2924 {
2925 vw->filename = g_strdup(filename);
50a14534 2926 }
4522c4ff
GB
2927
2928 /* Refresh window's title */
2929 file = window_get_filename ( vw );
4c77d5e0
GB
2930 title = g_strdup_printf( "%s - Viking", file );
2931 gtk_window_set_title ( GTK_WINDOW(vw), title );
2932 g_free ( title );
50a14534
EB
2933}
2934
4522c4ff
GB
2935static const gchar *window_get_filename ( VikWindow *vw )
2936{
2937 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2938}
2939
7bc965c0
GB
2940GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2941{
2942 GtkWidget *mode_button;
2943 gchar *buttonname;
2944 switch ( mode ) {
794c8f18 2945#ifdef VIK_CONFIG_EXPEDIA
7bc965c0 2946 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
794c8f18 2947#endif
7bc965c0 2948 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
d587678a 2949 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
794c8f18 2950 default: buttonname = "/ui/MainMenu/View/ModeUTM";
7bc965c0
GB
2951 }
2952 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2953 g_assert ( mode_button );
2954 return mode_button;
2955}
2956
01da6b4d
GB
2957/**
2958 * vik_window_get_pan_move:
2959 * @vw: some VikWindow
2960 *
2961 * Retrieves @vw's pan_move.
2962 *
2963 * Should be removed as soon as possible.
2964 *
2965 * Returns: @vw's pan_move
2966 *
2967 * Since: 0.9.96
2968 **/
1c6a6010
GB
2969gboolean vik_window_get_pan_move ( VikWindow *vw )
2970{
2971 return vw->pan_move;
2972}
2973
13505702
GB
2974static void on_activate_recent_item (GtkRecentChooser *chooser,
2975 VikWindow *self)
2976{
2977 gchar *filename;
2978
2979 filename = gtk_recent_chooser_get_current_uri (chooser);
2980 if (filename != NULL)
2981 {
2982 GFile *file = g_file_new_for_uri ( filename );
2983 gchar *path = g_file_get_path ( file );
2984 g_object_unref ( file );
2985 if ( self->filename )
2986 {
d8ff1421
RN
2987 GSList *filenames = NULL;
2988 filenames = g_slist_append ( filenames, path );
13505702 2989 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
d8ff1421 2990 // NB: GSList & contents are freed by main.open_window
13505702 2991 }
d8ff1421 2992 else {
756d53f5 2993 vik_window_open_file ( self, path, TRUE );
d8ff1421
RN
2994 g_free ( path );
2995 }
13505702
GB
2996 }
2997
2998 g_free (filename);
2999}
3000
3001static void setup_recent_files (VikWindow *self)
3002{
3003 GtkRecentManager *manager;
3004 GtkRecentFilter *filter;
3005 GtkWidget *menu, *menu_item;
3006
3007 filter = gtk_recent_filter_new ();
3008 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3009 gtk_recent_filter_add_group(filter, "viking");
3010
3011 manager = gtk_recent_manager_get_default ();
3012 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3013 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3014 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
50843c96 3015 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
13505702
GB
3016
3017 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3018 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3019
3020 g_signal_connect (G_OBJECT (menu), "item-activated",
3021 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3022}
3023
d17be106
RN
3024/*
3025 *
3026 */
3027static void update_recently_used_document (VikWindow *vw, const gchar *filename)
13505702
GB
3028{
3029 /* Update Recently Used Document framework */
3030 GtkRecentManager *manager = gtk_recent_manager_get_default();
3031 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3032 gchar *groups[] = {"viking", NULL};
3033 GFile *file = g_file_new_for_commandline_arg(filename);
3034 gchar *uri = g_file_get_uri(file);
3035 gchar *basename = g_path_get_basename(filename);
3036 g_object_unref(file);
3037 file = NULL;
3038
3039 recent_data->display_name = basename;
3040 recent_data->description = NULL;
3041 recent_data->mime_type = "text/x-gps-data";
3042 recent_data->app_name = (gchar *) g_get_application_name ();
3043 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3044 recent_data->groups = groups;
3045 recent_data->is_private = FALSE;
3046 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3047 {
d17be106
RN
3048 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3049 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3050 g_free ( msg );
13505702
GB
3051 }
3052
3053 g_free (uri);
3054 g_free (basename);
3055 g_free (recent_data->app_exec);
3056 g_slice_free (GtkRecentData, recent_data);
3057}
3058
55d3a53c
RN
3059/**
3060 * Call this before doing things that may take a long time and otherwise not show any other feedback
3061 * such as loading and saving files
3062 */
3063void vik_window_set_busy_cursor ( VikWindow *vw )
3064{
3065 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3066 // Viewport has a separate cursor
fafd0b95 3067 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
55d3a53c
RN
3068 // Ensure cursor updated before doing stuff
3069 while( gtk_events_pending() )
3070 gtk_main_iteration();
3071}
3072
3073void vik_window_clear_busy_cursor ( VikWindow *vw )
3074{
3075 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3076 // Restore viewport cursor
fafd0b95 3077 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
55d3a53c
RN
3078}
3079
50a14534
EB
3080void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3081{
55d3a53c 3082 vik_window_set_busy_cursor ( vw );
1b14d0d2
RN
3083
3084 // Enable the *new* filename to be accessible by the Layers codez
3085 gchar *original_filename = g_strdup ( vw->filename );
3086 g_free ( vw->filename );
3087 vw->filename = g_strdup ( filename );
3088 gboolean success = FALSE;
3089 gboolean restore_original_filename = FALSE;
3090
a14f46cf
RN
3091 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
3092 switch ( vw->loaded_type )
50a14534 3093 {
ba9d0a00 3094 case LOAD_TYPE_READ_FAILURE:
4c77d5e0 3095 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
50a14534 3096 break;
ba9d0a00
RN
3097 case LOAD_TYPE_GPSBABEL_FAILURE:
3098 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3099 break;
54b84792
RN
3100 case LOAD_TYPE_GPX_FAILURE:
3101 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3102 break;
cb5ec7a8
RN
3103 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3104 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3105 break;
327a2533
RN
3106 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3107 {
3108 // Since we can process .vik files with issues just show a warning in the status bar
3109 // Not that a user can do much about it... or tells them what this issue is yet...
3110 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3111 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3112 g_free ( msg );
3113 }
3114 // No break, carry on to show any data
ba9d0a00 3115 case LOAD_TYPE_VIK_SUCCESS:
50a14534 3116 {
1b14d0d2 3117 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
50a14534 3118 GtkWidget *mode_button;
13505702 3119 /* Update UI */
50a14534
EB
3120 if ( change_filename )
3121 window_set_filename ( vw, filename );
7bc965c0 3122 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
50a14534
EB
3123 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. */
3124 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3125 vw->only_updating_coord_mode_ui = FALSE;
3126
3127 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
1657065a 3128
87d052f9
RN
3129 // Slightly long winded methods to align loaded viewport settings with the UI
3130 // Since the rewrite for toolbar + menu actions
3131 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3132 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3133 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3134 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3135 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3136 if ( vp_state_scale != ui_state_scale ) {
3137 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3138 toggle_draw_scale ( NULL, vw );
3139 }
3140 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3141 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3142 if ( vp_state_centermark != ui_state_centermark ) {
3143 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3144 toggle_draw_centermark ( NULL, vw );
3145 }
3146 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3147 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3148 if ( vp_state_highlight != ui_state_highlight ) {
3149 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3150 toggle_draw_highlight ( NULL, vw );
3151 }
50a14534 3152 }
1b14d0d2 3153 // NB No break, carry on to redraw
ba9d0a00 3154 //case LOAD_TYPE_OTHER_SUCCESS:
13505702 3155 default:
1b14d0d2
RN
3156 success = TRUE;
3157 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3158 restore_original_filename = ! restore_original_filename;
d17be106 3159 update_recently_used_document (vw, filename);
13505702 3160 draw_update ( vw );
ba9d0a00 3161 break;
50a14534 3162 }
55d3a53c 3163
1b14d0d2
RN
3164 if ( ! success || restore_original_filename )
3165 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3166 window_set_filename ( vw, original_filename );
3167 g_free ( original_filename );
3168
55d3a53c 3169 vik_window_clear_busy_cursor ( vw );
50a14534 3170}
55d3a53c 3171
e4afc73a 3172static void load_file ( GtkAction *a, VikWindow *vw )
50a14534 3173{
6e4a49aa
MA
3174 GSList *files = NULL;
3175 GSList *cur_file = NULL;
e4afc73a
EB
3176 gboolean newwindow;
3177 if (!strcmp(gtk_action_get_name(a), "Open")) {
3178 newwindow = TRUE;
3179 }
3180 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3181 newwindow = FALSE;
3182 }
3183 else {
8dc8da82 3184 g_critical("Houston, we've had a problem.");
e4afc73a
EB
3185 return;
3186 }
3187
50a14534
EB
3188 if ( ! vw->open_dia )
3189 {
6e4a49aa 3190 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
31729835
RN
3191 GTK_WINDOW(vw),
3192 GTK_FILE_CHOOSER_ACTION_OPEN,
3193 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3194 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3195 NULL);
3196 gchar *cwd = g_get_current_dir();
3197 if ( cwd ) {
3198 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
3199 g_free ( cwd );
3200 }
3201
269fe4da
RN
3202 GtkFileFilter *filter;
3203 // NB file filters are listed this way for alphabetical ordering
3204#ifdef VIK_CONFIG_GEOCACHES
3205 filter = gtk_file_filter_new ();
3206 gtk_file_filter_set_name( filter, _("Geocaching") );
3207 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3208 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3209#endif
3210
3211 filter = gtk_file_filter_new ();
3212 gtk_file_filter_set_name( filter, _("Google Earth") );
3213 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3214 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3215
3216 filter = gtk_file_filter_new ();
3217 gtk_file_filter_set_name( filter, _("GPX") );
3218 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3219 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3220
5bed0ef6
RN
3221 filter = gtk_file_filter_new ();
3222 gtk_file_filter_set_name ( filter, _("JPG") );
3223 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3224 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3225
269fe4da
RN
3226 filter = gtk_file_filter_new ();
3227 gtk_file_filter_set_name( filter, _("Viking") );
3228 gtk_file_filter_add_pattern ( filter, "*.vik" );
3229 gtk_file_filter_add_pattern ( filter, "*.viking" );
3230 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3231
3232 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3233 // However assume this are barely used and thus not worthy of inclusion
3234 // as they'll just make the options too many and have no clear file pattern
3235 // one can always use the all option
3236 filter = gtk_file_filter_new ();
3237 gtk_file_filter_set_name( filter, _("All") );
3238 gtk_file_filter_add_pattern ( filter, "*" );
3239 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3240 // Default to any file - same as before open filters were added
3241 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3242
6e4a49aa 3243 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
50a14534
EB
3244 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
3245 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
3246 }
6e4a49aa 3247 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
50a14534
EB
3248 {
3249 gtk_widget_hide ( vw->open_dia );
a5fd2196 3250#ifdef VIKING_PROMPT_IF_MODIFIED
50a14534 3251 if ( (vw->modified || vw->filename) && newwindow )
a5fd2196
QT
3252#else
3253 if ( vw->filename && newwindow )
3254#endif
6e4a49aa 3255 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
50a14534 3256 else {
6e4a49aa
MA
3257 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
3258 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
d4a8b54d 3259 gboolean first_vik_file = TRUE;
6e4a49aa
MA
3260 cur_file = files;
3261 while ( cur_file ) {
d4a8b54d 3262
6e4a49aa 3263 gchar *file_name = cur_file->data;
d4a8b54d
RN
3264 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3265 // Load first of many .vik files in current window
3266 if ( first_vik_file ) {
3267 vik_window_open_file ( vw, file_name, TRUE );
3268 first_vik_file = FALSE;
3269 }
3270 else {
3271 // Load each subsequent .vik file in a separate window
3272 VikWindow *newvw = vik_window_new_window ();
3273 if (newvw)
3274 vik_window_open_file ( newvw, file_name, TRUE );
3275 }
3276 }
3277 else
3278 // Other file types
3279 vik_window_open_file ( vw, file_name, change_fn );
3280
6e4a49aa
MA
3281 g_free (file_name);
3282 cur_file = g_slist_next (cur_file);
50a14534 3283 }
6e4a49aa 3284 g_slist_free (files);
50a14534
EB
3285 }
3286 }
3287 else
3288 gtk_widget_hide ( vw->open_dia );
3289}
3290
e4afc73a 3291static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
50a14534
EB
3292{
3293 gboolean rv = FALSE;
3294 const gchar *fn;
3295 if ( ! vw->save_dia )
3296 {
6e4a49aa 3297 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
31729835
RN
3298 GTK_WINDOW(vw),
3299 GTK_FILE_CHOOSER_ACTION_SAVE,
3300 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3301 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3302 NULL);
3303 gchar *cwd = g_get_current_dir();
3304 if ( cwd ) {
3305 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
3306 g_free ( cwd );
3307 }
3308
471d418c
RN
3309 GtkFileFilter *filter;
3310 filter = gtk_file_filter_new ();
3311 gtk_file_filter_set_name( filter, _("All") );
3312 gtk_file_filter_add_pattern ( filter, "*" );
3313 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3314
3315 filter = gtk_file_filter_new ();
3316 gtk_file_filter_set_name( filter, _("Viking") );
3317 gtk_file_filter_add_pattern ( filter, "*.vik" );
3318 gtk_file_filter_add_pattern ( filter, "*.viking" );
3319 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3320 // Default to a Viking file
3321 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3322
50a14534
EB
3323 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
3324 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
3325 }
c38eb451 3326 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
8f75f616 3327 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
140d0d67 3328 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
c38eb451
RN
3329 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3330
3331 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
50a14534 3332
6e4a49aa 3333 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
50a14534 3334 {
6e4a49aa 3335 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
d91e5f2b 3336 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(vw->save_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
50a14534
EB
3337 {
3338 window_set_filename ( vw, fn );
3339 rv = window_save ( vw );
3340 vw->modified = FALSE;
3341 break;
3342 }
3343 }
c38eb451 3344 g_free ( auto_save_name );
50a14534
EB
3345 gtk_widget_hide ( vw->save_dia );
3346 return rv;
3347}
3348
3349static gboolean window_save ( VikWindow *vw )
3350{
55d3a53c
RN
3351 vik_window_set_busy_cursor ( vw );
3352 gboolean success = TRUE;
3353
50a14534 3354 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
13505702 3355 {
d17be106 3356 update_recently_used_document ( vw, vw->filename );
13505702 3357 }
50a14534
EB
3358 else
3359 {
4c77d5e0 3360 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
55d3a53c 3361 success = FALSE;
50a14534 3362 }
55d3a53c
RN
3363 vik_window_clear_busy_cursor ( vw );
3364 return success;
50a14534
EB
3365}
3366
e4afc73a 3367static gboolean save_file ( GtkAction *a, VikWindow *vw )
50a14534
EB
3368{
3369 if ( ! vw->filename )
e4afc73a 3370 return save_file_as ( NULL, vw );
50a14534
EB
3371 else
3372 {
3373 vw->modified = FALSE;
3374 return window_save ( vw );
3375 }
3376}
3377
0bb36e78
RN
3378/**
3379 * export_to:
3380 *
3381 * Export all TRW Layers in the list to individual files in the specified directory
3382 *
3383 * Returns: %TRUE on success
3384 */
3385static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3386{
3387 gboolean success = TRUE;
3388
3389 gint export_count = 0;
3390
3391 vik_window_set_busy_cursor ( vw );
3392
3393 while ( gl ) {
3394
3395 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3396
3397 // Some protection in attempting to write too many same named files
3398 // As this will get horribly slow...
3399 gboolean safe = FALSE;
3400 gint ii = 2;
3401 while ( ii < 5000 ) {
3402 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3403 // Try rename
3404 g_free ( fn );
3405 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3406 }
3407 else {
3408 safe = TRUE;
3409 break;
3410 }
3411 ii++;
3412 }
3413 if ( ii == 5000 )
3414 success = FALSE;
3415
3416 // NB: We allow exporting empty layers
3417 if ( safe ) {
3418 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3419
3420 // Show some progress
3421 if ( this_success ) {
3422 export_count++;
6e4bf640 3423 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
0bb36e78
RN
3424 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3425 while ( gtk_events_pending() )
3426 gtk_main_iteration ();
3427 g_free ( message );
3428 }
3429
3430 success = success && this_success;
3431 }
3432
3433 g_free ( fn );
3434 gl = g_list_next ( gl );
3435 }
3436
3437 vik_window_clear_busy_cursor ( vw );
3438
3439 // Confirm what happened.
3440 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3441 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3442 g_free ( message );
3443
3444 return success;
3445}
3446
3447static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3448{
3449 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3450
3451 if ( !gl ) {
3452 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3453 return;
3454 }
3455
dc453cfd 3456 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
0bb36e78 3457 GTK_WINDOW(vw),
dc453cfd 3458 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
0bb36e78
RN
3459 GTK_STOCK_CANCEL,
3460 GTK_RESPONSE_REJECT,
3461 GTK_STOCK_OK,
3462 GTK_RESPONSE_ACCEPT,
3463 NULL );
dc453cfd
GB
3464 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3465 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3466 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
0bb36e78
RN
3467
3468 gtk_widget_show_all ( dialog );
3469
3470 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
dc453cfd 3471 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
0bb36e78
RN
3472 gtk_widget_destroy ( dialog );
3473 if ( dir ) {
3474 if ( !export_to ( vw, gl, vft, dir, extension ) )
3475 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3476 g_free ( dir );
3477 }
3478 }
3479 else
3480 gtk_widget_destroy ( dialog );
3481
3482 g_list_free ( gl );
3483}
3484
3485static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3486{
3487 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3488}
3489
3490static void export_to_kml ( GtkAction *a, VikWindow *vw )
3491{
3492 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3493}
3494
440d1f8e
RN
3495#if !GLIB_CHECK_VERSION(2,26,0)
3496typedef struct stat GStatBuf;
3497#endif
3498
3f46fe76
RN
3499static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3500{
3501 gchar *message = NULL;
3502 if ( vw->filename ) {
3503 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3504 // Get some timestamp information of the file
3505 GStatBuf stat_buf;
3506 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3507 gchar time_buf[64];
3508 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
c86a3725
RN
3509 gchar *size = NULL;
3510 gint byte_size = stat_buf.st_size;
3511#if GLIB_CHECK_VERSION(2,30,0)
3512 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3513#else
3514 size = g_format_size_for_display ( byte_size );
3515#endif
3516 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3517 g_free (size);
3f46fe76
RN
3518 }
3519 }
3520 else
3521 message = g_strdup ( _("File not accessible") );
3522 }
3523 else
3524 message = g_strdup ( _("No Viking File") );
3525
3526 // Show the info
3527 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3528 g_free ( message );
3529}
3530
9cc13848
RN
3531static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3532{
3533 vik_datasource_mode_t mode = datasource->mode;
3534 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3535 mode = VIK_DATASOURCE_CREATENEWLAYER;
3536 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3537}
3538
1d1bc3c1
EB
3539static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3540{
9cc13848 3541 my_acquire ( vw, &vik_datasource_gps_interface );
7b3479e3
EB
3542}
3543
31349009
GB
3544static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3545{
9cc13848 3546 my_acquire ( vw, &vik_datasource_file_interface );
31349009
GB
3547}
3548
c0c5893f
RN
3549static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3550{
3551 my_acquire ( vw, &vik_datasource_geojson_interface );
3552}
3553
7f95fd54 3554static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
7b3479e3 3555{
9cc13848 3556 my_acquire ( vw, &vik_datasource_routing_interface );
1d1bc3c1
EB
3557}
3558
9c4555df
GB
3559#ifdef VIK_CONFIG_OPENSTREETMAP
3560static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3561{
9cc13848 3562 my_acquire ( vw, &vik_datasource_osm_interface );
9c4555df 3563}
3cc57413
RN
3564
3565static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3566{
9cc13848 3567 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3cc57413 3568}
9c4555df
GB
3569#endif
3570
1ef9e637 3571#ifdef VIK_CONFIG_GEOCACHES
3333c069
EB
3572static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3573{
9cc13848 3574 my_acquire ( vw, &vik_datasource_gc_interface );
3333c069 3575}
1ef9e637 3576#endif
3333c069 3577
f75d0233
RN
3578#ifdef VIK_CONFIG_GEOTAG
3579static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3580{
9cc13848 3581 my_acquire ( vw, &vik_datasource_geotag_interface );
f75d0233
RN
3582}
3583#endif
3584
3c29a566
RN
3585#ifdef VIK_CONFIG_GEONAMES
3586static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3587{
9cc13848 3588 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3c29a566
RN
3589}
3590#endif
3591
c6acf18d
RN
3592static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3593{
9cc13848 3594 my_acquire ( vw, &vik_datasource_url_interface );
c6acf18d
RN
3595}
3596
5210c3d3
GB
3597static void goto_default_location( GtkAction *a, VikWindow *vw)
3598{
3599 struct LatLon ll;
3600 ll.lat = a_vik_get_default_lat();
3601 ll.lon = a_vik_get_default_long();
be5554c5 3602 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
5210c3d3
GB
3603 vik_layers_panel_emit_update(vw->viking_vlp);
3604}
3605
3606
369126f3
QT
3607static void goto_address( GtkAction *a, VikWindow *vw)
3608{
c36a079a
RN
3609 a_vik_goto ( vw, vw->viking_vvp );
3610 vik_layers_panel_emit_update ( vw->viking_vlp );
369126f3
QT
3611}
3612
7c259702
JJ
3613static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3614{
3615 a_mapcache_flush();
3616}
3617
7c8bddff
RN
3618static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3619{
3620 const VikCoord* coord;
3621 struct UTM utm;
3622 gchar *lat = NULL, *lon = NULL;
3623
3624 coord = vik_viewport_get_center ( vw->viking_vvp );
3625 vik_coord_to_utm ( coord, &utm );
3626
3627 gboolean full_format = FALSE;
3628 a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3629
3630 if ( full_format )
3631 // Bells & Whistles - may include degrees, minutes and second symbols
3632 get_location_strings ( vw, utm, &lat, &lon );
3633 else {
3634 // Simple x.xx y.yy format
3635 struct LatLon ll;
3636 a_coords_utm_to_latlon ( &utm, &ll );
3637 lat = g_strdup_printf ( "%.6f", ll.lat );
3638 lon = g_strdup_printf ( "%.6f", ll.lon );
3639 }
3640
3641 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3642 g_free (lat);
3643 g_free (lon);
3644
3645 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3646
3647 g_free ( msg );
3648}
3649
a7023a1b
RN
3650static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3651{
3652 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3653
3654 if ( !texts[1] )
3655 return; // Internally broken :(
3656
3657 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3658 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3659 // NB no update needed
3660
3661 g_strfreev ( texts );
3662}
3663
98acb9a1
RN
3664static void preferences_change_update ( VikWindow *vw, gpointer data )
3665{
3666 // Want to update all TrackWaypoint layers
3667 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3668
7fe46df4
RN
3669 if ( !layers )
3670 return;
3671
3672 while ( layers ) {
98acb9a1 3673 // Reset the individual waypoints themselves due to the preferences change
7fe46df4 3674 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
98acb9a1 3675 vik_trw_layer_reset_waypoints ( vtl );
7fe46df4 3676 layers = g_list_next ( layers );
98acb9a1
RN
3677 }
3678
3679 g_list_free ( layers );
3680
3681 draw_update ( vw );
3682}
3683
17a1f8f9
EB
3684static void preferences_cb ( GtkAction *a, VikWindow *vw )
3685{
9be0449f
RN
3686 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3687
17a1f8f9 3688 a_preferences_show_window ( GTK_WINDOW(vw) );
9be0449f 3689
98acb9a1
RN
3690 // Has the waypoint size setting changed?
3691 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3692 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
9be0449f
RN
3693 clear_garmin_icon_syms ();
3694
98acb9a1
RN
3695 // Update all windows
3696 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3697 }
74562734
RN
3698
3699 // Ensure TZ Lookup initialized
3700 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3701 vu_setup_lat_lon_tz_lookup();
75b7457a
RN
3702
3703 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
17a1f8f9
EB
3704}
3705
5210c3d3
GB
3706static void default_location_cb ( GtkAction *a, VikWindow *vw )
3707{
3708 /* Simplistic repeat of preference setting
3709 Only the name & type are important for setting the preference via this 'external' way */
3710 VikLayerParam pref_lat[] = {
a7023a1b
RN
3711 { VIK_LAYER_NUM_TYPES,
3712 VIKING_PREFERENCES_NAMESPACE "default_latitude",
5210c3d3
GB
3713 VIK_LAYER_PARAM_DOUBLE,
3714 VIK_LOCATION_LAT,
3715 NULL,
3716 VIK_LAYER_WIDGET_SPINBUTTON,
3717 NULL,
e6994d8d 3718 NULL,
a87f8fa1
RN
3719 NULL,
3720 NULL,
3721 NULL,
63959706 3722 NULL,
a87f8fa1 3723 },
5210c3d3
GB
3724 };
3725 VikLayerParam pref_lon[] = {
a7023a1b
RN
3726 { VIK_LAYER_NUM_TYPES,
3727 VIKING_PREFERENCES_NAMESPACE "default_longitude",
5210c3d3
GB
3728 VIK_LAYER_PARAM_DOUBLE,
3729 VIK_LOCATION_LONG,
3730 NULL,
3731 VIK_LAYER_WIDGET_SPINBUTTON,
3732 NULL,
e6994d8d 3733 NULL,
a87f8fa1
RN
3734 NULL,
3735 NULL,
3736 NULL,
63959706 3737 NULL,
a87f8fa1 3738 },
5210c3d3
GB
3739 };
3740
3741 /* Get current center */
3742 struct LatLon ll;
3743 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3744
3745 /* Apply to preferences */
3746 VikLayerParamData vlp_data;
3747 vlp_data.d = ll.lat;
3748 a_preferences_run_setparam (vlp_data, pref_lat);
3749 vlp_data.d = ll.lon;
3750 a_preferences_run_setparam (vlp_data, pref_lon);
3751 /* Remember to save */
3752 a_preferences_save_to_file();
3753}
3754
e4afc73a 3755static void clear_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
3756{
3757 vik_layers_panel_clear ( vw->viking_vlp );
3758 window_set_filename ( vw, NULL );
3759 draw_update ( vw );
3760}
3761
e4afc73a 3762static void window_close ( GtkAction *a, VikWindow *vw )
50a14534
EB
3763{
3764 if ( ! delete_event ( vw ) )
3765 gtk_widget_destroy ( GTK_WIDGET(vw) );
3766}
3767
2bf7cadd
QT
3768static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3769{
7bb60307 3770 if (save_file( NULL, vw)) {
2bf7cadd 3771 window_close( NULL, vw);
7bb60307
QT
3772 return(TRUE);
3773 }
2bf7cadd
QT
3774 else
3775 return(FALSE);
3776}
3777
e4afc73a 3778static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
3779{
3780 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3781 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3782 {
3783 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3784 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3785 draw_update ( vw );
3786 }
3787}
3788
3789static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3790{
3791 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3792 GdkPixbuf *pixbuf_to_save;
3793 gdouble old_xmpp, old_ympp;
3794 GError *error = NULL;
3795
d78751d4
RN
3796 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3797 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3798 GTK_MESSAGE_INFO,
3799 GTK_BUTTONS_NONE,
3800 _("Generating image file...") );
3801
3802 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3803 // Ensure dialog shown
3804 gtk_widget_show_all ( msgbox );
3805 // Try harder...
3806 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3807 while ( gtk_events_pending() )
3808 gtk_main_iteration ();
3809 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3810 // At least the empty box can give a clue something's going on + the statusbar msg...
3811 // Windows version under Wine OK!
3812
50a14534
EB
3813 /* backup old zoom & set new */
3814 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3815 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3816 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3817
3818 /* reset width and height: */
3819 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3820
3821 /* draw all layers */
3822 draw_redraw ( vw );
3823
3824 /* save buffer as file. */
3825 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 3826 if ( !pixbuf_to_save ) {
d78751d4
RN
3827 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3828 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
5cd09d57
RN
3829 goto cleanup;
3830 }
3831
50a14534
EB
3832 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3833 if (error)
3834 {
7742da66 3835 g_warning("Unable to write to file %s: %s", fn, error->message );
d78751d4 3836 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
50a14534
EB
3837 g_error_free (error);
3838 }
d78751d4
RN
3839 else {
3840 // Success
3841 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3842 }
50a14534
EB
3843 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3844
5cd09d57 3845 cleanup:
d78751d4
RN
3846 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3847 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3848 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3849
50a14534
EB
3850 /* pretend like nothing happened ;) */
3851 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3852 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3853 vik_viewport_configure ( vw->viking_vvp );
3854 draw_update ( vw );
3855}
3856
3857static 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 )
3858{
3859 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3860 gchar *name_of_file = g_malloc ( size );
3861 guint x = 1, y = 1;
3862 struct UTM utm_orig, utm;
3863
3864 /* *** copied from above *** */
3865 GdkPixbuf *pixbuf_to_save;
3866 gdouble old_xmpp, old_ympp;
3867 GError *error = NULL;
3868
3869 /* backup old zoom & set new */
3870 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3871 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3872 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3873
3874 /* reset width and height: do this only once for all images (same size) */
3875 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3876 /* *** end copy from above *** */
3877
3878 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3879
f83131b9 3880 g_mkdir(fn,0777);
50a14534
EB
3881
3882 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3883
3884 for ( y = 1; y <= tiles_h; y++ )
3885 {
3886 for ( x = 1; x <= tiles_w; x++ )
3887 {
3d9454e6 3888 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
3889 utm = utm_orig;
3890 if ( tiles_w & 0x1 )
3891 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3892 else
3893 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3894 if ( tiles_h & 0x1 ) /* odd */
3895 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3896 else /* even */
3897 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3898
3899 /* move to correct place. */
be5554c5 3900 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
50a14534
EB
3901
3902 draw_redraw ( vw );
3903
3904 /* save buffer as file. */
3905 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);
3906 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3907 if (error)
3908 {
d17be106
RN
3909 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3910 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3911 g_free ( msg );
50a14534
EB
3912 g_error_free (error);
3913 }
3914
3915 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3916 }
3917 }
3918
be5554c5 3919 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
50a14534
EB
3920 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3921 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3922 vik_viewport_configure ( vw->viking_vvp );
3923 draw_update ( vw );
3924
3925 g_free ( name_of_file );
3926}
3927
3928static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3929{
3930 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3931 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
267e3ce7
RN
3932
3933 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3934 gdouble zoom = pow (2, active-2 );
3935
50a14534
EB
3936 gdouble width_min, width_max, height_min, height_max;
3937 gint width, height;
3938
3939 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3940 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3941
3942 /* TODO: support for xzoom and yzoom values */
267e3ce7
RN
3943 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3944 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
50a14534
EB
3945
3946 if ( width > width_max || width < width_min || height > height_max || height < height_min )
4c77d5e0 3947 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
50a14534
EB
3948
3949 gtk_spin_button_set_value ( width_spin, width );
3950 gtk_spin_button_set_value ( height_spin, height );
3951}
3952
3953static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3954{
3955 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
267e3ce7
RN
3956
3957 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3958 gdouble zoom = pow (2, active-2 );
3959
50a14534
EB
3960 gchar *label_text;
3961 gdouble w, h;
267e3ce7
RN
3962 w = gtk_spin_button_get_value(width_spin) * zoom;
3963 h = gtk_spin_button_get_value(height_spin) * zoom;
50a14534
EB
3964 if (pass_along[4]) /* save many images; find TOTAL area covered */
3965 {
3966 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3967 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3968 }
6f9336aa
RN
3969 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3970 switch (dist_units) {
3971 case VIK_UNITS_DISTANCE_KILOMETRES:
3972 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3973 break;
3974 case VIK_UNITS_DISTANCE_MILES:
3975 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3976 break;
b22233bd
RN
3977 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3978 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
3979 break;
6f9336aa
RN
3980 default:
3981 label_text = g_strdup_printf ("Just to keep the compiler happy");
3982 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3983 }
3984
50a14534
EB
3985 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3986 g_free ( label_text );
3987}
3988
310cbaf7
RN
3989/*
3990 * Get an allocated filename (or directory as specified)
3991 */
3992static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3993{
3994 gchar *fn = NULL;
3995 if ( one_image_only )
3996 {
3997 // Single file
3998 if (!vw->save_img_dia) {
3999 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
4000 GTK_WINDOW(vw),
4001 GTK_FILE_CHOOSER_ACTION_SAVE,
4002 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4003 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4004 NULL);
67b63c20 4005
31729835
RN
4006 gchar *cwd = g_get_current_dir();
4007 if ( cwd ) {
4008 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
4009 g_free ( cwd );
4010 }
4011
67b63c20
RN
4012 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
4013 /* Add filters */
4014 GtkFileFilter *filter;
4015 filter = gtk_file_filter_new ();
4016 gtk_file_filter_set_name ( filter, _("All") );
4017 gtk_file_filter_add_pattern ( filter, "*" );
4018 gtk_file_chooser_add_filter ( chooser, filter );
4019
4020 filter = gtk_file_filter_new ();
4021 gtk_file_filter_set_name ( filter, _("JPG") );
4022 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4023 gtk_file_chooser_add_filter ( chooser, filter );
4024
2ec82cbf
RN
4025 if ( !vw->draw_image_save_as_png )
4026 gtk_file_chooser_set_filter ( chooser, filter );
4027
67b63c20
RN
4028 filter = gtk_file_filter_new ();
4029 gtk_file_filter_set_name ( filter, _("PNG") );
4030 gtk_file_filter_add_mime_type ( filter, "image/png");
4031 gtk_file_chooser_add_filter ( chooser, filter );
4032
2ec82cbf
RN
4033 if ( vw->draw_image_save_as_png )
4034 gtk_file_chooser_set_filter ( chooser, filter );
67b63c20 4035
310cbaf7
RN
4036 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
4037 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
4038 }
4039
4040 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
4041 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
4042 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4043 if ( ! a_dialog_yes_or_no ( GTK_WINDOW(vw->save_img_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
4044 fn = NULL;
4045 }
4046 gtk_widget_hide ( vw->save_img_dia );
4047 }
4048 else {
4049 // A directory
4050 // For some reason this method is only written to work in UTM...
4051 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4052 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4053 return fn;
4054 }
4055
4056 if (!vw->save_img_dir_dia) {
4057 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4058 GTK_WINDOW(vw),
4059 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4060 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4061 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4062 NULL);
4063 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
4064 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
4065 }
4066
4067 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
4068 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
4069 }
4070 gtk_widget_hide ( vw->save_img_dir_dia );
4071 }
4072 return fn;
4073}
4074
4075static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
50a14534
EB
4076{
4077 /* todo: default for answers inside VikWindow or static (thruout instance) */
4c77d5e0 4078 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
50a14534
EB
4079 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4080 GTK_STOCK_CANCEL,
4081 GTK_RESPONSE_REJECT,
4082 GTK_STOCK_OK,
4083 GTK_RESPONSE_ACCEPT,
10888930 4084 NULL );
50a14534
EB
4085 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4086 GtkWidget *png_radio, *jpeg_radio;
4087 GtkWidget *current_window_button;
4088 gpointer current_window_pass_along[7];
267e3ce7 4089 GtkWidget *zoom_label, *zoom_combo;
50a14534
EB
4090 GtkWidget *total_size_label;
4091
4092 /* only used if (!one_image_only) */
886031df 4093 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
50a14534 4094
4c77d5e0 4095 width_label = gtk_label_new ( _("Width (pixels):") );
5cd09d57 4096 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4c77d5e0 4097 height_label = gtk_label_new ( _("Height (pixels):") );
5cd09d57
RN
4098 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4099#ifdef WINDOWS
4100 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4101#endif
4c77d5e0 4102 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
50a14534 4103 /* TODO: separate xzoom and yzoom factors */
267e3ce7
RN
4104 zoom_combo = create_zoom_combo_all_levels();
4105
4106 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4db16a36 4107 gint active = 2 + round ( log (mpp) / log (2) );
267e3ce7
RN
4108
4109 // Can we not hard code size here?
4110 if ( active > 17 )
4111 active = 17;
06716e69
RN
4112 if ( active < 0 )
4113 active = 0;
267e3ce7 4114 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
50a14534
EB
4115
4116 total_size_label = gtk_label_new ( NULL );
4117
4c77d5e0 4118 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
50a14534
EB
4119 current_window_pass_along [0] = vw;
4120 current_window_pass_along [1] = width_spin;
4121 current_window_pass_along [2] = height_spin;
267e3ce7 4122 current_window_pass_along [3] = zoom_combo;
50a14534
EB
4123 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
4124 current_window_pass_along [5] = NULL;
4125 current_window_pass_along [6] = total_size_label;
4126 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4127
4c77d5e0
GB
4128 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4129 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
50a14534 4130
59d5fb31
RN
4131 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4132 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4133
50a14534
EB
4134 if ( ! vw->draw_image_save_as_png )
4135 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4136
9b082b39
RN
4137 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4138 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4139 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4140 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
5cd09d57 4141#ifdef WINDOWS
9b082b39 4142 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
5cd09d57 4143#endif
9b082b39 4144 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
9b082b39
RN
4145 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4146 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
50a14534
EB
4147
4148 if ( ! one_image_only )
4149 {
4150 GtkWidget *tiles_width_label, *tiles_height_label;
4151
4c77d5e0 4152 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
50a14534 4153 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4c77d5e0 4154 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
50a14534 4155 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
9b082b39
RN
4156 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4157 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4158 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4159 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
50a14534
EB
4160
4161 current_window_pass_along [4] = tiles_width_spin;
4162 current_window_pass_along [5] = tiles_height_spin;
4163 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4164 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4165 }
9b082b39 4166 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
50a14534
EB
4167 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4168 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
267e3ce7 4169 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
50a14534
EB
4170
4171 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4172
1fb999d3
RN
4173 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4174
9b082b39 4175 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
50a14534
EB
4176
4177 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4178 {
4179 gtk_widget_hide ( GTK_WIDGET(dialog) );
310cbaf7
RN
4180
4181 gchar *fn = draw_image_filename ( vw, one_image_only );
4182 if ( !fn )
4183 return;
4184
c60f82df
RN
4185 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4186 gdouble zoom = pow (2, active_z-2 );
267e3ce7 4187
50a14534
EB
4188 if ( one_image_only )
4189 save_image_file ( vw, fn,
4190 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4191 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
267e3ce7 4192 zoom,
50a14534
EB
4193 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
4194 else {
e5657444
RN
4195 // NB is in UTM mode ATM
4196 save_image_dir ( vw, fn,
50a14534
EB
4197 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4198 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
267e3ce7 4199 zoom,
50a14534
EB
4200 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4201 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4202 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
50a14534 4203 }
310cbaf7
RN
4204
4205 g_free ( fn );
50a14534
EB
4206 }
4207 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4208}
4209
4210
e4afc73a 4211static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
50a14534 4212{
310cbaf7 4213 draw_to_image_file ( vw, TRUE );
50a14534
EB
4214}
4215
e4afc73a 4216static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
50a14534 4217{
310cbaf7 4218 draw_to_image_file ( vw, FALSE );
50a14534
EB
4219}
4220
42f34743
QT
4221static void print_cb ( GtkAction *a, VikWindow *vw )
4222{
4223 a_print(vw, vw->viking_vvp);
4224}
4225
50a14534 4226/* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
e4afc73a 4227static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
50a14534 4228{
75b7457a
RN
4229 const gchar *name = gtk_action_get_name(a);
4230 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4231 if ( tbutton )
4232 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4233
e4afc73a 4234 VikViewportDrawMode drawmode;
75b7457a 4235 if (!g_strcmp0(name, "ModeUTM")) {
e4afc73a
EB
4236 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4237 }
75b7457a 4238 else if (!g_strcmp0(name, "ModeLatLon")) {
d587678a
GB
4239 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4240 }
75b7457a 4241 else if (!g_strcmp0(name, "ModeExpedia")) {
e4afc73a
EB
4242 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4243 }
75b7457a 4244 else if (!g_strcmp0(name, "ModeMercator")) {
e4afc73a
EB
4245 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4246 }
4247 else {
8dc8da82 4248 g_critical("Houston, we've had a problem.");
e4afc73a
EB
4249 return;
4250 }
4251
50a14534
EB
4252 if ( !vw->only_updating_coord_mode_ui )
4253 {
4254 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4255 if ( olddrawmode != drawmode )
4256 {
4257 /* this takes care of coord mode too */
4258 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4259 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4260 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4261 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4262 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4263 }
4264 draw_update ( vw );
4265 }
4266 }
4267}
4268
87d052f9 4269static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
35c7c0ba 4270{
75b7457a 4271 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
48df6aa3 4272 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
75b7457a
RN
4273 if ( !check_box )
4274 return;
4275 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
1657065a 4276 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
35c7c0ba
EB
4277 draw_update ( vw );
4278}
4279
87d052f9 4280static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
c933487f 4281{
75b7457a 4282 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
48df6aa3 4283 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
75b7457a
RN
4284 if ( !check_box )
4285 return;
4286 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
1657065a 4287 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
c933487f
QT
4288 draw_update ( vw );
4289}
4290
87d052f9 4291static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
2afcef36 4292{
75b7457a 4293 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2afcef36 4294 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
75b7457a
RN
4295 if ( !check_box )
4296 return;
4297 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
87d052f9
RN
4298 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4299 draw_update ( vw );
2afcef36
RN
4300}
4301
e4afc73a 4302static void set_bg_color ( GtkAction *a, VikWindow *vw )
50a14534 4303{
4c77d5e0 4304 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
50a14534 4305 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
9b082b39
RN
4306 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4307 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
50a14534
EB
4308 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4309 {
9b082b39 4310 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
50a14534
EB
4311 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4312 draw_update ( vw );
4313 }
4314 g_free ( color );
4315 gtk_widget_destroy ( colorsd );
4316}
4317
480fb7e1
RN
4318static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4319{
4320 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4321 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
9b082b39
RN
4322 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4323 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
480fb7e1
RN
4324 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4325 {
9b082b39 4326 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
480fb7e1
RN
4327 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4328 draw_update ( vw );
4329 }
4330 g_free ( color );
4331 gtk_widget_destroy ( colorsd );
4332}
4333
941aa6e9 4334
941aa6e9
AF
4335/***********************************************************************************************
4336 ** GUI Creation
4337 ***********************************************************************************************/
4338
e4afc73a 4339static GtkActionEntry entries[] = {
5515e2d3
JJ
4340 { "File", NULL, N_("_File"), 0, 0, 0 },
4341 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4342 { "View", NULL, N_("_View"), 0, 0, 0 },
48df6aa3 4343 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
5515e2d3
JJ
4344 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4345 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4346 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4347 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
92806042 4348 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
5515e2d3
JJ
4349 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4350
4351 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
06526f88
RN
4352 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4353 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4354 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
0bb36e78
RN
4355 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4356 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
d6de71f9 4357 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
06526f88 4358 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
31349009 4359 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
7f95fd54 4360 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
9c4555df
GB
4361#ifdef VIK_CONFIG_OPENSTREETMAP
4362 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3cc57413 4363 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
9c4555df 4364#endif
1ef9e637 4365#ifdef VIK_CONFIG_GEOCACHES
06526f88 4366 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
f75d0233
RN
4367#endif
4368#ifdef VIK_CONFIG_GEOTAG
4369 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3c29a566 4370#endif
c6acf18d 4371 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
3c29a566
RN
4372#ifdef VIK_CONFIG_GEONAMES
4373 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
1ef9e637 4374#endif
5515e2d3 4375 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
06526f88 4376 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3f46fe76 4377 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
06526f88 4378 { "GenImg", GTK_STOCK_CLEAR, N_("_Generate Image File..."), NULL, N_("Save a snapshot of the workspace into a file"), (GCallback)draw_to_image_file_cb },
22f536ea 4379 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
5515e2d3 4380 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
5515e2d3
JJ
4381 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4382 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4383
be5554c5
RN
4384 { "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 },
4385 { "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 4386 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
cbd293bb 4387 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
402fee6c 4388 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
150618fe 4389 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
6b59f63d 4390 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
22f536ea
RN
4391 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4392 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4393 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4394 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4395 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
cbd293bb
RN
4396 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4397 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4398 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4399 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
22f536ea 4400 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
5515e2d3 4401
22f536ea
RN
4402 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4403 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4404 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4405 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
5515e2d3 4406 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
7c8bddff 4407 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
06526f88 4408 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
5210c3d3 4409 { "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 4410 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
6d61ba92 4411 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
22f536ea 4412 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
5515e2d3 4413
22f536ea
RN
4414 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4415 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
e4afc73a
EB
4416};
4417
a3040a49
RN
4418static GtkActionEntry debug_entries[] = {
4419 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
fca1f3ad 4420 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
a3040a49
RN
4421};
4422
0bb36e78
RN
4423static GtkActionEntry entries_gpsbabel[] = {
4424 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4425};
4426
c0c5893f
RN
4427static GtkActionEntry entries_geojson[] = {
4428 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4429};
4430
e4afc73a
EB
4431/* Radio items */
4432static GtkRadioActionEntry mode_entries[] = {
123fff51
RN
4433 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4434 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4435 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4436 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
50a14534
EB
4437};
4438
35c7c0ba 4439static GtkToggleActionEntry toggle_entries[] = {
87d052f9
RN
4440 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4441 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4442 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
cbd293bb 4443 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
48df6aa3 4444 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
a459ee10 4445 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
48df6aa3
RN
4446 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4447 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
35c7c0ba
EB
4448};
4449
75b7457a
RN
4450// This must match the toggle entries order above
4451static gpointer toggle_entries_toolbar_cb[] = {
4452 (GCallback)tb_set_draw_scale,
4453 (GCallback)tb_set_draw_centermark,
4454 (GCallback)tb_set_draw_highlight,
4455 (GCallback)tb_full_screen_cb,
4456 (GCallback)tb_view_side_panel_cb,
4457 (GCallback)tb_view_statusbar_cb,
4458 (GCallback)tb_view_toolbar_cb,
4459 (GCallback)tb_view_main_menu_cb,
4460};
4461
a527cfff 4462#include "menu.xml.h"
e4afc73a 4463static void window_create_ui( VikWindow *window )
50a14534 4464{
e4afc73a
EB
4465 GtkUIManager *uim;
4466 GtkActionGroup *action_group;
50a14534 4467 GtkAccelGroup *accel_group;
e4afc73a 4468 GError *error;
e4afc73a
EB
4469 guint i, j, mid;
4470 GtkIconFactory *icon_factory;
4471 GtkIconSet *icon_set;
e4afc73a
EB
4472 GtkRadioActionEntry *tools = NULL, *radio;
4473 guint ntools;
4474
4475 uim = gtk_ui_manager_new ();
4476 window->uim = uim;
4477
9593a4c9
EB
4478 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4479 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
576cbd17 4480 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
a47bfefa 4481 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
941aa6e9 4482
75b7457a
RN
4483 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4484 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4485 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4486 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4487
e4afc73a 4488 error = NULL;
a527cfff 4489 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
e4afc73a
EB
4490 g_error_free (error);
4491 exit (1);
4492 }
4493
4494 action_group = gtk_action_group_new ("MenuActions");
5515e2d3 4495 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
e4afc73a 4496 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
35c7c0ba 4497 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
6a9ff0ee 4498 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
4499 if ( vik_debug ) {
4500 if ( gtk_ui_manager_add_ui_from_string ( uim,
fca1f3ad
RN
4501 "<ui><menubar name='MainMenu'><menu action='Help'>"
4502 "<menuitem action='MapCacheInfo'/>"
4503 "<menuitem action='BackForwardInfo'/>"
4504 "</menu></menubar></ui>",
a3040a49
RN
4505 -1, NULL ) ) {
4506 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4507 }
4508 }
4509
75b7457a
RN
4510 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4511 if ( entries[i].callback )
4512 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4513 }
4514
4515 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4516 g_print ( "Broken entries definitions\n" );
4517 exit (1);
4518 }
4519 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4520 if ( toggle_entries_toolbar_cb[i] )
4521 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4522 }
4523
4524 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4525 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4526 }
e4afc73a 4527
0bb36e78 4528 // Use this to see if GPSBabel is available:
430a37a9 4529 if ( a_babel_available () ) {
0bb36e78
RN
4530 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4531 if ( gtk_ui_manager_add_ui_from_string ( uim,
4532 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4533 -1, &error ) )
4534 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4535 }
4536
c0c5893f
RN
4537 // GeoJSON import capability
4538 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4539 if ( gtk_ui_manager_add_ui_from_string ( uim,
4540 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4541 -1, &error ) )
4542 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4543 }
4544
e4afc73a 4545 icon_factory = gtk_icon_factory_new ();
ee36ac4f 4546 gtk_icon_factory_add_default (icon_factory);
e4afc73a
EB
4547
4548 register_vik_icons(icon_factory);
4549
79dce0cb
RN
4550 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4551 // so that it can be applied to the UI in one action group add function call below
e4afc73a 4552 ntools = 0;
79dce0cb 4553 for (i=0; i<window->vt->n_tools; i++) {
e4afc73a
EB
4554 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4555 radio = &tools[ntools];
4556 ntools++;
79dce0cb 4557 *radio = window->vt->tools[i].ti.radioActionEntry;
e4afc73a 4558 radio->value = ntools;
79dce0cb 4559 }
e4afc73a
EB
4560
4561 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4562 GtkActionEntry action;
4563 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4564 vik_layer_get_interface(i)->name,
4565 vik_layer_get_interface(i)->name,
4566 GTK_UI_MANAGER_MENUITEM, FALSE);
4567
4568 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4569 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4570 gtk_icon_set_unref (icon_set);
4571
4572 action.name = vik_layer_get_interface(i)->name;
4573 action.stock_id = vik_layer_get_interface(i)->name;
da12e3d1 4574 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
75078768 4575 action.accelerator = vik_layer_get_interface(i)->accelerator;
e4afc73a
EB
4576 action.tooltip = NULL;
4577 action.callback = (GCallback)menu_addlayer_cb;
4578 gtk_action_group_add_actions(action_group, &action, 1, window);
4579
644eea0e
RN
4580 g_free ( (gchar*)action.label );
4581
e4afc73a
EB
4582 if ( vik_layer_get_interface(i)->tools_count ) {
4583 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
e4afc73a
EB
4584 }
4585
79dce0cb 4586 // Further tool copying for to apply to the UI, also apply menu UI setup
e4afc73a
EB
4587 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4588 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4589 radio = &tools[ntools];
4590 ntools++;
4591
e4afc73a 4592 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
79dce0cb
RN
4593 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4594 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
e4afc73a 4595 GTK_UI_MANAGER_MENUITEM, FALSE);
e4afc73a 4596
9593a4c9 4597 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
75b7457a 4598 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
941aa6e9 4599
79dce0cb
RN
4600 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4601 // Overwrite with actual number to use
e4afc73a
EB
4602 radio->value = ntools;
4603 }
a7023a1b
RN
4604
4605 GtkActionEntry action_dl;
644eea0e 4606 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
a7023a1b
RN
4607 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4608 vik_layer_get_interface(i)->name,
644eea0e 4609 layername,
a7023a1b 4610 GTK_UI_MANAGER_MENUITEM, FALSE);
644eea0e 4611 g_free (layername);
a7023a1b
RN
4612
4613 // For default layers use action names of the form 'Layer<LayerName>'
4614 // This is to avoid clashing with just the layer name used above for the tool actions
4615 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4616 action_dl.stock_id = NULL;
4617 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4618 action_dl.accelerator = NULL;
4619 action_dl.tooltip = NULL;
4620 action_dl.callback = (GCallback)layer_defaults_cb;
4621 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
644eea0e
RN
4622 g_free ( (gchar*)action_dl.name );
4623 g_free ( (gchar*)action_dl.label );
e4afc73a 4624 }
ee36ac4f 4625 g_object_unref (icon_factory);
50a14534 4626
75b7457a 4627 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
e4afc73a 4628 g_free(tools);
50a14534 4629
e4afc73a 4630 gtk_ui_manager_insert_action_group (uim, action_group, 0);
50a14534 4631
79845167
QT
4632 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4633 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4634 GtkAction *action = gtk_action_group_get_action(action_group,
79dce0cb 4635 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
79845167
QT
4636 g_object_set(action, "sensitive", FALSE, NULL);
4637 }
4638 }
a147baa7
RN
4639
4640 // This is done last so we don't need to track the value of mid anymore
4641 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4642
79845167
QT
4643 window->action_group = action_group;
4644
e4afc73a
EB
4645 accel_group = gtk_ui_manager_get_accel_group (uim);
4646 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4647 gtk_ui_manager_ensure_update (uim);
13505702
GB
4648
4649 setup_recent_files(window);
e4afc73a 4650}
50a14534 4651
50a14534 4652
79dce0cb
RN
4653// TODO - add method to add tool icons defined from outside this file
4654// and remove the reverse dependency on icon definition from this file
e4afc73a 4655static struct {
4bd45256 4656 const GdkPixdata *data;
e4afc73a
EB
4657 gchar *stock_id;
4658} stock_icons[] = {
79dce0cb 4659 { &mover_22_pixbuf, "vik-icon-pan" },
5bfafde9
GB
4660 { &zoom_18_pixbuf, "vik-icon-zoom" },
4661 { &ruler_18_pixbuf, "vik-icon-ruler" },
a47bfefa 4662 { &select_18_pixbuf, "vik-icon-select" },
e37b2a6d 4663 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
79dce0cb 4664 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
06a6af5b 4665 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
79dce0cb
RN
4666 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4667 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4668 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4669 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4670 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4671 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4672 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4673 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
e4afc73a
EB
4674};
4675
e4afc73a
EB
4676static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4677
4678static void
4679register_vik_icons (GtkIconFactory *icon_factory)
4680{
4681 GtkIconSet *icon_set;
e4afc73a 4682 gint i;
e4afc73a
EB
4683
4684 for (i = 0; i < n_stock_icons; i++) {
4bd45256
EB
4685 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4686 stock_icons[i].data, FALSE, NULL ));
e4afc73a
EB
4687 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4688 gtk_icon_set_unref (icon_set);
4689 }
50a14534 4690}
bce3a7b0 4691
9d7c24ed
RN
4692gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4693{
4694 return vw->selected_vtl;
4695}
4696
4697void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4698{
113c74f6
RN
4699 vw->selected_vtl = vtl;
4700 vw->containing_vtl = vtl;
9d7c24ed
RN
4701 /* Clear others */
4702 vw->selected_track = NULL;
4703 vw->selected_tracks = NULL;
4704 vw->selected_waypoint = NULL;
4705 vw->selected_waypoints = NULL;
04f36d92
RN
4706 // Set highlight thickness
4707 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
9d7c24ed
RN
4708}
4709
1c70a947 4710GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
9d7c24ed
RN
4711{
4712 return vw->selected_tracks;
4713}
4714
1c70a947 4715void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
9d7c24ed 4716{
1c70a947 4717 vw->selected_tracks = ght;
113c74f6 4718 vw->containing_vtl = vtl;
9d7c24ed
RN
4719 /* Clear others */
4720 vw->selected_vtl = NULL;
4721 vw->selected_track = NULL;
4722 vw->selected_waypoint = NULL;
4723 vw->selected_waypoints = NULL;
04f36d92
RN
4724 // Set highlight thickness
4725 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
9d7c24ed
RN
4726}
4727
4728gpointer vik_window_get_selected_track ( VikWindow *vw )
4729{
4730 return vw->selected_track;
4731}
4732
b16effab 4733void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
9d7c24ed
RN
4734{
4735 vw->selected_track = vt;
113c74f6 4736 vw->containing_vtl = vtl;
9d7c24ed
RN
4737 /* Clear others */
4738 vw->selected_vtl = NULL;
4739 vw->selected_tracks = NULL;
4740 vw->selected_waypoint = NULL;
4741 vw->selected_waypoints = NULL;
04f36d92
RN
4742 // Set highlight thickness
4743 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
9d7c24ed 4744}
04f36d92 4745
1c70a947 4746GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
9d7c24ed
RN
4747{
4748 return vw->selected_waypoints;
4749}
4750
1c70a947 4751void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
9d7c24ed 4752{
1c70a947 4753 vw->selected_waypoints = ght;
113c74f6 4754 vw->containing_vtl = vtl;
9d7c24ed
RN
4755 /* Clear others */
4756 vw->selected_vtl = NULL;
4757 vw->selected_track = NULL;
4758 vw->selected_tracks = NULL;
4759 vw->selected_waypoint = NULL;
4760}
4761
4762gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4763{
4764 return vw->selected_waypoint;
4765}
4766
b16effab 4767void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
9d7c24ed
RN
4768{
4769 vw->selected_waypoint = vwp;
113c74f6 4770 vw->containing_vtl = vtl;
9d7c24ed
RN
4771 /* Clear others */
4772 vw->selected_vtl = NULL;
4773 vw->selected_track = NULL;
4774 vw->selected_tracks = NULL;
4775 vw->selected_waypoints = NULL;
4776}
4777
4778gboolean vik_window_clear_highlight ( VikWindow *vw )
4779{
4780 gboolean need_redraw = FALSE;
4781 if ( vw->selected_vtl != NULL ) {
4782 vw->selected_vtl = NULL;
4783 need_redraw = TRUE;
4784 }
4785 if ( vw->selected_track != NULL ) {
4786 vw->selected_track = NULL;
4787 need_redraw = TRUE;
4788 }
4789 if ( vw->selected_tracks != NULL ) {
4790 vw->selected_tracks = NULL;
4791 need_redraw = TRUE;
4792 }
4793 if ( vw->selected_waypoint != NULL ) {
4794 vw->selected_waypoint = NULL;
4795 need_redraw = TRUE;
4796 }
4797 if ( vw->selected_waypoints != NULL ) {
4798 vw->selected_waypoints = NULL;
4799 need_redraw = TRUE;
4800 }
4801 return need_redraw;
4802}
fa51adec 4803
86642fdf
RN
4804/**
4805 * May return NULL if the window no longer exists
4806 */
fa51adec
RN
4807GThread *vik_window_get_thread ( VikWindow *vw )
4808{
86642fdf
RN
4809 if ( vw )
4810 return vw->thread;
4811 return NULL;
fa51adec 4812}