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