2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3 * Copyright (C) 2014 The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
17 * The Jalview Authors are detailed in the 'AUTHORS' file.
19 package jalview.appletgui;
24 import java.awt.event.*;
26 import jalview.analysis.AlignmentSorter;
27 import jalview.commands.OrderCommand;
28 import jalview.datamodel.*;
29 import jalview.schemes.AnnotationColourGradient;
30 import jalview.schemes.GraduatedColor;
32 public class FeatureSettings extends Panel implements ItemListener,
33 MouseListener, MouseMotionListener, ActionListener,
46 Panel featurePanel = new Panel();
48 ScrollPane scrollPane;
50 boolean alignmentHasFeatures = false;
54 Scrollbar transparency;
56 public FeatureSettings(final AlignmentPanel ap)
60 ap.av.featureSettings = this;
61 fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
63 transparency = new Scrollbar(Scrollbar.HORIZONTAL,
64 100 - (int) (fr.transparency * 100), 1, 1, 100);
66 if (fr.transparencySetter != null)
68 transparency.addAdjustmentListener(this);
72 transparency.setEnabled(false);
75 java.net.URL url = getClass().getResource("/images/link.gif");
78 linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
81 if (av.featuresDisplayed == null)
88 this.setLayout(new BorderLayout());
89 scrollPane = new ScrollPane();
90 scrollPane.add(featurePanel);
91 if (alignmentHasFeatures)
93 add(scrollPane, BorderLayout.CENTER);
96 Button invert = new Button("Invert Selection");
97 invert.addActionListener(this);
99 Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
100 lowerPanel.add(invert);
102 Panel tPanel = new Panel(new BorderLayout());
104 if (fr.transparencySetter != null)
106 tPanel.add(transparency, BorderLayout.CENTER);
107 tPanel.add(new Label("Transparency"), BorderLayout.EAST);
112 new Label("Transparency not available in this web browser"),
113 BorderLayout.CENTER);
116 lowerPanel.add(tPanel, BorderLayout.SOUTH);
118 add(lowerPanel, BorderLayout.SOUTH);
120 if (groupPanel != null)
123 .setLayout(new GridLayout(
124 (fr.featureGroups.size() - fr.hiddenGroups.size()) / 4 + 1,
126 groupPanel.validate();
128 add(groupPanel, BorderLayout.NORTH);
132 final FeatureSettings me = this;
133 frame.addWindowListener(new WindowAdapter()
135 public void windowClosing(WindowEvent e)
137 if (me.av.featureSettings == me)
139 me.av.featureSettings = null;
145 int height = featurePanel.getComponentCount() * 50 + 60;
147 height = Math.max(200, height);
148 height = Math.min(400, height);
150 jalview.bin.JalviewLite.addFrame(frame, "Feature Settings", width,
154 public void paint(Graphics g)
156 g.setColor(Color.black);
157 g.drawString("No Features added to this alignment!!", 10, 20);
158 g.drawString("(Features can be added from searches or", 10, 40);
159 g.drawString("from Jalview / GFF features files)", 10, 60);
162 protected void popupSort(final MyCheckbox check, final Hashtable minmax,
165 final String type = check.type;
166 final Object typeCol = fr.getFeatureStyle(type);
167 java.awt.PopupMenu men = new PopupMenu("Settings for " + type);
168 java.awt.MenuItem scr = new MenuItem("Sort by Score");
170 final FeatureSettings me = this;
171 scr.addActionListener(new ActionListener()
174 public void actionPerformed(ActionEvent e)
176 me.sortByScore(new String[]
181 MenuItem dens = new MenuItem("Sort by Density");
182 dens.addActionListener(new ActionListener()
185 public void actionPerformed(ActionEvent e)
187 me.sortByDens(new String[]
195 final Object typeMinMax = minmax.get(type);
197 * final java.awt.CheckboxMenuItem chb = new
198 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
199 * moment chb.setState(minmax.get(type) != null);
200 * chb.addActionListener(new ActionListener() {
202 * public void actionPerformed(ActionEvent e) {
203 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
204 * null); } else { minmax.put(type, typeMinMax); } }
208 if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
210 // graduated colourschemes for those where minmax exists for the
211 // positional features
212 MenuItem mxcol = new MenuItem(
213 (typeCol instanceof Color) ? "Graduated Colour"
216 mxcol.addActionListener(new ActionListener()
219 public void actionPerformed(ActionEvent e)
221 if (typeCol instanceof Color)
223 new FeatureColourChooser(me, type);
224 // write back the current colour object to update the table
225 check.updateColor(fr.getFeatureStyle(type));
229 new UserDefinedColours(me, check.type,
230 ((GraduatedColor) typeCol));
237 this.featurePanel.add(men);
238 men.show(this.featurePanel, x, y);
241 public void setTableData()
243 alignmentHasFeatures = fr.buildGroupHash();
244 if (alignmentHasFeatures)
253 * rebuilds the group panel
255 public void rebuildGroups()
257 boolean rdrw = false;
258 if (groupPanel == null)
260 groupPanel = new Panel();
265 groupPanel.removeAll();
267 // TODO: JAL-964 - smoothly incorporate new group entries if panel already
268 // displayed and new groups present
269 Enumeration gps = fr.featureGroups.keys();
270 while (gps.hasMoreElements())
272 String group = (String) gps.nextElement();
273 Boolean vis = (Boolean) fr.featureGroups.get(group);
274 Checkbox check = new MyCheckbox(group, vis.booleanValue(),
275 (fr.featureLinks != null && fr.featureLinks
276 .containsKey(group)));
277 check.addMouseListener(this);
278 check.setFont(new Font("Serif", Font.BOLD, 12));
279 check.addItemListener(this);
280 check.setVisible(fr.hiddenGroups.contains(group));
281 groupPanel.add(check);
285 groupPanel.validate();
289 // This routine adds and removes checkboxes depending on
290 // Group selection states
291 void resetTable(boolean groupsChanged)
293 SequenceFeature[] tmpfeatures;
294 String group = null, type;
295 Vector visibleChecks = new Vector();
296 AlignmentI alignment = av.getAlignment();
297 for (int i = 0; i < alignment.getHeight(); i++)
299 if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
304 tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
306 while (index < tmpfeatures.length)
308 group = tmpfeatures[index].featureGroup;
310 if (group == null || fr.featureGroups.get(group) == null
311 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
313 type = tmpfeatures[index].getType();
314 if (!visibleChecks.contains(type))
316 visibleChecks.addElement(type);
324 int cSize = featurePanel.getComponentCount();
326 // This will remove any checkboxes which shouldn't be
328 for (int i = 0; i < cSize; i++)
330 comps = featurePanel.getComponents();
331 check = (MyCheckbox) comps[i];
332 if (!visibleChecks.contains(check.type))
334 featurePanel.remove(i);
340 if (fr.renderOrder != null)
342 // First add the checks in the previous render order,
343 // in case the window has been closed and reopened
344 for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
346 String item = fr.renderOrder[ro];
348 if (!visibleChecks.contains(item))
353 visibleChecks.removeElement(item);
355 addCheck(false, item);
359 // now add checkboxes which should be visible,
360 // if they have not already been added
361 Enumeration en = visibleChecks.elements();
363 while (en.hasMoreElements())
365 addCheck(groupsChanged, en.nextElement().toString());
368 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
370 featurePanel.validate();
372 if (scrollPane != null)
374 scrollPane.validate();
377 itemStateChanged(null);
381 * update the checklist of feature types with the given type
383 * @param groupsChanged
384 * true means if the type is not in the display list then it will be
385 * added and displayed
387 * feature type to be checked for in the list.
389 void addCheck(boolean groupsChanged, String type)
392 Component[] comps = featurePanel.getComponents();
395 for (int i = 0; i < featurePanel.getComponentCount(); i++)
397 check = (MyCheckbox) comps[i];
398 if (check.type.equals(type))
407 boolean selected = false;
408 if (groupsChanged || av.featuresDisplayed.containsKey(type))
413 check = new MyCheckbox(
416 (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
417 fr.getFeatureStyle(type));
419 check.addMouseListener(this);
420 check.addMouseMotionListener(this);
421 check.addItemListener(this);
424 // add at beginning of stack.
425 featurePanel.add(check, 0);
429 // add at end of stack.
430 featurePanel.add(check);
435 public void actionPerformed(ActionEvent evt)
437 for (int i = 0; i < featurePanel.getComponentCount(); i++)
439 Checkbox check = (Checkbox) featurePanel.getComponent(i);
440 check.setState(!check.getState());
445 public void itemStateChanged(ItemEvent evt)
449 // Is the source a top level featureGroup?
450 Checkbox source = (Checkbox) evt.getSource();
451 if (fr.featureGroups.containsKey(source.getLabel()))
453 fr.featureGroups.put(source.getLabel(),
454 new Boolean(source.getState()));
455 ap.seqPanel.seqCanvas.repaint();
456 if (ap.overviewPanel != null)
458 ap.overviewPanel.updateOverviewImage();
468 void selectionChanged()
470 Component[] comps = featurePanel.getComponents();
471 int cSize = comps.length;
473 Object[][] tmp = new Object[cSize][3];
475 for (int i = 0; i < cSize; i++)
477 MyCheckbox check = (MyCheckbox) comps[i];
478 tmp[tmpSize][0] = check.type;
479 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
480 tmp[tmpSize][2] = new Boolean(check.getState());
484 Object[][] data = new Object[tmpSize][3];
485 System.arraycopy(tmp, 0, data, 0, tmpSize);
487 fr.setFeaturePriority(data);
489 ap.paintAlignment(true);
492 MyCheckbox selectedCheck;
494 boolean dragging = false;
496 public void mousePressed(MouseEvent evt)
499 selectedCheck = (MyCheckbox) evt.getSource();
501 if (fr.featureLinks != null
502 && fr.featureLinks.containsKey(selectedCheck.type))
504 if (evt.getX() > selectedCheck.stringWidth + 20)
512 public void mouseDragged(MouseEvent evt)
514 if (((Component) evt.getSource()).getParent() != featurePanel)
521 public void mouseReleased(MouseEvent evt)
523 if (((Component) evt.getSource()).getParent() != featurePanel)
528 Component comp = null;
529 Checkbox target = null;
531 int height = evt.getY() + evt.getComponent().getLocation().y;
533 if (height > featurePanel.getSize().height)
537 .getComponent(featurePanel.getComponentCount() - 1);
541 comp = featurePanel.getComponent(0);
545 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
546 + evt.getComponent().getLocation().y);
549 if (comp != null && comp instanceof Checkbox)
551 target = (Checkbox) comp;
554 if (selectedCheck != null && target != null && selectedCheck != target)
556 int targetIndex = -1;
557 for (int i = 0; i < featurePanel.getComponentCount(); i++)
559 if (target == featurePanel.getComponent(i))
566 featurePanel.remove(selectedCheck);
567 featurePanel.add(selectedCheck, targetIndex);
568 featurePanel.validate();
569 itemStateChanged(null);
573 public void setUserColour(String feature, Object originalColour)
575 if (originalColour instanceof Color
576 || originalColour instanceof GraduatedColor)
578 fr.setColour(feature, originalColour);
583 "Implementation error: Unsupported feature colour object.");
588 public void refreshTable()
590 featurePanel.removeAll();
592 ap.paintAlignment(true);
595 public void mouseEntered(MouseEvent evt)
599 public void mouseExited(MouseEvent evt)
603 public void mouseClicked(MouseEvent evt)
605 MyCheckbox check = (MyCheckbox) evt.getSource();
606 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
608 this.popupSort(check, fr.minmax, evt.getX(), evt.getY());
610 if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
612 if (evt.getX() > check.stringWidth + 20)
615 String link = fr.featureLinks.get(check.type).toString();
616 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1),
617 link.substring(0, link.indexOf("|")));
621 if (check.getParent() != featurePanel)
626 if (evt.getClickCount() > 1)
628 Object fcol = fr.getFeatureStyle(check.type);
629 if (fcol instanceof Color)
631 new UserDefinedColours(this, check.type, (Color) fcol);
635 new FeatureColourChooser(this, check.type);
636 // write back the current colour object to update the table
637 check.updateColor(fr.getFeatureStyle(check.type));
642 public void mouseMoved(MouseEvent evt)
646 public void adjustmentValueChanged(AdjustmentEvent evt)
648 fr.transparency = ((float) (100 - transparency.getValue()) / 100f);
649 ap.seqPanel.seqCanvas.repaint();
653 class MyCheckbox extends Checkbox
657 public int stringWidth;
665 public void updateColor(Object newcol)
667 if (newcol instanceof Color)
669 col = (Color) newcol;
672 else if (newcol instanceof GraduatedColor)
674 gcol = (GraduatedColor) newcol;
679 throw new Error("Invalid color for MyCheckBox");
687 String vlabel = type;
688 if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
691 + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
694 if (gcol.isColourByLabel())
696 setBackground(Color.white);
697 vlabel += " (by Label)";
701 setBackground(gcol.getMinColor());
703 this.setLabel(vlabel);
708 public MyCheckbox(String label, boolean checked, boolean haslink)
710 super(label, checked);
712 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
713 stringWidth = fm.stringWidth(label);
714 this.hasLink = haslink;
717 public MyCheckbox(String type, boolean selected, boolean b,
720 this(type, selected, b);
721 updateColor(featureStyle);
724 public void paint(Graphics g)
726 Dimension d = getSize();
729 if (gcol.isColourByLabel())
731 g.setColor(Color.white);
732 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
734 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
737 * // g.setFont(g.getFont().deriveFont( //
738 * AffineTransform.getScaleInstance( //
739 * width/g.getFontMetrics().stringWidth("Label"), //
740 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
747 Color maxCol = gcol.getMaxColor();
749 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
756 g.drawImage(linkImage, stringWidth + 25,
757 (getSize().height - linkImage.getHeight(this)) / 2, this);
762 protected void sortByDens(String[] typ)
764 sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
767 private String[] getDisplayedFeatureTypes()
772 synchronized (fr.renderOrder)
774 typ = new String[fr.renderOrder.length];
775 System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
776 for (int i = 0; i < typ.length; i++)
778 if (av.featuresDisplayed.get(typ[i]) == null)
788 protected void sortBy(String[] typ, String methodText, final String method)
792 typ = getDisplayedFeatureTypes();
795 gps = fr.getGroups(true);
798 for (int i = 0; i < typ.length; i++)
800 System.err.println("Sorting on Types:" + typ[i]);
806 for (int i = 0; i < gps.length; i++)
808 System.err.println("Sorting on groups:" + gps[i]);
811 AlignmentPanel alignPanel = ap;
812 AlignmentI al = alignPanel.av.getAlignment();
815 SequenceGroup sg = alignPanel.av.getSelectionGroup();
818 start = sg.getStartRes();
819 stop = sg.getEndRes();
824 stop = al.getWidth();
826 SequenceI[] oldOrder = al.getSequencesArray();
827 AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
828 this.ap.alignFrame.addHistoryItem(new OrderCommand(methodText,
829 oldOrder, alignPanel.av.getAlignment()));
830 alignPanel.paintAlignment(true);
834 protected void sortByScore(String[] typ)
836 sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);