Merge branch 'develop' into features/r2_11_2_alphafold/JAL-629
authorBen Soares <b.soares@dundee.ac.uk>
Wed, 22 Feb 2023 12:40:43 +0000 (12:40 +0000)
committerBen Soares <b.soares@dundee.ac.uk>
Wed, 22 Feb 2023 12:40:43 +0000 (12:40 +0000)
1  2 
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/bin/Cache.java
src/jalview/bin/Jalview.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/StructureChooser.java
src/jalview/io/FileLoader.java

@@@ -32,7 -32,20 +32,20 @@@ action.load_project = Load Projec
  action.save_project = Save Project
  action.save_project_as = Save Project as...
  action.quit = Quit
- label.quit_jalview = Quit Jalview?
+ action.force_quit = Force quit
+ label.quit_jalview = Are you sure you want to quit Jalview?
+ label.wait_for_save = Wait for save
+ label.unsaved_changes = There are unsaved changes.
+ label.unsaved_alignments = There are unsaved alignments.
+ label.save_in_progress = Some files are still saving:
+ label.confirm_quit_viewer = An external viewer is still open.  Close the external window as well?
+ label.confirm_quit_viewers = External viewers are still open.  Close these external windows as well?
+ label.unknown = Unknown
+ label.quit_after_saving = Jalview will quit after saving.
+ label.all_saved = All files saved.
+ label.quitting_bye = Quitting, bye!
+ action.wait = Wait
+ action.cancel_quit = Cancel quit
  action.expand_views = Expand Views
  action.gather_views = Gather Views
  action.page_setup = Page Setup...
@@@ -203,6 -216,7 +216,7 @@@ label.colourScheme_turnpropensity = Tur
  label.colourScheme_buriedindex = Buried Index
  label.colourScheme_purine/pyrimidine = Purine/Pyrimidine
  label.colourScheme_nucleotide = Nucleotide
+ label.colourScheme_nucleotideambiguity = Nucleotide Ambiguity
  label.colourScheme_t-coffeescores = T-Coffee Scores
  label.colourScheme_rnahelices = By RNA Helices
  label.colourScheme_sequenceid = Sequence ID Colour
@@@ -503,6 -517,7 +517,7 @@@ label.delete_gaps = Delete {0} gap
  label.sequence_details = Sequence Details
  label.viewer_help = {0} Help
  label.close_viewer = Close Viewer
+ label.close_viewers = Close Viewers
  label.confirm_close_viewer = This will close Jalview''s connection to {0}.<br>Do you want to close the {1} window as well?
  label.all = All
  label.sort_by = Sort alignment by
@@@ -1426,4 -1441,3 +1441,4 @@@ label.memory_example_text = Maximum mem
  label.memory_example_tooltip = The memory allocated to Jalview is the smaller of the percentage of physical memory (default 90%) and the maximum absolute memory (default 32GB). If your computer's memory cannot be ascertained then the maximum absolute memory defaults to 8GB (if not customised).<br>Jalview will always try and reserve 512MB for the OS and at least 512MB for itself.
  warning.wrong_jvm_version_title = Wrong Java Version
  warning.wrong_jvm_version_message = The Java version being used (Java {0}) may lead to problems.\nThis installation of Jalview should be used with Java {1}.
 +label.alphafold_reliability = Alphafold Reliability
@@@ -32,7 -32,20 +32,20 @@@ action.load_project = Cargar proyect
  action.save_project = Guardar proyecto
  action.save_project_as = Guardar proyecto como...
  action.quit = Salir
- label.quit_jalview = Salir de Jalview?
+ action.force_quit = Forzar la salida
+ label.quit_jalview = ¿Estás seguro de que quieres salir de Jalview?
+ label.wait_for_save = Esperar a guardar
+ label.unsaved_changes = Hay cambios sin guardar.
+ label.unsaved_alignments = Hay alineamientos sin guardar.
+ label.save_in_progress = Algunos archivos aún se están guardando:
+ label.confirm_quit_viewer = Un visualizador externo todavía está abierto. ¿Cerrar también la ventana externa?
+ label.confirm_quit_viewers = Los visualizadores externos siguen abiertos. ¿Cerrar también estas ventanas externas?
+ label.unknown = desconocido
+ label.quit_after_saving = Jalview se cerrará después de guardar.
+ label.all_saved = Todos los archivos guardados.
+ label.quitting_bye = Saliendo ¡chao!
+ action.wait = Espere
+ action.cancel_quit = Cancelar la salida
  action.expand_views = Expandir vistas
  action.gather_views = Capturar vistas
  action.page_setup = Configuración de la página
@@@ -194,6 -207,7 +207,7 @@@ label.colourScheme_turnpropensity = Ten
  label.colourScheme_buriedindex = Índice de encubrimiento
  label.colourScheme_purine/pyrimidine = Purina/Pirimidina
  label.colourScheme_nucleotide = Nucleótido
+ label.colourScheme_nucleotideambiguity = Ambigüedad de nucleótido
  label.colourScheme_t-coffeescores = Puntuación del T-Coffee
  label.colourScheme_rnahelices = Por hélices de RNA
  label.colourScheme_sequenceid = Color de ID de secuencia
@@@ -1082,6 -1096,7 +1096,7 @@@ label.hide_all_al_annotations=Ocultar a
  label.show_all_seq_annotations=Mostrar anotaciones relacionadas con las secuencias
  label.hide_all_seq_annotations=Ocultar anotaciones relacionadas con las secuencias
  label.close_viewer=Cerrar Visualizador
+ label.close_viewers=Cerrar Visualizadores
  label.all_views=Todas las Vistas
  label.search_result=Resultado de búsqueda
  action.fetch_sequences=Recuperar Secuencias...
@@@ -1411,4 -1426,4 +1426,4 @@@ label.memory_example_text = Memoria máx
  label.memory_example_tooltip = La memoria asignada a Jalview es el menor entre el porcentaje de memoria física (predeterminado 90%) y la memoria absoluta máxima (predeterminado 32 GB). Si no se puede determinar la memoria de su computadora, la memoria absoluta máxima predeterminada es de 8 GB (si no está personalizada).<br>Jalview siempre intentará reservar 512 MB para el sistema operativo y al menos 512 MB para sí mismo.
  warning.wrong_jvm_version_title = Versión incorrecta de Java
  warning.wrong_jvm_version_message = La versión de Java que se está utilizando (Java {0}) puede generar problemas.\nEsta instalación de Jalview debe usarse con Java {1}.
 -
 +label.alphafold_reliability = Fiabilidad Alphafold
@@@ -322,16 -322,6 +322,16 @@@ public class Cach
  
    private static boolean propsAreReadOnly = Platform.isJS();
  
 +  public static boolean isPropsAreReadOnly()
 +  {
 +    return propsAreReadOnly;
 +  }
 +
 +  public static void setPropsAreReadOnly(boolean propsAreReadOnly)
 +  {
 +    Cache.propsAreReadOnly = propsAreReadOnly;
 +  }
 +
    private final static String JS_PROPERTY_PREFIX = "jalview_";
  
    /**
      sb.append("Java version: ");
      sb.append(System.getProperty("java.version"));
      sb.append("\n");
+     sb.append("Java platform: ");
      sb.append(System.getProperty("os.arch"));
      sb.append(" ");
      sb.append(System.getProperty("os.name"));
      sb.append(" (");
      sb.append(lafClass);
      sb.append(")\n");
+     appendIfNotNull(sb, "Channel: ",
+             ChannelProperties.getProperty("channel"), "\n", null);
      if (Console.isDebugEnabled()
              || !"release".equals(ChannelProperties.getProperty("channel")))
      {
-       appendIfNotNull(sb, "Channel: ",
-               ChannelProperties.getProperty("channel"), "\n", null);
        appendIfNotNull(sb, "Getdown appdir: ",
                System.getProperty("getdowninstanceappdir"), "\n", null);
        appendIfNotNull(sb, "Getdown appbase: ",
                System.getProperty("getdowninstanceappbase"), "\n", null);
        appendIfNotNull(sb, "Java home: ", System.getProperty("java.home"),
                "\n", "unknown");
+       appendIfNotNull(sb, "Preferences file: ", propertiesFile, "\n",
+               "unknown");
      }
      return sb.toString();
    }
@@@ -45,23 -45,28 +45,29 @@@ import java.util.logging.ConsoleHandler
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
+ import javax.swing.JDialog;
+ import javax.swing.JFrame;
  import javax.swing.JOptionPane;
  import javax.swing.SwingUtilities;
  import javax.swing.UIManager;
  import javax.swing.UIManager.LookAndFeelInfo;
+ import javax.swing.UnsupportedLookAndFeelException;
  
  import com.formdev.flatlaf.FlatLightLaf;
+ import com.formdev.flatlaf.themes.FlatMacLightLaf;
  import com.formdev.flatlaf.util.SystemInfo;
  import com.threerings.getdown.util.LaunchUtil;
  
  //import edu.stanford.ejalbert.launching.IBrowserLaunching;
  import groovy.lang.Binding;
  import groovy.util.GroovyScriptEngine;
 +import jalview.bin.ArgParser.Arg;
  import jalview.ext.so.SequenceOntology;
  import jalview.gui.AlignFrame;
  import jalview.gui.Desktop;
  import jalview.gui.PromptUserConfig;
+ import jalview.gui.QuitHandler;
+ import jalview.gui.QuitHandler.QResponse;
  import jalview.io.AppletFormatAdapter;
  import jalview.io.BioJsHTMLOutput;
  import jalview.io.DataSourceType;
@@@ -272,6 -277,28 +278,28 @@@ public class Jalvie
      if (!Platform.isJS())
      {
        System.setSecurityManager(null);
+       Runtime.getRuntime().addShutdownHook(new Thread()
+       {
+         public void run()
+         {
+           Console.debug("Running shutdown hook");
+           if (QuitHandler.gotQuitResponse() == QResponse.CANCEL_QUIT)
+           {
+             // Got to here by a SIGTERM signal.
+             // Note we will not actually cancel the quit from here -- it's too
+             // late -- but we can wait for saving files.
+             Console.debug("Checking for saving files");
+             QuitHandler.getQuitResponse(false);
+           }
+           else
+           {
+             Console.debug("Nothing more to do");
+           }
+           Console.debug("Exiting, bye!");
+           // shutdownHook cannot be cancelled, JVM will now halt
+         }
+       });
      }
  
      System.out
      System.out.println(System.getProperty("os.arch") + " "
              + System.getProperty("os.name") + " "
              + System.getProperty("os.version"));
      String val = System.getProperty("sys.install4jVersion");
      if (val != null)
      {
        System.out.println("Launcher version: " + val);
      }
  
+     if (Platform.isLinux() && LaunchUtils.getJavaVersion() < 11)
+     {
+       System.setProperty("flatlaf.uiScale", "1");
+     }
      // report Jalview version
      Cache.loadBuildProperties(true);
  
 +    // old ArgsParser
      ArgsParser aparser = new ArgsParser(args);
      boolean headless = false;
  
 +    try
 +    {
 +      Console.initLogger();
 +    } catch (NoClassDefFoundError error)
 +    {
 +      error.printStackTrace();
 +      System.out.println("\nEssential logging libraries not found."
 +              + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
 +      System.exit(0);
 +    }
 +
      String usrPropsFile = aparser.getValue("props");
-     Cache.loadProperties(usrPropsFile); // must do this before
+     Cache.loadProperties(usrPropsFile); // must do this
+                                         // before
      if (usrPropsFile != null)
      {
        System.out.println(
                "CMD [-props " + usrPropsFile + "] executed successfully!");
      }
  
 +    // set log level from cache properties
 +    Console.setLogLevel(Cache.getDefault(Cache.JALVIEWLOGLEVEL, "INFO"));
 +
 +    // new ArgParser
 +    ArgParser argparser = new ArgParser(args); // do this after
 +                                               // Console.initLogger, but TODO
 +                                               // want --props before then
 +                                               // CATCH22
 +
 +    if (argparser.isSet(Arg.HEADLESS))
 +      headless = argparser.getBool(Arg.HEADLESS);
 +    boolean commandsSuccess = Commands.processArgs(argparser, headless);
 +    if (commandsSuccess)
 +    {
 +      Console.info("Successfully completed commands");
 +      if (headless)
 +        System.exit(0);
 +    }
 +    else
 +    {
 +      Console.warn("Error when running commands");
 +      if (headless)
 +        System.exit(1);
 +    }
 +
      if (!Platform.isJS())
      /**
       * Java only
       * @j2sIgnore
       */
      {
 +      if (argparser.isSet(Arg.HEADLESS))
 +      {
 +        headless = argparser.getBool(Arg.HEADLESS);
 +      }
 +
        if (aparser.contains("help") || aparser.contains("h"))
        {
          showUsage();
          System.exit(0);
        }
 -      if (aparser.contains("nodisplay") || aparser.contains("nogui")
 -              || aparser.contains("headless"))
 +      if (headless || aparser.contains("nodisplay")
 +              || aparser.contains("nogui") || aparser.contains("headless"))
        {
          System.setProperty("java.awt.headless", "true");
          headless = true;
      System.setProperty("http.agent",
              "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
  
+     try
+     {
+       Console.initLogger();
+     } catch (
+     NoClassDefFoundError error)
+     {
+       error.printStackTrace();
+       System.out.println("\nEssential logging libraries not found."
+               + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview");
+       System.exit(0);
+     }
      desktop = null;
  
      setLookAndFeel();
      }
  
      String file = null, data = null;
      FileFormatI format = null;
      DataSourceType protocol = null;
      FileLoader fileLoader = new FileLoader(!headless);
  
      String groovyscript = null; // script to execute after all loading is
        System.out.println("No files to open!");
        System.exit(1);
      }
      long progress = -1;
      // Finally, deal with the remaining input data.
      if (file != null)
          }
        }
      }
      AlignFrame startUpAlframe = null;
      // We'll only open the default file if the desktop is visible.
      // And the user
  
        startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
                format);
+       // don't ask to save when quitting if only the startup file has been
+       // opened
+       Console.debug("Resetting up-to-date flag for startup file");
+       startUpAlframe.getViewport().setSavedUpToDate(true);
        // extract groovy arguments before anything else.
      }
  
          Console.error("Could not set requested laf=" + laf);
        }
        break;
-     case "quaqua":
-       lafSet = setQuaquaLookAndFeel();
-       if (!lafSet)
-       {
-         Console.error("Could not set requested laf=" + laf);
-       }
-       break;
-     case "vaqua":
-       lafSet = setVaquaLookAndFeel();
-       if (!lafSet)
-       {
-         Console.error("Could not set requested laf=" + laf);
-       }
-       break;
      case "mac":
        lafSet = setMacLookAndFeel();
        if (!lafSet)
        setSystemLookAndFeel();
        if (Platform.isLinux())
        {
-         setMetalLookAndFeel();
+         setLinuxLookAndFeel();
        }
        if (Platform.isMac())
        {
  
    private static boolean setFlatLookAndFeel()
    {
-     boolean set = setSpecificLookAndFeel("flatlaf light",
-             "com.formdev.flatlaf.FlatLightLaf", false);
-     if (set)
+     boolean set = false;
+     if (SystemInfo.isMacOS)
      {
-       if (Platform.isMac())
+       try
        {
-         System.setProperty("apple.laf.useScreenMenuBar", "true");
-         System.setProperty("apple.awt.application.name",
-                 ChannelProperties.getProperty("app_name"));
-         System.setProperty("apple.awt.application.appearance", "system");
-         if (SystemInfo.isMacFullWindowContentSupported
-                 && Desktop.desktop != null)
-         {
-           Desktop.desktop.getRootPane()
-                   .putClientProperty("apple.awt.fullWindowContent", true);
-           Desktop.desktop.getRootPane()
-                   .putClientProperty("apple.awt.transparentTitleBar", true);
-         }
+         UIManager.setLookAndFeel(
+                 "com.formdev.flatlaf.themes.FlatMacLightLaf");
+         set = true;
+         Console.debug("Using FlatMacLightLaf");
+       } catch (ClassNotFoundException | InstantiationException
+               | IllegalAccessException | UnsupportedLookAndFeelException e)
+       {
+         Console.debug("Exception loading FlatLightLaf", e);
+       }
+       System.setProperty("apple.laf.useScreenMenuBar", "true");
+       System.setProperty("apple.awt.application.name",
+               ChannelProperties.getProperty("app_name"));
+       System.setProperty("apple.awt.application.appearance", "system");
+       if (SystemInfo.isMacFullWindowContentSupported
+               && Desktop.desktop != null)
+       {
+         Console.debug("Setting transparent title bar");
+         Desktop.desktop.getRootPane()
+                 .putClientProperty("apple.awt.fullWindowContent", true);
+         Desktop.desktop.getRootPane()
+                 .putClientProperty("apple.awt.transparentTitleBar", true);
+         Desktop.desktop.getRootPane()
+                 .putClientProperty("apple.awt.fullscreenable", true);
+       }
+       SwingUtilities.invokeLater(() -> {
+         FlatMacLightLaf.setup();
+       });
+       Console.debug("Using FlatMacLightLaf");
+       set = true;
+     }
+     if (!set)
+     {
+       try
+       {
+         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
+         set = true;
+         Console.debug("Using FlatLightLaf");
+       } catch (ClassNotFoundException | InstantiationException
+               | IllegalAccessException | UnsupportedLookAndFeelException e)
+       {
+         Console.debug("Exception loading FlatLightLaf", e);
+       }
+       // Windows specific properties here
+       SwingUtilities.invokeLater(() -> {
+         FlatLightLaf.setup();
+       });
+       Console.debug("Using FlatLightLaf");
+       set = true;
+     }
+     else if (SystemInfo.isLinux)
+     {
+       try
+       {
+         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
+         set = true;
+         Console.debug("Using FlatLightLaf");
+       } catch (ClassNotFoundException | InstantiationException
+               | IllegalAccessException | UnsupportedLookAndFeelException e)
+       {
+         Console.debug("Exception loading FlatLightLaf", e);
+       }
+       // enable custom window decorations
+       JFrame.setDefaultLookAndFeelDecorated(true);
+       JDialog.setDefaultLookAndFeelDecorated(true);
+       SwingUtilities.invokeLater(() -> {
+         FlatLightLaf.setup();
+       });
+       Console.debug("Using FlatLightLaf");
+       set = true;
+     }
  
-         SwingUtilities.invokeLater(() -> {
-           FlatLightLaf.setup();
-         });
+     if (!set)
+     {
+       try
+       {
+         UIManager.setLookAndFeel("com.formdev.flatlaf.FlatLightLaf");
+         set = true;
+         Console.debug("Using FlatLightLaf");
+       } catch (ClassNotFoundException | InstantiationException
+               | IllegalAccessException | UnsupportedLookAndFeelException e)
+       {
+         Console.debug("Exception loading FlatLightLaf", e);
        }
+     }
  
+     if (set)
+     {
+       UIManager.put("TabbedPane.tabType", "card");
        UIManager.put("TabbedPane.showTabSeparators", true);
+       UIManager.put("TabbedPane.showContentSeparator", true);
        UIManager.put("TabbedPane.tabSeparatorsFullHeight", true);
        UIManager.put("TabbedPane.tabsOverlapBorder", true);
-       // UIManager.put("TabbedPane.hasFullBorder", true);
+       UIManager.put("TabbedPane.hasFullBorder", true);
        UIManager.put("TabbedPane.tabLayoutPolicy", "scroll");
        UIManager.put("TabbedPane.scrollButtonsPolicy", "asNeeded");
        UIManager.put("TabbedPane.smoothScrolling", true);
        UIManager.put("TabbedPane.tabWidthMode", "compact");
        UIManager.put("TabbedPane.selectedBackground", Color.white);
      }
-     return set;
-   }
  
-   private static boolean setQuaquaLookAndFeel()
-   {
-     return setSpecificLookAndFeel("quaqua",
-             ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass()
-                     .getName(),
-             false);
-   }
-   private static boolean setVaquaLookAndFeel()
-   {
-     return setSpecificLookAndFeel("vaqua",
-             "org.violetlib.aqua.AquaLookAndFeel", false);
+     Desktop.setLiveDragMode(Cache.getDefault("FLAT_LIVE_DRAG_MODE", true));
+     return set;
    }
  
    private static boolean setMacLookAndFeel()
      return set;
    }
  
+   private static boolean setLinuxLookAndFeel()
+   {
+     boolean set = false;
+     set = setFlatLookAndFeel();
+     if (!set)
+       set = setMetalLookAndFeel();
+     // avoid GtkLookAndFeel -- not good results especially on HiDPI
+     if (!set)
+       set = setNimbusLookAndFeel();
+     return set;
+   }
    private static void showUsage()
    {
      System.out.println(
    }
  
    /**
-    * Quit method delegates to Desktop.quit - unless running in headless mode
-    * when it just ends the JVM
+    * jalview.bin.Jalview.quit() will just run the non-GUI shutdownHook and exit
     */
    public void quit()
    {
-     if (desktop != null)
-     {
-       desktop.quit();
-     }
-     else
-     {
-       System.exit(0);
-     }
+     // System.exit will run the shutdownHook first
+     System.exit(0);
    }
  
    public static AlignFrame getCurrentAlignFrame()
@@@ -59,6 -59,7 +59,7 @@@ import java.util.Hashtable
  import java.util.List;
  import java.util.Locale;
  import java.util.Vector;
+ import java.util.concurrent.Callable;
  
  import javax.swing.ButtonGroup;
  import javax.swing.JCheckBoxMenuItem;
@@@ -349,6 -350,8 +350,8 @@@ public class AlignFrame extends GAlignF
     */
    void init()
    {
+     setFrameIcon(null);
      // setBackground(Color.white); // BH 2019
  
      if (!Jalview.isHeadlessMode())
    @Override
    public void setProgressBar(String message, long id)
    {
 -    progressBar.setProgressBar(message, id);
 +    if (!Platform.isHeadless())
 +      progressBar.setProgressBar(message, id);
    }
  
    @Override
        lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file,
                shortName);
  
+       Console.debug("lastSaveSuccessful=" + lastSaveSuccessful);
+       if (lastSaveSuccessful)
+       {
+         this.getViewport().setSavedUpToDate(true);
+       }
        statusBar.setText(MessageManager.formatMessage(
                "label.successfully_saved_to_file_in_format", new Object[]
                { file, format }));
      }
  
      AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
-     Runnable cancelAction = new Runnable()
-     {
-       @Override
-       public void run()
+     Callable<Void> cancelAction = () -> {
+       lastSaveSuccessful = false;
+       return null;
+     };
+     Callable<Void> outputAction = () -> {
+       // todo defer this to inside formatSequences (or later)
+       AlignmentExportData exportData = viewport.getAlignExportData(options);
+       String output = new FormatAdapter(alignPanel, options)
+               .formatSequences(format, exportData.getAlignment(),
+                       exportData.getOmitHidden(),
+                       exportData.getStartEndPostions(),
+                       viewport.getAlignment().getHiddenColumns());
+       if (output == null)
        {
          lastSaveSuccessful = false;
        }
-     };
-     Runnable outputAction = new Runnable()
-     {
-       @Override
-       public void run()
+       else
        {
-         // todo defer this to inside formatSequences (or later)
-         AlignmentExportData exportData = viewport
-                 .getAlignExportData(options);
-         String output = new FormatAdapter(alignPanel, options)
-                 .formatSequences(format, exportData.getAlignment(),
-                         exportData.getOmitHidden(),
-                         exportData.getStartEndPostions(),
-                         viewport.getAlignment().getHiddenColumns());
-         if (output == null)
+         // create backupfiles object and get new temp filename destination
+         boolean doBackup = BackupFiles.getEnabled();
+         BackupFiles backupfiles = null;
+         if (doBackup)
          {
-           lastSaveSuccessful = false;
+           Console.trace("ALIGNFRAME making backupfiles object for " + file);
+           backupfiles = new BackupFiles(file);
          }
-         else
+         try
          {
-           // create backupfiles object and get new temp filename destination
-           boolean doBackup = BackupFiles.getEnabled();
-           BackupFiles backupfiles = null;
-           if (doBackup)
+           String tempFilePath = doBackup ? backupfiles.getTempFilePath()
+                   : file;
+           Console.trace("ALIGNFRAME setting PrintWriter");
+           PrintWriter out = new PrintWriter(new FileWriter(tempFilePath));
+           if (backupfiles != null)
            {
-             Console.trace(
-                     "ALIGNFRAME making backupfiles object for " + file);
-             backupfiles = new BackupFiles(file);
+             Console.trace("ALIGNFRAME about to write to temp file "
+                     + backupfiles.getTempFilePath());
            }
-           try
-           {
-             String tempFilePath = doBackup ? backupfiles.getTempFilePath()
-                     : file;
-             Console.trace("ALIGNFRAME setting PrintWriter");
-             PrintWriter out = new PrintWriter(new FileWriter(tempFilePath));
  
-             if (backupfiles != null)
-             {
-               Console.trace("ALIGNFRAME about to write to temp file "
-                       + backupfiles.getTempFilePath());
-             }
+           out.print(output);
+           Console.trace("ALIGNFRAME about to close file");
+           out.close();
+           Console.trace("ALIGNFRAME closed file");
+           AlignFrame.this.setTitle(file);
+           statusBar.setText(MessageManager.formatMessage(
+                   "label.successfully_saved_to_file_in_format", new Object[]
+                   { fileName, format.getName() }));
+           lastSaveSuccessful = true;
+         } catch (IOException e)
+         {
+           lastSaveSuccessful = false;
+           Console.error(
+                   "ALIGNFRAME Something happened writing the temp file");
+           Console.error(e.getMessage());
+           Console.debug(Cache.getStackTraceString(e));
+         } catch (Exception ex)
+         {
+           lastSaveSuccessful = false;
+           Console.error(
+                   "ALIGNFRAME Something unexpected happened writing the temp file");
+           Console.error(ex.getMessage());
+           Console.debug(Cache.getStackTraceString(ex));
+         }
  
-             out.print(output);
-             Console.trace("ALIGNFRAME about to close file");
-             out.close();
-             Console.trace("ALIGNFRAME closed file");
-             AlignFrame.this.setTitle(file);
-             statusBar.setText(MessageManager.formatMessage(
-                     "label.successfully_saved_to_file_in_format",
-                     new Object[]
-                     { fileName, format.getName() }));
-             lastSaveSuccessful = true;
-           } catch (IOException e)
-           {
-             lastSaveSuccessful = false;
-             Console.error(
-                     "ALIGNFRAME Something happened writing the temp file");
-             Console.error(e.getMessage());
-             Console.debug(Cache.getStackTraceString(e));
-           } catch (Exception ex)
-           {
-             lastSaveSuccessful = false;
-             Console.error(
-                     "ALIGNFRAME Something unexpected happened writing the temp file");
-             Console.error(ex.getMessage());
-             Console.debug(Cache.getStackTraceString(ex));
-           }
+         if (doBackup)
+         {
+           backupfiles.setWriteSuccess(lastSaveSuccessful);
+           Console.debug("ALIGNFRAME writing temp file was "
+                   + (lastSaveSuccessful ? "" : "NOT ") + "successful");
+           // do the backup file roll and rename the temp file to actual file
+           Console.trace("ALIGNFRAME about to rollBackupsAndRenameTempFile");
+           lastSaveSuccessful = backupfiles.rollBackupsAndRenameTempFile();
+           Console.debug("ALIGNFRAME performed rollBackupsAndRenameTempFile "
+                   + (lastSaveSuccessful ? "" : "un") + "successfully");
+         }
  
-           if (doBackup)
-           {
-             backupfiles.setWriteSuccess(lastSaveSuccessful);
-             Console.debug("ALIGNFRAME writing temp file was "
-                     + (lastSaveSuccessful ? "" : "NOT ") + "successful");
-             // do the backup file roll and rename the temp file to actual file
-             Console.trace(
-                     "ALIGNFRAME about to rollBackupsAndRenameTempFile");
-             lastSaveSuccessful = backupfiles.rollBackupsAndRenameTempFile();
-             Console.debug(
-                     "ALIGNFRAME performed rollBackupsAndRenameTempFile "
-                             + (lastSaveSuccessful ? "" : "un")
-                             + "successfully");
-           }
+         Console.debug("lastSaveSuccessful=" + lastSaveSuccessful);
+         if (lastSaveSuccessful)
+         {
+           AlignFrame.this.getViewport().setSavedUpToDate(true);
          }
        }
+       return null;
      };
  
      /*
      }
      else
      {
-       outputAction.run();
+       try
+       {
+         outputAction.call();
+       } catch (Exception e)
+       {
+         // TODO Auto-generated catch block
+         e.printStackTrace();
+       }
      }
    }
  
      FileFormatI fileFormat = FileFormats.getInstance()
              .forName(fileFormatName);
      AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
-     Runnable outputAction = new Runnable()
-     {
-       @Override
-       public void run()
+     Callable<Void> outputAction = () -> {
+       // todo defer this to inside formatSequences (or later)
+       AlignmentExportData exportData = viewport.getAlignExportData(options);
+       CutAndPasteTransfer cap = new CutAndPasteTransfer();
+       cap.setForInput(null);
+       try
        {
-         // todo defer this to inside formatSequences (or later)
-         AlignmentExportData exportData = viewport
-                 .getAlignExportData(options);
-         CutAndPasteTransfer cap = new CutAndPasteTransfer();
-         cap.setForInput(null);
-         try
-         {
-           FileFormatI format = fileFormat;
-           cap.setText(new FormatAdapter(alignPanel, options)
-                   .formatSequences(format, exportData.getAlignment(),
-                           exportData.getOmitHidden(),
-                           exportData.getStartEndPostions(),
-                           viewport.getAlignment().getHiddenColumns()));
-           Desktop.addInternalFrame(cap, MessageManager.formatMessage(
-                   "label.alignment_output_command", new Object[]
-                   { fileFormat.getName() }), 600, 500);
-         } catch (OutOfMemoryError oom)
-         {
-           new OOMWarning("Outputting alignment as " + fileFormat.getName(),
-                   oom);
-           cap.dispose();
-         }
+         FileFormatI format = fileFormat;
+         cap.setText(new FormatAdapter(alignPanel, options).formatSequences(
+                 format, exportData.getAlignment(),
+                 exportData.getOmitHidden(),
+                 exportData.getStartEndPostions(),
+                 viewport.getAlignment().getHiddenColumns()));
+         Desktop.addInternalFrame(cap, MessageManager.formatMessage(
+                 "label.alignment_output_command", new Object[]
+                 { fileFormat.getName() }), 600, 500);
+       } catch (OutOfMemoryError oom)
+       {
+         new OOMWarning("Outputting alignment as " + fileFormat.getName(),
+                 oom);
+         cap.dispose();
        }
+       return null;
      };
  
      /*
      }
      else
      {
-       outputAction.run();
+       try
+       {
+         outputAction.call();
+       } catch (Exception e)
+       {
+         e.printStackTrace();
+       }
      }
    }
  
              .getString("label.load_jalview_annotations");
      chooser.setDialogTitle(tooltip);
      chooser.setToolTipText(tooltip);
-     chooser.setResponseHandler(0, new Runnable()
-     {
-       @Override
-       public void run()
-       {
-         String choice = chooser.getSelectedFile().getPath();
-         Cache.setProperty("LAST_DIRECTORY", choice);
-         loadJalviewDataFile(chooser.getSelectedFile(), null, null, null);
-       }
+     chooser.setResponseHandler(0, () -> {
+       String choice = chooser.getSelectedFile().getPath();
+       Cache.setProperty("LAST_DIRECTORY", choice);
+       loadJalviewDataFile(chooser.getSelectedFile(), null, null, null);
+       return null;
      });
  
      chooser.showOpenDialog(this);
        return;
      }
  
-     Runnable okAction = new Runnable()
-     {
-       @Override
-       public void run()
-       {
-         SequenceI[] cut = sg.getSequences()
-                 .toArray(new SequenceI[sg.getSize()]);
+     Callable okAction = () -> {
+       SequenceI[] cut = sg.getSequences()
+               .toArray(new SequenceI[sg.getSize()]);
  
-         addHistoryItem(new EditCommand(
-                 MessageManager.getString("label.cut_sequences"), Action.CUT,
-                 cut, sg.getStartRes(),
-                 sg.getEndRes() - sg.getStartRes() + 1,
-                 viewport.getAlignment()));
+       addHistoryItem(new EditCommand(
+               MessageManager.getString("label.cut_sequences"), Action.CUT,
+               cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
+               viewport.getAlignment()));
  
-         viewport.setSelectionGroup(null);
-         viewport.sendSelection();
-         viewport.getAlignment().deleteGroup(sg);
+       viewport.setSelectionGroup(null);
+       viewport.sendSelection();
+       viewport.getAlignment().deleteGroup(sg);
  
-         viewport.firePropertyChange("alignment", null,
-                 viewport.getAlignment().getSequences());
-         if (viewport.getAlignment().getHeight() < 1)
+       viewport.firePropertyChange("alignment", null,
+               viewport.getAlignment().getSequences());
+       if (viewport.getAlignment().getHeight() < 1)
+       {
+         try
+         {
+           AlignFrame.this.setClosed(true);
+         } catch (Exception ex)
          {
-           try
-           {
-             AlignFrame.this.setClosed(true);
-           } catch (Exception ex)
-           {
-           }
          }
        }
+       return null;
      };
  
      /*
      }
      else
      {
-       okAction.run();
+       try
+       {
+         okAction.call();
+       } catch (Exception e)
+       {
+         e.printStackTrace();
+       }
      }
    }
  
        viewport.setGatherViewsHere(true);
      }
      tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
      return newap;
    }
  
    }
  
    /**
-    * DOCUMENT ME!
+    * Opens an Overview panel for the alignment, unless one is open already
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void overviewMenuItem_actionPerformed(ActionEvent e)
    {
+     boolean showHiddenRegions = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START,
+                 false);
+     openOverviewPanel(showHiddenRegions);
+   }
+   public OverviewPanel openOverviewPanel(boolean showHidden)
+   {
      if (alignPanel.overviewPanel != null)
      {
-       return;
+       return alignPanel.overviewPanel;
      }
      JInternalFrame frame = new JInternalFrame();
-     final OverviewPanel overview = new OverviewPanel(alignPanel);
+     final OverviewPanel overview = new OverviewPanel(alignPanel, frame, showHidden);
      frame.setContentPane(overview);
-     Desktop.addInternalFrame(frame, MessageManager
-             .formatMessage("label.overview_params", new Object[]
-             { this.getTitle() }), true, frame.getWidth(), frame.getHeight(),
+     Desktop.addInternalFrame(frame, "", true, frame.getWidth(), frame.getHeight(),
              true, true);
+     frame.setFrameIcon(null);
      frame.pack();
      frame.setLayer(JLayeredPane.PALETTE_LAYER);
+     final AlignmentPanel thePanel = this.alignPanel; 
      frame.addInternalFrameListener(
              new javax.swing.event.InternalFrameAdapter()
              {
                        javax.swing.event.InternalFrameEvent evt)
                {
                  overview.dispose();
-                 alignPanel.setOverviewPanel(null);
+                 thePanel.setOverviewPanel(null);
                }
              });
      if (getKeyListeners().length > 0)
      }
  
      alignPanel.setOverviewPanel(overview);
+     alignPanel.setOverviewTitle(this);
+     
+     return overview;
    }
  
    @Override
      chooser.setToolTipText(
              MessageManager.getString("label.load_tree_file"));
  
-     chooser.setResponseHandler(0, new Runnable()
-     {
-       @Override
-       public void run()
+     chooser.setResponseHandler(0, () -> {
+       String filePath = chooser.getSelectedFile().getPath();
+       Cache.setProperty("LAST_DIRECTORY", filePath);
+       NewickFile fin = null;
+       try
        {
-         String filePath = chooser.getSelectedFile().getPath();
-         Cache.setProperty("LAST_DIRECTORY", filePath);
-         NewickFile fin = null;
-         try
-         {
-           fin = new NewickFile(new FileParse(chooser.getSelectedFile(),
-                   DataSourceType.FILE));
-           viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
-         } catch (Exception ex)
-         {
-           JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
-                   MessageManager
-                           .getString("label.problem_reading_tree_file"),
-                   JvOptionPane.WARNING_MESSAGE);
-           ex.printStackTrace();
-         }
-         if (fin != null && fin.hasWarningMessage())
-         {
-           JvOptionPane.showMessageDialog(Desktop.desktop,
-                   fin.getWarningMessage(),
-                   MessageManager.getString(
-                           "label.possible_problem_with_tree_file"),
-                   JvOptionPane.WARNING_MESSAGE);
-         }
+         fin = new NewickFile(new FileParse(chooser.getSelectedFile(),
+                 DataSourceType.FILE));
+         viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
+       } catch (Exception ex)
+       {
+         JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
+                 MessageManager.getString("label.problem_reading_tree_file"),
+                 JvOptionPane.WARNING_MESSAGE);
+         ex.printStackTrace();
+       }
+       if (fin != null && fin.hasWarningMessage())
+       {
+         JvOptionPane.showMessageDialog(Desktop.desktop,
+                 fin.getWarningMessage(),
+                 MessageManager
+                         .getString("label.possible_problem_with_tree_file"),
+                 JvOptionPane.WARNING_MESSAGE);
        }
+       return null;
      });
      chooser.showOpenDialog(this);
    }
    {
      if (index > -1)
      {
+       /*
+        * update current Overview window title (if there is one)
+        * to add view name "Original" if necessary
+        */
+       alignPanel.setOverviewTitle(this);
+       
+       /*
+        * switch panels and set Overview title (if there is one
+        * because it was opened automatically)
+        */
        alignPanel = alignPanels.get(index);
+       alignPanel.setOverviewTitle(this);
        viewport = alignPanel.av;
        avc.setViewportAndAlignmentPanel(viewport, alignPanel);
        setMenusFromViewport(viewport);
      chooser.setDialogTitle(MessageManager.getString("label.load_vcf_file"));
      chooser.setToolTipText(MessageManager.getString("label.load_vcf_file"));
      final AlignFrame us = this;
-     chooser.setResponseHandler(0, new Runnable()
-     {
-       @Override
-       public void run()
-       {
-         String choice = chooser.getSelectedFile().getPath();
-         Cache.setProperty("LAST_DIRECTORY", choice);
-         SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
-         new VCFLoader(choice).loadVCF(seqs, us);
-       }
+     chooser.setResponseHandler(0, () -> {
+       String choice = chooser.getSelectedFile().getPath();
+       Cache.setProperty("LAST_DIRECTORY", choice);
+       SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
+       new VCFLoader(choice).loadVCF(seqs, us);
+       return null;
      });
      chooser.showOpenDialog(null);
  
@@@ -25,7 -25,6 +25,7 @@@ import java.awt.Dimension
  import java.awt.Font;
  import java.awt.FontMetrics;
  import java.awt.Rectangle;
 +import java.io.File;
  import java.util.ArrayList;
  import java.util.Hashtable;
  import java.util.List;
@@@ -53,13 -52,6 +53,13 @@@ import jalview.datamodel.SearchResults
  import jalview.datamodel.SearchResultsI;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
 +import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormatException;
 +import jalview.io.FileFormatI;
 +import jalview.io.FileFormats;
 +import jalview.io.FileLoader;
 +import jalview.io.IdentifyFile;
  import jalview.renderer.ResidueShader;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemeProperty;
@@@ -737,47 -729,6 +737,47 @@@ public class AlignViewport extends Alig
    }
  
    /**
 +   * Load a File into this AlignViewport attempting to detect format if not
 +   * given or given as null.
 +   * 
 +   * @param file
 +   * @param format
 +   */
 +  public void addFile(File file, FileFormatI format)
 +  {
 +    DataSourceType protocol = AppletFormatAdapter.checkProtocol(file);
 +
 +    if (format == null)
 +    {
 +      try
 +      {
 +        format = new IdentifyFile().identify(file, protocol);
 +      } catch (FileFormatException e1)
 +      {
 +        jalview.bin.Console.error("Unknown file format for '" + file + "'");
 +      }
 +    }
 +    else if (FileFormats.getInstance().isIdentifiable(format))
 +    {
 +      try
 +      {
 +        format = new IdentifyFile().identify(file, protocol);
 +      } catch (FileFormatException e)
 +      {
 +        jalview.bin.Console.error("Unknown file format for '" + file + "'",
 +                e);
 +      }
 +    }
 +
 +    new FileLoader().LoadFile(this, file, DataSourceType.FILE, format);
 +  }
 +
 +  public void addFile(File file)
 +  {
 +    addFile(file, null);
 +  }
 +
 +  /**
     * Show a dialog with the option to open and link (cDNA <-> protein) as a new
     * alignment, either as a standalone alignment or in a split frame. Returns
     * true if the new alignment was opened, false if not, because the user
       * in reverse order)
       */
      JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop)
-             .setResponseHandler(0, new Runnable()
-             {
-               @Override
-               public void run()
-               {
-                 addDataToAlignment(al);
-               }
-             }).setResponseHandler(1, new Runnable()
-             {
-               @Override
-               public void run()
-               {
-                 us.openLinkedAlignmentAs(al, title, true);
-               }
-             }).setResponseHandler(2, new Runnable()
-             {
-               @Override
-               public void run()
-               {
-                 us.openLinkedAlignmentAs(al, title, false);
-               }
+             .setResponseHandler(0, () -> {
+               addDataToAlignment(al);
+               return null;
+             }).setResponseHandler(1, () -> {
+               us.openLinkedAlignmentAs(al, title, true);
+               return null;
+             }).setResponseHandler(2, () -> {
+               us.openLinkedAlignmentAs(al, title, false);
+               return null;
              });
      dialog.showDialog(question,
              MessageManager.getString("label.open_split_window"),
@@@ -24,13 -24,13 +24,14 @@@ package jalview.gui
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.ItemEvent;
 +import java.io.File;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Locale;
+ import java.util.concurrent.Callable;
  import java.util.concurrent.Executors;
  
  import javax.swing.JCheckBox;
@@@ -65,7 -65,6 +66,7 @@@ import jalview.jbgui.GStructureChooser
  import jalview.structure.StructureMapping;
  import jalview.structure.StructureSelectionManager;
  import jalview.util.MessageManager;
 +import jalview.util.Platform;
  import jalview.ws.DBRefFetcher;
  import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
  import jalview.ws.seqfetcher.DbSourceProxy;
@@@ -118,19 -117,11 +119,19 @@@ public class StructureChooser extends G
  
    List<SequenceI> seqsWithoutSourceDBRef = null;
  
 +  private boolean noChooserGUI = false;
 +
    private static StructureViewer lastTargetedView = null;
  
    public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
            AlignmentPanel ap)
    {
 +    this(selectedSeqs, selectedSeq, ap, false);
 +  }
 +
 +  public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
 +          AlignmentPanel ap, boolean noChooserGUI)
 +  {
      // which FTS engine to use
      data = StructureChooserQuerySource.getQuerySourceFor(selectedSeqs);
      initDialog();
      this.selectedSequence = selectedSeq;
      this.selectedSequences = selectedSeqs;
      this.progressIndicator = (ap == null) ? null : ap.alignFrame;
 +    this.noChooserGUI = noChooserGUI;
      init();
  
    }
      populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
      discoverStructureViews();
      updateProgressIndicator(null, startTime);
 -    mainFrame.setVisible(true);
 +    mainFrame.setVisible(noChooserGUI);
      updateCurrentView();
    }
  
      };
  
      // fetch db refs if OK pressed
-     final Runnable discoverCanonicalDBrefs = new Runnable()
-     {
-       @Override
-       public void run()
-       {
-         btn_queryTDB.setEnabled(false);
-         populateSeqsWithoutSourceDBRef();
+     final Callable discoverCanonicalDBrefs = () -> {
+       btn_queryTDB.setEnabled(false);
+       populateSeqsWithoutSourceDBRef();
  
-         final int y = seqsWithoutSourceDBRef.size();
-         if (y > 0)
-         {
-           final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
-                   .toArray(new SequenceI[y]);
-           DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
-                   progressBar, new DbSourceProxy[]
-                   { new jalview.ws.dbsources.Uniprot() }, null, false);
-           dbRefFetcher.addListener(afterDbRefFetch);
-           // ideally this would also gracefully run with callbacks
-           dbRefFetcher.fetchDBRefs(true);
-         }
-         else
-         {
-           // call finished action directly
-           afterDbRefFetch.finished();
-         }
+       final int y = seqsWithoutSourceDBRef.size();
+       if (y > 0)
+       {
+         final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
+                 .toArray(new SequenceI[y]);
+         DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
+                 progressBar, new DbSourceProxy[]
+                 { new jalview.ws.dbsources.Uniprot() }, null, false);
+         dbRefFetcher.addListener(afterDbRefFetch);
+         // ideally this would also gracefully run with callbacks
+         dbRefFetcher.fetchDBRefs(true);
        }
+       else
+       {
+         // call finished action directly
+         afterDbRefFetch.finished();
+       }
+       return null;
      };
-     final Runnable revertview = new Runnable()
-     {
-       @Override
-       public void run()
+     final Callable revertview = () -> {
+       if (lastSelected != null)
        {
-         if (lastSelected != null)
-         {
-           cmb_filterOption.setSelectedItem(lastSelected);
-         }
-       };
+         cmb_filterOption.setSelectedItem(lastSelected);
+       }
+       return null;
      };
      int threshold = Cache.getDefault("UNIPROT_AUTOFETCH_THRESHOLD",
              THRESHOLD_WARN_UNIPROT_FETCH_NEEDED);
      Console.debug("Using Uniprot fetch threshold of " + threshold);
      if (ignoreGui || seqsWithoutSourceDBRef.size() < threshold)
      {
-       Executors.defaultThreadFactory().newThread(discoverCanonicalDBrefs)
-               .start();
+       Executors.newSingleThreadExecutor().submit(discoverCanonicalDBrefs);
        return;
      }
      // need cancel and no to result in the discoverPDB action - mocked is
    @Override
    public void setProgressBar(String message, long id)
    {
 -    progressBar.setProgressBar(message, id);
 +    if (!Platform.isHeadless())
 +      progressBar.setProgressBar(message, id);
    }
  
    @Override
    {
      return notQueriedTDBYet;
    }
 +
 +  /**
 +   * Open a single structure file for a given sequence
 +   */
 +  public static void openStructureFileForSequence(AlignmentPanel ap,
 +          SequenceI seq, File sFile)
 +  {
 +    StructureChooser sc = new StructureChooser(new SequenceI[] { seq }, seq,
 +            ap, true);
 +    StructureSelectionManager ssm = ap.getStructureSelectionManager();
 +    PDBEntry fileEntry = null;
 +    try
 +    {
 +      fileEntry = new AssociatePdbFileWithSeq().associatePdbWithSeq(
 +              sFile.getAbsolutePath(), DataSourceType.FILE, seq, true,
 +              Desktop.instance);
 +    } catch (Exception e)
 +    {
 +      Console.error("Could not open structure file '"
 +              + sFile.getAbsolutePath() + "'");
 +      return;
 +    }
 +
 +    StructureViewer sViewer = sc.launchStructureViewer(ssm,
 +            new PDBEntry[]
 +            { fileEntry }, ap, new SequenceI[] { seq });
 +
 +    sc.mainFrame.dispose();
 +    sc.noChooserGUI = false;
 +  }
  }
@@@ -41,6 -41,7 +41,7 @@@ import jalview.gui.AlignFrame
  import jalview.gui.AlignViewport;
  import jalview.gui.Desktop;
  import jalview.gui.JvOptionPane;
+ import jalview.gui.QuitHandler;
  import jalview.json.binding.biojson.v1.ColourSchemeMapper;
  import jalview.project.Jalview2XML;
  import jalview.schemes.ColourSchemeI;
@@@ -272,7 -273,6 +273,7 @@@ public class FileLoader implements Runn
              ? "Copied From Clipboard"
              : file;
      Runtime rt = Runtime.getRuntime();
 +
      try
      {
        if (Desktop.instance != null)
                    MessageManager.getString("label.couldnt_read_data"),
                    JvOptionPane.WARNING_MESSAGE);
          }
+         this.setShouldBeSaved();
          return;
        }
        // TODO: cache any stream datasources as a temporary file (eg. PDBs
              {
                if (selectedFile == null)
                {
 -                al = fa.readFile(file, protocol, format);
 +                al = fa.readFile(null, file, protocol, format);
  
                }
                else
                {
 -                al = fa.readFile(selectedFile, protocol, format);
 +                al = fa.readFile(selectedFile, null, protocol, format);
                }
                source = fa.getAlignFile(); // keep reference for later if
  
                // that perform queries to find the 'current working alignment'
                Desktop.addInternalFrame(alignFrame, title,
                        AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+               /*
+                * for an Overview automatically opened with alignment,
+                * set its title now alignFrame title has been set
+                */
+               alignFrame.alignPanel.setOverviewTitle(alignFrame);
              }
  
              try
        Desktop.instance.stopLoading();
      }
  
+     this.setShouldBeSaved();
    }
  
    /**
      return tempStructFile.toString();
    }
  
+   /*
+    * set whether quit should ask to save when just loaded this source
+    */
+   private void setShouldBeSaved()
+   {
+     if (protocol == null)
+       return;
+     AlignFrame af = this.alignFrame;
+     if (af == null)
+       return;
+     AlignViewport avp = af.getViewport();
+     if (avp == null)
+       return;
+     avp.setSavedUpToDate(!protocol.isDynamic(),
+             QuitHandler.Message.UNSAVED_ALIGNMENTS);
+   }
  }