JAL-3981 strategic newlines to help speed up Java's html parser
[jalview.git] / src / jalview / gui / PopupMenu.java
index 568f7f1..6903034 100644 (file)
@@ -20,6 +20,9 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
+import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -39,18 +42,21 @@ import java.util.Vector;
 
 import javax.swing.ButtonGroup;
 import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JColorChooser;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
+import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JScrollPane;
 
 import jalview.analysis.AAFrequency;
 import jalview.analysis.AlignmentAnnotationUtils;
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.Conservation;
 import jalview.api.AlignViewportI;
-import jalview.bin.Cache;
+import jalview.bin.Console;
 import jalview.commands.ChangeCaseCommand;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
@@ -64,6 +70,7 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.gui.ColourMenuHelper.ColourChangeListener;
+import jalview.gui.JalviewColourChooser.ColourChooserListener;
 import jalview.io.FileFormatI;
 import jalview.io.FileFormats;
 import jalview.io.FormatAdapter;
@@ -77,6 +84,7 @@ import jalview.util.Comparison;
 import jalview.util.GroupUrlLink;
 import jalview.util.GroupUrlLink.UrlStringTooLongException;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.util.StringUtils;
 import jalview.util.UrlLink;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
@@ -222,13 +230,13 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         urlLink = new UrlLink(link);
       } catch (Exception foo)
       {
-        Cache.log.error("Exception for URLLink '" + link + "'", foo);
+        Console.error("Exception for URLLink '" + link + "'", foo);
         continue;
       }
 
       if (!urlLink.isValid())
       {
-        Cache.log.error(urlLink.getInvalidMessage());
+        Console.error(urlLink.getInvalidMessage());
         continue;
       }
 
@@ -351,14 +359,15 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   }
 
   /**
-   * Constructor for a PopupMenu for a click in the alignment panel (on a residue)
+   * Constructor for a PopupMenu for a click in the alignment panel (on a
+   * residue)
    * 
    * @param ap
-   *              the panel in which the mouse is clicked
+   *          the panel in which the mouse is clicked
    * @param seq
-   *              the sequence under the mouse
+   *          the sequence under the mouse
    * @throws NullPointerException
-   *                                if seq is null
+   *           if seq is null
    */
   public PopupMenu(final AlignmentPanel ap, SequenceI seq, int column)
   {
@@ -369,13 +378,13 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
    * Constructor for a PopupMenu for a click in the sequence id panel
    * 
    * @param alignPanel
-   *                     the panel in which the mouse is clicked
+   *          the panel in which the mouse is clicked
    * @param seq
-   *                     the sequence under the mouse click
+   *          the sequence under the mouse click
    * @param groupLinks
-   *                     templates for sequence external links
+   *          templates for sequence external links
    * @throws NullPointerException
-   *                                if seq is null
+   *           if seq is null
    */
   public PopupMenu(final AlignmentPanel alignPanel, final SequenceI seq,
           List<String> groupLinks)
@@ -391,11 +400,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
    * @param alignPanel
    * @param seq
    * @param column
-   *                      aligned column position (0...)
+   *          aligned column position (0...)
    * @param groupLinks
    */
-  private PopupMenu(boolean fromIdPanel,
-          final AlignmentPanel alignPanel,
+  private PopupMenu(boolean fromIdPanel, final AlignmentPanel alignPanel,
           final SequenceI seq, final int column, List<String> groupLinks)
   {
     Objects.requireNonNull(seq);
@@ -435,9 +443,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     /*
      * And repeat for the current selection group (if there is one):
      */
-    final List<SequenceI> selectedGroup = (alignPanel.av.getSelectionGroup() == null
-            ? Collections.<SequenceI> emptyList()
-            : alignPanel.av.getSelectionGroup().getSequences());
+    final List<SequenceI> selectedGroup = (alignPanel.av
+            .getSelectionGroup() == null
+                    ? Collections.<SequenceI> emptyList()
+                    : alignPanel.av.getSelectionGroup().getSequences());
     buildAnnotationTypesMenus(groupShowAnnotationsMenu,
             groupHideAnnotationsMenu, selectedGroup);
     configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
@@ -663,7 +672,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         buildGroupURLMenu(sg, groupLinks);
       }
       // Add a 'show all structures' for the current selection
-      Hashtable<String, PDBEntry> pdbe = new Hashtable<>(), reppdb = new Hashtable<>();
+      Hashtable<String, PDBEntry> pdbe = new Hashtable<>(),
+              reppdb = new Hashtable<>();
 
       SequenceI sqass = null;
       for (SequenceI sq : alignPanel.av.getSequenceSelection())
@@ -700,7 +710,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     {
       createGroupMenuItem.setVisible(true);
       unGroupMenuItem.setVisible(false);
-      editGroupMenu.setText(MessageManager.getString("action.edit_new_group"));
+      editGroupMenu
+              .setText(MessageManager.getString("action.edit_new_group"));
     }
     else
     {
@@ -727,8 +738,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
    * <li>positional feature links (alignment panel popup menu)</li>
    * <li>feature details links (alignment panel popup menu)</li>
    * </ul>
-   * If this panel is also showed complementary (CDS/protein) features, then links
-   * to their feature details are also added.
+   * If this panel is also showed complementary (CDS/protein) features, then
+   * links to their feature details are also added.
    * 
    * @param seq
    * @param column
@@ -831,10 +842,14 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       /*
        * show local rather than linked feature coordinates
        */
-      int[] beginRange = mf.getMappedPositions(start, start);
-      start = beginRange[0];
-      int[] endRange = mf.getMappedPositions(end, end);
-      end = endRange[endRange.length - 1];
+      int[] localRange = mf.getMappedPositions(start, end);
+      if (localRange == null)
+      {
+        // e.g. variant extending to stop codon so not mappable
+        return;
+      }
+      start = localRange[0];
+      end = localRange[localRange.length - 1];
     }
     StringBuilder desc = new StringBuilder();
     desc.append(sf.getType()).append(" ").append(String.valueOf(start));
@@ -888,13 +903,38 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   protected void showFeatureDetails(SequenceFeature sf, String seqName,
           MappedFeatures mf)
   {
-    CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
-    // it appears Java's CSS does not support border-collapse :-(
-    cap.addStylesheetRule("table { border-collapse: collapse;}");
-    cap.addStylesheetRule("table, td, th {border: 1px solid black;}");
-    cap.setText(sf.getDetailsReport(seqName, mf));
-
-    Desktop.addInternalFrame(cap,
+    JInternalFrame details;
+    if (Platform.isJS())
+    {
+      details = new JInternalFrame();
+      JPanel panel = new JPanel(new BorderLayout());
+      panel.setOpaque(true);
+      panel.setBackground(Color.white);
+      // TODO JAL-3026 set style of table correctly for feature details
+      JLabel reprt = new JLabel(MessageManager
+              .formatMessage("label.html_content", new Object[]
+              { sf.getDetailsReport(seqName, mf) }));
+      reprt.setBackground(Color.WHITE);
+      reprt.setOpaque(true);
+      panel.add(reprt, BorderLayout.CENTER);
+      details.setContentPane(panel);
+      details.pack();
+    }
+    else
+    /**
+     * Java only
+     * 
+     * @j2sIgnore
+     */
+    {
+      CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
+      // it appears Java's CSS does not support border-collapse :-(
+      cap.addStylesheetRule("table { border-collapse: collapse;}");
+      cap.addStylesheetRule("table, td, th {border: 1px solid black;}");
+      cap.setText(sf.getDetailsReport(seqName, mf));
+      details = cap;
+    }
+    Desktop.addInternalFrame(details,
             MessageManager.getString("label.feature_details"), 500, 500);
   }
 
@@ -1087,12 +1127,14 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       {
         sqi = sqi.getDatasetSequence();
       }
-      DBRefEntry[] dbr = sqi.getDBRefs();
-      if (dbr != null && dbr.length > 0)
+      List<DBRefEntry> dbr = sqi.getDBRefs();
+      int nd;
+      if (dbr != null && (nd = dbr.size()) > 0)
       {
-        for (int d = 0; d < dbr.length; d++)
+        for (int d = 0; d < nd; d++)
         {
-          String src = dbr[d].getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase();
+          DBRefEntry e = dbr.get(d);
+          String src = e.getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase(Locale.ROOT);
           Object[] sarray = commonDbrefs.get(src);
           if (sarray == null)
           {
@@ -1105,10 +1147,10 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
           if (((String[]) sarray[1])[sq] == null)
           {
-            if (!dbr[d].hasMap() || (dbr[d].getMap()
-                    .locateMappedRange(start, end) != null))
+            if (!e.hasMap()
+                    || (e.getMap().locateMappedRange(start, end) != null))
             {
-              ((String[]) sarray[1])[sq] = dbr[d].getAccessionId();
+              ((String[]) sarray[1])[sq] = e.getAccessionId();
               ((int[]) sarray[0])[0]++;
             }
           }
@@ -1126,20 +1168,19 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         urlLink = new GroupUrlLink(link);
       } catch (Exception foo)
       {
-        Cache.log.error("Exception for GroupURLLink '" + link + "'", foo);
+        Console.error("Exception for GroupURLLink '" + link + "'", foo);
         continue;
       }
-      ;
       if (!urlLink.isValid())
       {
-        Cache.log.error(urlLink.getInvalidMessage());
+        Console.error(urlLink.getInvalidMessage());
         continue;
       }
       final String label = urlLink.getLabel();
       boolean usingNames = false;
       // Now see which parts of the group apply for this URL
       String ltarget = urlLink.getTarget(); // jalview.util.DBRefUtils.getCanonicalName(urlLink.getTarget());
-      Object[] idset = commonDbrefs.get(ltarget.toUpperCase());
+      Object[] idset = commonDbrefs.get(ltarget.toUpperCase(Locale.ROOT));
       String[] seqstr, ids; // input to makeUrl
       if (idset != null)
       {
@@ -1429,7 +1470,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
-        editSequence_actionPerformed(actionEvent);
+        editSequence_actionPerformed();
       }
     });
     makeReferenceSeq.setText(
@@ -1746,7 +1787,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         }
         inserts.and(sq.getInsertionsAsBits());
       }
-      hidden.clearAndHideColumns(inserts, ap.av.getSelectionGroup().getStartRes(),
+      hidden.clearAndHideColumns(inserts,
+              ap.av.getSelectionGroup().getStartRes(),
               ap.av.getSelectionGroup().getEndRes());
     }
 
@@ -1768,21 +1810,47 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
   public void createSequenceDetailsReport(SequenceI[] sequences)
   {
-    CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
     StringBuilder contents = new StringBuilder(128);
+    contents.append("<html><body>");
     for (SequenceI seq : sequences)
     {
       contents.append("<p><h2>" + MessageManager.formatMessage(
               "label.create_sequence_details_report_annotation_for",
               new Object[]
-              { seq.getDisplayId(true) }) + "</h2></p><p>");
+              { seq.getDisplayId(true) }) + "</h2></p>\n<p>");
       new SequenceAnnotationReport(false).createSequenceAnnotationReport(
               contents, seq, true, true, ap.getSeqPanel().seqCanvas.fr);
       contents.append("</p>");
     }
-    cap.setText("<html>" + contents.toString() + "</html>");
+    contents.append("</body></html>");
+    String report = contents.toString();
+
+    JInternalFrame frame;
+    if (Platform.isJS())
+    {
+      JLabel textLabel = new JLabel();
+      textLabel.setText(report);
+      textLabel.setBackground(Color.WHITE);
+      JPanel pane = new JPanel(new BorderLayout());
+      pane.setOpaque(true);
+      pane.setBackground(Color.WHITE);
+      pane.add(textLabel, BorderLayout.NORTH);
+      frame = new JInternalFrame();
+      frame.getContentPane().add(new JScrollPane(pane));
+    }
+    else
+    /**
+     * Java only
+     * 
+     * @j2sIgnore
+     */
+    {
+      CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
+      cap.setText(report);
+      frame = cap;
+    }
 
-    Desktop.addInternalFrame(cap,
+    Desktop.addInternalFrame(frame,
             MessageManager.formatMessage("label.sequence_details_for",
                     (sequences.length == 1 ? new Object[]
                     { sequences[0].getDisplayId(true) }
@@ -1790,7 +1858,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
                             { MessageManager
                                     .getString("label.selection") })),
             500, 400);
-
   }
 
   protected void showNonconserved_actionPerformed()
@@ -1909,30 +1976,27 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Shows a dialog where group name and description may be edited
    */
   protected void groupName_actionPerformed()
   {
-
     SequenceGroup sg = getGroup();
     EditNameDialog dialog = new EditNameDialog(sg.getName(),
             sg.getDescription(),
-            "       " + MessageManager.getString("label.group_name") + " ",
-            MessageManager.getString("label.group_description") + " ",
+            MessageManager.getString("label.group_name"),
+            MessageManager.getString("label.group_description"));
+    dialog.showDialog(ap.alignFrame,
             MessageManager.getString("label.edit_group_name_description"),
-            ap.alignFrame);
-
-    if (!dialog.accept)
-    {
-      return;
-    }
-
-    sg.setName(dialog.getName());
-    sg.setDescription(dialog.getDescription());
-    refresh();
+            new Runnable()
+            {
+              @Override
+              public void run()
+              {
+                sg.setName(dialog.getName());
+                sg.setDescription(dialog.getDescription());
+                refresh();
+              }
+            });
   }
 
   /**
@@ -1954,48 +2018,40 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
   /**
    * Shows a dialog where the sequence name and description may be edited. If a
-   * name containing spaces is entered, these are converted to underscores, with a
-   * warning message.
+   * name containing spaces is entered, these are converted to underscores, with
+   * a warning message.
    */
   void sequenceName_actionPerformed()
   {
     EditNameDialog dialog = new EditNameDialog(sequence.getName(),
             sequence.getDescription(),
-            "       " + MessageManager.getString("label.sequence_name")
-                    + " ",
-            MessageManager.getString("label.sequence_description") + " ",
-            MessageManager.getString(
-                    "label.edit_sequence_name_description"),
-            ap.alignFrame);
-
-    if (!dialog.accept)
-    {
-      return;
-    }
-
-    String name = dialog.getName();
-    if (name != null)
-    {
-      if (name.indexOf(" ") > -1)
-      {
-        JvOptionPane.showMessageDialog(ap,
-                MessageManager
-                        .getString("label.spaces_converted_to_backslashes"),
-                MessageManager
-                        .getString("label.no_spaces_allowed_sequence_name"),
-                JvOptionPane.WARNING_MESSAGE);
-        name = name.replace(' ', '_');
-      }
-
-      sequence.setName(name);
-      ap.paintAlignment(false, false);
-    }
-
-    sequence.setDescription(dialog.getDescription());
-
-    ap.av.firePropertyChange("alignment", null,
-            ap.av.getAlignment().getSequences());
-
+            MessageManager.getString("label.sequence_name"),
+            MessageManager.getString("label.sequence_description"));
+    dialog.showDialog(ap.alignFrame, MessageManager.getString(
+            "label.edit_sequence_name_description"), new Runnable()
+            {
+              @Override
+              public void run()
+              {
+                if (dialog.getName() != null)
+                {
+                  if (dialog.getName().indexOf(" ") > -1)
+                  {
+                    JvOptionPane.showMessageDialog(ap,
+                            MessageManager.getString(
+                                    "label.spaces_converted_to_underscores"),
+                            MessageManager.getString(
+                                    "label.no_spaces_allowed_sequence_name"),
+                            JvOptionPane.WARNING_MESSAGE);
+                  }
+                  sequence.setName(dialog.getName().replace(' ', '_'));
+                  ap.paintAlignment(false, false);
+                }
+                sequence.setDescription(dialog.getDescription());
+                ap.av.firePropertyChange("alignment", null,
+                        ap.av.getAlignment().getSequences());
+              }
+            });
   }
 
   /**
@@ -2020,24 +2076,22 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Offers a colour chooser and sets the selected colour as the group outline
    */
   protected void outline_actionPerformed()
   {
-    SequenceGroup sg = getGroup();
-    Color col = JColorChooser.showDialog(this,
-            MessageManager.getString("label.select_outline_colour"),
-            Color.BLUE);
-
-    if (col != null)
+    String title = MessageManager.getString("label.select_outline_colour");
+    ColourChooserListener listener = new ColourChooserListener()
     {
-      sg.setOutlineColour(col);
-    }
-
-    refresh();
+      @Override
+      public void colourSelected(Color c)
+      {
+        getGroup().setOutlineColour(c);
+        refresh();
+      }
+    };
+    JalviewColourChooser.showColourChooser(Desktop.getDesktop(), title,
+            Color.BLUE, listener);
   }
 
   /**
@@ -2083,12 +2137,12 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
   public void copy_actionPerformed()
   {
-    ap.alignFrame.copy_actionPerformed(null);
+    ap.alignFrame.copy_actionPerformed();
   }
 
   public void cut_actionPerformed()
   {
-    ap.alignFrame.cut_actionPerformed(null);
+    ap.alignFrame.cut_actionPerformed();
   }
 
   void changeCase(ActionEvent e)
@@ -2184,13 +2238,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
      */
     if (!seqs.isEmpty())
     {
-      if (ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-              .amendFeatures(seqs, features, true, ap))
-      {
-        ap.alignFrame.setShowSeqFeatures(true);
-        ap.av.setSearchResults(null); // clear highlighting
-        ap.repaint(); // draw new/amended features
-      }
+      new FeatureEditor(ap, seqs, features, true).showDialog();
     }
   }
 
@@ -2203,7 +2251,12 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     }
   }
 
-  public void editSequence_actionPerformed(ActionEvent actionEvent)
+  /**
+   * Shows a dialog where sequence characters may be edited. Any changes are
+   * applied, and added as an available 'Undo' item in the edit commands
+   * history.
+   */
+  public void editSequence_actionPerformed()
   {
     SequenceGroup sg = ap.av.getSelectionGroup();
 
@@ -2216,26 +2269,29 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       }
 
       EditNameDialog dialog = new EditNameDialog(
-              seq.getSequenceAsString(sg.getStartRes(),
-                      sg.getEndRes() + 1),
-              null, MessageManager.getString("label.edit_sequence"), null,
+              seq.getSequenceAsString(sg.getStartRes(), sg.getEndRes() + 1),
+              null, MessageManager.getString("label.edit_sequence"), null);
+      dialog.showDialog(ap.alignFrame,
               MessageManager.getString("label.edit_sequence"),
-              ap.alignFrame);
-
-      if (dialog.accept)
-      {
-        EditCommand editCommand = new EditCommand(
-                MessageManager.getString("label.edit_sequences"),
-                Action.REPLACE,
-                dialog.getName().replace(' ', ap.av.getGapCharacter()),
-                sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
-                sg.getStartRes(), sg.getEndRes() + 1, ap.av.getAlignment());
-
-        ap.alignFrame.addHistoryItem(editCommand);
-
-        ap.av.firePropertyChange("alignment", null,
-                ap.av.getAlignment().getSequences());
-      }
+              new Runnable()
+              {
+                @Override
+                public void run()
+                {
+                  EditCommand editCommand = new EditCommand(
+                          MessageManager.getString("label.edit_sequences"),
+                          Action.REPLACE,
+                          dialog.getName().replace(' ',
+                                  ap.av.getGapCharacter()),
+                          sg.getSequencesAsArray(
+                                  ap.av.getHiddenRepSequences()),
+                          sg.getStartRes(), sg.getEndRes() + 1,
+                          ap.av.getAlignment());
+                  ap.alignFrame.addHistoryItem(editCommand);
+                  ap.av.firePropertyChange("alignment", null,
+                          ap.av.getAlignment().getSequences());
+                }
+              });
     }
   }