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