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