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