Merge branch 'develop' into bug/JAL-4421_customise_volume_name_of_DMG_installers
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index f2ab8a6..d26f023 100644 (file)
  */
 package jalview.viewmodel;
 
+import java.awt.Color;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.analysis.Connectivity;
 import jalview.analysis.Conservation;
 import jalview.analysis.TreeModel;
 import jalview.api.AlignCalcManagerI;
@@ -29,6 +42,7 @@ import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeaturesDisplayedI;
 import jalview.api.ViewStyleI;
+import jalview.bin.Console;
 import jalview.commands.CommandI;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
@@ -37,6 +51,8 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.ContactListI;
+import jalview.datamodel.ContactMatrixI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.ProfilesI;
@@ -45,6 +61,8 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.gui.QuitHandler;
+import jalview.project.Jalview2XML;
 import jalview.renderer.ResidueShader;
 import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
@@ -59,20 +77,9 @@ import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
 import jalview.workers.ComplementConsensusThread;
 import jalview.workers.ConsensusThread;
+import jalview.workers.SecondaryStructureConsensusThread;
 import jalview.workers.StrucConsensusThread;
 
-import java.awt.Color;
-import java.beans.PropertyChangeSupport;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
 /**
  * base class holding visualization and analysis attributes and common logic for
  * an active alignment view displayed in the GUI
@@ -100,6 +107,11 @@ public abstract class AlignmentViewport
   protected Deque<CommandI> redoList = new ArrayDeque<>();
 
   /**
+   * used to determine if quit should be confirmed
+   */
+  private boolean savedUpToDate = false;
+
+  /**
    * alignment displayed in the viewport. Please use get/setter
    */
   protected AlignmentI alignment;
@@ -664,8 +676,7 @@ public abstract class AlignmentViewport
          * retain any colour thresholds per group while
          * changing choice of colour scheme (JAL-2386)
          */
-        sg.setColourScheme(
-                cs == null ? null : cs.getInstance(this, sg));
+        sg.setColourScheme(cs == null ? null : cs.getInstance(this, sg));
         if (cs != null)
         {
           sg.getGroupColourScheme().alignmentChanged(sg,
@@ -688,6 +699,8 @@ public abstract class AlignmentViewport
   }
 
   protected AlignmentAnnotation consensus;
+  
+  protected AlignmentAnnotation secondaryStructureConsensus;
 
   protected AlignmentAnnotation complementConsensus;
 
@@ -700,6 +713,8 @@ public abstract class AlignmentViewport
   protected AlignmentAnnotation quality;
 
   protected AlignmentAnnotation[] groupConsensus;
+  
+  protected AlignmentAnnotation[] groupSSConsensus;
 
   protected AlignmentAnnotation[] groupConservation;
 
@@ -707,6 +722,10 @@ public abstract class AlignmentViewport
    * results of alignment consensus analysis for visible portion of view
    */
   protected ProfilesI hconsensus = null;
+  
+  protected ProfilesI hSSConsensus = null;
+  
+  
 
   /**
    * results of cDNA complement consensus visible portion of view
@@ -744,7 +763,13 @@ public abstract class AlignmentViewport
   {
     this.hconsensus = hconsensus;
   }
-
+  
+  @Override
+  public void setSequenceSSConsensusHash(ProfilesI hSSConsensus)
+  {
+    this.hSSConsensus = hSSConsensus;
+  }
+  
   @Override
   public void setComplementConsensusHash(
           Hashtable<String, Object>[] hconsensus)
@@ -757,6 +782,12 @@ public abstract class AlignmentViewport
   {
     return hconsensus;
   }
+  
+  @Override
+  public ProfilesI getSequenceSSConsensusHash()
+  {
+    return hSSConsensus;
+  }
 
   @Override
   public Hashtable<String, Object>[] getComplementConsensusHash()
@@ -796,6 +827,14 @@ public abstract class AlignmentViewport
     return consensus;
   }
 
+  
+  @Override
+  public AlignmentAnnotation getAlignmentSecondaryStructureConsensusAnnotation()
+  {
+    return secondaryStructureConsensus;
+  }
+  
+
   @Override
   public AlignmentAnnotation getAlignmentGapAnnotation()
   {
@@ -887,6 +926,26 @@ public abstract class AlignmentViewport
       }
     }
   }
+  
+  
+  
+
+  /**
+   * trigger update of consensus annotation
+   */
+  public void updateSecondaryStructureConsensus(final AlignmentViewPanel ap)
+  {
+    // see note in mantis : issue number 8585
+    if (secondaryStructureConsensus == null || !autoCalculateConsensus)
+    {
+      return;
+    }
+    if (calculator
+            .getRegisteredWorkersOfClass(SecondaryStructureConsensusThread.class) == null)
+    {
+      calculator.registerWorker(new SecondaryStructureConsensusThread(this, ap));
+    }
+  }
 
   // --------START Structure Conservation
   public void updateStrucConsensus(final AlignmentViewPanel ap)
@@ -925,7 +984,8 @@ public abstract class AlignmentViewport
     }
     if (calculator.workingInvolvedWith(alignmentAnnotation))
     {
-      // System.err.println("grey out ("+alignmentAnnotation.label+")");
+      // jalview.bin.Console.errPrintln("grey out
+      // ("+alignmentAnnotation.label+")");
       return true;
     }
     return false;
@@ -949,6 +1009,7 @@ public abstract class AlignmentViewport
     consensus = null;
     complementConsensus = null;
     strucConsensus = null;
+    secondaryStructureConsensus = null;
     conservation = null;
     quality = null;
     groupConsensus = null;
@@ -963,6 +1024,7 @@ public abstract class AlignmentViewport
     ranges = null;
     currentTree = null;
     selectionGroup = null;
+    colSel = null;
     setAlignment(null);
   }
 
@@ -994,6 +1056,8 @@ public abstract class AlignmentViewport
    * should consensus profile be rendered by default
    */
   protected boolean showSequenceLogo = false;
+  
+  protected boolean showSequenceSSLogo = false;
 
   /**
    * should consensus profile be rendered normalised to row height
@@ -1004,6 +1068,13 @@ public abstract class AlignmentViewport
    * should consensus histograms be rendered by default
    */
   protected boolean showConsensusHistogram = true;
+  
+  protected boolean showSSConsensusHistogram = true;
+
+  public void setShowSSConsensusHistogram(boolean showSSConsensusHistogram)
+  {
+    this.showSSConsensusHistogram = showSSConsensusHistogram;
+  }
 
   /**
    * @return the showConsensusProfile
@@ -1013,6 +1084,12 @@ public abstract class AlignmentViewport
   {
     return showSequenceLogo;
   }
+  
+  @Override
+  public boolean isShowSequenceSSLogo()
+  {
+    return showSequenceSSLogo;
+  }
 
   /**
    * @param showSequenceLogo
@@ -1031,6 +1108,18 @@ public abstract class AlignmentViewport
     }
     this.showSequenceLogo = showSequenceLogo;
   }
+  
+  public void setShowSequenceSSLogo(boolean showSequenceSSLogo)
+  {
+    if (showSequenceSSLogo != this.showSequenceSSLogo)
+    {
+      // TODO: decouple settings setting from calculation when refactoring
+      // annotation update method from alignframe to viewport
+      this.showSequenceSSLogo = showSequenceSSLogo;
+      calculator.updateAnnotationFor(SecondaryStructureConsensusThread.class);
+    }
+    this.showSequenceSSLogo = showSequenceSSLogo;
+  }
 
   /**
    * @param showConsensusHistogram
@@ -1085,6 +1174,12 @@ public abstract class AlignmentViewport
   {
     return this.showConsensusHistogram;
   }
+  
+  @Override
+  public boolean isShowSSConsensusHistogram()
+  {
+    return this.showSSConsensusHistogram;
+  }
 
   /**
    * when set, updateAlignment will always ensure sequences are of equal length
@@ -1197,7 +1292,7 @@ public abstract class AlignmentViewport
   {
     if (sequenceSetID != null)
     {
-      System.err.println(
+      jalview.bin.Console.errPrintln(
               "Warning - overwriting a sequenceSetId for a viewport!");
     }
     sequenceSetID = new String(newid);
@@ -1236,6 +1331,7 @@ public abstract class AlignmentViewport
     if (ap != null)
     {
       updateConsensus(ap);
+      updateSecondaryStructureConsensus(ap);
       if (residueShading != null)
       {
         residueShading.setThreshold(residueShading.getThreshold(),
@@ -1308,7 +1404,9 @@ public abstract class AlignmentViewport
 
   protected boolean showQuality = true;
 
-  protected boolean showConsensus = true;
+  protected boolean showConsensus = true;  
+
+  protected boolean showSSConsensus = false;
 
   protected boolean showOccupancy = true;
 
@@ -1323,21 +1421,6 @@ public abstract class AlignmentViewport
    */
   private boolean followHighlight = true;
 
-  private boolean disableFastPaint; // BH 2019.04.18
-
-  /**
-   * BH 2019.04.18 When gap filling is on and a modification is made to fill
-   * those, we need to disallow fast painting for paste just once
-   * 
-   * @return
-   */
-  public boolean isFastPaintDisabled()
-  {
-    boolean ret = disableFastPaint;
-    disableFastPaint = false;
-    return ret;
-  }
-
   /**
    * Property change listener for changes in alignment
    * 
@@ -1381,6 +1464,12 @@ public abstract class AlignmentViewport
     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
   }
 
+  @Override
+  public void notifyAlignmentChanged()
+  {
+    firePropertyChange("alignment", null, alignment);
+  }
+
   // common hide/show column stuff
 
   public void hideSelectedColumns()
@@ -1835,9 +1924,8 @@ public abstract class AlignmentViewport
         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
         if (selectedOnly && selectionGroup != null)
         {
-          clone.makeVisibleAnnotation(
-                  selectionGroup.getStartRes(), selectionGroup.getEndRes(),
-                  alignment.getHiddenColumns());
+          clone.makeVisibleAnnotation(selectionGroup.getStartRes(),
+                  selectionGroup.getEndRes(), alignment.getHiddenColumns());
         }
         else
         {
@@ -1872,15 +1960,12 @@ public abstract class AlignmentViewport
   {
     if (isPadGaps())
     {
-      if (alignment.padGaps())
-      {
-        // the new alignment has been modified -- can't fast paint
-        disableFastPaint = true;
-      }
+      alignment.padGaps();
     }
     if (autoCalculateConsensus)
     {
       updateConsensus(ap);
+      updateSecondaryStructureConsensus(ap);
     }
     if (hconsensus != null && autoCalculateConsensus)
     {
@@ -1964,13 +2049,19 @@ public abstract class AlignmentViewport
       consensus = new AlignmentAnnotation("Consensus",
               MessageManager.getString("label.consensus_descr"),
               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
+      
+      secondaryStructureConsensus = new AlignmentAnnotation(MessageManager.getString("label.ssconsensus_label"),
+              MessageManager.getString("label.ssconsensus_descr"),
+              new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
+      
       initConsensus(consensus);
+      initSSConsensus(secondaryStructureConsensus);
       initGapCounts();
-
       initComplementConsensus();
     }
   }
 
+
   /**
    * If this is a protein alignment and there are mappings to cDNA, adds the
    * cDNA consensus annotation and returns true, else returns false.
@@ -2021,6 +2112,17 @@ public abstract class AlignmentViewport
       alignment.addAnnotation(aa);
     }
   }
+  
+  private void initSSConsensus(AlignmentAnnotation aa)
+  {
+    aa.hasText = true;
+    aa.autoCalculated = true;
+
+    if (showSSConsensus)
+    {
+      alignment.addAnnotation(aa);
+    }
+  }
 
   // these should be extracted from the view model - style and settings for
   // derived annotation
@@ -2110,7 +2212,7 @@ public abstract class AlignmentViewport
       {
         if (aa == null)
         {
-          System.err.println("Null annotation row: ignoring.");
+          jalview.bin.Console.errPrintln("Null annotation row: ignoring.");
           continue;
         }
         if (!aa.visible)
@@ -2142,7 +2244,7 @@ public abstract class AlignmentViewport
 
         if (aa.graph > 0)
         {
-          aa.height += aa.graphHeight;
+          aa.height += aa.graphHeight + 20;
         }
 
         if (aa.height == 0)
@@ -2169,7 +2271,9 @@ public abstract class AlignmentViewport
     boolean conv = isShowGroupConservation();
     boolean cons = isShowGroupConsensus();
     boolean showprf = isShowSequenceLogo();
+    boolean showSSprf = isShowSequenceSSLogo();
     boolean showConsHist = isShowConsensusHistogram();
+    boolean showSSConsHist = isShowSSConsensusHistogram();
     boolean normLogo = isNormaliseSequenceLogo();
 
     /**
@@ -2206,7 +2310,9 @@ public abstract class AlignmentViewport
         {
           // set defaults for this group's conservation/consensus
           sg.setshowSequenceLogo(showprf);
+          sg.setshowSequenceSSLogo(showSSprf);
           sg.setShowConsensusHistogram(showConsHist);
+          sg.setShowSSConsensusHistogram(showSSConsHist);
           sg.setNormaliseSequenceLogo(normLogo);
         }
         if (conv)
@@ -2218,6 +2324,7 @@ public abstract class AlignmentViewport
         {
           updateCalcs = true;
           alignment.addAnnotation(sg.getConsensus(), 0);
+          alignment.addAnnotation(sg.getSSConsensus(), 0);
         }
         // refresh the annotation rows
         if (updateCalcs)
@@ -2286,7 +2393,7 @@ public abstract class AlignmentViewport
   public void clearSequenceColours()
   {
     sequenceColours.clear();
-  };
+  }
 
   @Override
   public AlignViewportI getCodingComplement()
@@ -2303,7 +2410,8 @@ public abstract class AlignmentViewport
   {
     if (this == av)
     {
-      System.err.println("Ignoring recursive setCodingComplement request");
+      jalview.bin.Console
+              .errPrintln("Ignoring recursive setCodingComplement request");
     }
     else
     {
@@ -2634,6 +2742,8 @@ public abstract class AlignmentViewport
     {
       this.historyList.push(command);
       broadcastCommand(command, false);
+      setSavedUpToDate(false);
+      Jalview2XML.setStateSavedUpToDate(false);
     }
   }
 
@@ -2739,6 +2849,30 @@ public abstract class AlignmentViewport
     viewStyle.setProteinFontAsCdna(b);
   }
 
+  @Override
+  public void setShowComplementFeatures(boolean b)
+  {
+    viewStyle.setShowComplementFeatures(b);
+  }
+
+  @Override
+  public boolean isShowComplementFeatures()
+  {
+    return viewStyle.isShowComplementFeatures();
+  }
+
+  @Override
+  public void setShowComplementFeaturesOnTop(boolean b)
+  {
+    viewStyle.setShowComplementFeaturesOnTop(b);
+  }
+
+  @Override
+  public boolean isShowComplementFeaturesOnTop()
+  {
+    return viewStyle.isShowComplementFeaturesOnTop();
+  }
+
   /**
    * @return true if view should scroll to show the highlighted region of a
    *         sequence
@@ -2927,6 +3061,19 @@ public abstract class AlignmentViewport
     return searchResults;
   }
 
+  @Override
+  public ContactListI getContactList(AlignmentAnnotation _aa, int column)
+  {
+    return alignment.getContactListFor(_aa, column);
+  }
+
+  @Override
+  public ContactMatrixI getContactMatrix(
+          AlignmentAnnotation alignmentAnnotation)
+  {
+    return alignment.getContactMatrixFor(alignmentAnnotation);
+  }
+
   /**
    * get the consensus sequence as displayed under the PID consensus annotation
    * row.
@@ -2967,6 +3114,41 @@ public abstract class AlignmentViewport
             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
     return sq;
   }
+  
+  public SequenceI getSSConsensusSeq()
+  {
+    if (secondaryStructureConsensus == null)
+    {
+      updateSecondaryStructureConsensus(null);
+    }
+    if (secondaryStructureConsensus == null)
+    {
+      return null;
+    }
+    StringBuffer seqs = new StringBuffer();
+    for (int i = 0; i < secondaryStructureConsensus.annotations.length; i++)
+    {
+      Annotation annotation = secondaryStructureConsensus.annotations[i];
+      if (annotation != null)
+      {
+        String description = annotation.description;
+        if (description != null && description.startsWith("["))
+        {
+          // consensus is a tie - just pick the first one
+          seqs.append(description.charAt(1));
+        }
+        else
+        {
+          seqs.append(annotation.displayCharacter);
+        }
+      }
+    }
+
+    SequenceI sq = new Sequence("Sec Str Consensus", seqs.toString());
+    sq.setDescription("Percentage Identity Sec Str Consensus "
+            + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
+    return sq;
+  }
 
   @Override
   public void setCurrentTree(TreeModel tree)
@@ -2981,7 +3163,8 @@ public abstract class AlignmentViewport
   }
 
   @Override
-  public AlignmentExportData getAlignExportData(AlignExportSettingsI options)
+  public AlignmentExportData getAlignExportData(
+          AlignExportSettingsI options)
   {
     AlignmentI alignmentToExport = null;
     String[] omitHidden = null;
@@ -3009,7 +3192,7 @@ public abstract class AlignmentViewport
             omitHidden, alignmentStartEnd);
     return ed;
   }
-  
+
   /**
    * flag set to indicate if structure views might be out of sync with sequences
    * in the alignment
@@ -3073,4 +3256,42 @@ public abstract class AlignmentViewport
       codingComplement.setUpdateStructures(needToUpdateStructureViews);
     }
   }
+
+  @Override
+  public Iterator<int[]> getViewAsVisibleContigs(boolean selectedRegionOnly)
+  {
+    int start = 0;
+    int end = 0;
+    if (selectedRegionOnly && selectionGroup != null)
+    {
+      start = selectionGroup.getStartRes();
+      end = selectionGroup.getEndRes() + 1;
+    }
+    else
+    {
+      end = alignment.getWidth();
+    }
+    return (alignment.getHiddenColumns().getVisContigsIterator(start, end,
+            false));
+  }
+
+  public void setSavedUpToDate(boolean s)
+  {
+    setSavedUpToDate(s, QuitHandler.Message.UNSAVED_CHANGES);
+  }
+
+  public void setSavedUpToDate(boolean s, QuitHandler.Message m)
+  {
+    Console.debug(
+            "Setting " + this.getViewId() + " setSavedUpToDate to " + s);
+    savedUpToDate = s;
+    QuitHandler.setMessage(m);
+  }
+
+  public boolean savedUpToDate()
+  {
+    Console.debug("Returning " + this.getViewId() + " savedUpToDate value: "
+            + savedUpToDate);
+    return savedUpToDate;
+  }
 }