2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
\r
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
\r
5 * This file is part of Jalview.
\r
7 * Jalview is free software: you can redistribute it and/or
\r
8 * modify it under the terms of the GNU General Public License
\r
9 * as published by the Free Software Foundation, either version 3
\r
10 * of the License, or (at your option) any later version.
\r
12 * Jalview is distributed in the hope that it will be useful, but
\r
13 * WITHOUT ANY WARRANTY; without even the implied warranty
\r
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
\r
15 * PURPOSE. See the GNU General Public License for more details.
\r
17 * You should have received a copy of the GNU General Public License
\r
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
\r
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
\r
21 package jalview.appletgui;
\r
23 import jalview.api.FeatureSettingsControllerI;
\r
24 import jalview.bin.JalviewLite;
\r
25 import jalview.datamodel.AlignmentI;
\r
26 import jalview.datamodel.SequenceFeature;
\r
27 import jalview.schemes.AnnotationColourGradient;
\r
28 import jalview.schemes.GraduatedColor;
\r
29 import jalview.util.MessageManager;
\r
31 import java.awt.BorderLayout;
\r
32 import javax.swing.JButton;
\r
33 import javax.swing.JCheckBox;
\r
34 import java.awt.Color;
\r
35 import java.awt.Component;
\r
36 import java.awt.Dimension;
\r
37 import java.awt.Font;
\r
38 import java.awt.FontMetrics;
\r
39 import javax.swing.JFrame;
\r
40 import java.awt.Graphics;
\r
41 import java.awt.GridLayout;
\r
42 import java.awt.Image;
\r
43 import javax.swing.JLabel;
\r
44 import javax.swing.JMenuItem;
\r
45 import javax.swing.JPanel;
\r
46 import javax.swing.JPopupMenu;
\r
47 import javax.swing.JScrollPane;
\r
48 import javax.swing.JScrollBar;
\r
49 import java.awt.event.ActionEvent;
\r
50 import java.awt.event.ActionListener;
\r
51 import java.awt.event.AdjustmentEvent;
\r
52 import java.awt.event.AdjustmentListener;
\r
53 import java.awt.event.InputEvent;
\r
54 import java.awt.event.ItemEvent;
\r
55 import java.awt.event.ItemListener;
\r
56 import java.awt.event.MouseEvent;
\r
57 import java.awt.event.MouseListener;
\r
58 import java.awt.event.MouseMotionListener;
\r
59 import java.awt.event.WindowAdapter;
\r
60 import java.awt.event.WindowEvent;
\r
61 import java.util.Enumeration;
\r
62 import java.util.Hashtable;
\r
63 import java.util.List;
\r
64 import java.util.Vector;
\r
66 public class FeatureSettings extends JPanel implements ItemListener,
\r
67 MouseListener, MouseMotionListener, ActionListener,
\r
68 AdjustmentListener, FeatureSettingsControllerI
\r
80 JPanel featurePanel = new JPanel();
\r
82 JScrollPane scrollPane;
\r
86 JScrollBar transparency;
\r
88 public FeatureSettings(final AlignmentPanel ap)
\r
92 ap.av.featureSettings = this;
\r
93 fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
\r
95 transparency = new JScrollBar(JScrollBar.HORIZONTAL,
\r
96 100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
\r
98 if (fr.isTransparencyAvailable())
\r
100 transparency.addAdjustmentListener(this);
\r
104 transparency.setEnabled(false);
\r
107 java.net.URL url = getClass().getResource("/images/link.gif");
\r
110 linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
\r
113 if (av.isShowSequenceFeatures() || !fr.hasRenderOrder())
\r
115 fr.findAllFeatures(true); // was default - now true to make all visible
\r
118 discoverAllFeatureData();
\r
120 this.setLayout(new BorderLayout());
\r
121 scrollPane = new JScrollPane();
\r
122 scrollPane.add(featurePanel);
\r
123 if (fr.getAllFeatureColours()!=null && fr.getAllFeatureColours().size()>0)
\r
125 add(scrollPane, BorderLayout.CENTER);
\r
128 JButton invert = new JButton("Invert Selection");
\r
129 invert.addActionListener(this);
\r
131 JPanel lowerPanel = new JPanel(new GridLayout(2, 1, 5, 10));
\r
132 lowerPanel.add(invert);
\r
134 JPanel tPanel = new JPanel(new BorderLayout());
\r
136 if (fr.isTransparencyAvailable())
\r
138 tPanel.add(transparency, BorderLayout.CENTER);
\r
139 tPanel.add(new JLabel("Transparency"), BorderLayout.EAST);
\r
144 new JLabel("Transparency not available in this web browser"),
\r
145 BorderLayout.CENTER);
\r
148 lowerPanel.add(tPanel, BorderLayout.SOUTH);
\r
150 add(lowerPanel, BorderLayout.SOUTH);
\r
152 if (groupPanel != null)
\r
155 .setLayout(new GridLayout(
\r
156 (fr.getFeatureGroupsSize()) / 4 + 1,
\r
157 4)); // JBPNote - this was scaled on number of visible groups. seems broken
\r
158 groupPanel.validate();
\r
160 add(groupPanel, BorderLayout.NORTH);
\r
162 frame = new JFrame();
\r
164 final FeatureSettings me = this;
\r
165 frame.addWindowListener(new WindowAdapter()
\r
167 public void windowClosing(WindowEvent e)
\r
169 if (me.av.featureSettings == me)
\r
171 me.av.featureSettings = null;
\r
177 int height = featurePanel.getComponentCount() * 50 + 60;
\r
179 height = Math.max(200, height);
\r
180 height = Math.min(400, height);
\r
182 JalviewLite.addFrame(frame,
\r
183 MessageManager.getString("label.feature_settings"), width,
\r
187 public void paint(Graphics g)
\r
189 g.setColor(Color.black);
\r
190 g.drawString(MessageManager
\r
191 .getString("label.no_features_added_to_this_alignment"), 10, 20);
\r
192 g.drawString(MessageManager
\r
193 .getString("label.features_can_be_added_from_searches_1"), 10,
\r
195 g.drawString(MessageManager
\r
196 .getString("label.features_can_be_added_from_searches_2"), 10,
\r
200 protected void popupSort(final MyCheckbox check, final Hashtable minmax,
\r
203 final String type = check.type;
\r
204 final Object typeCol = fr.getFeatureStyle(type);
\r
205 JPopupMenu men = new JPopupMenu(MessageManager.formatMessage(
\r
206 "label.settings_for_type", new String[]
\r
208 JMenuItem scr = new JMenuItem(
\r
209 MessageManager.getString("label.sort_by_score"));
\r
211 final FeatureSettings me = this;
\r
212 scr.addActionListener(new ActionListener()
\r
215 public void actionPerformed(ActionEvent e)
\r
217 me.ap.alignFrame.avc.sortAlignmentByFeatureScore(new String[]
\r
222 JMenuItem dens = new JMenuItem(
\r
223 MessageManager.getString("label.sort_by_density"));
\r
224 dens.addActionListener(new ActionListener()
\r
227 public void actionPerformed(ActionEvent e)
\r
229 me.ap.alignFrame.avc.sortAlignmentByFeatureDensity(new String[]
\r
235 if (minmax != null)
\r
237 final Object typeMinMax = minmax.get(type);
\r
239 * final java.awt.CheckboxMenuItem chb = new
\r
240 * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
\r
241 * moment chb.setState(minmax.get(type) != null);
\r
242 * chb.addActionListener(new ActionListener() {
\r
244 * public void actionPerformed(ActionEvent e) {
\r
245 * chb.setState(chb.isSelected()); if (chb.isSelected()) { minmax.put(type,
\r
246 * null); } else { minmax.put(type, typeMinMax); } }
\r
248 * }); men.add(chb);
\r
250 if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
\r
252 // graduated colourschemes for those where minmax exists for the
\r
253 // positional features
\r
254 JMenuItem mxcol = new JMenuItem(
\r
255 (typeCol instanceof Color) ? "Graduated Colour"
\r
256 : "Single Colour");
\r
258 mxcol.addActionListener(new ActionListener()
\r
261 public void actionPerformed(ActionEvent e)
\r
263 if (typeCol instanceof Color)
\r
265 new FeatureColourChooser(me, type);
\r
266 // write back the current colour object to update the table
\r
267 check.updateColor(fr.getFeatureStyle(type));
\r
271 new UserDefinedColours(me, check.type,
\r
272 ((GraduatedColor) typeCol));
\r
279 this.featurePanel.add(men);
\r
280 men.show(this.featurePanel, x, y);
\r
284 public void discoverAllFeatureData()
\r
286 if (fr.getAllFeatureColours()!=null && fr.getAllFeatureColours().size()>0)
\r
295 * rebuilds the group panel
\r
297 public void rebuildGroups()
\r
299 boolean rdrw = false;
\r
300 if (groupPanel == null)
\r
302 groupPanel = new JPanel();
\r
307 groupPanel.removeAll();
\r
309 // TODO: JAL-964 - smoothly incorporate new group entries if panel already
\r
310 // displayed and new groups present
\r
311 for (String group:fr.getFeatureGroups())
\r
313 boolean vis = fr.checkGroupVisibility(group, false);
\r
314 JCheckBox check = new MyCheckbox(group, vis,
\r
315 (fr.featureLinks != null && fr.featureLinks
\r
316 .containsKey(group)));
\r
317 check.addMouseListener(this);
\r
318 check.setFont(new Font("Serif", Font.BOLD, 12));
\r
319 check.addItemListener(groupItemListener);
\r
320 // note - visibility seems to correlate with displayed. ???wtf ??
\r
321 check.setVisible(vis);
\r
322 groupPanel.add(check);
\r
326 groupPanel.validate();
\r
329 // This routine adds and removes checkboxes depending on
\r
330 // Group selection states
\r
331 void resetTable(boolean groupsChanged)
\r
333 SequenceFeature[] tmpfeatures;
\r
334 String group = null, type;
\r
335 Vector visibleChecks = new Vector();
\r
336 AlignmentI alignment = av.getAlignment();
\r
337 for (int i = 0; i < alignment.getHeight(); i++)
\r
339 if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
\r
344 tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
\r
346 while (index < tmpfeatures.length)
\r
348 group = tmpfeatures[index].featureGroup;
\r
350 if (group == null || fr.checkGroupVisibility(group, true))
\r
352 type = tmpfeatures[index].getType();
\r
353 if (!visibleChecks.contains(type))
\r
355 visibleChecks.addElement(type);
\r
363 int cSize = featurePanel.getComponentCount();
\r
365 // This will remove any checkboxes which shouldn't be
\r
367 for (int i = 0; i < cSize; i++)
\r
369 comps = featurePanel.getComponents();
\r
370 check = (MyCheckbox) comps[i];
\r
371 if (!visibleChecks.contains(check.type))
\r
373 featurePanel.remove(i);
\r
379 if (fr.getRenderOrder() != null)
\r
381 // First add the checks in the previous render order,
\r
382 // in case the window has been closed and reopened
\r
383 List<String> rol = fr.getRenderOrder();
\r
384 for (int ro = rol.size() - 1; ro > -1; ro--)
\r
386 String item = rol.get(ro);
\r
388 if (!visibleChecks.contains(item))
\r
393 visibleChecks.removeElement(item);
\r
395 addCheck(false, item);
\r
399 // now add checkboxes which should be visible,
\r
400 // if they have not already been added
\r
401 Enumeration en = visibleChecks.elements();
\r
403 while (en.hasMoreElements())
\r
405 addCheck(groupsChanged, en.nextElement().toString());
\r
408 featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(),
\r
410 featurePanel.validate();
\r
412 if (scrollPane != null)
\r
414 scrollPane.validate();
\r
417 itemStateChanged(null);
\r
421 * update the checklist of feature types with the given type
\r
423 * @param groupsChanged
\r
424 * true means if the type is not in the display list then it will be
\r
425 * added and displayed
\r
427 * feature type to be checked for in the list.
\r
429 void addCheck(boolean groupsChanged, String type)
\r
432 Component[] comps = featurePanel.getComponents();
\r
435 for (int i = 0; i < featurePanel.getComponentCount(); i++)
\r
437 check = (MyCheckbox) comps[i];
\r
438 if (check.type.equals(type))
\r
447 boolean selected = false;
\r
448 if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
\r
453 check = new MyCheckbox(
\r
456 (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
\r
457 fr.getFeatureStyle(type));
\r
459 check.addMouseListener(this);
\r
460 check.addMouseMotionListener(this);
\r
461 check.addItemListener(this);
\r
464 // add at beginning of stack.
\r
465 featurePanel.add(check, 0);
\r
469 // add at end of stack.
\r
470 featurePanel.add(check);
\r
475 public void actionPerformed(ActionEvent evt)
\r
477 for (int i = 0; i < featurePanel.getComponentCount(); i++)
\r
479 JCheckBox check = (JCheckBox) featurePanel.getComponent(i);
\r
480 check.setSelected(!check.isSelected());
\r
482 selectionChanged();
\r
485 private ItemListener groupItemListener = new ItemListener() {
\r
486 public void itemStateChanged(ItemEvent evt) {
\r
487 JCheckBox source = (JCheckBox) evt.getSource();
\r
488 fr.setGroupVisibility(source.getLabel(),
\r
489 source.isSelected());
\r
490 ap.seqPanel.seqCanvas.repaint();
\r
491 if (ap.overviewPanel != null)
\r
493 ap.overviewPanel.updateOverviewImage();
\r
499 public void itemStateChanged(ItemEvent evt)
\r
501 selectionChanged();
\r
504 void selectionChanged()
\r
506 Component[] comps = featurePanel.getComponents();
\r
507 int cSize = comps.length;
\r
509 Object[][] tmp = new Object[cSize][3];
\r
511 for (int i = 0; i < cSize; i++)
\r
513 MyCheckbox check = (MyCheckbox) comps[i];
\r
514 tmp[tmpSize][0] = check.type;
\r
515 tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
\r
516 tmp[tmpSize][2] = new Boolean(check.isSelected());
\r
520 Object[][] data = new Object[tmpSize][3];
\r
521 System.arraycopy(tmp, 0, data, 0, tmpSize);
\r
523 fr.setFeaturePriority(data);
\r
525 ap.paintAlignment(true);
\r
528 MyCheckbox selectedCheck;
\r
530 boolean dragging = false;
\r
532 public void mousePressed(MouseEvent evt)
\r
535 selectedCheck = (MyCheckbox) evt.getSource();
\r
537 if (fr.featureLinks != null
\r
538 && fr.featureLinks.containsKey(selectedCheck.type))
\r
540 if (evt.getX() > selectedCheck.stringWidth + 20)
\r
548 public void mouseDragged(MouseEvent evt)
\r
550 if (((Component) evt.getSource()).getParent() != featurePanel)
\r
557 public void mouseReleased(MouseEvent evt)
\r
559 if (((Component) evt.getSource()).getParent() != featurePanel)
\r
564 Component comp = null;
\r
565 JCheckBox target = null;
\r
567 int height = evt.getY() + evt.getComponent().getLocation().y;
\r
569 if (height > featurePanel.getSize().height)
\r
572 comp = featurePanel
\r
573 .getComponent(featurePanel.getComponentCount() - 1);
\r
575 else if (height < 0)
\r
577 comp = featurePanel.getComponent(0);
\r
581 comp = featurePanel.getComponentAt(evt.getX(), evt.getY()
\r
582 + evt.getComponent().getLocation().y);
\r
585 if (comp != null && comp instanceof JCheckBox)
\r
587 target = (JCheckBox) comp;
\r
590 if (selectedCheck != null && target != null && selectedCheck != target)
\r
592 int targetIndex = -1;
\r
593 for (int i = 0; i < featurePanel.getComponentCount(); i++)
\r
595 if (target == featurePanel.getComponent(i))
\r
602 featurePanel.remove(selectedCheck);
\r
603 featurePanel.add(selectedCheck, targetIndex);
\r
604 featurePanel.validate();
\r
605 itemStateChanged(null);
\r
609 public void setUserColour(String feature, Object originalColour)
\r
611 if (originalColour instanceof Color
\r
612 || originalColour instanceof GraduatedColor)
\r
614 fr.setColour(feature, originalColour);
\r
618 throw new Error(MessageManager.getString("error.implementation_error_unsupported_feature_colour_object"));
\r
623 public void refreshTable()
\r
625 featurePanel.removeAll();
\r
627 ap.paintAlignment(true);
\r
630 public void mouseEntered(MouseEvent evt)
\r
634 public void mouseExited(MouseEvent evt)
\r
638 public void mouseClicked(MouseEvent evt)
\r
640 MyCheckbox check = (MyCheckbox) evt.getSource();
\r
641 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
\r
643 this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
\r
645 if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
\r
647 if (evt.getX() > check.stringWidth + 20)
\r
650 String link = fr.featureLinks.get(check.type).toString();
\r
651 ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1),
\r
652 link.substring(0, link.indexOf("|")));
\r
656 if (check.getParent() != featurePanel)
\r
661 if (evt.getClickCount() > 1)
\r
663 Object fcol = fr.getFeatureStyle(check.type);
\r
664 if (fcol instanceof Color)
\r
666 new UserDefinedColours(this, check.type, (Color) fcol);
\r
670 new FeatureColourChooser(this, check.type);
\r
671 // write back the current colour object to update the table
\r
672 check.updateColor(fr.getFeatureStyle(check.type));
\r
677 public void mouseMoved(MouseEvent evt)
\r
681 public void adjustmentValueChanged(AdjustmentEvent evt)
\r
683 fr.setTransparency((100 - transparency.getValue()) / 100f);
\r
684 ap.seqPanel.seqCanvas.repaint();
\r
688 class MyCheckbox extends JCheckBox
\r
690 public String type;
\r
692 public int stringWidth;
\r
696 GraduatedColor gcol;
\r
700 public void updateColor(Object newcol)
\r
702 if (newcol instanceof Color)
\r
704 col = (Color) newcol;
\r
707 else if (newcol instanceof GraduatedColor)
\r
709 gcol = (GraduatedColor) newcol;
\r
714 throw new Error(MessageManager.getString("error.invalid_colour_for_mycheckbox"));
\r
718 setBackground(col);
\r
722 String vlabel = type;
\r
723 if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
\r
726 + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
\r
729 if (gcol.isColourByLabel())
\r
731 setBackground(Color.white);
\r
732 vlabel += " (by Label)";
\r
736 setBackground(gcol.getMinColor());
\r
738 this.setLabel(vlabel);
\r
743 public MyCheckbox(String label, boolean checked, boolean haslink)
\r
745 super(label, checked);
\r
747 FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
\r
748 stringWidth = fm.stringWidth(label);
\r
749 this.hasLink = haslink;
\r
752 public MyCheckbox(String type, boolean selected, boolean b,
\r
753 Object featureStyle)
\r
755 this(type, selected, b);
\r
756 updateColor(featureStyle);
\r
759 public void paint(Graphics g)
\r
761 Dimension d = getSize();
\r
764 if (gcol.isColourByLabel())
\r
766 g.setColor(Color.white);
\r
767 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
\r
769 * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
\r
772 * // g.setFont(g.getFont().deriveFont( //
\r
773 * AffineTransform.getScaleInstance( //
\r
774 * width/g.getFontMetrics().stringWidth("Label"), //
\r
775 * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
\r
782 Color maxCol = gcol.getMaxColor();
\r
783 g.setColor(maxCol);
\r
784 g.fillRect(d.width / 2, 0, d.width / 2, d.height);
\r
791 g.drawImage(linkImage, stringWidth + 25,
\r
792 (getSize().height - linkImage.getHeight(this)) / 2, this);
\r