]> git.street.me.uk Git - andy/viking.git/blobdiff - src/vikwindow.c
Refactor: update OSM track upload
[andy/viking.git] / src / vikwindow.c
index 51772ad126c8b7d107542c49f34dc372dc66a238..324d0bbd91fc1c9a9bd2dc6da70416e6f1c9829d 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
  * Copyright (C) 2005-2006, Alex Foobarian <foobarian@gmail.com>
- * Copyright (C) 2012, Rob Norris <rw_norris@hotmail.com>
+ * Copyright (C) 2012-2013, Rob Norris <rw_norris@hotmail.com>
  *
  * 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
@@ -86,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, vik_statusbar_type_t vs_type, const gchar *message );
 static void destroy_window ( GtkWidget *widget,
                              gpointer   data );
 
@@ -179,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;
@@ -215,7 +215,6 @@ enum {
 enum {
   VW_NEWWINDOW_SIGNAL,
   VW_OPENWINDOW_SIGNAL,
-  VW_STATUSBAR_UPDATE_SIGNAL,
   VW_LAST_SIGNAL
 };
 
@@ -244,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, vs_type, message );
-}
+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
@@ -283,10 +295,13 @@ static void destroy_window ( GtkWidget *widget,
       gtk_main_quit ();
 }
 
-static void statusbar_update ( VikWindow *vw, vik_statusbar_type_t vs_type, const gchar *message )
-{
-  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 ()
 {
@@ -300,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;
@@ -312,6 +359,100 @@ 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_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 */
@@ -319,7 +460,7 @@ static void open_window ( VikWindow *vw, GSList *files )
   while ( cur_file ) {
     // Only open a new window if a viking file
     gchar *file_name = cur_file->data;
-    if (vw != NULL && check_file_magic_vik ( file_name ) ) {
+    if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
       VikWindow *newvw = vik_window_new_window ();
       if (newvw)
         vik_window_open_file ( newvw, file_name, TRUE );
@@ -364,6 +505,12 @@ static void window_finalize ( GObject *gob )
   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);
 }
@@ -376,7 +523,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, g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
 
   object_class = G_OBJECT_CLASS (klass);
 
@@ -476,6 +622,66 @@ static gint zoom_popup_handler (GtkWidget *widget)
   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, "" ) ) {
+          // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
+          //  thus need to convert the text into a plain string
+          gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
+          if ( filename )
+            filenames = g_slist_append ( filenames, filename );
+        }
+        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;
@@ -499,15 +705,28 @@ static void vik_window_init ( VikWindow *vw )
   gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
 
   vw->filename = 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);
@@ -539,8 +758,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 );
@@ -554,11 +771,55 @@ static void vik_window_init ( VikWindow *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]
@@ -675,6 +936,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;
 }
 
@@ -2229,7 +2521,7 @@ 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(vw->viking_vvp)->window, vw->busy_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();
@@ -2239,14 +2531,14 @@ 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(vw->viking_vvp)->window, vw->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 )
 {
   vik_window_set_busy_cursor ( vw );
-
-  switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
+  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.") );
@@ -2550,7 +2842,7 @@ static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const g
       // Show some progress
       if ( this_success ) {
         export_count++;
-        gchar *message = g_strconcat ( _("Exporting to file: "), fn, NULL );
+        gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
         vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
         while ( gtk_events_pending() )
           gtk_main_iteration ();
@@ -2583,25 +2875,22 @@ static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *ex
     return;
   }
 
-  GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Export to directory"),
+  GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
                                                     GTK_WINDOW(vw),
-                                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
                                                     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(dialog)->vbox), 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_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
+  gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
+  gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
 
   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) );
+    gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
     gtk_widget_destroy ( dialog );
     if ( dir ) {
       if ( !export_to ( vw, gl, vft, dir, extension ) )
@@ -2625,6 +2914,46 @@ static void export_to_kml ( GtkAction *a, VikWindow *vw )
   export_to_common ( vw, FILE_TYPE_KML, ".kml" );
 }
 
+#if !GLIB_CHECK_VERSION(2,26,0)
+typedef struct stat GStatBuf;
+#endif
+
+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
@@ -2640,12 +2969,10 @@ static void acquire_from_file ( GtkAction *a, VikWindow *vw )
   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, NULL, NULL );
+  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 )
@@ -2681,6 +3008,12 @@ static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
 }
 #endif
 
+static void acquire_from_url ( GtkAction *a, VikWindow *vw )
+{
+  vik_datasource_url_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
+  a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_url_interface, NULL, NULL );
+}
+
 static void goto_default_location( GtkAction *a, VikWindow *vw)
 {
   struct LatLon ll;
@@ -2721,12 +3054,14 @@ 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 );
 
-  GList *iter = g_list_first ( layers );
-  while ( iter ) {
+  if ( !layers )
+    return;
+
+  while ( layers ) {
     // Reset the individual waypoints themselves due to the preferences change
-    VikTrwLayer *vtl = VIK_TRW_LAYER(VIK_LAYER(layers->data));
+    VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
     vik_trw_layer_reset_waypoints ( vtl );
-       iter = g_list_next ( iter );
+    layers = g_list_next ( layers );
   }
 
   g_list_free ( layers );
@@ -2763,7 +3098,10 @@ static void default_location_cb ( GtkAction *a, VikWindow *vw )
       VIK_LAYER_WIDGET_SPINBUTTON,
       NULL,
       NULL,
-      NULL },
+      NULL,
+      NULL,
+      NULL,
+    },
   };
   VikLayerParam pref_lon[] = {
     { VIK_LAYER_NUM_TYPES,
@@ -2774,7 +3112,10 @@ static void default_location_cb ( GtkAction *a, VikWindow *vw )
       VIK_LAYER_WIDGET_SPINBUTTON,
       NULL,
       NULL,
-      NULL },
+      NULL,
+      NULL,
+      NULL,
+    },
   };
 
   /* Get current center */
@@ -3162,6 +3503,9 @@ static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
   png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
   jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
 
+  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);
+
   if ( ! vw->draw_image_save_as_png )
     gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
 
@@ -3173,8 +3517,6 @@ static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
   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_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);
 
@@ -3380,9 +3722,7 @@ static GtkActionEntry entries[] = {
   { "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    },
@@ -3393,11 +3733,13 @@ static GtkActionEntry entries[] = {
 #ifdef VIK_CONFIG_GEOTAG
   { "AcquireGeotag", NULL,               N_("From Geotagged _Images..."), NULL,         N_("Create waypoints from geotagged images"),       (GCallback)acquire_from_geotag   },
 #endif
+  { "AcquireURL", NULL,                  N_("From _URL..."),              NULL,         N_("Get a file from a URL"),                        (GCallback)acquire_from_url },
 #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"),                         "<control>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  },
   { "Print",    GTK_STOCK_PRINT,        N_("_Print..."),          NULL,         N_("Print maps"), (GCallback)print_cb },
@@ -3536,6 +3878,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);
@@ -3564,10 +3908,12 @@ static void window_create_ui( VikWindow *window )
     }
 
     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,
-                         g_strdup_printf("Layer%s", vik_layer_get_interface(i)->fixed_layer_name),
+                         layername,
                          GTK_UI_MANAGER_MENUITEM, FALSE);
+    g_free (layername);
 
     // For default layers use action names of the form 'Layer<LayerName>'
     // This is to avoid clashing with just the layer name used above for the tool actions
@@ -3578,6 +3924,8 @@ static void window_create_ui( VikWindow *window )
     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);