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