2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
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
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.appletgui;
23 import jalview.api.FeatureSettingsControllerI;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.SequenceFeature;
26 import jalview.schemes.AnnotationColourGradient;
27 import jalview.schemes.GraduatedColor;
28 import jalview.util.MessageManager;
30 import java.awt.BorderLayout;
31 import java.awt.Button;
32 import java.awt.Checkbox;
33 import java.awt.Color;
34 import java.awt.Component;
35 import java.awt.Dimension;
37 import java.awt.FontMetrics;
38 import java.awt.Frame;
39 import java.awt.Graphics;
40 import java.awt.GridLayout;
41 import java.awt.Image;
42 import java.awt.Label;
43 import java.awt.MenuItem;
44 import java.awt.Panel;
45 import java.awt.PopupMenu;
46 import java.awt.ScrollPane;
47 import java.awt.Scrollbar;
48 import java.awt.event.ActionEvent;
49 import java.awt.event.ActionListener;
50 import java.awt.event.AdjustmentEvent;
51 import java.awt.event.AdjustmentListener;
52 import java.awt.event.InputEvent;
53 import java.awt.event.ItemEvent;
54 import java.awt.event.ItemListener;
55 import java.awt.event.MouseEvent;
56 import java.awt.event.MouseListener;
57 import java.awt.event.MouseMotionListener;
58 import java.awt.event.WindowAdapter;
59 import java.awt.event.WindowEvent;
60 import java.util.Enumeration;
61 import java.util.Hashtable;
62 import java.util.List;
63 import java.util.Vector;
65 public class FeatureSettings extends Panel implements ItemListener,
66 MouseListener, MouseMotionListener, ActionListener,
67 AdjustmentListener, FeatureSettingsControllerI
79 Panel featurePanel = new Panel();
81 ScrollPane scrollPane;
85 Scrollbar transparency;
87 public FeatureSettings(final AlignmentPanel ap)
91 ap.av.featureSettings = this;
92 fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
94 transparency = new Scrollbar(Scrollbar.HORIZONTAL,
95 100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
97 if (fr.isTransparencyAvailable())
99 transparency.addAdjustmentListener(this);
103 transparency.setEnabled(false);
106 java.net.URL url = getClass().getResource("/images/link.gif");
109 linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
112 if (av.isShowSequenceFeatures() || !fr.hasRenderOrder())
114 fr.findAllFeatures(true); // was default - now true to make all visible
117 discoverAllFeatureData();
119 this.setLayout(new BorderLayout());
120 scrollPane = new ScrollPane();
121 scrollPane.add(featurePanel);
122 if (fr.getAllFeatureColours()!=null && fr.getAllFeatureColours().size()>0)
124 add(scrollPane, BorderLayout.CENTER);
127 Button invert = new Button("Invert Selection");
128 invert.addActionListener(this);
130 Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
131 lowerPanel.add(invert);
133 Panel tPanel = new Panel(new BorderLayout());
135 if (fr.isTransparencyAvailable())
137 tPanel.add(transparency, BorderLayout.CENTER);
138 tPanel.add(new Label("Transparency"), BorderLayout.EAST);
143 new Label("Transparency not available in this web browser"),
144 BorderLayout.CENTER);
147 lowerPanel.add(tPanel, BorderLayout.SOUTH);
149 add(lowerPanel, BorderLayout.SOUTH);
151 if (groupPanel != null)
154 .setLayout(new GridLayout(
155 (fr.getFeatureGroupsSize()) / 4 + 1,
156 4)); // JBPNote - this was scaled on number of visible groups. seems broken
157 groupPanel.validate();
159 add(groupPanel, BorderLayout.NORTH);
163 final FeatureSettings me = this;
164 frame.addWindowListener(new WindowAdapter()
166 public void windowClosing(WindowEvent e)
168 if (me.av.featureSettings == me)
170 me.av.featureSettings = null;
176 int height = featurePanel.getComponentCount() * 50 + 60;
178 height = Math.max(200, height);
179 height = Math.min(400, height);
181 jalview.bin.JalviewLite.addFrame(frame,
182 MessageManager.getString("label.feature_settings"), width,
186 public void paint(Graphics g)
188 g.setColor(Color.black);
189 g.drawString(MessageManager
190 .getString("label.no_features_added_to_this_alignment"), 10, 20);
191 g.drawString(MessageManager
192 .getString("label.features_can_be_added_from_searches_1"), 10,
194 g.drawString(MessageManager
195 .getString("label.features_can_be_added_from_searches_2"), 10,
199 protected void popupSort(final MyCheckbox check, final Hashtable minmax,
202 final String type = check.type;
203 final Object typeCol = fr.getFeatureStyle(type);
204 java.awt.PopupMenu men = new PopupMenu(MessageManager.formatMessage(
205 "label.settings_for_type", new String[]
207 java.awt.MenuItem scr = new MenuItem(
208 MessageManager.getString("label.sort_by_score"));
210 final FeatureSettings me = this;
211 scr.addActionListener(new ActionListener()
214 public void actionPerformed(ActionEvent e)
216 me.ap.alignFrame.avc.sortAlignmentByFeatureScore(new String[]
221 MenuItem dens = new MenuItem(
222 MessageManager.getString("label.sort_by_density"));
223 dens.addActionListener(new ActionListener()
226 public void actionPerformed(ActionEvent e)
228 me.ap.alignFrame.avc.sortAlignmentByFeatureDensity(new String[]
236 final Object typeMinMax = minmax.get(type);
238 * final java.awt.CheckboxMenuItem chb = new
239 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
240 * moment chb.setState(minmax.get(type) != null);
241 * chb.addActionListener(new ActionListener() {
243 * public void actionPerformed(ActionEvent e) {
244 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
245 * null); } else { minmax.put(type, typeMinMax); } }
249 if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
251 // graduated colourschemes for those where minmax exists for the
252 // positional features
253 MenuItem mxcol = new MenuItem(
254 (typeCol instanceof Color) ? "Graduated Colour"
257 mxcol.addActionListener(new ActionListener()
260 public void actionPerformed(ActionEvent e)
262 if (typeCol instanceof Color)
264 new FeatureColourChooser(me, type);
265 // write back the current colour object to update the table
266 check.updateColor(fr.getFeatureStyle(type));
270 new UserDefinedColours(me, check.type,
271 ((GraduatedColor) typeCol));
278 this.featurePanel.add(men);
279 men.show(this.featurePanel, x, y);
283 public void discoverAllFeatureData()
285 if (fr.getAllFeatureColours()!=null && fr.getAllFeatureColours().size()>0)
294 * rebuilds the group panel
296 public void rebuildGroups()
298 boolean rdrw = false;
299 if (groupPanel == null)
301 groupPanel = new Panel();
306 groupPanel.removeAll();
308 // TODO: JAL-964 - smoothly incorporate new group entries if panel already
309 // displayed and new groups present
310 for (String group:fr.getFeatureGroups())
312 boolean vis = fr.checkGroupVisibility(group, false);
313 Checkbox check = new MyCheckbox(group, vis,
314 (fr.featureLinks != null && fr.featureLinks
315 .containsKey(group)));
316 check.addMouseListener(this);
317 check.setFont(new Font("Serif", Font.BOLD, 12));
318 check.addItemListener(groupItemListener);
319 // note - visibility seems to correlate with displayed. ???wtf ??
320 check.setVisible(vis);
321 groupPanel.add(check);
325 groupPanel.validate();
328 // This routine adds and removes checkboxes depending on
329 // Group selection states
330 void resetTable(boolean groupsChanged)
332 SequenceFeature[] tmpfeatures;
333 String group = null, type;
334 Vector visibleChecks = new Vector();
335 AlignmentI alignment = av.getAlignment();
336 for (int i = 0; i < alignment.getHeight(); i++)
338 if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
343 tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
345 while (index < tmpfeatures.length)
347 group = tmpfeatures[index].featureGroup;
349 if (group == null || fr.checkGroupVisibility(group, true))
351 type = tmpfeatures[index].getType();
352 if (!visibleChecks.contains(type))
354 visibleChecks.addElement(type);
362 int cSize = featurePanel.getComponentCount();
364 // This will remove any checkboxes which shouldn't be
366 for (int i = 0; i < cSize; i++)
368 comps = featurePanel.getComponents();
369 check = (MyCheckbox) comps[i];
370 if (!visibleChecks.contains(check.type))
372 featurePanel.remove(i);
378 if (fr.getRenderOrder() != null)
380 // First add the checks in the previous render order,
381 // in case the window has been closed and reopened
382 List<String> rol = fr.getRenderOrder();
383 for (int ro = rol.size() - 1; ro > -1; ro--)
385 String item = rol.get(ro);
387 if (!visibleChecks.contains(item))
392 visibleChecks.removeElement(item);
394 addCheck(false, item);
398 // now add checkboxes which should be visible,
399 // if they have not already been added
400 Enumeration en = visibleChecks.elements();
402 while (en.hasMoreElements())
404 addCheck(groupsChanged, en.nextElement().toString());
407 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
409 featurePanel.validate();
411 if (scrollPane != null)
413 scrollPane.validate();
416 itemStateChanged(null);
420 * update the checklist of feature types with the given type
422 * @param groupsChanged
423 * true means if the type is not in the display list then it will be
424 * added and displayed
426 * feature type to be checked for in the list.
428 void addCheck(boolean groupsChanged, String type)
431 Component[] comps = featurePanel.getComponents();
434 for (int i = 0; i < featurePanel.getComponentCount(); i++)
436 check = (MyCheckbox) comps[i];
437 if (check.type.equals(type))
446 boolean selected = false;
447 if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
452 check = new MyCheckbox(
455 (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
456 fr.getFeatureStyle(type));
458 check.addMouseListener(this);
459 check.addMouseMotionListener(this);
460 check.addItemListener(this);
463 // add at beginning of stack.
464 featurePanel.add(check, 0);
468 // add at end of stack.
469 featurePanel.add(check);
474 public void actionPerformed(ActionEvent evt)
476 for (int i = 0; i < featurePanel.getComponentCount(); i++)
478 Checkbox check = (Checkbox) featurePanel.getComponent(i);
479 check.setState(!check.getState());
484 private ItemListener groupItemListener = new ItemListener() {
485 public void itemStateChanged(ItemEvent evt) {
486 Checkbox source = (Checkbox) evt.getSource();
487 fr.setGroupVisibility(source.getLabel(),
489 ap.seqPanel.seqCanvas.repaint();
490 if (ap.overviewPanel != null)
492 ap.overviewPanel.updateOverviewImage();
498 public void itemStateChanged(ItemEvent evt)
503 void selectionChanged()
505 Component[] comps = featurePanel.getComponents();
506 int cSize = comps.length;
508 Object[][] tmp = new Object[cSize][3];
510 for (int i = 0; i < cSize; i++)
512 MyCheckbox check = (MyCheckbox) comps[i];
513 tmp[tmpSize][0] = check.type;
514 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
515 tmp[tmpSize][2] = new Boolean(check.getState());
519 Object[][] data = new Object[tmpSize][3];
520 System.arraycopy(tmp, 0, data, 0, tmpSize);
522 fr.setFeaturePriority(data);
524 ap.paintAlignment(true);
527 MyCheckbox selectedCheck;
529 boolean dragging = false;
531 public void mousePressed(MouseEvent evt)
534 selectedCheck = (MyCheckbox) evt.getSource();
536 if (fr.featureLinks != null
537 && fr.featureLinks.containsKey(selectedCheck.type))
539 if (evt.getX() > selectedCheck.stringWidth + 20)
547 public void mouseDragged(MouseEvent evt)
549 if (((Component) evt.getSource()).getParent() != featurePanel)
556 public void mouseReleased(MouseEvent evt)
558 if (((Component) evt.getSource()).getParent() != featurePanel)
563 Component comp = null;
564 Checkbox target = null;
566 int height = evt.getY() + evt.getComponent().getLocation().y;
568 if (height > featurePanel.getSize().height)
572 .getComponent(featurePanel.getComponentCount() - 1);
576 comp = featurePanel.getComponent(0);
580 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
581 + evt.getComponent().getLocation().y);
584 if (comp != null && comp instanceof Checkbox)
586 target = (Checkbox) comp;
589 if (selectedCheck != null && target != null && selectedCheck != target)
591 int targetIndex = -1;
592 for (int i = 0; i < featurePanel.getComponentCount(); i++)
594 if (target == featurePanel.getComponent(i))
601 featurePanel.remove(selectedCheck);
602 featurePanel.add(selectedCheck, targetIndex);
603 featurePanel.validate();
604 itemStateChanged(null);
608 public void setUserColour(String feature, Object originalColour)
610 if (originalColour instanceof Color
611 || originalColour instanceof GraduatedColor)
613 fr.setColour(feature, originalColour);
617 throw new Error(MessageManager.getString("error.implementation_error_unsupported_feature_colour_object"));
622 public void refreshTable()
624 featurePanel.removeAll();
626 ap.paintAlignment(true);
629 public void mouseEntered(MouseEvent evt)
633 public void mouseExited(MouseEvent evt)
637 public void mouseClicked(MouseEvent evt)
639 MyCheckbox check = (MyCheckbox) evt.getSource();
640 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
642 this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
644 if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
646 if (evt.getX() > check.stringWidth + 20)
649 String link = fr.featureLinks.get(check.type).toString();
650 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1),
651 link.substring(0, link.indexOf("|")));
655 if (check.getParent() != featurePanel)
660 if (evt.getClickCount() > 1)
662 Object fcol = fr.getFeatureStyle(check.type);
663 if (fcol instanceof Color)
665 new UserDefinedColours(this, check.type, (Color) fcol);
669 new FeatureColourChooser(this, check.type);
670 // write back the current colour object to update the table
671 check.updateColor(fr.getFeatureStyle(check.type));
676 public void mouseMoved(MouseEvent evt)
680 public void adjustmentValueChanged(AdjustmentEvent evt)
682 fr.setTransparency((100 - transparency.getValue()) / 100f);
683 ap.seqPanel.seqCanvas.repaint();
687 class MyCheckbox extends Checkbox
691 public int stringWidth;
699 public void updateColor(Object newcol)
701 if (newcol instanceof Color)
703 col = (Color) newcol;
706 else if (newcol instanceof GraduatedColor)
708 gcol = (GraduatedColor) newcol;
713 throw new Error(MessageManager.getString("error.invalid_colour_for_mycheckbox"));
721 String vlabel = type;
722 if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
725 + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
728 if (gcol.isColourByLabel())
730 setBackground(Color.white);
731 vlabel += " (by Label)";
735 setBackground(gcol.getMinColor());
737 this.setLabel(vlabel);
742 public MyCheckbox(String label, boolean checked, boolean haslink)
744 super(label, checked);
746 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
747 stringWidth = fm.stringWidth(label);
748 this.hasLink = haslink;
751 public MyCheckbox(String type, boolean selected, boolean b,
754 this(type, selected, b);
755 updateColor(featureStyle);
758 public void paint(Graphics g)
760 Dimension d = getSize();
763 if (gcol.isColourByLabel())
765 g.setColor(Color.white);
766 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
768 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
771 * // g.setFont(g.getFont().deriveFont( //
772 * AffineTransform.getScaleInstance( //
773 * width/g.getFontMetrics().stringWidth("Label"), //
774 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
781 Color maxCol = gcol.getMaxColor();
783 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
790 g.drawImage(linkImage, stringWidth + 25,
791 (getSize().height - linkImage.getHeight(this)) / 2, this);