2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3 * Copyright (C) 2008 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.*;
30 public class FeatureSettings extends Panel implements ItemListener,
31 MouseListener, MouseMotionListener, ActionListener,
44 Panel featurePanel = new Panel();
46 ScrollPane scrollPane;
48 boolean alignmentHasFeatures = false;
52 Scrollbar transparency;
54 public FeatureSettings(final AlignmentPanel ap)
58 ap.av.featureSettings = this;
59 fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
61 transparency = new Scrollbar(Scrollbar.HORIZONTAL,
62 100 - (int) (fr.transparency * 100), 1, 1, 100);
64 if (fr.transparencySetter != null)
66 transparency.addAdjustmentListener(this);
70 transparency.setEnabled(false);
73 java.net.URL url = getClass().getResource("/images/link.gif");
76 linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
79 if (av.featuresDisplayed == null)
86 this.setLayout(new BorderLayout());
87 scrollPane = new ScrollPane();
88 scrollPane.add(featurePanel);
89 if (alignmentHasFeatures)
91 add(scrollPane, BorderLayout.CENTER);
94 Button invert = new Button("Invert Selection");
95 invert.addActionListener(this);
97 Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
98 lowerPanel.add(invert);
100 Panel tPanel = new Panel(new BorderLayout());
102 if (fr.transparencySetter != null)
104 tPanel.add(transparency, BorderLayout.CENTER);
105 tPanel.add(new Label("Transparency"), BorderLayout.EAST);
110 new Label("Transparency not available in this web browser"),
111 BorderLayout.CENTER);
114 lowerPanel.add(tPanel, BorderLayout.SOUTH);
116 add(lowerPanel, BorderLayout.SOUTH);
118 if (groupPanel != null)
120 groupPanel.setLayout(new GridLayout(fr.featureGroups.size() / 4 + 1,
122 groupPanel.validate();
124 add(groupPanel, BorderLayout.NORTH);
128 final FeatureSettings me = this;
129 frame.addWindowListener(new WindowAdapter()
131 public void windowClosing(WindowEvent e)
133 if (me.av.featureSettings == me)
135 me.av.featureSettings = null;
141 int height = featurePanel.getComponentCount() * 50 + 60;
143 height = Math.max(200, height);
144 height = Math.min(400, height);
146 jalview.bin.JalviewLite.addFrame(frame, "Feature Settings", width,
150 public void paint(Graphics g)
152 g.setColor(Color.black);
153 g.drawString("No Features added to this alignment!!", 10, 20);
154 g.drawString("(Features can be added from searches or", 10, 40);
155 g.drawString("from Jalview / GFF features files)", 10, 60);
157 protected void popupSort(final String type, final Hashtable minmax,
160 java.awt.PopupMenu men = new PopupMenu("Settings for " + type);
161 java.awt.MenuItem scr = new MenuItem("Sort by Score");
163 final FeatureSettings me = this;
164 scr.addActionListener(new ActionListener()
167 public void actionPerformed(ActionEvent e)
169 me.sortByScore(new String[]
174 MenuItem dens = new MenuItem("Sort by Density");
175 dens.addActionListener(new ActionListener()
178 public void actionPerformed(ActionEvent e)
180 me.sortByDens(new String[]
188 final Object typeMinMax = minmax.get(type);
189 final java.awt.CheckboxMenuItem chb = new java.awt.CheckboxMenuItem("Vary Height");
190 // this is broken at the moment
191 chb.setState(minmax.get(type) != null);
192 chb.addActionListener(new ActionListener()
195 public void actionPerformed(ActionEvent e)
197 chb.setState(chb.getState());
200 minmax.put(type, null);
204 minmax.put(type, typeMinMax);
210 if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
212 // graduated colourschemes for those where minmax exists for the positional features
213 MenuItem mxcol = new MenuItem("Min Max Colour");
215 mxcol.addActionListener(new ActionListener()
218 public void actionPerformed(ActionEvent e)
220 new FeatureColourChooser(me, type);
226 this.featurePanel.add(men);
227 men.show(this.featurePanel, x, y);
230 public void setTableData()
232 alignmentHasFeatures = fr.buildGroupHash();
233 if (alignmentHasFeatures)
242 * rebuilds the group panel
244 public void rebuildGroups()
246 boolean rdrw = false;
247 if (groupPanel == null)
249 groupPanel = new Panel();
254 groupPanel.removeAll();
257 Enumeration gps = fr.featureGroups.keys();
258 while (gps.hasMoreElements())
260 String group = (String) gps.nextElement();
261 Boolean vis = (Boolean) fr.featureGroups.get(group);
262 Checkbox check = new MyCheckbox(group, vis.booleanValue(),
263 (fr.featureLinks != null && fr.featureLinks
264 .containsKey(group)));
265 check.addMouseListener(this);
266 check.setFont(new Font("Serif", Font.BOLD, 12));
267 check.addItemListener(this);
268 groupPanel.add(check);
272 groupPanel.validate();
276 // This routine adds and removes checkboxes depending on
277 // Group selection states
278 void resetTable(boolean groupsChanged)
280 SequenceFeature[] tmpfeatures;
281 String group = null, type;
282 Vector visibleChecks = new Vector();
284 for (int i = 0; i < av.alignment.getHeight(); i++)
286 if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
291 tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
293 while (index < tmpfeatures.length)
295 group = tmpfeatures[index].featureGroup;
297 if (group == null || fr.featureGroups.get(group) == null
298 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
300 type = tmpfeatures[index].getType();
301 if (!visibleChecks.contains(type))
303 visibleChecks.addElement(type);
311 int cSize = featurePanel.getComponentCount();
313 // This will remove any checkboxes which shouldn't be
315 for (int i = 0; i < cSize; i++)
317 comps = featurePanel.getComponents();
318 check = (Checkbox) comps[i];
319 if (!visibleChecks.contains(check.getLabel()))
321 featurePanel.remove(i);
327 if (fr.renderOrder != null)
329 // First add the checks in the previous render order,
330 // in case the window has been closed and reopened
331 for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
333 String item = fr.renderOrder[ro];
335 if (!visibleChecks.contains(item))
340 visibleChecks.removeElement(item);
342 addCheck(false, item);
346 // now add checkboxes which should be visible,
347 // if they have not already been added
348 Enumeration en = visibleChecks.elements();
350 while (en.hasMoreElements())
352 addCheck(groupsChanged, en.nextElement().toString());
355 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
357 featurePanel.validate();
359 if (scrollPane != null)
361 scrollPane.validate();
364 itemStateChanged(null);
368 * update the checklist of feature types with the given type
370 * @param groupsChanged
371 * true means if the type is not in the display list then it
372 * will be added and displayed
374 * feature type to be checked for in the list.
376 void addCheck(boolean groupsChanged, String type)
379 Component[] comps = featurePanel.getComponents();
382 for (int i = 0; i < featurePanel.getComponentCount(); i++)
384 check = (Checkbox) comps[i];
385 if (check.getLabel().equals(type))
394 boolean selected = false;
395 if (groupsChanged || av.featuresDisplayed.containsKey(type))
400 check = new MyCheckbox(
403 (fr.featureLinks != null && fr.featureLinks.containsKey(type)));
405 check.addMouseListener(this);
406 check.addMouseMotionListener(this);
407 check.setBackground(fr.getColour(type));
408 check.addItemListener(this);
411 // add at beginning of stack.
412 featurePanel.add(check, 0);
416 // add at end of stack.
417 featurePanel.add(check);
422 public void actionPerformed(ActionEvent evt)
424 for (int i = 0; i < featurePanel.getComponentCount(); i++)
426 Checkbox check = (Checkbox) featurePanel.getComponent(i);
427 check.setState(!check.getState());
432 public void itemStateChanged(ItemEvent evt)
436 // Is the source a top level featureGroup?
437 Checkbox source = (Checkbox) evt.getSource();
438 if (fr.featureGroups.containsKey(source.getLabel()))
440 fr.featureGroups.put(source.getLabel(), new Boolean(source
442 ap.seqPanel.seqCanvas.repaint();
443 if (ap.overviewPanel != null)
445 ap.overviewPanel.updateOverviewImage();
455 void selectionChanged()
457 Component[] comps = featurePanel.getComponents();
458 int cSize = comps.length;
460 Object[][] tmp = new Object[cSize][3];
462 for (int i = 0; i < cSize; i++)
464 Checkbox check = (Checkbox) comps[i];
465 tmp[tmpSize][0] = check.getLabel();
466 tmp[tmpSize][1] = fr.getColour(check.getLabel());
467 tmp[tmpSize][2] = new Boolean(check.getState());
471 Object[][] data = new Object[tmpSize][3];
472 System.arraycopy(tmp, 0, data, 0, tmpSize);
474 fr.setFeaturePriority(data);
476 ap.paintAlignment(true);
479 MyCheckbox selectedCheck;
481 boolean dragging = false;
483 public void mousePressed(MouseEvent evt)
486 selectedCheck = (MyCheckbox) evt.getSource();
488 if (fr.featureLinks != null
489 && fr.featureLinks.containsKey(selectedCheck.getLabel()))
491 if (evt.getX() > selectedCheck.stringWidth + 20)
499 public void mouseDragged(MouseEvent evt)
501 if (((Component) evt.getSource()).getParent() != featurePanel)
508 public void mouseReleased(MouseEvent evt)
510 if (((Component) evt.getSource()).getParent() != featurePanel)
515 Component comp = null;
516 Checkbox target = null;
518 int height = evt.getY() + evt.getComponent().getLocation().y;
520 if (height > featurePanel.getSize().height)
524 .getComponent(featurePanel.getComponentCount() - 1);
528 comp = featurePanel.getComponent(0);
532 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
533 + evt.getComponent().getLocation().y);
536 if (comp != null && comp instanceof Checkbox)
538 target = (Checkbox) comp;
541 if (selectedCheck != null && target != null && selectedCheck != target)
543 int targetIndex = -1;
544 for (int i = 0; i < featurePanel.getComponentCount(); i++)
546 if (target == featurePanel.getComponent(i))
553 featurePanel.remove(selectedCheck);
554 featurePanel.add(selectedCheck, targetIndex);
555 featurePanel.validate();
556 itemStateChanged(null);
560 public void setUserColour(String feature, Color col)
562 fr.setColour(feature, col);
563 featurePanel.removeAll();
565 ap.paintAlignment(true);
568 public void mouseEntered(MouseEvent evt)
572 public void mouseExited(MouseEvent evt)
576 public void mouseClicked(MouseEvent evt)
578 MyCheckbox check = (MyCheckbox) evt.getSource();
579 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK)!=0)
581 this.popupSort(check.getLabel(), fr.minmax, evt.getX(), evt.getY());
583 if (fr.featureLinks != null
584 && fr.featureLinks.containsKey(check.getLabel()))
586 if (evt.getX() > check.stringWidth + 20)
589 String link = fr.featureLinks.get(check.getLabel()).toString();
590 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1), link
591 .substring(0, link.indexOf("|")));
595 if (check.getParent() != featurePanel)
600 if (evt.getClickCount() > 1)
602 new UserDefinedColours(this, check.getLabel(), fr.getColour(check
607 public void mouseMoved(MouseEvent evt)
611 public void adjustmentValueChanged(AdjustmentEvent evt)
613 fr.transparency = ((float) (100 - transparency.getValue()) / 100f);
614 ap.seqPanel.seqCanvas.repaint();
618 class MyCheckbox extends Checkbox
620 public int stringWidth;
624 public MyCheckbox(String label, boolean checked, boolean haslink)
626 super(label, checked);
628 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
629 stringWidth = fm.stringWidth(label);
630 this.hasLink = haslink;
633 public void paint(Graphics g)
637 g.drawImage(linkImage, stringWidth + 25,
638 (getSize().height - linkImage.getHeight(this)) / 2, this);
642 protected void sortByDens(String[] typ)
644 sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
646 private String[] getDisplayedFeatureTypes()
651 synchronized (fr.renderOrder)
653 typ = new String[fr.renderOrder.length];
654 System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
655 for (int i = 0; i < typ.length; i++)
657 if (av.featuresDisplayed.get(typ[i]) == null)
668 protected void sortBy(String[] typ, String methodText, final String method)
672 typ = getDisplayedFeatureTypes();
675 gps = fr.getGroups(true);
678 for (int i = 0; i < typ.length; i++)
680 System.err.println("Sorting on Types:" + typ[i]);
686 for (int i = 0; i < gps.length; i++)
688 System.err.println("Sorting on groups:" + gps[i]);
691 AlignmentPanel alignPanel = ap;
692 AlignmentI al = alignPanel.av.getAlignment();
695 SequenceGroup sg = alignPanel.av.getSelectionGroup();
698 start = sg.getStartRes();
699 stop = sg.getEndRes();
704 stop = al.getWidth();
706 SequenceI[] oldOrder = al.getSequencesArray();
707 AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
708 this.ap.alignFrame.addHistoryItem(new OrderCommand(methodText, oldOrder, alignPanel.av
710 alignPanel.paintAlignment(true);
714 protected void sortByScore(String[] typ)
716 sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);