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.
23 import jalview.api.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.bin.Cache;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.SequenceI;
28 import jalview.datamodel.features.FeatureAttributes;
29 import jalview.gui.Help.HelpId;
30 import jalview.io.JalviewFileChooser;
31 import jalview.io.JalviewFileView;
32 import jalview.schemabinding.version2.JalviewUserColours;
33 import jalview.schemes.FeatureColour;
34 import jalview.util.Format;
35 import jalview.util.MessageManager;
36 import jalview.util.Platform;
37 import jalview.util.QuickSort;
38 import jalview.util.ReverseListIterator;
39 import jalview.util.matcher.Condition;
40 import jalview.util.matcher.KeyedMatcher;
41 import jalview.util.matcher.KeyedMatcherI;
42 import jalview.util.matcher.KeyedMatcherSet;
43 import jalview.util.matcher.KeyedMatcherSetI;
44 import jalview.viewmodel.AlignmentViewport;
45 import jalview.ws.DasSequenceFeatureFetcher;
46 import jalview.ws.dbsources.das.api.jalviewSourceI;
48 import java.awt.BorderLayout;
49 import java.awt.Color;
50 import java.awt.Component;
51 import java.awt.Dimension;
52 import java.awt.FlowLayout;
54 import java.awt.Graphics;
55 import java.awt.GridLayout;
56 import java.awt.Rectangle;
57 import java.awt.event.ActionEvent;
58 import java.awt.event.ActionListener;
59 import java.awt.event.FocusAdapter;
60 import java.awt.event.FocusEvent;
61 import java.awt.event.ItemEvent;
62 import java.awt.event.ItemListener;
63 import java.awt.event.MouseAdapter;
64 import java.awt.event.MouseEvent;
65 import java.awt.event.MouseMotionAdapter;
66 import java.beans.PropertyChangeEvent;
67 import java.beans.PropertyChangeListener;
69 import java.io.FileInputStream;
70 import java.io.FileOutputStream;
71 import java.io.InputStreamReader;
72 import java.io.OutputStreamWriter;
73 import java.io.PrintWriter;
74 import java.util.ArrayList;
75 import java.util.Arrays;
76 import java.util.HashSet;
77 import java.util.Hashtable;
78 import java.util.Iterator;
79 import java.util.List;
82 import java.util.Vector;
84 import javax.help.HelpSetException;
85 import javax.swing.AbstractCellEditor;
86 import javax.swing.BorderFactory;
87 import javax.swing.BoxLayout;
88 import javax.swing.ButtonGroup;
89 import javax.swing.Icon;
90 import javax.swing.JButton;
91 import javax.swing.JCheckBox;
92 import javax.swing.JCheckBoxMenuItem;
93 import javax.swing.JColorChooser;
94 import javax.swing.JComboBox;
95 import javax.swing.JDialog;
96 import javax.swing.JInternalFrame;
97 import javax.swing.JLabel;
98 import javax.swing.JLayeredPane;
99 import javax.swing.JMenuItem;
100 import javax.swing.JPanel;
101 import javax.swing.JPopupMenu;
102 import javax.swing.JRadioButton;
103 import javax.swing.JScrollPane;
104 import javax.swing.JSlider;
105 import javax.swing.JTabbedPane;
106 import javax.swing.JTable;
107 import javax.swing.JTextArea;
108 import javax.swing.JTextField;
109 import javax.swing.ListSelectionModel;
110 import javax.swing.SwingConstants;
111 import javax.swing.SwingUtilities;
112 import javax.swing.event.ChangeEvent;
113 import javax.swing.event.ChangeListener;
114 import javax.swing.plaf.basic.BasicArrowButton;
115 import javax.swing.table.AbstractTableModel;
116 import javax.swing.table.TableCellEditor;
117 import javax.swing.table.TableCellRenderer;
119 public class FeatureSettings extends JPanel
120 implements FeatureSettingsControllerI
122 private static final int MIN_WIDTH = 400;
124 private static final int MIN_HEIGHT = 400;
126 private static final int MAX_TOOLTIP_LENGTH = 50;
128 DasSourceBrowser dassourceBrowser;
130 DasSequenceFeatureFetcher dasFeatureFetcher;
132 JPanel dasSettingsPane = new JPanel();
134 final FeatureRenderer fr;
136 public final AlignFrame af;
139 * 'original' fields hold settings to restore on Cancel
141 Object[][] originalData;
143 private float originalTransparency;
145 private Map<String, KeyedMatcherSetI> originalFilters;
147 final JInternalFrame frame;
149 JScrollPane scrollPane = new JScrollPane();
155 JSlider transparency = new JSlider();
158 * when true, constructor is still executing - so ignore UI events
160 protected volatile boolean inConstruction = true;
162 int selectedRow = -1;
164 JButton fetchDAS = new JButton();
166 JButton saveDAS = new JButton();
168 JButton cancelDAS = new JButton();
170 boolean resettingTable = false;
173 * true when Feature Settings are updating from feature renderer
175 private boolean handlingUpdate = false;
178 * holds {featureCount, totalExtent} for each feature type
180 Map<String, float[]> typeWidth = null;
183 * fields of the feature filters tab
185 private JPanel filtersPane;
187 private JPanel chooseFiltersPanel;
189 private JComboBox<String> filteredFeatureChoice;
191 private JRadioButton andFilters;
193 private JRadioButton orFilters;
196 * filters for the currently selected feature type
198 private List<KeyedMatcherI> filters;
200 private JTextArea filtersAsText;
207 public FeatureSettings(AlignFrame alignFrame)
209 this.af = alignFrame;
210 fr = af.getFeatureRenderer();
212 // save transparency for restore on Cancel
213 originalTransparency = fr.getTransparency();
214 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
215 transparency.setMaximum(100 - originalTransparencyAsPercent);
217 originalFilters = fr.getFeatureFilters();
222 } catch (Exception ex)
224 ex.printStackTrace();
230 public String getToolTipText(MouseEvent e)
232 if (table.columnAtPoint(e.getPoint()) == 0)
235 * Tooltip for feature name only
237 return JvSwingUtils.wrapTooltip(true, MessageManager
238 .getString("label.feature_settings_click_drag"));
243 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
244 table.setFont(new Font("Verdana", Font.PLAIN, 12));
245 table.setDefaultRenderer(Color.class, new ColorRenderer());
247 table.setDefaultEditor(Color.class, new ColorEditor(this));
249 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
250 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
251 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
253 table.addMouseListener(new MouseAdapter()
256 public void mousePressed(MouseEvent evt)
258 selectedRow = table.rowAtPoint(evt.getPoint());
259 if (evt.isPopupTrigger())
261 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
262 table.getValueAt(selectedRow, 1), fr.getMinMax(),
263 evt.getX(), evt.getY());
265 else if (evt.getClickCount() == 2)
267 boolean invertSelection = evt.isAltDown();
268 boolean toggleSelection = Platform.isControlDown(evt);
269 boolean extendSelection = evt.isShiftDown();
270 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
271 invertSelection, extendSelection, toggleSelection,
272 (String) table.getValueAt(selectedRow, 0));
276 // isPopupTrigger fires on mouseReleased on Windows
278 public void mouseReleased(MouseEvent evt)
280 selectedRow = table.rowAtPoint(evt.getPoint());
281 if (evt.isPopupTrigger())
283 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
284 table.getValueAt(selectedRow, 1), fr.getMinMax(),
285 evt.getX(), evt.getY());
290 table.addMouseMotionListener(new MouseMotionAdapter()
293 public void mouseDragged(MouseEvent evt)
295 int newRow = table.rowAtPoint(evt.getPoint());
296 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
299 * reposition 'selectedRow' to 'newRow' (the dragged to location)
300 * this could be more than one row away for a very fast drag action
301 * so just swap it with adjacent rows until we get it there
303 Object[][] data = ((FeatureTableModel) table.getModel())
305 int direction = newRow < selectedRow ? -1 : 1;
306 for (int i = selectedRow; i != newRow; i += direction)
308 Object[] temp = data[i];
309 data[i] = data[i + direction];
310 data[i + direction] = temp;
312 updateFeatureRenderer(data);
314 selectedRow = newRow;
318 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
319 // MessageManager.getString("label.feature_settings_click_drag")));
320 scrollPane.setViewportView(table);
322 dassourceBrowser = new DasSourceBrowser(this);
323 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
325 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
327 fr.findAllFeatures(true); // display everything!
330 discoverAllFeatureData();
331 final PropertyChangeListener change;
332 final FeatureSettings fs = this;
333 fr.addPropertyChangeListener(change = new PropertyChangeListener()
336 public void propertyChange(PropertyChangeEvent evt)
338 if (!fs.resettingTable && !fs.handlingUpdate)
340 fs.handlingUpdate = true;
341 fs.resetTable(null); // new groups may be added with new seuqence
342 // feature types only
343 fs.handlingUpdate = false;
349 frame = new JInternalFrame();
350 frame.setContentPane(this);
351 if (Platform.isAMac())
353 Desktop.addInternalFrame(frame,
354 MessageManager.getString("label.sequence_feature_settings"),
359 Desktop.addInternalFrame(frame,
360 MessageManager.getString("label.sequence_feature_settings"),
363 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
365 frame.addInternalFrameListener(
366 new javax.swing.event.InternalFrameAdapter()
369 public void internalFrameClosed(
370 javax.swing.event.InternalFrameEvent evt)
372 fr.removePropertyChangeListener(change);
373 dassourceBrowser.fs = null;
376 frame.setLayer(JLayeredPane.PALETTE_LAYER);
377 inConstruction = false;
380 protected void popupSort(final int selectedRow, final String type,
381 final Object typeCol, final Map<String, float[][]> minmax, int x,
384 final FeatureColourI featureColour = (FeatureColourI) typeCol;
386 JPopupMenu men = new JPopupMenu(MessageManager
387 .formatMessage("label.settings_for_param", new String[]
389 JMenuItem scr = new JMenuItem(
390 MessageManager.getString("label.sort_by_score"));
392 final FeatureSettings me = this;
393 scr.addActionListener(new ActionListener()
397 public void actionPerformed(ActionEvent e)
400 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
405 JMenuItem dens = new JMenuItem(
406 MessageManager.getString("label.sort_by_density"));
407 dens.addActionListener(new ActionListener()
411 public void actionPerformed(ActionEvent e)
414 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
422 * variable colour options include colour by label, by score,
423 * by selected attribute text, or attribute value
425 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
426 MessageManager.getString("label.variable_colour"));
427 mxcol.setSelected(!featureColour.isSimpleColour());
429 mxcol.addActionListener(new ActionListener()
431 JColorChooser colorChooser;
434 public void actionPerformed(ActionEvent e)
436 if (e.getSource() == mxcol)
438 if (featureColour.isSimpleColour())
440 FeatureColourChooser fc = new FeatureColourChooser(me.fr, type);
441 fc.addActionListener(this);
445 // bring up simple color chooser
446 colorChooser = new JColorChooser();
447 JDialog dialog = JColorChooser.createDialog(me,
448 "Select new Colour", true, // modal
449 colorChooser, this, // OK button handler
450 null); // no CANCEL button handler
451 colorChooser.setColor(featureColour.getMaxColour());
452 dialog.setVisible(true);
457 if (e.getSource() instanceof FeatureColourChooser)
459 FeatureColourChooser fc = (FeatureColourChooser) e.getSource();
460 table.setValueAt(fc.getLastColour(), selectedRow, 1);
465 // probably the color chooser!
466 table.setValueAt(new FeatureColour(colorChooser.getColor()),
469 me.updateFeatureRenderer(
470 ((FeatureTableModel) table.getModel()).getData(), false);
477 JMenuItem selCols = new JMenuItem(
478 MessageManager.getString("label.select_columns_containing"));
479 selCols.addActionListener(new ActionListener()
482 public void actionPerformed(ActionEvent arg0)
484 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
488 JMenuItem clearCols = new JMenuItem(MessageManager
489 .getString("label.select_columns_not_containing"));
490 clearCols.addActionListener(new ActionListener()
493 public void actionPerformed(ActionEvent arg0)
495 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
499 JMenuItem hideCols = new JMenuItem(
500 MessageManager.getString("label.hide_columns_containing"));
501 hideCols.addActionListener(new ActionListener()
504 public void actionPerformed(ActionEvent arg0)
506 fr.ap.alignFrame.hideFeatureColumns(type, true);
509 JMenuItem hideOtherCols = new JMenuItem(
510 MessageManager.getString("label.hide_columns_not_containing"));
511 hideOtherCols.addActionListener(new ActionListener()
514 public void actionPerformed(ActionEvent arg0)
516 fr.ap.alignFrame.hideFeatureColumns(type, false);
522 men.add(hideOtherCols);
523 men.show(table, x, y);
527 synchronized public void discoverAllFeatureData()
529 Set<String> allGroups = new HashSet<>();
530 AlignmentI alignment = af.getViewport().getAlignment();
532 for (int i = 0; i < alignment.getHeight(); i++)
534 SequenceI seq = alignment.getSequenceAt(i);
535 for (String group : seq.getFeatures().getFeatureGroups(true))
537 if (group != null && !allGroups.contains(group))
539 allGroups.add(group);
540 checkGroupState(group);
545 populateFilterableFeatures();
553 * Synchronise gui group list and check visibility of group
556 * @return true if group is visible
558 private boolean checkGroupState(String group)
560 boolean visible = fr.checkGroupVisibility(group, true);
562 for (int g = 0; g < groupPanel.getComponentCount(); g++)
564 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
566 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
571 final String grp = group;
572 final JCheckBox check = new JCheckBox(group, visible);
573 check.setFont(new Font("Serif", Font.BOLD, 12));
574 check.setToolTipText(group);
575 check.addItemListener(new ItemListener()
578 public void itemStateChanged(ItemEvent evt)
580 fr.setGroupVisibility(check.getText(), check.isSelected());
581 resetTable(new String[] { grp });
582 af.alignPanel.paintAlignment(true, true);
585 groupPanel.add(check);
589 synchronized void resetTable(String[] groupChanged)
595 resettingTable = true;
596 typeWidth = new Hashtable<>();
597 // TODO: change avWidth calculation to 'per-sequence' average and use long
600 Set<String> displayableTypes = new HashSet<>();
601 Set<String> foundGroups = new HashSet<>();
604 * determine which feature types may be visible depending on
605 * which groups are selected, and recompute average width data
607 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
610 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
613 * get the sequence's groups for positional features
614 * and keep track of which groups are visible
616 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
617 Set<String> visibleGroups = new HashSet<>();
618 for (String group : groups)
620 if (group == null || checkGroupState(group))
622 visibleGroups.add(group);
625 foundGroups.addAll(groups);
628 * get distinct feature types for visible groups
629 * record distinct visible types, and their count and total length
631 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
632 visibleGroups.toArray(new String[visibleGroups.size()]));
633 for (String type : types)
635 displayableTypes.add(type);
636 float[] avWidth = typeWidth.get(type);
639 avWidth = new float[2];
640 typeWidth.put(type, avWidth);
642 // todo this could include features with a non-visible group
643 // - do we greatly care?
644 // todo should we include non-displayable features here, and only
645 // update when features are added?
646 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
647 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
651 Object[][] data = new Object[displayableTypes.size()][3];
654 if (fr.hasRenderOrder())
658 fr.findAllFeatures(groupChanged != null); // prod to update
659 // colourschemes. but don't
661 // First add the checks in the previous render order,
662 // in case the window has been closed and reopened
664 List<String> frl = fr.getRenderOrder();
665 for (int ro = frl.size() - 1; ro > -1; ro--)
667 String type = frl.get(ro);
669 if (!displayableTypes.contains(type))
674 data[dataIndex][0] = type;
675 data[dataIndex][1] = fr.getFeatureStyle(type);
676 data[dataIndex][2] = new Boolean(
677 af.getViewport().getFeaturesDisplayed().isVisible(type));
679 displayableTypes.remove(type);
684 * process any extra features belonging only to
685 * a group which was just selected
687 while (!displayableTypes.isEmpty())
689 String type = displayableTypes.iterator().next();
690 data[dataIndex][0] = type;
692 data[dataIndex][1] = fr.getFeatureStyle(type);
693 if (data[dataIndex][1] == null)
695 // "Colour has been updated in another view!!"
696 fr.clearRenderOrder();
700 data[dataIndex][2] = new Boolean(true);
702 displayableTypes.remove(type);
705 if (originalData == null)
707 originalData = new Object[data.length][3];
708 for (int i = 0; i < data.length; i++)
710 System.arraycopy(data[i], 0, originalData[i], 0, 3);
715 updateOriginalData(data);
718 table.setModel(new FeatureTableModel(data));
719 table.getColumnModel().getColumn(0).setPreferredWidth(200);
721 groupPanel.setLayout(
722 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
723 pruneGroups(foundGroups);
724 groupPanel.validate();
726 updateFeatureRenderer(data, groupChanged != null);
727 resettingTable = false;
731 * Updates 'originalData' (used for restore on Cancel) if we detect that
732 * changes have been made outwith this dialog
734 * <li>a new feature type added (and made visible)</li>
735 * <li>a feature colour changed (in the Amend Features dialog)</li>
740 protected void updateOriginalData(Object[][] foundData)
742 // todo LinkedHashMap instead of Object[][] would be nice
744 Object[][] currentData = ((FeatureTableModel) table.getModel())
746 for (Object[] row : foundData)
748 String type = (String) row[0];
749 boolean found = false;
750 for (Object[] current : currentData)
752 if (type.equals(current[0]))
756 * currently dependent on object equality here;
757 * really need an equals method on FeatureColour
759 if (!row[1].equals(current[1]))
762 * feature colour has changed externally - update originalData
764 for (Object[] original : originalData)
766 if (type.equals(original[0]))
768 original[1] = row[1];
779 * new feature detected - add to original data (on top)
781 Object[][] newData = new Object[originalData.length + 1][3];
782 for (int i = 0; i < originalData.length; i++)
784 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
787 originalData = newData;
793 * Remove from the groups panel any checkboxes for groups that are not in the
794 * foundGroups set. This enables removing a group from the display when the
795 * last feature in that group is deleted.
799 protected void pruneGroups(Set<String> foundGroups)
801 for (int g = 0; g < groupPanel.getComponentCount(); g++)
803 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
804 if (!foundGroups.contains(checkbox.getText()))
806 groupPanel.remove(checkbox);
812 * reorder data based on the featureRenderers global priority list.
816 private void ensureOrder(Object[][] data)
818 boolean sort = false;
819 float[] order = new float[data.length];
820 for (int i = 0; i < order.length; i++)
822 order[i] = fr.getOrder(data[i][0].toString());
825 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
829 sort = sort || order[i - 1] > order[i];
834 jalview.util.QuickSort.sort(order, data);
840 JalviewFileChooser chooser = new JalviewFileChooser("fc",
841 "Sequence Feature Colours");
842 chooser.setFileView(new JalviewFileView());
843 chooser.setDialogTitle(
844 MessageManager.getString("label.load_feature_colours"));
845 chooser.setToolTipText(MessageManager.getString("action.load"));
847 int value = chooser.showOpenDialog(this);
849 if (value == JalviewFileChooser.APPROVE_OPTION)
851 File file = chooser.getSelectedFile();
855 InputStreamReader in = new InputStreamReader(
856 new FileInputStream(file), "UTF-8");
858 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
860 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
863 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
866 Color mincol = null, maxcol = null;
869 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
870 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
872 } catch (Exception e)
874 Cache.log.warn("Couldn't parse out graduated feature color.",
877 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
878 newcol.getMin(), newcol.getMax());
879 if (newcol.hasAutoScale())
881 gcol.setAutoScaled(newcol.getAutoScale());
883 if (newcol.hasColourByLabel())
885 gcol.setColourByLabel(newcol.getColourByLabel());
887 if (newcol.hasThreshold())
889 gcol.setThreshold(newcol.getThreshold());
891 if (newcol.getThreshType().length() > 0)
893 String ttyp = newcol.getThreshType();
894 if (ttyp.equalsIgnoreCase("ABOVE"))
896 gcol.setAboveThreshold(true);
898 if (ttyp.equalsIgnoreCase("BELOW"))
900 gcol.setBelowThreshold(true);
903 fr.setColour(name = newcol.getName(), gcol);
907 Color color = new Color(
908 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
909 fr.setColour(name = jucs.getColour(i).getName(),
910 new FeatureColour(color));
912 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
917 Object[][] data = ((FeatureTableModel) table.getModel())
920 updateFeatureRenderer(data, false);
923 } catch (Exception ex)
925 System.out.println("Error loading User Colour File\n" + ex);
932 JalviewFileChooser chooser = new JalviewFileChooser("fc",
933 "Sequence Feature Colours");
934 chooser.setFileView(new JalviewFileView());
935 chooser.setDialogTitle(
936 MessageManager.getString("label.save_feature_colours"));
937 chooser.setToolTipText(MessageManager.getString("action.save"));
939 int value = chooser.showSaveDialog(this);
941 if (value == JalviewFileChooser.APPROVE_OPTION)
943 String choice = chooser.getSelectedFile().getPath();
944 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
945 ucs.setSchemeName("Sequence Features");
948 PrintWriter out = new PrintWriter(new OutputStreamWriter(
949 new FileOutputStream(choice), "UTF-8"));
951 Set<String> fr_colours = fr.getAllFeatureColours();
952 Iterator<String> e = fr_colours.iterator();
953 float[] sortOrder = new float[fr_colours.size()];
954 String[] sortTypes = new String[fr_colours.size()];
958 sortTypes[i] = e.next();
959 sortOrder[i] = fr.getOrder(sortTypes[i]);
962 QuickSort.sort(sortOrder, sortTypes);
964 for (i = 0; i < sortTypes.length; i++)
966 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
967 col.setName(sortTypes[i]);
968 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
969 if (fcol.isSimpleColour())
971 col.setRGB(Format.getHexString(fcol.getColour()));
975 col.setRGB(Format.getHexString(fcol.getMaxColour()));
976 col.setMin(fcol.getMin());
977 col.setMax(fcol.getMax());
979 jalview.util.Format.getHexString(fcol.getMinColour()));
980 col.setAutoScale(fcol.isAutoScaled());
981 col.setThreshold(fcol.getThreshold());
982 col.setColourByLabel(fcol.isColourByLabel());
983 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
984 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
990 } catch (Exception ex)
992 ex.printStackTrace();
997 public void invertSelection()
999 for (int i = 0; i < table.getRowCount(); i++)
1001 Boolean value = (Boolean) table.getValueAt(i, 2);
1003 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
1007 public void orderByAvWidth()
1009 if (table == null || table.getModel() == null)
1013 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1014 float[] width = new float[data.length];
1018 for (int i = 0; i < data.length; i++)
1020 awidth = typeWidth.get(data[i][0]);
1023 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1024 // weight - but have to make per
1025 // sequence, too (awidth[2])
1026 // if (width[i]==1) // hack to distinguish single width sequences.
1038 boolean sort = false;
1039 for (int i = 0; i < width.length; i++)
1041 // awidth = (float[]) typeWidth.get(data[i][0]);
1044 width[i] = fr.getOrder(data[i][0].toString());
1047 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1052 width[i] /= max; // normalize
1053 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1057 sort = sort || width[i - 1] > width[i];
1062 jalview.util.QuickSort.sort(width, data);
1063 // update global priority order
1066 updateFeatureRenderer(data, false);
1074 frame.setClosed(true);
1075 } catch (Exception exe)
1081 public void updateFeatureRenderer(Object[][] data)
1083 updateFeatureRenderer(data, true);
1087 * Update the priority order of features; only repaint if this changed the
1088 * order of visible features
1093 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1095 if (fr.setFeaturePriority(data, visibleNew))
1097 af.alignPanel.paintAlignment(true, true);
1101 private void jbInit() throws Exception
1103 this.setLayout(new BorderLayout());
1105 JPanel settingsPane = new JPanel();
1106 settingsPane.setLayout(new BorderLayout());
1108 filtersPane = new JPanel();
1110 dasSettingsPane.setLayout(new BorderLayout());
1112 JPanel bigPanel = new JPanel();
1113 bigPanel.setLayout(new BorderLayout());
1115 groupPanel = new JPanel();
1116 bigPanel.add(groupPanel, BorderLayout.NORTH);
1118 JButton invert = new JButton(
1119 MessageManager.getString("label.invert_selection"));
1120 invert.setFont(JvSwingUtils.getLabelFont());
1121 invert.addActionListener(new ActionListener()
1124 public void actionPerformed(ActionEvent e)
1130 JButton optimizeOrder = new JButton(
1131 MessageManager.getString("label.optimise_order"));
1132 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1133 optimizeOrder.addActionListener(new ActionListener()
1136 public void actionPerformed(ActionEvent e)
1142 JButton sortByScore = new JButton(
1143 MessageManager.getString("label.seq_sort_by_score"));
1144 sortByScore.setFont(JvSwingUtils.getLabelFont());
1145 sortByScore.addActionListener(new ActionListener()
1148 public void actionPerformed(ActionEvent e)
1150 af.avc.sortAlignmentByFeatureScore(null);
1153 JButton sortByDens = new JButton(
1154 MessageManager.getString("label.sequence_sort_by_density"));
1155 sortByDens.setFont(JvSwingUtils.getLabelFont());
1156 sortByDens.addActionListener(new ActionListener()
1159 public void actionPerformed(ActionEvent e)
1161 af.avc.sortAlignmentByFeatureDensity(null);
1165 JButton help = new JButton(MessageManager.getString("action.help"));
1166 help.setFont(JvSwingUtils.getLabelFont());
1167 help.addActionListener(new ActionListener()
1170 public void actionPerformed(ActionEvent e)
1174 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1175 } catch (HelpSetException e1)
1177 e1.printStackTrace();
1181 help.setFont(JvSwingUtils.getLabelFont());
1182 help.setText(MessageManager.getString("action.help"));
1183 help.addActionListener(new ActionListener()
1186 public void actionPerformed(ActionEvent e)
1190 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1191 } catch (HelpSetException e1)
1193 e1.printStackTrace();
1198 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1199 cancel.setFont(JvSwingUtils.getLabelFont());
1200 cancel.addActionListener(new ActionListener()
1203 public void actionPerformed(ActionEvent e)
1205 fr.setTransparency(originalTransparency);
1206 fr.setFeatureFilters(originalFilters);
1207 updateFeatureRenderer(originalData);
1212 JButton ok = new JButton(MessageManager.getString("action.ok"));
1213 ok.setFont(JvSwingUtils.getLabelFont());
1214 ok.addActionListener(new ActionListener()
1217 public void actionPerformed(ActionEvent e)
1223 JButton loadColours = new JButton(
1224 MessageManager.getString("label.load_colours"));
1225 loadColours.setFont(JvSwingUtils.getLabelFont());
1226 loadColours.addActionListener(new ActionListener()
1229 public void actionPerformed(ActionEvent e)
1235 JButton saveColours = new JButton(
1236 MessageManager.getString("label.save_colours"));
1237 saveColours.setFont(JvSwingUtils.getLabelFont());
1238 saveColours.addActionListener(new ActionListener()
1241 public void actionPerformed(ActionEvent e)
1246 transparency.addChangeListener(new ChangeListener()
1249 public void stateChanged(ChangeEvent evt)
1251 if (!inConstruction)
1253 fr.setTransparency((100 - transparency.getValue()) / 100f);
1254 af.alignPanel.paintAlignment(true,true);
1259 transparency.setMaximum(70);
1260 transparency.setToolTipText(
1261 MessageManager.getString("label.transparency_tip"));
1262 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1263 fetchDAS.addActionListener(new ActionListener()
1266 public void actionPerformed(ActionEvent e)
1268 fetchDAS_actionPerformed(e);
1271 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1272 saveDAS.addActionListener(new ActionListener()
1275 public void actionPerformed(ActionEvent e)
1277 saveDAS_actionPerformed(e);
1281 JPanel dasButtonPanel = new JPanel();
1282 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1283 dasSettingsPane.setBorder(null);
1284 cancelDAS.setEnabled(false);
1285 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1286 cancelDAS.addActionListener(new ActionListener()
1289 public void actionPerformed(ActionEvent e)
1291 cancelDAS_actionPerformed(e);
1295 JTabbedPane tabbedPane = new JTabbedPane();
1296 this.add(tabbedPane, BorderLayout.CENTER);
1297 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1299 tabbedPane.addTab(MessageManager.getString("label.filters"),
1301 // tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1302 // dasSettingsPane);
1304 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1305 bigPanel.add(transPanel, BorderLayout.SOUTH);
1307 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1308 transbuttons.add(optimizeOrder);
1309 transbuttons.add(invert);
1310 transbuttons.add(sortByScore);
1311 transbuttons.add(sortByDens);
1312 transbuttons.add(help);
1313 transPanel.add(transparency);
1314 transPanel.add(transbuttons);
1316 JPanel buttonPanel = new JPanel();
1317 buttonPanel.add(ok);
1318 buttonPanel.add(cancel);
1319 buttonPanel.add(loadColours);
1320 buttonPanel.add(saveColours);
1321 bigPanel.add(scrollPane, BorderLayout.CENTER);
1322 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1323 dasButtonPanel.add(fetchDAS);
1324 dasButtonPanel.add(cancelDAS);
1325 dasButtonPanel.add(saveDAS);
1326 settingsPane.add(bigPanel, BorderLayout.CENTER);
1327 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1333 * Populates initial layout of the feature attribute filters panel
1335 protected void initFiltersTab()
1337 filters = new ArrayList<>();
1340 * choose feature type
1342 JPanel chooseTypePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1343 chooseTypePanel.setBackground(Color.white);
1344 JvSwingUtils.createItalicTitledBorder(chooseTypePanel,
1345 MessageManager.getString("label.feature_type"), true);
1346 filteredFeatureChoice = new JComboBox<>();
1347 filteredFeatureChoice.addItemListener(new ItemListener()
1350 public void itemStateChanged(ItemEvent e)
1352 refreshFiltersDisplay();
1355 chooseTypePanel.add(new JLabel(MessageManager
1356 .getString("label.feature_to_filter")));
1357 chooseTypePanel.add(filteredFeatureChoice);
1358 populateFilterableFeatures();
1361 * the panel with the filters for the selected feature type
1363 JPanel filtersPanel = new JPanel();
1364 filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
1365 filtersPanel.setBackground(Color.white);
1366 JvSwingUtils.createItalicTitledBorder(filtersPanel,
1367 MessageManager.getString("label.filters"), true);
1370 * add AND or OR radio buttons
1372 JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1373 andOrPanel.setBackground(Color.white);
1374 andFilters = new JRadioButton("And");
1375 orFilters = new JRadioButton("Or");
1376 ActionListener actionListener = new ActionListener()
1379 public void actionPerformed(ActionEvent e)
1384 andFilters.addActionListener(actionListener);
1385 orFilters.addActionListener(actionListener);
1386 ButtonGroup andOr = new ButtonGroup();
1387 andOr.add(andFilters);
1388 andOr.add(orFilters);
1389 andFilters.setSelected(true);
1390 andOrPanel.add(new JLabel(MessageManager
1391 .getString("label.join_conditions")));
1392 andOrPanel.add(andFilters);
1393 andOrPanel.add(orFilters);
1394 filtersPanel.add(andOrPanel);
1397 * panel with filters - populated by refreshFiltersDisplay
1399 chooseFiltersPanel = new JPanel();
1400 chooseFiltersPanel.setLayout(new BoxLayout(chooseFiltersPanel,
1402 filtersPanel.add(chooseFiltersPanel);
1405 * a read-only text view of the current filters
1407 JPanel showFiltersPanel = new JPanel(new BorderLayout(5, 5));
1408 showFiltersPanel.setBackground(Color.white);
1409 JvSwingUtils.createItalicTitledBorder(showFiltersPanel,
1410 MessageManager.getString("label.match_condition"), true);
1411 filtersAsText = new JTextArea();
1412 filtersAsText.setLineWrap(true);
1413 filtersAsText.setWrapStyleWord(true);
1414 showFiltersPanel.add(filtersAsText);
1416 filtersPane.setLayout(new BorderLayout());
1417 filtersPane.add(chooseTypePanel, BorderLayout.NORTH);
1418 filtersPane.add(filtersPanel, BorderLayout.CENTER);
1419 filtersPane.add(showFiltersPanel, BorderLayout.SOUTH);
1422 * update display for initial feature type selection
1424 refreshFiltersDisplay();
1428 * Adds entries to the 'choose feature to filter' drop-down choice. Only
1429 * feature types which have known attributes (so can be filtered) are
1430 * included, so recall this method to update the list (check for newly added
1433 protected void populateFilterableFeatures()
1436 * suppress action handler while updating the list
1438 ItemListener listener = filteredFeatureChoice.getItemListeners()[0];
1439 filteredFeatureChoice.removeItemListener(listener);
1441 filteredFeatureChoice.removeAllItems();
1442 ReverseListIterator<String> types = new ReverseListIterator<>(
1443 fr.getRenderOrder());
1445 boolean found = false;
1446 while (types.hasNext())
1448 String type = types.next();
1449 if (FeatureAttributes.getInstance().hasAttributes(type))
1451 filteredFeatureChoice.addItem(type);
1457 filteredFeatureChoice // todo i18n
1458 .addItem("No filterable feature attributes known");
1461 filteredFeatureChoice.addItemListener(listener);
1466 * Refreshes the display to show any filters currently configured for the
1467 * selected feature type (editable, with 'remove' option), plus one extra row
1468 * for adding a condition. This should be called on change of selected feature
1469 * type, or after a filter has been removed, added or amended.
1471 protected void refreshFiltersDisplay()
1474 * clear the panel and list of filter conditions
1476 chooseFiltersPanel.removeAll();
1480 * look up attributes known for feature type
1482 String selectedType = (String) filteredFeatureChoice.getSelectedItem();
1483 List<String> attNames = FeatureAttributes.getInstance().getAttributes(
1487 * if this feature type has filters set, load them first
1489 KeyedMatcherSetI featureFilters = fr.getFeatureFilter(selectedType);
1490 filtersAsText.setText("");
1491 if (featureFilters != null)
1493 filtersAsText.setText(featureFilters.toString());
1494 if (!featureFilters.isAnded())
1496 orFilters.setSelected(true);
1498 featureFilters.getMatchers().forEach(matcher -> filters.add(matcher));
1502 * and an empty filter for the user to populate (add)
1504 KeyedMatcherI noFilter = new KeyedMatcher("", Condition.values()[0], "");
1505 filters.add(noFilter);
1508 * render the conditions in rows, each in its own JPanel
1510 int filterIndex = 0;
1511 for (KeyedMatcherI filter : filters)
1513 String key = filter.getKey();
1514 Condition condition = filter.getMatcher()
1516 String pattern = filter.getMatcher().getPattern();
1517 JPanel row = addFilter(key, attNames, condition, pattern, filterIndex);
1518 chooseFiltersPanel.add(row);
1522 filtersPane.validate();
1523 filtersPane.repaint();
1527 * A helper method that constructs a panel with one filter condition:
1529 * <li>a drop-down list of attribute names to choose from</li>
1530 * <li>a drop-down list of conditions to choose from</li>
1531 * <li>a text field for input of a match pattern</li>
1532 * <li>optionally, a 'remove' button</li>
1534 * If attribute, condition or pattern are not null, they are set as defaults
1535 * for the input fields. The 'remove' button is added unless the pattern is
1536 * null or empty (incomplete filter condition).
1542 * @param filterIndex
1545 protected JPanel addFilter(String attribute, List<String> attNames,
1546 Condition cond, String pattern, int filterIndex)
1548 JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
1549 filterRow.setBackground(Color.white);
1552 * inputs for attribute, condition, pattern
1555 * drop-down choice of attribute, with description as a tooltip
1556 * if we can obtain it
1558 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1559 final JComboBox<String> attCombo = populateAttributesDropdown(
1560 featureType, attNames);
1561 JComboBox<Condition> condCombo = new JComboBox<>();
1562 JTextField patternField = new JTextField(8);
1565 * action handlers that validate and (if valid) apply changes
1567 ActionListener actionListener = new ActionListener()
1570 public void actionPerformed(ActionEvent e)
1572 if (attCombo.getSelectedItem() != null)
1574 if (validateFilter(patternField, condCombo))
1576 updateFilter(attCombo, condCombo, patternField, filterIndex);
1582 ItemListener itemListener = new ItemListener()
1585 public void itemStateChanged(ItemEvent e)
1587 actionListener.actionPerformed(null);
1591 if ("".equals(attribute))
1593 attCombo.setSelectedItem(null);
1597 attCombo.setSelectedItem(attribute);
1599 attCombo.addItemListener(itemListener);
1601 filterRow.add(attCombo);
1604 * drop-down choice of test condition
1606 for (Condition c : Condition.values())
1608 condCombo.addItem(c);
1612 condCombo.setSelectedItem(cond);
1614 condCombo.addItemListener(itemListener);
1615 filterRow.add(condCombo);
1618 * pattern to match against
1620 patternField.setText(pattern);
1621 patternField.addActionListener(actionListener);
1622 patternField.addFocusListener(new FocusAdapter()
1625 public void focusLost(FocusEvent e)
1627 actionListener.actionPerformed(null);
1630 filterRow.add(patternField);
1633 * add remove button if filter is populated (non-empty pattern)
1635 if (pattern != null && pattern.trim().length() > 0)
1637 // todo: gif for - button
1638 JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
1639 removeCondition.setToolTipText(MessageManager
1640 .getString("label.delete_row"));
1641 removeCondition.addActionListener(new ActionListener()
1644 public void actionPerformed(ActionEvent e)
1646 filters.remove(filterIndex);
1650 filterRow.add(removeCondition);
1657 * A helper method to build the drop-down choice of attributes for a feature.
1658 * Where metadata is available with a description for an attribute, that is
1659 * added as a tooltip.
1661 * @param featureType
1664 protected JComboBox<String> populateAttributesDropdown(
1665 String featureType, List<String> attNames)
1667 List<String> tooltips = new ArrayList<>();
1668 FeatureAttributes fa = FeatureAttributes.getInstance();
1669 for (String attName : attNames)
1671 String desc = fa.getDescription(featureType, attName);
1672 if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
1674 desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "...";
1676 tooltips.add(desc == null ? "" : desc);
1679 JComboBox<String> attCombo = JvSwingUtils.buildComboWithTooltips(
1680 attNames, tooltips);
1681 if (attNames.isEmpty())
1683 attCombo.setToolTipText(MessageManager
1684 .getString("label.no_attributes"));
1690 * Action on any change to feature filtering, namely
1692 * <li>change of selected attribute</li>
1693 * <li>change of selected condition</li>
1694 * <li>change of match pattern</li>
1695 * <li>removal of a condition</li>
1697 * The action should be to
1699 * <li>parse and validate the filters</li>
1700 * <li>if valid, update the filter text box</li>
1701 * <li>and apply the filters to the viewport</li>
1704 protected void filtersChanged()
1707 * update the filter conditions for the feature type
1709 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1710 boolean anded = andFilters.isSelected();
1711 KeyedMatcherSetI combined = new KeyedMatcherSet();
1713 for (KeyedMatcherI filter : filters)
1715 String pattern = filter.getMatcher().getPattern();
1716 if (pattern.trim().length() > 0)
1720 combined.and(filter);
1724 combined.or(filter);
1730 * save the filter conditions in the FeatureRenderer
1731 * (note this might now be an empty filter with no conditions)
1733 fr.setFeatureFilter(featureType, combined);
1735 filtersAsText.setText(combined.toString());
1737 refreshFiltersDisplay();
1739 af.alignPanel.paintAlignment(true, true);
1743 * Constructs a filter condition from the given input fields, and replaces the
1744 * condition at filterIndex with the new one
1749 * @param filterIndex
1751 protected void updateFilter(JComboBox<String> attCombo,
1752 JComboBox<Condition> condCombo, JTextField valueField,
1755 String attName = (String) attCombo.getSelectedItem();
1756 Condition cond = (Condition) condCombo.getSelectedItem();
1757 String pattern = valueField.getText();
1758 KeyedMatcherI km = new KeyedMatcher(attName, cond, pattern);
1760 filters.set(filterIndex, km);
1763 public void fetchDAS_actionPerformed(ActionEvent e)
1765 fetchDAS.setEnabled(false);
1766 cancelDAS.setEnabled(true);
1767 dassourceBrowser.setGuiEnabled(false);
1768 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1769 .getSelectedSources();
1770 doDasFeatureFetch(selectedSources, true, true);
1774 * get the features from selectedSources for all or the current selection
1776 * @param selectedSources
1777 * @param checkDbRefs
1778 * @param promptFetchDbRefs
1780 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1781 boolean checkDbRefs, boolean promptFetchDbRefs)
1783 SequenceI[] dataset, seqs;
1785 AlignmentViewport vp = af.getViewport();
1786 if (vp.getSelectionGroup() != null
1787 && vp.getSelectionGroup().getSize() > 0)
1789 iSize = vp.getSelectionGroup().getSize();
1790 dataset = new SequenceI[iSize];
1791 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1795 iSize = vp.getAlignment().getHeight();
1796 seqs = vp.getAlignment().getSequencesArray();
1799 dataset = new SequenceI[iSize];
1800 for (int i = 0; i < iSize; i++)
1802 dataset[i] = seqs[i].getDatasetSequence();
1805 cancelDAS.setEnabled(true);
1806 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1807 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1808 af.getViewport().setShowSequenceFeatures(true);
1809 af.showSeqFeatures.setSelected(true);
1813 * blocking call to initialise the das source browser
1815 public void initDasSources()
1817 dassourceBrowser.initDasSources();
1821 * examine the current list of das sources and return any matching the given
1822 * nicknames in sources
1825 * Vector of Strings to resolve to DAS source nicknames.
1826 * @return sources that are present in source list.
1828 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1830 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1834 * get currently selected das sources. ensure you have called initDasSources
1835 * before calling this.
1837 * @return vector of selected das source nicknames
1839 public Vector<jalviewSourceI> getSelectedSources()
1841 return dassourceBrowser.getSelectedSources();
1845 * properly initialise DAS fetcher and then initiate a new thread to fetch
1846 * features from the named sources (rather than any turned on by default)
1850 * if true then runs in same thread, otherwise passes to the Swing
1853 public void fetchDasFeatures(Vector<String> sources, boolean block)
1856 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1857 .resolveSourceNicknames(sources);
1858 if (resolved.size() == 0)
1860 resolved = dassourceBrowser.getSelectedSources();
1862 if (resolved.size() > 0)
1864 final List<jalviewSourceI> dassources = resolved;
1865 fetchDAS.setEnabled(false);
1866 // cancelDAS.setEnabled(true); doDasFetch does this.
1867 Runnable fetcher = new Runnable()
1873 doDasFeatureFetch(dassources, true, false);
1883 SwingUtilities.invokeLater(fetcher);
1888 public void saveDAS_actionPerformed(ActionEvent e)
1891 .saveProperties(jalview.bin.Cache.applicationProperties);
1894 public void complete()
1896 fetchDAS.setEnabled(true);
1897 cancelDAS.setEnabled(false);
1898 dassourceBrowser.setGuiEnabled(true);
1902 public void cancelDAS_actionPerformed(ActionEvent e)
1904 if (dasFeatureFetcher != null)
1906 dasFeatureFetcher.cancel();
1911 public void noDasSourceActive()
1914 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1915 MessageManager.getString("label.no_das_sources_selected_warn"),
1916 MessageManager.getString("label.no_das_sources_selected_title"),
1917 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1921 * Answers true unless a numeric condition has been selected with a
1922 * non-numeric value. Sets the value field to RED with a tooltip if in error.
1924 * If the pattern entered is empty, this method returns false, but does not
1925 * mark the field as invalid. This supports selecting an attribute for a new
1926 * condition before a match pattern has been entered.
1931 protected boolean validateFilter(JTextField value,
1932 JComboBox<Condition> condCombo)
1934 if (value == null || condCombo == null)
1936 return true; // fields not populated
1939 Condition cond = (Condition) condCombo.getSelectedItem();
1940 value.setBackground(Color.white);
1941 value.setToolTipText("");
1942 String v1 = value.getText().trim();
1943 if (v1.length() == 0)
1948 if (cond.isNumeric())
1953 } catch (NumberFormatException e)
1955 value.setBackground(Color.red);
1956 value.setToolTipText(MessageManager
1957 .getString("label.numeric_required"));
1965 // ///////////////////////////////////////////////////////////////////////
1966 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1967 // ///////////////////////////////////////////////////////////////////////
1968 class FeatureTableModel extends AbstractTableModel
1970 FeatureTableModel(Object[][] data)
1975 private String[] columnNames = {
1976 MessageManager.getString("label.feature_type"),
1977 MessageManager.getString("action.colour"),
1978 MessageManager.getString("label.display") };
1980 private Object[][] data;
1982 public Object[][] getData()
1987 public void setData(Object[][] data)
1993 public int getColumnCount()
1995 return columnNames.length;
1998 public Object[] getRow(int row)
2004 public int getRowCount()
2010 public String getColumnName(int col)
2012 return columnNames[col];
2016 public Object getValueAt(int row, int col)
2018 return data[row][col];
2022 public Class getColumnClass(int c)
2024 return getValueAt(0, c).getClass();
2028 public boolean isCellEditable(int row, int col)
2030 return col == 0 ? false : true;
2034 public void setValueAt(Object value, int row, int col)
2036 data[row][col] = value;
2037 fireTableCellUpdated(row, col);
2038 updateFeatureRenderer(data);
2043 class ColorRenderer extends JLabel implements TableCellRenderer
2045 javax.swing.border.Border unselectedBorder = null;
2047 javax.swing.border.Border selectedBorder = null;
2049 final String baseTT = "Click to edit, right/apple click for menu.";
2051 public ColorRenderer()
2053 setOpaque(true); // MUST do this for background to show up.
2054 setHorizontalTextPosition(SwingConstants.CENTER);
2055 setVerticalTextPosition(SwingConstants.CENTER);
2059 public Component getTableCellRendererComponent(JTable tbl, Object color,
2060 boolean isSelected, boolean hasFocus, int row, int column)
2062 FeatureColourI cellColour = (FeatureColourI) color;
2064 setToolTipText(baseTT);
2065 setBackground(tbl.getBackground());
2066 if (!cellColour.isSimpleColour())
2068 Rectangle cr = tbl.getCellRect(row, column, false);
2069 FeatureSettings.renderGraduatedColor(this, cellColour,
2070 (int) cr.getWidth(), (int) cr.getHeight());
2076 setBackground(cellColour.getColour());
2080 if (selectedBorder == null)
2082 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2083 tbl.getSelectionBackground());
2085 setBorder(selectedBorder);
2089 if (unselectedBorder == null)
2091 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2092 tbl.getBackground());
2094 setBorder(unselectedBorder);
2102 * update comp using rendering settings from gcol
2107 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
2109 int w = comp.getWidth(), h = comp.getHeight();
2112 w = (int) comp.getPreferredSize().getWidth();
2113 h = (int) comp.getPreferredSize().getHeight();
2120 renderGraduatedColor(comp, gcol, w, h);
2123 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
2126 boolean thr = false;
2127 StringBuilder tt = new StringBuilder();
2128 StringBuilder tx = new StringBuilder();
2130 if (gcol.isColourByAttribute())
2132 tx.append(gcol.getAttributeName());
2134 else if (!gcol.isColourByLabel())
2136 tx.append(MessageManager.getString("label.score"));
2139 if (gcol.isAboveThreshold())
2143 tt.append("Thresholded (Above ").append(gcol.getThreshold())
2146 if (gcol.isBelowThreshold())
2150 tt.append("Thresholded (Below ").append(gcol.getThreshold())
2153 if (gcol.isColourByLabel())
2155 tt.append("Coloured by label text. ").append(tt);
2160 if (!gcol.isColourByAttribute())
2168 Color newColor = gcol.getMaxColour();
2169 comp.setBackground(newColor);
2170 // System.err.println("Width is " + w / 2);
2171 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
2172 comp.setIcon(ficon);
2173 // tt+="RGB value: Max (" + newColor.getRed() + ", "
2174 // + newColor.getGreen() + ", " + newColor.getBlue()
2175 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
2176 // + ", " + minCol.getBlue() + ")");
2178 comp.setHorizontalAlignment(SwingConstants.CENTER);
2179 comp.setText(tx.toString());
2180 if (tt.length() > 0)
2182 if (comp.getToolTipText() == null)
2184 comp.setToolTipText(tt.toString());
2188 comp.setToolTipText(tt.append(" ").append(comp.getToolTipText())
2195 class FeatureIcon implements Icon
2197 FeatureColourI gcol;
2201 boolean midspace = false;
2203 int width = 50, height = 20;
2205 int s1, e1; // start and end of midpoint band for thresholded symbol
2207 Color mpcolour = Color.white;
2209 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2229 public int getIconWidth()
2235 public int getIconHeight()
2241 public void paintIcon(Component c, Graphics g, int x, int y)
2244 if (gcol.isColourByLabel())
2247 g.fillRect(0, 0, width, height);
2248 // need an icon here.
2249 g.setColor(gcol.getMaxColour());
2251 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2253 // g.setFont(g.getFont().deriveFont(
2254 // AffineTransform.getScaleInstance(
2255 // width/g.getFontMetrics().stringWidth("Label"),
2256 // height/g.getFontMetrics().getHeight())));
2258 g.drawString(MessageManager.getString("label.label"), 0, 0);
2263 Color minCol = gcol.getMinColour();
2265 g.fillRect(0, 0, s1, height);
2268 g.setColor(Color.white);
2269 g.fillRect(s1, 0, e1 - s1, height);
2271 g.setColor(gcol.getMaxColour());
2272 g.fillRect(0, e1, width - e1, height);
2277 class ColorEditor extends AbstractCellEditor
2278 implements TableCellEditor, ActionListener
2282 FeatureColourI currentColor;
2284 FeatureColourChooser chooser;
2290 JColorChooser colorChooser;
2294 protected static final String EDIT = "edit";
2296 int selectedRow = 0;
2298 public ColorEditor(FeatureSettings me)
2301 // Set up the editor (from the table's point of view),
2302 // which is a button.
2303 // This button brings up the color chooser dialog,
2304 // which is the editor from the user's point of view.
2305 button = new JButton();
2306 button.setActionCommand(EDIT);
2307 button.addActionListener(this);
2308 button.setBorderPainted(false);
2309 // Set up the dialog that the button brings up.
2310 colorChooser = new JColorChooser();
2311 dialog = JColorChooser.createDialog(button,
2312 MessageManager.getString("label.select_new_colour"), true, // modal
2313 colorChooser, this, // OK button handler
2314 null); // no CANCEL button handler
2318 * Handles events from the editor button and from the dialog's OK button.
2321 public void actionPerformed(ActionEvent e)
2324 if (EDIT.equals(e.getActionCommand()))
2326 // The user has clicked the cell, so
2327 // bring up the dialog.
2328 if (currentColor.isSimpleColour())
2330 // bring up simple color chooser
2331 button.setBackground(currentColor.getColour());
2332 colorChooser.setColor(currentColor.getColour());
2333 dialog.setVisible(true);
2337 // bring up graduated chooser.
2338 chooser = new FeatureColourChooser(me.fr, type);
2339 chooser.setRequestFocusEnabled(true);
2340 chooser.requestFocus();
2341 chooser.addActionListener(this);
2343 // Make the renderer reappear.
2344 fireEditingStopped();
2348 { // User pressed dialog's "OK" button.
2349 if (currentColor.isSimpleColour())
2351 currentColor = new FeatureColour(colorChooser.getColor());
2355 currentColor = chooser.getLastColour();
2357 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
2358 fireEditingStopped();
2359 me.table.validate();
2363 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2365 public Object getCellEditorValue()
2367 return currentColor;
2370 // Implement the one method defined by TableCellEditor.
2372 public Component getTableCellEditorComponent(JTable table, Object value,
2373 boolean isSelected, int row, int column)
2375 currentColor = (FeatureColourI) value;
2376 this.selectedRow = row;
2377 type = me.table.getValueAt(row, 0).toString();
2378 button.setOpaque(true);
2379 button.setBackground(me.getBackground());
2380 if (!currentColor.isSimpleColour())
2382 JLabel btn = new JLabel();
2383 btn.setSize(button.getSize());
2384 FeatureSettings.renderGraduatedColor(btn, currentColor);
2385 button.setBackground(btn.getBackground());
2386 button.setIcon(btn.getIcon());
2387 button.setText(btn.getText());
2392 button.setIcon(null);
2393 button.setBackground(currentColor.getColour());