2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5.1)
3 * Copyright (C) 2010 J Procter, AM Waterhouse, 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)
121 groupPanel.setLayout(new GridLayout(fr.featureGroups.size() / 4 + 1,
123 groupPanel.validate();
125 add(groupPanel, BorderLayout.NORTH);
129 final FeatureSettings me = this;
130 frame.addWindowListener(new WindowAdapter()
132 public void windowClosing(WindowEvent e)
134 if (me.av.featureSettings == me)
136 me.av.featureSettings = null;
142 int height = featurePanel.getComponentCount() * 50 + 60;
144 height = Math.max(200, height);
145 height = Math.min(400, height);
147 jalview.bin.JalviewLite.addFrame(frame, "Feature Settings", width,
151 public void paint(Graphics g)
153 g.setColor(Color.black);
154 g.drawString("No Features added to this alignment!!", 10, 20);
155 g.drawString("(Features can be added from searches or", 10, 40);
156 g.drawString("from Jalview / GFF features files)", 10, 60);
159 protected void popupSort(final MyCheckbox check, final Hashtable minmax,
162 final String type = check.type;
163 final Object typeCol = fr.getFeatureStyle(type);
164 java.awt.PopupMenu men = new PopupMenu("Settings for " + type);
165 java.awt.MenuItem scr = new MenuItem("Sort by Score");
167 final FeatureSettings me = this;
168 scr.addActionListener(new ActionListener()
171 public void actionPerformed(ActionEvent e)
173 me.sortByScore(new String[]
178 MenuItem dens = new MenuItem("Sort by Density");
179 dens.addActionListener(new ActionListener()
182 public void actionPerformed(ActionEvent e)
184 me.sortByDens(new String[]
192 final Object typeMinMax = minmax.get(type);
194 * final java.awt.CheckboxMenuItem chb = new
195 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
196 * moment chb.setState(minmax.get(type) != null);
197 * chb.addActionListener(new ActionListener() {
199 * public void actionPerformed(ActionEvent e) {
200 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
201 * null); } else { minmax.put(type, typeMinMax); } }
205 if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
207 // graduated colourschemes for those where minmax exists for the
208 // positional features
209 MenuItem mxcol = new MenuItem(
210 (typeCol instanceof Color) ? "Graduated Colour"
213 mxcol.addActionListener(new ActionListener()
216 public void actionPerformed(ActionEvent e)
218 if (typeCol instanceof Color)
220 new FeatureColourChooser(me, type);
221 // write back the current colour object to update the table
222 check.updateColor(fr.getFeatureStyle(type));
226 new UserDefinedColours(me, check.type,
227 ((GraduatedColor) typeCol));
234 this.featurePanel.add(men);
235 men.show(this.featurePanel, x, y);
238 public void setTableData()
240 alignmentHasFeatures = fr.buildGroupHash();
241 if (alignmentHasFeatures)
250 * rebuilds the group panel
252 public void rebuildGroups()
254 boolean rdrw = false;
255 if (groupPanel == null)
257 groupPanel = new Panel();
262 groupPanel.removeAll();
265 Enumeration gps = fr.featureGroups.keys();
266 while (gps.hasMoreElements())
268 String group = (String) gps.nextElement();
269 Boolean vis = (Boolean) fr.featureGroups.get(group);
270 Checkbox check = new MyCheckbox(group, vis.booleanValue(),
271 (fr.featureLinks != null && fr.featureLinks
272 .containsKey(group)));
273 check.addMouseListener(this);
274 check.setFont(new Font("Serif", Font.BOLD, 12));
275 check.addItemListener(this);
276 groupPanel.add(check);
280 groupPanel.validate();
284 // This routine adds and removes checkboxes depending on
285 // Group selection states
286 void resetTable(boolean groupsChanged)
288 SequenceFeature[] tmpfeatures;
289 String group = null, type;
290 Vector visibleChecks = new Vector();
292 for (int i = 0; i < av.alignment.getHeight(); i++)
294 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
299 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
301 while (index < tmpfeatures.length)
303 group = tmpfeatures[index].featureGroup;
305 if (group == null || fr.featureGroups.get(group) == null
306 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
308 type = tmpfeatures[index].getType();
309 if (!visibleChecks.contains(type))
311 visibleChecks.addElement(type);
319 int cSize = featurePanel.getComponentCount();
321 // This will remove any checkboxes which shouldn't be
323 for (int i = 0; i < cSize; i++)
325 comps = featurePanel.getComponents();
326 check = (MyCheckbox) comps[i];
327 if (!visibleChecks.contains(check.type))
329 featurePanel.remove(i);
335 if (fr.renderOrder != null)
337 // First add the checks in the previous render order,
338 // in case the window has been closed and reopened
339 for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
341 String item = fr.renderOrder[ro];
343 if (!visibleChecks.contains(item))
348 visibleChecks.removeElement(item);
350 addCheck(false, item);
354 // now add checkboxes which should be visible,
355 // if they have not already been added
356 Enumeration en = visibleChecks.elements();
358 while (en.hasMoreElements())
360 addCheck(groupsChanged, en.nextElement().toString());
363 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
365 featurePanel.validate();
367 if (scrollPane != null)
369 scrollPane.validate();
372 itemStateChanged(null);
376 * update the checklist of feature types with the given type
378 * @param groupsChanged
379 * true means if the type is not in the display list then it will be
380 * added and displayed
382 * feature type to be checked for in the list.
384 void addCheck(boolean groupsChanged, String type)
387 Component[] comps = featurePanel.getComponents();
390 for (int i = 0; i < featurePanel.getComponentCount(); i++)
392 check = (MyCheckbox) comps[i];
393 if (check.type.equals(type))
402 boolean selected = false;
403 if (groupsChanged || av.featuresDisplayed.containsKey(type))
408 check = new MyCheckbox(
411 (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
412 fr.getFeatureStyle(type));
414 check.addMouseListener(this);
415 check.addMouseMotionListener(this);
416 check.addItemListener(this);
419 // add at beginning of stack.
420 featurePanel.add(check, 0);
424 // add at end of stack.
425 featurePanel.add(check);
430 public void actionPerformed(ActionEvent evt)
432 for (int i = 0; i < featurePanel.getComponentCount(); i++)
434 Checkbox check = (Checkbox) featurePanel.getComponent(i);
435 check.setState(!check.getState());
440 public void itemStateChanged(ItemEvent evt)
444 // Is the source a top level featureGroup?
445 Checkbox source = (Checkbox) evt.getSource();
446 if (fr.featureGroups.containsKey(source.getLabel()))
448 fr.featureGroups.put(source.getLabel(), new Boolean(source
450 ap.seqPanel.seqCanvas.repaint();
451 if (ap.overviewPanel != null)
453 ap.overviewPanel.updateOverviewImage();
463 void selectionChanged()
465 Component[] comps = featurePanel.getComponents();
466 int cSize = comps.length;
468 Object[][] tmp = new Object[cSize][3];
470 for (int i = 0; i < cSize; i++)
472 MyCheckbox check = (MyCheckbox) comps[i];
473 tmp[tmpSize][0] = check.type;
474 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
475 tmp[tmpSize][2] = new Boolean(check.getState());
479 Object[][] data = new Object[tmpSize][3];
480 System.arraycopy(tmp, 0, data, 0, tmpSize);
482 fr.setFeaturePriority(data);
484 ap.paintAlignment(true);
487 MyCheckbox selectedCheck;
489 boolean dragging = false;
491 public void mousePressed(MouseEvent evt)
494 selectedCheck = (MyCheckbox) evt.getSource();
496 if (fr.featureLinks != null
497 && fr.featureLinks.containsKey(selectedCheck.type))
499 if (evt.getX() > selectedCheck.stringWidth + 20)
507 public void mouseDragged(MouseEvent evt)
509 if (((Component) evt.getSource()).getParent() != featurePanel)
516 public void mouseReleased(MouseEvent evt)
518 if (((Component) evt.getSource()).getParent() != featurePanel)
523 Component comp = null;
524 Checkbox target = null;
526 int height = evt.getY() + evt.getComponent().getLocation().y;
528 if (height > featurePanel.getSize().height)
532 .getComponent(featurePanel.getComponentCount() - 1);
536 comp = featurePanel.getComponent(0);
540 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
541 + evt.getComponent().getLocation().y);
544 if (comp != null && comp instanceof Checkbox)
546 target = (Checkbox) comp;
549 if (selectedCheck != null && target != null && selectedCheck != target)
551 int targetIndex = -1;
552 for (int i = 0; i < featurePanel.getComponentCount(); i++)
554 if (target == featurePanel.getComponent(i))
561 featurePanel.remove(selectedCheck);
562 featurePanel.add(selectedCheck, targetIndex);
563 featurePanel.validate();
564 itemStateChanged(null);
568 public void setUserColour(String feature, Object originalColour)
570 if (originalColour instanceof Color
571 || originalColour instanceof GraduatedColor)
573 fr.setColour(feature, originalColour);
578 "Implementation error: Unsupported feature colour object.");
583 public void refreshTable()
585 featurePanel.removeAll();
587 ap.paintAlignment(true);
590 public void mouseEntered(MouseEvent evt)
594 public void mouseExited(MouseEvent evt)
598 public void mouseClicked(MouseEvent evt)
600 MyCheckbox check = (MyCheckbox) evt.getSource();
601 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
603 this.popupSort(check, fr.minmax, evt.getX(), evt.getY());
605 if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
607 if (evt.getX() > check.stringWidth + 20)
610 String link = fr.featureLinks.get(check.type).toString();
611 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1), link
612 .substring(0, link.indexOf("|")));
616 if (check.getParent() != featurePanel)
621 if (evt.getClickCount() > 1)
623 Object fcol = fr.getFeatureStyle(check.type);
624 if (fcol instanceof Color)
626 new UserDefinedColours(this, check.type, (Color) fcol);
630 new FeatureColourChooser(this, check.type);
631 // write back the current colour object to update the table
632 check.updateColor(fr.getFeatureStyle(check.type));
637 public void mouseMoved(MouseEvent evt)
641 public void adjustmentValueChanged(AdjustmentEvent evt)
643 fr.transparency = ((float) (100 - transparency.getValue()) / 100f);
644 ap.seqPanel.seqCanvas.repaint();
648 class MyCheckbox extends Checkbox
652 public int stringWidth;
660 public void updateColor(Object newcol)
662 if (newcol instanceof Color)
664 col = (Color) newcol;
667 else if (newcol instanceof GraduatedColor)
669 gcol = (GraduatedColor) newcol;
674 throw new Error("Invalid color for MyCheckBox");
682 String vlabel = type;
683 if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
686 + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
689 if (gcol.isColourByLabel())
691 setBackground(Color.white);
692 vlabel += " (by Label)";
696 setBackground(gcol.getMinColor());
698 this.setLabel(vlabel);
703 public MyCheckbox(String label, boolean checked, boolean haslink)
705 super(label, checked);
707 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
708 stringWidth = fm.stringWidth(label);
709 this.hasLink = haslink;
712 public MyCheckbox(String type, boolean selected, boolean b,
715 this(type, selected, b);
716 updateColor(featureStyle);
719 public void paint(Graphics g)
721 Dimension d = getSize();
724 if (gcol.isColourByLabel())
726 g.setColor(Color.white);
727 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
729 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
732 * // g.setFont(g.getFont().deriveFont( //
733 * AffineTransform.getScaleInstance( //
734 * width/g.getFontMetrics().stringWidth("Label"), //
735 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
742 Color maxCol = gcol.getMaxColor();
744 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
751 g.drawImage(linkImage, stringWidth + 25,
752 (getSize().height - linkImage.getHeight(this)) / 2, this);
757 protected void sortByDens(String[] typ)
759 sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
762 private String[] getDisplayedFeatureTypes()
767 synchronized (fr.renderOrder)
769 typ = new String[fr.renderOrder.length];
770 System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
771 for (int i = 0; i < typ.length; i++)
773 if (av.featuresDisplayed.get(typ[i]) == null)
783 protected void sortBy(String[] typ, String methodText, final String method)
787 typ = getDisplayedFeatureTypes();
790 gps = fr.getGroups(true);
793 for (int i = 0; i < typ.length; i++)
795 System.err.println("Sorting on Types:" + typ[i]);
801 for (int i = 0; i < gps.length; i++)
803 System.err.println("Sorting on groups:" + gps[i]);
806 AlignmentPanel alignPanel = ap;
807 AlignmentI al = alignPanel.av.getAlignment();
810 SequenceGroup sg = alignPanel.av.getSelectionGroup();
813 start = sg.getStartRes();
814 stop = sg.getEndRes();
819 stop = al.getWidth();
821 SequenceI[] oldOrder = al.getSequencesArray();
822 AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
823 this.ap.alignFrame.addHistoryItem(new OrderCommand(methodText,
824 oldOrder, alignPanel.av.getAlignment()));
825 alignPanel.paintAlignment(true);
829 protected void sortByScore(String[] typ)
831 sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);