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.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.SequenceFeature;
27 import jalview.util.MessageManager;
29 import java.awt.BorderLayout;
30 import java.awt.Button;
31 import java.awt.Checkbox;
32 import java.awt.Color;
33 import java.awt.Component;
34 import java.awt.Dimension;
36 import java.awt.FontMetrics;
37 import java.awt.Frame;
38 import java.awt.Graphics;
39 import java.awt.GridLayout;
40 import java.awt.Image;
41 import java.awt.Label;
42 import java.awt.MenuItem;
43 import java.awt.Panel;
44 import java.awt.PopupMenu;
45 import java.awt.ScrollPane;
46 import java.awt.Scrollbar;
47 import java.awt.event.ActionEvent;
48 import java.awt.event.ActionListener;
49 import java.awt.event.AdjustmentEvent;
50 import java.awt.event.AdjustmentListener;
51 import java.awt.event.InputEvent;
52 import java.awt.event.ItemEvent;
53 import java.awt.event.ItemListener;
54 import java.awt.event.MouseEvent;
55 import java.awt.event.MouseListener;
56 import java.awt.event.MouseMotionListener;
57 import java.awt.event.WindowAdapter;
58 import java.awt.event.WindowEvent;
59 import java.util.Enumeration;
60 import java.util.List;
62 import java.util.Vector;
64 public class FeatureSettings extends Panel implements ItemListener,
65 MouseListener, MouseMotionListener, ActionListener,
66 AdjustmentListener, FeatureSettingsControllerI
78 Panel featurePanel = new Panel();
80 ScrollPane scrollPane;
84 Scrollbar transparency;
86 public FeatureSettings(final AlignmentPanel ap)
90 ap.av.featureSettings = this;
91 fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
93 transparency = new Scrollbar(Scrollbar.HORIZONTAL,
94 100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
96 if (fr.isTransparencyAvailable())
98 transparency.addAdjustmentListener(this);
102 transparency.setEnabled(false);
105 java.net.URL url = getClass().getResource("/images/link.gif");
108 linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
111 if (av.isShowSequenceFeatures() || !fr.hasRenderOrder())
113 fr.findAllFeatures(true); // was default - now true to make all visible
116 discoverAllFeatureData();
118 this.setLayout(new BorderLayout());
119 scrollPane = new ScrollPane();
120 scrollPane.add(featurePanel);
121 if (fr.getAllFeatureColours() != null
122 && 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)
153 groupPanel.setLayout(new GridLayout(
154 (fr.getFeatureGroupsSize()) / 4 + 1, 4)); // JBPNote - this was
155 // scaled on number of
156 // visible groups. seems
158 groupPanel.validate();
160 add(groupPanel, BorderLayout.NORTH);
164 final FeatureSettings me = this;
165 frame.addWindowListener(new WindowAdapter()
168 public void windowClosing(WindowEvent e)
170 if (me.av.featureSettings == me)
172 me.av.featureSettings = null;
178 int height = featurePanel.getComponentCount() * 50 + 60;
180 height = Math.max(200, height);
181 height = Math.min(400, height);
183 jalview.bin.JalviewLite.addFrame(frame,
184 MessageManager.getString("label.sequence_feature_settings"),
189 public void paint(Graphics g)
191 g.setColor(Color.black);
192 g.drawString(MessageManager
193 .getString("label.no_features_added_to_this_alignment"), 10, 20);
194 g.drawString(MessageManager
195 .getString("label.features_can_be_added_from_searches_1"), 10,
197 g.drawString(MessageManager
198 .getString("label.features_can_be_added_from_searches_2"), 10,
202 protected void popupSort(final MyCheckbox check,
203 final Map<String, float[][]> minmax,
206 final String type = check.type;
207 final FeatureColourI typeCol = fr.getFeatureStyle(type);
208 PopupMenu men = new PopupMenu(MessageManager.formatMessage(
209 "label.settings_for_type", new String[] { type }));
210 MenuItem scr = new MenuItem(
211 MessageManager.getString("label.sort_by_score"));
213 final FeatureSettings me = this;
214 scr.addActionListener(new ActionListener()
218 public void actionPerformed(ActionEvent e)
221 .sortAlignmentByFeatureScore(new String[] { type });
225 MenuItem dens = new MenuItem(
226 MessageManager.getString("label.sort_by_density"));
227 dens.addActionListener(new ActionListener()
231 public void actionPerformed(ActionEvent e)
234 .sortAlignmentByFeatureDensity(new String[] { type });
241 final float[][] typeMinMax = minmax.get(type);
243 * final java.awt.CheckboxMenuItem chb = new
244 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
245 * moment chb.setState(minmax.get(type) != null);
246 * chb.addActionListener(new ActionListener() {
248 * public void actionPerformed(ActionEvent e) {
249 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
250 * null); } else { minmax.put(type, typeMinMax); } }
254 if (typeMinMax != null && typeMinMax[0] != null)
256 // graduated colourschemes for those where minmax exists for the
257 // positional features
258 MenuItem mxcol = new MenuItem(
259 (typeCol.isSimpleColour()) ? "Graduated Colour"
262 mxcol.addActionListener(new ActionListener()
266 public void actionPerformed(ActionEvent e)
268 if (typeCol.isSimpleColour())
270 new FeatureColourChooser(me, type);
271 // write back the current colour object to update the table
272 check.updateColor(fr.getFeatureStyle(type));
276 new UserDefinedColours(me, check.type, typeCol);
283 this.featurePanel.add(men);
284 men.show(this.featurePanel, x, y);
288 public void discoverAllFeatureData()
290 if (fr.getAllFeatureColours() != null
291 && fr.getAllFeatureColours().size() > 0)
300 * rebuilds the group panel
302 public void rebuildGroups()
304 boolean rdrw = false;
305 if (groupPanel == null)
307 groupPanel = new Panel();
312 groupPanel.removeAll();
314 // TODO: JAL-964 - smoothly incorporate new group entries if panel already
315 // displayed and new groups present
316 for (String group : fr.getFeatureGroups())
318 boolean vis = fr.checkGroupVisibility(group, false);
319 Checkbox check = new MyCheckbox(group, vis,
320 (fr.featureLinks != null && fr.featureLinks
321 .containsKey(group)));
322 check.addMouseListener(this);
323 check.setFont(new Font("Serif", Font.BOLD, 12));
324 check.addItemListener(groupItemListener);
325 // note - visibility seems to correlate with displayed. ???wtf ??
326 check.setVisible(vis);
327 groupPanel.add(check);
331 groupPanel.validate();
335 // This routine adds and removes checkboxes depending on
336 // Group selection states
337 void resetTable(boolean groupsChanged)
339 SequenceFeature[] tmpfeatures;
340 String group = null, type;
341 Vector<String> visibleChecks = new Vector<String>();
342 AlignmentI alignment = av.getAlignment();
343 for (int i = 0; i < alignment.getHeight(); i++)
345 if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
350 tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
352 while (index < tmpfeatures.length)
354 group = tmpfeatures[index].featureGroup;
356 if (group == null || fr.checkGroupVisibility(group, true))
358 type = tmpfeatures[index].getType();
359 if (!visibleChecks.contains(type))
361 visibleChecks.addElement(type);
369 int cSize = featurePanel.getComponentCount();
371 // This will remove any checkboxes which shouldn't be
373 for (int i = 0; i < cSize; i++)
375 comps = featurePanel.getComponents();
376 check = (MyCheckbox) comps[i];
377 if (!visibleChecks.contains(check.type))
379 featurePanel.remove(i);
385 if (fr.getRenderOrder() != null)
387 // First add the checks in the previous render order,
388 // in case the window has been closed and reopened
389 List<String> rol = fr.getRenderOrder();
390 for (int ro = rol.size() - 1; ro > -1; ro--)
392 String item = rol.get(ro);
394 if (!visibleChecks.contains(item))
399 visibleChecks.removeElement(item);
401 addCheck(false, item);
405 // now add checkboxes which should be visible,
406 // if they have not already been added
407 Enumeration<String> en = visibleChecks.elements();
409 while (en.hasMoreElements())
411 addCheck(groupsChanged, en.nextElement().toString());
414 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
416 featurePanel.validate();
418 if (scrollPane != null)
420 scrollPane.validate();
423 itemStateChanged(null);
427 * update the checklist of feature types with the given type
429 * @param groupsChanged
430 * true means if the type is not in the display list then it will be
431 * added and displayed
433 * feature type to be checked for in the list.
435 void addCheck(boolean groupsChanged, String type)
438 Component[] comps = featurePanel.getComponents();
441 for (int i = 0; i < featurePanel.getComponentCount(); i++)
443 check = (MyCheckbox) comps[i];
444 if (check.type.equals(type))
453 boolean selected = false;
454 if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
459 check = new MyCheckbox(
462 (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
463 fr.getFeatureStyle(type));
465 check.addMouseListener(this);
466 check.addMouseMotionListener(this);
467 check.addItemListener(this);
470 // add at beginning of stack.
471 featurePanel.add(check, 0);
475 // add at end of stack.
476 featurePanel.add(check);
482 public void actionPerformed(ActionEvent evt)
484 for (int i = 0; i < featurePanel.getComponentCount(); i++)
486 Checkbox check = (Checkbox) featurePanel.getComponent(i);
487 check.setState(!check.getState());
492 private ItemListener groupItemListener = new ItemListener()
495 public void itemStateChanged(ItemEvent evt)
497 Checkbox source = (Checkbox) evt.getSource();
498 fr.setGroupVisibility(source.getLabel(), source.getState());
499 ap.seqPanel.seqCanvas.repaint();
500 if (ap.overviewPanel != null)
502 ap.overviewPanel.updateOverviewImage();
510 public void itemStateChanged(ItemEvent evt)
515 void selectionChanged()
517 Component[] comps = featurePanel.getComponents();
518 int cSize = comps.length;
520 Object[][] tmp = new Object[cSize][3];
522 for (int i = 0; i < cSize; i++)
524 MyCheckbox check = (MyCheckbox) comps[i];
525 tmp[tmpSize][0] = check.type;
526 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
527 tmp[tmpSize][2] = new Boolean(check.getState());
531 Object[][] data = new Object[tmpSize][3];
532 System.arraycopy(tmp, 0, data, 0, tmpSize);
534 fr.setFeaturePriority(data);
536 ap.paintAlignment(true);
539 MyCheckbox selectedCheck;
541 boolean dragging = false;
544 public void mousePressed(MouseEvent evt)
547 selectedCheck = (MyCheckbox) evt.getSource();
549 if (fr.featureLinks != null
550 && fr.featureLinks.containsKey(selectedCheck.type))
552 if (evt.getX() > selectedCheck.stringWidth + 20)
561 public void mouseDragged(MouseEvent evt)
563 if (((Component) evt.getSource()).getParent() != featurePanel)
571 public void mouseReleased(MouseEvent evt)
573 if (((Component) evt.getSource()).getParent() != featurePanel)
578 Component comp = null;
579 Checkbox target = null;
581 int height = evt.getY() + evt.getComponent().getLocation().y;
583 if (height > featurePanel.getSize().height)
587 .getComponent(featurePanel.getComponentCount() - 1);
591 comp = featurePanel.getComponent(0);
595 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
596 + evt.getComponent().getLocation().y);
599 if (comp != null && comp instanceof Checkbox)
601 target = (Checkbox) comp;
604 if (selectedCheck != null && target != null && selectedCheck != target)
606 int targetIndex = -1;
607 for (int i = 0; i < featurePanel.getComponentCount(); i++)
609 if (target == featurePanel.getComponent(i))
616 featurePanel.remove(selectedCheck);
617 featurePanel.add(selectedCheck, targetIndex);
618 featurePanel.validate();
619 itemStateChanged(null);
623 public void setUserColour(String feature, FeatureColourI originalColour)
625 fr.setColour(feature, originalColour);
629 public void refreshTable()
631 featurePanel.removeAll();
633 ap.paintAlignment(true);
637 public void mouseEntered(MouseEvent evt)
642 public void mouseExited(MouseEvent evt)
647 public void mouseClicked(MouseEvent evt)
649 MyCheckbox check = (MyCheckbox) evt.getSource();
650 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
652 this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
654 if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
656 if (evt.getX() > check.stringWidth + 20)
659 String link = fr.featureLinks.get(check.type).toString();
660 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1),
661 link.substring(0, link.indexOf("|")));
665 if (check.getParent() != featurePanel)
670 if (evt.getClickCount() > 1)
672 FeatureColourI fcol = fr.getFeatureStyle(check.type);
673 if (fcol.isSimpleColour())
675 new UserDefinedColours(this, check.type, fcol.getColour());
679 new FeatureColourChooser(this, check.type);
680 // write back the current colour object to update the table
681 check.updateColor(fr.getFeatureStyle(check.type));
687 public void mouseMoved(MouseEvent evt)
692 public void adjustmentValueChanged(AdjustmentEvent evt)
694 fr.setTransparency((100 - transparency.getValue()) / 100f);
695 ap.seqPanel.seqCanvas.repaint();
699 class MyCheckbox extends Checkbox
703 public int stringWidth;
709 public void updateColor(FeatureColourI newcol)
712 if (col.isSimpleColour())
714 setBackground(col.getColour());
718 String vlabel = type;
719 if (col.isAboveThreshold())
723 else if (col.isBelowThreshold())
727 if (col.isColourByLabel())
729 setBackground(Color.white);
730 vlabel += " (by Label)";
734 setBackground(col.getMinColour());
736 this.setLabel(vlabel);
741 public MyCheckbox(String label, boolean checked, boolean haslink)
743 super(label, checked);
745 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
746 stringWidth = fm.stringWidth(label);
747 this.hasLink = haslink;
750 public MyCheckbox(String type, boolean selected, boolean b,
751 FeatureColourI featureStyle)
753 this(type, selected, b);
754 updateColor(featureStyle);
758 public void paint(Graphics g)
760 Dimension d = getSize();
761 if (col.isColourByLabel())
763 g.setColor(Color.white);
764 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
766 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
769 * // g.setFont(g.getFont().deriveFont( //
770 * AffineTransform.getScaleInstance( //
771 * width/g.getFontMetrics().stringWidth("Label"), //
772 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
777 else if (col.isGraduatedColour())
779 Color maxCol = col.getMaxColour();
781 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
787 g.drawImage(linkImage, stringWidth + 25,
788 (getSize().height - linkImage.getHeight(this)) / 2, this);