2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3 * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
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/>.
18 package jalview.appletgui;
23 import java.awt.event.*;
25 import jalview.analysis.AlignmentSorter;
26 import jalview.commands.OrderCommand;
27 import jalview.datamodel.*;
28 import jalview.schemes.AnnotationColourGradient;
29 import jalview.schemes.GraduatedColor;
31 public class FeatureSettings extends Panel implements ItemListener,
32 MouseListener, MouseMotionListener, ActionListener,
45 Panel featurePanel = new Panel();
47 ScrollPane scrollPane;
49 boolean alignmentHasFeatures = false;
53 Scrollbar transparency;
55 public FeatureSettings(final AlignmentPanel ap)
59 ap.av.featureSettings = this;
60 fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
62 transparency = new Scrollbar(Scrollbar.HORIZONTAL,
63 100 - (int) (fr.transparency * 100), 1, 1, 100);
65 if (fr.transparencySetter != null)
67 transparency.addAdjustmentListener(this);
71 transparency.setEnabled(false);
74 java.net.URL url = getClass().getResource("/images/link.gif");
77 linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
80 if (av.featuresDisplayed == null)
87 this.setLayout(new BorderLayout());
88 scrollPane = new ScrollPane();
89 scrollPane.add(featurePanel);
90 if (alignmentHasFeatures)
92 add(scrollPane, BorderLayout.CENTER);
95 Button invert = new Button("Invert Selection");
96 invert.addActionListener(this);
98 Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
99 lowerPanel.add(invert);
101 Panel tPanel = new Panel(new BorderLayout());
103 if (fr.transparencySetter != null)
105 tPanel.add(transparency, BorderLayout.CENTER);
106 tPanel.add(new Label("Transparency"), BorderLayout.EAST);
111 new Label("Transparency not available in this web browser"),
112 BorderLayout.CENTER);
115 lowerPanel.add(tPanel, BorderLayout.SOUTH);
117 add(lowerPanel, BorderLayout.SOUTH);
119 if (groupPanel != null)
122 .setLayout(new GridLayout(
123 (fr.featureGroups.size() - fr.hiddenGroups.size()) / 4 + 1,
125 groupPanel.validate();
127 add(groupPanel, BorderLayout.NORTH);
131 final FeatureSettings me = this;
132 frame.addWindowListener(new WindowAdapter()
134 public void windowClosing(WindowEvent e)
136 if (me.av.featureSettings == me)
138 me.av.featureSettings = null;
144 int height = featurePanel.getComponentCount() * 50 + 60;
146 height = Math.max(200, height);
147 height = Math.min(400, height);
149 jalview.bin.JalviewLite.addFrame(frame, "Feature Settings", width,
153 public void paint(Graphics g)
155 g.setColor(Color.black);
156 g.drawString("No Features added to this alignment!!", 10, 20);
157 g.drawString("(Features can be added from searches or", 10, 40);
158 g.drawString("from Jalview / GFF features files)", 10, 60);
161 protected void popupSort(final MyCheckbox check, final Hashtable minmax,
164 final String type = check.type;
165 final Object typeCol = fr.getFeatureStyle(type);
166 java.awt.PopupMenu men = new PopupMenu("Settings for " + type);
167 java.awt.MenuItem scr = new MenuItem("Sort by Score");
169 final FeatureSettings me = this;
170 scr.addActionListener(new ActionListener()
173 public void actionPerformed(ActionEvent e)
175 me.sortByScore(new String[]
180 MenuItem dens = new MenuItem("Sort by Density");
181 dens.addActionListener(new ActionListener()
184 public void actionPerformed(ActionEvent e)
186 me.sortByDens(new String[]
194 final Object typeMinMax = minmax.get(type);
196 * final java.awt.CheckboxMenuItem chb = new
197 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
198 * moment chb.setState(minmax.get(type) != null);
199 * chb.addActionListener(new ActionListener() {
201 * public void actionPerformed(ActionEvent e) {
202 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
203 * null); } else { minmax.put(type, typeMinMax); } }
207 if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
209 // graduated colourschemes for those where minmax exists for the
210 // positional features
211 MenuItem mxcol = new MenuItem(
212 (typeCol instanceof Color) ? "Graduated Colour"
215 mxcol.addActionListener(new ActionListener()
218 public void actionPerformed(ActionEvent e)
220 if (typeCol instanceof Color)
222 new FeatureColourChooser(me, type);
223 // write back the current colour object to update the table
224 check.updateColor(fr.getFeatureStyle(type));
228 new UserDefinedColours(me, check.type,
229 ((GraduatedColor) typeCol));
236 this.featurePanel.add(men);
237 men.show(this.featurePanel, x, y);
240 public void setTableData()
242 alignmentHasFeatures = fr.buildGroupHash();
243 if (alignmentHasFeatures)
252 * rebuilds the group panel
254 public void rebuildGroups()
256 boolean rdrw = false;
257 if (groupPanel == null)
259 groupPanel = new Panel();
264 groupPanel.removeAll();
266 // TODO: JAL-964 - smoothly incorporate new group entries if panel already displayed and new groups present
267 Enumeration gps = fr.featureGroups.keys();
268 while (gps.hasMoreElements())
270 String group = (String) gps.nextElement();
271 Boolean vis = (Boolean) fr.featureGroups.get(group);
272 Checkbox check = new MyCheckbox(group, vis.booleanValue(),
273 (fr.featureLinks != null && fr.featureLinks
274 .containsKey(group)));
275 check.addMouseListener(this);
276 check.setFont(new Font("Serif", Font.BOLD, 12));
277 check.addItemListener(this);
278 check.setVisible(fr.hiddenGroups.contains(group));
279 groupPanel.add(check);
283 groupPanel.validate();
287 // This routine adds and removes checkboxes depending on
288 // Group selection states
289 void resetTable(boolean groupsChanged)
291 SequenceFeature[] tmpfeatures;
292 String group = null, type;
293 Vector visibleChecks = new Vector();
294 AlignmentI alignment=av.getAlignment();
295 for (int i = 0; i < alignment.getHeight(); i++)
297 if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
302 tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
304 while (index < tmpfeatures.length)
306 group = tmpfeatures[index].featureGroup;
308 if (group == null || fr.featureGroups.get(group) == null
309 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
311 type = tmpfeatures[index].getType();
312 if (!visibleChecks.contains(type))
314 visibleChecks.addElement(type);
322 int cSize = featurePanel.getComponentCount();
324 // This will remove any checkboxes which shouldn't be
326 for (int i = 0; i < cSize; i++)
328 comps = featurePanel.getComponents();
329 check = (MyCheckbox) comps[i];
330 if (!visibleChecks.contains(check.type))
332 featurePanel.remove(i);
338 if (fr.renderOrder != null)
340 // First add the checks in the previous render order,
341 // in case the window has been closed and reopened
342 for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
344 String item = fr.renderOrder[ro];
346 if (!visibleChecks.contains(item))
351 visibleChecks.removeElement(item);
353 addCheck(false, item);
357 // now add checkboxes which should be visible,
358 // if they have not already been added
359 Enumeration en = visibleChecks.elements();
361 while (en.hasMoreElements())
363 addCheck(groupsChanged, en.nextElement().toString());
366 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
368 featurePanel.validate();
370 if (scrollPane != null)
372 scrollPane.validate();
375 itemStateChanged(null);
379 * update the checklist of feature types with the given type
381 * @param groupsChanged
382 * true means if the type is not in the display list then it will be
383 * added and displayed
385 * feature type to be checked for in the list.
387 void addCheck(boolean groupsChanged, String type)
390 Component[] comps = featurePanel.getComponents();
393 for (int i = 0; i < featurePanel.getComponentCount(); i++)
395 check = (MyCheckbox) comps[i];
396 if (check.type.equals(type))
405 boolean selected = false;
406 if (groupsChanged || av.featuresDisplayed.containsKey(type))
411 check = new MyCheckbox(
414 (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
415 fr.getFeatureStyle(type));
417 check.addMouseListener(this);
418 check.addMouseMotionListener(this);
419 check.addItemListener(this);
422 // add at beginning of stack.
423 featurePanel.add(check, 0);
427 // add at end of stack.
428 featurePanel.add(check);
433 public void actionPerformed(ActionEvent evt)
435 for (int i = 0; i < featurePanel.getComponentCount(); i++)
437 Checkbox check = (Checkbox) featurePanel.getComponent(i);
438 check.setState(!check.getState());
443 public void itemStateChanged(ItemEvent evt)
447 // Is the source a top level featureGroup?
448 Checkbox source = (Checkbox) evt.getSource();
449 if (fr.featureGroups.containsKey(source.getLabel()))
451 fr.featureGroups.put(source.getLabel(),
452 new Boolean(source.getState()));
453 ap.seqPanel.seqCanvas.repaint();
454 if (ap.overviewPanel != null)
456 ap.overviewPanel.updateOverviewImage();
466 void selectionChanged()
468 Component[] comps = featurePanel.getComponents();
469 int cSize = comps.length;
471 Object[][] tmp = new Object[cSize][3];
473 for (int i = 0; i < cSize; i++)
475 MyCheckbox check = (MyCheckbox) comps[i];
476 tmp[tmpSize][0] = check.type;
477 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
478 tmp[tmpSize][2] = new Boolean(check.getState());
482 Object[][] data = new Object[tmpSize][3];
483 System.arraycopy(tmp, 0, data, 0, tmpSize);
485 fr.setFeaturePriority(data);
487 ap.paintAlignment(true);
490 MyCheckbox selectedCheck;
492 boolean dragging = false;
494 public void mousePressed(MouseEvent evt)
497 selectedCheck = (MyCheckbox) evt.getSource();
499 if (fr.featureLinks != null
500 && fr.featureLinks.containsKey(selectedCheck.type))
502 if (evt.getX() > selectedCheck.stringWidth + 20)
510 public void mouseDragged(MouseEvent evt)
512 if (((Component) evt.getSource()).getParent() != featurePanel)
519 public void mouseReleased(MouseEvent evt)
521 if (((Component) evt.getSource()).getParent() != featurePanel)
526 Component comp = null;
527 Checkbox target = null;
529 int height = evt.getY() + evt.getComponent().getLocation().y;
531 if (height > featurePanel.getSize().height)
535 .getComponent(featurePanel.getComponentCount() - 1);
539 comp = featurePanel.getComponent(0);
543 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
544 + evt.getComponent().getLocation().y);
547 if (comp != null && comp instanceof Checkbox)
549 target = (Checkbox) comp;
552 if (selectedCheck != null && target != null && selectedCheck != target)
554 int targetIndex = -1;
555 for (int i = 0; i < featurePanel.getComponentCount(); i++)
557 if (target == featurePanel.getComponent(i))
564 featurePanel.remove(selectedCheck);
565 featurePanel.add(selectedCheck, targetIndex);
566 featurePanel.validate();
567 itemStateChanged(null);
571 public void setUserColour(String feature, Object originalColour)
573 if (originalColour instanceof Color
574 || originalColour instanceof GraduatedColor)
576 fr.setColour(feature, originalColour);
581 "Implementation error: Unsupported feature colour object.");
586 public void refreshTable()
588 featurePanel.removeAll();
590 ap.paintAlignment(true);
593 public void mouseEntered(MouseEvent evt)
597 public void mouseExited(MouseEvent evt)
601 public void mouseClicked(MouseEvent evt)
603 MyCheckbox check = (MyCheckbox) evt.getSource();
604 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
606 this.popupSort(check, fr.minmax, evt.getX(), evt.getY());
608 if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
610 if (evt.getX() > check.stringWidth + 20)
613 String link = fr.featureLinks.get(check.type).toString();
614 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1),
615 link.substring(0, link.indexOf("|")));
619 if (check.getParent() != featurePanel)
624 if (evt.getClickCount() > 1)
626 Object fcol = fr.getFeatureStyle(check.type);
627 if (fcol instanceof Color)
629 new UserDefinedColours(this, check.type, (Color) fcol);
633 new FeatureColourChooser(this, check.type);
634 // write back the current colour object to update the table
635 check.updateColor(fr.getFeatureStyle(check.type));
640 public void mouseMoved(MouseEvent evt)
644 public void adjustmentValueChanged(AdjustmentEvent evt)
646 fr.transparency = ((float) (100 - transparency.getValue()) / 100f);
647 ap.seqPanel.seqCanvas.repaint();
651 class MyCheckbox extends Checkbox
655 public int stringWidth;
663 public void updateColor(Object newcol)
665 if (newcol instanceof Color)
667 col = (Color) newcol;
670 else if (newcol instanceof GraduatedColor)
672 gcol = (GraduatedColor) newcol;
677 throw new Error("Invalid color for MyCheckBox");
685 String vlabel = type;
686 if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
689 + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
692 if (gcol.isColourByLabel())
694 setBackground(Color.white);
695 vlabel += " (by Label)";
699 setBackground(gcol.getMinColor());
701 this.setLabel(vlabel);
706 public MyCheckbox(String label, boolean checked, boolean haslink)
708 super(label, checked);
710 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
711 stringWidth = fm.stringWidth(label);
712 this.hasLink = haslink;
715 public MyCheckbox(String type, boolean selected, boolean b,
718 this(type, selected, b);
719 updateColor(featureStyle);
722 public void paint(Graphics g)
724 Dimension d = getSize();
727 if (gcol.isColourByLabel())
729 g.setColor(Color.white);
730 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
732 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
735 * // g.setFont(g.getFont().deriveFont( //
736 * AffineTransform.getScaleInstance( //
737 * width/g.getFontMetrics().stringWidth("Label"), //
738 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
745 Color maxCol = gcol.getMaxColor();
747 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
754 g.drawImage(linkImage, stringWidth + 25,
755 (getSize().height - linkImage.getHeight(this)) / 2, this);
760 protected void sortByDens(String[] typ)
762 sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
765 private String[] getDisplayedFeatureTypes()
770 synchronized (fr.renderOrder)
772 typ = new String[fr.renderOrder.length];
773 System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
774 for (int i = 0; i < typ.length; i++)
776 if (av.featuresDisplayed.get(typ[i]) == null)
786 protected void sortBy(String[] typ, String methodText, final String method)
790 typ = getDisplayedFeatureTypes();
793 gps = fr.getGroups(true);
796 for (int i = 0; i < typ.length; i++)
798 System.err.println("Sorting on Types:" + typ[i]);
804 for (int i = 0; i < gps.length; i++)
806 System.err.println("Sorting on groups:" + gps[i]);
809 AlignmentPanel alignPanel = ap;
810 AlignmentI al = alignPanel.av.getAlignment();
813 SequenceGroup sg = alignPanel.av.getSelectionGroup();
816 start = sg.getStartRes();
817 stop = sg.getEndRes();
822 stop = al.getWidth();
824 SequenceI[] oldOrder = al.getSequencesArray();
825 AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
826 this.ap.alignFrame.addHistoryItem(new OrderCommand(methodText,
827 oldOrder, alignPanel.av.getAlignment()));
828 alignPanel.paintAlignment(true);
832 protected void sortByScore(String[] typ)
834 sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);