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.Arrays;
60 import java.util.Enumeration;
61 import java.util.HashSet;
62 import java.util.List;
65 import java.util.Vector;
67 public class FeatureSettings extends Panel
68 implements ItemListener, MouseListener, MouseMotionListener,
69 ActionListener, AdjustmentListener, FeatureSettingsControllerI
81 Panel featurePanel = new Panel();
83 ScrollPane scrollPane;
87 Scrollbar transparency;
89 public FeatureSettings(final AlignmentPanel ap)
93 ap.av.featureSettings = this;
94 fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
96 transparency = new Scrollbar(Scrollbar.HORIZONTAL,
97 100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
99 transparency.addAdjustmentListener(this);
101 java.net.URL url = getClass().getResource("/images/link.gif");
104 linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
107 if (av.isShowSequenceFeatures() || !fr.hasRenderOrder())
109 fr.findAllFeatures(true); // was default - now true to make all visible
111 groupPanel = new Panel();
113 discoverAllFeatureData();
115 this.setLayout(new BorderLayout());
116 scrollPane = new ScrollPane();
117 scrollPane.add(featurePanel);
118 if (fr.getAllFeatureColours() != null
119 && fr.getAllFeatureColours().size() > 0)
121 add(scrollPane, BorderLayout.CENTER);
124 Button invert = new Button("Invert Selection");
125 invert.addActionListener(this);
127 Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
128 lowerPanel.add(invert);
130 Panel tPanel = new Panel(new BorderLayout());
132 tPanel.add(transparency, BorderLayout.CENTER);
133 tPanel.add(new Label("Transparency"), BorderLayout.EAST);
135 lowerPanel.add(tPanel, BorderLayout.SOUTH);
137 add(lowerPanel, BorderLayout.SOUTH);
139 groupPanel.setLayout(
140 new GridLayout((fr.getFeatureGroupsSize()) / 4 + 1, 4)); // JBPNote
151 groupPanel.validate();
153 add(groupPanel, BorderLayout.NORTH);
157 final FeatureSettings me = this;
158 frame.addWindowListener(new WindowAdapter()
161 public void windowClosing(WindowEvent e)
163 if (me.av.featureSettings == me)
165 me.av.featureSettings = null;
171 int height = featurePanel.getComponentCount() * 50 + 60;
173 height = Math.max(200, height);
174 height = Math.min(400, height);
176 jalview.bin.JalviewLite.addFrame(frame,
177 MessageManager.getString("label.sequence_feature_settings"),
182 public void paint(Graphics g)
184 g.setColor(Color.black);
185 g.drawString(MessageManager.getString(
186 "label.no_features_added_to_this_alignment"), 10, 20);
187 g.drawString(MessageManager.getString(
188 "label.features_can_be_added_from_searches_1"), 10, 40);
189 g.drawString(MessageManager.getString(
190 "label.features_can_be_added_from_searches_2"), 10, 60);
193 protected void popupSort(final MyCheckbox check,
194 final Map<String, float[][]> minmax, int x, int y)
196 final String type = check.type;
197 final FeatureColourI typeCol = fr.getFeatureStyle(type);
198 PopupMenu men = new PopupMenu(MessageManager
199 .formatMessage("label.settings_for_type", new String[]
201 java.awt.MenuItem scr = new MenuItem(
202 MessageManager.getString("label.sort_by_score"));
204 final FeatureSettings me = this;
205 scr.addActionListener(new ActionListener()
209 public void actionPerformed(ActionEvent e)
212 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
217 MenuItem dens = new MenuItem(
218 MessageManager.getString("label.sort_by_density"));
219 dens.addActionListener(new ActionListener()
223 public void actionPerformed(ActionEvent e)
226 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
235 final float[][] typeMinMax = minmax.get(type);
237 * final java.awt.CheckboxMenuItem chb = new
238 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
239 * moment chb.setState(minmax.get(type) != null);
240 * chb.addActionListener(new ActionListener() {
242 * public void actionPerformed(ActionEvent e) {
243 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
244 * null); } else { minmax.put(type, typeMinMax); } }
248 if (typeMinMax != null && typeMinMax[0] != null)
250 // graduated colourschemes for those where minmax exists for the
251 // positional features
252 MenuItem mxcol = new MenuItem(
253 (typeCol.isSimpleColour()) ? "Graduated Colour"
256 mxcol.addActionListener(new ActionListener()
260 public void actionPerformed(ActionEvent e)
262 if (typeCol.isSimpleColour())
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, typeCol);
278 MenuItem selectContaining = new MenuItem(
279 MessageManager.getString("label.select_columns_containing"));
280 selectContaining.addActionListener(new ActionListener()
283 public void actionPerformed(ActionEvent e)
285 me.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
289 men.add(selectContaining);
291 MenuItem selectNotContaining = new MenuItem(MessageManager
292 .getString("label.select_columns_not_containing"));
293 selectNotContaining.addActionListener(new ActionListener()
296 public void actionPerformed(ActionEvent e)
298 me.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
302 men.add(selectNotContaining);
304 MenuItem hideContaining = new MenuItem(
305 MessageManager.getString("label.hide_columns_containing"));
306 hideContaining.addActionListener(new ActionListener()
309 public void actionPerformed(ActionEvent e)
311 hideFeatureColumns(type, true);
314 men.add(hideContaining);
316 MenuItem hideNotContaining = new MenuItem(
317 MessageManager.getString("label.hide_columns_not_containing"));
318 hideNotContaining.addActionListener(new ActionListener()
321 public void actionPerformed(ActionEvent e)
323 hideFeatureColumns(type, false);
326 men.add(hideNotContaining);
328 this.featurePanel.add(men);
329 men.show(this.featurePanel, x, y);
333 public void discoverAllFeatureData()
335 if (fr.getAllFeatureColours() != null
336 && fr.getAllFeatureColours().size() > 0)
345 * Answers the visibility of the given group, and adds a checkbox for it if
346 * there is not one already
348 public boolean checkGroupState(String group)
350 boolean visible = fr.checkGroupVisibility(group, true);
353 * is there already a checkbox for this group?
355 for (int g = 0; g < groupPanel.getComponentCount(); g++)
357 if (((Checkbox) groupPanel.getComponent(g)).getLabel().equals(group))
359 ((Checkbox) groupPanel.getComponent(g)).setState(visible);
367 Checkbox check = new MyCheckbox(group, visible, false);
368 check.addMouseListener(this);
369 check.setFont(new Font("Serif", Font.BOLD, 12));
370 check.addItemListener(groupItemListener);
371 groupPanel.add(check);
373 groupPanel.validate();
377 // This routine adds and removes checkboxes depending on
378 // Group selection states
379 void resetTable(boolean groupsChanged)
381 SequenceFeature[] tmpfeatures;
382 String group = null, type;
383 Vector<String> visibleChecks = new Vector<String>();
384 Set<String> foundGroups = new HashSet<String>();
385 AlignmentI alignment = av.getAlignment();
387 for (int i = 0; i < alignment.getHeight(); i++)
389 if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
394 tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
396 while (index < tmpfeatures.length)
398 group = tmpfeatures[index].featureGroup;
399 foundGroups.add(group);
401 if (group == null || checkGroupState(group))
403 type = tmpfeatures[index].getType();
404 if (!visibleChecks.contains(type))
406 visibleChecks.addElement(type);
414 * remove any checkboxes for groups not present
416 pruneGroups(foundGroups);
419 int cSize = featurePanel.getComponentCount();
421 // This will remove any checkboxes which shouldn't be
423 for (int i = 0; i < cSize; i++)
425 comps = featurePanel.getComponents();
426 check = (MyCheckbox) comps[i];
427 if (!visibleChecks.contains(check.type))
429 featurePanel.remove(i);
435 if (fr.getRenderOrder() != null)
437 // First add the checks in the previous render order,
438 // in case the window has been closed and reopened
439 List<String> rol = fr.getRenderOrder();
440 for (int ro = rol.size() - 1; ro > -1; ro--)
442 String item = rol.get(ro);
444 if (!visibleChecks.contains(item))
449 visibleChecks.removeElement(item);
451 addCheck(false, item);
455 // now add checkboxes which should be visible,
456 // if they have not already been added
457 Enumeration<String> en = visibleChecks.elements();
459 while (en.hasMoreElements())
461 addCheck(groupsChanged, en.nextElement().toString());
464 featurePanel.setLayout(
465 new GridLayout(featurePanel.getComponentCount(), 1, 10, 5));
466 featurePanel.validate();
468 if (scrollPane != null)
470 scrollPane.validate();
473 itemStateChanged(null);
477 * Remove from the groups panel any checkboxes for groups that are not in the
478 * foundGroups set. This enables removing a group from the display when the
479 * last feature in that group is deleted.
483 protected void pruneGroups(Set<String> foundGroups)
485 for (int g = 0; g < groupPanel.getComponentCount(); g++)
487 Checkbox checkbox = (Checkbox) groupPanel.getComponent(g);
488 if (!foundGroups.contains(checkbox.getLabel()))
490 groupPanel.remove(checkbox);
496 * update the checklist of feature types with the given type
498 * @param groupsChanged
499 * true means if the type is not in the display list then it will be
500 * added and displayed
502 * feature type to be checked for in the list.
504 void addCheck(boolean groupsChanged, String type)
507 Component[] comps = featurePanel.getComponents();
510 for (int i = 0; i < featurePanel.getComponentCount(); i++)
512 check = (MyCheckbox) comps[i];
513 if (check.type.equals(type))
522 boolean selected = false;
523 if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
528 check = new MyCheckbox(type, selected, false,
529 fr.getFeatureStyle(type));
531 check.addMouseListener(this);
532 check.addMouseMotionListener(this);
533 check.addItemListener(this);
536 // add at beginning of stack.
537 featurePanel.add(check, 0);
541 // add at end of stack.
542 featurePanel.add(check);
548 public void actionPerformed(ActionEvent evt)
550 for (int i = 0; i < featurePanel.getComponentCount(); i++)
552 Checkbox check = (Checkbox) featurePanel.getComponent(i);
553 check.setState(!check.getState());
555 selectionChanged(true);
558 private ItemListener groupItemListener = new ItemListener()
561 public void itemStateChanged(ItemEvent evt)
563 Checkbox source = (Checkbox) evt.getSource();
564 fr.setGroupVisibility(source.getLabel(), source.getState());
565 ap.seqPanel.seqCanvas.repaint();
566 if (ap.overviewPanel != null)
568 ap.overviewPanel.updateOverviewImage();
576 public void itemStateChanged(ItemEvent evt)
578 selectionChanged(true);
581 void selectionChanged(boolean updateOverview)
583 Component[] comps = featurePanel.getComponents();
584 int cSize = comps.length;
586 Object[][] tmp = new Object[cSize][3];
588 for (int i = 0; i < cSize; i++)
590 MyCheckbox check = (MyCheckbox) comps[i];
591 tmp[tmpSize][0] = check.type;
592 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
593 tmp[tmpSize][2] = new Boolean(check.getState());
597 Object[][] data = new Object[tmpSize][3];
598 System.arraycopy(tmp, 0, data, 0, tmpSize);
600 fr.setFeaturePriority(data);
602 ap.paintAlignment(updateOverview);
605 MyCheckbox selectedCheck;
607 boolean dragging = false;
610 public void mouseDragged(MouseEvent evt)
612 if (((Component) evt.getSource()).getParent() != featurePanel)
620 public void mouseReleased(MouseEvent evt)
622 if (((Component) evt.getSource()).getParent() != featurePanel)
627 Component comp = null;
628 Checkbox target = null;
630 int height = evt.getY() + evt.getComponent().getLocation().y;
632 if (height > featurePanel.getSize().height)
636 .getComponent(featurePanel.getComponentCount() - 1);
640 comp = featurePanel.getComponent(0);
644 comp = featurePanel.getComponentAt(evt.getX(),
645 evt.getY() + evt.getComponent().getLocation().y);
648 if (comp != null && comp instanceof Checkbox)
650 target = (Checkbox) comp;
653 if (selectedCheck != null && target != null && selectedCheck != target)
655 int targetIndex = -1;
656 for (int i = 0; i < featurePanel.getComponentCount(); i++)
658 if (target == featurePanel.getComponent(i))
665 featurePanel.remove(selectedCheck);
666 featurePanel.add(selectedCheck, targetIndex);
667 featurePanel.validate();
668 itemStateChanged(null);
672 public void setUserColour(String feature, FeatureColourI originalColour)
674 fr.setColour(feature, originalColour);
678 public void refreshTable()
680 featurePanel.removeAll();
682 ap.paintAlignment(true);
686 public void mouseEntered(MouseEvent evt)
691 public void mouseExited(MouseEvent evt)
696 public void mouseClicked(MouseEvent evt)
698 MyCheckbox check = (MyCheckbox) evt.getSource();
699 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
701 this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
704 if (check.getParent() != featurePanel)
709 if (evt.getClickCount() > 1)
711 FeatureColourI fcol = fr.getFeatureStyle(check.type);
712 if (fcol.isSimpleColour())
714 new UserDefinedColours(this, check.type, fcol.getColour());
718 new FeatureColourChooser(this, check.type);
719 // write back the current colour object to update the table
720 check.updateColor(fr.getFeatureStyle(check.type));
726 public void mouseMoved(MouseEvent evt)
731 public void adjustmentValueChanged(AdjustmentEvent evt)
733 fr.setTransparency((100 - transparency.getValue()) / 100f);
734 ap.paintAlignment(true);
737 class MyCheckbox extends Checkbox
741 public int stringWidth;
747 public void updateColor(FeatureColourI newcol)
750 if (col.isSimpleColour())
752 setBackground(col.getColour());
756 String vlabel = type;
757 if (col.isAboveThreshold())
761 else if (col.isBelowThreshold())
765 if (col.isColourByLabel())
767 setBackground(Color.white);
768 vlabel += " (by Label)";
772 setBackground(col.getMinColour());
774 this.setLabel(vlabel);
779 public MyCheckbox(String label, boolean checked, boolean haslink)
781 super(label, checked);
783 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
784 stringWidth = fm.stringWidth(label);
785 this.hasLink = haslink;
788 public MyCheckbox(String type, boolean selected, boolean b,
789 FeatureColourI featureStyle)
791 this(type, selected, b);
792 updateColor(featureStyle);
796 public void paint(Graphics g)
798 Dimension d = getSize();
801 if (col.isColourByLabel())
803 g.setColor(Color.white);
804 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
806 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
809 * // g.setFont(g.getFont().deriveFont( //
810 * AffineTransform.getScaleInstance( //
811 * width/g.getFontMetrics().stringWidth("Label"), //
812 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
817 else if (col.isGraduatedColour())
819 Color maxCol = col.getMaxColour();
821 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
828 g.drawImage(linkImage, stringWidth + 25,
829 (getSize().height - linkImage.getHeight(this)) / 2, this);
835 * Hide columns containing (or not containing) a given feature type
838 * @param columnsContaining
840 void hideFeatureColumns(final String type, boolean columnsContaining)
842 if (ap.alignFrame.avc.markColumnsContainingFeatures(columnsContaining,
845 if (ap.alignFrame.avc.markColumnsContainingFeatures(
846 !columnsContaining, false, false, type))
848 ap.alignFrame.viewport.hideSelectedColumns();
854 public void mousePressed(MouseEvent e)
856 // TODO Auto-generated method stub