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