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 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("Graduated Colour");
211 mxcol.addActionListener(new ActionListener()
214 public void actionPerformed(ActionEvent e)
216 new FeatureColourChooser(me, type);
217 // write back the current colour object to update the table
218 check.updateColor(fr.getFeatureStyle(type));
224 this.featurePanel.add(men);
225 men.show(this.featurePanel, x, y);
228 public void setTableData()
230 alignmentHasFeatures = fr.buildGroupHash();
231 if (alignmentHasFeatures)
240 * rebuilds the group panel
242 public void rebuildGroups()
244 boolean rdrw = false;
245 if (groupPanel == null)
247 groupPanel = new Panel();
252 groupPanel.removeAll();
255 Enumeration gps = fr.featureGroups.keys();
256 while (gps.hasMoreElements())
258 String group = (String) gps.nextElement();
259 Boolean vis = (Boolean) fr.featureGroups.get(group);
260 Checkbox check = new MyCheckbox(group, vis.booleanValue(),
261 (fr.featureLinks != null && fr.featureLinks
262 .containsKey(group)));
263 check.addMouseListener(this);
264 check.setFont(new Font("Serif", Font.BOLD, 12));
265 check.addItemListener(this);
266 groupPanel.add(check);
270 groupPanel.validate();
274 // This routine adds and removes checkboxes depending on
275 // Group selection states
276 void resetTable(boolean groupsChanged)
278 SequenceFeature[] tmpfeatures;
279 String group = null, type;
280 Vector visibleChecks = new Vector();
282 for (int i = 0; i < av.alignment.getHeight(); i++)
284 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
289 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
291 while (index < tmpfeatures.length)
293 group = tmpfeatures[index].featureGroup;
295 if (group == null || fr.featureGroups.get(group) == null
296 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
298 type = tmpfeatures[index].getType();
299 if (!visibleChecks.contains(type))
301 visibleChecks.addElement(type);
309 int cSize = featurePanel.getComponentCount();
311 // This will remove any checkboxes which shouldn't be
313 for (int i = 0; i < cSize; i++)
315 comps = featurePanel.getComponents();
316 check = (MyCheckbox) comps[i];
317 if (!visibleChecks.contains(check.type))
319 featurePanel.remove(i);
325 if (fr.renderOrder != null)
327 // First add the checks in the previous render order,
328 // in case the window has been closed and reopened
329 for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
331 String item = fr.renderOrder[ro];
333 if (!visibleChecks.contains(item))
338 visibleChecks.removeElement(item);
340 addCheck(false, item);
344 // now add checkboxes which should be visible,
345 // if they have not already been added
346 Enumeration en = visibleChecks.elements();
348 while (en.hasMoreElements())
350 addCheck(groupsChanged, en.nextElement().toString());
353 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
355 featurePanel.validate();
357 if (scrollPane != null)
359 scrollPane.validate();
362 itemStateChanged(null);
366 * update the checklist of feature types with the given type
368 * @param groupsChanged
369 * true means if the type is not in the display list then it will be
370 * added and displayed
372 * feature type to be checked for in the list.
374 void addCheck(boolean groupsChanged, String type)
377 Component[] comps = featurePanel.getComponents();
380 for (int i = 0; i < featurePanel.getComponentCount(); i++)
382 check = (MyCheckbox) comps[i];
383 if (check.type.equals(type))
392 boolean selected = false;
393 if (groupsChanged || av.featuresDisplayed.containsKey(type))
398 check = new MyCheckbox(
401 (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
402 fr.getFeatureStyle(type));
404 check.addMouseListener(this);
405 check.addMouseMotionListener(this);
406 check.addItemListener(this);
409 // add at beginning of stack.
410 featurePanel.add(check, 0);
414 // add at end of stack.
415 featurePanel.add(check);
420 public void actionPerformed(ActionEvent evt)
422 for (int i = 0; i < featurePanel.getComponentCount(); i++)
424 Checkbox check = (Checkbox) featurePanel.getComponent(i);
425 check.setState(!check.getState());
430 public void itemStateChanged(ItemEvent evt)
434 // Is the source a top level featureGroup?
435 Checkbox source = (Checkbox) evt.getSource();
436 if (fr.featureGroups.containsKey(source.getLabel()))
438 fr.featureGroups.put(source.getLabel(), new Boolean(source
440 ap.seqPanel.seqCanvas.repaint();
441 if (ap.overviewPanel != null)
443 ap.overviewPanel.updateOverviewImage();
453 void selectionChanged()
455 Component[] comps = featurePanel.getComponents();
456 int cSize = comps.length;
458 Object[][] tmp = new Object[cSize][3];
460 for (int i = 0; i < cSize; i++)
462 MyCheckbox check = (MyCheckbox) comps[i];
463 tmp[tmpSize][0] = check.type;
464 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
465 tmp[tmpSize][2] = new Boolean(check.getState());
469 Object[][] data = new Object[tmpSize][3];
470 System.arraycopy(tmp, 0, data, 0, tmpSize);
472 fr.setFeaturePriority(data);
474 ap.paintAlignment(true);
477 MyCheckbox selectedCheck;
479 boolean dragging = false;
481 public void mousePressed(MouseEvent evt)
484 selectedCheck = (MyCheckbox) evt.getSource();
486 if (fr.featureLinks != null
487 && fr.featureLinks.containsKey(selectedCheck.type))
489 if (evt.getX() > selectedCheck.stringWidth + 20)
497 public void mouseDragged(MouseEvent evt)
499 if (((Component) evt.getSource()).getParent() != featurePanel)
506 public void mouseReleased(MouseEvent evt)
508 if (((Component) evt.getSource()).getParent() != featurePanel)
513 Component comp = null;
514 Checkbox target = null;
516 int height = evt.getY() + evt.getComponent().getLocation().y;
518 if (height > featurePanel.getSize().height)
522 .getComponent(featurePanel.getComponentCount() - 1);
526 comp = featurePanel.getComponent(0);
530 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
531 + evt.getComponent().getLocation().y);
534 if (comp != null && comp instanceof Checkbox)
536 target = (Checkbox) comp;
539 if (selectedCheck != null && target != null && selectedCheck != target)
541 int targetIndex = -1;
542 for (int i = 0; i < featurePanel.getComponentCount(); i++)
544 if (target == featurePanel.getComponent(i))
551 featurePanel.remove(selectedCheck);
552 featurePanel.add(selectedCheck, targetIndex);
553 featurePanel.validate();
554 itemStateChanged(null);
558 public void setUserColour(String feature, Color col)
560 fr.setColour(feature, col);
561 featurePanel.removeAll();
563 ap.paintAlignment(true);
566 public void mouseEntered(MouseEvent evt)
570 public void mouseExited(MouseEvent evt)
574 public void mouseClicked(MouseEvent evt)
576 MyCheckbox check = (MyCheckbox) evt.getSource();
577 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
579 this.popupSort(check, fr.minmax, evt.getX(), evt.getY());
581 if (fr.featureLinks != null
582 && fr.featureLinks.containsKey(check.type))
584 if (evt.getX() > check.stringWidth + 20)
587 String link = fr.featureLinks.get(check.type).toString();
588 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1), link
589 .substring(0, link.indexOf("|")));
593 if (check.getParent() != featurePanel)
598 if (evt.getClickCount() > 1)
600 Object fcol = fr.getFeatureStyle(check.type);
601 if (fcol instanceof Color)
603 new UserDefinedColours(this, check.type, (Color) fcol);
605 new FeatureColourChooser(this, check.type);
606 // write back the current colour object to update the table
607 check.updateColor(fr.getFeatureStyle(check.type));
612 public void mouseMoved(MouseEvent evt)
616 public void adjustmentValueChanged(AdjustmentEvent evt)
618 fr.transparency = ((float) (100 - transparency.getValue()) / 100f);
619 ap.seqPanel.seqCanvas.repaint();
623 class MyCheckbox extends Checkbox
626 public int stringWidth;
634 public void updateColor(Object newcol)
636 if (newcol instanceof Color)
638 col = (Color) newcol;
641 else if (newcol instanceof GraduatedColor)
643 gcol = (GraduatedColor) newcol;
648 throw new Error("Invalid color for MyCheckBox");
656 String vlabel = type;
657 if (gcol.getThreshType()!=AnnotationColourGradient.NO_THRESHOLD)
659 vlabel += " "+((gcol.getThreshType()==AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)" : "(<)");
661 if (gcol.isColourByLabel()) {
662 setBackground(Color.white);
663 vlabel += " (by Label)";
665 setBackground(gcol.getMinColor());
667 this.setLabel(vlabel);
672 public MyCheckbox(String label, boolean checked, boolean haslink)
674 super(label, checked);
676 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
677 stringWidth = fm.stringWidth(label);
678 this.hasLink = haslink;
681 public MyCheckbox(String type, boolean selected, boolean b,
684 this(type,selected,b);
685 updateColor(featureStyle);
688 public void paint(Graphics g)
690 int width = getWidth(), height = getHeight();
693 if (gcol.isColourByLabel())
695 g.setColor(Color.white);
696 g.fillRect(width/2, 0,width/2, height);
697 /*g.setColor(Color.black);
698 Font f=g.getFont().deriveFont(9);
701 // g.setFont(g.getFont().deriveFont(
702 // AffineTransform.getScaleInstance(
703 // width/g.getFontMetrics().stringWidth("Label"),
704 // height/g.getFontMetrics().getHeight())));
705 g.drawString("Label", width/2, 0); */
710 Color maxCol = gcol.getMaxColor();
712 g.fillRect(width/2, 0,width/2, height);
719 g.drawImage(linkImage, stringWidth + 25,
720 (getSize().height - linkImage.getHeight(this)) / 2, this);
725 protected void sortByDens(String[] typ)
727 sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
730 private String[] getDisplayedFeatureTypes()
735 synchronized (fr.renderOrder)
737 typ = new String[fr.renderOrder.length];
738 System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
739 for (int i = 0; i < typ.length; i++)
741 if (av.featuresDisplayed.get(typ[i]) == null)
751 protected void sortBy(String[] typ, String methodText, final String method)
755 typ = getDisplayedFeatureTypes();
758 gps = fr.getGroups(true);
761 for (int i = 0; i < typ.length; i++)
763 System.err.println("Sorting on Types:" + typ[i]);
769 for (int i = 0; i < gps.length; i++)
771 System.err.println("Sorting on groups:" + gps[i]);
774 AlignmentPanel alignPanel = ap;
775 AlignmentI al = alignPanel.av.getAlignment();
778 SequenceGroup sg = alignPanel.av.getSelectionGroup();
781 start = sg.getStartRes();
782 stop = sg.getEndRes();
787 stop = al.getWidth();
789 SequenceI[] oldOrder = al.getSequencesArray();
790 AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
791 this.ap.alignFrame.addHistoryItem(new OrderCommand(methodText,
792 oldOrder, alignPanel.av.getAlignment()));
793 alignPanel.paintAlignment(true);
797 protected void sortByScore(String[] typ)
799 sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);