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