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.Arrays;
61 import java.util.Enumeration;
62 import java.util.List;
64 import java.util.Vector;
66 public class FeatureSettings extends Panel implements ItemListener,
67 MouseListener, MouseMotionListener, ActionListener,
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
111 discoverAllFeatureData();
113 this.setLayout(new BorderLayout());
114 scrollPane = new ScrollPane();
115 scrollPane.add(featurePanel);
116 if (fr.getAllFeatureColours() != null
117 && fr.getAllFeatureColours().size() > 0)
119 add(scrollPane, BorderLayout.CENTER);
122 Button invert = new Button("Invert Selection");
123 invert.addActionListener(this);
125 Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
126 lowerPanel.add(invert);
128 Panel tPanel = new Panel(new BorderLayout());
130 tPanel.add(transparency, BorderLayout.CENTER);
131 tPanel.add(new Label("Transparency"), BorderLayout.EAST);
133 lowerPanel.add(tPanel, BorderLayout.SOUTH);
135 add(lowerPanel, BorderLayout.SOUTH);
137 if (groupPanel != null)
139 groupPanel.setLayout(new GridLayout(
140 (fr.getFeatureGroupsSize()) / 4 + 1, 4)); // JBPNote - this was
141 // scaled on number of
142 // visible groups. seems
144 groupPanel.validate();
146 add(groupPanel, BorderLayout.NORTH);
150 final FeatureSettings me = this;
151 frame.addWindowListener(new WindowAdapter()
154 public void windowClosing(WindowEvent e)
156 if (me.av.featureSettings == me)
158 me.av.featureSettings = null;
164 int height = featurePanel.getComponentCount() * 50 + 60;
166 height = Math.max(200, height);
167 height = Math.min(400, height);
169 jalview.bin.JalviewLite.addFrame(frame,
170 MessageManager.getString("label.sequence_feature_settings"),
175 public void paint(Graphics g)
177 g.setColor(Color.black);
178 g.drawString(MessageManager
179 .getString("label.no_features_added_to_this_alignment"), 10, 20);
180 g.drawString(MessageManager
181 .getString("label.features_can_be_added_from_searches_1"), 10,
183 g.drawString(MessageManager
184 .getString("label.features_can_be_added_from_searches_2"), 10,
188 protected void popupSort(final MyCheckbox check,
189 final Map<String, float[][]> minmax,
192 final String type = check.type;
193 final Object typeCol = fr.getFeatureStyle(type);
194 java.awt.PopupMenu men = new PopupMenu(MessageManager.formatMessage(
195 "label.settings_for_type", new String[] { type }));
196 java.awt.MenuItem scr = new MenuItem(
197 MessageManager.getString("label.sort_by_score"));
199 final FeatureSettings me = this;
200 scr.addActionListener(new ActionListener()
204 public void actionPerformed(ActionEvent e)
206 me.ap.alignFrame.avc.sortAlignmentByFeatureScore(Arrays
207 .asList(new String[] { type }));
211 MenuItem dens = new MenuItem(
212 MessageManager.getString("label.sort_by_density"));
213 dens.addActionListener(new ActionListener()
217 public void actionPerformed(ActionEvent e)
219 me.ap.alignFrame.avc.sortAlignmentByFeatureDensity(Arrays
220 .asList(new String[] { type }));
228 final float[][] typeMinMax = minmax.get(type);
230 * final java.awt.CheckboxMenuItem chb = new
231 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
232 * moment chb.setState(minmax.get(type) != null);
233 * chb.addActionListener(new ActionListener() {
235 * public void actionPerformed(ActionEvent e) {
236 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
237 * null); } else { minmax.put(type, typeMinMax); } }
241 if (typeMinMax != null && typeMinMax[0] != null)
243 // graduated colourschemes for those where minmax exists for the
244 // positional features
245 MenuItem mxcol = new MenuItem(
246 (typeCol instanceof Color) ? "Graduated Colour"
249 mxcol.addActionListener(new ActionListener()
253 public void actionPerformed(ActionEvent e)
255 if (typeCol instanceof Color)
257 new FeatureColourChooser(me, type);
258 // write back the current colour object to update the table
259 check.updateColor(fr.getFeatureStyle(type));
263 new UserDefinedColours(me, check.type,
264 ((GraduatedColor) typeCol));
272 MenuItem selectContaining = new MenuItem(
273 MessageManager.getString("label.select_columns_containing"));
274 selectContaining.addActionListener(new ActionListener()
277 public void actionPerformed(ActionEvent e)
279 me.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
283 men.add(selectContaining);
285 MenuItem selectNotContaining = new MenuItem(
286 MessageManager.getString("label.select_columns_not_containing"));
287 selectNotContaining.addActionListener(new ActionListener()
290 public void actionPerformed(ActionEvent e)
292 me.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
296 men.add(selectNotContaining);
298 MenuItem hideContaining = new MenuItem(
299 MessageManager.getString("label.hide_columns_containing"));
300 hideContaining.addActionListener(new ActionListener()
303 public void actionPerformed(ActionEvent e)
305 hideFeatureColumns(type, true);
308 men.add(hideContaining);
310 MenuItem hideNotContaining = new MenuItem(
311 MessageManager.getString("label.hide_columns_not_containing"));
312 hideNotContaining.addActionListener(new ActionListener()
315 public void actionPerformed(ActionEvent e)
317 hideFeatureColumns(type, false);
320 men.add(hideNotContaining);
322 this.featurePanel.add(men);
323 men.show(this.featurePanel, x, y);
327 public void discoverAllFeatureData()
329 if (fr.getAllFeatureColours() != null
330 && fr.getAllFeatureColours().size() > 0)
339 * rebuilds the group panel
341 public void rebuildGroups()
343 boolean rdrw = false;
344 if (groupPanel == null)
346 groupPanel = new Panel();
351 groupPanel.removeAll();
353 // TODO: JAL-964 - smoothly incorporate new group entries if panel already
354 // displayed and new groups present
355 for (String group : fr.getFeatureGroups())
357 boolean vis = fr.checkGroupVisibility(group, false);
358 Checkbox check = new MyCheckbox(group, vis, false);
359 check.addMouseListener(this);
360 check.setFont(new Font("Serif", Font.BOLD, 12));
361 check.addItemListener(groupItemListener);
362 // note - visibility seems to correlate with displayed. ???wtf ??
363 check.setVisible(vis);
364 groupPanel.add(check);
368 groupPanel.validate();
372 // This routine adds and removes checkboxes depending on
373 // Group selection states
374 void resetTable(boolean groupsChanged)
376 SequenceFeature[] tmpfeatures;
377 String group = null, type;
378 Vector<String> visibleChecks = new Vector<String>();
379 AlignmentI alignment = av.getAlignment();
380 for (int i = 0; i < alignment.getHeight(); i++)
382 if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
387 tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
389 while (index < tmpfeatures.length)
391 group = tmpfeatures[index].featureGroup;
393 if (group == null || fr.checkGroupVisibility(group, true))
395 type = tmpfeatures[index].getType();
396 if (!visibleChecks.contains(type))
398 visibleChecks.addElement(type);
406 int cSize = featurePanel.getComponentCount();
408 // This will remove any checkboxes which shouldn't be
410 for (int i = 0; i < cSize; i++)
412 comps = featurePanel.getComponents();
413 check = (MyCheckbox) comps[i];
414 if (!visibleChecks.contains(check.type))
416 featurePanel.remove(i);
422 if (fr.getRenderOrder() != null)
424 // First add the checks in the previous render order,
425 // in case the window has been closed and reopened
426 List<String> rol = fr.getRenderOrder();
427 for (int ro = rol.size() - 1; ro > -1; ro--)
429 String item = rol.get(ro);
431 if (!visibleChecks.contains(item))
436 visibleChecks.removeElement(item);
438 addCheck(false, item);
442 // now add checkboxes which should be visible,
443 // if they have not already been added
444 Enumeration<String> en = visibleChecks.elements();
446 while (en.hasMoreElements())
448 addCheck(groupsChanged, en.nextElement().toString());
451 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
453 featurePanel.validate();
455 if (scrollPane != null)
457 scrollPane.validate();
460 itemStateChanged(null);
464 * update the checklist of feature types with the given type
466 * @param groupsChanged
467 * true means if the type is not in the display list then it will be
468 * added and displayed
470 * feature type to be checked for in the list.
472 void addCheck(boolean groupsChanged, String type)
475 Component[] comps = featurePanel.getComponents();
478 for (int i = 0; i < featurePanel.getComponentCount(); i++)
480 check = (MyCheckbox) comps[i];
481 if (check.type.equals(type))
490 boolean selected = false;
491 if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
496 check = new MyCheckbox(type, selected, false,
497 fr.getFeatureStyle(type));
499 check.addMouseListener(this);
500 check.addMouseMotionListener(this);
501 check.addItemListener(this);
504 // add at beginning of stack.
505 featurePanel.add(check, 0);
509 // add at end of stack.
510 featurePanel.add(check);
516 public void actionPerformed(ActionEvent evt)
518 for (int i = 0; i < featurePanel.getComponentCount(); i++)
520 Checkbox check = (Checkbox) featurePanel.getComponent(i);
521 check.setState(!check.getState());
526 private ItemListener groupItemListener = new ItemListener()
529 public void itemStateChanged(ItemEvent evt)
531 Checkbox source = (Checkbox) evt.getSource();
532 fr.setGroupVisibility(source.getLabel(), source.getState());
533 ap.seqPanel.seqCanvas.repaint();
534 if (ap.overviewPanel != null)
536 ap.overviewPanel.updateOverviewImage();
544 public void itemStateChanged(ItemEvent evt)
549 void selectionChanged()
551 Component[] comps = featurePanel.getComponents();
552 int cSize = comps.length;
554 Object[][] tmp = new Object[cSize][3];
556 for (int i = 0; i < cSize; i++)
558 MyCheckbox check = (MyCheckbox) comps[i];
559 tmp[tmpSize][0] = check.type;
560 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
561 tmp[tmpSize][2] = new Boolean(check.getState());
565 Object[][] data = new Object[tmpSize][3];
566 System.arraycopy(tmp, 0, data, 0, tmpSize);
568 fr.setFeaturePriority(data);
570 ap.paintAlignment(true);
573 MyCheckbox selectedCheck;
575 boolean dragging = false;
578 public void mouseDragged(MouseEvent evt)
580 if (((Component) evt.getSource()).getParent() != featurePanel)
588 public void mouseReleased(MouseEvent evt)
590 if (((Component) evt.getSource()).getParent() != featurePanel)
595 Component comp = null;
596 Checkbox target = null;
598 int height = evt.getY() + evt.getComponent().getLocation().y;
600 if (height > featurePanel.getSize().height)
604 .getComponent(featurePanel.getComponentCount() - 1);
608 comp = featurePanel.getComponent(0);
612 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
613 + evt.getComponent().getLocation().y);
616 if (comp != null && comp instanceof Checkbox)
618 target = (Checkbox) comp;
621 if (selectedCheck != null && target != null && selectedCheck != target)
623 int targetIndex = -1;
624 for (int i = 0; i < featurePanel.getComponentCount(); i++)
626 if (target == featurePanel.getComponent(i))
633 featurePanel.remove(selectedCheck);
634 featurePanel.add(selectedCheck, targetIndex);
635 featurePanel.validate();
636 itemStateChanged(null);
640 public void setUserColour(String feature, Object originalColour)
642 if (originalColour instanceof Color
643 || originalColour instanceof GraduatedColor)
645 fr.setColour(feature, originalColour);
651 .getString("error.implementation_error_unsupported_feature_colour_object"));
656 public void refreshTable()
658 featurePanel.removeAll();
660 ap.paintAlignment(true);
664 public void mouseEntered(MouseEvent evt)
669 public void mouseExited(MouseEvent evt)
674 public void mouseClicked(MouseEvent evt)
676 MyCheckbox check = (MyCheckbox) evt.getSource();
677 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
679 this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
682 if (check.getParent() != featurePanel)
687 if (evt.getClickCount() > 1)
689 Object fcol = fr.getFeatureStyle(check.type);
690 if (fcol instanceof Color)
692 new UserDefinedColours(this, check.type, (Color) fcol);
696 new FeatureColourChooser(this, check.type);
697 // write back the current colour object to update the table
698 check.updateColor(fr.getFeatureStyle(check.type));
704 public void mouseMoved(MouseEvent evt)
709 public void adjustmentValueChanged(AdjustmentEvent evt)
711 fr.setTransparency((100 - transparency.getValue()) / 100f);
712 ap.seqPanel.seqCanvas.repaint();
716 class MyCheckbox extends Checkbox
720 public int stringWidth;
728 public void updateColor(Object newcol)
730 if (newcol instanceof Color)
732 col = (Color) newcol;
735 else if (newcol instanceof GraduatedColor)
737 gcol = (GraduatedColor) newcol;
744 .getString("error.invalid_colour_for_mycheckbox"));
752 String vlabel = type;
753 if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
756 + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
759 if (gcol.isColourByLabel())
761 setBackground(Color.white);
762 vlabel += " (by Label)";
766 setBackground(gcol.getMinColor());
768 this.setLabel(vlabel);
773 public MyCheckbox(String label, boolean checked, boolean haslink)
775 super(label, checked);
777 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
778 stringWidth = fm.stringWidth(label);
779 this.hasLink = haslink;
782 public MyCheckbox(String type, boolean selected, boolean b,
785 this(type, selected, b);
786 updateColor(featureStyle);
790 public void paint(Graphics g)
792 Dimension d = getSize();
795 if (gcol.isColourByLabel())
797 g.setColor(Color.white);
798 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
800 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
803 * // g.setFont(g.getFont().deriveFont( //
804 * AffineTransform.getScaleInstance( //
805 * width/g.getFontMetrics().stringWidth("Label"), //
806 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
813 Color maxCol = gcol.getMaxColor();
815 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
822 g.drawImage(linkImage, stringWidth + 25,
823 (getSize().height - linkImage.getHeight(this)) / 2, this);
829 public void mousePressed(MouseEvent e)
834 * Hide columns containing (or not containing) a given feature type
837 * @param columnsContaining
839 void hideFeatureColumns(final String type,
840 boolean columnsContaining)
842 if (ap.alignFrame.avc.markColumnsContainingFeatures(
843 columnsContaining, false, false, type))
845 if (ap.alignFrame.avc.markColumnsContainingFeatures(
846 !columnsContaining, false, false, type))
848 ap.alignFrame.viewport.hideSelectedColumns();