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