JAL-1620 version bump and release notes
[jalview.git] / src / jalview / appletgui / AnnotationLabels.java
index 2184851..85a2fbe 100755 (executable)
@@ -1,28 +1,32 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
- * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
+ * Copyright (C) 2014 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  * Jalview is free software: you can redistribute it and/or
  * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
- * 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
  * Jalview is distributed in the hope that it will be useful, but 
  * WITHOUT ANY WARRANTY; without even the implied warranty 
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
  * PURPOSE.  See the GNU General Public License for more details.
  * 
- * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.appletgui;
 
 import java.util.*;
-
 import java.awt.*;
 import java.awt.event.*;
 
 import jalview.datamodel.*;
+import jalview.util.MessageManager;
+import jalview.util.ParseHtmlBodyAndLinks;
 
 public class AnnotationLabels extends Panel implements ActionListener,
         MouseListener, MouseMotionListener
@@ -64,6 +68,23 @@ public class AnnotationLabels extends Panel implements ActionListener,
     this.ap = ap;
     this.av = ap.av;
     setLayout(null);
+
+    /**
+     * this retrieves the adjustable height glyph from resources. we don't use
+     * it at the moment. java.net.URL url =
+     * getClass().getResource("/images/idwidth.gif"); Image temp = null;
+     * 
+     * if (url != null) { temp =
+     * java.awt.Toolkit.getDefaultToolkit().createImage(url); }
+     * 
+     * try { MediaTracker mt = new MediaTracker(this); mt.addImage(temp, 0);
+     * mt.waitForID(0); } catch (Exception ex) { }
+     * 
+     * BufferedImage bi = new BufferedImage(temp.getHeight(this),
+     * temp.getWidth(this), BufferedImage.TYPE_INT_RGB); Graphics2D g =
+     * (Graphics2D) bi.getGraphics(); g.rotate(Math.toRadians(90));
+     * g.drawImage(temp, 0, -bi.getWidth(this), this); image = (Image) bi;
+     */
     addMouseListener(this);
     addMouseMotionListener(this);
   }
@@ -73,10 +94,13 @@ public class AnnotationLabels extends Panel implements ActionListener,
     this.av = av;
   }
 
-  public void setScrollOffset(int y)
+  public void setScrollOffset(int y, boolean repaint)
   {
     scrollOffset = y;
-    repaint();
+    if (repaint)
+    {
+      repaint();
+    }
   }
 
   /**
@@ -88,7 +112,8 @@ public class AnnotationLabels extends Panel implements ActionListener,
   int getSelectedRow(int y)
   {
     int row = -2;
-    AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
+    AlignmentAnnotation[] aa = ap.av.getAlignment()
+            .getAlignmentAnnotation();
 
     if (aa == null)
     {
@@ -115,20 +140,20 @@ public class AnnotationLabels extends Panel implements ActionListener,
 
   public void actionPerformed(ActionEvent evt)
   {
-    AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
+    AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
 
     if (evt.getActionCommand().equals(ADDNEW))
     {
       AlignmentAnnotation newAnnotation = new AlignmentAnnotation("", null,
-              new Annotation[ap.av.alignment.getWidth()]);
+              new Annotation[ap.av.getAlignment().getWidth()]);
 
       if (!editLabelDescription(newAnnotation))
       {
         return;
       }
 
-      ap.av.alignment.addAnnotation(newAnnotation);
-      ap.av.alignment.setAnnotationIndex(newAnnotation, 0);
+      ap.av.getAlignment().addAnnotation(newAnnotation);
+      ap.av.getAlignment().setAnnotationIndex(newAnnotation, 0);
     }
     else if (evt.getActionCommand().equals(EDITNAME))
     {
@@ -200,34 +225,110 @@ public class AnnotationLabels extends Panel implements ActionListener,
 
   }
 
+  boolean resizePanel = false;
+
   public void mouseMoved(MouseEvent evt)
   {
-    int row = getSelectedRow(evt.getY() - scrollOffset);
+    resizePanel = evt.getY() < 10 && evt.getX() < 14;
+    int row = getSelectedRow(evt.getY() + scrollOffset);
 
     if (row > -1)
     {
+      ParseHtmlBodyAndLinks phb = new ParseHtmlBodyAndLinks(
+              av.getAlignment().getAlignmentAnnotation()[row]
+                      .getDescription(true),
+              true, "\n");
       if (tooltip == null)
       {
-        tooltip = new Tooltip(
-                ap.av.alignment.getAlignmentAnnotation()[row]
-                        .getDescription(true),
-                this);
+        tooltip = new Tooltip(phb.getNonHtmlContent(), this);
       }
       else
       {
-        tooltip.setTip(ap.av.alignment.getAlignmentAnnotation()[row]
-                .getDescription(true));
+        tooltip.setTip(phb.getNonHtmlContent());
       }
     }
     else if (tooltip != null)
     {
       tooltip.setTip("");
     }
+  }
+
+  /**
+   * curent drag position
+   */
+  MouseEvent dragEvent = null;
+
+  /**
+   * flag to indicate drag events should be ignored
+   */
+  private boolean dragCancelled = false;
 
+  /**
+   * clear any drag events in progress
+   */
+  public void cancelDrag()
+  {
+    dragEvent = null;
+    dragCancelled = true;
   }
 
   public void mouseDragged(MouseEvent evt)
   {
+    if (dragCancelled)
+    {
+      return;
+    }
+    ;
+    dragEvent = evt;
+
+    if (resizePanel)
+    {
+      Dimension d = ap.annotationPanelHolder.getSize(), e = ap.annotationSpaceFillerHolder
+              .getSize(), f = ap.seqPanelHolder.getSize();
+      int dif = evt.getY() - oldY;
+
+      dif /= ap.av.charHeight;
+      dif *= ap.av.charHeight;
+
+      if ((d.height - dif) > 20 && (f.height + dif) > 20)
+      {
+        ap.annotationPanel.setSize(d.width, d.height - dif);
+        setSize(new Dimension(e.width, d.height - dif));
+        ap.annotationSpaceFillerHolder.setSize(new Dimension(e.width,
+                d.height - dif));
+        ap.annotationPanelHolder.setSize(new Dimension(d.width, d.height
+                - dif));
+        ap.apvscroll.setValues(ap.apvscroll.getValue(), d.height - dif, 0,
+                av.calcPanelHeight());
+        f.height += dif;
+        ap.seqPanelHolder.setPreferredSize(f);
+        ap.setScrollValues(av.getStartRes(), av.getStartSeq());
+        ap.validate();
+        // ap.paintAlignment(true);
+        ap.addNotify();
+      }
+
+    }
+    else
+    {
+      int diff;
+      if ((diff = 6 - evt.getY()) > 0)
+      {
+        // nudge scroll up
+        ap.apvscroll.setValue(ap.apvscroll.getValue() - diff);
+        ap.adjustmentValueChanged(null);
+
+      }
+      else if ((0 < (diff = 6
+              - ap.annotationSpaceFillerHolder.getSize().height
+              + evt.getY())))
+      {
+        // nudge scroll down
+        ap.apvscroll.setValue(ap.apvscroll.getValue() + diff);
+        ap.adjustmentValueChanged(null);
+      }
+      repaint();
+    }
   }
 
   public void mouseClicked(MouseEvent evt)
@@ -236,34 +337,106 @@ public class AnnotationLabels extends Panel implements ActionListener,
 
   public void mouseReleased(MouseEvent evt)
   {
+    if (!resizePanel && !dragCancelled)
+    {
+      int start = selectedRow;
+
+      int end = getSelectedRow(evt.getY() + scrollOffset);
+
+      if (start > -1 && start != end)
+      {
+        // Swap these annotations
+        AlignmentAnnotation startAA = ap.av.getAlignment()
+                .getAlignmentAnnotation()[start];
+        if (end == -1)
+        {
+          end = ap.av.getAlignment().getAlignmentAnnotation().length - 1;
+        }
+        AlignmentAnnotation endAA = ap.av.getAlignment()
+                .getAlignmentAnnotation()[end];
+
+        ap.av.getAlignment().getAlignmentAnnotation()[end] = startAA;
+        ap.av.getAlignment().getAlignmentAnnotation()[start] = endAA;
+      }
+    }
+    resizePanel = false;
+    dragEvent = null;
+    dragCancelled = false;
+    repaint();
+    ap.annotationPanel.repaint();
   }
 
   public void mouseEntered(MouseEvent evt)
   {
+    if (evt.getY() < 10 && evt.getX() < 14)
+    {
+      resizePanel = true;
+      repaint();
+    }
   }
 
   public void mouseExited(MouseEvent evt)
   {
+    dragCancelled = false;
+
+    if (dragEvent == null)
+    {
+      resizePanel = false;
+    }
+    else
+    {
+      if (!resizePanel)
+      {
+        dragEvent = null;
+      }
+    }
+    repaint();
   }
 
   public void mousePressed(MouseEvent evt)
   {
-    selectedRow = getSelectedRow(evt.getY() - scrollOffset);
-    AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
+    oldY = evt.getY();
+    if (resizePanel)
+    {
+      return;
+    }
+    dragCancelled = false;
+    // todo: move below to mouseClicked ?
+    selectedRow = getSelectedRow(evt.getY() + scrollOffset);
+
+    AlignmentAnnotation[] aa = ap.av.getAlignment()
+            .getAlignmentAnnotation();
 
     // DETECT RIGHT MOUSE BUTTON IN AWT
     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
     {
 
+      PopupMenu popup = new PopupMenu(
+              MessageManager.getString("label.annotations"));
 
-    PopupMenu popup = new PopupMenu("Annotations");
-
-    MenuItem item = new MenuItem(ADDNEW);
-    item.addActionListener(this);
-    popup.add(item);
-    if (selectedRow < 0)
-    {
-      // this never happens at moment: - see comment on JAL-563
+      MenuItem item = new MenuItem(ADDNEW);
+      item.addActionListener(this);
+      popup.add(item);
+      if (selectedRow < 0)
+      {
+        // this never happens at moment: - see comment on JAL-563
+        if (hasHiddenRows)
+        {
+          item = new MenuItem(SHOWALL);
+          item.addActionListener(this);
+          popup.add(item);
+        }
+        this.add(popup);
+        popup.show(this, evt.getX(), evt.getY());
+        return;
+      }
+      // add the rest if there are actually rows to show
+      item = new MenuItem(EDITNAME);
+      item.addActionListener(this);
+      popup.add(item);
+      item = new MenuItem(HIDE);
+      item.addActionListener(this);
+      popup.add(item);
       if (hasHiddenRows)
       {
         item = new MenuItem(SHOWALL);
@@ -271,49 +444,184 @@ public class AnnotationLabels extends Panel implements ActionListener,
         popup.add(item);
       }
       this.add(popup);
-      popup.show(this, evt.getX(), evt.getY());
-      return;
-    }
-    // add the rest if there are actually rows to show
-    item = new MenuItem(EDITNAME);
-    item.addActionListener(this);
-    popup.add(item);
-    item = new MenuItem(HIDE);
-    item.addActionListener(this);
-    popup.add(item);
-    if (hasHiddenRows)
-    {
-      item = new MenuItem(SHOWALL);
+      item = new MenuItem(OUTPUT_TEXT);
       item.addActionListener(this);
       popup.add(item);
-    }
-    this.add(popup);
-    item = new MenuItem(OUTPUT_TEXT);
-    item.addActionListener(this);
-    popup.add(item);
-
-    if (aa[selectedRow] == ap.av.consensus)
-    {
-      popup.addSeparator();
-      final CheckboxMenuItem cbmi = new CheckboxMenuItem(
-              "Ignore Gaps In Consensus", ap.av.getIgnoreGapsConsensus());
-
-      cbmi.addItemListener(new ItemListener()
+      if (selectedRow < aa.length)
       {
-        public void itemStateChanged(ItemEvent e)
+        if (aa[selectedRow].autoCalculated)
         {
-          ap.av.setIgnoreGapsConsensus(cbmi.getState());
-          ap.paintAlignment(true);
+          if (aa[selectedRow].label.indexOf("Consensus") > -1)
+          {
+            popup.addSeparator();
+            final CheckboxMenuItem cbmi = new CheckboxMenuItem(
+                    MessageManager.getString("label.ignore_gaps_consensus"),
+                    (aa[selectedRow].groupRef != null) ? aa[selectedRow].groupRef
+                            .getIgnoreGapsConsensus() : ap.av
+                            .getIgnoreGapsConsensus());
+            final AlignmentAnnotation aaa = aa[selectedRow];
+            cbmi.addItemListener(new ItemListener()
+            {
+              public void itemStateChanged(ItemEvent e)
+              {
+                if (aaa.groupRef != null)
+                {
+                  // TODO: pass on reference to ap so the view can be updated.
+                  aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState());
+                }
+                else
+                {
+                  ap.av.setIgnoreGapsConsensus(cbmi.getState());
+                }
+                ap.paintAlignment(true);
+              }
+            });
+            popup.add(cbmi);
+            if (aaa.groupRef != null)
+            {
+              final CheckboxMenuItem chist = new CheckboxMenuItem(
+                      MessageManager.getString("label.show_group_histogram"),
+                      aa[selectedRow].groupRef.isShowConsensusHistogram());
+              chist.addItemListener(new ItemListener()
+              {
+                public void itemStateChanged(ItemEvent e)
+                {
+                  // TODO: pass on reference
+                  // to ap
+                  // so the
+                  // view
+                  // can be
+                  // updated.
+                  aaa.groupRef.setShowConsensusHistogram(chist.getState());
+                  ap.repaint();
+                  // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+                }
+              });
+              popup.add(chist);
+              final CheckboxMenuItem cprofl = new CheckboxMenuItem(
+                      MessageManager.getString("label.show_group_logo"),
+                      aa[selectedRow].groupRef.isShowSequenceLogo());
+              cprofl.addItemListener(new ItemListener()
+              {
+                public void itemStateChanged(ItemEvent e)
+                {
+                  // TODO: pass on reference
+                  // to ap
+                  // so the
+                  // view
+                  // can be
+                  // updated.
+                  aaa.groupRef.setshowSequenceLogo(cprofl.getState());
+                  ap.repaint();
+                  // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+                }
+              });
+
+              popup.add(cprofl);
+              final CheckboxMenuItem cprofn = new CheckboxMenuItem(
+                      MessageManager.getString("label.normalise_group_logo"),
+                      aa[selectedRow].groupRef.isNormaliseSequenceLogo());
+              cprofn.addItemListener(new ItemListener()
+              {
+                public void itemStateChanged(ItemEvent e)
+                {
+                  // TODO: pass on reference
+                  // to ap
+                  // so the
+                  // view
+                  // can be
+                  // updated.
+                  aaa.groupRef.setshowSequenceLogo(true);
+                  aaa.groupRef.setNormaliseSequenceLogo(cprofn.getState());
+                  ap.repaint();
+                  // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+                }
+              });
+              popup.add(cprofn);
+            }
+            else
+            {
+              final CheckboxMenuItem chist = new CheckboxMenuItem(
+                      MessageManager.getString("label.show_histogram"), av.isShowConsensusHistogram());
+              chist.addItemListener(new ItemListener()
+              {
+                public void itemStateChanged(ItemEvent e)
+                {
+                  // TODO: pass on reference
+                  // to ap
+                  // so the
+                  // view
+                  // can be
+                  // updated.
+                  av.setShowConsensusHistogram(chist.getState());
+                  ap.alignFrame.showConsensusHistogram.setState(chist
+                          .getState()); // TODO: implement
+                                        // ap.updateGUI()/alignFrame.updateGUI
+                                        // for applet
+                  ap.repaint();
+                  // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+                }
+              });
+              popup.add(chist);
+              final CheckboxMenuItem cprof = new CheckboxMenuItem(
+                      MessageManager.getString("label.show_logo"), av.isShowSequenceLogo());
+              cprof.addItemListener(new ItemListener()
+              {
+                public void itemStateChanged(ItemEvent e)
+                {
+                  // TODO: pass on reference
+                  // to ap
+                  // so the
+                  // view
+                  // can be
+                  // updated.
+                  av.setShowSequenceLogo(cprof.getState());
+                  ap.alignFrame.showSequenceLogo.setState(cprof.getState()); // TODO:
+                                                                             // implement
+                                                                             // ap.updateGUI()/alignFrame.updateGUI
+                                                                             // for
+                                                                             // applet
+                  ap.repaint();
+                  // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+                }
+              });
+              popup.add(cprof);
+              final CheckboxMenuItem cprofn = new CheckboxMenuItem(
+                      MessageManager.getString("label.normalise_logo"), av.isNormaliseSequenceLogo());
+              cprofn.addItemListener(new ItemListener()
+              {
+                public void itemStateChanged(ItemEvent e)
+                {
+                  // TODO: pass on reference
+                  // to ap
+                  // so the
+                  // view
+                  // can be
+                  // updated.
+                  av.setShowSequenceLogo(true);
+                  ap.alignFrame.normSequenceLogo.setState(cprofn.getState()); // TODO:
+                                                                              // implement
+                                                                              // ap.updateGUI()/alignFrame.updateGUI
+                                                                              // for
+                                                                              // applet
+                  av.setNormaliseSequenceLogo(cprofn.getState());
+                  ap.repaint();
+                  // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+                }
+              });
+              popup.add(cprofn);
+            }
+
+            item = new MenuItem(COPYCONS_SEQ);
+            item.addActionListener(this);
+            popup.add(item);
+          }
         }
-      });
-      popup.add(cbmi);
-      item = new MenuItem(COPYCONS_SEQ);
-      item.addActionListener(this);
-      popup.add(item);
+      }
+      popup.show(this, evt.getX(), evt.getY());
     }
-
-    popup.show(this, evt.getX(), evt.getY());
-    } else {
+    else
+    {
       // selection action.
       if (selectedRow > -1 && selectedRow < aa.length)
       {
@@ -321,7 +629,8 @@ public class AnnotationLabels extends Panel implements ActionListener,
         {
           if (evt.getClickCount() >= 2)
           {
-            // todo: make the ap scroll to the selection - not necessary, first click highlights/scrolls, second selects
+            // todo: make the ap scroll to the selection - not necessary, first
+            // click highlights/scrolls, second selects
             ap.seqPanel.ap.idPanel.highlightSearchResults(null);
             ap.av.setSelectionGroup(// new SequenceGroup(
             aa[selectedRow].groupRef); // );
@@ -339,17 +648,40 @@ public class AnnotationLabels extends Panel implements ActionListener,
         }
         else if (aa[selectedRow].sequenceRef != null)
         {
-          Vector sr = new Vector();
-          sr.addElement(aa[selectedRow].sequenceRef);
           if (evt.getClickCount() == 1)
           {
-            ap.seqPanel.ap.idPanel.highlightSearchResults(sr);
+            ap.seqPanel.ap.idPanel.highlightSearchResults(Arrays
+                    .asList(new SequenceI[]
+                    { aa[selectedRow].sequenceRef }));
           }
           else if (evt.getClickCount() >= 2)
           {
             ap.seqPanel.ap.idPanel.highlightSearchResults(null);
-            SequenceGroup sg = new SequenceGroup();
-            sg.addSequence(aa[selectedRow].sequenceRef, false);
+            SequenceGroup sg = ap.av.getSelectionGroup();
+            if (sg!=null)
+            {
+              // we make a copy rather than edit the current selection if no modifiers pressed
+              // see Enhancement JAL-1557
+              if (!(evt.isControlDown() || evt.isShiftDown()))
+              {
+                sg = new SequenceGroup(sg);
+                sg.clear();
+                sg.addSequence(aa[selectedRow].sequenceRef, false);
+              } else {
+                if (evt.isControlDown())
+                {
+                  sg.addOrRemove(aa[selectedRow].sequenceRef, true);
+                } else {
+                  // notionally, we should also add intermediate sequences from last added sequence ?
+                  sg.addSequence(aa[selectedRow].sequenceRef, true);
+                }
+              }
+            } else {
+              sg = new SequenceGroup();
+              sg.setStartRes(0);
+              sg.setEndRes(ap.av.getAlignment().getWidth()-1);
+              sg.addSequence(aa[selectedRow].sequenceRef, false);
+            }
             ap.av.setSelectionGroup(sg);
             ap.paintAlignment(false);
             PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
@@ -378,7 +710,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
     jalview.appletgui.AlignFrame.copiedSequences.append(sq.getName() + "\t"
             + sq.getStart() + "\t" + sq.getEnd() + "\t"
             + sq.getSequenceAsString() + "\n");
-    if (av.hasHiddenColumns)
+    if (av.hasHiddenColumns())
     {
       jalview.appletgui.AlignFrame.copiedHiddenColumns = new Vector();
       for (int i = 0; i < av.getColumnSelection().getHiddenColumns().size(); i++)
@@ -401,7 +733,9 @@ public class AnnotationLabels extends Panel implements ActionListener,
   public void paint(Graphics g)
   {
     int w = getSize().width;
-    if (image == null || w != image.getWidth(this))
+    int h = getSize().height;
+    if (image == null || w != image.getWidth(this)
+            || h != image.getHeight(this))
     {
       image = createImage(w, ap.annotationPanel.getSize().height);
     }
@@ -417,10 +751,10 @@ public class AnnotationLabels extends Panel implements ActionListener,
     g.setColor(Color.white);
     g.fillRect(0, 0, getSize().width, getSize().height);
 
-    g.translate(0, scrollOffset);
+    g.translate(0, -scrollOffset);
     g.setColor(Color.black);
 
-    AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
+    AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
     int y = 0, fy = g.getFont().getSize();
     int x = 0, offset;
 
@@ -443,6 +777,27 @@ public class AnnotationLabels extends Panel implements ActionListener,
         g.drawString(aa[i].label, x, y + offset);
       }
     }
-  }
+    g.translate(0, +scrollOffset);
+    if (resizePanel)
+    {
+      g.setColor(Color.red);
+      g.setPaintMode();
+      g.drawLine(2, 8, 5, 2);
+      g.drawLine(5, 2, 8, 8);
+    }
+    else if (!dragCancelled && dragEvent != null && aa != null)
+    {
+      g.setColor(Color.lightGray);
+      g.drawString(aa[selectedRow].label, dragEvent.getX(),
+              dragEvent.getY());
+    }
 
+    if (!av.wrapAlignment && ((aa == null) || (aa.length < 1)))
+    {
+      g.setColor(Color.black);
+      g.drawString(MessageManager.getString("label.right_click"), 2, 8);
+      g.drawString(MessageManager.getString("label.to_add_annotation"), 2,
+              18);
+    }
+  }
 }