X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/55340efac3ba71a35fab6efb0e492f967a140aeb..66e15e9fda3b72e58f67fed38e3cc59413cf5359:/src/vikwindow.c?ds=sidebyside diff --git a/src/vikwindow.c b/src/vikwindow.c index 378aec2e..a08d9cda 100644 --- a/src/vikwindow.c +++ b/src/vikwindow.c @@ -3,7 +3,7 @@ * * Copyright (C) 2003-2005, Evan Battaglia * Copyright (C) 2005-2006, Alex Foobarian - * Copyright (C) 2012, Rob Norris + * Copyright (C) 2012-2013, Rob Norris * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,10 +34,13 @@ #include "mapcache.h" #include "print.h" #include "preferences.h" +#include "viklayer_defaults.h" #include "icons/icons.h" #include "vikexttools.h" +#include "vikexttool_datasources.h" #include "garminsymbols.h" #include "vikmapslayer.h" +#include "geonamessearch.h" #ifdef HAVE_STDLIB_H #include @@ -61,6 +64,7 @@ // why not be allowed to open a thousand more... #define MAX_WINDOWS 1024 static guint window_count = 0; +static GSList *window_list = NULL; #define VIKING_WINDOW_WIDTH 1000 #define VIKING_WINDOW_HEIGHT 800 @@ -72,6 +76,7 @@ static void window_finalize ( GObject *gob ); static GObjectClass *parent_class; static void window_set_filename ( VikWindow *vw, const gchar *filename ); +static const gchar *window_get_filename ( VikWindow *vw ); static VikWindow *window_new (); @@ -81,7 +86,6 @@ static void newwindow_cb ( GtkAction *a, VikWindow *vw ); // Signals static void open_window ( VikWindow *vw, GSList *files ); -static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type ); static void destroy_window ( GtkWidget *widget, gpointer data ); @@ -154,9 +158,9 @@ struct _VikWindow { VikStatusbar *viking_vs; GtkToolbar *toolbar; - GtkComboBox *tb_zoom_combo; - GtkItemFactory *item_factory; + GdkCursor *busy_cursor; + GdkCursor *viewport_cursor; // only a reference /* tool management state */ guint current_tool; @@ -174,6 +178,7 @@ struct _VikWindow { gchar *filename; gboolean modified; + VikLoadType_t loaded_type; GtkWidget *open_dia, *save_dia; GtkWidget *save_img_dia, *save_img_dir_dia; @@ -181,6 +186,7 @@ struct _VikWindow { gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */ GtkUIManager *uim; + GThread *thread; /* half-drawn update */ VikLayer *trigger; VikCoord trigger_center; @@ -188,15 +194,11 @@ struct _VikWindow { /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */ /* Only one of these items can be selected at the same time */ gpointer selected_vtl; /* notionally VikTrwLayer */ - gpointer selected_tracks; /* notionally GList */ + GHashTable *selected_tracks; gpointer selected_track; /* notionally VikTrack */ - gpointer selected_waypoints; /* notionally GList */ + GHashTable *selected_waypoints; gpointer selected_waypoint; /* notionally VikWaypoint */ /* only use for individual track or waypoint */ - ////// NEED TO THINK ABOUT VALIDITY OF THESE ////// - ////// i.e. what happens when stuff is deleted elsewhere ////// - ////// Generally seems alright as can not access them ////// - ////// containing_vtl now seems unecessary ////// /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */ gpointer containing_vtl; /* notionally VikTrwLayer */ }; @@ -213,7 +215,6 @@ enum { enum { VW_NEWWINDOW_SIGNAL, VW_OPENWINDOW_SIGNAL, - VW_STATUSBAR_UPDATE_SIGNAL, VW_LAST_SIGNAL }; @@ -242,35 +243,48 @@ VikStatusbar * vik_window_get_statusbar ( VikWindow *vw ) return vw->viking_vs; } -/** - * For signalling the update from a background thread - */ -void vik_window_signal_statusbar_update (VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type) -{ - g_signal_emit ( G_OBJECT(vw), window_signals[VW_STATUSBAR_UPDATE_SIGNAL], 0, message, vs_type ); -} +typedef struct { + VikStatusbar *vs; + vik_statusbar_type_t vs_type; + gchar* message; // Always make a copy of this data +} statusbar_idle_data; /** * For the actual statusbar update! */ -static gboolean statusbar_idle_update ( gpointer indata ) +static gboolean statusbar_idle_update ( statusbar_idle_data *sid ) { - gpointer *data = indata; - vik_statusbar_set_message ( data[0], GPOINTER_TO_INT(data[2]), data[1] ); + vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message ); + g_free ( sid->message ); + g_free ( sid ); return FALSE; } /** - * Update statusbar in the main thread + * vik_window_statusbar_update: + * @vw: The main window in which the statusbar will be updated. + * @message: The string to be displayed. This is copied. + * @vs_type: The part of the statusbar to be updated. + * + * This updates any part of the statusbar with the new string. + * It handles calling from the main thread or any background thread + * ATM this mostly used from background threads - as from the main thread + * one may use the vik_statusbar_set_message() directly. */ -static void window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type ) +void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type ) { - // ATM we know the message has been statically allocated so this is OK (no need to handle any freeing) - static gpointer data[3]; - data[0] = vw->viking_vs; - data[1] = (gchar*) message; - data[2] = GINT_TO_POINTER(vs_type); - g_idle_add ( (GSourceFunc) statusbar_idle_update, data ); + statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) ); + sid->vs = vw->viking_vs; + sid->vs_type = vs_type; + sid->message = g_strdup ( message ); + + if ( g_thread_self() == vik_window_get_thread ( vw ) ) { + g_idle_add ( (GSourceFunc) statusbar_idle_update, sid ); + } + else { + // From a background thread + gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid ); + } } // Actual signal handlers @@ -281,10 +295,13 @@ static void destroy_window ( GtkWidget *widget, gtk_main_quit (); } -static void statusbar_update ( VikWindow *vw, const gchar *message, vik_statusbar_type_t vs_type ) -{ - window_statusbar_update ( vw, message, vs_type ); -} +#define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel" +#define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar" +#define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar" +// Menubar setting to off is never auto saved in case it's accidentally turned off +// It's not so obvious so to recover the menu visibility. +// Thus this value is for setting manually via editting the settings file directly +#define VIK_SETTINGS_WIN_MENUBAR "window_menubar" VikWindow *vik_window_new_window () { @@ -298,11 +315,43 @@ VikWindow *vik_window_new_window () G_CALLBACK (vik_window_new_window), NULL); g_signal_connect (G_OBJECT (vw), "openwindow", G_CALLBACK (open_window), NULL); - g_signal_connect (G_OBJECT (vw), "statusbarupdate", - G_CALLBACK (statusbar_update), vw); gtk_widget_show_all ( GTK_WIDGET(vw) ); + if ( a_vik_get_restore_window_state() ) { + // These settings are applied after the show all as these options hide widgets + gboolean sidepanel; + if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) ) + if ( ! sidepanel ) { + gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) ); + GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" ); + gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE ); + } + + gboolean statusbar; + if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) ) + if ( ! statusbar ) { + gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) ); + GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" ); + gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE ); + } + + gboolean toolbar; + if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) ) + if ( ! toolbar ) { + gtk_widget_hide ( GTK_WIDGET(vw->toolbar) ); + GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" ); + gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE ); + } + + gboolean menubar; + if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) ) + if ( ! menubar ) { + gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) ); + GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" ); + gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE ); + } + } window_count++; return vw; @@ -310,6 +359,101 @@ VikWindow *vik_window_new_window () return NULL; } +/** + * determine_location_thread: + * @vw: The window that will get updated + * @threaddata: Data used by our background thread mechanism + * + * Use the features in vikgoto to determine where we are + * Then set up the viewport: + * 1. To goto the location + * 2. Set an appropriate level zoom for the location type + * 3. Some statusbar message feedback + */ +static int determine_location_thread ( VikWindow *vw, gpointer threaddata ) +{ + struct LatLon ll; + gchar *name = NULL; + gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name ); + + int result = a_background_thread_progress ( threaddata, 1.0 ); + if ( result != 0 ) { + vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO ); + return -1; /* Abort thread */ + } + + if ( ans ) { + // Zoom out a little + gdouble zoom = 16.0; + + if ( ans == 2 ) { + // Position found with city precision - so zoom out more + zoom = 128.0; + } + else if ( ans == 3 ) { + // Position found via country name search - so zoom wayyyy out + zoom = 2048.0; + } + + vik_viewport_set_zoom ( vw->viking_vvp, zoom ); + vik_viewport_set_center_latlon ( vw->viking_vvp, &ll ); + + gchar *message = g_strdup_printf ( _("Location found: %s"), name ); + vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO ); + g_free ( name ); + g_free ( message ); + + // Signal to redraw from the background + vik_layers_panel_emit_update ( vw->viking_vlp ); + } + else + vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO ); + + return 0; +} + +/** + * Steps to be taken once initial loading has completed + */ +void vik_window_new_window_finish ( VikWindow *vw ) +{ + // Don't add a map if we've loaded a Viking file already + if ( vw->filename ) + return; + + if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) { + vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE ); + if ( vw->filename ) + return; + } + + // Maybe add a default map layer + if ( a_vik_get_add_default_map_layer () ) { + VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, NULL, FALSE) ); + vik_maps_layer_pretend_licence_shown ( vml ); + vik_layer_rename ( VIK_LAYER(vml), _("Default Map") ); + vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE ); + + draw_update ( vw ); + } + + // If not loaded any file, maybe try the location lookup + if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) { + if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) { + + vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") ); + + a_background_thread ( GTK_WINDOW(vw), + _("Determining location"), + (vik_thr_func) determine_location_thread, + vw, + NULL, + NULL, + 1 ); + } + } +} + static void open_window ( VikWindow *vw, GSList *files ) { gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */ @@ -359,6 +503,16 @@ static void window_finalize ( GObject *gob ) a_background_remove_window ( vw ); + window_list = g_slist_remove ( window_list, vw ); + + gdk_cursor_unref ( vw->busy_cursor ); + int tt; + for (tt = 0; tt < vw->vt->n_tools; tt++ ) + if ( vw->vt->tools[tt].ti.destroy ) + vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state ); + g_free ( vw->vt->tools ); + g_free ( vw->vt ); + G_OBJECT_CLASS(parent_class)->finalize(gob); } @@ -370,7 +524,6 @@ static void vik_window_class_init ( VikWindowClass *klass ) 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); 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); - window_signals[VW_STATUSBAR_UPDATE_SIGNAL] = g_signal_new ( "statusbarupdate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, statusbarupdate), NULL, NULL, gtk_marshal_VOID__POINTER_UINT, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); object_class = G_OBJECT_CLASS (klass); @@ -380,22 +533,17 @@ static void vik_window_class_init ( VikWindowClass *klass ) } -static void set_toolbar_zoom ( VikWindow *vw, gdouble mpp ) +static void zoom_changed (GtkMenuShell *menushell, + gpointer user_data) { - gint active = 2 + ( log (mpp) / log (2) ); - // Can we not hard code size here? - if ( active > 17 ) - active = 17; - gtk_combo_box_set_active ( vw->tb_zoom_combo, active ); -} + VikWindow *vw = VIK_WINDOW (user_data); -static void zoom_changed ( GtkComboBox *combo, VikWindow *vw ) -{ - gint active = gtk_combo_box_get_active ( combo ); + GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) ); + gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" )); - // But has it really changed? - // Unfortunately this function gets invoked even on manual setting of the combo value gdouble zoom_request = pow (2, active-2 ); + + // But has it really changed? gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp ); if ( current_zoom != 0.0 && zoom_request != current_zoom ) { vik_viewport_set_zoom ( vw->viking_vvp, zoom_request ); @@ -404,33 +552,132 @@ static void zoom_changed ( GtkComboBox *combo, VikWindow *vw ) } } +/** + * @mpp: The initial zoom level + */ +static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp ) +{ + GtkWidget *menu = gtk_menu_new (); + char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" }; + + int i; + for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++) + { + GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i)); + } + + gint active = 2 + round ( log (mpp) / log (2) ); + // Ensure value derived from mpp is in bounds of the menu + if ( active >= G_N_ELEMENTS(itemLabels) ) + active = G_N_ELEMENTS(itemLabels) - 1; + if ( active < 0 ) + active = 0; + gtk_menu_set_active ( GTK_MENU(menu), active ); + + return menu; +} + static GtkWidget *create_zoom_combo_all_levels () { - GtkWidget *zoom_combo = gtk_combo_box_new_text(); - GtkComboBox *combo = GTK_COMBO_BOX ( zoom_combo ); - gtk_combo_box_append_text ( combo, "0.25"); - gtk_combo_box_append_text ( combo, "0.5"); - gtk_combo_box_append_text ( combo, "1"); - gtk_combo_box_append_text ( combo, "2"); - gtk_combo_box_append_text ( combo, "4"); - gtk_combo_box_append_text ( combo, "8"); - gtk_combo_box_append_text ( combo, "16"); - gtk_combo_box_append_text ( combo, "32"); - gtk_combo_box_append_text ( combo, "64"); - gtk_combo_box_append_text ( combo, "128"); - gtk_combo_box_append_text ( combo, "256"); - gtk_combo_box_append_text ( combo, "512"); - gtk_combo_box_append_text ( combo, "1024"); - gtk_combo_box_append_text ( combo, "2048"); - gtk_combo_box_append_text ( combo, "4096"); - gtk_combo_box_append_text ( combo, "8192"); - gtk_combo_box_append_text ( combo, "16384"); - gtk_combo_box_append_text ( combo, "32768"); + GtkWidget *combo = vik_combo_box_text_new(); + vik_combo_box_text_append ( combo, "0.25"); + vik_combo_box_text_append ( combo, "0.5"); + vik_combo_box_text_append ( combo, "1"); + vik_combo_box_text_append ( combo, "2"); + vik_combo_box_text_append ( combo, "4"); + vik_combo_box_text_append ( combo, "8"); + vik_combo_box_text_append ( combo, "16"); + vik_combo_box_text_append ( combo, "32"); + vik_combo_box_text_append ( combo, "64"); + vik_combo_box_text_append ( combo, "128"); + vik_combo_box_text_append ( combo, "256"); + vik_combo_box_text_append ( combo, "512"); + vik_combo_box_text_append ( combo, "1024"); + vik_combo_box_text_append ( combo, "2048"); + vik_combo_box_text_append ( combo, "4096"); + vik_combo_box_text_append ( combo, "8192"); + vik_combo_box_text_append ( combo, "16384"); + vik_combo_box_text_append ( combo, "32768"); /* Create tooltip */ - gtk_widget_set_tooltip_text (GTK_WIDGET (combo), _("Select zoom level")); - return zoom_combo; + gtk_widget_set_tooltip_text (combo, _("Select zoom level")); + return combo; +} + +static gint zoom_popup_handler (GtkWidget *widget) +{ + GtkMenu *menu; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_MENU (widget), FALSE); + + /* The "widget" is the menu that was supplied when + * g_signal_connect_swapped() was called. + */ + menu = GTK_MENU (widget); + + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, + 1, gtk_get_current_event_time()); + return TRUE; } +enum { + TARGET_URIS, +}; + +static void drag_data_received_cb ( GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint target_type, + guint time, + gpointer data ) +{ + gboolean success = FALSE; + + if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) { + switch (target_type) { + case TARGET_URIS: { + gchar *str = (gchar*)gtk_selection_data_get_data(selection_data); + g_debug ("drag received string:%s \n", str); + + // Convert string into GSList of individual entries for use with our open signal + gchar **entries = g_strsplit(str, "\r\n", 0); + GSList *filenames = NULL; + gint entry_runner = 0; + gchar *entry = entries[entry_runner]; + while (entry) { + if ( g_strcmp0 ( entry, "" ) ) + filenames = g_slist_append ( filenames, entry ); + entry_runner++; + entry = entries[entry_runner]; + } + + if ( filenames ) + g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames ); + // NB: GSList & contents are freed by main.open_window + + success = TRUE; + break; + } + default: break; + } + } + + gtk_drag_finish ( context, success, FALSE, time ); +} + +#define VIK_SETTINGS_WIN_MAX "window_maximized" +#define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen" +#define VIK_SETTINGS_WIN_WIDTH "window_width" +#define VIK_SETTINGS_WIN_HEIGHT "window_height" +#define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width" +#define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height" +#define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png" + static void vik_window_init ( VikWindow *vw ) { GtkWidget *main_vbox; @@ -448,20 +695,34 @@ static void vik_window_init ( VikWindow *vw ) window_set_filename (vw, NULL); vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar")); + vw->busy_cursor = gdk_cursor_new ( GDK_WATCH ); + // Set the default tool gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) ); vw->filename = NULL; - vw->item_factory = NULL; - + vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none vw->modified = FALSE; vw->only_updating_coord_mode_ui = FALSE; vw->pan_move = FALSE; vw->pan_x = vw->pan_y = -1; - vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH; - vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT; - vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG; + + gint draw_image_width; + if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) ) + vw->draw_image_width = draw_image_width; + else + vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH; + gint draw_image_height; + if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) ) + vw->draw_image_height = draw_image_height; + else + vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT; + gboolean draw_image_save_as_png; + if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) ) + vw->draw_image_save_as_png = draw_image_save_as_png; + else + vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG; main_vbox = gtk_vbox_new(FALSE, 1); gtk_container_add (GTK_CONTAINER (vw), main_vbox); @@ -471,16 +732,13 @@ static void vik_window_init ( VikWindow *vw ) gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS); - vik_ext_tools_add_menu_items ( vw, vw->uim ); - - vw->tb_zoom_combo = GTK_COMBO_BOX(create_zoom_combo_all_levels()); + vik_ext_tool_datasources_add_menu_items ( vw, vw->uim ); - g_signal_connect ( G_OBJECT(vw->tb_zoom_combo), "changed", G_CALLBACK(zoom_changed), vw ); - - // Add the zoom combo to the toolbar at the end - GtkToolItem *tooli = gtk_tool_item_new (); - gtk_container_add ( GTK_CONTAINER(tooli), GTK_WIDGET (vw->tb_zoom_combo) ); - gtk_toolbar_insert ( vw->toolbar, tooli, gtk_toolbar_get_n_items (vw->toolbar) ); + GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom"); + GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) ); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu); + g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw); + g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu ); g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL); @@ -496,8 +754,6 @@ static void vik_window_init ( VikWindow *vw ) // Allow key presses to be processed anywhere g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw); - gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT); - hpaned = gtk_hpaned_new (); gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE ); gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE ); @@ -509,10 +765,61 @@ static void vik_window_init ( VikWindow *vw ) a_background_add_window ( vw ); + window_list = g_slist_prepend ( window_list, vw); + + gint height = VIKING_WINDOW_HEIGHT; + gint width = VIKING_WINDOW_WIDTH; + + if ( a_vik_get_restore_window_state() ) { + if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) { + // Enforce a basic minimum size + if ( height < 160 ) + height = 160; + } + else + // No setting - so use default + height = VIKING_WINDOW_HEIGHT; + + if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) { + // Enforce a basic minimum size + if ( width < 320 ) + width = 320; + } + else + // No setting - so use default + width = VIKING_WINDOW_WIDTH; + + gboolean maxed; + if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) ) + if ( maxed ) + gtk_window_maximize ( GTK_WINDOW(vw) ); + + gboolean full; + if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) { + if ( full ) { + gtk_window_fullscreen ( GTK_WINDOW(vw) ); + GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" ); + gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE ); + } + } + } + + gtk_window_set_default_size ( GTK_WINDOW(vw), width, height ); + vw->open_dia = NULL; vw->save_dia = NULL; vw->save_img_dia = NULL; vw->save_img_dir_dia = NULL; + + // Only accept Drag and Drop of files onto the viewport + gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY ); + gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) ); + g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL ); + + // Store the thread value so comparisons can be made to determine the gdk update method + // Hopefully we are storing the main thread value here :) + // [ATM any window initialization is always be performed by the main thread] + vw->thread = g_thread_self(); } static VikWindow *window_new () @@ -616,7 +923,7 @@ static gboolean delete_event( VikWindow *vw ) _("Do you want to save the changes you made to the document \"%s\"?\n" "\n" "Your changes will be lost if you don't save them."), - vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") ) ); + window_get_filename ( vw ) ) ); gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL ); switch ( gtk_dialog_run ( dia ) ) { @@ -625,6 +932,37 @@ static gboolean delete_event( VikWindow *vw ) default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw); } } + + if ( window_count == 1 ) { + // On the final window close - save latest state - if it's wanted... + if ( a_vik_get_restore_window_state() ) { + gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window ); + gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED; + a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max ); + + gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN; + a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen ); + + a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) ); + + a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) ); + + a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->toolbar)) ); + + // If supersized - no need to save the enlarged width+height values + if ( ! (state_fullscreen || state_max) ) { + gint width, height; + gtk_window_get_size ( GTK_WINDOW (vw), &width, &height ); + a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width ); + a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height ); + } + } + + a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width ); + a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height ); + a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png ); + } + return FALSE; } @@ -675,8 +1013,6 @@ static void draw_status ( VikWindow *vw ) g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit ); vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level ); - // OK maybe not quite in the statusbar - but we have the zoom level so use it - set_toolbar_zoom ( vw, xmpp ); // But it's a status of some kind! draw_status_tool ( vw ); } @@ -695,11 +1031,10 @@ static void window_configure_event ( VikWindow *vw ) if (first) { // This is a hack to set the cursor corresponding to the first tool // FIXME find the correct way to initialize both tool and its cursor - const GdkCursor *cursor = NULL; first = 0; - cursor = toolbox_get_cursor(vw->vt, "Pan"); + vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan"); /* We set cursor, even if it is NULL: it resets to default */ - gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor ); + gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor ); } } @@ -934,8 +1269,8 @@ static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gin gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); gdouble dx = (x2-x1)/len*10; gdouble dy = (y2-y1)/len*10; - gdouble c = cos(15.0 * M_PI/180.0); - gdouble s = sin(15.0 * M_PI/180.0); + gdouble c = cos(DEG2RAD(15.0)); + gdouble s = sin(DEG2RAD(15.0)); gdouble angle; gdouble baseangle = 0; gint i; @@ -960,29 +1295,8 @@ static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gin /* draw compass */ #define CR 80 #define CW 4 - angle = atan2(dy, dx) + M_PI_2; - - if ( vik_viewport_get_drawmode ( vvp ) == VIK_VIEWPORT_DRAWMODE_UTM) { - VikCoord test; - struct LatLon ll; - struct UTM u; - gint tx, ty; - - vik_viewport_screen_to_coord ( vvp, x1, y1, &test ); - vik_coord_to_latlon ( &test, &ll ); - ll.lat += vik_viewport_get_ympp ( vvp ) * vik_viewport_get_height ( vvp ) / 11000.0; // about 11km per degree latitude - a_coords_latlon_to_utm ( &ll, &u ); - vik_coord_load_from_utm ( &test, VIK_VIEWPORT_DRAWMODE_UTM, &u ); - vik_viewport_coord_to_screen ( vvp, &test, &tx, &ty ); - - baseangle = M_PI - atan2(tx-x1, ty-y1); - angle -= baseangle; - } - if (angle<0) - angle+=2*M_PI; - if (angle>2*M_PI) - angle-=2*M_PI; + vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle ); { GdkColor color; @@ -991,14 +1305,14 @@ static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gin gdk_color_parse("#2255cc", &color); gdk_gc_set_rgb_fg_color(thickgc, &color); } - gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - baseangle*180/M_PI)*64, -angle*180/M_PI*64); + 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); gdk_gc_copy(thickgc, gc); gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); for (i=0; i<180; i++) { - c = cos(i*M_PI/90.0 + baseangle); - s = sin(i*M_PI/90.0 + baseangle); + c = cos(DEG2RAD(i)*2 + baseangle); + s = sin(DEG2RAD(i)*2 + baseangle); if (i%5) { gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s); @@ -1026,7 +1340,7 @@ static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gin gint wb, hb, xb, yb; pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL); - pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc); + pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc); pango_layout_set_text(pl, "N", -1); gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl); @@ -1074,7 +1388,7 @@ static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gin LABEL(xd, yd, wd, hd); /* draw label with bearing */ - g_sprintf(str, "%3.1f°", angle*180.0/M_PI); + g_sprintf(str, "%3.1f°", RAD2DEG(angle)); pango_layout_set_text(pl, str, -1); pango_layout_get_pixel_size ( pl, &wb, &hb ); xb = x1 + CR*cos(angle-M_PI_2); @@ -1180,25 +1494,25 @@ static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, r w1 = vik_viewport_get_width(vvp); h1 = vik_viewport_get_height(vvp); if (!buf) { - buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 ); + buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 ); } gdk_drawable_get_size(buf, &w2, &h2); if (w1 != w2 || h1 != h2) { g_object_unref ( G_OBJECT ( buf ) ); - buf = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 ); + buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 ); } vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord ); vik_coord_to_latlon ( &coord, &ll ); vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy ); - gdk_draw_drawable (buf, GTK_WIDGET(vvp)->style->black_gc, + gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc, vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1); - draw_ruler(vvp, buf, GTK_WIDGET(vvp)->style->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) ); + 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)) ); if (draw_buf_done) { static gpointer pass_along[3]; - pass_along[0] = GTK_WIDGET(vvp)->window; - pass_along[1] = GTK_WIDGET(vvp)->style->black_gc; + pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp)); + pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc; pass_along[2] = buf; g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL); draw_buf_done = FALSE; @@ -1287,7 +1601,7 @@ static void zoomtool_resize_pixmap (zoom_tool_state_t *zts) if ( !zts->pixmap ) { // Totally new - zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 ); + zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 ); } gdk_drawable_get_size ( zts->pixmap, &w2, &h2 ); @@ -1295,7 +1609,7 @@ static void zoomtool_resize_pixmap (zoom_tool_state_t *zts) if ( w1 != w2 || h1 != h2 ) { // Has changed - delete and recreate with new values g_object_unref ( G_OBJECT ( zts->pixmap ) ); - zts->pixmap = gdk_pixmap_new ( GTK_WIDGET(zts->vw->viking_vvp)->window, w1, h1, -1 ); + zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 ); } } @@ -1384,7 +1698,7 @@ static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event // Blank out currently drawn area gdk_draw_drawable ( zts->pixmap, - GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, + gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, vik_viewport_get_pixmap(zts->vw->viking_vvp), 0, 0, 0, 0, -1, -1); @@ -1408,13 +1722,13 @@ static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event } // Draw the box - gdk_draw_rectangle (zts->pixmap, GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc, FALSE, xx, yy, width, height); + gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height); // Only actually draw when there's time to do so if (draw_buf_done) { static gpointer pass_along[3]; - pass_along[0] = GTK_WIDGET(zts->vw->viking_vvp)->window; - pass_along[1] = GTK_WIDGET(zts->vw->viking_vvp)->style->black_gc; + pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)); + pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc; pass_along[2] = zts->pixmap; g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL); draw_buf_done = FALSE; @@ -1672,6 +1986,12 @@ static VikToolInterface select_tool = static void draw_pan_cb ( GtkAction *a, VikWindow *vw ) { + // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here + // Thus if currently editting, ensure we don't move the viewport when Ctrl+ is received + VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp ); + if ( sel && vik_treeview_get_editing ( sel->vt ) ) + return; + if (!strcmp(gtk_action_get_name(a), "PanNorth")) { vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 ); } else if (!strcmp(gtk_action_get_name(a), "PanEast")) { @@ -1767,12 +2087,12 @@ static void draw_refresh_cb ( GtkAction *a, VikWindow *vw ) static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw ) { - gint type; + VikLayerTypeEnum type; for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) { if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) { if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) { - draw_update ( vw ); - vw->modified = TRUE; + draw_update ( vw ); + vw->modified = TRUE; } } } @@ -1791,7 +2111,7 @@ static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw ) static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw ) { - if ( a_clipboard_paste ( vw->viking_vlp ) ) + if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) ) { vw->modified = TRUE; } @@ -1808,7 +2128,6 @@ static void help_help_cb ( GtkAction *a, VikWindow *vw ) #ifdef WINDOWS ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL); #else /* WINDOWS */ -#if GTK_CHECK_VERSION (2, 14, 0) gchar *uri; uri = g_strdup_printf("ghelp:%s", PACKAGE); GError *error = NULL; @@ -1822,9 +2141,6 @@ static void help_help_cb ( GtkAction *a, VikWindow *vw ) g_error_free ( error ); } g_free(uri); -#else - a_dialog_error_msg ( GTK_WINDOW(vw), "Help is not available in this build." ); // Unlikely to happen so not going to bother with I8N -#endif #endif /* WINDOWS */ } @@ -2016,16 +2332,14 @@ void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id ) static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw ) { /* White Magic, my friends ... White Magic... */ - int layer_id, tool_id; - const GdkCursor *cursor = NULL; - + gint tool_id; toolbox_activate(vw->vt, gtk_action_get_name(a)); - cursor = toolbox_get_cursor(vw->vt, gtk_action_get_name(a)); + vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a)); - if ( GTK_WIDGET(vw->viking_vvp)->window ) + if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) ) /* We set cursor, even if it is NULL: it resets to default */ - gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, (GdkCursor *)cursor ); + gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor ); if (!strcmp(gtk_action_get_name(a), "Pan")) { vw->current_tool = TOOL_PAN; @@ -2041,6 +2355,7 @@ static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw ) } else { /* TODO: only enable tools from active layer */ + VikLayerTypeEnum layer_id; for (layer_id=0; layer_idtools_count; tool_id++ ) { if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) { @@ -2063,18 +2378,24 @@ static void window_set_filename ( VikWindow *vw, const gchar *filename ) if ( filename == NULL ) { vw->filename = NULL; - file = _("Untitled"); } else { vw->filename = g_strdup(filename); - file = a_file_basename ( filename ); } + + /* Refresh window's title */ + file = window_get_filename ( vw ); title = g_strdup_printf( "%s - Viking", file ); gtk_window_set_title ( GTK_WINDOW(vw), title ); g_free ( title ); } +static const gchar *window_get_filename ( VikWindow *vw ) +{ + return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled"); +} + GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode ) { GtkWidget *mode_button; @@ -2188,9 +2509,32 @@ static void update_recently_used_document(const gchar *filename) g_slice_free (GtkRecentData, recent_data); } +/** + * Call this before doing things that may take a long time and otherwise not show any other feedback + * such as loading and saving files + */ +void vik_window_set_busy_cursor ( VikWindow *vw ) +{ + gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor ); + // Viewport has a separate cursor + gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor ); + // Ensure cursor updated before doing stuff + while( gtk_events_pending() ) + gtk_main_iteration(); +} + +void vik_window_clear_busy_cursor ( VikWindow *vw ) +{ + gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL ); + // Restore viewport cursor + gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor ); +} + void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename ) { - switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) ) + vik_window_set_busy_cursor ( vw ); + vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ); + switch ( vw->loaded_type ) { case LOAD_TYPE_READ_FAILURE: a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") ); @@ -2244,7 +2588,10 @@ void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean chang draw_update ( vw ); break; } + + vik_window_clear_busy_cursor ( vw ); } + static void load_file ( GtkAction *a, VikWindow *vw ) { GSList *files = NULL; @@ -2264,11 +2611,17 @@ static void load_file ( GtkAction *a, VikWindow *vw ) if ( ! vw->open_dia ) { vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "), - GTK_WINDOW(vw), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); + GTK_WINDOW(vw), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + gchar *cwd = g_get_current_dir(); + if ( cwd ) { + gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd ); + g_free ( cwd ); + } + GtkFileFilter *filter; // NB file filters are listed this way for alphabetical ordering #ifdef VIK_CONFIG_GEOCACHES @@ -2360,11 +2713,17 @@ static gboolean save_file_as ( GtkAction *a, VikWindow *vw ) if ( ! vw->save_dia ) { vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."), - GTK_WINDOW(vw), - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); + GTK_WINDOW(vw), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gchar *cwd = g_get_current_dir(); + if ( cwd ) { + gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd ); + g_free ( cwd ); + } + GtkFileFilter *filter; filter = gtk_file_filter_new (); gtk_file_filter_set_name( filter, _("All") ); @@ -2383,7 +2742,7 @@ static gboolean save_file_as ( GtkAction *a, VikWindow *vw ) gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE ); } // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File - gchar* auto_save_name = strdup ( vw->filename ? a_file_basename ( vw->filename ) : _("Untitled") ); + gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) ); if ( ! check_file_ext ( auto_save_name, ".vik" ) ) auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL ); @@ -2407,16 +2766,20 @@ static gboolean save_file_as ( GtkAction *a, VikWindow *vw ) static gboolean window_save ( VikWindow *vw ) { + vik_window_set_busy_cursor ( vw ); + gboolean success = TRUE; + if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) ) { update_recently_used_document ( vw->filename ); - return TRUE; } else { a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") ); - return FALSE; + success = FALSE; } + vik_window_clear_busy_cursor ( vw ); + return success; } static gboolean save_file ( GtkAction *a, VikWindow *vw ) @@ -2430,6 +2793,162 @@ static gboolean save_file ( GtkAction *a, VikWindow *vw ) } } +/** + * export_to: + * + * Export all TRW Layers in the list to individual files in the specified directory + * + * Returns: %TRUE on success + */ +static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension ) +{ + gboolean success = TRUE; + + gint export_count = 0; + + vik_window_set_busy_cursor ( vw ); + + while ( gl ) { + + gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL ); + + // Some protection in attempting to write too many same named files + // As this will get horribly slow... + gboolean safe = FALSE; + gint ii = 2; + while ( ii < 5000 ) { + if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) { + // Try rename + g_free ( fn ); + fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension ); + } + else { + safe = TRUE; + break; + } + ii++; + } + if ( ii == 5000 ) + success = FALSE; + + // NB: We allow exporting empty layers + if ( safe ) { + gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE ); + + // Show some progress + if ( this_success ) { + export_count++; + gchar *message = g_strconcat ( _("Exporting to file: "), fn, NULL ); + vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message ); + while ( gtk_events_pending() ) + gtk_main_iteration (); + g_free ( message ); + } + + success = success && this_success; + } + + g_free ( fn ); + gl = g_list_next ( gl ); + } + + vik_window_clear_busy_cursor ( vw ); + + // Confirm what happened. + gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count ); + vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message ); + g_free ( message ); + + return success; +} + +static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension ) +{ + GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE ); + + if ( !gl ) { + a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") ); + return; + } + + GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Export to directory"), + GTK_WINDOW(vw), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL ); + + GtkWidget *gw = gtk_file_chooser_widget_new ( GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ); + gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), gw, TRUE, TRUE, 0 ); + + // try to make it a nice size - otherwise seems to default to something impractically small + gtk_window_set_default_size ( GTK_WINDOW(dialog), 600, 300 ); + + gtk_widget_show_all ( dialog ); + + if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { + gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(gw) ); + gtk_widget_destroy ( dialog ); + if ( dir ) { + if ( !export_to ( vw, gl, vft, dir, extension ) ) + a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") ); + g_free ( dir ); + } + } + else + gtk_widget_destroy ( dialog ); + + g_list_free ( gl ); +} + +static void export_to_gpx ( GtkAction *a, VikWindow *vw ) +{ + export_to_common ( vw, FILE_TYPE_GPX, ".gpx" ); +} + +static void export_to_kml ( GtkAction *a, VikWindow *vw ) +{ + export_to_common ( vw, FILE_TYPE_KML, ".kml" ); +} + +static void file_properties_cb ( GtkAction *a, VikWindow *vw ) +{ + gchar *message = NULL; + if ( vw->filename ) { + if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) { + // Get some timestamp information of the file + GStatBuf stat_buf; + if ( g_stat ( vw->filename, &stat_buf ) == 0 ) { + gchar time_buf[64]; + strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) ); + gchar *size = NULL; + gint byte_size = stat_buf.st_size; + // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte) + // hence using 1000 rather than 1024 + // so get output as per 'ls' or the Gtk file open dialog + if ( byte_size < 1000 ) + size = g_strdup_printf ( _("%d bytes"), byte_size ); + else if ( byte_size < 1000*1000 ) + size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 ); + else + size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) ); + message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size ); + g_free (size); + } + } + else + message = g_strdup ( _("File not accessible") ); + } + else + message = g_strdup ( _("No Viking File") ); + + // Show the info + a_dialog_info_msg ( GTK_WINDOW(vw), message ); + g_free ( message ); +} + static void acquire_from_gps ( GtkAction *a, VikWindow *vw ) { // Via the file menu, acquiring from a GPS makes a new layer @@ -2437,32 +2956,35 @@ static void acquire_from_gps ( GtkAction *a, VikWindow *vw ) // thus maintain the behaviour ATM. // Hence explicit setting here (as the value may be changed elsewhere) vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER; - a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface ); + a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL ); } static void acquire_from_file ( GtkAction *a, VikWindow *vw ) { - a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface ); + a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL ); } -#ifdef VIK_CONFIG_GOOGLE -static void acquire_from_google ( GtkAction *a, VikWindow *vw ) +static void acquire_from_routing ( GtkAction *a, VikWindow *vw ) { - a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface ); + a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_routing_interface, NULL, NULL ); } -#endif #ifdef VIK_CONFIG_OPENSTREETMAP static void acquire_from_osm ( GtkAction *a, VikWindow *vw ) { - a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface ); + a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL ); +} + +static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw ) +{ + a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL ); } #endif #ifdef VIK_CONFIG_GEOCACHES static void acquire_from_gc ( GtkAction *a, VikWindow *vw ) { - a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface ); + a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL ); } #endif @@ -2470,7 +2992,14 @@ static void acquire_from_gc ( GtkAction *a, VikWindow *vw ) static void acquire_from_geotag ( GtkAction *a, VikWindow *vw ) { vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER; - a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface ); + a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL ); +} +#endif + +#ifdef VIK_CONFIG_GEONAMES +static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw ) +{ + a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL ); } #endif @@ -2486,7 +3015,8 @@ static void goto_default_location( GtkAction *a, VikWindow *vw) static void goto_address( GtkAction *a, VikWindow *vw) { - a_vik_goto(vw, vw->viking_vlp, vw->viking_vvp); + a_vik_goto ( vw, vw->viking_vvp ); + vik_layers_panel_emit_update ( vw->viking_vlp ); } static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw ) @@ -2494,17 +3024,54 @@ static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw ) a_mapcache_flush(); } +static void layer_defaults_cb ( GtkAction *a, VikWindow *vw ) +{ + gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 ); + + if ( !texts[1] ) + return; // Internally broken :( + + if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) ) + a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") ); + // NB no update needed + + g_strfreev ( texts ); +} + +static void preferences_change_update ( VikWindow *vw, gpointer data ) +{ + // Want to update all TrackWaypoint layers + GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE ); + + if ( !layers ) + return; + + while ( layers ) { + // Reset the individual waypoints themselves due to the preferences change + VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data); + vik_trw_layer_reset_waypoints ( vtl ); + layers = g_list_next ( layers ); + } + + g_list_free ( layers ); + + draw_update ( vw ); +} + static void preferences_cb ( GtkAction *a, VikWindow *vw ) { gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons(); a_preferences_show_window ( GTK_WINDOW(vw) ); - // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed - if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) + // Has the waypoint size setting changed? + if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) { + // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed clear_garmin_icon_syms (); - draw_update ( vw ); + // Update all windows + g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL ); + } } static void default_location_cb ( GtkAction *a, VikWindow *vw ) @@ -2512,22 +3079,32 @@ static void default_location_cb ( GtkAction *a, VikWindow *vw ) /* Simplistic repeat of preference setting Only the name & type are important for setting the preference via this 'external' way */ VikLayerParam pref_lat[] = { - { VIKING_PREFERENCES_NAMESPACE "default_latitude", + { VIK_LAYER_NUM_TYPES, + VIKING_PREFERENCES_NAMESPACE "default_latitude", VIK_LAYER_PARAM_DOUBLE, VIK_LOCATION_LAT, NULL, VIK_LAYER_WIDGET_SPINBUTTON, NULL, - NULL }, + NULL, + NULL, + NULL, + NULL, + }, }; VikLayerParam pref_lon[] = { - { VIKING_PREFERENCES_NAMESPACE "default_longitude", + { VIK_LAYER_NUM_TYPES, + VIKING_PREFERENCES_NAMESPACE "default_longitude", VIK_LAYER_PARAM_DOUBLE, VIK_LOCATION_LONG, NULL, VIK_LAYER_WIDGET_SPINBUTTON, NULL, - NULL }, + NULL, + NULL, + NULL, + NULL, + }, }; /* Get current center */ @@ -2790,6 +3367,12 @@ static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only ) GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + gchar *cwd = g_get_current_dir(); + if ( cwd ) { + gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd ); + g_free ( cwd ); + } + GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia ); /* Add filters */ GtkFileFilter *filter; @@ -2803,13 +3386,16 @@ static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only ) gtk_file_filter_add_mime_type ( filter, "image/jpeg"); gtk_file_chooser_add_filter ( chooser, filter ); + if ( !vw->draw_image_save_as_png ) + gtk_file_chooser_set_filter ( chooser, filter ); + filter = gtk_file_filter_new (); gtk_file_filter_set_name ( filter, _("PNG") ); gtk_file_filter_add_mime_type ( filter, "image/png"); gtk_file_chooser_add_filter ( chooser, filter ); - // Default to pngs - gtk_file_chooser_set_filter ( chooser, filter ); + if ( vw->draw_image_save_as_png ) + gtk_file_chooser_set_filter ( chooser, filter ); gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) ); gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE ); @@ -2882,11 +3468,13 @@ static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only ) zoom_combo = create_zoom_combo_all_levels(); gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp); - gint active = 2 + ( log (mpp) / log (2) ); + gint active = 2 + round ( log (mpp) / log (2) ); // Can we not hard code size here? if ( active > 17 ) active = 17; + if ( active < 0 ) + active = 0; gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active ); total_size_label = gtk_label_new ( NULL ); @@ -2907,18 +3495,18 @@ static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only ) if ( ! vw->draw_image_save_as_png ) gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE ); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0); #ifdef WINDOWS - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), win_warning_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0); #endif - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_combo, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0); if ( ! one_image_only ) { @@ -2928,17 +3516,17 @@ static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only ) tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 ); tiles_height_label = gtk_label_new ( _("North-south image tiles:") ); tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 ); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0); current_window_pass_along [4] = tiles_width_spin; current_window_pass_along [5] = tiles_height_spin; g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); } - gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0); g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); @@ -2947,7 +3535,7 @@ static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only ) gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT ); - gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox ); + gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) ); if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { @@ -2993,12 +3581,10 @@ static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw ) draw_to_image_file ( vw, FALSE ); } -#if GTK_CHECK_VERSION(2,10,0) static void print_cb ( GtkAction *a, VikWindow *vw ) { a_print(vw, vw->viking_vvp); } -#endif /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */ static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw ) @@ -3069,11 +3655,11 @@ static void set_bg_color ( GtkAction *a, VikWindow *vw ) { GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") ); GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp ); - gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color ); - gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color ); + gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color ); + gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color ); if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK ) { - gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color ); + gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color ); vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color ); draw_update ( vw ); } @@ -3085,11 +3671,11 @@ static void set_highlight_color ( GtkAction *a, VikWindow *vw ) { GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") ); GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp ); - gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color ); - gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color ); + gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color ); + gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color ); if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK ) { - gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color ); + gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color ); vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color ); draw_update ( vw ); } @@ -3119,30 +3705,31 @@ static GtkActionEntry entries[] = { { "Open", GTK_STOCK_OPEN, N_("_Open..."), "O", N_("Open a file"), (GCallback)load_file }, { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL }, { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file }, + { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL }, + { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx }, { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL }, { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps }, { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file }, -#ifdef VIK_CONFIG_GOOGLE - { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google }, -#endif + { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing }, #ifdef VIK_CONFIG_OPENSTREETMAP { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm }, + { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm }, #endif #ifdef VIK_CONFIG_GEOCACHES { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc }, #endif #ifdef VIK_CONFIG_GEOTAG { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag }, +#endif +#ifdef VIK_CONFIG_GEONAMES + { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia }, #endif { "Save", GTK_STOCK_SAVE, N_("_Save"), "S", N_("Save the file"), (GCallback)save_file }, { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as }, + { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb }, { "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 }, { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb }, - -#if GTK_CHECK_VERSION(2,10,0) { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb }, -#endif - { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "W", N_("Exit the program"), (GCallback)window_close }, { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit }, @@ -3156,24 +3743,6 @@ static GtkActionEntry entries[] = { { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "plus", NULL, (GCallback)draw_zoom_cb }, { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "minus", NULL, (GCallback)draw_zoom_cb }, { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "Z", NULL, (GCallback)zoom_to_cb }, - { "Zoom0.25", NULL, N_("0.25"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom0.5", NULL, N_("0.5"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom1", NULL, N_("1"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom2", NULL, N_("2"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom4", NULL, N_("4"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom8", NULL, N_("8"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom16", NULL, N_("16"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom32", NULL, N_("32"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom64", NULL, N_("64"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom128", NULL, N_("128"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom256", NULL, N_("256"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom512", NULL, N_("512"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom1024", NULL, N_("1024"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom2048", NULL, N_("2048"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom4096", NULL, N_("4096"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom8192", NULL, N_("8192"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom16384", NULL, N_("16384"), NULL, NULL, (GCallback)draw_zoom_cb }, - { "Zoom32768", NULL, N_("32768"), NULL, NULL, (GCallback)draw_zoom_cb }, { "PanNorth", NULL, N_("Pan _North"), "Up", NULL, (GCallback)draw_pan_cb }, { "PanEast", NULL, N_("Pan _East"), "Right", NULL, (GCallback)draw_pan_cb }, { "PanSouth", NULL, N_("Pan _South"), "Down", NULL, (GCallback)draw_pan_cb }, @@ -3188,12 +3757,17 @@ static GtkActionEntry entries[] = { { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb }, { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb }, { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb }, + { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL }, { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb }, { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb }, { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb }, }; +static GtkActionEntry entries_gpsbabel[] = { + { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml }, +}; + /* Radio items */ /* FIXME use VIEWPORT_DRAWMODE values */ static GtkRadioActionEntry mode_entries[] = { @@ -3247,6 +3821,15 @@ static void window_create_ui( VikWindow *window ) gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window); gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window); + // Use this to see if GPSBabel is available: + if ( a_babel_device_list ) { + // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file + if ( gtk_ui_manager_add_ui_from_string ( uim, + "", + -1, &error ) ) + gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window ); + } + icon_factory = gtk_icon_factory_new (); gtk_icon_factory_add_default (icon_factory); @@ -3282,6 +3865,8 @@ static void window_create_ui( VikWindow *window ) action.callback = (GCallback)menu_addlayer_cb; gtk_action_group_add_actions(action_group, &action, 1, window); + g_free ( (gchar*)action.label ); + if ( vik_layer_get_interface(i)->tools_count ) { gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE); gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE); @@ -3308,6 +3893,26 @@ static void window_create_ui( VikWindow *window ) // Overwrite with actual number to use radio->value = ntools; } + + GtkActionEntry action_dl; + gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name ); + gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults", + vik_layer_get_interface(i)->name, + layername, + GTK_UI_MANAGER_MENUITEM, FALSE); + g_free (layername); + + // For default layers use action names of the form 'Layer' + // This is to avoid clashing with just the layer name used above for the tool actions + action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL); + action_dl.stock_id = NULL; + action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator + action_dl.accelerator = NULL; + action_dl.tooltip = NULL; + action_dl.callback = (GCallback)layer_defaults_cb; + gtk_action_group_add_actions(action_group, &action_dl, 1, window); + g_free ( (gchar*)action_dl.name ); + g_free ( (gchar*)action_dl.label ); } g_object_unref (icon_factory); @@ -3323,6 +3928,10 @@ static void window_create_ui( VikWindow *window ) g_object_set(action, "sensitive", FALSE, NULL); } } + + // This is done last so we don't need to track the value of mid anymore + vik_ext_tools_add_action_items ( window, window->uim, action_group, mid ); + window->action_group = action_group; accel_group = gtk_ui_manager_get_accel_group (uim); @@ -3343,7 +3952,7 @@ static struct { { &zoom_18_pixbuf, "vik-icon-zoom" }, { &ruler_18_pixbuf, "vik-icon-ruler" }, { &select_18_pixbuf, "vik-icon-select" }, - { &begintr_18_pixbuf, "vik-icon-Begin Track" }, + { &vik_new_route_18_pixbuf, "vik-icon-Create Route" }, { &route_finder_18_pixbuf, "vik-icon-Route Finder" }, { &demdl_18_pixbuf, "vik-icon-DEM Download" }, { &showpic_18_pixbuf, "vik-icon-Show Picture" }, @@ -3390,14 +3999,14 @@ void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl ) vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) ); } -gpointer vik_window_get_selected_tracks ( VikWindow *vw ) +GHashTable *vik_window_get_selected_tracks ( VikWindow *vw ) { return vw->selected_tracks; } -void vik_window_set_selected_tracks ( VikWindow *vw, gpointer gl, gpointer vtl ) +void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl ) { - vw->selected_tracks = gl; + vw->selected_tracks = ght; vw->containing_vtl = vtl; /* Clear others */ vw->selected_vtl = NULL; @@ -3426,14 +4035,14 @@ void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl ) vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) ); } -gpointer vik_window_get_selected_waypoints ( VikWindow *vw ) +GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw ) { return vw->selected_waypoints; } -void vik_window_set_selected_waypoints ( VikWindow *vw, gpointer gl, gpointer vtl ) +void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl ) { - vw->selected_waypoints = gl; + vw->selected_waypoints = ght; vw->containing_vtl = vtl; /* Clear others */ vw->selected_vtl = NULL; @@ -3483,3 +4092,8 @@ gboolean vik_window_clear_highlight ( VikWindow *vw ) } return need_redraw; } + +GThread *vik_window_get_thread ( VikWindow *vw ) +{ + return vw->thread; +}