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.SequenceI;
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.ArrayList;
60 import java.util.Arrays;
61 import java.util.HashSet;
62 import java.util.List;
66 public class FeatureSettings extends Panel
67 implements ItemListener, MouseListener, MouseMotionListener,
68 AdjustmentListener, FeatureSettingsControllerI
80 Panel featurePanel = new Panel();
82 ScrollPane scrollPane;
86 Scrollbar transparency;
88 public FeatureSettings(final AlignmentPanel ap)
92 ap.av.featureSettings = this;
93 fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
95 transparency = new Scrollbar(Scrollbar.HORIZONTAL,
96 100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
98 transparency.addAdjustmentListener(this);
100 java.net.URL url = getClass().getResource("/images/link.gif");
103 linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
106 if (av.isShowSequenceFeatures() || !fr.hasRenderOrder())
108 fr.findAllFeatures(true); // was default - now true to make all visible
110 groupPanel = new Panel();
112 discoverAllFeatureData();
114 this.setLayout(new BorderLayout());
115 scrollPane = new ScrollPane();
116 scrollPane.add(featurePanel);
117 if (fr.getAllFeatureColours() != null
118 && fr.getAllFeatureColours().size() > 0)
120 add(scrollPane, BorderLayout.CENTER);
123 Button invert = new Button(
124 MessageManager.getString("label.invert_selection"));
125 invert.addActionListener(new ActionListener()
129 public void actionPerformed(ActionEvent e)
135 Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
136 lowerPanel.add(invert);
138 Panel tPanel = new Panel(new BorderLayout());
140 tPanel.add(transparency, BorderLayout.CENTER);
141 tPanel.add(new Label("Transparency"), BorderLayout.EAST);
143 lowerPanel.add(tPanel, BorderLayout.SOUTH);
145 add(lowerPanel, BorderLayout.SOUTH);
147 groupPanel.setLayout(
148 new GridLayout((fr.getFeatureGroupsSize()) / 4 + 1, 4)); // JBPNote
159 groupPanel.validate();
161 add(groupPanel, BorderLayout.NORTH);
165 final FeatureSettings me = this;
166 frame.addWindowListener(new WindowAdapter()
169 public void windowClosing(WindowEvent e)
171 if (me.av.featureSettings == me)
173 me.av.featureSettings = null;
179 int height = featurePanel.getComponentCount() * 50 + 60;
181 height = Math.max(200, height);
182 height = Math.min(400, height);
184 jalview.bin.JalviewLite.addFrame(frame,
185 MessageManager.getString("label.sequence_feature_settings"),
190 public void paint(Graphics g)
192 g.setColor(Color.black);
193 g.drawString(MessageManager.getString(
194 "label.no_features_added_to_this_alignment"), 10, 20);
195 g.drawString(MessageManager.getString(
196 "label.features_can_be_added_from_searches_1"), 10, 40);
197 g.drawString(MessageManager.getString(
198 "label.features_can_be_added_from_searches_2"), 10, 60);
201 protected void popupSort(final MyCheckbox check,
202 final Map<String, float[][]> minmax, int x, int y)
204 final String type = check.type;
205 final FeatureColourI typeCol = fr.getFeatureStyle(type);
206 PopupMenu men = new PopupMenu(MessageManager
207 .formatMessage("label.settings_for_type", new String[]
209 java.awt.MenuItem scr = new MenuItem(
210 MessageManager.getString("label.sort_by_score"));
212 final FeatureSettings me = this;
213 scr.addActionListener(new ActionListener()
217 public void actionPerformed(ActionEvent e)
220 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
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(Arrays.asList(new String[]
243 final float[][] typeMinMax = minmax.get(type);
245 * final java.awt.CheckboxMenuItem chb = new
246 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
247 * moment chb.setState(minmax.get(type) != null);
248 * chb.addActionListener(new ActionListener() {
250 * public void actionPerformed(ActionEvent e) {
251 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
252 * null); } else { minmax.put(type, typeMinMax); } }
256 if (typeMinMax != null && typeMinMax[0] != null)
258 // graduated colourschemes for those where minmax exists for the
259 // positional features
260 MenuItem mxcol = new MenuItem(
261 (typeCol.isSimpleColour()) ? "Graduated Colour"
264 mxcol.addActionListener(new ActionListener()
268 public void actionPerformed(ActionEvent e)
270 if (typeCol.isSimpleColour())
272 new FeatureColourChooser(me, type);
273 // write back the current colour object to update the table
274 check.updateColor(fr.getFeatureStyle(type));
278 new UserDefinedColours(me, check.type, typeCol);
286 MenuItem selectContaining = new MenuItem(
287 MessageManager.getString("label.select_columns_containing"));
288 selectContaining.addActionListener(new ActionListener()
291 public void actionPerformed(ActionEvent e)
293 me.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
297 men.add(selectContaining);
299 MenuItem selectNotContaining = new MenuItem(MessageManager
300 .getString("label.select_columns_not_containing"));
301 selectNotContaining.addActionListener(new ActionListener()
304 public void actionPerformed(ActionEvent e)
306 me.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
310 men.add(selectNotContaining);
312 MenuItem hideContaining = new MenuItem(
313 MessageManager.getString("label.hide_columns_containing"));
314 hideContaining.addActionListener(new ActionListener()
317 public void actionPerformed(ActionEvent e)
319 hideFeatureColumns(type, true);
322 men.add(hideContaining);
324 MenuItem hideNotContaining = new MenuItem(
325 MessageManager.getString("label.hide_columns_not_containing"));
326 hideNotContaining.addActionListener(new ActionListener()
329 public void actionPerformed(ActionEvent e)
331 hideFeatureColumns(type, false);
334 men.add(hideNotContaining);
336 this.featurePanel.add(men);
337 men.show(this.featurePanel, x, y);
341 public void discoverAllFeatureData()
343 if (fr.getAllFeatureColours() != null
344 && fr.getAllFeatureColours().size() > 0)
353 * Answers the visibility of the given group, and adds a checkbox for it if
354 * there is not one already
356 public boolean checkGroupState(String group)
358 boolean visible = fr.checkGroupVisibility(group, true);
361 * is there already a checkbox for this group?
363 for (int g = 0; g < groupPanel.getComponentCount(); g++)
365 if (((Checkbox) groupPanel.getComponent(g)).getLabel().equals(group))
367 ((Checkbox) groupPanel.getComponent(g)).setState(visible);
375 Checkbox check = new MyCheckbox(group, visible, false);
376 check.addMouseListener(this);
377 check.setFont(new Font("Serif", Font.BOLD, 12));
378 check.addItemListener(groupItemListener);
379 groupPanel.add(check);
381 groupPanel.validate();
385 // This routine adds and removes checkboxes depending on
386 // Group selection states
387 void resetTable(boolean groupsChanged)
389 List<String> displayableTypes = new ArrayList<>();
390 Set<String> foundGroups = new HashSet<>();
392 AlignmentI alignment = av.getAlignment();
394 for (int i = 0; i < alignment.getHeight(); i++)
396 SequenceI seq = alignment.getSequenceAt(i);
399 * get the sequence's groups for positional features
400 * and keep track of which groups are visible
402 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
403 Set<String> visibleGroups = new HashSet<>();
404 for (String group : groups)
406 // if (group == null || fr.checkGroupVisibility(group, true))
407 if (group == null || checkGroupState(group))
409 visibleGroups.add(group);
412 foundGroups.addAll(groups);
415 * get distinct feature types for visible groups
416 * record distinct visible types
418 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
419 visibleGroups.toArray(new String[visibleGroups.size()]));
420 displayableTypes.addAll(types);
424 * remove any checkboxes for groups not present
426 pruneGroups(foundGroups);
429 int cSize = featurePanel.getComponentCount();
431 // This will remove any checkboxes which shouldn't be
433 for (int i = 0; i < cSize; i++)
435 comps = featurePanel.getComponents();
436 check = (MyCheckbox) comps[i];
437 if (!displayableTypes.contains(check.type))
439 featurePanel.remove(i);
445 if (fr.getRenderOrder() != null)
447 // First add the checks in the previous render order,
448 // in case the window has been closed and reopened
449 List<String> rol = fr.getRenderOrder();
450 for (int ro = rol.size() - 1; ro > -1; ro--)
452 String item = rol.get(ro);
454 if (!displayableTypes.contains(item))
459 displayableTypes.remove(item);
461 addCheck(false, item);
466 * now add checkboxes which should be visible,
467 * if they have not already been added
469 for (String type : displayableTypes)
471 addCheck(groupsChanged, type);
474 featurePanel.setLayout(
475 new GridLayout(featurePanel.getComponentCount(), 1, 10, 5));
476 featurePanel.validate();
478 if (scrollPane != null)
480 scrollPane.validate();
483 itemStateChanged(null);
487 * Remove from the groups panel any checkboxes for groups that are not in the
488 * foundGroups set. This enables removing a group from the display when the
489 * last feature in that group is deleted.
493 protected void pruneGroups(Set<String> foundGroups)
495 for (int g = 0; g < groupPanel.getComponentCount(); g++)
497 Checkbox checkbox = (Checkbox) groupPanel.getComponent(g);
498 if (!foundGroups.contains(checkbox.getLabel()))
500 groupPanel.remove(checkbox);
506 * update the checklist of feature types with the given type
508 * @param groupsChanged
509 * true means if the type is not in the display list then it will be
510 * added and displayed
512 * feature type to be checked for in the list.
514 void addCheck(boolean groupsChanged, String type)
517 Component[] comps = featurePanel.getComponents();
520 for (int i = 0; i < featurePanel.getComponentCount(); i++)
522 check = (MyCheckbox) comps[i];
523 if (check.type.equals(type))
532 boolean selected = false;
533 if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
538 check = new MyCheckbox(type, selected, false,
539 fr.getFeatureStyle(type));
541 check.addMouseListener(this);
542 check.addMouseMotionListener(this);
543 check.addItemListener(this);
546 // add at beginning of stack.
547 featurePanel.add(check, 0);
551 // add at end of stack.
552 featurePanel.add(check);
557 protected void invertSelection()
559 for (int i = 0; i < featurePanel.getComponentCount(); i++)
561 Checkbox check = (Checkbox) featurePanel.getComponent(i);
562 check.setState(!check.getState());
564 selectionChanged(true);
567 private ItemListener groupItemListener = new ItemListener()
570 public void itemStateChanged(ItemEvent evt)
572 Checkbox source = (Checkbox) evt.getSource();
573 fr.setGroupVisibility(source.getLabel(), source.getState());
574 ap.seqPanel.seqCanvas.repaint();
575 if (ap.overviewPanel != null)
577 ap.overviewPanel.updateOverviewImage();
585 public void itemStateChanged(ItemEvent evt)
587 selectionChanged(true);
590 void selectionChanged(boolean updateOverview)
592 Component[] comps = featurePanel.getComponents();
593 int cSize = comps.length;
595 Object[][] tmp = new Object[cSize][3];
597 for (int i = 0; i < cSize; i++)
599 MyCheckbox check = (MyCheckbox) comps[i];
600 tmp[tmpSize][0] = check.type;
601 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
602 tmp[tmpSize][2] = new Boolean(check.getState());
606 Object[][] data = new Object[tmpSize][3];
607 System.arraycopy(tmp, 0, data, 0, tmpSize);
609 fr.setFeaturePriority(data);
611 ap.paintAlignment(updateOverview, updateOverview);
614 MyCheckbox selectedCheck;
616 boolean dragging = false;
619 public void mouseDragged(MouseEvent evt)
621 if (((Component) evt.getSource()).getParent() != featurePanel)
629 public void mouseReleased(MouseEvent evt)
631 if (((Component) evt.getSource()).getParent() != featurePanel)
636 Component comp = null;
637 Checkbox target = null;
639 int height = evt.getY() + evt.getComponent().getLocation().y;
641 if (height > featurePanel.getSize().height)
645 .getComponent(featurePanel.getComponentCount() - 1);
649 comp = featurePanel.getComponent(0);
653 comp = featurePanel.getComponentAt(evt.getX(),
654 evt.getY() + evt.getComponent().getLocation().y);
657 if (comp != null && comp instanceof Checkbox)
659 target = (Checkbox) comp;
662 if (selectedCheck != null && target != null && selectedCheck != target)
664 int targetIndex = -1;
665 for (int i = 0; i < featurePanel.getComponentCount(); i++)
667 if (target == featurePanel.getComponent(i))
674 featurePanel.remove(selectedCheck);
675 featurePanel.add(selectedCheck, targetIndex);
676 featurePanel.validate();
677 itemStateChanged(null);
681 public void setUserColour(String feature, FeatureColourI originalColour)
683 fr.setColour(feature, originalColour);
687 public void refreshTable()
689 featurePanel.removeAll();
691 ap.paintAlignment(true, true);
695 public void mouseEntered(MouseEvent evt)
700 public void mouseExited(MouseEvent evt)
705 public void mouseClicked(MouseEvent evt)
707 MyCheckbox check = (MyCheckbox) evt.getSource();
708 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
710 this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
713 if (check.getParent() != featurePanel)
718 if (evt.getClickCount() > 1)
720 FeatureColourI fcol = fr.getFeatureStyle(check.type);
721 if (fcol.isSimpleColour())
723 new UserDefinedColours(this, check.type, fcol.getColour());
727 new FeatureColourChooser(this, check.type);
728 // write back the current colour object to update the table
729 check.updateColor(fr.getFeatureStyle(check.type));
735 public void mouseMoved(MouseEvent evt)
740 public void adjustmentValueChanged(AdjustmentEvent evt)
742 fr.setTransparency((100 - transparency.getValue()) / 100f);
743 ap.paintAlignment(true, true);
746 class MyCheckbox extends Checkbox
750 public int stringWidth;
756 public void updateColor(FeatureColourI newcol)
759 if (col.isSimpleColour())
761 setBackground(col.getColour());
765 String vlabel = type;
766 if (col.isAboveThreshold())
770 else if (col.isBelowThreshold())
774 if (col.isColourByLabel())
776 setBackground(Color.white);
777 vlabel += " (by Label)";
781 setBackground(col.getMinColour());
783 this.setLabel(vlabel);
788 public MyCheckbox(String label, boolean checked, boolean haslink)
790 super(label, checked);
792 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
793 stringWidth = fm.stringWidth(label);
794 this.hasLink = haslink;
797 public MyCheckbox(String type, boolean selected, boolean b,
798 FeatureColourI featureStyle)
800 this(type, selected, b);
801 updateColor(featureStyle);
805 public void paint(Graphics g)
807 Dimension d = getSize();
810 if (col.isColourByLabel())
812 g.setColor(Color.white);
813 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
815 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
818 * // g.setFont(g.getFont().deriveFont( //
819 * AffineTransform.getScaleInstance( //
820 * width/g.getFontMetrics().stringWidth("Label"), //
821 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
826 else if (col.isGraduatedColour())
828 Color maxCol = col.getMaxColour();
830 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
837 g.drawImage(linkImage, stringWidth + 25,
838 (getSize().height - linkImage.getHeight(this)) / 2, this);
844 * Hide columns containing (or not containing) a given feature type
847 * @param columnsContaining
849 void hideFeatureColumns(final String type, boolean columnsContaining)
851 if (ap.alignFrame.avc.markColumnsContainingFeatures(columnsContaining,
854 if (ap.alignFrame.avc.markColumnsContainingFeatures(
855 !columnsContaining, false, false, type))
857 ap.alignFrame.viewport.hideSelectedColumns();
863 public void mousePressed(MouseEvent e)
865 // TODO Auto-generated method stub