Merge branch 'develop' into feature/JAL-244_Automatically_adjust_left_margin_to_avoid...
[jalview.git] / src / jalview / gui / AlignFrame.java
index 5370437..3e24e67 100644 (file)
@@ -57,6 +57,7 @@ import java.util.Deque;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Locale;
 import java.util.Vector;
 
 import javax.swing.ButtonGroup;
@@ -91,6 +92,7 @@ import jalview.api.SplitContainerI;
 import jalview.api.ViewStyleI;
 import jalview.api.analysis.SimilarityParamsI;
 import jalview.bin.Cache;
+import jalview.bin.Console;
 import jalview.bin.Jalview;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
@@ -109,6 +111,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.ContactMatrixI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SeqCigar;
@@ -137,6 +140,7 @@ import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
 import jalview.io.ScoreMatrixFile;
 import jalview.io.TCoffeeScoreFile;
+import jalview.io.exceptions.ImageOutputException;
 import jalview.io.vcf.VCFLoader;
 import jalview.jbgui.GAlignFrame;
 import jalview.project.Jalview2XML;
@@ -148,6 +152,7 @@ import jalview.util.HttpUtils;
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.util.imagemaker.BitmapImageSizing;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
 import jalview.ws.DBRefFetcher;
@@ -347,6 +352,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   void init()
   {
+    setFrameIcon(null);
+
     // setBackground(Color.white); // BH 2019
 
     if (!Jalview.isHeadlessMode())
@@ -366,8 +373,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // modifyPID.setEnabled(false);
     }
 
-    String sortby = jalview.bin.Cache.getDefault("SORT_ALIGNMENT",
-            "No sort");
+    String sortby = Cache.getDefault("SORT_ALIGNMENT", "No sort");
 
     if (sortby.equals("Id"))
     {
@@ -409,7 +415,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       wrapMenuItem_actionPerformed(null);
     }
 
-    if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW", false))
+    if (Cache.getDefault("SHOW_OVERVIEW", false))
     {
       this.overviewMenuItem_actionPerformed(null);
     }
@@ -484,10 +490,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 }
               }
             });
-    if (Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase()
+    if (Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase(Locale.ROOT)
             .indexOf("devel") > -1
-            || Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase()
-                    .indexOf("test") > -1)
+            || Cache.getDefault("VERSION", "DEVELOPMENT")
+                    .toLowerCase(Locale.ROOT).indexOf("test") > -1)
     {
       formatMenu.add(vsel);
     }
@@ -565,7 +571,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().moveCursor(0, 1);
+            alignPanel.getSeqPanel().moveCursor(0, 1, evt.isShiftDown());
           }
           break;
 
@@ -576,7 +582,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().moveCursor(0, -1);
+            alignPanel.getSeqPanel().moveCursor(0, -1, evt.isShiftDown());
           }
 
           break;
@@ -589,7 +595,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.getSeqPanel().moveCursor(-1, 0);
+            alignPanel.getSeqPanel().moveCursor(-1, 0, evt.isShiftDown());
           }
 
           break;
@@ -601,7 +607,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.getSeqPanel().moveCursor(1, 0);
+            alignPanel.getSeqPanel().moveCursor(1, 0, evt.isShiftDown());
           }
           break;
 
@@ -976,14 +982,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void setProgressBar(String message, long id)
   {
-    progressBar.setProgressBar(message, id);
+    if (!Platform.isHeadless() && progressBar != null)
+      progressBar.setProgressBar(message, id);
   }
 
   @Override
   public void registerHandler(final long id,
           final IProgressIndicatorHandler handler)
   {
-    progressBar.registerHandler(id, handler);
+    if (progressBar != null)
+      progressBar.registerHandler(id, handler);
   }
 
   /**
@@ -993,7 +1001,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public boolean operationInProgress()
   {
-    return progressBar.operationInProgress();
+    return progressBar == null ? false : progressBar.operationInProgress();
   }
 
   /**
@@ -1012,7 +1020,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   public String getVersion()
   {
-    return jalview.bin.Cache.getProperty("VERSION");
+    return Cache.getProperty("VERSION");
   }
 
   public FeatureRenderer getFeatureRenderer()
@@ -1214,7 +1222,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       }
       else
       {
-        Cache.log.error(MessageManager
+        Console.error(MessageManager
                 .formatMessage("label.couldnt_save_file", new Object[]
                 { lastFilenameSaved }));
       }
@@ -1257,6 +1265,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       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 }));
@@ -1265,95 +1279,85 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
-    Runnable cancelAction = new Runnable()
-    {
-      @Override
-      public void run()
+    Runnable cancelAction = () -> {
+      lastSaveSuccessful = false;
+    };
+    Runnable 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)
           {
-            Cache.log.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;
-            Cache.log.trace("ALIGNFRAME setting PrintWriter");
-            PrintWriter out = new PrintWriter(new FileWriter(tempFilePath));
 
-            if (backupfiles != null)
-            {
-              Cache.log.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);
-            Cache.log.trace("ALIGNFRAME about to close file");
-            out.close();
-            Cache.log.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;
-            Cache.log.error(
-                    "ALIGNFRAME Something happened writing the temp file");
-            Cache.log.error(e.getMessage());
-            Cache.log.debug(Cache.getStackTraceString(e));
-          } catch (Exception ex)
-          {
-            lastSaveSuccessful = false;
-            Cache.log.error(
-                    "ALIGNFRAME Something unexpected happened writing the temp file");
-            Cache.log.error(ex.getMessage());
-            Cache.log.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);
-            Cache.log.debug("ALIGNFRAME writing temp file was "
-                    + (lastSaveSuccessful ? "" : "NOT ") + "successful");
-            // do the backup file roll and rename the temp file to actual file
-            Cache.log.trace(
-                    "ALIGNFRAME about to rollBackupsAndRenameTempFile");
-            lastSaveSuccessful = backupfiles.rollBackupsAndRenameTempFile();
-            Cache.log.debug(
-                    "ALIGNFRAME performed rollBackupsAndRenameTempFile "
-                            + (lastSaveSuccessful ? "" : "un")
-                            + "successfully");
-          }
+        Console.debug("lastSaveSuccessful=" + lastSaveSuccessful);
+        if (lastSaveSuccessful)
+        {
+          AlignFrame.this.getViewport().setSavedUpToDate(true);
         }
       }
     };
@@ -1371,7 +1375,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     else
     {
-      outputAction.run();
+      try
+      {
+        outputAction.run();
+      } catch (Exception e)
+      {
+        // TODO Auto-generated catch block
+        e.printStackTrace();
+      }
     }
   }
 
@@ -1388,33 +1399,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     FileFormatI fileFormat = FileFormats.getInstance()
             .forName(fileFormatName);
     AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
-    Runnable outputAction = new Runnable()
-    {
-      @Override
-      public void run()
+    Runnable 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();
       }
     };
 
@@ -1430,7 +1435,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     else
     {
-      outputAction.run();
+      try
+      {
+        outputAction.run();
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
     }
   }
 
@@ -1444,19 +1455,75 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void htmlMenuItem_actionPerformed(ActionEvent e)
   {
     HtmlSvgOutput htmlSVG = new HtmlSvgOutput(alignPanel);
-    htmlSVG.exportHTML(null);
+    try
+    {
+      htmlSVG.exportHTML(null);
+    } catch (ImageOutputException x)
+    {
+      // report problem to console and raise dialog
+    }
   }
 
   @Override
   public void bioJSMenuItem_actionPerformed(ActionEvent e)
   {
     BioJsHTMLOutput bjs = new BioJsHTMLOutput(alignPanel);
-    bjs.exportHTML(null);
+    try
+    {
+      bjs.exportHTML(null);
+    } catch (ImageOutputException x)
+    {
+      // report problem to console and raise dialog
+    }
   }
 
   public void createImageMap(File file, String image)
   {
-    alignPanel.makePNGImageMap(file, image);
+    try
+    {
+      alignPanel.makePNGImageMap(file, image);
+    } catch (ImageOutputException x)
+    {
+      // report problem to console and raise dialog
+    }
+  }
+
+  @Override
+  public void createPNG_actionPerformed(ActionEvent e)
+  {
+    try
+    {
+      createPNG(null);
+    } catch (ImageOutputException ioex)
+    {
+      // raise dialog, and report via console
+    }
+  }
+
+  @Override
+  public void createEPS_actionPerformed(ActionEvent e)
+  {
+    try
+    {
+      createEPS(null);
+    } catch (ImageOutputException ioex)
+    {
+      // raise dialog, and report via console
+    }
+
+  }
+
+  @Override
+  public void createSVG_actionPerformed(ActionEvent e)
+  {
+    try
+    {
+      createSVG(null);
+    } catch (ImageOutputException ioex)
+    {
+      // raise dialog, and report via console
+    }
+
   }
 
   /**
@@ -1465,10 +1532,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param f
    */
-  @Override
-  public void createPNG(File f)
+  public void createPNG(File f) throws ImageOutputException
   {
-    alignPanel.makeAlignmentImage(TYPE.PNG, f);
+    createPNG(f, null, BitmapImageSizing.nullBitmapImageSizing());
+  }
+
+  public void createPNG(File f, String renderer, BitmapImageSizing userBis)
+          throws ImageOutputException
+  {
+    alignPanel.makeAlignmentImage(TYPE.PNG, f, renderer, userBis);
   }
 
   /**
@@ -1477,10 +1549,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param f
    */
-  @Override
-  public void createEPS(File f)
+  public void createEPS(File f) throws ImageOutputException
+  {
+    createEPS(f, null);
+  }
+
+  public void createEPS(File f, String renderer) throws ImageOutputException
   {
-    alignPanel.makeAlignmentImage(TYPE.EPS, f);
+    alignPanel.makeAlignmentImage(TYPE.EPS, f, renderer);
   }
 
   /**
@@ -1489,10 +1565,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param f
    */
-  @Override
-  public void createSVG(File f)
+  public void createSVG(File f) throws ImageOutputException
   {
-    alignPanel.makeAlignmentImage(TYPE.SVG, f);
+    createSVG(f, null);
+  }
+
+  public void createSVG(File f, String renderer) throws ImageOutputException
+  {
+    alignPanel.makeAlignmentImage(TYPE.SVG, f, renderer);
   }
 
   @Override
@@ -1532,21 +1612,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void associatedData_actionPerformed(ActionEvent e)
   {
     final JalviewFileChooser chooser = new JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
+            Cache.getProperty("LAST_DIRECTORY"));
     chooser.setFileView(new JalviewFileView());
     String tooltip = MessageManager
             .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();
-        jalview.bin.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);
     });
 
     chooser.showOpenDialog(this);
@@ -1731,7 +1806,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       if (originalSource != viewport)
       {
-        Cache.log.warn(
+        Console.warn(
                 "Implementation worry: mismatch of viewport origin for undo");
       }
       originalSource.updateHiddenColumns();
@@ -1771,7 +1846,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       if (originalSource != viewport)
       {
-        Cache.log.warn(
+        Console.warn(
                 "Implementation worry: mismatch of viewport origin for redo");
       }
       originalSource.updateHiddenColumns();
@@ -2278,10 +2353,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         .intValue();
               }
             }
-            alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation
-            // was
-            // duplicated
-            // earlier
+            // annotation was duplicated earlier
+            alignment.addAnnotation(sequences[i].getAnnotation()[a]);
+            // take care of contact matrix too
+            ContactMatrixI cm = sequences[i]
+                    .getContactMatrixFor(sequences[i].getAnnotation()[a]);
+            if (cm != null)
+            {
+              alignment.addContactListFor(sequences[i].getAnnotation()[a],
+                      cm);
+            }
+
             alignment.setAnnotationIndex(sequences[i].getAnnotation()[a],
                     a);
           }
@@ -2468,34 +2550,28 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    Runnable okAction = new Runnable()
-    {
-      @Override
-      public void run()
-      {
-        SequenceI[] cut = sg.getSequences()
-                .toArray(new SequenceI[sg.getSize()]);
+    Runnable 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)
-          {
-          }
         }
       }
     };
@@ -2521,7 +2597,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     else
     {
-      okAction.run();
+      try
+      {
+        okAction.run();
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
     }
   }
 
@@ -2627,6 +2709,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.invertColumnSelection();
     alignPanel.paintAlignment(true, false);
+    PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
 
@@ -2825,7 +2908,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void findMenuItem_actionPerformed(ActionEvent e)
   {
-    new Finder(alignPanel);
+    new Finder(alignPanel, false, null);
   }
 
   /**
@@ -2907,6 +2990,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       viewport.setGatherViewsHere(true);
     }
     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
+
     return newap;
   }
 
@@ -3071,10 +3155,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void wrapMenuItem_actionPerformed(ActionEvent e)
   {
-    scaleAbove.setVisible(wrapMenuItem.isSelected());
-    scaleLeft.setVisible(wrapMenuItem.isSelected());
-    scaleRight.setVisible(wrapMenuItem.isSelected());
-    viewport.setWrapAlignment(wrapMenuItem.isSelected());
+    setWrapFormat(wrapMenuItem.isSelected());
+  }
+
+  public void setWrapFormat(boolean b)
+  {
+    scaleAbove.setVisible(b);
+    scaleLeft.setVisible(b);
+    scaleRight.setVisible(b);
+    viewport.setWrapAlignment(b);
     alignPanel.updateLayout();
   }
 
@@ -3422,28 +3511,34 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * 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(),
-            true, true);
+    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()
             {
@@ -3452,7 +3547,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       javax.swing.event.InternalFrameEvent evt)
               {
                 overview.dispose();
-                alignPanel.setOverviewPanel(null);
+                thePanel.setOverviewPanel(null);
               }
             });
     if (getKeyListeners().length > 0)
@@ -3461,6 +3556,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
 
     alignPanel.setOverviewPanel(overview);
+    alignPanel.setOverviewTitle(this);
+
+    return overview;
   }
 
   @Override
@@ -4067,42 +4165,36 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     // Pick the tree file
     JalviewFileChooser chooser = new JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
+            Cache.getProperty("LAST_DIRECTORY"));
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.getString("label.select_newick_like_tree_file"));
     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);
       }
     });
     chooser.showOpenDialog(this);
@@ -4169,6 +4261,57 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return tp;
   }
 
+  public void showContactMapTree(AlignmentAnnotation aa, ContactMatrixI cm)
+  {
+    int x = 4, y = 5;
+    int w = 400, h = 500;
+
+    try
+    {
+      NewickFile fin = new NewickFile(
+              new FileParse(cm.getNewick(), DataSourceType.PASTE));
+      String title = aa.label + " "
+              + cm.getTreeMethod() + " tree" + (aa.sequenceRef != null
+                      ? (" for " + aa.sequenceRef.getDisplayId(false))
+                      : "");
+
+      showColumnWiseTree(fin, aa, title, w, h, x, y);
+    } catch (Throwable xx)
+    {
+      Console.error("Unexpected exception showing tree for contact matrix",
+              xx);
+    }
+  }
+
+  public TreePanel showColumnWiseTree(NewickFile nf, AlignmentAnnotation aa,
+          String treeTitle, int w, int h, int x, int y)
+  {
+    try
+    {
+      nf.parse();
+      if (nf.getTree() == null)
+      {
+        return null;
+      }
+      TreePanel tp = new TreePanel(alignPanel, nf, aa, treeTitle);
+
+      tp.setSize(w, h);
+
+      if (x > 0 && y > 0)
+      {
+        tp.setLocation(x, y);
+      }
+
+      Desktop.addInternalFrame(tp, treeTitle, w, h);
+      return tp;
+    } catch (Throwable xx)
+    {
+      Console.error("Unexpected exception showing tree for contact matrix",
+              xx);
+    }
+    return null;
+  }
+
   private boolean buildingMenu = false;
 
   /**
@@ -4286,9 +4429,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                   webService.add(me.webServiceNoServices);
                 }
                 // TODO: move into separate menu builder class.
-                boolean new_sspred = false;
-                if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
                 {
+                  // logic for 2.11.1.4 is
+                  // always look to see if there is a discover. if there isn't
+                  // we can't show any Jws2 services
+                  // if there are services available, show them - regardless of
+                  // the 'show JWS2 preference'
+                  // if the discoverer is running then say so
+                  // otherwise offer to trigger discovery if 'show JWS2' is not
+                  // enabled
                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
                   if (jws2servs != null)
                   {
@@ -4297,7 +4446,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       jws2servs.attachWSMenuEntry(webService, me);
                       for (Jws2Instance sv : jws2servs.getServices())
                       {
-                        if (sv.description.toLowerCase().contains("jpred"))
+                        if (sv.description.toLowerCase(Locale.ROOT)
+                                .contains("jpred"))
                         {
                           for (JMenuItem jmi : legacyItems)
                           {
@@ -4305,8 +4455,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                           }
                         }
                       }
-
                     }
+
                     if (jws2servs.isRunning())
                     {
                       JMenuItem tm = new JMenuItem(
@@ -4314,6 +4464,26 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       tm.setEnabled(false);
                       webService.add(tm);
                     }
+                    else if (!Cache.getDefault("SHOW_JWS2_SERVICES", true))
+                    {
+                      JMenuItem enableJws2 = new JMenuItem(
+                              "Discover Web Services");
+                      enableJws2.setToolTipText(
+                              "Select to start JABA Web Service discovery (or enable option in Web Service preferences)");
+                      enableJws2.setEnabled(true);
+                      enableJws2.addActionListener(new ActionListener()
+                      {
+
+                        @Override
+                        public void actionPerformed(ActionEvent e)
+                        {
+                          // start service discoverer, but ignore preference
+                          Desktop.instance.startServiceDiscovery(false,
+                                  true);
+                        }
+                      });
+                      webService.add(enableJws2);
+                    }
                   }
                 }
                 build_urlServiceMenu(me.webService);
@@ -4331,7 +4501,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 }
               } catch (Exception e)
               {
-                Cache.log.debug(
+                Console.debug(
                         "Exception during web service menu building process.",
                         e);
               }
@@ -4428,7 +4598,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       showProducts.setEnabled(showp);
     } catch (Exception e)
     {
-      Cache.log.warn(
+      Console.warn(
               "canShowProducts threw an exception - please report to help@jalview.org",
               e);
       return false;
@@ -4469,8 +4639,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       al = dna.translateCdna(codeTable);
     } catch (Exception ex)
     {
-      jalview.bin.Cache.log.error(
-              "Exception during translation. Please report this !", ex);
+      Console.error("Exception during translation. Please report this !",
+              ex);
       final String msg = MessageManager.getString(
               "label.error_when_translating_sequences_submit_bug_report");
       final String errorTitle = MessageManager
@@ -4832,7 +5002,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
         } catch (Exception x)
         {
-          Cache.log.debug(
+          Console.debug(
                   "Exception when processing data source as T-COFFEE score file",
                   x);
           tcf = null;
@@ -4930,7 +5100,19 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     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);
@@ -5594,7 +5776,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    *          update non-sequence-related annotations
    */
   @Override
-  protected void setAnnotationsVisibility(boolean visible,
+  public void setAnnotationsVisibility(boolean visible,
           boolean forSequences, boolean forAlignment)
   {
     AlignmentAnnotation[] anns = alignPanel.getAlignment()
@@ -5800,6 +5982,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                     | ActionEvent.CTRL_MASK)) != 0);
   }
 
+  @Override
+  protected void copyHighlightedColumns_actionPerformed(
+          ActionEvent actionEvent)
+  {
+    avc.copyHighlightedRegionsToClipboard();
+  }
+
   /**
    * Rebuilds the Colour menu, including any user-defined colours which have
    * been loaded either on startup or during the session
@@ -5848,16 +6037,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     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);
     });
     chooser.showOpenDialog(null);
 
@@ -5876,6 +6060,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     return lastFeatureSettingsBounds;
   }
+
 }
 
 class PrintThread extends Thread