2 * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3 * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
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)
122 groupPanel.setLayout(new GridLayout(fr.featureGroups.size() / 4 + 1,
124 groupPanel.validate();
126 add(groupPanel, BorderLayout.NORTH);
130 final FeatureSettings me = this;
131 frame.addWindowListener(new WindowAdapter()
133 public void windowClosing(WindowEvent e)
135 if (me.av.featureSettings == me)
137 me.av.featureSettings = null;
143 int height = featurePanel.getComponentCount() * 50 + 60;
145 height = Math.max(200, height);
146 height = Math.min(400, height);
148 jalview.bin.JalviewLite.addFrame(frame, "Feature Settings", width,
152 public void paint(Graphics g)
154 g.setColor(Color.black);
155 g.drawString("No Features added to this alignment!!", 10, 20);
156 g.drawString("(Features can be added from searches or", 10, 40);
157 g.drawString("from Jalview / GFF features files)", 10, 60);
160 protected void popupSort(final MyCheckbox check, final Hashtable minmax,
163 final String type = check.type;
164 final Object typeCol = fr.getFeatureStyle(type);
165 java.awt.PopupMenu men = new PopupMenu("Settings for " + type);
166 java.awt.MenuItem scr = new MenuItem("Sort by Score");
168 final FeatureSettings me = this;
169 scr.addActionListener(new ActionListener()
172 public void actionPerformed(ActionEvent e)
174 me.sortByScore(new String[]
179 MenuItem dens = new MenuItem("Sort by Density");
180 dens.addActionListener(new ActionListener()
183 public void actionPerformed(ActionEvent e)
185 me.sortByDens(new String[]
193 final Object typeMinMax = minmax.get(type);
195 * final java.awt.CheckboxMenuItem chb = new
196 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
197 * moment chb.setState(minmax.get(type) != null);
198 * chb.addActionListener(new ActionListener() {
200 * public void actionPerformed(ActionEvent e) {
201 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
202 * null); } else { minmax.put(type, typeMinMax); } }
206 if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
208 // graduated colourschemes for those where minmax exists for the
209 // positional features
210 MenuItem mxcol = new MenuItem(
211 (typeCol instanceof Color) ? "Graduated Colour"
214 mxcol.addActionListener(new ActionListener()
217 public void actionPerformed(ActionEvent e)
219 if (typeCol instanceof Color)
221 new FeatureColourChooser(me, type);
222 // write back the current colour object to update the table
223 check.updateColor(fr.getFeatureStyle(type));
227 new UserDefinedColours(me, check.type,
228 ((GraduatedColor) typeCol));
235 this.featurePanel.add(men);
236 men.show(this.featurePanel, x, y);
239 public void setTableData()
241 alignmentHasFeatures = fr.buildGroupHash();
242 if (alignmentHasFeatures)
251 * rebuilds the group panel
253 public void rebuildGroups()
255 boolean rdrw = false;
256 if (groupPanel == null)
258 groupPanel = new Panel();
263 groupPanel.removeAll();
266 Enumeration gps = fr.featureGroups.keys();
267 while (gps.hasMoreElements())
269 String group = (String) gps.nextElement();
270 Boolean vis = (Boolean) fr.featureGroups.get(group);
271 Checkbox check = new MyCheckbox(group, vis.booleanValue(),
272 (fr.featureLinks != null && fr.featureLinks
273 .containsKey(group)));
274 check.addMouseListener(this);
275 check.setFont(new Font("Serif", Font.BOLD, 12));
276 check.addItemListener(this);
277 groupPanel.add(check);
281 groupPanel.validate();
285 // This routine adds and removes checkboxes depending on
286 // Group selection states
287 void resetTable(boolean groupsChanged)
289 SequenceFeature[] tmpfeatures;
290 String group = null, type;
291 Vector visibleChecks = new Vector();
293 for (int i = 0; i < av.alignment.getHeight(); i++)
295 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
300 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
302 while (index < tmpfeatures.length)
304 group = tmpfeatures[index].featureGroup;
306 if (group == null || fr.featureGroups.get(group) == null
307 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
309 type = tmpfeatures[index].getType();
310 if (!visibleChecks.contains(type))
312 visibleChecks.addElement(type);
320 int cSize = featurePanel.getComponentCount();
322 // This will remove any checkboxes which shouldn't be
324 for (int i = 0; i < cSize; i++)
326 comps = featurePanel.getComponents();
327 check = (MyCheckbox) comps[i];
328 if (!visibleChecks.contains(check.type))
330 featurePanel.remove(i);
336 if (fr.renderOrder != null)
338 // First add the checks in the previous render order,
339 // in case the window has been closed and reopened
340 for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
342 String item = fr.renderOrder[ro];
344 if (!visibleChecks.contains(item))
349 visibleChecks.removeElement(item);
351 addCheck(false, item);
355 // now add checkboxes which should be visible,
356 // if they have not already been added
357 Enumeration en = visibleChecks.elements();
359 while (en.hasMoreElements())
361 addCheck(groupsChanged, en.nextElement().toString());
364 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
366 featurePanel.validate();
368 if (scrollPane != null)
370 scrollPane.validate();
373 itemStateChanged(null);
377 * update the checklist of feature types with the given type
379 * @param groupsChanged
380 * true means if the type is not in the display list then it will be
381 * added and displayed
383 * feature type to be checked for in the list.
385 void addCheck(boolean groupsChanged, String type)
388 Component[] comps = featurePanel.getComponents();
391 for (int i = 0; i < featurePanel.getComponentCount(); i++)
393 check = (MyCheckbox) comps[i];
394 if (check.type.equals(type))
403 boolean selected = false;
404 if (groupsChanged || av.featuresDisplayed.containsKey(type))
409 check = new MyCheckbox(
412 (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
413 fr.getFeatureStyle(type));
415 check.addMouseListener(this);
416 check.addMouseMotionListener(this);
417 check.addItemListener(this);
420 // add at beginning of stack.
421 featurePanel.add(check, 0);
425 // add at end of stack.
426 featurePanel.add(check);
431 public void actionPerformed(ActionEvent evt)
433 for (int i = 0; i < featurePanel.getComponentCount(); i++)
435 Checkbox check = (Checkbox) featurePanel.getComponent(i);
436 check.setState(!check.getState());
441 public void itemStateChanged(ItemEvent evt)
445 // Is the source a top level featureGroup?
446 Checkbox source = (Checkbox) evt.getSource();
447 if (fr.featureGroups.containsKey(source.getLabel()))
449 fr.featureGroups.put(source.getLabel(), new Boolean(source
451 ap.seqPanel.seqCanvas.repaint();
452 if (ap.overviewPanel != null)
454 ap.overviewPanel.updateOverviewImage();
464 void selectionChanged()
466 Component[] comps = featurePanel.getComponents();
467 int cSize = comps.length;
469 Object[][] tmp = new Object[cSize][3];
471 for (int i = 0; i < cSize; i++)
473 MyCheckbox check = (MyCheckbox) comps[i];
474 tmp[tmpSize][0] = check.type;
475 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
476 tmp[tmpSize][2] = new Boolean(check.getState());
480 Object[][] data = new Object[tmpSize][3];
481 System.arraycopy(tmp, 0, data, 0, tmpSize);
483 fr.setFeaturePriority(data);
485 ap.paintAlignment(true);
488 MyCheckbox selectedCheck;
490 boolean dragging = false;
492 public void mousePressed(MouseEvent evt)
495 selectedCheck = (MyCheckbox) evt.getSource();
497 if (fr.featureLinks != null
498 && fr.featureLinks.containsKey(selectedCheck.type))
500 if (evt.getX() > selectedCheck.stringWidth + 20)
508 public void mouseDragged(MouseEvent evt)
510 if (((Component) evt.getSource()).getParent() != featurePanel)
517 public void mouseReleased(MouseEvent evt)
519 if (((Component) evt.getSource()).getParent() != featurePanel)
524 Component comp = null;
525 Checkbox target = null;
527 int height = evt.getY() + evt.getComponent().getLocation().y;
529 if (height > featurePanel.getSize().height)
533 .getComponent(featurePanel.getComponentCount() - 1);
537 comp = featurePanel.getComponent(0);
541 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
542 + evt.getComponent().getLocation().y);
545 if (comp != null && comp instanceof Checkbox)
547 target = (Checkbox) comp;
550 if (selectedCheck != null && target != null && selectedCheck != target)
552 int targetIndex = -1;
553 for (int i = 0; i < featurePanel.getComponentCount(); i++)
555 if (target == featurePanel.getComponent(i))
562 featurePanel.remove(selectedCheck);
563 featurePanel.add(selectedCheck, targetIndex);
564 featurePanel.validate();
565 itemStateChanged(null);
569 public void setUserColour(String feature, Object originalColour)
571 if (originalColour instanceof Color
572 || originalColour instanceof GraduatedColor)
574 fr.setColour(feature, originalColour);
579 "Implementation error: Unsupported feature colour object.");
584 public void refreshTable()
586 featurePanel.removeAll();
588 ap.paintAlignment(true);
591 public void mouseEntered(MouseEvent evt)
595 public void mouseExited(MouseEvent evt)
599 public void mouseClicked(MouseEvent evt)
601 MyCheckbox check = (MyCheckbox) evt.getSource();
602 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
604 this.popupSort(check, fr.minmax, evt.getX(), evt.getY());
606 if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
608 if (evt.getX() > check.stringWidth + 20)
611 String link = fr.featureLinks.get(check.type).toString();
612 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1), link
613 .substring(0, link.indexOf("|")));
617 if (check.getParent() != featurePanel)
622 if (evt.getClickCount() > 1)
624 Object fcol = fr.getFeatureStyle(check.type);
625 if (fcol instanceof Color)
627 new UserDefinedColours(this, check.type, (Color) fcol);
631 new FeatureColourChooser(this, check.type);
632 // write back the current colour object to update the table
633 check.updateColor(fr.getFeatureStyle(check.type));
638 public void mouseMoved(MouseEvent evt)
642 public void adjustmentValueChanged(AdjustmentEvent evt)
644 fr.transparency = ((float) (100 - transparency.getValue()) / 100f);
645 ap.seqPanel.seqCanvas.repaint();
649 class MyCheckbox extends Checkbox
653 public int stringWidth;
661 public void updateColor(Object newcol)
663 if (newcol instanceof Color)
665 col = (Color) newcol;
668 else if (newcol instanceof GraduatedColor)
670 gcol = (GraduatedColor) newcol;
675 throw new Error("Invalid color for MyCheckBox");
683 String vlabel = type;
684 if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
687 + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
690 if (gcol.isColourByLabel())
692 setBackground(Color.white);
693 vlabel += " (by Label)";
697 setBackground(gcol.getMinColor());
699 this.setLabel(vlabel);
704 public MyCheckbox(String label, boolean checked, boolean haslink)
706 super(label, checked);
708 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
709 stringWidth = fm.stringWidth(label);
710 this.hasLink = haslink;
713 public MyCheckbox(String type, boolean selected, boolean b,
716 this(type, selected, b);
717 updateColor(featureStyle);
720 public void paint(Graphics g)
722 Dimension d = getSize();
725 if (gcol.isColourByLabel())
727 g.setColor(Color.white);
728 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
730 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
733 * // g.setFont(g.getFont().deriveFont( //
734 * AffineTransform.getScaleInstance( //
735 * width/g.getFontMetrics().stringWidth("Label"), //
736 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
743 Color maxCol = gcol.getMaxColor();
745 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
752 g.drawImage(linkImage, stringWidth + 25,
753 (getSize().height - linkImage.getHeight(this)) / 2, this);
758 protected void sortByDens(String[] typ)
760 sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
763 private String[] getDisplayedFeatureTypes()
768 synchronized (fr.renderOrder)
770 typ = new String[fr.renderOrder.length];
771 System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
772 for (int i = 0; i < typ.length; i++)
774 if (av.featuresDisplayed.get(typ[i]) == null)
784 protected void sortBy(String[] typ, String methodText, final String method)
788 typ = getDisplayedFeatureTypes();
791 gps = fr.getGroups(true);
794 for (int i = 0; i < typ.length; i++)
796 System.err.println("Sorting on Types:" + typ[i]);
802 for (int i = 0; i < gps.length; i++)
804 System.err.println("Sorting on groups:" + gps[i]);
807 AlignmentPanel alignPanel = ap;
808 AlignmentI al = alignPanel.av.getAlignment();
811 SequenceGroup sg = alignPanel.av.getSelectionGroup();
814 start = sg.getStartRes();
815 stop = sg.getEndRes();
820 stop = al.getWidth();
822 SequenceI[] oldOrder = al.getSequencesArray();
823 AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
824 this.ap.alignFrame.addHistoryItem(new OrderCommand(methodText,
825 oldOrder, alignPanel.av.getAlignment()));
826 alignPanel.paintAlignment(true);
830 protected void sortByScore(String[] typ)
832 sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);