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 chooseTypePanel.setBorder(BorderFactory
1345 .createTitledBorder(MessageManager
1346 .getString("label.feature_type")));
1347 filteredFeatureChoice = new JComboBox<>();
1348 filteredFeatureChoice.addItemListener(new ItemListener()
1351 public void itemStateChanged(ItemEvent e)
1353 refreshFiltersDisplay();
1356 chooseTypePanel.add(new JLabel(MessageManager
1357 .getString("label.feature_to_filter")));
1358 chooseTypePanel.add(filteredFeatureChoice);
1359 populateFilterableFeatures();
1362 * the panel with the filters for the selected feature type
1364 JPanel filtersPanel = new JPanel();
1365 filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
1366 filtersPanel.setBackground(Color.white);
1367 filtersPanel.setBorder(BorderFactory
1368 .createTitledBorder(MessageManager.getString("label.filters")));
1371 * add AND or OR radio buttons
1373 JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1374 andOrPanel.setBackground(Color.white);
1375 andFilters = new JRadioButton("And");
1376 orFilters = new JRadioButton("Or");
1377 ActionListener actionListener = new ActionListener()
1380 public void actionPerformed(ActionEvent e)
1385 andFilters.addActionListener(actionListener);
1386 orFilters.addActionListener(actionListener);
1387 ButtonGroup andOr = new ButtonGroup();
1388 andOr.add(andFilters);
1389 andOr.add(orFilters);
1390 andFilters.setSelected(true);
1391 andOrPanel.add(new JLabel(MessageManager
1392 .getString("label.join_conditions")));
1393 andOrPanel.add(andFilters);
1394 andOrPanel.add(orFilters);
1395 filtersPanel.add(andOrPanel);
1398 * panel with filters - populated by refreshFiltersDisplay
1400 chooseFiltersPanel = new JPanel();
1401 chooseFiltersPanel.setLayout(new BoxLayout(chooseFiltersPanel,
1403 filtersPanel.add(chooseFiltersPanel);
1406 * a read-only text view of the current filters
1408 JPanel showFiltersPanel = new JPanel(new BorderLayout(5, 5));
1409 showFiltersPanel.setBackground(Color.white);
1410 showFiltersPanel.setBorder(BorderFactory
1411 .createTitledBorder(MessageManager
1412 .getString("label.match_condition")));
1413 filtersAsText = new JTextArea();
1414 filtersAsText.setLineWrap(true);
1415 filtersAsText.setWrapStyleWord(true);
1416 showFiltersPanel.add(filtersAsText);
1418 filtersPane.setLayout(new BorderLayout());
1419 filtersPane.add(chooseTypePanel, BorderLayout.NORTH);
1420 filtersPane.add(filtersPanel, BorderLayout.CENTER);
1421 filtersPane.add(showFiltersPanel, BorderLayout.SOUTH);
1424 * update display for initial feature type selection
1426 refreshFiltersDisplay();
1430 * Adds entries to the 'choose feature to filter' drop-down choice. Only
1431 * feature types which have known attributes (so can be filtered) are
1432 * included, so recall this method to update the list (check for newly added
1435 protected void populateFilterableFeatures()
1438 * suppress action handler while updating the list
1440 ItemListener listener = filteredFeatureChoice.getItemListeners()[0];
1441 filteredFeatureChoice.removeItemListener(listener);
1443 filteredFeatureChoice.removeAllItems();
1444 ReverseListIterator<String> types = new ReverseListIterator<>(
1445 fr.getRenderOrder());
1447 boolean found = false;
1448 while (types.hasNext())
1450 String type = types.next();
1451 if (FeatureAttributes.getInstance().hasAttributes(type))
1453 filteredFeatureChoice.addItem(type);
1459 filteredFeatureChoice // todo i18n
1460 .addItem("No filterable feature attributes known");
1463 filteredFeatureChoice.addItemListener(listener);
1468 * Refreshes the display to show any filters currently configured for the
1469 * selected feature type (editable, with 'remove' option), plus one extra row
1470 * for adding a condition. This should be called on change of selected feature
1471 * type, or after a filter has been removed, added or amended.
1473 protected void refreshFiltersDisplay()
1476 * clear the panel and list of filter conditions
1478 chooseFiltersPanel.removeAll();
1482 * look up attributes known for feature type
1484 String selectedType = (String) filteredFeatureChoice.getSelectedItem();
1485 List<String> attNames = FeatureAttributes.getInstance().getAttributes(
1489 * if this feature type has filters set, load them first
1491 KeyedMatcherSetI featureFilters = fr.getFeatureFilter(selectedType);
1492 filtersAsText.setText("");
1493 if (featureFilters != null)
1495 filtersAsText.setText(featureFilters.toString());
1496 if (!featureFilters.isAnded())
1498 orFilters.setSelected(true);
1500 featureFilters.getMatchers().forEach(matcher -> filters.add(matcher));
1504 * and an empty filter for the user to populate (add)
1506 KeyedMatcherI noFilter = new KeyedMatcher("", Condition.values()[0], "");
1507 filters.add(noFilter);
1510 * render the conditions in rows, each in its own JPanel
1512 int filterIndex = 0;
1513 for (KeyedMatcherI filter : filters)
1515 String key = filter.getKey();
1516 Condition condition = filter.getMatcher()
1518 String pattern = filter.getMatcher().getPattern();
1519 JPanel row = addFilter(key, attNames, condition, pattern, filterIndex);
1520 chooseFiltersPanel.add(row);
1524 filtersPane.validate();
1525 filtersPane.repaint();
1529 * A helper method that constructs a panel with one filter condition:
1531 * <li>a drop-down list of attribute names to choose from</li>
1532 * <li>a drop-down list of conditions to choose from</li>
1533 * <li>a text field for input of a match pattern</li>
1534 * <li>optionally, a 'remove' button</li>
1536 * If attribute, condition or pattern are not null, they are set as defaults
1537 * for the input fields. The 'remove' button is added unless the pattern is
1538 * null or empty (incomplete filter condition).
1544 * @param filterIndex
1547 protected JPanel addFilter(String attribute, List<String> attNames,
1548 Condition cond, String pattern, int filterIndex)
1550 JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
1551 filterRow.setBackground(Color.white);
1554 * inputs for attribute, condition, pattern
1557 * drop-down choice of attribute, with description as a tooltip
1558 * if we can obtain it
1560 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1561 final JComboBox<String> attCombo = populateAttributesDropdown(
1562 featureType, attNames);
1563 JComboBox<Condition> condCombo = new JComboBox<>();
1564 JTextField patternField = new JTextField(8);
1567 * action handlers that validate and (if valid) apply changes
1569 ActionListener actionListener = new ActionListener()
1572 public void actionPerformed(ActionEvent e)
1574 if (attCombo.getSelectedItem() != null)
1576 if (validateFilter(patternField, condCombo))
1578 updateFilter(attCombo, condCombo, patternField, filterIndex);
1584 ItemListener itemListener = new ItemListener()
1587 public void itemStateChanged(ItemEvent e)
1589 actionListener.actionPerformed(null);
1593 if ("".equals(attribute))
1595 attCombo.setSelectedItem(null);
1599 attCombo.setSelectedItem(attribute);
1601 attCombo.addItemListener(itemListener);
1603 filterRow.add(attCombo);
1606 * drop-down choice of test condition
1608 for (Condition c : Condition.values())
1610 condCombo.addItem(c);
1614 condCombo.setSelectedItem(cond);
1616 condCombo.addItemListener(itemListener);
1617 filterRow.add(condCombo);
1620 * pattern to match against
1622 patternField.setText(pattern);
1623 patternField.addActionListener(actionListener);
1624 patternField.addFocusListener(new FocusAdapter()
1627 public void focusLost(FocusEvent e)
1629 actionListener.actionPerformed(null);
1632 filterRow.add(patternField);
1635 * add remove button if filter is populated (non-empty pattern)
1637 if (pattern != null && pattern.trim().length() > 0)
1639 // todo: gif for - button
1640 JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
1641 removeCondition.setToolTipText(MessageManager
1642 .getString("label.delete_row"));
1643 removeCondition.addActionListener(new ActionListener()
1646 public void actionPerformed(ActionEvent e)
1648 filters.remove(filterIndex);
1652 filterRow.add(removeCondition);
1659 * A helper method to build the drop-down choice of attributes for a feature.
1660 * Where metadata is available with a description for an attribute, that is
1661 * added as a tooltip.
1663 * @param featureType
1666 protected JComboBox<String> populateAttributesDropdown(
1667 String featureType, List<String> attNames)
1669 List<String> tooltips = new ArrayList<>();
1670 FeatureAttributes fa = FeatureAttributes.getInstance();
1671 for (String attName : attNames)
1673 String desc = fa.getDescription(featureType, attName);
1674 if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
1676 desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "...";
1678 tooltips.add(desc == null ? "" : desc);
1681 JComboBox<String> attCombo = JvSwingUtils.buildComboWithTooltips(
1682 attNames, tooltips);
1683 if (attNames.isEmpty())
1685 attCombo.setToolTipText(MessageManager
1686 .getString("label.no_attributes"));
1692 * Action on any change to feature filtering, namely
1694 * <li>change of selected attribute</li>
1695 * <li>change of selected condition</li>
1696 * <li>change of match pattern</li>
1697 * <li>removal of a condition</li>
1699 * The action should be to
1701 * <li>parse and validate the filters</li>
1702 * <li>if valid, update the filter text box</li>
1703 * <li>and apply the filters to the viewport</li>
1706 protected void filtersChanged()
1709 * update the filter conditions for the feature type
1711 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1712 boolean anded = andFilters.isSelected();
1713 KeyedMatcherSetI combined = new KeyedMatcherSet();
1715 for (KeyedMatcherI filter : filters)
1717 String pattern = filter.getMatcher().getPattern();
1718 if (pattern.trim().length() > 0)
1722 combined.and(filter);
1726 combined.or(filter);
1732 * save the filter conditions in the FeatureRenderer
1733 * (note this might now be an empty filter with no conditions)
1735 fr.setFeatureFilter(featureType, combined);
1737 filtersAsText.setText(combined.toString());
1739 refreshFiltersDisplay();
1741 af.alignPanel.paintAlignment(true, true);
1745 * Constructs a filter condition from the given input fields, and replaces the
1746 * condition at filterIndex with the new one
1751 * @param filterIndex
1753 protected void updateFilter(JComboBox<String> attCombo,
1754 JComboBox<Condition> condCombo, JTextField valueField,
1757 String attName = (String) attCombo.getSelectedItem();
1758 Condition cond = (Condition) condCombo.getSelectedItem();
1759 String pattern = valueField.getText();
1760 KeyedMatcherI km = new KeyedMatcher(attName, cond, pattern);
1762 filters.set(filterIndex, km);
1765 public void fetchDAS_actionPerformed(ActionEvent e)
1767 fetchDAS.setEnabled(false);
1768 cancelDAS.setEnabled(true);
1769 dassourceBrowser.setGuiEnabled(false);
1770 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1771 .getSelectedSources();
1772 doDasFeatureFetch(selectedSources, true, true);
1776 * get the features from selectedSources for all or the current selection
1778 * @param selectedSources
1779 * @param checkDbRefs
1780 * @param promptFetchDbRefs
1782 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1783 boolean checkDbRefs, boolean promptFetchDbRefs)
1785 SequenceI[] dataset, seqs;
1787 AlignmentViewport vp = af.getViewport();
1788 if (vp.getSelectionGroup() != null
1789 && vp.getSelectionGroup().getSize() > 0)
1791 iSize = vp.getSelectionGroup().getSize();
1792 dataset = new SequenceI[iSize];
1793 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1797 iSize = vp.getAlignment().getHeight();
1798 seqs = vp.getAlignment().getSequencesArray();
1801 dataset = new SequenceI[iSize];
1802 for (int i = 0; i < iSize; i++)
1804 dataset[i] = seqs[i].getDatasetSequence();
1807 cancelDAS.setEnabled(true);
1808 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1809 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1810 af.getViewport().setShowSequenceFeatures(true);
1811 af.showSeqFeatures.setSelected(true);
1815 * blocking call to initialise the das source browser
1817 public void initDasSources()
1819 dassourceBrowser.initDasSources();
1823 * examine the current list of das sources and return any matching the given
1824 * nicknames in sources
1827 * Vector of Strings to resolve to DAS source nicknames.
1828 * @return sources that are present in source list.
1830 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1832 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1836 * get currently selected das sources. ensure you have called initDasSources
1837 * before calling this.
1839 * @return vector of selected das source nicknames
1841 public Vector<jalviewSourceI> getSelectedSources()
1843 return dassourceBrowser.getSelectedSources();
1847 * properly initialise DAS fetcher and then initiate a new thread to fetch
1848 * features from the named sources (rather than any turned on by default)
1852 * if true then runs in same thread, otherwise passes to the Swing
1855 public void fetchDasFeatures(Vector<String> sources, boolean block)
1858 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1859 .resolveSourceNicknames(sources);
1860 if (resolved.size() == 0)
1862 resolved = dassourceBrowser.getSelectedSources();
1864 if (resolved.size() > 0)
1866 final List<jalviewSourceI> dassources = resolved;
1867 fetchDAS.setEnabled(false);
1868 // cancelDAS.setEnabled(true); doDasFetch does this.
1869 Runnable fetcher = new Runnable()
1875 doDasFeatureFetch(dassources, true, false);
1885 SwingUtilities.invokeLater(fetcher);
1890 public void saveDAS_actionPerformed(ActionEvent e)
1893 .saveProperties(jalview.bin.Cache.applicationProperties);
1896 public void complete()
1898 fetchDAS.setEnabled(true);
1899 cancelDAS.setEnabled(false);
1900 dassourceBrowser.setGuiEnabled(true);
1904 public void cancelDAS_actionPerformed(ActionEvent e)
1906 if (dasFeatureFetcher != null)
1908 dasFeatureFetcher.cancel();
1913 public void noDasSourceActive()
1916 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1917 MessageManager.getString("label.no_das_sources_selected_warn"),
1918 MessageManager.getString("label.no_das_sources_selected_title"),
1919 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1923 * Answers true unless a numeric condition has been selected with a
1924 * non-numeric value. Sets the value field to RED with a tooltip if in error.
1926 * If the pattern entered is empty, this method returns false, but does not
1927 * mark the field as invalid. This supports selecting an attribute for a new
1928 * condition before a match pattern has been entered.
1933 protected boolean validateFilter(JTextField value,
1934 JComboBox<Condition> condCombo)
1936 if (value == null || condCombo == null)
1938 return true; // fields not populated
1941 Condition cond = (Condition) condCombo.getSelectedItem();
1942 value.setBackground(Color.white);
1943 value.setToolTipText("");
1944 String v1 = value.getText().trim();
1945 if (v1.length() == 0)
1950 if (cond.isNumeric())
1955 } catch (NumberFormatException e)
1957 value.setBackground(Color.red);
1958 value.setToolTipText(MessageManager
1959 .getString("label.numeric_required"));
1967 // ///////////////////////////////////////////////////////////////////////
1968 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1969 // ///////////////////////////////////////////////////////////////////////
1970 class FeatureTableModel extends AbstractTableModel
1972 FeatureTableModel(Object[][] data)
1977 private String[] columnNames = {
1978 MessageManager.getString("label.feature_type"),
1979 MessageManager.getString("action.colour"),
1980 MessageManager.getString("label.display") };
1982 private Object[][] data;
1984 public Object[][] getData()
1989 public void setData(Object[][] data)
1995 public int getColumnCount()
1997 return columnNames.length;
2000 public Object[] getRow(int row)
2006 public int getRowCount()
2012 public String getColumnName(int col)
2014 return columnNames[col];
2018 public Object getValueAt(int row, int col)
2020 return data[row][col];
2024 public Class getColumnClass(int c)
2026 return getValueAt(0, c).getClass();
2030 public boolean isCellEditable(int row, int col)
2032 return col == 0 ? false : true;
2036 public void setValueAt(Object value, int row, int col)
2038 data[row][col] = value;
2039 fireTableCellUpdated(row, col);
2040 updateFeatureRenderer(data);
2045 class ColorRenderer extends JLabel implements TableCellRenderer
2047 javax.swing.border.Border unselectedBorder = null;
2049 javax.swing.border.Border selectedBorder = null;
2051 final String baseTT = "Click to edit, right/apple click for menu.";
2053 public ColorRenderer()
2055 setOpaque(true); // MUST do this for background to show up.
2056 setHorizontalTextPosition(SwingConstants.CENTER);
2057 setVerticalTextPosition(SwingConstants.CENTER);
2061 public Component getTableCellRendererComponent(JTable tbl, Object color,
2062 boolean isSelected, boolean hasFocus, int row, int column)
2064 FeatureColourI cellColour = (FeatureColourI) color;
2066 setToolTipText(baseTT);
2067 setBackground(tbl.getBackground());
2068 if (!cellColour.isSimpleColour())
2070 Rectangle cr = tbl.getCellRect(row, column, false);
2071 FeatureSettings.renderGraduatedColor(this, cellColour,
2072 (int) cr.getWidth(), (int) cr.getHeight());
2078 setBackground(cellColour.getColour());
2082 if (selectedBorder == null)
2084 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2085 tbl.getSelectionBackground());
2087 setBorder(selectedBorder);
2091 if (unselectedBorder == null)
2093 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2094 tbl.getBackground());
2096 setBorder(unselectedBorder);
2104 * update comp using rendering settings from gcol
2109 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
2111 int w = comp.getWidth(), h = comp.getHeight();
2114 w = (int) comp.getPreferredSize().getWidth();
2115 h = (int) comp.getPreferredSize().getHeight();
2122 renderGraduatedColor(comp, gcol, w, h);
2125 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
2128 boolean thr = false;
2129 StringBuilder tt = new StringBuilder();
2130 StringBuilder tx = new StringBuilder();
2132 if (gcol.isColourByAttribute())
2134 tx.append(gcol.getAttributeName());
2136 else if (!gcol.isColourByLabel())
2138 tx.append(MessageManager.getString("label.score"));
2141 if (gcol.isAboveThreshold())
2145 tt.append("Thresholded (Above ").append(gcol.getThreshold())
2148 if (gcol.isBelowThreshold())
2152 tt.append("Thresholded (Below ").append(gcol.getThreshold())
2155 if (gcol.isColourByLabel())
2157 tt.append("Coloured by label text. ").append(tt);
2162 if (!gcol.isColourByAttribute())
2170 Color newColor = gcol.getMaxColour();
2171 comp.setBackground(newColor);
2172 // System.err.println("Width is " + w / 2);
2173 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
2174 comp.setIcon(ficon);
2175 // tt+="RGB value: Max (" + newColor.getRed() + ", "
2176 // + newColor.getGreen() + ", " + newColor.getBlue()
2177 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
2178 // + ", " + minCol.getBlue() + ")");
2180 comp.setHorizontalAlignment(SwingConstants.CENTER);
2181 comp.setText(tx.toString());
2182 if (tt.length() > 0)
2184 if (comp.getToolTipText() == null)
2186 comp.setToolTipText(tt.toString());
2190 comp.setToolTipText(tt.append(" ").append(comp.getToolTipText())
2197 class FeatureIcon implements Icon
2199 FeatureColourI gcol;
2203 boolean midspace = false;
2205 int width = 50, height = 20;
2207 int s1, e1; // start and end of midpoint band for thresholded symbol
2209 Color mpcolour = Color.white;
2211 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2231 public int getIconWidth()
2237 public int getIconHeight()
2243 public void paintIcon(Component c, Graphics g, int x, int y)
2246 if (gcol.isColourByLabel())
2249 g.fillRect(0, 0, width, height);
2250 // need an icon here.
2251 g.setColor(gcol.getMaxColour());
2253 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2255 // g.setFont(g.getFont().deriveFont(
2256 // AffineTransform.getScaleInstance(
2257 // width/g.getFontMetrics().stringWidth("Label"),
2258 // height/g.getFontMetrics().getHeight())));
2260 g.drawString(MessageManager.getString("label.label"), 0, 0);
2265 Color minCol = gcol.getMinColour();
2267 g.fillRect(0, 0, s1, height);
2270 g.setColor(Color.white);
2271 g.fillRect(s1, 0, e1 - s1, height);
2273 g.setColor(gcol.getMaxColour());
2274 g.fillRect(0, e1, width - e1, height);
2279 class ColorEditor extends AbstractCellEditor
2280 implements TableCellEditor, ActionListener
2284 FeatureColourI currentColor;
2286 FeatureColourChooser chooser;
2292 JColorChooser colorChooser;
2296 protected static final String EDIT = "edit";
2298 int selectedRow = 0;
2300 public ColorEditor(FeatureSettings me)
2303 // Set up the editor (from the table's point of view),
2304 // which is a button.
2305 // This button brings up the color chooser dialog,
2306 // which is the editor from the user's point of view.
2307 button = new JButton();
2308 button.setActionCommand(EDIT);
2309 button.addActionListener(this);
2310 button.setBorderPainted(false);
2311 // Set up the dialog that the button brings up.
2312 colorChooser = new JColorChooser();
2313 dialog = JColorChooser.createDialog(button,
2314 MessageManager.getString("label.select_new_colour"), true, // modal
2315 colorChooser, this, // OK button handler
2316 null); // no CANCEL button handler
2320 * Handles events from the editor button and from the dialog's OK button.
2323 public void actionPerformed(ActionEvent e)
2326 if (EDIT.equals(e.getActionCommand()))
2328 // The user has clicked the cell, so
2329 // bring up the dialog.
2330 if (currentColor.isSimpleColour())
2332 // bring up simple color chooser
2333 button.setBackground(currentColor.getColour());
2334 colorChooser.setColor(currentColor.getColour());
2335 dialog.setVisible(true);
2339 // bring up graduated chooser.
2340 chooser = new FeatureColourChooser(me.fr, type);
2341 chooser.setRequestFocusEnabled(true);
2342 chooser.requestFocus();
2343 chooser.addActionListener(this);
2345 // Make the renderer reappear.
2346 fireEditingStopped();
2350 { // User pressed dialog's "OK" button.
2351 if (currentColor.isSimpleColour())
2353 currentColor = new FeatureColour(colorChooser.getColor());
2357 currentColor = chooser.getLastColour();
2359 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
2360 fireEditingStopped();
2361 me.table.validate();
2365 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2367 public Object getCellEditorValue()
2369 return currentColor;
2372 // Implement the one method defined by TableCellEditor.
2374 public Component getTableCellEditorComponent(JTable table, Object value,
2375 boolean isSelected, int row, int column)
2377 currentColor = (FeatureColourI) value;
2378 this.selectedRow = row;
2379 type = me.table.getValueAt(row, 0).toString();
2380 button.setOpaque(true);
2381 button.setBackground(me.getBackground());
2382 if (!currentColor.isSimpleColour())
2384 JLabel btn = new JLabel();
2385 btn.setSize(button.getSize());
2386 FeatureSettings.renderGraduatedColor(btn, currentColor);
2387 button.setBackground(btn.getBackground());
2388 button.setIcon(btn.getIcon());
2389 button.setText(btn.getText());
2394 button.setIcon(null);
2395 button.setBackground(currentColor.getColour());