]> git.street.me.uk Git - andy/viking.git/blame - src/vikwindow.c
Fix a_vik_gpx_export_wpt_sym_name internal type
[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>
79dce0cb 6 * Copyright (C) 2012, 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"
34e71b99 32#include "vikgoto.h"
071da616 33#include "dems.h"
7c259702 34#include "mapcache.h"
42f34743 35#include "print.h"
17a1f8f9 36#include "preferences.h"
a7023a1b 37#include "viklayer_defaults.h"
f2f2f7bf 38#include "icons/icons.h"
92806042 39#include "vikexttools.h"
9be0449f 40#include "garminsymbols.h"
6b59f63d 41#include "vikmapslayer.h"
3c29a566 42#include "geonamessearch.h"
50a14534 43
8c00358d 44#ifdef HAVE_STDLIB_H
e4afc73a 45#include <stdlib.h>
8c00358d
GB
46#endif
47#ifdef HAVE_MATH_H
50a14534 48#include <math.h>
8c00358d
GB
49#endif
50#ifdef HAVE_STRING_H
50a14534 51#include <string.h>
8c00358d 52#endif
50a14534 53#include <ctype.h>
f83131b9
MA
54#include <glib.h>
55#include <glib/gstdio.h>
1d1bc3c1 56#include <glib/gprintf.h>
4c77d5e0 57#include <glib/gi18n.h>
314084b8 58#include <gio/gio.h>
7622022f 59#include <gdk/gdkkeysyms.h>
50a14534 60
8a13dbd2
RN
61// This seems rather arbitary, quite large and pointless
62// I mean, if you have a thousand windows open;
63// why not be allowed to open a thousand more...
64#define MAX_WINDOWS 1024
65static guint window_count = 0;
98acb9a1 66static GSList *window_list = NULL;
8a13dbd2 67
3570ad57
QT
68#define VIKING_WINDOW_WIDTH 1000
69#define VIKING_WINDOW_HEIGHT 800
50a14534
EB
70#define DRAW_IMAGE_DEFAULT_WIDTH 1280
71#define DRAW_IMAGE_DEFAULT_HEIGHT 1024
72#define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
73
74static void window_finalize ( GObject *gob );
75static GObjectClass *parent_class;
76
4c77d5e0 77static void window_set_filename ( VikWindow *vw, const gchar *filename );
4522c4ff 78static const gchar *window_get_filename ( VikWindow *vw );
50a14534 79
8a13dbd2
RN
80static VikWindow *window_new ();
81
50a14534
EB
82static void draw_update ( VikWindow *vw );
83
e4afc73a 84static void newwindow_cb ( GtkAction *a, VikWindow *vw );
50a14534 85
8a13dbd2
RN
86// Signals
87static void open_window ( VikWindow *vw, GSList *files );
461c9c4f 88static void statusbar_update ( VikWindow *vw, vik_statusbar_type_t vs_type, const gchar *message );
8a13dbd2
RN
89static void destroy_window ( GtkWidget *widget,
90 gpointer data );
91
50a14534
EB
92/* Drawing & stuff */
93
94static gboolean delete_event( VikWindow *vw );
95
777e2d4d
EB
96static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
97
bce3a7b0 98static void window_configure_event ( VikWindow *vw );
50a14534
EB
99static void draw_sync ( VikWindow *vw );
100static void draw_redraw ( VikWindow *vw );
941aa6e9 101static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
50a14534
EB
102static void draw_click ( VikWindow *vw, GdkEventButton *event );
103static void draw_release ( VikWindow *vw, GdkEventButton *event );
104static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
e4afc73a
EB
105static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
106static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
6b59f63d 107static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
50a14534 108
c8430548 109static void draw_status ( VikWindow *vw );
50a14534
EB
110
111/* End Drawing Functions */
112
e4afc73a
EB
113static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
114static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
115static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
941aa6e9
AF
116
117/* tool management */
118typedef struct {
119 VikToolInterface ti;
120 gpointer state;
9593a4c9 121 gint layer_type;
941aa6e9 122} toolbox_tool_t;
9593a4c9 123#define TOOL_LAYER_TYPE_NONE -1
941aa6e9
AF
124
125typedef struct {
126 int active_tool;
127 int n_tools;
128 toolbox_tool_t *tools;
129 VikWindow *vw;
130} toolbox_tools_t;
131
e4afc73a 132static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
941aa6e9 133static toolbox_tools_t* toolbox_create(VikWindow *vw);
9593a4c9 134static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
941aa6e9
AF
135static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
136static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
f2f2f7bf 137static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
941aa6e9 138static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
dc2c040e 139static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
941aa6e9
AF
140static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
141
50a14534 142
941aa6e9 143/* ui creation */
e4afc73a 144static void window_create_ui( VikWindow *window );
941aa6e9 145static void register_vik_icons (GtkIconFactory *icon_factory);
50a14534 146
941aa6e9 147/* i/o */
e4afc73a
EB
148static void load_file ( GtkAction *a, VikWindow *vw );
149static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
150static gboolean save_file ( GtkAction *a, VikWindow *vw );
2bf7cadd 151static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
50a14534
EB
152static gboolean window_save ( VikWindow *vw );
153
154struct _VikWindow {
155 GtkWindow gtkwindow;
156 VikViewport *viking_vvp;
157 VikLayersPanel *viking_vlp;
158 VikStatusbar *viking_vs;
159
e4afc73a
EB
160 GtkToolbar *toolbar;
161
55d3a53c
RN
162 GdkCursor *busy_cursor;
163 GdkCursor *viewport_cursor; // only a reference
164
941aa6e9 165 /* tool management state */
50a14534 166 guint current_tool;
941aa6e9 167 toolbox_tools_t *vt;
50a14534
EB
168 guint16 tool_layer_id;
169 guint16 tool_tool_id;
170
79845167
QT
171 GtkActionGroup *action_group;
172
b71eff77 173 gboolean pan_move;
50a14534
EB
174 gint pan_x, pan_y;
175
176 guint draw_image_width, draw_image_height;
177 gboolean draw_image_save_as_png;
178
179 gchar *filename;
180 gboolean modified;
181
182 GtkWidget *open_dia, *save_dia;
f2a1ca71 183 GtkWidget *save_img_dia, *save_img_dir_dia;
50a14534
EB
184
185 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
e4afc73a 186 GtkUIManager *uim;
c9177aae 187
fa51adec 188 GThread *thread;
c9177aae
QT
189 /* half-drawn update */
190 VikLayer *trigger;
191 VikCoord trigger_center;
9d7c24ed
RN
192
193 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
194 /* Only one of these items can be selected at the same time */
195 gpointer selected_vtl; /* notionally VikTrwLayer */
1c70a947 196 GHashTable *selected_tracks;
9d7c24ed 197 gpointer selected_track; /* notionally VikTrack */
1c70a947 198 GHashTable *selected_waypoints;
9d7c24ed 199 gpointer selected_waypoint; /* notionally VikWaypoint */
43f2e1da 200 /* only use for individual track or waypoint */
113c74f6
RN
201 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
202 gpointer containing_vtl; /* notionally VikTrwLayer */
50a14534
EB
203};
204
205enum {
576cbd17
GB
206 TOOL_PAN = 0,
207 TOOL_ZOOM,
50a14534 208 TOOL_RULER,
a47bfefa 209 TOOL_SELECT,
50a14534
EB
210 TOOL_LAYER,
211 NUMBER_OF_TOOLS
212};
213
214enum {
215 VW_NEWWINDOW_SIGNAL,
216 VW_OPENWINDOW_SIGNAL,
90142302 217 VW_STATUSBAR_UPDATE_SIGNAL,
50a14534
EB
218 VW_LAST_SIGNAL
219};
220
221static guint window_signals[VW_LAST_SIGNAL] = { 0 };
222
79dce0cb 223// TODO get rid of this as this is unnecessary duplication...
a47bfefa 224static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
50a14534 225
f3c798e9 226G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
50a14534 227
014128f6
QT
228VikViewport * vik_window_viewport(VikWindow *vw)
229{
230 return(vw->viking_vvp);
231}
232
55477ce6
RN
233VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
234{
235 return(vw->viking_vlp);
236}
237
c06a63ad
RN
238/**
239 * Returns the statusbar for the window
240 */
241VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
242{
243 return vw->viking_vs;
244}
245
90142302
RN
246/**
247 * For signalling the update from a background thread
248 */
8e7bf2f6 249void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type)
90142302 250{
461c9c4f 251 g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, vs_type, message );
90142302
RN
252}
253
254/**
255 * For the actual statusbar update!
256 */
3ceb0792 257static gboolean statusbar_idle_update ( gpointer indata )
90142302
RN
258{
259 gpointer *data = indata;
8e7bf2f6 260 vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] );
3ceb0792 261 return FALSE;
90142302
RN
262}
263
264/**
265 * Update statusbar in the main thread
266 */
8e7bf2f6 267static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
90142302
RN
268{
269 // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing)
8e7bf2f6 270 static gpointer data[3];
90142302
RN
271 data[0] = vw->viking_vs;
272 data[1] = (gchar*) message;
8e7bf2f6 273 data[2] = GINT_TO_POINTER(vs_type);
90142302
RN
274 g_idle_add ( (GSourceFunc) statusbar_idle_update, data );
275}
276
8a13dbd2
RN
277// Actual signal handlers
278static void destroy_window ( GtkWidget *widget,
279 gpointer data )
280{
281 if ( ! --window_count )
282 gtk_main_quit ();
283}
284
461c9c4f 285static void statusbar_update ( VikWindow *vw, vik_statusbar_type_t vs_type, const gchar *message )
8a13dbd2 286{
8e7bf2f6 287 window_statusbar_update ( vw, message, vs_type );
8a13dbd2
RN
288}
289
290VikWindow *vik_window_new_window ()
291{
292 if ( window_count < MAX_WINDOWS )
293 {
294 VikWindow *vw = window_new ();
295
296 g_signal_connect (G_OBJECT (vw), "destroy",
297 G_CALLBACK (destroy_window), NULL);
298 g_signal_connect (G_OBJECT (vw), "newwindow",
299 G_CALLBACK (vik_window_new_window), NULL);
300 g_signal_connect (G_OBJECT (vw), "openwindow",
301 G_CALLBACK (open_window), NULL);
302 g_signal_connect (G_OBJECT (vw), "statusbarupdate",
8e7bf2f6 303 G_CALLBACK (statusbar_update), vw);
8a13dbd2
RN
304
305 gtk_widget_show_all ( GTK_WIDGET(vw) );
306
307 window_count++;
308
309 return vw;
310 }
311 return NULL;
312}
313
314static void open_window ( VikWindow *vw, GSList *files )
315{
316 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
317 GSList *cur_file = files;
318 while ( cur_file ) {
319 // Only open a new window if a viking file
320 gchar *file_name = cur_file->data;
321 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
322 VikWindow *newvw = vik_window_new_window ();
323 if (newvw)
d4a8b54d 324 vik_window_open_file ( newvw, file_name, TRUE );
8a13dbd2
RN
325 }
326 else {
327 vik_window_open_file ( vw, file_name, change_fn );
328 }
329 g_free (file_name);
330 cur_file = g_slist_next (cur_file);
331 }
332 g_slist_free (files);
333}
334// End signals
335
79845167
QT
336void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
337{
338 int i, j, tool_count;
339 VikLayerInterface *layer_interface;
340
341 if (!vw->action_group) return;
342
343 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
344 GtkAction *action;
345 layer_interface = vik_layer_get_interface(i);
346 tool_count = layer_interface->tools_count;
347
348 for (j = 0; j < tool_count; j++) {
349 action = gtk_action_group_get_action(vw->action_group,
79dce0cb 350 layer_interface->tools[j].radioActionEntry.name);
79845167
QT
351 g_object_set(action, "sensitive", i == vl->type, NULL);
352 }
353 }
354}
355
50a14534
EB
356static void window_finalize ( GObject *gob )
357{
358 VikWindow *vw = VIK_WINDOW(gob);
359 g_return_if_fail ( vw != NULL );
360
90142302 361 a_background_remove_window ( vw );
50a14534 362
98acb9a1
RN
363 window_list = g_slist_remove ( window_list, vw );
364
55d3a53c
RN
365 gdk_cursor_unref ( vw->busy_cursor );
366
50a14534
EB
367 G_OBJECT_CLASS(parent_class)->finalize(gob);
368}
369
bce3a7b0 370
f3c798e9 371static void vik_window_class_init ( VikWindowClass *klass )
50a14534
EB
372{
373 /* destructor */
374 GObjectClass *object_class;
375
376 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 377 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);
461c9c4f 378 window_signals[VW_STATUSBAR_UPDATE_SIGNAL] = g_signal_new ( "statusbarupdate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, statusbarupdate), NULL, NULL, g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
50a14534
EB
379
380 object_class = G_OBJECT_CLASS (klass);
381
382 object_class->finalize = window_finalize;
383
384 parent_class = g_type_class_peek_parent (klass);
385
386}
387
5c0bf50b
GB
388static void zoom_changed (GtkMenuShell *menushell,
389 gpointer user_data)
ac4478f4 390{
5c0bf50b
GB
391 VikWindow *vw = VIK_WINDOW (user_data);
392
393 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
74dd7f07 394 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
5c0bf50b
GB
395
396 gdouble zoom_request = pow (2, active-2 );
ac4478f4 397
c4b6a67d
GB
398 // But has it really changed?
399 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
400 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
401 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
402 // Force drawing update
403 draw_update ( vw );
404 }
c4b6a67d
GB
405}
406
5c0bf50b 407static GtkWidget * create_zoom_menu_all_levels ()
ac4478f4 408{
5c0bf50b
GB
409 GtkWidget *menu = gtk_menu_new ();
410 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768", NULL };
ac4478f4 411
5c0bf50b
GB
412 int i;
413 for (i = 0 ; itemLabels[i] != NULL ; i++)
414 {
415 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
416 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
417 gtk_widget_show (item);
74dd7f07 418 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
5c0bf50b
GB
419 }
420
421 return menu;
ac4478f4
RN
422}
423
424static GtkWidget *create_zoom_combo_all_levels ()
425{
1bc1c05b
RN
426 GtkWidget *combo = vik_combo_box_text_new();
427 vik_combo_box_text_append ( combo, "0.25");
428 vik_combo_box_text_append ( combo, "0.5");
429 vik_combo_box_text_append ( combo, "1");
430 vik_combo_box_text_append ( combo, "2");
431 vik_combo_box_text_append ( combo, "4");
432 vik_combo_box_text_append ( combo, "8");
433 vik_combo_box_text_append ( combo, "16");
434 vik_combo_box_text_append ( combo, "32");
435 vik_combo_box_text_append ( combo, "64");
436 vik_combo_box_text_append ( combo, "128");
437 vik_combo_box_text_append ( combo, "256");
438 vik_combo_box_text_append ( combo, "512");
439 vik_combo_box_text_append ( combo, "1024");
440 vik_combo_box_text_append ( combo, "2048");
441 vik_combo_box_text_append ( combo, "4096");
442 vik_combo_box_text_append ( combo, "8192");
443 vik_combo_box_text_append ( combo, "16384");
444 vik_combo_box_text_append ( combo, "32768");
d0bf2f3a 445 /* Create tooltip */
1bc1c05b
RN
446 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
447 return combo;
ac4478f4
RN
448}
449
5c0bf50b
GB
450static gint zoom_popup_handler (GtkWidget *widget)
451{
452 GtkMenu *menu;
453
454 g_return_val_if_fail (widget != NULL, FALSE);
455 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
456
457 /* The "widget" is the menu that was supplied when
458 * g_signal_connect_swapped() was called.
459 */
460 menu = GTK_MENU (widget);
461
462 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
463 1, gtk_get_current_event_time());
464 return TRUE;
465}
466
f3c798e9 467static void vik_window_init ( VikWindow *vw )
50a14534
EB
468{
469 GtkWidget *main_vbox;
470 GtkWidget *hpaned;
471
79845167 472 vw->action_group = NULL;
50a14534 473
24277274 474 vw->viking_vvp = vik_viewport_new();
50a14534
EB
475 vw->viking_vlp = vik_layers_panel_new();
476 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
477 vw->viking_vs = vik_statusbar_new();
478
941aa6e9
AF
479 vw->vt = toolbox_create(vw);
480 window_create_ui(vw);
4c77d5e0 481 window_set_filename (vw, NULL);
64e3d6c9
RN
482 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
483
55d3a53c
RN
484 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
485
33bc7c1b
RN
486 // Set the default tool
487 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
941aa6e9 488
50a14534
EB
489 vw->filename = NULL;
490
50a14534
EB
491 vw->modified = FALSE;
492 vw->only_updating_coord_mode_ui = FALSE;
b71eff77
JJ
493
494 vw->pan_move = FALSE;
50a14534
EB
495 vw->pan_x = vw->pan_y = -1;
496 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
497 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
498 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
499
500 main_vbox = gtk_vbox_new(FALSE, 1);
501 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
502
e4afc73a 503 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
64e3d6c9
RN
504 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
505 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
506 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
50a14534 507
5c0bf50b
GB
508 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
509 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ();
510 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
511 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
512 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
ac4478f4 513
50a14534
EB
514 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
515
516 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
bce3a7b0 517 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
fe27d6d2 518 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
519 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
520 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
521 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
522 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
523 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
524
6b59f63d
RN
525 // Allow key presses to be processed anywhere
526 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
777e2d4d 527
9a3cdf12 528 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
50a14534
EB
529
530 hpaned = gtk_hpaned_new ();
181f5d0c
MA
531 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
532 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
50a14534
EB
533
534 /* This packs the button into the window (a gtk container). */
535 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
536
537 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
538
90142302 539 a_background_add_window ( vw );
50a14534 540
98acb9a1
RN
541 window_list = g_slist_prepend ( window_list, vw);
542
50a14534
EB
543 vw->open_dia = NULL;
544 vw->save_dia = NULL;
f2a1ca71
QT
545 vw->save_img_dia = NULL;
546 vw->save_img_dir_dia = NULL;
fa51adec
RN
547
548 // Store the thread value so comparisons can be made to determine the gdk update method
549 // Hopefully we are storing the main thread value here :)
550 // [ATM any window initialization is always be performed by the main thread]
551 vw->thread = g_thread_self();
50a14534
EB
552}
553
8a13dbd2 554static VikWindow *window_new ()
50a14534
EB
555{
556 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
557}
558
6b59f63d
RN
559/**
560 * Update the displayed map
561 * Only update the top most visible map layer
562 * ATM this assumes (as per defaults) the top most map has full alpha setting
563 * such that other other maps even though they may be active will not be seen
564 * It's more complicated to work out which maps are actually visible due to alpha settings
565 * and overkill for this simple refresh method.
566 */
567static void simple_map_update ( VikWindow *vw, gboolean only_new )
568{
569 // Find the most relevent single map layer to operate on
570 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
571 if ( vl )
572 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
573}
574
575/**
576 * This is the global key press handler
577 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
578 */
777e2d4d
EB
579static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
580{
6b59f63d
RN
581 // The keys handled here are not in the menuing system for a couple of reasons:
582 // . Keeps the menu size compact (alebit at expense of discoverably)
583 // . Allows differing key bindings to perform the same actions
584
585 // First decide if key events are related to the maps layer
586 gboolean map_download = FALSE;
587 gboolean map_download_only_new = TRUE; // Only new or reload
588
589 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
590
591 // Standard 'Refresh' keys: F5 or Ctrl+r
592 // Note 'F5' is actually handled via draw_refresh_cb() later on
593 // (not 'R' it's 'r' notice the case difference!!)
594 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
595 map_download = TRUE;
596 map_download_only_new = TRUE;
597 }
598 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
599 // Note the use of uppercase R here since shift key has been pressed
600 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
601 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
602 map_download = TRUE;
603 map_download_only_new = FALSE;
604 }
605
606 if ( map_download ) {
607 simple_map_update ( vw, map_download_only_new );
608 }
609
777e2d4d
EB
610 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
611 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
612 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
613 if ( vl && ltype == vl->type )
614 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
615 }
7622022f 616
c68b3c06
RN
617 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
618 if ( vw->current_tool < TOOL_LAYER ) {
619 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
620 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
621 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
622 }
a7114521
RN
623 }
624
7622022f
RN
625 /* Restore Main Menu via Escape key if the user has hidden it */
626 /* This key is more likely to be used as they may not remember the function key */
627 if ( event->keyval == GDK_Escape ) {
48df6aa3 628 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
7622022f
RN
629 if ( check_box ) {
630 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
631 if ( !state ) {
632 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
633 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
634 return TRUE; /* handled keypress */
635 }
636 }
637 }
638
777e2d4d
EB
639 return FALSE; /* don't handle the keypress */
640}
641
50a14534
EB
642static gboolean delete_event( VikWindow *vw )
643{
a5fd2196 644#ifdef VIKING_PROMPT_IF_MODIFIED
50a14534 645 if ( vw->modified )
a5fd2196
QT
646#else
647 if (0)
648#endif
50a14534
EB
649 {
650 GtkDialog *dia;
651 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
4c77d5e0
GB
652 _("Do you want to save the changes you made to the document \"%s\"?\n"
653 "\n"
654 "Your changes will be lost if you don't save them."),
4522c4ff 655 window_get_filename ( vw ) ) );
4c77d5e0 656 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
657 switch ( gtk_dialog_run ( dia ) )
658 {
659 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
660 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
e4afc73a 661 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
50a14534
EB
662 }
663 }
664 return FALSE;
665}
666
667/* Drawing stuff */
e4afc73a 668static void newwindow_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
669{
670 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
671}
672
673static void draw_update ( VikWindow *vw )
674{
675 draw_redraw (vw);
676 draw_sync (vw);
677}
678
679static void draw_sync ( VikWindow *vw )
680{
681 vik_viewport_sync(vw->viking_vvp);
682 draw_status ( vw );
ac4478f4
RN
683}
684
685/*
686 * Split the status update, as sometimes only need to update the tool part
687 * also on initialization the zoom related stuff is not ready to be used
688 */
689static void draw_status_tool ( VikWindow *vw )
690{
691 if ( vw->current_tool == TOOL_LAYER )
692 // Use tooltip rather than the internal name as the tooltip is i8n
693 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 );
694 else
695 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
50a14534
EB
696}
697
698static void draw_status ( VikWindow *vw )
699{
700 static gchar zoom_level[22];
82940cf6
GB
701 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
702 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
703 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
704 if (xmpp != ympp)
705 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
706 else
fb5e99bb
RN
707 if ( (int)xmpp - xmpp < 0.0 )
708 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
709 else
710 /* xmpp should be a whole number so don't show useless .000 bit */
711 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
50a14534 712
4efc10ca 713 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
ac4478f4
RN
714
715 draw_status_tool ( vw );
50a14534
EB
716}
717
c9177aae
QT
718void vik_window_set_redraw_trigger(VikLayer *vl)
719{
730a38c1 720 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
be3b5803
GB
721 if (NULL != vw)
722 vw->trigger = vl;
c9177aae
QT
723}
724
bce3a7b0
EB
725static void window_configure_event ( VikWindow *vw )
726{
f2f2f7bf 727 static int first = 1;
bce3a7b0 728 draw_redraw ( vw );
372132a6
GB
729 if (first) {
730 // This is a hack to set the cursor corresponding to the first tool
731 // FIXME find the correct way to initialize both tool and its cursor
732 first = 0;
55d3a53c 733 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
f2f2f7bf 734 /* We set cursor, even if it is NULL: it resets to default */
9b082b39 735 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
372132a6 736 }
bce3a7b0
EB
737}
738
50a14534
EB
739static void draw_redraw ( VikWindow *vw )
740{
c9177aae
QT
741 VikCoord old_center = vw->trigger_center;
742 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
743 VikLayer *new_trigger = vw->trigger;
744 vw->trigger = NULL;
0df66d57
EB
745 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
746
747 if ( ! new_trigger )
748 ; /* do nothing -- have to redraw everything. */
07c9d0bf 749 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
0df66d57
EB
750 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
751 else
752 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
753
754 /* actually draw */
50a14534 755 vik_viewport_clear ( vw->viking_vvp);
50a14534 756 vik_layers_panel_draw_all ( vw->viking_vlp );
acaf7113 757 vik_viewport_draw_scale ( vw->viking_vvp );
82aa018d 758 vik_viewport_draw_copyright ( vw->viking_vvp );
c933487f 759 vik_viewport_draw_centermark ( vw->viking_vvp );
26336cf0 760 vik_viewport_draw_logo ( vw->viking_vvp );
0df66d57
EB
761
762 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
50a14534
EB
763}
764
941aa6e9
AF
765gboolean draw_buf_done = TRUE;
766
767static gboolean draw_buf(gpointer data)
768{
769 gpointer *pass_along = data;
770 gdk_threads_enter();
771 gdk_draw_drawable (pass_along[0], pass_along[1],
772 pass_along[2], 0, 0, 0, 0, -1, -1);
773 draw_buf_done = TRUE;
774 gdk_threads_leave();
775 return FALSE;
776}
777
778
779/* Mouse event handlers ************************************************************************/
780
b57126a3
GB
781static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
782{
783 /* set panning origin */
b71eff77 784 vw->pan_move = FALSE;
b57126a3
GB
785 vw->pan_x = (gint) event->x;
786 vw->pan_y = (gint) event->y;
787}
788
941aa6e9
AF
789static void draw_click (VikWindow *vw, GdkEventButton *event)
790{
165d30aa 791 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
941aa6e9
AF
792
793 /* middle button pressed. we reserve all middle button and scroll events
794 * for panning and zooming; tools only get left/right/movement
795 */
796 if ( event->button == 2) {
ef5e8132
RN
797 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
798 // Tool still may need to do something (such as disable something)
799 toolbox_click(vw->vt, event);
b57126a3 800 vik_window_pan_click ( vw, event );
941aa6e9
AF
801 }
802 else {
803 toolbox_click(vw->vt, event);
804 }
805}
806
b57126a3
GB
807static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
808{
809 if ( vw->pan_x != -1 ) {
b71eff77
JJ
810 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
811 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
b71eff77
JJ
812 vw->pan_move = TRUE;
813 vw->pan_x = event->x;
814 vw->pan_y = event->y;
a7abaae5 815 draw_update ( vw );
b57126a3
GB
816 }
817}
818
941aa6e9
AF
819static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
820{
821 static VikCoord coord;
822 static struct UTM utm;
823 static struct LatLon ll;
a58aaed4
GB
824 #define BUFFER_SIZE 50
825 static char pointer_buf[BUFFER_SIZE];
826 gchar *lat = NULL, *lon = NULL;
071da616 827 gint16 alt;
228213c5
QT
828 gdouble zoom;
829 VikDemInterpol interpol_method;
941aa6e9 830
fe27d6d2
SB
831 /* This is a hack, but work far the best, at least for single pointer systems.
832 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
833 gint x, y;
834 gdk_window_get_pointer (event->window, &x, &y, NULL);
835 event->x = x;
836 event->y = y;
837
576cbd17 838 toolbox_move(vw->vt, event);
941aa6e9
AF
839
840 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
841 vik_coord_to_utm ( &coord, &utm );
1a6e7d70
RN
842
843 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
844 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
845 // ZONE[N|S] EASTING NORTHING
846 lat = g_malloc(4*sizeof(gchar));
847 // NB zone is stored in a char but is an actual number
848 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
849 lon = g_malloc(16*sizeof(gchar));
850 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
851 }
852 else {
853 a_coords_utm_to_latlon ( &utm, &ll );
854 a_coords_latlon_to_string ( &ll, &lat, &lon );
855 }
856
228213c5
QT
857 /* Change interpolate method according to scale */
858 zoom = vik_viewport_get_zoom(vw->viking_vvp);
859 if (zoom > 2.0)
860 interpol_method = VIK_DEM_INTERPOL_NONE;
861 else if (zoom >= 1.0)
862 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
863 else
864 interpol_method = VIK_DEM_INTERPOL_BEST;
8c5f013a
RN
865 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
866 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
867 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
868 else
6c20e59a 869 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
8c5f013a 870 }
071da616 871 else
a58aaed4
GB
872 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
873 g_free (lat);
874 lat = NULL;
875 g_free (lon);
876 lon = NULL;
4efc10ca 877 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
941aa6e9 878
b57126a3 879 vik_window_pan_move ( vw, event );
fe27d6d2
SB
880
881 /* This is recommended by the GTK+ documentation, but does not work properly.
882 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
883 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
884 */
885 /* gdk_event_request_motions ( event ); */
b57126a3
GB
886}
887
888static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
889{
b71eff77 890 if ( vw->pan_move == FALSE )
b57126a3
GB
891 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
892 else
893 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
894 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
a7abaae5 895 vw->pan_move = FALSE;
b57126a3 896 vw->pan_x = vw->pan_y = -1;
a7abaae5 897 draw_update ( vw );
941aa6e9
AF
898}
899
900static void draw_release ( VikWindow *vw, GdkEventButton *event )
901{
165d30aa
EB
902 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
903
941aa6e9 904 if ( event->button == 2 ) { /* move / pan */
ef5e8132
RN
905 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
906 // Tool still may need to do something (such as reenable something)
907 toolbox_release(vw->vt, event);
908 vik_window_pan_release ( vw, event );
941aa6e9
AF
909 }
910 else {
911 toolbox_release(vw->vt, event);
912 }
913}
914
915static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
916{
d1a45556
JN
917 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
918 if ( modifiers == GDK_CONTROL_MASK ) {
8c721f83
EB
919 /* control == pan up & down */
920 if ( event->direction == GDK_SCROLL_UP )
921 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
922 else
923 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
924 } else if ( modifiers == GDK_SHIFT_MASK ) {
925 /* shift == pan left & right */
8c721f83
EB
926 if ( event->direction == GDK_SCROLL_UP )
927 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
928 else
929 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 930 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
15ff5402
RN
931 // This zoom is on the center position
932 if ( event->direction == GDK_SCROLL_UP )
933 vik_viewport_zoom_in (vw->viking_vvp);
934 else
935 vik_viewport_zoom_out (vw->viking_vvp);
936 } else {
937 /* make sure mouse is still over the same point on the map when we zoom */
3c575a4a
JN
938 VikCoord coord;
939 gint x, y;
940 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
941 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
942 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
943 if ( event->direction == GDK_SCROLL_UP )
8c721f83 944 vik_viewport_zoom_in (vw->viking_vvp);
3c575a4a 945 else
8c721f83 946 vik_viewport_zoom_out(vw->viking_vvp);
3c575a4a
JN
947 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
948 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
949 center_y + (y - event->y) );
8c721f83
EB
950 }
951
941aa6e9
AF
952 draw_update(vw);
953}
954
955
956
957/********************************************************************************
958 ** Ruler tool code
959 ********************************************************************************/
e4847ce9
AF
960static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
961{
e4847ce9
AF
962 PangoLayout *pl;
963 gchar str[128];
0dff88ea
AF
964 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
965 GdkGC *thickgc = gdk_gc_new(d);
966
e4847ce9
AF
967 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
968 gdouble dx = (x2-x1)/len*10;
969 gdouble dy = (y2-y1)/len*10;
0da53bd9
GB
970 gdouble c = cos(DEG2RAD(15.0));
971 gdouble s = sin(DEG2RAD(15.0));
0dff88ea 972 gdouble angle;
15614495 973 gdouble baseangle = 0;
0dff88ea 974 gint i;
e4847ce9 975
0dff88ea 976 /* draw line with arrow ends */
d9ffd267
EB
977 {
978 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
979 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
980 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
981 }
982
15614495
AF
983 a_viewport_clip_line(&x1, &y1, &x2, &y2);
984 gdk_draw_line(d, gc, x1, y1, x2, y2);
d9ffd267 985
e4847ce9
AF
986 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
987 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
988 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
989 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
990 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
991 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
992
0dff88ea
AF
993 /* draw compass */
994#define CR 80
995#define CW 4
15614495 996
9a3538f5 997 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
0dff88ea
AF
998
999 {
1000 GdkColor color;
1001 gdk_gc_copy(thickgc, gc);
1002 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1003 gdk_color_parse("#2255cc", &color);
1004 gdk_gc_set_rgb_fg_color(thickgc, &color);
1005 }
0da53bd9 1006 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 1007
e4847ce9 1008
0dff88ea
AF
1009 gdk_gc_copy(thickgc, gc);
1010 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1011 for (i=0; i<180; i++) {
0da53bd9
GB
1012 c = cos(DEG2RAD(i)*2 + baseangle);
1013 s = sin(DEG2RAD(i)*2 + baseangle);
e4847ce9 1014
0dff88ea
AF
1015 if (i%5) {
1016 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1017 } else {
1018 gdouble ticksize = 2*CW;
1019 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1020 }
e4847ce9 1021 }
0dff88ea
AF
1022
1023 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1024 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1025 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
15614495
AF
1026 c = (CR+CW*2)*cos(baseangle);
1027 s = (CR+CW*2)*sin(baseangle);
1028 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1029 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
0dff88ea
AF
1030
1031 /* draw labels */
1032#define LABEL(x, y, w, h) { \
1033 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1034 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1035 gdk_draw_layout(d, gc, (x), (y), pl); }
1036 {
1037 gint wd, hd, xd, yd;
1038 gint wb, hb, xb, yb;
1039
1040 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
ff37db21 1041 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
0dff88ea
AF
1042 pango_layout_set_text(pl, "N", -1);
1043 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1044
1045 /* draw label with distance */
6f9336aa
RN
1046 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1047 switch (dist_units) {
1048 case VIK_UNITS_DISTANCE_KILOMETRES:
1049 if (distance >= 1000 && distance < 100000) {
1050 g_sprintf(str, "%3.2f km", distance/1000.0);
1051 } else if (distance < 1000) {
1052 g_sprintf(str, "%d m", (int)distance);
1053 } else {
1054 g_sprintf(str, "%d km", (int)distance/1000);
1055 }
1056 break;
1057 case VIK_UNITS_DISTANCE_MILES:
433b3f7f
RN
1058 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1059 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1060 } else if (distance < VIK_MILES_TO_METERS(1)) {
6f9336aa
RN
1061 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1062 } else {
433b3f7f 1063 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
6f9336aa
RN
1064 }
1065 break;
1066 default:
1067 g_critical("Houston, we've had a problem. distance=%d", dist_units);
0dff88ea 1068 }
6f9336aa 1069
0dff88ea
AF
1070 pango_layout_set_text(pl, str, -1);
1071
1072 pango_layout_get_pixel_size ( pl, &wd, &hd );
1073 if (dy>0) {
1074 xd = (x1+x2)/2 + dy;
1075 yd = (y1+y2)/2 - hd/2 - dx;
1076 } else {
1077 xd = (x1+x2)/2 - dy;
1078 yd = (y1+y2)/2 - hd/2 + dx;
1079 }
024f32c1
EB
1080
1081 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1082 xd = x2 + 10;
1083 yd = y2 - 5;
1084 }
1085
0dff88ea
AF
1086 LABEL(xd, yd, wd, hd);
1087
1088 /* draw label with bearing */
0da53bd9 1089 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
0dff88ea
AF
1090 pango_layout_set_text(pl, str, -1);
1091 pango_layout_get_pixel_size ( pl, &wb, &hb );
1092 xb = x1 + CR*cos(angle-M_PI_2);
1093 yb = y1 + CR*sin(angle-M_PI_2);
1094
024f32c1
EB
1095 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1096 xb = x2 + 10;
1097 yb = y2 + 10;
1098 }
1099
0dff88ea
AF
1100 {
1101 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1102 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1103 xb = xd + wd + 5;
1104 }
1105 }
1106 LABEL(xb, yb, wb, hb);
e4847ce9 1107 }
03d62e57 1108#undef LABEL
e4847ce9 1109
024f32c1 1110 g_object_unref ( G_OBJECT ( pl ) );
0dff88ea
AF
1111 g_object_unref ( G_OBJECT ( labgc ) );
1112 g_object_unref ( G_OBJECT ( thickgc ) );
e4847ce9
AF
1113}
1114
941aa6e9
AF
1115typedef struct {
1116 VikWindow *vw;
1117 VikViewport *vvp;
1118 gboolean has_oldcoord;
1119 VikCoord oldcoord;
1120} ruler_tool_state_t;
03d62e57 1121
941aa6e9 1122static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
03d62e57 1123{
941aa6e9
AF
1124 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1125 s->vw = vw;
1126 s->vvp = vvp;
1127 s->has_oldcoord = FALSE;
1128 return s;
03d62e57
AF
1129}
1130
941aa6e9 1131static void ruler_destroy (ruler_tool_state_t *s)
50a14534 1132{
941aa6e9
AF
1133 g_free(s);
1134}
50a14534 1135
941aa6e9
AF
1136static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1137{
1138 struct LatLon ll;
1139 VikCoord coord;
1140 gchar *temp;
1141 if ( event->button == 1 ) {
a58aaed4 1142 gchar *lat=NULL, *lon=NULL;
941aa6e9
AF
1143 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1144 vik_coord_to_latlon ( &coord, &ll );
a58aaed4 1145 a_coords_latlon_to_string ( &ll, &lat, &lon );
941aa6e9 1146 if ( s->has_oldcoord ) {
6f9336aa
RN
1147 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1148 switch (dist_units) {
1149 case VIK_UNITS_DISTANCE_KILOMETRES:
1150 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1151 break;
1152 case VIK_UNITS_DISTANCE_MILES:
433b3f7f 1153 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
6f9336aa
RN
1154 break;
1155 default:
1156 temp = g_strdup_printf ("Just to keep the compiler happy");
1157 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1158 }
1159
941aa6e9
AF
1160 s->has_oldcoord = FALSE;
1161 }
1162 else {
a58aaed4 1163 temp = g_strdup_printf ( "%s %s", lat, lon );
941aa6e9
AF
1164 s->has_oldcoord = TRUE;
1165 }
50a14534 1166
4efc10ca 1167 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
941aa6e9 1168 g_free ( temp );
e4847ce9 1169
941aa6e9
AF
1170 s->oldcoord = coord;
1171 }
1172 else {
1173 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1174 draw_update ( s->vw );
1175 }
1176 return VIK_LAYER_TOOL_ACK;
1177}
e4847ce9 1178
dc2c040e 1179static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
941aa6e9
AF
1180{
1181 VikViewport *vvp = s->vvp;
1182 VikWindow *vw = s->vw;
1183
1184 struct LatLon ll;
1185 VikCoord coord;
1186 gchar *temp;
1187
1188 if ( s->has_oldcoord ) {
1189 int oldx, oldy, w1, h1, w2, h2;
1190 static GdkPixmap *buf = NULL;
a58aaed4 1191 gchar *lat=NULL, *lon=NULL;
941aa6e9
AF
1192 w1 = vik_viewport_get_width(vvp);
1193 h1 = vik_viewport_get_height(vvp);
1194 if (!buf) {
9b082b39 1195 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
941aa6e9
AF
1196 }
1197 gdk_drawable_get_size(buf, &w2, &h2);
1198 if (w1 != w2 || h1 != h2) {
1199 g_object_unref ( G_OBJECT ( buf ) );
9b082b39 1200 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
e4847ce9 1201 }
e4847ce9 1202
941aa6e9
AF
1203 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1204 vik_coord_to_latlon ( &coord, &ll );
1205 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1206
ff37db21 1207 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
941aa6e9 1208 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
ff37db21 1209 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
1210 if (draw_buf_done) {
1211 static gpointer pass_along[3];
9b082b39 1212 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
ff37db21 1213 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
941aa6e9
AF
1214 pass_along[2] = buf;
1215 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1216 draw_buf_done = FALSE;
1217 }
a58aaed4 1218 a_coords_latlon_to_string(&ll, &lat, &lon);
6f9336aa
RN
1219 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1220 switch (dist_units) {
1221 case VIK_UNITS_DISTANCE_KILOMETRES:
1222 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1223 break;
1224 case VIK_UNITS_DISTANCE_MILES:
433b3f7f 1225 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
6f9336aa
RN
1226 break;
1227 default:
1228 temp = g_strdup_printf ("Just to keep the compiler happy");
1229 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1230 }
4efc10ca 1231 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
941aa6e9 1232 g_free ( temp );
acaf7113 1233 }
941aa6e9
AF
1234 return VIK_LAYER_TOOL_ACK;
1235}
50a14534 1236
941aa6e9
AF
1237static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1238{
1239 return VIK_LAYER_TOOL_ACK;
50a14534
EB
1240}
1241
941aa6e9 1242static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
50a14534 1243{
941aa6e9 1244 draw_update ( s->vw );
50a14534
EB
1245}
1246
0eb26799
RN
1247static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1248{
1249 if (event->keyval == GDK_Escape) {
1250 s->has_oldcoord = FALSE;
1251 ruler_deactivate ( vl, s );
1252 return TRUE;
1253 }
6b59f63d 1254 // Regardless of whether we used it, return false so other GTK things may use it
0eb26799
RN
1255 return FALSE;
1256}
1257
6b59f63d
RN
1258static VikToolInterface ruler_tool =
1259 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1260 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
941aa6e9
AF
1261 (VikToolConstructorFunc) ruler_create,
1262 (VikToolDestructorFunc) ruler_destroy,
1263 (VikToolActivationFunc) NULL,
1264 (VikToolActivationFunc) ruler_deactivate,
1265 (VikToolMouseFunc) ruler_click,
dc2c040e 1266 (VikToolMouseMoveFunc) ruler_move,
f2f2f7bf 1267 (VikToolMouseFunc) ruler_release,
0eb26799 1268 (VikToolKeyFunc) ruler_key_press,
ef5e8132 1269 FALSE,
f2f2f7bf 1270 GDK_CURSOR_IS_PIXMAP,
5bfafde9 1271 &cursor_ruler_pixbuf };
941aa6e9
AF
1272/*** end ruler code ********************************************************/
1273
1274
1275
1276/********************************************************************************
1277 ** Zoom tool code
1278 ********************************************************************************/
2eea9a36
RN
1279
1280typedef struct {
1281 VikWindow *vw;
1282 GdkPixmap *pixmap;
1283 // Track zoom bounds for zoom tool with shift modifier:
1284 gboolean bounds_active;
1285 gint start_x;
1286 gint start_y;
1287} zoom_tool_state_t;
1288
1289/*
1290 * In case the screen size has changed
1291 */
1292static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1293{
1294 int w1, h1, w2, h2;
1295
1296 // Allocate a drawing area the size of the viewport
1297 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1298 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1299
1300 if ( !zts->pixmap ) {
1301 // Totally new
9b082b39 1302 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
2eea9a36
RN
1303 }
1304
1305 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1306
1307 if ( w1 != w2 || h1 != h2 ) {
1308 // Has changed - delete and recreate with new values
1309 g_object_unref ( G_OBJECT ( zts->pixmap ) );
9b082b39 1310 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
2eea9a36
RN
1311 }
1312}
1313
941aa6e9 1314static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
50a14534 1315{
2eea9a36
RN
1316 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1317 zts->vw = vw;
1318 zts->pixmap = NULL;
1319 zts->start_x = 0;
1320 zts->start_y = 0;
1321 zts->bounds_active = FALSE;
1322 return zts;
50a14534
EB
1323}
1324
2eea9a36 1325static void zoomtool_destroy ( zoom_tool_state_t *zts)
50a14534 1326{
2eea9a36
RN
1327 if ( zts->pixmap )
1328 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1329 g_free(zts);
1330}
1331
1332static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1333{
1334 zts->vw->modified = TRUE;
15ff5402
RN
1335 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1336
1337 VikCoord coord;
1338 gint x, y;
2eea9a36
RN
1339 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1340 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1341
1342 gboolean skip_update = FALSE;
1343
1344 zts->bounds_active = FALSE;
15ff5402
RN
1345
1346 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1347 // This zoom is on the center position
2eea9a36 1348 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
15ff5402 1349 if ( event->button == 1 )
2eea9a36 1350 vik_viewport_zoom_in (zts->vw->viking_vvp);
15ff5402 1351 else if ( event->button == 3 )
2eea9a36 1352 vik_viewport_zoom_out (zts->vw->viking_vvp);
15ff5402 1353 }
130ac805
RN
1354 else if ( modifiers == GDK_CONTROL_MASK ) {
1355 // This zoom is to recenter on the mouse position
2eea9a36 1356 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
130ac805 1357 if ( event->button == 1 )
2eea9a36 1358 vik_viewport_zoom_in (zts->vw->viking_vvp);
130ac805 1359 else if ( event->button == 3 )
2eea9a36
RN
1360 vik_viewport_zoom_out (zts->vw->viking_vvp);
1361 }
1362 else if ( modifiers == GDK_SHIFT_MASK ) {
1363 // Get start of new zoom bounds
1364 if ( event->button == 1 ) {
1365 zts->bounds_active = TRUE;
1366 zts->start_x = (gint) event->x;
1367 zts->start_y = (gint) event->y;
1368 skip_update = TRUE;
1369 }
130ac805 1370 }
15ff5402
RN
1371 else {
1372 /* make sure mouse is still over the same point on the map when we zoom */
2eea9a36 1373 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
15ff5402 1374 if ( event->button == 1 )
2eea9a36 1375 vik_viewport_zoom_in (zts->vw->viking_vvp);
15ff5402 1376 else if ( event->button == 3 )
2eea9a36
RN
1377 vik_viewport_zoom_out(zts->vw->viking_vvp);
1378 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1379 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
15ff5402
RN
1380 center_x + (x - event->x),
1381 center_y + (y - event->y) );
1382 }
2eea9a36
RN
1383
1384 if ( !skip_update )
1385 draw_update ( zts->vw );
1386
941aa6e9
AF
1387 return VIK_LAYER_TOOL_ACK;
1388}
50a14534 1389
2eea9a36 1390static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
941aa6e9 1391{
2eea9a36
RN
1392 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1393
1394 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1395 zoomtool_resize_pixmap ( zts );
1396
1397 // Blank out currently drawn area
1398 gdk_draw_drawable ( zts->pixmap,
ff37db21 1399 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
2eea9a36
RN
1400 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1401 0, 0, 0, 0, -1, -1);
1402
1403 // Calculate new box starting point & size in pixels
1404 int xx, yy, width, height;
1405 if ( event->y > zts->start_y ) {
1406 yy = zts->start_y;
1407 height = event->y-zts->start_y;
1408 }
1409 else {
1410 yy = event->y;
1411 height = zts->start_y-event->y;
1412 }
1413 if ( event->x > zts->start_x ) {
1414 xx = zts->start_x;
1415 width = event->x-zts->start_x;
1416 }
1417 else {
1418 xx = event->x;
1419 width = zts->start_x-event->x;
1420 }
1421
1422 // Draw the box
ff37db21 1423 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
2eea9a36
RN
1424
1425 // Only actually draw when there's time to do so
1426 if (draw_buf_done) {
1427 static gpointer pass_along[3];
9b082b39 1428 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
ff37db21 1429 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
2eea9a36
RN
1430 pass_along[2] = zts->pixmap;
1431 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1432 draw_buf_done = FALSE;
1433 }
1434 }
941aa6e9
AF
1435 return VIK_LAYER_TOOL_ACK;
1436}
50a14534 1437
2eea9a36 1438static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
941aa6e9 1439{
2eea9a36
RN
1440 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1441
1442 zts->bounds_active = FALSE;
1443
1444 // Ensure haven't just released on the exact same position
1445 // i.e. probably haven't moved the mouse at all
1446 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1447
1448 VikCoord coord1, coord2;
1449 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1450 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1451
1452 // From the extend of the bounds pick the best zoom level
1453 // c.f. trw_layer_zoom_to_show_latlons()
1454 // Maybe refactor...
1455 struct LatLon ll1, ll2;
1456 vik_coord_to_latlon(&coord1, &ll1);
1457 vik_coord_to_latlon(&coord2, &ll2);
1458 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1459 (ll1.lon+ll2.lon)/2 };
1460
1461 VikCoord new_center;
1462 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1463 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1464
1465 /* Convert into definite 'smallest' and 'largest' positions */
1466 struct LatLon minmin;
1467 if ( ll1.lat < ll2.lat )
1468 minmin.lat = ll1.lat;
1469 else
1470 minmin.lat = ll2.lat;
1471
1472 struct LatLon maxmax;
1473 if ( ll1.lon > ll2.lon )
1474 maxmax.lon = ll1.lon;
1475 else
1476 maxmax.lon = ll2.lon;
1477
1478 /* Always recalculate the 'best' zoom level */
1479 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1480 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1481
1482 gdouble min_lat, max_lat, min_lon, max_lon;
1483 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1484 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1485 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1486 /* NB I think the logic used in this test to determine if the bounds is within view
1487 fails if track goes across 180 degrees longitude.
1488 Hopefully that situation is not too common...
1489 Mind you viking doesn't really do edge locations to well anyway */
1490 if ( min_lat < minmin.lat &&
1491 max_lat > minmin.lat &&
1492 min_lon < maxmax.lon &&
1493 max_lon > maxmax.lon )
1494 /* Found within zoom level */
1495 break;
1496
1497 /* Try next */
1498 zoom = zoom * 2;
1499 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1500 }
1501
1502 draw_update ( zts->vw );
1503 }
941aa6e9 1504 return VIK_LAYER_TOOL_ACK;
50a14534
EB
1505}
1506
941aa6e9 1507static VikToolInterface zoom_tool =
79dce0cb 1508 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
941aa6e9 1509 (VikToolConstructorFunc) zoomtool_create,
2eea9a36 1510 (VikToolDestructorFunc) zoomtool_destroy,
941aa6e9
AF
1511 (VikToolActivationFunc) NULL,
1512 (VikToolActivationFunc) NULL,
1513 (VikToolMouseFunc) zoomtool_click,
dc2c040e 1514 (VikToolMouseMoveFunc) zoomtool_move,
f2f2f7bf
GB
1515 (VikToolMouseFunc) zoomtool_release,
1516 NULL,
ef5e8132 1517 FALSE,
f2f2f7bf 1518 GDK_CURSOR_IS_PIXMAP,
5bfafde9 1519 &cursor_zoom_pixbuf };
463f9d07 1520/*** end zoom code ********************************************************/
941aa6e9 1521
576cbd17
GB
1522/********************************************************************************
1523 ** Pan tool code
1524 ********************************************************************************/
1525static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1526{
1527 return vw;
1528}
1529
1530static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1531{
1532 vw->modified = TRUE;
1533 if ( event->button == 1 )
1534 vik_window_pan_click ( vw, event );
1535 draw_update ( vw );
1536 return VIK_LAYER_TOOL_ACK;
1537}
1538
dc2c040e 1539static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
576cbd17
GB
1540{
1541 vik_window_pan_move ( vw, event );
1542 return VIK_LAYER_TOOL_ACK;
1543}
1544
1545static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1546{
1547 if ( event->button == 1 )
1548 vik_window_pan_release ( vw, event );
1549 return VIK_LAYER_TOOL_ACK;
1550}
1551
1552static VikToolInterface pan_tool =
79dce0cb 1553 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
576cbd17
GB
1554 (VikToolConstructorFunc) pantool_create,
1555 (VikToolDestructorFunc) NULL,
1556 (VikToolActivationFunc) NULL,
1557 (VikToolActivationFunc) NULL,
1558 (VikToolMouseFunc) pantool_click,
dc2c040e 1559 (VikToolMouseMoveFunc) pantool_move,
f2f2f7bf
GB
1560 (VikToolMouseFunc) pantool_release,
1561 NULL,
ef5e8132 1562 FALSE,
f2f2f7bf 1563 GDK_FLEUR };
576cbd17
GB
1564/*** end pan code ********************************************************/
1565
a47bfefa
RN
1566/********************************************************************************
1567 ** Select tool code
1568 ********************************************************************************/
1569static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1570{
08f14055
RN
1571 tool_ed_t *t = g_new(tool_ed_t, 1);
1572 t->vw = vw;
1573 t->vvp = vvp;
1574 t->vtl = NULL;
1575 t->is_waypoint = FALSE;
1576 return t;
1577}
1578
1579static void selecttool_destroy (tool_ed_t *t)
1580{
1581 g_free(t);
a47bfefa
RN
1582}
1583
1584typedef struct {
1585 gboolean cont;
1586 VikViewport *vvp;
1587 GdkEventButton *event;
08f14055 1588 tool_ed_t *tool_edit;
a47bfefa
RN
1589} clicker;
1590
1591static void click_layer_selected (VikLayer *vl, clicker *ck)
1592{
1593 /* Do nothing when function call returns true; */
1594 /* i.e. stop on first found item */
1595 if ( ck->cont )
1596 if ( vl->visible )
08f14055
RN
1597 if ( vik_layer_get_interface(vl->type)->select_click )
1598 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
a47bfefa
RN
1599}
1600
08f14055 1601static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
a47bfefa
RN
1602{
1603 /* Only allow selection on primary button */
1604 if ( event->button == 1 ) {
1605 /* Enable click to apply callback to potentially all track/waypoint layers */
1606 /* Useful as we can find things that aren't necessarily in the currently selected layer */
aa7ed888 1607 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
1608 clicker ck;
1609 ck.cont = TRUE;
08f14055 1610 ck.vvp = t->vw->viking_vvp;
a47bfefa 1611 ck.event = event;
08f14055 1612 ck.tool_edit = t;
a47bfefa
RN
1613 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1614 g_list_free ( gl );
1615
1616 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1617 if ( ck.cont ) {
1618 GtkTreeIter iter;
08f14055 1619 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
a47bfefa
RN
1620
1621 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1622 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1623 gint type = vik_treeview_item_get_type ( vtv, &iter );
1624 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1625 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1626
1627 vik_treeview_item_unselect ( vtv, &iter );
08f14055
RN
1628 if ( vik_window_clear_highlight ( t->vw ) )
1629 draw_update ( t->vw );
a47bfefa
RN
1630 }
1631 }
1632 }
1633 }
1634 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1635 if ( vl->visible )
1636 /* Act on currently selected item to show menu */
60a69560 1637 if ( t->vw->selected_track || t->vw->selected_waypoint )
a47bfefa 1638 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
08f14055 1639 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
a47bfefa
RN
1640 }
1641
1642 return VIK_LAYER_TOOL_ACK;
1643}
1644
08f14055
RN
1645static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1646{
1647 /* Only allow selection on primary button */
1648 if ( event->button == 1 ) {
1649 // Don't care about vl here
1650 if ( t->vtl )
1651 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1652 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1653 }
1654 return VIK_LAYER_TOOL_ACK;
1655}
1656
1657static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1658{
1659 /* Only allow selection on primary button */
1660 if ( event->button == 1 ) {
1661 // Don't care about vl here
1662 if ( t->vtl )
1663 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1664 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1665 }
1666 return VIK_LAYER_TOOL_ACK;
1667}
1668
a47bfefa 1669static VikToolInterface select_tool =
79dce0cb 1670 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
a47bfefa 1671 (VikToolConstructorFunc) selecttool_create,
08f14055 1672 (VikToolDestructorFunc) selecttool_destroy,
a47bfefa
RN
1673 (VikToolActivationFunc) NULL,
1674 (VikToolActivationFunc) NULL,
1675 (VikToolMouseFunc) selecttool_click,
08f14055
RN
1676 (VikToolMouseMoveFunc) selecttool_move,
1677 (VikToolMouseFunc) selecttool_release,
a47bfefa 1678 (VikToolKeyFunc) NULL,
ef5e8132 1679 FALSE,
a47bfefa
RN
1680 GDK_LEFT_PTR,
1681 NULL,
1682 NULL };
1683/*** end select tool code ********************************************************/
1684
8c721f83
EB
1685static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1686{
ee4c7107
RN
1687 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
1688 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
1689 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
1690 if ( sel && vik_treeview_get_editing ( sel->vt ) )
1691 return;
1692
8c721f83
EB
1693 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1694 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1695 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1696 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1697 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1698 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1699 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1700 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1701 }
1702 draw_update ( vw );
1703}
941aa6e9 1704
7de42638
EB
1705static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1706{
1707 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1708 g_assert(check_box);
1709 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1710 if ( state )
1711 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1712 else
1713 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1714}
941aa6e9 1715
e4afc73a 1716static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
50a14534 1717{
e4afc73a
EB
1718 guint what = 128;
1719
1720 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1721 what = -3;
1722 }
1723 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1724 what = -4;
1725 }
1726 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1727 what = -2;
1728 }
1729 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1730 what = -1;
1731 }
1732 else {
1733 gchar *s = (gchar *)gtk_action_get_name(a);
1734 what = atoi(s+4);
1735 }
1736
50a14534
EB
1737 switch (what)
1738 {
1739 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1740 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1741 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1742 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1743 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1744 }
1745 draw_update ( vw );
1746}
1747
61a9fb73 1748static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
1749{
1750 VikCoord new_center;
e4afc73a
EB
1751
1752 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
50a14534
EB
1753 struct LatLon ll, llold;
1754 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1755 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1756 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1757 else
1758 return;
1759 }
e4afc73a 1760 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
50a14534
EB
1761 struct UTM utm, utmold;
1762 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1763 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1764 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1765 else
1766 return;
1767 }
e4afc73a 1768 else {
8dc8da82 1769 g_critical("Houston, we've had a problem.");
e4afc73a
EB
1770 return;
1771 }
50a14534
EB
1772
1773 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1774 draw_update ( vw );
1775}
1776
6b59f63d
RN
1777/**
1778 * Refresh maps displayed
1779 */
1780static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1781{
1782 // Only get 'new' maps
1783 simple_map_update ( vw, TRUE );
1784}
1785
e4afc73a 1786static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
50a14534 1787{
b5926b35 1788 VikLayerTypeEnum type;
e4afc73a
EB
1789 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1790 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1791 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
b5926b35
RN
1792 draw_update ( vw );
1793 vw->modified = TRUE;
e4afc73a
EB
1794 }
1795 }
50a14534
EB
1796 }
1797}
1798
e4afc73a 1799static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
50a14534 1800{
2cebc318 1801 a_clipboard_copy_selected ( vw->viking_vlp );
50a14534
EB
1802}
1803
e4afc73a 1804static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
50a14534 1805{
169acf64 1806 vik_layers_panel_cut_selected ( vw->viking_vlp );
169acf64 1807 vw->modified = TRUE;
50a14534
EB
1808}
1809
e4afc73a 1810static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
50a14534 1811{
ee4c7107 1812 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
50a14534 1813 {
50a14534
EB
1814 vw->modified = TRUE;
1815 }
1816}
1817
e4afc73a 1818static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
1819{
1820 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
4c77d5e0 1821 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
50a14534
EB
1822}
1823
5ff75d1e
GB
1824static void help_help_cb ( GtkAction *a, VikWindow *vw )
1825{
6ace3182
MA
1826#ifdef WINDOWS
1827 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1828#else /* WINDOWS */
5ff75d1e
GB
1829 gchar *uri;
1830 uri = g_strdup_printf("ghelp:%s", PACKAGE);
9080b461
RN
1831 GError *error = NULL;
1832 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1833 if ( !show && !error )
1834 // No error to show, so unlikely this will get called
1835 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1836 else if ( error ) {
1837 // Main error path
1838 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 );
1839 g_error_free ( error );
1840 }
5ff75d1e 1841 g_free(uri);
6ace3182 1842#endif /* WINDOWS */
5ff75d1e
GB
1843}
1844
d0a5f320
AF
1845static void help_about_cb ( GtkAction *a, VikWindow *vw )
1846{
1847 a_dialog_about(GTK_WINDOW(vw));
1848}
1849
e4afc73a 1850static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
1851{
1852 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1853 {
1854 vik_layers_panel_delete_selected ( vw->viking_vlp );
1855 vw->modified = TRUE;
1856 }
1857 else
4c77d5e0 1858 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
50a14534
EB
1859}
1860
181f5d0c
MA
1861static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1862{
48df6aa3 1863 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
181f5d0c
MA
1864 g_assert(check_box);
1865 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1866 if ( state )
1867 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1868 else
1869 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1870}
1871
a459ee10
RN
1872static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1873{
48df6aa3 1874 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
a459ee10
RN
1875 if ( !check_box )
1876 return;
1877 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1878 if ( state )
1879 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1880 else
1881 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1882}
1883
e7591765
RN
1884static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1885{
48df6aa3 1886 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
e7591765
RN
1887 if ( !check_box )
1888 return;
1889 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1890 if ( state )
1891 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1892 else
1893 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1894}
1895
7622022f
RN
1896static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1897{
48df6aa3 1898 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
7622022f
RN
1899 if ( !check_box )
1900 return;
1901 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1902 if ( !state )
1903 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1904 else
1905 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1906}
1907
941aa6e9
AF
1908/***************************************
1909 ** tool management routines
1910 **
1911 ***************************************/
1912
1913static toolbox_tools_t* toolbox_create(VikWindow *vw)
1914{
1915 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1916 vt->tools = NULL;
1917 vt->n_tools = 0;
1918 vt->active_tool = -1;
1919 vt->vw = vw;
941aa6e9
AF
1920 return vt;
1921}
1922
9593a4c9 1923static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
941aa6e9
AF
1924{
1925 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1926 vt->tools[vt->n_tools].ti = *vti;
9593a4c9 1927 vt->tools[vt->n_tools].layer_type = layer_type;
941aa6e9
AF
1928 if (vti->create) {
1929 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1930 }
1931 else {
1932 vt->tools[vt->n_tools].state = NULL;
1933 }
1934 vt->n_tools++;
1935}
1936
1937static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1938{
1939 int i;
1940 for (i=0; i<vt->n_tools; i++) {
79dce0cb 1941 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
941aa6e9
AF
1942 break;
1943 }
1944 }
1945 return i;
1946}
1947
1948static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1949{
1950 int tool = toolbox_get_tool(vt, tool_name);
1951 toolbox_tool_t *t = &vt->tools[tool];
1952 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1953
1954 if (tool == vt->n_tools) {
7742da66 1955 g_critical("trying to activate a non-existent tool...");
d84ade77 1956 return;
941aa6e9
AF
1957 }
1958 /* is the tool already active? */
1959 if (vt->active_tool == tool) {
1960 return;
1961 }
1962
1963 if (vt->active_tool != -1) {
1964 if (vt->tools[vt->active_tool].ti.deactivate) {
1965 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1966 }
1967 }
1968 if (t->ti.activate) {
1969 t->ti.activate(vl, t->state);
1970 }
1971 vt->active_tool = tool;
1972}
1973
f2f2f7bf
GB
1974static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1975{
1976 int tool = toolbox_get_tool(vt, tool_name);
1977 toolbox_tool_t *t = &vt->tools[tool];
1978 if (t->ti.cursor == NULL) {
1979 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1980 GError *cursor_load_err = NULL;
1981 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1982 /* TODO: settable offeset */
1983 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
1984 g_object_unref ( G_OBJECT(cursor_pixbuf) );
1985 } else {
1986 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
1987 }
1988 }
1989 return t->ti.cursor;
1990}
1991
941aa6e9
AF
1992static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
1993{
1994 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1995 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
9593a4c9
EB
1996 gint ltype = vt->tools[vt->active_tool].layer_type;
1997 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
1998 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
941aa6e9
AF
1999 }
2000}
2001
dc2c040e 2002static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
941aa6e9
AF
2003{
2004 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2005 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
9593a4c9
EB
2006 gint ltype = vt->tools[vt->active_tool].layer_type;
2007 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
165d30aa
EB
2008 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2009 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
941aa6e9
AF
2010 }
2011}
2012
2013static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2014{
2015 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
9593a4c9
EB
2016 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2017 gint ltype = vt->tools[vt->active_tool].layer_type;
2018 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2019 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
941aa6e9
AF
2020 }
2021}
2022/** End tool management ************************************/
2023
8fb71d6c
EB
2024void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2025{
79dce0cb 2026 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
8fb71d6c 2027}
941aa6e9
AF
2028
2029/* this function gets called whenever a toolbar tool is clicked */
e4afc73a 2030static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
50a14534
EB
2031{
2032 /* White Magic, my friends ... White Magic... */
b5926b35 2033 gint tool_id;
941aa6e9
AF
2034 toolbox_activate(vw->vt, gtk_action_get_name(a));
2035
55d3a53c 2036 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
33bc7c1b 2037
9b082b39 2038 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
33bc7c1b 2039 /* We set cursor, even if it is NULL: it resets to default */
9b082b39 2040 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
f2f2f7bf 2041
576cbd17
GB
2042 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2043 vw->current_tool = TOOL_PAN;
576cbd17
GB
2044 }
2045 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
e4afc73a
EB
2046 vw->current_tool = TOOL_ZOOM;
2047 }
2048 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2049 vw->current_tool = TOOL_RULER;
2050 }
a47bfefa
RN
2051 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2052 vw->current_tool = TOOL_SELECT;
2053 }
e4afc73a 2054 else {
79845167 2055 /* TODO: only enable tools from active layer */
b5926b35 2056 VikLayerTypeEnum layer_id;
8fb71d6c
EB
2057 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2058 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
79dce0cb 2059 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
8fb71d6c
EB
2060 vw->current_tool = TOOL_LAYER;
2061 vw->tool_layer_id = layer_id;
2062 vw->tool_tool_id = tool_id;
e4afc73a
EB
2063 }
2064 }
2065 }
2066 }
ac4478f4 2067 draw_status_tool ( vw );
50a14534
EB
2068}
2069
2070static void window_set_filename ( VikWindow *vw, const gchar *filename )
2071{
2072 gchar *title;
4c77d5e0 2073 const gchar *file;
50a14534
EB
2074 if ( vw->filename )
2075 g_free ( vw->filename );
2076 if ( filename == NULL )
2077 {
2078 vw->filename = NULL;
50a14534
EB
2079 }
2080 else
2081 {
2082 vw->filename = g_strdup(filename);
50a14534 2083 }
4522c4ff
GB
2084
2085 /* Refresh window's title */
2086 file = window_get_filename ( vw );
4c77d5e0
GB
2087 title = g_strdup_printf( "%s - Viking", file );
2088 gtk_window_set_title ( GTK_WINDOW(vw), title );
2089 g_free ( title );
50a14534
EB
2090}
2091
4522c4ff
GB
2092static const gchar *window_get_filename ( VikWindow *vw )
2093{
2094 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2095}
2096
7bc965c0
GB
2097GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2098{
2099 GtkWidget *mode_button;
2100 gchar *buttonname;
2101 switch ( mode ) {
794c8f18 2102#ifdef VIK_CONFIG_EXPEDIA
7bc965c0 2103 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
794c8f18 2104#endif
7bc965c0 2105 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
d587678a 2106 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
794c8f18 2107 default: buttonname = "/ui/MainMenu/View/ModeUTM";
7bc965c0
GB
2108 }
2109 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2110 g_assert ( mode_button );
2111 return mode_button;
2112}
2113
01da6b4d
GB
2114/**
2115 * vik_window_get_pan_move:
2116 * @vw: some VikWindow
2117 *
2118 * Retrieves @vw's pan_move.
2119 *
2120 * Should be removed as soon as possible.
2121 *
2122 * Returns: @vw's pan_move
2123 *
2124 * Since: 0.9.96
2125 **/
1c6a6010
GB
2126gboolean vik_window_get_pan_move ( VikWindow *vw )
2127{
2128 return vw->pan_move;
2129}
2130
13505702
GB
2131static void on_activate_recent_item (GtkRecentChooser *chooser,
2132 VikWindow *self)
2133{
2134 gchar *filename;
2135
2136 filename = gtk_recent_chooser_get_current_uri (chooser);
2137 if (filename != NULL)
2138 {
2139 GFile *file = g_file_new_for_uri ( filename );
2140 gchar *path = g_file_get_path ( file );
2141 g_object_unref ( file );
2142 if ( self->filename )
2143 {
d8ff1421
RN
2144 GSList *filenames = NULL;
2145 filenames = g_slist_append ( filenames, path );
13505702 2146 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
d8ff1421 2147 // NB: GSList & contents are freed by main.open_window
13505702 2148 }
d8ff1421 2149 else {
756d53f5 2150 vik_window_open_file ( self, path, TRUE );
d8ff1421
RN
2151 g_free ( path );
2152 }
13505702
GB
2153 }
2154
2155 g_free (filename);
2156}
2157
2158static void setup_recent_files (VikWindow *self)
2159{
2160 GtkRecentManager *manager;
2161 GtkRecentFilter *filter;
2162 GtkWidget *menu, *menu_item;
2163
2164 filter = gtk_recent_filter_new ();
2165 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2166 gtk_recent_filter_add_group(filter, "viking");
2167
2168 manager = gtk_recent_manager_get_default ();
2169 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2170 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2171 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2172
2173 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2174 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2175
2176 g_signal_connect (G_OBJECT (menu), "item-activated",
2177 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2178}
2179
2180static void update_recently_used_document(const gchar *filename)
2181{
2182 /* Update Recently Used Document framework */
2183 GtkRecentManager *manager = gtk_recent_manager_get_default();
2184 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2185 gchar *groups[] = {"viking", NULL};
2186 GFile *file = g_file_new_for_commandline_arg(filename);
2187 gchar *uri = g_file_get_uri(file);
2188 gchar *basename = g_path_get_basename(filename);
2189 g_object_unref(file);
2190 file = NULL;
2191
2192 recent_data->display_name = basename;
2193 recent_data->description = NULL;
2194 recent_data->mime_type = "text/x-gps-data";
2195 recent_data->app_name = (gchar *) g_get_application_name ();
2196 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2197 recent_data->groups = groups;
2198 recent_data->is_private = FALSE;
2199 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2200 {
2201 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2202 }
2203
2204 g_free (uri);
2205 g_free (basename);
2206 g_free (recent_data->app_exec);
2207 g_slice_free (GtkRecentData, recent_data);
2208}
2209
55d3a53c
RN
2210/**
2211 * Call this before doing things that may take a long time and otherwise not show any other feedback
2212 * such as loading and saving files
2213 */
2214void vik_window_set_busy_cursor ( VikWindow *vw )
2215{
2216 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2217 // Viewport has a separate cursor
2218 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->busy_cursor );
2219 // Ensure cursor updated before doing stuff
2220 while( gtk_events_pending() )
2221 gtk_main_iteration();
2222}
2223
2224void vik_window_clear_busy_cursor ( VikWindow *vw )
2225{
2226 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2227 // Restore viewport cursor
2228 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->viewport_cursor );
2229}
2230
50a14534
EB
2231void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2232{
55d3a53c
RN
2233 vik_window_set_busy_cursor ( vw );
2234
50a14534
EB
2235 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2236 {
ba9d0a00 2237 case LOAD_TYPE_READ_FAILURE:
4c77d5e0 2238 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
50a14534 2239 break;
ba9d0a00
RN
2240 case LOAD_TYPE_GPSBABEL_FAILURE:
2241 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2242 break;
54b84792
RN
2243 case LOAD_TYPE_GPX_FAILURE:
2244 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2245 break;
cb5ec7a8
RN
2246 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2247 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2248 break;
327a2533
RN
2249 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2250 {
2251 // Since we can process .vik files with issues just show a warning in the status bar
2252 // Not that a user can do much about it... or tells them what this issue is yet...
2253 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2254 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2255 g_free ( msg );
2256 }
2257 // No break, carry on to show any data
ba9d0a00 2258 case LOAD_TYPE_VIK_SUCCESS:
50a14534
EB
2259 {
2260 GtkWidget *mode_button;
13505702 2261 /* Update UI */
50a14534
EB
2262 if ( change_filename )
2263 window_set_filename ( vw, filename );
7bc965c0 2264 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
50a14534
EB
2265 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. */
2266 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2267 vw->only_updating_coord_mode_ui = FALSE;
2268
2269 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2afcef36 2270
48df6aa3 2271 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
1657065a
QT
2272 g_assert ( mode_button );
2273 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2274
48df6aa3 2275 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
1657065a
QT
2276 g_assert ( mode_button );
2277 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2afcef36
RN
2278
2279 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2280 g_assert ( mode_button );
2281 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
50a14534 2282 }
ba9d0a00 2283 //case LOAD_TYPE_OTHER_SUCCESS:
13505702
GB
2284 default:
2285 update_recently_used_document(filename);
2286 draw_update ( vw );
ba9d0a00 2287 break;
50a14534 2288 }
55d3a53c
RN
2289
2290 vik_window_clear_busy_cursor ( vw );
50a14534 2291}
55d3a53c 2292
e4afc73a 2293static void load_file ( GtkAction *a, VikWindow *vw )
50a14534 2294{
6e4a49aa
MA
2295 GSList *files = NULL;
2296 GSList *cur_file = NULL;
e4afc73a
EB
2297 gboolean newwindow;
2298 if (!strcmp(gtk_action_get_name(a), "Open")) {
2299 newwindow = TRUE;
2300 }
2301 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2302 newwindow = FALSE;
2303 }
2304 else {
8dc8da82 2305 g_critical("Houston, we've had a problem.");
e4afc73a
EB
2306 return;
2307 }
2308
50a14534
EB
2309 if ( ! vw->open_dia )
2310 {
6e4a49aa 2311 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
31729835
RN
2312 GTK_WINDOW(vw),
2313 GTK_FILE_CHOOSER_ACTION_OPEN,
2314 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2315 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2316 NULL);
2317 gchar *cwd = g_get_current_dir();
2318 if ( cwd ) {
2319 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2320 g_free ( cwd );
2321 }
2322
269fe4da
RN
2323 GtkFileFilter *filter;
2324 // NB file filters are listed this way for alphabetical ordering
2325#ifdef VIK_CONFIG_GEOCACHES
2326 filter = gtk_file_filter_new ();
2327 gtk_file_filter_set_name( filter, _("Geocaching") );
2328 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2329 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2330#endif
2331
2332 filter = gtk_file_filter_new ();
2333 gtk_file_filter_set_name( filter, _("Google Earth") );
2334 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2335 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2336
2337 filter = gtk_file_filter_new ();
2338 gtk_file_filter_set_name( filter, _("GPX") );
2339 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2340 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2341
2342 filter = gtk_file_filter_new ();
2343 gtk_file_filter_set_name( filter, _("Viking") );
2344 gtk_file_filter_add_pattern ( filter, "*.vik" );
2345 gtk_file_filter_add_pattern ( filter, "*.viking" );
2346 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2347
2348 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2349 // However assume this are barely used and thus not worthy of inclusion
2350 // as they'll just make the options too many and have no clear file pattern
2351 // one can always use the all option
2352 filter = gtk_file_filter_new ();
2353 gtk_file_filter_set_name( filter, _("All") );
2354 gtk_file_filter_add_pattern ( filter, "*" );
2355 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2356 // Default to any file - same as before open filters were added
2357 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2358
6e4a49aa 2359 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
50a14534
EB
2360 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2361 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2362 }
6e4a49aa 2363 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
50a14534
EB
2364 {
2365 gtk_widget_hide ( vw->open_dia );
a5fd2196 2366#ifdef VIKING_PROMPT_IF_MODIFIED
50a14534 2367 if ( (vw->modified || vw->filename) && newwindow )
a5fd2196
QT
2368#else
2369 if ( vw->filename && newwindow )
2370#endif
6e4a49aa 2371 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
50a14534 2372 else {
6e4a49aa
MA
2373 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2374 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
d4a8b54d 2375 gboolean first_vik_file = TRUE;
6e4a49aa
MA
2376 cur_file = files;
2377 while ( cur_file ) {
d4a8b54d 2378
6e4a49aa 2379 gchar *file_name = cur_file->data;
d4a8b54d
RN
2380 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2381 // Load first of many .vik files in current window
2382 if ( first_vik_file ) {
2383 vik_window_open_file ( vw, file_name, TRUE );
2384 first_vik_file = FALSE;
2385 }
2386 else {
2387 // Load each subsequent .vik file in a separate window
2388 VikWindow *newvw = vik_window_new_window ();
2389 if (newvw)
2390 vik_window_open_file ( newvw, file_name, TRUE );
2391 }
2392 }
2393 else
2394 // Other file types
2395 vik_window_open_file ( vw, file_name, change_fn );
2396
6e4a49aa
MA
2397 g_free (file_name);
2398 cur_file = g_slist_next (cur_file);
50a14534 2399 }
6e4a49aa 2400 g_slist_free (files);
50a14534
EB
2401 }
2402 }
2403 else
2404 gtk_widget_hide ( vw->open_dia );
2405}
2406
e4afc73a 2407static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
50a14534
EB
2408{
2409 gboolean rv = FALSE;
2410 const gchar *fn;
2411 if ( ! vw->save_dia )
2412 {
6e4a49aa 2413 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
31729835
RN
2414 GTK_WINDOW(vw),
2415 GTK_FILE_CHOOSER_ACTION_SAVE,
2416 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2417 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2418 NULL);
2419 gchar *cwd = g_get_current_dir();
2420 if ( cwd ) {
2421 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2422 g_free ( cwd );
2423 }
2424
471d418c
RN
2425 GtkFileFilter *filter;
2426 filter = gtk_file_filter_new ();
2427 gtk_file_filter_set_name( filter, _("All") );
2428 gtk_file_filter_add_pattern ( filter, "*" );
2429 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2430
2431 filter = gtk_file_filter_new ();
2432 gtk_file_filter_set_name( filter, _("Viking") );
2433 gtk_file_filter_add_pattern ( filter, "*.vik" );
2434 gtk_file_filter_add_pattern ( filter, "*.viking" );
2435 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2436 // Default to a Viking file
2437 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2438
50a14534
EB
2439 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2440 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2441 }
c38eb451 2442 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
8f75f616 2443 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
c38eb451
RN
2444 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2445 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2446
2447 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
50a14534 2448
6e4a49aa 2449 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
50a14534 2450 {
6e4a49aa 2451 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
d91e5f2b 2452 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
2453 {
2454 window_set_filename ( vw, fn );
2455 rv = window_save ( vw );
2456 vw->modified = FALSE;
2457 break;
2458 }
2459 }
c38eb451 2460 g_free ( auto_save_name );
50a14534
EB
2461 gtk_widget_hide ( vw->save_dia );
2462 return rv;
2463}
2464
2465static gboolean window_save ( VikWindow *vw )
2466{
55d3a53c
RN
2467 vik_window_set_busy_cursor ( vw );
2468 gboolean success = TRUE;
2469
50a14534 2470 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
13505702
GB
2471 {
2472 update_recently_used_document ( vw->filename );
13505702 2473 }
50a14534
EB
2474 else
2475 {
4c77d5e0 2476 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
55d3a53c 2477 success = FALSE;
50a14534 2478 }
55d3a53c
RN
2479 vik_window_clear_busy_cursor ( vw );
2480 return success;
50a14534
EB
2481}
2482
e4afc73a 2483static gboolean save_file ( GtkAction *a, VikWindow *vw )
50a14534
EB
2484{
2485 if ( ! vw->filename )
e4afc73a 2486 return save_file_as ( NULL, vw );
50a14534
EB
2487 else
2488 {
2489 vw->modified = FALSE;
2490 return window_save ( vw );
2491 }
2492}
2493
0bb36e78
RN
2494/**
2495 * export_to:
2496 *
2497 * Export all TRW Layers in the list to individual files in the specified directory
2498 *
2499 * Returns: %TRUE on success
2500 */
2501static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2502{
2503 gboolean success = TRUE;
2504
2505 gint export_count = 0;
2506
2507 vik_window_set_busy_cursor ( vw );
2508
2509 while ( gl ) {
2510
2511 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
2512
2513 // Some protection in attempting to write too many same named files
2514 // As this will get horribly slow...
2515 gboolean safe = FALSE;
2516 gint ii = 2;
2517 while ( ii < 5000 ) {
2518 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
2519 // Try rename
2520 g_free ( fn );
2521 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
2522 }
2523 else {
2524 safe = TRUE;
2525 break;
2526 }
2527 ii++;
2528 }
2529 if ( ii == 5000 )
2530 success = FALSE;
2531
2532 // NB: We allow exporting empty layers
2533 if ( safe ) {
2534 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
2535
2536 // Show some progress
2537 if ( this_success ) {
2538 export_count++;
2539 gchar *message = g_strconcat ( _("Exporting to file: "), fn, NULL );
2540 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2541 while ( gtk_events_pending() )
2542 gtk_main_iteration ();
2543 g_free ( message );
2544 }
2545
2546 success = success && this_success;
2547 }
2548
2549 g_free ( fn );
2550 gl = g_list_next ( gl );
2551 }
2552
2553 vik_window_clear_busy_cursor ( vw );
2554
2555 // Confirm what happened.
2556 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
2557 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2558 g_free ( message );
2559
2560 return success;
2561}
2562
2563static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
2564{
2565 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2566
2567 if ( !gl ) {
2568 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
2569 return;
2570 }
2571
2572 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Export to directory"),
2573 GTK_WINDOW(vw),
2574 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2575 GTK_STOCK_CANCEL,
2576 GTK_RESPONSE_REJECT,
2577 GTK_STOCK_OK,
2578 GTK_RESPONSE_ACCEPT,
2579 NULL );
2580
2581 GtkWidget *gw = gtk_file_chooser_widget_new ( GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
2582 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), gw, TRUE, TRUE, 0 );
2583
2584 // try to make it a nice size - otherwise seems to default to something impractically small
2585 gtk_window_set_default_size ( GTK_WINDOW(dialog), 600, 300 );
2586
2587 gtk_widget_show_all ( dialog );
2588
2589 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
2590 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(gw) );
2591 gtk_widget_destroy ( dialog );
2592 if ( dir ) {
2593 if ( !export_to ( vw, gl, vft, dir, extension ) )
2594 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
2595 g_free ( dir );
2596 }
2597 }
2598 else
2599 gtk_widget_destroy ( dialog );
2600
2601 g_list_free ( gl );
2602}
2603
2604static void export_to_gpx ( GtkAction *a, VikWindow *vw )
2605{
2606 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
2607}
2608
2609static void export_to_kml ( GtkAction *a, VikWindow *vw )
2610{
2611 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
2612}
2613
1d1bc3c1
EB
2614static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2615{
16fc32f6
RN
2616 // Via the file menu, acquiring from a GPS makes a new layer
2617 // this has always been the way (not entirely sure if this was the real intention!)
2618 // thus maintain the behaviour ATM.
2619 // Hence explicit setting here (as the value may be changed elsewhere)
2620 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
7b3479e3
EB
2621 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface );
2622}
2623
31349009
GB
2624static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2625{
2626 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface );
2627}
2628
55340efa 2629#ifdef VIK_CONFIG_GOOGLE
7b3479e3
EB
2630static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2631{
2632 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface );
1d1bc3c1 2633}
ebf1bd39 2634#endif
1d1bc3c1 2635
9c4555df
GB
2636#ifdef VIK_CONFIG_OPENSTREETMAP
2637static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2638{
2639 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface );
2640}
3cc57413
RN
2641
2642static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
2643{
2644 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface );
2645}
9c4555df
GB
2646#endif
2647
1ef9e637 2648#ifdef VIK_CONFIG_GEOCACHES
3333c069
EB
2649static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2650{
2651 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface );
2652}
1ef9e637 2653#endif
3333c069 2654
f75d0233
RN
2655#ifdef VIK_CONFIG_GEOTAG
2656static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2657{
2658 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2659 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface );
2660}
2661#endif
2662
3c29a566
RN
2663#ifdef VIK_CONFIG_GEONAMES
2664static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
2665{
2666 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface );
2667}
2668#endif
2669
5210c3d3
GB
2670static void goto_default_location( GtkAction *a, VikWindow *vw)
2671{
2672 struct LatLon ll;
2673 ll.lat = a_vik_get_default_lat();
2674 ll.lon = a_vik_get_default_long();
2675 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2676 vik_layers_panel_emit_update(vw->viking_vlp);
2677}
2678
2679
369126f3
QT
2680static void goto_address( GtkAction *a, VikWindow *vw)
2681{
c36a079a
RN
2682 a_vik_goto ( vw, vw->viking_vvp );
2683 vik_layers_panel_emit_update ( vw->viking_vlp );
369126f3
QT
2684}
2685
7c259702
JJ
2686static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2687{
2688 a_mapcache_flush();
2689}
2690
a7023a1b
RN
2691static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
2692{
2693 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
2694
2695 if ( !texts[1] )
2696 return; // Internally broken :(
2697
2698 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
2699 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
2700 // NB no update needed
2701
2702 g_strfreev ( texts );
2703}
2704
98acb9a1
RN
2705static void preferences_change_update ( VikWindow *vw, gpointer data )
2706{
2707 // Want to update all TrackWaypoint layers
2708 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2709
2710 GList *iter = g_list_first ( layers );
2711 while ( iter ) {
2712 // Reset the individual waypoints themselves due to the preferences change
2713 VikTrwLayer *vtl = VIK_TRW_LAYER(VIK_LAYER(layers->data));
2714 vik_trw_layer_reset_waypoints ( vtl );
2715 iter = g_list_next ( iter );
2716 }
2717
2718 g_list_free ( layers );
2719
2720 draw_update ( vw );
2721}
2722
17a1f8f9
EB
2723static void preferences_cb ( GtkAction *a, VikWindow *vw )
2724{
9be0449f
RN
2725 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2726
17a1f8f9 2727 a_preferences_show_window ( GTK_WINDOW(vw) );
9be0449f 2728
98acb9a1
RN
2729 // Has the waypoint size setting changed?
2730 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
2731 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
9be0449f
RN
2732 clear_garmin_icon_syms ();
2733
98acb9a1
RN
2734 // Update all windows
2735 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
2736 }
17a1f8f9
EB
2737}
2738
5210c3d3
GB
2739static void default_location_cb ( GtkAction *a, VikWindow *vw )
2740{
2741 /* Simplistic repeat of preference setting
2742 Only the name & type are important for setting the preference via this 'external' way */
2743 VikLayerParam pref_lat[] = {
a7023a1b
RN
2744 { VIK_LAYER_NUM_TYPES,
2745 VIKING_PREFERENCES_NAMESPACE "default_latitude",
5210c3d3
GB
2746 VIK_LAYER_PARAM_DOUBLE,
2747 VIK_LOCATION_LAT,
2748 NULL,
2749 VIK_LAYER_WIDGET_SPINBUTTON,
2750 NULL,
e6994d8d 2751 NULL,
5210c3d3
GB
2752 NULL },
2753 };
2754 VikLayerParam pref_lon[] = {
a7023a1b
RN
2755 { VIK_LAYER_NUM_TYPES,
2756 VIKING_PREFERENCES_NAMESPACE "default_longitude",
5210c3d3
GB
2757 VIK_LAYER_PARAM_DOUBLE,
2758 VIK_LOCATION_LONG,
2759 NULL,
2760 VIK_LAYER_WIDGET_SPINBUTTON,
2761 NULL,
e6994d8d 2762 NULL,
5210c3d3
GB
2763 NULL },
2764 };
2765
2766 /* Get current center */
2767 struct LatLon ll;
2768 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2769
2770 /* Apply to preferences */
2771 VikLayerParamData vlp_data;
2772 vlp_data.d = ll.lat;
2773 a_preferences_run_setparam (vlp_data, pref_lat);
2774 vlp_data.d = ll.lon;
2775 a_preferences_run_setparam (vlp_data, pref_lon);
2776 /* Remember to save */
2777 a_preferences_save_to_file();
2778}
2779
e4afc73a 2780static void clear_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
2781{
2782 vik_layers_panel_clear ( vw->viking_vlp );
2783 window_set_filename ( vw, NULL );
2784 draw_update ( vw );
2785}
2786
e4afc73a 2787static void window_close ( GtkAction *a, VikWindow *vw )
50a14534
EB
2788{
2789 if ( ! delete_event ( vw ) )
2790 gtk_widget_destroy ( GTK_WIDGET(vw) );
2791}
2792
2bf7cadd
QT
2793static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2794{
7bb60307 2795 if (save_file( NULL, vw)) {
2bf7cadd 2796 window_close( NULL, vw);
7bb60307
QT
2797 return(TRUE);
2798 }
2bf7cadd
QT
2799 else
2800 return(FALSE);
2801}
2802
e4afc73a 2803static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
50a14534
EB
2804{
2805 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2806 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2807 {
2808 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2809 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2810 draw_update ( vw );
2811 }
2812}
2813
2814static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2815{
2816 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2817 GdkPixbuf *pixbuf_to_save;
2818 gdouble old_xmpp, old_ympp;
2819 GError *error = NULL;
2820
d78751d4
RN
2821 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2822 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2823 GTK_MESSAGE_INFO,
2824 GTK_BUTTONS_NONE,
2825 _("Generating image file...") );
2826
2827 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2828 // Ensure dialog shown
2829 gtk_widget_show_all ( msgbox );
2830 // Try harder...
2831 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2832 while ( gtk_events_pending() )
2833 gtk_main_iteration ();
2834 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2835 // At least the empty box can give a clue something's going on + the statusbar msg...
2836 // Windows version under Wine OK!
2837
50a14534
EB
2838 /* backup old zoom & set new */
2839 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2840 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2841 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2842
2843 /* reset width and height: */
2844 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2845
2846 /* draw all layers */
2847 draw_redraw ( vw );
2848
2849 /* save buffer as file. */
2850 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 2851 if ( !pixbuf_to_save ) {
d78751d4
RN
2852 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2853 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
5cd09d57
RN
2854 goto cleanup;
2855 }
2856
50a14534
EB
2857 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2858 if (error)
2859 {
7742da66 2860 g_warning("Unable to write to file %s: %s", fn, error->message );
d78751d4 2861 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
50a14534
EB
2862 g_error_free (error);
2863 }
d78751d4
RN
2864 else {
2865 // Success
2866 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2867 }
50a14534
EB
2868 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2869
5cd09d57 2870 cleanup:
d78751d4
RN
2871 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2872 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2873 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2874
50a14534
EB
2875 /* pretend like nothing happened ;) */
2876 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2877 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2878 vik_viewport_configure ( vw->viking_vvp );
2879 draw_update ( vw );
2880}
2881
2882static 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 )
2883{
2884 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2885 gchar *name_of_file = g_malloc ( size );
2886 guint x = 1, y = 1;
2887 struct UTM utm_orig, utm;
2888
2889 /* *** copied from above *** */
2890 GdkPixbuf *pixbuf_to_save;
2891 gdouble old_xmpp, old_ympp;
2892 GError *error = NULL;
2893
2894 /* backup old zoom & set new */
2895 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2896 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2897 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2898
2899 /* reset width and height: do this only once for all images (same size) */
2900 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2901 /* *** end copy from above *** */
2902
2903 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2904
f83131b9 2905 g_mkdir(fn,0777);
50a14534
EB
2906
2907 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2908
2909 for ( y = 1; y <= tiles_h; y++ )
2910 {
2911 for ( x = 1; x <= tiles_w; x++ )
2912 {
3d9454e6 2913 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
2914 utm = utm_orig;
2915 if ( tiles_w & 0x1 )
2916 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2917 else
2918 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2919 if ( tiles_h & 0x1 ) /* odd */
2920 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2921 else /* even */
2922 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2923
2924 /* move to correct place. */
2925 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2926
2927 draw_redraw ( vw );
2928
2929 /* save buffer as file. */
2930 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);
2931 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2932 if (error)
2933 {
7742da66 2934 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
50a14534
EB
2935 g_error_free (error);
2936 }
2937
2938 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2939 }
2940 }
2941
2942 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2943 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2944 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2945 vik_viewport_configure ( vw->viking_vvp );
2946 draw_update ( vw );
2947
2948 g_free ( name_of_file );
2949}
2950
2951static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2952{
2953 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2954 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
267e3ce7
RN
2955
2956 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2957 gdouble zoom = pow (2, active-2 );
2958
50a14534
EB
2959 gdouble width_min, width_max, height_min, height_max;
2960 gint width, height;
2961
2962 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2963 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2964
2965 /* TODO: support for xzoom and yzoom values */
267e3ce7
RN
2966 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2967 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
50a14534
EB
2968
2969 if ( width > width_max || width < width_min || height > height_max || height < height_min )
4c77d5e0 2970 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
50a14534
EB
2971
2972 gtk_spin_button_set_value ( width_spin, width );
2973 gtk_spin_button_set_value ( height_spin, height );
2974}
2975
2976static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2977{
2978 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
267e3ce7
RN
2979
2980 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2981 gdouble zoom = pow (2, active-2 );
2982
50a14534
EB
2983 gchar *label_text;
2984 gdouble w, h;
267e3ce7
RN
2985 w = gtk_spin_button_get_value(width_spin) * zoom;
2986 h = gtk_spin_button_get_value(height_spin) * zoom;
50a14534
EB
2987 if (pass_along[4]) /* save many images; find TOTAL area covered */
2988 {
2989 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
2990 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
2991 }
6f9336aa
RN
2992 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2993 switch (dist_units) {
2994 case VIK_UNITS_DISTANCE_KILOMETRES:
2995 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
2996 break;
2997 case VIK_UNITS_DISTANCE_MILES:
2998 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
2999 break;
3000 default:
3001 label_text = g_strdup_printf ("Just to keep the compiler happy");
3002 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3003 }
3004
50a14534
EB
3005 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3006 g_free ( label_text );
3007}
3008
310cbaf7
RN
3009/*
3010 * Get an allocated filename (or directory as specified)
3011 */
3012static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3013{
3014 gchar *fn = NULL;
3015 if ( one_image_only )
3016 {
3017 // Single file
3018 if (!vw->save_img_dia) {
3019 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3020 GTK_WINDOW(vw),
3021 GTK_FILE_CHOOSER_ACTION_SAVE,
3022 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3023 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3024 NULL);
67b63c20 3025
31729835
RN
3026 gchar *cwd = g_get_current_dir();
3027 if ( cwd ) {
3028 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3029 g_free ( cwd );
3030 }
3031
67b63c20
RN
3032 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3033 /* Add filters */
3034 GtkFileFilter *filter;
3035 filter = gtk_file_filter_new ();
3036 gtk_file_filter_set_name ( filter, _("All") );
3037 gtk_file_filter_add_pattern ( filter, "*" );
3038 gtk_file_chooser_add_filter ( chooser, filter );
3039
3040 filter = gtk_file_filter_new ();
3041 gtk_file_filter_set_name ( filter, _("JPG") );
3042 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3043 gtk_file_chooser_add_filter ( chooser, filter );
3044
3045 filter = gtk_file_filter_new ();
3046 gtk_file_filter_set_name ( filter, _("PNG") );
3047 gtk_file_filter_add_mime_type ( filter, "image/png");
3048 gtk_file_chooser_add_filter ( chooser, filter );
3049
3050 // Default to pngs
3051 gtk_file_chooser_set_filter ( chooser, filter );
3052
310cbaf7
RN
3053 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3054 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3055 }
3056
3057 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3058 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3059 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3060 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 ) ) )
3061 fn = NULL;
3062 }
3063 gtk_widget_hide ( vw->save_img_dia );
3064 }
3065 else {
3066 // A directory
3067 // For some reason this method is only written to work in UTM...
3068 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3069 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3070 return fn;
3071 }
3072
3073 if (!vw->save_img_dir_dia) {
3074 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3075 GTK_WINDOW(vw),
3076 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3077 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3078 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3079 NULL);
3080 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3081 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3082 }
3083
3084 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3085 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3086 }
3087 gtk_widget_hide ( vw->save_img_dir_dia );
3088 }
3089 return fn;
3090}
3091
3092static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
50a14534
EB
3093{
3094 /* todo: default for answers inside VikWindow or static (thruout instance) */
4c77d5e0 3095 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
50a14534
EB
3096 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3097 GTK_STOCK_CANCEL,
3098 GTK_RESPONSE_REJECT,
3099 GTK_STOCK_OK,
3100 GTK_RESPONSE_ACCEPT,
10888930 3101 NULL );
50a14534
EB
3102 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3103 GtkWidget *png_radio, *jpeg_radio;
3104 GtkWidget *current_window_button;
3105 gpointer current_window_pass_along[7];
267e3ce7 3106 GtkWidget *zoom_label, *zoom_combo;
50a14534
EB
3107 GtkWidget *total_size_label;
3108
3109 /* only used if (!one_image_only) */
886031df 3110 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
50a14534 3111
4c77d5e0 3112 width_label = gtk_label_new ( _("Width (pixels):") );
5cd09d57 3113 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4c77d5e0 3114 height_label = gtk_label_new ( _("Height (pixels):") );
5cd09d57
RN
3115 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3116#ifdef WINDOWS
3117 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3118#endif
4c77d5e0 3119 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
50a14534 3120 /* TODO: separate xzoom and yzoom factors */
267e3ce7
RN
3121 zoom_combo = create_zoom_combo_all_levels();
3122
3123 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4db16a36 3124 gint active = 2 + round ( log (mpp) / log (2) );
267e3ce7
RN
3125
3126 // Can we not hard code size here?
3127 if ( active > 17 )
3128 active = 17;
3129 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
50a14534
EB
3130
3131 total_size_label = gtk_label_new ( NULL );
3132
4c77d5e0 3133 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
50a14534
EB
3134 current_window_pass_along [0] = vw;
3135 current_window_pass_along [1] = width_spin;
3136 current_window_pass_along [2] = height_spin;
267e3ce7 3137 current_window_pass_along [3] = zoom_combo;
50a14534
EB
3138 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3139 current_window_pass_along [5] = NULL;
3140 current_window_pass_along [6] = total_size_label;
3141 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3142
4c77d5e0
GB
3143 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3144 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
50a14534
EB
3145
3146 if ( ! vw->draw_image_save_as_png )
3147 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3148
9b082b39
RN
3149 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3150 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3151 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3152 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
5cd09d57 3153#ifdef WINDOWS
9b082b39 3154 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
5cd09d57 3155#endif
9b082b39
RN
3156 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3157 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3158 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3159 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3160 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
50a14534
EB
3161
3162 if ( ! one_image_only )
3163 {
3164 GtkWidget *tiles_width_label, *tiles_height_label;
3165
4c77d5e0 3166 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
50a14534 3167 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4c77d5e0 3168 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
50a14534 3169 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
9b082b39
RN
3170 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3171 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3172 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3173 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
50a14534
EB
3174
3175 current_window_pass_along [4] = tiles_width_spin;
3176 current_window_pass_along [5] = tiles_height_spin;
3177 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3178 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3179 }
9b082b39 3180 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
50a14534
EB
3181 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3182 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
267e3ce7 3183 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
50a14534
EB
3184
3185 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3186
1fb999d3
RN
3187 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3188
9b082b39 3189 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
50a14534
EB
3190
3191 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3192 {
3193 gtk_widget_hide ( GTK_WIDGET(dialog) );
310cbaf7
RN
3194
3195 gchar *fn = draw_image_filename ( vw, one_image_only );
3196 if ( !fn )
3197 return;
3198
267e3ce7
RN
3199 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3200 gdouble zoom = pow (2, active-2 );
3201
50a14534
EB
3202 if ( one_image_only )
3203 save_image_file ( vw, fn,
3204 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3205 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
267e3ce7 3206 zoom,
50a14534
EB
3207 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3208 else {
e5657444
RN
3209 // NB is in UTM mode ATM
3210 save_image_dir ( vw, fn,
50a14534
EB
3211 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3212 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
267e3ce7 3213 zoom,
50a14534
EB
3214 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3215 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3216 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
50a14534 3217 }
310cbaf7
RN
3218
3219 g_free ( fn );
50a14534
EB
3220 }
3221 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3222}
3223
3224
e4afc73a 3225static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
50a14534 3226{
310cbaf7 3227 draw_to_image_file ( vw, TRUE );
50a14534
EB
3228}
3229
e4afc73a 3230static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
50a14534 3231{
310cbaf7 3232 draw_to_image_file ( vw, FALSE );
50a14534
EB
3233}
3234
42f34743
QT
3235static void print_cb ( GtkAction *a, VikWindow *vw )
3236{
3237 a_print(vw, vw->viking_vvp);
3238}
3239
50a14534 3240/* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
e4afc73a 3241static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
50a14534 3242{
e4afc73a
EB
3243 VikViewportDrawMode drawmode;
3244 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3245 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3246 }
d587678a
GB
3247 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3248 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3249 }
e4afc73a
EB
3250 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3251 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3252 }
e4afc73a
EB
3253 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3254 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3255 }
3256 else {
8dc8da82 3257 g_critical("Houston, we've had a problem.");
e4afc73a
EB
3258 return;
3259 }
3260
50a14534
EB
3261 if ( !vw->only_updating_coord_mode_ui )
3262 {
3263 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3264 if ( olddrawmode != drawmode )
3265 {
3266 /* this takes care of coord mode too */
3267 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3268 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3269 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3270 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3271 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3272 }
3273 draw_update ( vw );
3274 }
3275 }
3276}
3277
35c7c0ba
EB
3278static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3279{
48df6aa3 3280 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
1657065a
QT
3281 g_assert(check_box);
3282 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3283 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
35c7c0ba
EB
3284 draw_update ( vw );
3285}
3286
c933487f
QT
3287static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3288{
48df6aa3 3289 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
1657065a
QT
3290 g_assert(check_box);
3291 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3292 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
c933487f
QT
3293 draw_update ( vw );
3294}
3295
2afcef36
RN
3296static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3297{
3298 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3299 g_assert(check_box);
3300 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3301 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3302 draw_update ( vw );
3303}
3304
e4afc73a 3305static void set_bg_color ( GtkAction *a, VikWindow *vw )
50a14534 3306{
4c77d5e0 3307 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
50a14534 3308 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
9b082b39
RN
3309 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3310 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
50a14534
EB
3311 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3312 {
9b082b39 3313 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
50a14534
EB
3314 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3315 draw_update ( vw );
3316 }
3317 g_free ( color );
3318 gtk_widget_destroy ( colorsd );
3319}
3320
480fb7e1
RN
3321static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3322{
3323 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3324 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
9b082b39
RN
3325 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3326 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
480fb7e1
RN
3327 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3328 {
9b082b39 3329 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
480fb7e1
RN
3330 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3331 draw_update ( vw );
3332 }
3333 g_free ( color );
3334 gtk_widget_destroy ( colorsd );
3335}
3336
941aa6e9
AF
3337
3338
3339/***********************************************************************************************
3340 ** GUI Creation
3341 ***********************************************************************************************/
3342
e4afc73a 3343static GtkActionEntry entries[] = {
5515e2d3
JJ
3344 { "File", NULL, N_("_File"), 0, 0, 0 },
3345 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3346 { "View", NULL, N_("_View"), 0, 0, 0 },
48df6aa3 3347 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
5515e2d3
JJ
3348 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3349 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3350 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3351 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
92806042 3352 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
5515e2d3
JJ
3353 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3354
3355 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
06526f88
RN
3356 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3357 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3358 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
0bb36e78
RN
3359 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3360 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
d6de71f9 3361 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
06526f88 3362 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
31349009 3363 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
55340efa 3364#ifdef VIK_CONFIG_GOOGLE
06526f88 3365 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
ebf1bd39 3366#endif
9c4555df
GB
3367#ifdef VIK_CONFIG_OPENSTREETMAP
3368 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3cc57413 3369 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
9c4555df 3370#endif
1ef9e637 3371#ifdef VIK_CONFIG_GEOCACHES
06526f88 3372 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
f75d0233
RN
3373#endif
3374#ifdef VIK_CONFIG_GEOTAG
3375 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3c29a566
RN
3376#endif
3377#ifdef VIK_CONFIG_GEONAMES
3378 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
1ef9e637 3379#endif
5515e2d3 3380 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
06526f88
RN
3381 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3382 { "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 },
3383 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
5515e2d3 3384 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
5515e2d3
JJ
3385 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3386 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3387
5210c3d3 3388 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
cbd293bb 3389 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
402fee6c 3390 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
150618fe 3391 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
6b59f63d 3392 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
480fb7e1 3393 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
cbd293bb 3394 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
5515e2d3
JJ
3395 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3396 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
22c15481 3397 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
cbd293bb
RN
3398 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3399 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3400 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3401 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
5515e2d3
JJ
3402 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3403
3404 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3405 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3406 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3407 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3408 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
06526f88 3409 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
5210c3d3 3410 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
06526f88 3411 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
6d61ba92 3412 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
5515e2d3
JJ
3413 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3414
5ff75d1e 3415 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
5515e2d3 3416 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
e4afc73a
EB
3417};
3418
0bb36e78
RN
3419static GtkActionEntry entries_gpsbabel[] = {
3420 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3421};
3422
e4afc73a 3423/* Radio items */
d587678a 3424/* FIXME use VIEWPORT_DRAWMODE values */
e4afc73a 3425static GtkRadioActionEntry mode_entries[] = {
5515e2d3
JJ
3426 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3427 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
ac16c140 3428 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
bd5e571a 3429 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
50a14534
EB
3430};
3431
35c7c0ba 3432static GtkToggleActionEntry toggle_entries[] = {
6b59f63d 3433 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
cbd293bb 3434 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
2afcef36 3435 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
cbd293bb 3436 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
48df6aa3 3437 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
a459ee10 3438 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
48df6aa3
RN
3439 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3440 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
35c7c0ba
EB
3441};
3442
a527cfff 3443#include "menu.xml.h"
e4afc73a 3444static void window_create_ui( VikWindow *window )
50a14534 3445{
e4afc73a
EB
3446 GtkUIManager *uim;
3447 GtkActionGroup *action_group;
50a14534 3448 GtkAccelGroup *accel_group;
e4afc73a 3449 GError *error;
e4afc73a
EB
3450 guint i, j, mid;
3451 GtkIconFactory *icon_factory;
3452 GtkIconSet *icon_set;
e4afc73a
EB
3453 GtkRadioActionEntry *tools = NULL, *radio;
3454 guint ntools;
3455
3456 uim = gtk_ui_manager_new ();
3457 window->uim = uim;
3458
9593a4c9
EB
3459 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3460 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
576cbd17 3461 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
a47bfefa 3462 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
941aa6e9 3463
e4afc73a 3464 error = NULL;
a527cfff 3465 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
e4afc73a
EB
3466 g_error_free (error);
3467 exit (1);
3468 }
3469
3470 action_group = gtk_action_group_new ("MenuActions");
5515e2d3 3471 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
e4afc73a 3472 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
35c7c0ba 3473 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
6a9ff0ee 3474 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
e4afc73a 3475
0bb36e78
RN
3476 // Use this to see if GPSBabel is available:
3477 if ( a_babel_device_list ) {
3478 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
3479 if ( gtk_ui_manager_add_ui_from_string ( uim,
3480 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
3481 -1, &error ) )
3482 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
3483 }
3484
e4afc73a 3485 icon_factory = gtk_icon_factory_new ();
ee36ac4f 3486 gtk_icon_factory_add_default (icon_factory);
e4afc73a
EB
3487
3488 register_vik_icons(icon_factory);
3489
79dce0cb
RN
3490 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3491 // so that it can be applied to the UI in one action group add function call below
e4afc73a 3492 ntools = 0;
79dce0cb 3493 for (i=0; i<window->vt->n_tools; i++) {
e4afc73a
EB
3494 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3495 radio = &tools[ntools];
3496 ntools++;
79dce0cb 3497 *radio = window->vt->tools[i].ti.radioActionEntry;
e4afc73a 3498 radio->value = ntools;
79dce0cb 3499 }
e4afc73a
EB
3500
3501 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3502 GtkActionEntry action;
3503 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3504 vik_layer_get_interface(i)->name,
3505 vik_layer_get_interface(i)->name,
3506 GTK_UI_MANAGER_MENUITEM, FALSE);
3507
3508 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3509 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3510 gtk_icon_set_unref (icon_set);
3511
3512 action.name = vik_layer_get_interface(i)->name;
3513 action.stock_id = vik_layer_get_interface(i)->name;
da12e3d1 3514 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
75078768 3515 action.accelerator = vik_layer_get_interface(i)->accelerator;
e4afc73a
EB
3516 action.tooltip = NULL;
3517 action.callback = (GCallback)menu_addlayer_cb;
3518 gtk_action_group_add_actions(action_group, &action, 1, window);
3519
3520 if ( vik_layer_get_interface(i)->tools_count ) {
3521 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3522 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3523 }
3524
79dce0cb 3525 // Further tool copying for to apply to the UI, also apply menu UI setup
e4afc73a
EB
3526 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3527 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3528 radio = &tools[ntools];
3529 ntools++;
3530
e4afc73a 3531 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
79dce0cb
RN
3532 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3533 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
e4afc73a
EB
3534 GTK_UI_MANAGER_MENUITEM, FALSE);
3535 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
79dce0cb
RN
3536 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3537 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
e4afc73a
EB
3538 GTK_UI_MANAGER_TOOLITEM, FALSE);
3539
9593a4c9 3540 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
941aa6e9 3541
79dce0cb
RN
3542 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3543 // Overwrite with actual number to use
e4afc73a
EB
3544 radio->value = ntools;
3545 }
a7023a1b
RN
3546
3547 GtkActionEntry action_dl;
3548 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
3549 vik_layer_get_interface(i)->name,
3550 g_strdup_printf("Layer%s", vik_layer_get_interface(i)->fixed_layer_name),
3551 GTK_UI_MANAGER_MENUITEM, FALSE);
3552
3553 // For default layers use action names of the form 'Layer<LayerName>'
3554 // This is to avoid clashing with just the layer name used above for the tool actions
3555 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
3556 action_dl.stock_id = NULL;
3557 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
3558 action_dl.accelerator = NULL;
3559 action_dl.tooltip = NULL;
3560 action_dl.callback = (GCallback)layer_defaults_cb;
3561 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
e4afc73a 3562 }
ee36ac4f 3563 g_object_unref (icon_factory);
50a14534 3564
e4afc73a
EB
3565 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3566 g_free(tools);
50a14534 3567
e4afc73a 3568 gtk_ui_manager_insert_action_group (uim, action_group, 0);
50a14534 3569
79845167
QT
3570 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3571 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3572 GtkAction *action = gtk_action_group_get_action(action_group,
79dce0cb 3573 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
79845167
QT
3574 g_object_set(action, "sensitive", FALSE, NULL);
3575 }
3576 }
a147baa7
RN
3577
3578 // This is done last so we don't need to track the value of mid anymore
3579 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
3580
79845167
QT
3581 window->action_group = action_group;
3582
e4afc73a
EB
3583 accel_group = gtk_ui_manager_get_accel_group (uim);
3584 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3585 gtk_ui_manager_ensure_update (uim);
13505702
GB
3586
3587 setup_recent_files(window);
e4afc73a 3588}
50a14534 3589
50a14534 3590
79dce0cb
RN
3591// TODO - add method to add tool icons defined from outside this file
3592// and remove the reverse dependency on icon definition from this file
e4afc73a 3593static struct {
4bd45256 3594 const GdkPixdata *data;
e4afc73a
EB
3595 gchar *stock_id;
3596} stock_icons[] = {
79dce0cb 3597 { &mover_22_pixbuf, "vik-icon-pan" },
5bfafde9
GB
3598 { &zoom_18_pixbuf, "vik-icon-zoom" },
3599 { &ruler_18_pixbuf, "vik-icon-ruler" },
a47bfefa 3600 { &select_18_pixbuf, "vik-icon-select" },
e37b2a6d 3601 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
79dce0cb 3602 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
06a6af5b 3603 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
79dce0cb
RN
3604 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3605 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3606 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3607 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3608 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3609 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3610 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3611 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
e4afc73a
EB
3612};
3613
e4afc73a
EB
3614static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3615
3616static void
3617register_vik_icons (GtkIconFactory *icon_factory)
3618{
3619 GtkIconSet *icon_set;
e4afc73a 3620 gint i;
e4afc73a
EB
3621
3622 for (i = 0; i < n_stock_icons; i++) {
4bd45256
EB
3623 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3624 stock_icons[i].data, FALSE, NULL ));
e4afc73a
EB
3625 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3626 gtk_icon_set_unref (icon_set);
3627 }
50a14534 3628}
bce3a7b0 3629
9d7c24ed
RN
3630gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3631{
3632 return vw->selected_vtl;
3633}
3634
3635void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3636{
113c74f6
RN
3637 vw->selected_vtl = vtl;
3638 vw->containing_vtl = vtl;
9d7c24ed
RN
3639 /* Clear others */
3640 vw->selected_track = NULL;
3641 vw->selected_tracks = NULL;
3642 vw->selected_waypoint = NULL;
3643 vw->selected_waypoints = NULL;
04f36d92
RN
3644 // Set highlight thickness
3645 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
9d7c24ed
RN
3646}
3647
1c70a947 3648GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
9d7c24ed
RN
3649{
3650 return vw->selected_tracks;
3651}
3652
1c70a947 3653void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
9d7c24ed 3654{
1c70a947 3655 vw->selected_tracks = ght;
113c74f6 3656 vw->containing_vtl = vtl;
9d7c24ed
RN
3657 /* Clear others */
3658 vw->selected_vtl = NULL;
3659 vw->selected_track = NULL;
3660 vw->selected_waypoint = NULL;
3661 vw->selected_waypoints = NULL;
04f36d92
RN
3662 // Set highlight thickness
3663 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
9d7c24ed
RN
3664}
3665
3666gpointer vik_window_get_selected_track ( VikWindow *vw )
3667{
3668 return vw->selected_track;
3669}
3670
b16effab 3671void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
9d7c24ed
RN
3672{
3673 vw->selected_track = vt;
113c74f6 3674 vw->containing_vtl = vtl;
9d7c24ed
RN
3675 /* Clear others */
3676 vw->selected_vtl = NULL;
3677 vw->selected_tracks = NULL;
3678 vw->selected_waypoint = NULL;
3679 vw->selected_waypoints = NULL;
04f36d92
RN
3680 // Set highlight thickness
3681 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
9d7c24ed 3682}
04f36d92 3683
1c70a947 3684GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
9d7c24ed
RN
3685{
3686 return vw->selected_waypoints;
3687}
3688
1c70a947 3689void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
9d7c24ed 3690{
1c70a947 3691 vw->selected_waypoints = ght;
113c74f6 3692 vw->containing_vtl = vtl;
9d7c24ed
RN
3693 /* Clear others */
3694 vw->selected_vtl = NULL;
3695 vw->selected_track = NULL;
3696 vw->selected_tracks = NULL;
3697 vw->selected_waypoint = NULL;
3698}
3699
3700gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3701{
3702 return vw->selected_waypoint;
3703}
3704
b16effab 3705void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
9d7c24ed
RN
3706{
3707 vw->selected_waypoint = vwp;
113c74f6 3708 vw->containing_vtl = vtl;
9d7c24ed
RN
3709 /* Clear others */
3710 vw->selected_vtl = NULL;
3711 vw->selected_track = NULL;
3712 vw->selected_tracks = NULL;
3713 vw->selected_waypoints = NULL;
3714}
3715
3716gboolean vik_window_clear_highlight ( VikWindow *vw )
3717{
3718 gboolean need_redraw = FALSE;
3719 if ( vw->selected_vtl != NULL ) {
3720 vw->selected_vtl = NULL;
3721 need_redraw = TRUE;
3722 }
3723 if ( vw->selected_track != NULL ) {
3724 vw->selected_track = NULL;
3725 need_redraw = TRUE;
3726 }
3727 if ( vw->selected_tracks != NULL ) {
3728 vw->selected_tracks = NULL;
3729 need_redraw = TRUE;
3730 }
3731 if ( vw->selected_waypoint != NULL ) {
3732 vw->selected_waypoint = NULL;
3733 need_redraw = TRUE;
3734 }
3735 if ( vw->selected_waypoints != NULL ) {
3736 vw->selected_waypoints = NULL;
3737 need_redraw = TRUE;
3738 }
3739 return need_redraw;
3740}
fa51adec
RN
3741
3742GThread *vik_window_get_thread ( VikWindow *vw )
3743{
3744 return vw->thread;
3745}