2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3 * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, 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
267 // displayed and new groups present
268 Enumeration gps = fr.featureGroups.keys();
269 while (gps.hasMoreElements())
271 String group = (String) gps.nextElement();
272 Boolean vis = (Boolean) fr.featureGroups.get(group);
273 Checkbox check = new MyCheckbox(group, vis.booleanValue(),
274 (fr.featureLinks != null && fr.featureLinks
275 .containsKey(group)));
276 check.addMouseListener(this);
277 check.setFont(new Font("Serif", Font.BOLD, 12));
278 check.addItemListener(this);
279 check.setVisible(fr.hiddenGroups.contains(group));
280 groupPanel.add(check);
284 groupPanel.validate();
288 // This routine adds and removes checkboxes depending on
289 // Group selection states
290 void resetTable(boolean groupsChanged)
292 SequenceFeature[] tmpfeatures;
293 String group = null, type;
294 Vector visibleChecks = new Vector();
295 AlignmentI alignment = av.getAlignment();
296 for (int i = 0; i < alignment.getHeight(); i++)
298 if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
303 tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
305 while (index < tmpfeatures.length)
307 group = tmpfeatures[index].featureGroup;
309 if (group == null || fr.featureGroups.get(group) == null
310 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
312 type = tmpfeatures[index].getType();
313 if (!visibleChecks.contains(type))
315 visibleChecks.addElement(type);
323 int cSize = featurePanel.getComponentCount();
325 // This will remove any checkboxes which shouldn't be
327 for (int i = 0; i < cSize; i++)
329 comps = featurePanel.getComponents();
330 check = (MyCheckbox) comps[i];
331 if (!visibleChecks.contains(check.type))
333 featurePanel.remove(i);
339 if (fr.renderOrder != null)
341 // First add the checks in the previous render order,
342 // in case the window has been closed and reopened
343 for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
345 String item = fr.renderOrder[ro];
347 if (!visibleChecks.contains(item))
352 visibleChecks.removeElement(item);
354 addCheck(false, item);
358 // now add checkboxes which should be visible,
359 // if they have not already been added
360 Enumeration en = visibleChecks.elements();
362 while (en.hasMoreElements())
364 addCheck(groupsChanged, en.nextElement().toString());
367 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
369 featurePanel.validate();
371 if (scrollPane != null)
373 scrollPane.validate();
376 itemStateChanged(null);
380 * update the checklist of feature types with the given type
382 * @param groupsChanged
383 * true means if the type is not in the display list then it will be
384 * added and displayed
386 * feature type to be checked for in the list.
388 void addCheck(boolean groupsChanged, String type)
391 Component[] comps = featurePanel.getComponents();
394 for (int i = 0; i < featurePanel.getComponentCount(); i++)
396 check = (MyCheckbox) comps[i];
397 if (check.type.equals(type))
406 boolean selected = false;
407 if (groupsChanged || av.featuresDisplayed.containsKey(type))
412 check = new MyCheckbox(
415 (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
416 fr.getFeatureStyle(type));
418 check.addMouseListener(this);
419 check.addMouseMotionListener(this);
420 check.addItemListener(this);
423 // add at beginning of stack.
424 featurePanel.add(check, 0);
428 // add at end of stack.
429 featurePanel.add(check);
434 public void actionPerformed(ActionEvent evt)
436 for (int i = 0; i < featurePanel.getComponentCount(); i++)
438 Checkbox check = (Checkbox) featurePanel.getComponent(i);
439 check.setState(!check.getState());
444 public void itemStateChanged(ItemEvent evt)
448 // Is the source a top level featureGroup?
449 Checkbox source = (Checkbox) evt.getSource();
450 if (fr.featureGroups.containsKey(source.getLabel()))
452 fr.featureGroups.put(source.getLabel(),
453 new Boolean(source.getState()));
454 ap.seqPanel.seqCanvas.repaint();
455 if (ap.overviewPanel != null)
457 ap.overviewPanel.updateOverviewImage();
467 void selectionChanged()
469 Component[] comps = featurePanel.getComponents();
470 int cSize = comps.length;
472 Object[][] tmp = new Object[cSize][3];
474 for (int i = 0; i < cSize; i++)
476 MyCheckbox check = (MyCheckbox) comps[i];
477 tmp[tmpSize][0] = check.type;
478 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
479 tmp[tmpSize][2] = new Boolean(check.getState());
483 Object[][] data = new Object[tmpSize][3];
484 System.arraycopy(tmp, 0, data, 0, tmpSize);
486 fr.setFeaturePriority(data);
488 ap.paintAlignment(true);
491 MyCheckbox selectedCheck;
493 boolean dragging = false;
495 public void mousePressed(MouseEvent evt)
498 selectedCheck = (MyCheckbox) evt.getSource();
500 if (fr.featureLinks != null
501 && fr.featureLinks.containsKey(selectedCheck.type))
503 if (evt.getX() > selectedCheck.stringWidth + 20)
511 public void mouseDragged(MouseEvent evt)
513 if (((Component) evt.getSource()).getParent() != featurePanel)
520 public void mouseReleased(MouseEvent evt)
522 if (((Component) evt.getSource()).getParent() != featurePanel)
527 Component comp = null;
528 Checkbox target = null;
530 int height = evt.getY() + evt.getComponent().getLocation().y;
532 if (height > featurePanel.getSize().height)
536 .getComponent(featurePanel.getComponentCount() - 1);
540 comp = featurePanel.getComponent(0);
544 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
545 + evt.getComponent().getLocation().y);
548 if (comp != null && comp instanceof Checkbox)
550 target = (Checkbox) comp;
553 if (selectedCheck != null && target != null && selectedCheck != target)
555 int targetIndex = -1;
556 for (int i = 0; i < featurePanel.getComponentCount(); i++)
558 if (target == featurePanel.getComponent(i))
565 featurePanel.remove(selectedCheck);
566 featurePanel.add(selectedCheck, targetIndex);
567 featurePanel.validate();
568 itemStateChanged(null);
572 public void setUserColour(String feature, Object originalColour)
574 if (originalColour instanceof Color
575 || originalColour instanceof GraduatedColor)
577 fr.setColour(feature, originalColour);
582 "Implementation error: Unsupported feature colour object.");
587 public void refreshTable()
589 featurePanel.removeAll();
591 ap.paintAlignment(true);
594 public void mouseEntered(MouseEvent evt)
598 public void mouseExited(MouseEvent evt)
602 public void mouseClicked(MouseEvent evt)
604 MyCheckbox check = (MyCheckbox) evt.getSource();
605 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
607 this.popupSort(check, fr.minmax, evt.getX(), evt.getY());
609 if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
611 if (evt.getX() > check.stringWidth + 20)
614 String link = fr.featureLinks.get(check.type).toString();
615 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1),
616 link.substring(0, link.indexOf("|")));
620 if (check.getParent() != featurePanel)
625 if (evt.getClickCount() > 1)
627 Object fcol = fr.getFeatureStyle(check.type);
628 if (fcol instanceof Color)
630 new UserDefinedColours(this, check.type, (Color) fcol);
634 new FeatureColourChooser(this, check.type);
635 // write back the current colour object to update the table
636 check.updateColor(fr.getFeatureStyle(check.type));
641 public void mouseMoved(MouseEvent evt)
645 public void adjustmentValueChanged(AdjustmentEvent evt)
647 fr.transparency = ((float) (100 - transparency.getValue()) / 100f);
648 ap.seqPanel.seqCanvas.repaint();
652 class MyCheckbox extends Checkbox
656 public int stringWidth;
664 public void updateColor(Object newcol)
666 if (newcol instanceof Color)
668 col = (Color) newcol;
671 else if (newcol instanceof GraduatedColor)
673 gcol = (GraduatedColor) newcol;
678 throw new Error("Invalid color for MyCheckBox");
686 String vlabel = type;
687 if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
690 + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
693 if (gcol.isColourByLabel())
695 setBackground(Color.white);
696 vlabel += " (by Label)";
700 setBackground(gcol.getMinColor());
702 this.setLabel(vlabel);
707 public MyCheckbox(String label, boolean checked, boolean haslink)
709 super(label, checked);
711 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
712 stringWidth = fm.stringWidth(label);
713 this.hasLink = haslink;
716 public MyCheckbox(String type, boolean selected, boolean b,
719 this(type, selected, b);
720 updateColor(featureStyle);
723 public void paint(Graphics g)
725 Dimension d = getSize();
728 if (gcol.isColourByLabel())
730 g.setColor(Color.white);
731 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
733 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
736 * // g.setFont(g.getFont().deriveFont( //
737 * AffineTransform.getScaleInstance( //
738 * width/g.getFontMetrics().stringWidth("Label"), //
739 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
746 Color maxCol = gcol.getMaxColor();
748 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
755 g.drawImage(linkImage, stringWidth + 25,
756 (getSize().height - linkImage.getHeight(this)) / 2, this);
761 protected void sortByDens(String[] typ)
763 sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
766 private String[] getDisplayedFeatureTypes()
771 synchronized (fr.renderOrder)
773 typ = new String[fr.renderOrder.length];
774 System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
775 for (int i = 0; i < typ.length; i++)
777 if (av.featuresDisplayed.get(typ[i]) == null)
787 protected void sortBy(String[] typ, String methodText, final String method)
791 typ = getDisplayedFeatureTypes();
794 gps = fr.getGroups(true);
797 for (int i = 0; i < typ.length; i++)
799 System.err.println("Sorting on Types:" + typ[i]);
805 for (int i = 0; i < gps.length; i++)
807 System.err.println("Sorting on groups:" + gps[i]);
810 AlignmentPanel alignPanel = ap;
811 AlignmentI al = alignPanel.av.getAlignment();
814 SequenceGroup sg = alignPanel.av.getSelectionGroup();
817 start = sg.getStartRes();
818 stop = sg.getEndRes();
823 stop = al.getWidth();
825 SequenceI[] oldOrder = al.getSequencesArray();
826 AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
827 this.ap.alignFrame.addHistoryItem(new OrderCommand(methodText,
828 oldOrder, alignPanel.av.getAlignment()));
829 alignPanel.paintAlignment(true);
833 protected void sortByScore(String[] typ)
835 sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);