]> git.street.me.uk Git - andy/viking.git/blobdiff - src/vikwindow.c
Add tooltip to new zoom level combo
[andy/viking.git] / src / vikwindow.c
index 4818ee171b03bd1eae90949892255afc5fcaabd6..22bb63cdf440715d45c839dfaf9e97340c60d7de 100644 (file)
@@ -37,6 +37,7 @@
 #include "icons/icons.h"
 #include "vikexttools.h"
 #include "garminsymbols.h"
+#include "vikmapslayer.h"
 
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -101,6 +102,7 @@ static void draw_release ( VikWindow *vw, GdkEventButton *event );
 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
+static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
 
 static void draw_status ( VikWindow *vw );
 
@@ -154,6 +156,7 @@ struct _VikWindow {
   VikStatusbar *viking_vs;
 
   GtkToolbar *toolbar;
+  GtkComboBox *tb_zoom_combo;
 
   GtkItemFactory *item_factory;
 
@@ -341,7 +344,7 @@ static void open_window ( VikWindow *vw, GSList *files )
     if (vw != NULL && check_file_magic_vik ( file_name ) ) {
       VikWindow *newvw = vik_window_new_window ();
       if (newvw)
-       vik_window_open_file ( newvw, file_name, change_fn );
+        vik_window_open_file ( newvw, file_name, TRUE );
     }
     else {
       vik_window_open_file ( vw, file_name, change_fn );
@@ -401,6 +404,58 @@ static void window_class_init ( VikWindowClass *klass )
 
 }
 
+static void set_toolbar_zoom ( VikWindow *vw, gdouble mpp )
+{
+  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 );
+}
+
+static void zoom_changed ( GtkComboBox *combo, VikWindow *vw )
+{
+  gint active = gtk_combo_box_get_active ( combo );
+
+  // 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 );
+  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 );
+    // Force drawing update
+    draw_update ( vw );
+  }
+}
+
+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");
+  /* Create tooltip */
+  GtkTooltips *tooltips = gtk_tooltips_new ();
+  gtk_tooltips_set_tip ( tooltips, GTK_WIDGET (combo), _("Select zoom level"), NULL);
+  return zoom_combo;
+}
+
 static void window_init ( VikWindow *vw )
 {
   GtkWidget *main_vbox;
@@ -443,6 +498,15 @@ static void window_init ( VikWindow *vw )
 
   vik_ext_tools_add_menu_items ( vw, vw->uim );
 
+  vw->tb_zoom_combo = GTK_COMBO_BOX(create_zoom_combo_all_levels());
+
+  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) );
+
   g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
 
   g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
@@ -454,7 +518,8 @@ static void window_init ( VikWindow *vw )
   g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
   g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
 
-  g_signal_connect_swapped (G_OBJECT (vw->viking_vvp), "key_press_event", G_CALLBACK (key_press_event), 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);
 
@@ -480,8 +545,57 @@ static VikWindow *window_new ()
   return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
 }
 
+/**
+ * Update the displayed map
+ *  Only update the top most visible map layer
+ *  ATM this assumes (as per defaults) the top most map has full alpha setting
+ *   such that other other maps even though they may be active will not be seen
+ *  It's more complicated to work out which maps are actually visible due to alpha settings
+ *   and overkill for this simple refresh method.
+ */
+static void simple_map_update ( VikWindow *vw, gboolean only_new )
+{
+  // Find the most relevent single map layer to operate on
+  VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
+  if ( vl )
+       vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
+}
+
+/**
+ * This is the global key press handler
+ *  Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
+ */
 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
 {
+  // The keys handled here are not in the menuing system for a couple of reasons:
+  //  . Keeps the menu size compact (alebit at expense of discoverably)
+  //  . Allows differing key bindings to perform the same actions
+
+  // First decide if key events are related to the maps layer
+  gboolean map_download = FALSE;
+  gboolean map_download_only_new = TRUE; // Only new or reload
+
+  GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
+
+  // Standard 'Refresh' keys: F5 or Ctrl+r
+  // Note 'F5' is actually handled via draw_refresh_cb() later on
+  //  (not 'R' it's 'r' notice the case difference!!)
+  if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
+       map_download = TRUE;
+       map_download_only_new = TRUE;
+  }
+  // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
+  // Note the use of uppercase R here since shift key has been pressed
+  else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
+           ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
+       map_download = TRUE;
+       map_download_only_new = FALSE;
+  }
+
+  if ( map_download ) {
+    simple_map_update ( vw, map_download_only_new );
+  }
+
   VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
   if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
     gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
@@ -555,7 +669,19 @@ static void draw_sync ( VikWindow *vw )
 {
   vik_viewport_sync(vw->viking_vvp);
   draw_status ( vw );
-  /* other things may be necc here later. */
+}
+
+/*
+ * Split the status update, as sometimes only need to update the tool part
+ *  also on initialization the zoom related stuff is not ready to be used
+ */
+static void draw_status_tool ( VikWindow *vw )
+{
+  if ( vw->current_tool == TOOL_LAYER )
+    // Use tooltip rather than the internal name as the tooltip is i8n
+    vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, vik_layer_get_interface(vw->tool_layer_id)->tools[vw->tool_tool_id].radioActionEntry.tooltip );
+  else
+    vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
 }
 
 static void draw_status ( VikWindow *vw )
@@ -572,13 +698,12 @@ static void draw_status ( VikWindow *vw )
     else
       /* xmpp should be a whole number so don't show useless .000 bit */
       g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
-  if ( vw->current_tool == TOOL_LAYER )
-    // Use tooltip rather than the internal name as the tooltip is i8n
-    vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, vik_layer_get_interface(vw->tool_layer_id)->tools[vw->tool_tool_id].radioActionEntry.tooltip );
-  else
-    vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
 
   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 );  
 }
 
 void vik_window_set_redraw_trigger(VikLayer *vl)
@@ -706,8 +831,21 @@ static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
 
   vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
   vik_coord_to_utm ( &coord, &utm );
-  a_coords_utm_to_latlon ( &utm, &ll );
-  a_coords_latlon_to_string ( &ll, &lat, &lon );
+
+  if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
+    // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
+    //  ZONE[N|S] EASTING NORTHING
+    lat = g_malloc(4*sizeof(gchar));
+    // NB zone is stored in a char but is an actual number
+    g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
+    lon = g_malloc(16*sizeof(gchar));
+    g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
+  }
+  else {
+    a_coords_utm_to_latlon ( &utm, &ll );
+    a_coords_latlon_to_string ( &ll, &lat, &lon );
+  }
+
   /* Change interpolate method according to scale */
   zoom = vik_viewport_get_zoom(vw->viking_vvp);
   if (zoom > 2.0)
@@ -813,7 +951,6 @@ static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
  ********************************************************************************/
 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
 {
-  PangoFontDescription *pfd;
   PangoLayout *pl;
   gchar str[128];
   GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
@@ -914,11 +1051,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);
-
-    pfd = pango_font_description_from_string ("Sans 8"); // FIXME: settable option? global variable?
-    pango_layout_set_font_description (pl, pfd);
-    pango_font_description_free (pfd);
-
+    pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
     pango_layout_set_text(pl, "N", -1);
     gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
 
@@ -1131,11 +1264,13 @@ static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_st
     ruler_deactivate ( vl, s );
     return TRUE;
   }
+  // Regardless of whether we used it, return false so other GTK things may use it
   return FALSE;
 }
 
-static VikToolInterface ruler_tool = 
-  { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>R", N_("Ruler Tool"), 2 },
+static VikToolInterface ruler_tool =
+  // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
+  { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
     (VikToolConstructorFunc) ruler_create,
     (VikToolDestructorFunc) ruler_destroy,
     (VikToolActivationFunc) NULL,
@@ -1617,7 +1752,7 @@ static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
   draw_update ( vw );
 }
 
-void draw_goto_cb ( GtkAction *a, VikWindow *vw )
+static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
 {
   VikCoord new_center;
 
@@ -1646,6 +1781,15 @@ void draw_goto_cb ( GtkAction *a, VikWindow *vw )
   draw_update ( vw );
 }
 
+/**
+ * Refresh maps displayed
+ */
+static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
+{
+  // Only get 'new' maps
+  simple_map_update ( vw, TRUE );
+}
+
 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
 {
   gint type;
@@ -1692,8 +1836,19 @@ static void help_help_cb ( GtkAction *a, VikWindow *vw )
 #if GTK_CHECK_VERSION (2, 14, 0)
   gchar *uri;
   uri = g_strdup_printf("ghelp:%s", PACKAGE);
-  gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, NULL);
+  GError *error = NULL;
+  gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
+  if ( !show && !error )
+    // No error to show, so unlikely this will get called
+    a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
+  else if ( error ) {
+    // Main error path
+    a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Help is not available because: %s.\nEnsure a Mime Type ghelp handler program is installed (e.g. yelp)."), error->message );
+    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 */
 }
@@ -1773,10 +1928,6 @@ static toolbox_tools_t* toolbox_create(VikWindow *vw)
   vt->n_tools = 0;
   vt->active_tool = -1;
   vt->vw = vw;
-  if (!vw->viking_vvp) {
-    g_critical("no viewport found.");
-    exit(1);
-  }
   return vt;
 }
 
@@ -1813,7 +1964,7 @@ static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
 
   if (tool == vt->n_tools) {
     g_critical("trying to activate a non-existent tool...");
-    exit(1);
+    return;
   }
   /* is the tool already active? */
   if (vt->active_tool == tool) {
@@ -1925,7 +2076,7 @@ static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
       }
     }
   }
-  draw_status ( vw );
+  draw_status_tool ( vw );
 }
 
 static void window_set_filename ( VikWindow *vw, const gchar *filename )
@@ -2078,6 +2229,15 @@ void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean chang
     case LOAD_TYPE_UNSUPPORTED_FAILURE:
       a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
       break;
+    case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
+    {
+      // Since we can process .vik files with issues just show a warning in the status bar
+      // Not that a user can do much about it... or tells them what this issue is yet...
+      gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
+      vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
+      g_free ( msg );
+    }
+      // No break, carry on to show any data
     case LOAD_TYPE_VIK_SUCCESS:
     {
       GtkWidget *mode_button;
@@ -2186,11 +2346,28 @@ static void load_file ( GtkAction *a, VikWindow *vw )
     else {
       files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
       gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
-      
+      gboolean first_vik_file = TRUE;
       cur_file = files;
       while ( cur_file ) {
+
         gchar *file_name = cur_file->data;
-        vik_window_open_file ( vw, file_name, change_fn );
+        if ( newwindow && check_file_magic_vik ( file_name ) ) {
+          // Load first of many .vik files in current window
+          if ( first_vik_file ) {
+            vik_window_open_file ( vw, file_name, TRUE );
+            first_vik_file = FALSE;
+          }
+          else {
+            // Load each subsequent .vik file in a separate window
+            VikWindow *newvw = vik_window_new_window ();
+            if (newvw)
+              vik_window_open_file ( newvw, file_name, TRUE );
+          }
+        }
+        else
+          // Other file types
+          vik_window_open_file ( vw, file_name, change_fn );
+
         g_free (file_name);
         cur_file = g_slist_next (cur_file);
       }
@@ -2213,6 +2390,20 @@ static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
                                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                      GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
                                      NULL);
+    GtkFileFilter *filter;
+    filter = gtk_file_filter_new ();
+    gtk_file_filter_set_name( filter, _("All") );
+    gtk_file_filter_add_pattern ( filter, "*" );
+    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
+
+    filter = gtk_file_filter_new ();
+    gtk_file_filter_set_name( filter, _("Viking") );
+    gtk_file_filter_add_pattern ( filter, "*.vik" );
+    gtk_file_filter_add_pattern ( filter, "*.viking" );
+    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
+    // Default to a Viking file
+    gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
+
     gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
     gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
   }
@@ -2419,6 +2610,23 @@ static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h,
   gdouble old_xmpp, old_ympp;
   GError *error = NULL;
 
+  GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
+                                               GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                               GTK_MESSAGE_INFO,
+                                               GTK_BUTTONS_NONE,
+                                              _("Generating image file...") );
+
+  g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
+  // Ensure dialog shown
+  gtk_widget_show_all ( msgbox );
+  // Try harder...
+  vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
+  while ( gtk_events_pending() )
+    gtk_main_iteration ();
+  // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
+  // At least the empty box can give a clue something's going on + the statusbar msg...
+  // Windows version under Wine OK!
+
   /* backup old zoom & set new */
   old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
   old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
@@ -2432,14 +2640,30 @@ static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h,
 
   /* save buffer as file. */
   pixbuf_to_save = gdk_pixbuf_get_from_drawable ( NULL, GDK_DRAWABLE(vik_viewport_get_pixmap ( vw->viking_vvp )), NULL, 0, 0, 0, 0, w, h);
+  if ( !pixbuf_to_save ) {
+    g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
+    gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
+    goto cleanup;
+  }
+
   gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
   if (error)
   {
     g_warning("Unable to write to file %s: %s", fn, error->message );
+    gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
     g_error_free (error);
   }
+  else {
+    // Success
+    gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
+  }
   g_object_unref ( G_OBJECT(pixbuf_to_save) );
 
+ cleanup:
+  vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
+  gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
+  gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
+
   /* pretend like nothing happened ;) */
   vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
   vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
@@ -2520,7 +2744,10 @@ static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButto
 {
   VikWindow *vw = VIK_WINDOW(pass_along[0]);
   GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
-  GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]);
+
+  gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
+  gdouble zoom = pow (2, active-2 );
+
   gdouble width_min, width_max, height_min, height_max;
   gint width, height;
 
@@ -2528,8 +2755,8 @@ static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButto
   gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
 
   /* TODO: support for xzoom and yzoom values */
-  width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin );
-  height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin );
+  width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
+  height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
 
   if ( width > width_max || width < width_min || height > height_max || height < height_min )
     a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
@@ -2541,11 +2768,14 @@ static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButto
 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
 {
   GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
-  GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]);
+
+  gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
+  gdouble zoom = pow (2, active-2 );
+
   gchar *label_text;
   gdouble w, h;
-  w = gtk_spin_button_get_value(width_spin) * gtk_spin_button_get_value(zoom_spin);
-  h = gtk_spin_button_get_value(height_spin) * gtk_spin_button_get_value(zoom_spin);
+  w = gtk_spin_button_get_value(width_spin) * zoom;
+  h = gtk_spin_button_get_value(height_spin) * zoom;
   if (pass_along[4]) /* save many images; find TOTAL area covered */
   {
     w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
@@ -2568,7 +2798,84 @@ static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointe
   g_free ( label_text );
 }
 
-static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_image_only )
+/*
+ * Get an allocated filename (or directory as specified)
+ */
+static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
+{
+  gchar *fn = NULL;
+  if ( one_image_only )
+  {
+    // Single file
+    if (!vw->save_img_dia) {
+      vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
+                                                      GTK_WINDOW(vw),
+                                                      GTK_FILE_CHOOSER_ACTION_SAVE,
+                                                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                                      GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+                                                      NULL);
+
+      GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
+      /* Add filters */
+      GtkFileFilter *filter;
+      filter = gtk_file_filter_new ();
+      gtk_file_filter_set_name ( filter, _("All") );
+      gtk_file_filter_add_pattern ( filter, "*" );
+      gtk_file_chooser_add_filter ( chooser, filter );
+
+      filter = gtk_file_filter_new ();
+      gtk_file_filter_set_name ( filter, _("JPG") );
+      gtk_file_filter_add_mime_type ( filter, "image/jpeg");
+      gtk_file_chooser_add_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 );
+
+      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 );
+    }
+
+    if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
+      fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
+      if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
+       if ( ! a_dialog_yes_or_no ( GTK_WINDOW(vw->save_img_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
+         fn = NULL;
+    }
+    gtk_widget_hide ( vw->save_img_dia );
+  }
+  else {
+    // A directory
+    // For some reason this method is only written to work in UTM...
+    if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
+      a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
+      return fn;
+    }
+
+    if (!vw->save_img_dir_dia) {
+      vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
+                                                          GTK_WINDOW(vw),
+                                                          GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+                                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                                          GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+                                                          NULL);
+      gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
+      gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
+    }
+
+    if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
+      fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
+    }
+    gtk_widget_hide ( vw->save_img_dir_dia );
+  }
+  return fn;
+}
+
+static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
 {
   /* todo: default for answers inside VikWindow or static (thruout instance) */
   GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
@@ -2582,21 +2889,30 @@ static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_im
   GtkWidget *png_radio, *jpeg_radio;
   GtkWidget *current_window_button;
   gpointer current_window_pass_along[7];
-  GtkWidget *zoom_label, *zoom_spin;
+  GtkWidget *zoom_label, *zoom_combo;
   GtkWidget *total_size_label;
 
   /* only used if (!one_image_only) */
   GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
 
-
   width_label = gtk_label_new ( _("Width (pixels):") );
-  width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 5000, 10, 100, 0 )), 10, 0 );
+  width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
   height_label = gtk_label_new ( _("Height (pixels):") );
-  height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 5000, 10, 100, 0 )), 10, 0 );
-
+  height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
+#ifdef WINDOWS
+  GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
+#endif
   zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
   /* TODO: separate xzoom and yzoom factors */
-  zoom_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vik_viewport_get_xmpp(vw->viking_vvp), VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM/2.0, 1, 100, 0 )), 16, 0);
+  zoom_combo = create_zoom_combo_all_levels();
+
+  gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
+  gint active = 2 + ( log (mpp) / log (2) );
+
+  // Can we not hard code size here?
+  if ( active > 17 )
+    active = 17;
+  gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
 
   total_size_label = gtk_label_new ( NULL );
 
@@ -2604,7 +2920,7 @@ static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_im
   current_window_pass_along [0] = vw;
   current_window_pass_along [1] = width_spin;
   current_window_pass_along [2] = height_spin;
-  current_window_pass_along [3] = zoom_spin;
+  current_window_pass_along [3] = zoom_combo;
   current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
   current_window_pass_along [5] = NULL;
   current_window_pass_along [6] = total_size_label;
@@ -2620,17 +2936,19 @@ static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_im
   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);
+#ifdef WINDOWS
+  gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 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_spin, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_combo, FALSE, FALSE, 0);
 
   if ( ! one_image_only )
   {
     GtkWidget *tiles_width_label, *tiles_height_label;
 
-
     tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
     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:") );
@@ -2648,7 +2966,7 @@ static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_im
   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 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_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 );
 
   draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
 
@@ -2659,22 +2977,32 @@ static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_im
   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
   {
     gtk_widget_hide ( GTK_WIDGET(dialog) );
+
+    gchar *fn = draw_image_filename ( vw, one_image_only );
+    if ( !fn )
+      return;
+
+    gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
+    gdouble zoom = pow (2, active-2 );
+
     if ( one_image_only )
       save_image_file ( vw, fn, 
                       vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
                       vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
-                      gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */
+                      zoom,
                       vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
     else {
       // NB is in UTM mode ATM
       save_image_dir ( vw, fn,
                        vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
                        vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
-                       gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */
+                       zoom,
                        vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
                        gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
                        gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
     }
+
+    g_free ( fn );
   }
   gtk_widget_destroy ( GTK_WIDGET(dialog) );
 }
@@ -2682,64 +3010,12 @@ static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_im
 
 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
 {
-  gchar *fn;
-  if (!vw->save_img_dia) {
-    vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
-                                     GTK_WINDOW(vw),
-                                     GTK_FILE_CHOOSER_ACTION_SAVE,
-                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                     GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
-                                     NULL);
-    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 );
-  }
-
-  while ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT )
-  {
-    fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
-    if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(vw->save_img_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
-    {
-      draw_to_image_file ( vw, fn, TRUE );
-      break;
-    }
-    g_free(fn);
-    fn = NULL;
-  }
-  gtk_widget_hide ( vw->save_img_dia );
+  draw_to_image_file ( vw, TRUE );
 }
 
 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
 {
-  gchar *fn = NULL;
-  
-  if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
-    a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
-    return;
-  }
-
-  if (!vw->save_img_dir_dia) {
-    vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
-                                     GTK_WINDOW(vw),
-                                     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
-                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
-                                     NULL);
-    gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
-    gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
-  }
-  
-  while ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT )
-  {
-    fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
-    if ( fn )
-    {
-      draw_to_image_file ( vw, fn, FALSE );
-      g_free(fn);
-      fn = NULL;
-      break;
-    }
-  }
-  gtk_widget_hide ( vw->save_img_dir_dia );
+  draw_to_image_file ( vw, FALSE );
 }
 
 #if GTK_CHECK_VERSION(2,10,0)
@@ -2899,6 +3175,7 @@ static GtkActionEntry entries[] = {
   { "GotoSearch", GTK_STOCK_JUMP_TO,     N_("Go to _Location..."),           NULL,         N_("Go to address/place using text search"),        (GCallback)goto_address       },
   { "GotoLL",    GTK_STOCK_JUMP_TO,      N_("_Go to Lat/Lon..."),           NULL,         N_("Go to arbitrary lat/lon coordinate"),         (GCallback)draw_goto_cb          },
   { "GotoUTM",   GTK_STOCK_JUMP_TO,      N_("Go to UTM..."),                  NULL,         N_("Go to arbitrary UTM coordinate"),               (GCallback)draw_goto_cb          },
+  { "Refresh",   GTK_STOCK_REFRESH,      N_("_Refresh"),                      "F5",         N_("Refresh any maps displayed"),               (GCallback)draw_refresh_cb       },
   { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."),       NULL,         NULL,                                           (GCallback)set_highlight_color   },
   { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."),      NULL,         NULL,                                           (GCallback)set_bg_color          },
   { "ZoomIn",    GTK_STOCK_ZOOM_IN,      N_("Zoom _In"),                   "<control>plus", NULL,                                           (GCallback)draw_zoom_cb          },
@@ -2952,7 +3229,7 @@ static GtkRadioActionEntry mode_entries[] = {
 };
 
 static GtkToggleActionEntry toggle_entries[] = {
-  { "ShowScale",      NULL,                 N_("Show _Scale"),               "F5",         N_("Show Scale"),                              (GCallback)set_draw_scale, TRUE },
+  { "ShowScale",      NULL,                 N_("Show _Scale"),               "<shift>F5",  N_("Show Scale"),                              (GCallback)set_draw_scale, TRUE },
   { "ShowCenterMark", NULL,                 N_("Show _Center Mark"),         "F6",         N_("Show Center Mark"),                        (GCallback)set_draw_centermark, TRUE },
   { "ShowHighlight",  GTK_STOCK_UNDERLINE,  N_("Show _Highlight"),           "F7",         N_("Show Highlight"),                          (GCallback)set_draw_highlight, TRUE },
   { "FullScreen",     GTK_STOCK_FULLSCREEN, N_("_Full Screen"),              "F11",        N_("Activate full screen mode"),               (GCallback)full_screen_cb, FALSE },
@@ -3161,7 +3438,7 @@ gpointer vik_window_get_selected_track ( VikWindow *vw )
   return vw->selected_track;
 }
 
-void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl, gpointer name )
+void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
 {
   vw->selected_track = vt;
   vw->containing_vtl = vtl;
@@ -3195,7 +3472,7 @@ gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
   return vw->selected_waypoint;
 }
 
-void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl, gpointer name )
+void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
 {
   vw->selected_waypoint = vwp;
   vw->containing_vtl    = vtl;