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.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 transparency.addAdjustmentListener(this);
99 java.net.URL url = getClass().getResource("/images/link.gif");
102 linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
105 if (av.isShowSequenceFeatures() || !fr.hasRenderOrder())
107 fr.findAllFeatures(true); // was default - now true to make all visible
110 discoverAllFeatureData();
112 this.setLayout(new BorderLayout());
113 scrollPane = new ScrollPane();
114 scrollPane.add(featurePanel);
115 if (fr.getAllFeatureColours() != null
116 && fr.getAllFeatureColours().size() > 0)
118 add(scrollPane, BorderLayout.CENTER);
121 Button invert = new Button("Invert Selection");
122 invert.addActionListener(this);
124 Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
125 lowerPanel.add(invert);
127 Panel tPanel = new Panel(new BorderLayout());
129 tPanel.add(transparency, BorderLayout.CENTER);
130 tPanel.add(new Label("Transparency"), BorderLayout.EAST);
132 lowerPanel.add(tPanel, BorderLayout.SOUTH);
134 add(lowerPanel, BorderLayout.SOUTH);
136 if (groupPanel != null)
138 groupPanel.setLayout(new GridLayout(
139 (fr.getFeatureGroupsSize()) / 4 + 1, 4)); // JBPNote - this was
140 // scaled on number of
141 // visible groups. seems
143 groupPanel.validate();
145 add(groupPanel, BorderLayout.NORTH);
149 final FeatureSettings me = this;
150 frame.addWindowListener(new WindowAdapter()
153 public void windowClosing(WindowEvent e)
155 if (me.av.featureSettings == me)
157 me.av.featureSettings = null;
163 int height = featurePanel.getComponentCount() * 50 + 60;
165 height = Math.max(200, height);
166 height = Math.min(400, height);
168 jalview.bin.JalviewLite.addFrame(frame,
169 MessageManager.getString("label.sequence_feature_settings"),
174 public void paint(Graphics g)
176 g.setColor(Color.black);
177 g.drawString(MessageManager
178 .getString("label.no_features_added_to_this_alignment"), 10, 20);
179 g.drawString(MessageManager
180 .getString("label.features_can_be_added_from_searches_1"), 10,
182 g.drawString(MessageManager
183 .getString("label.features_can_be_added_from_searches_2"), 10,
187 protected void popupSort(final MyCheckbox check,
188 final Map<String, float[][]> minmax,
191 final String type = check.type;
192 final FeatureColourI typeCol = fr.getFeatureStyle(type);
193 PopupMenu men = new PopupMenu(MessageManager.formatMessage(
194 "label.settings_for_type", new String[] { type }));
195 java.awt.MenuItem scr = new MenuItem(
196 MessageManager.getString("label.sort_by_score"));
198 final FeatureSettings me = this;
199 scr.addActionListener(new ActionListener()
203 public void actionPerformed(ActionEvent e)
205 me.ap.alignFrame.avc.sortAlignmentByFeatureScore(Arrays
206 .asList(new String[] { type }));
210 MenuItem dens = new MenuItem(
211 MessageManager.getString("label.sort_by_density"));
212 dens.addActionListener(new ActionListener()
216 public void actionPerformed(ActionEvent e)
218 me.ap.alignFrame.avc.sortAlignmentByFeatureDensity(Arrays
219 .asList(new String[] { type }));
227 final float[][] typeMinMax = minmax.get(type);
229 * final java.awt.CheckboxMenuItem chb = new
230 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
231 * moment chb.setState(minmax.get(type) != null);
232 * chb.addActionListener(new ActionListener() {
234 * public void actionPerformed(ActionEvent e) {
235 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
236 * null); } else { minmax.put(type, typeMinMax); } }
240 if (typeMinMax != null && typeMinMax[0] != null)
242 // graduated colourschemes for those where minmax exists for the
243 // positional features
244 MenuItem mxcol = new MenuItem(
245 (typeCol.isSimpleColour()) ? "Graduated Colour"
248 mxcol.addActionListener(new ActionListener()
252 public void actionPerformed(ActionEvent e)
254 if (typeCol.isSimpleColour())
256 new FeatureColourChooser(me, type);
257 // write back the current colour object to update the table
258 check.updateColor(fr.getFeatureStyle(type));
262 new UserDefinedColours(me, check.type, typeCol);
270 MenuItem selectContaining = new MenuItem(
271 MessageManager.getString("label.select_columns_containing"));
272 selectContaining.addActionListener(new ActionListener()
275 public void actionPerformed(ActionEvent e)
277 me.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
281 men.add(selectContaining);
283 MenuItem selectNotContaining = new MenuItem(
284 MessageManager.getString("label.select_columns_not_containing"));
285 selectNotContaining.addActionListener(new ActionListener()
288 public void actionPerformed(ActionEvent e)
290 me.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
294 men.add(selectNotContaining);
296 MenuItem hideContaining = new MenuItem(
297 MessageManager.getString("label.hide_columns_containing"));
298 hideContaining.addActionListener(new ActionListener()
301 public void actionPerformed(ActionEvent e)
303 hideFeatureColumns(type, true);
306 men.add(hideContaining);
308 MenuItem hideNotContaining = new MenuItem(
309 MessageManager.getString("label.hide_columns_not_containing"));
310 hideNotContaining.addActionListener(new ActionListener()
313 public void actionPerformed(ActionEvent e)
315 hideFeatureColumns(type, false);
318 men.add(hideNotContaining);
320 this.featurePanel.add(men);
321 men.show(this.featurePanel, x, y);
325 public void discoverAllFeatureData()
327 if (fr.getAllFeatureColours() != null
328 && fr.getAllFeatureColours().size() > 0)
337 * rebuilds the group panel
339 public void rebuildGroups()
341 boolean rdrw = false;
342 if (groupPanel == null)
344 groupPanel = new Panel();
349 groupPanel.removeAll();
351 // TODO: JAL-964 - smoothly incorporate new group entries if panel already
352 // displayed and new groups present
353 for (String group : fr.getFeatureGroups())
355 boolean vis = fr.checkGroupVisibility(group, false);
356 Checkbox check = new MyCheckbox(group, vis, false);
357 check.addMouseListener(this);
358 check.setFont(new Font("Serif", Font.BOLD, 12));
359 check.addItemListener(groupItemListener);
360 // note - visibility seems to correlate with displayed. ???wtf ??
361 check.setVisible(vis);
362 groupPanel.add(check);
366 groupPanel.validate();
370 // This routine adds and removes checkboxes depending on
371 // Group selection states
372 void resetTable(boolean groupsChanged)
374 SequenceFeature[] tmpfeatures;
375 String group = null, type;
376 Vector<String> visibleChecks = new Vector<String>();
377 AlignmentI alignment = av.getAlignment();
378 for (int i = 0; i < alignment.getHeight(); i++)
380 if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
385 tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
387 while (index < tmpfeatures.length)
389 group = tmpfeatures[index].featureGroup;
391 if (group == null || fr.checkGroupVisibility(group, true))
393 type = tmpfeatures[index].getType();
394 if (!visibleChecks.contains(type))
396 visibleChecks.addElement(type);
404 int cSize = featurePanel.getComponentCount();
406 // This will remove any checkboxes which shouldn't be
408 for (int i = 0; i < cSize; i++)
410 comps = featurePanel.getComponents();
411 check = (MyCheckbox) comps[i];
412 if (!visibleChecks.contains(check.type))
414 featurePanel.remove(i);
420 if (fr.getRenderOrder() != null)
422 // First add the checks in the previous render order,
423 // in case the window has been closed and reopened
424 List<String> rol = fr.getRenderOrder();
425 for (int ro = rol.size() - 1; ro > -1; ro--)
427 String item = rol.get(ro);
429 if (!visibleChecks.contains(item))
434 visibleChecks.removeElement(item);
436 addCheck(false, item);
440 // now add checkboxes which should be visible,
441 // if they have not already been added
442 Enumeration<String> en = visibleChecks.elements();
444 while (en.hasMoreElements())
446 addCheck(groupsChanged, en.nextElement().toString());
449 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
451 featurePanel.validate();
453 if (scrollPane != null)
455 scrollPane.validate();
458 itemStateChanged(null);
462 * update the checklist of feature types with the given type
464 * @param groupsChanged
465 * true means if the type is not in the display list then it will be
466 * added and displayed
468 * feature type to be checked for in the list.
470 void addCheck(boolean groupsChanged, String type)
473 Component[] comps = featurePanel.getComponents();
476 for (int i = 0; i < featurePanel.getComponentCount(); i++)
478 check = (MyCheckbox) comps[i];
479 if (check.type.equals(type))
488 boolean selected = false;
489 if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
494 check = new MyCheckbox(type, selected, false,
495 fr.getFeatureStyle(type));
497 check.addMouseListener(this);
498 check.addMouseMotionListener(this);
499 check.addItemListener(this);
502 // add at beginning of stack.
503 featurePanel.add(check, 0);
507 // add at end of stack.
508 featurePanel.add(check);
514 public void actionPerformed(ActionEvent evt)
516 for (int i = 0; i < featurePanel.getComponentCount(); i++)
518 Checkbox check = (Checkbox) featurePanel.getComponent(i);
519 check.setState(!check.getState());
524 private ItemListener groupItemListener = new ItemListener()
527 public void itemStateChanged(ItemEvent evt)
529 Checkbox source = (Checkbox) evt.getSource();
530 fr.setGroupVisibility(source.getLabel(), source.getState());
531 ap.seqPanel.seqCanvas.repaint();
532 if (ap.overviewPanel != null)
534 ap.overviewPanel.updateOverviewImage();
542 public void itemStateChanged(ItemEvent evt)
547 void selectionChanged()
549 Component[] comps = featurePanel.getComponents();
550 int cSize = comps.length;
552 Object[][] tmp = new Object[cSize][3];
554 for (int i = 0; i < cSize; i++)
556 MyCheckbox check = (MyCheckbox) comps[i];
557 tmp[tmpSize][0] = check.type;
558 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
559 tmp[tmpSize][2] = new Boolean(check.getState());
563 Object[][] data = new Object[tmpSize][3];
564 System.arraycopy(tmp, 0, data, 0, tmpSize);
566 fr.setFeaturePriority(data);
568 ap.paintAlignment(true);
571 MyCheckbox selectedCheck;
573 boolean dragging = false;
576 public void mouseDragged(MouseEvent evt)
578 if (((Component) evt.getSource()).getParent() != featurePanel)
586 public void mouseReleased(MouseEvent evt)
588 if (((Component) evt.getSource()).getParent() != featurePanel)
593 Component comp = null;
594 Checkbox target = null;
596 int height = evt.getY() + evt.getComponent().getLocation().y;
598 if (height > featurePanel.getSize().height)
602 .getComponent(featurePanel.getComponentCount() - 1);
606 comp = featurePanel.getComponent(0);
610 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
611 + evt.getComponent().getLocation().y);
614 if (comp != null && comp instanceof Checkbox)
616 target = (Checkbox) comp;
619 if (selectedCheck != null && target != null && selectedCheck != target)
621 int targetIndex = -1;
622 for (int i = 0; i < featurePanel.getComponentCount(); i++)
624 if (target == featurePanel.getComponent(i))
631 featurePanel.remove(selectedCheck);
632 featurePanel.add(selectedCheck, targetIndex);
633 featurePanel.validate();
634 itemStateChanged(null);
638 public void setUserColour(String feature, FeatureColourI originalColour)
640 fr.setColour(feature, originalColour);
644 public void refreshTable()
646 featurePanel.removeAll();
648 ap.paintAlignment(true);
652 public void mouseEntered(MouseEvent evt)
657 public void mouseExited(MouseEvent evt)
662 public void mouseClicked(MouseEvent evt)
664 MyCheckbox check = (MyCheckbox) evt.getSource();
665 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
667 this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
670 if (check.getParent() != featurePanel)
675 if (evt.getClickCount() > 1)
677 FeatureColourI fcol = fr.getFeatureStyle(check.type);
678 if (fcol.isSimpleColour())
680 new UserDefinedColours(this, check.type, fcol.getColour());
684 new FeatureColourChooser(this, check.type);
685 // write back the current colour object to update the table
686 check.updateColor(fr.getFeatureStyle(check.type));
692 public void mouseMoved(MouseEvent evt)
697 public void adjustmentValueChanged(AdjustmentEvent evt)
699 fr.setTransparency((100 - transparency.getValue()) / 100f);
700 ap.seqPanel.seqCanvas.repaint();
704 class MyCheckbox extends Checkbox
708 public int stringWidth;
714 public void updateColor(FeatureColourI newcol)
717 if (col.isSimpleColour())
719 setBackground(col.getColour());
723 String vlabel = type;
724 if (col.isAboveThreshold())
728 else if (col.isBelowThreshold())
732 if (col.isColourByLabel())
734 setBackground(Color.white);
735 vlabel += " (by Label)";
739 setBackground(col.getMinColour());
741 this.setLabel(vlabel);
746 public MyCheckbox(String label, boolean checked, boolean haslink)
748 super(label, checked);
750 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
751 stringWidth = fm.stringWidth(label);
752 this.hasLink = haslink;
755 public MyCheckbox(String type, boolean selected, boolean b,
756 FeatureColourI featureStyle)
758 this(type, selected, b);
759 updateColor(featureStyle);
763 public void paint(Graphics g)
765 Dimension d = getSize();
768 if (col.isColourByLabel())
770 g.setColor(Color.white);
771 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
773 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
776 * // g.setFont(g.getFont().deriveFont( //
777 * AffineTransform.getScaleInstance( //
778 * width/g.getFontMetrics().stringWidth("Label"), //
779 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
784 else if (col.isGraduatedColour())
786 Color maxCol = col.getMaxColour();
788 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
795 g.drawImage(linkImage, stringWidth + 25,
796 (getSize().height - linkImage.getHeight(this)) / 2, this);
802 * Hide columns containing (or not containing) a given feature type
805 * @param columnsContaining
807 void hideFeatureColumns(final String type,
808 boolean columnsContaining)
810 if (ap.alignFrame.avc.markColumnsContainingFeatures(
811 columnsContaining, false, false, type))
813 if (ap.alignFrame.avc.markColumnsContainingFeatures(
814 !columnsContaining, false, false, type))
816 ap.alignFrame.viewport.hideSelectedColumns();
822 public void mousePressed(MouseEvent e)
824 // TODO Auto-generated method stub