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.LayoutManager;
57 import java.awt.Rectangle;
58 import java.awt.event.ActionEvent;
59 import java.awt.event.ActionListener;
60 import java.awt.event.FocusAdapter;
61 import java.awt.event.FocusEvent;
62 import java.awt.event.ItemEvent;
63 import java.awt.event.ItemListener;
64 import java.awt.event.MouseAdapter;
65 import java.awt.event.MouseEvent;
66 import java.awt.event.MouseMotionAdapter;
67 import java.beans.PropertyChangeEvent;
68 import java.beans.PropertyChangeListener;
70 import java.io.FileInputStream;
71 import java.io.FileOutputStream;
72 import java.io.InputStreamReader;
73 import java.io.OutputStreamWriter;
74 import java.io.PrintWriter;
75 import java.util.ArrayList;
76 import java.util.Arrays;
77 import java.util.HashSet;
78 import java.util.Hashtable;
79 import java.util.Iterator;
80 import java.util.List;
83 import java.util.Vector;
85 import javax.help.HelpSetException;
86 import javax.swing.AbstractCellEditor;
87 import javax.swing.BorderFactory;
88 import javax.swing.BoxLayout;
89 import javax.swing.ButtonGroup;
90 import javax.swing.Icon;
91 import javax.swing.JButton;
92 import javax.swing.JCheckBox;
93 import javax.swing.JCheckBoxMenuItem;
94 import javax.swing.JColorChooser;
95 import javax.swing.JComboBox;
96 import javax.swing.JDialog;
97 import javax.swing.JInternalFrame;
98 import javax.swing.JLabel;
99 import javax.swing.JLayeredPane;
100 import javax.swing.JMenuItem;
101 import javax.swing.JPanel;
102 import javax.swing.JPopupMenu;
103 import javax.swing.JRadioButton;
104 import javax.swing.JScrollPane;
105 import javax.swing.JSlider;
106 import javax.swing.JTabbedPane;
107 import javax.swing.JTable;
108 import javax.swing.JTextArea;
109 import javax.swing.JTextField;
110 import javax.swing.ListSelectionModel;
111 import javax.swing.SwingConstants;
112 import javax.swing.SwingUtilities;
113 import javax.swing.event.ChangeEvent;
114 import javax.swing.event.ChangeListener;
115 import javax.swing.plaf.basic.BasicArrowButton;
116 import javax.swing.table.AbstractTableModel;
117 import javax.swing.table.TableCellEditor;
118 import javax.swing.table.TableCellRenderer;
120 public class FeatureSettings extends JPanel
121 implements FeatureSettingsControllerI
123 private static final String COLON = ":";
125 private static final int MIN_WIDTH = 400;
127 private static final int MIN_HEIGHT = 400;
129 private static final int MAX_TOOLTIP_LENGTH = 50;
131 DasSourceBrowser dassourceBrowser;
133 DasSequenceFeatureFetcher dasFeatureFetcher;
135 JPanel dasSettingsPane = new JPanel();
137 final FeatureRenderer fr;
139 public final AlignFrame af;
142 * 'original' fields hold settings to restore on Cancel
144 Object[][] originalData;
146 private float originalTransparency;
148 private Map<String, KeyedMatcherSetI> originalFilters;
150 final JInternalFrame frame;
152 JScrollPane scrollPane = new JScrollPane();
158 JSlider transparency = new JSlider();
161 * when true, constructor is still executing - so ignore UI events
163 protected volatile boolean inConstruction = true;
165 int selectedRow = -1;
167 JButton fetchDAS = new JButton();
169 JButton saveDAS = new JButton();
171 JButton cancelDAS = new JButton();
173 boolean resettingTable = false;
176 * true when Feature Settings are updating from feature renderer
178 private boolean handlingUpdate = false;
181 * holds {featureCount, totalExtent} for each feature type
183 Map<String, float[]> typeWidth = null;
186 * fields of the feature filters tab
188 private JPanel filtersPane;
190 private JPanel chooseFiltersPanel;
192 private JComboBox<String> filteredFeatureChoice;
194 private JRadioButton andFilters;
196 private JRadioButton orFilters;
199 * filters for the currently selected feature type
201 private List<KeyedMatcherI> filters;
203 private JTextArea filtersAsText;
205 // set white normally, black to debug layout
206 private Color debugBorderColour = Color.white;
213 public FeatureSettings(AlignFrame alignFrame)
215 this.af = alignFrame;
216 fr = af.getFeatureRenderer();
218 // save transparency for restore on Cancel
219 originalTransparency = fr.getTransparency();
220 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
221 transparency.setMaximum(100 - originalTransparencyAsPercent);
223 originalFilters = fr.getFeatureFilters();
228 } catch (Exception ex)
230 ex.printStackTrace();
236 public String getToolTipText(MouseEvent e)
238 if (table.columnAtPoint(e.getPoint()) == 0)
241 * Tooltip for feature name only
243 return JvSwingUtils.wrapTooltip(true, MessageManager
244 .getString("label.feature_settings_click_drag"));
249 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
250 table.setFont(new Font("Verdana", Font.PLAIN, 12));
251 table.setDefaultRenderer(Color.class, new ColorRenderer());
253 table.setDefaultEditor(Color.class, new ColorEditor(this));
255 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
256 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
257 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
259 table.addMouseListener(new MouseAdapter()
262 public void mousePressed(MouseEvent evt)
264 selectedRow = table.rowAtPoint(evt.getPoint());
265 if (evt.isPopupTrigger())
267 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
268 table.getValueAt(selectedRow, 1), fr.getMinMax(),
269 evt.getX(), evt.getY());
271 else if (evt.getClickCount() == 2)
273 boolean invertSelection = evt.isAltDown();
274 boolean toggleSelection = Platform.isControlDown(evt);
275 boolean extendSelection = evt.isShiftDown();
276 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
277 invertSelection, extendSelection, toggleSelection,
278 (String) table.getValueAt(selectedRow, 0));
282 // isPopupTrigger fires on mouseReleased on Windows
284 public void mouseReleased(MouseEvent evt)
286 selectedRow = table.rowAtPoint(evt.getPoint());
287 if (evt.isPopupTrigger())
289 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
290 table.getValueAt(selectedRow, 1), fr.getMinMax(),
291 evt.getX(), evt.getY());
296 table.addMouseMotionListener(new MouseMotionAdapter()
299 public void mouseDragged(MouseEvent evt)
301 int newRow = table.rowAtPoint(evt.getPoint());
302 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
305 * reposition 'selectedRow' to 'newRow' (the dragged to location)
306 * this could be more than one row away for a very fast drag action
307 * so just swap it with adjacent rows until we get it there
309 Object[][] data = ((FeatureTableModel) table.getModel())
311 int direction = newRow < selectedRow ? -1 : 1;
312 for (int i = selectedRow; i != newRow; i += direction)
314 Object[] temp = data[i];
315 data[i] = data[i + direction];
316 data[i + direction] = temp;
318 updateFeatureRenderer(data);
320 selectedRow = newRow;
324 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
325 // MessageManager.getString("label.feature_settings_click_drag")));
326 scrollPane.setViewportView(table);
328 dassourceBrowser = new DasSourceBrowser(this);
329 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
331 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
333 fr.findAllFeatures(true); // display everything!
336 discoverAllFeatureData();
337 final PropertyChangeListener change;
338 final FeatureSettings fs = this;
339 fr.addPropertyChangeListener(change = new PropertyChangeListener()
342 public void propertyChange(PropertyChangeEvent evt)
344 if (!fs.resettingTable && !fs.handlingUpdate)
346 fs.handlingUpdate = true;
347 fs.resetTable(null); // new groups may be added with new seuqence
348 // feature types only
349 fs.handlingUpdate = false;
355 frame = new JInternalFrame();
356 frame.setContentPane(this);
357 if (Platform.isAMac())
359 Desktop.addInternalFrame(frame,
360 MessageManager.getString("label.sequence_feature_settings"),
365 Desktop.addInternalFrame(frame,
366 MessageManager.getString("label.sequence_feature_settings"),
369 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
371 frame.addInternalFrameListener(
372 new javax.swing.event.InternalFrameAdapter()
375 public void internalFrameClosed(
376 javax.swing.event.InternalFrameEvent evt)
378 fr.removePropertyChangeListener(change);
379 dassourceBrowser.fs = null;
382 frame.setLayer(JLayeredPane.PALETTE_LAYER);
383 inConstruction = false;
386 protected void popupSort(final int selectedRow, final String type,
387 final Object typeCol, final Map<String, float[][]> minmax, int x,
390 final FeatureColourI featureColour = (FeatureColourI) typeCol;
392 JPopupMenu men = new JPopupMenu(MessageManager
393 .formatMessage("label.settings_for_param", new String[]
395 JMenuItem scr = new JMenuItem(
396 MessageManager.getString("label.sort_by_score"));
398 final FeatureSettings me = this;
399 scr.addActionListener(new ActionListener()
403 public void actionPerformed(ActionEvent e)
406 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
411 JMenuItem dens = new JMenuItem(
412 MessageManager.getString("label.sort_by_density"));
413 dens.addActionListener(new ActionListener()
417 public void actionPerformed(ActionEvent e)
420 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
428 * variable colour options include colour by label, by score,
429 * by selected attribute text, or attribute value
431 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
432 MessageManager.getString("label.variable_colour"));
433 mxcol.setSelected(!featureColour.isSimpleColour());
435 mxcol.addActionListener(new ActionListener()
437 JColorChooser colorChooser;
440 public void actionPerformed(ActionEvent e)
442 if (e.getSource() == mxcol)
444 if (featureColour.isSimpleColour())
446 FeatureColourChooser fc = new FeatureColourChooser(me.fr, type);
447 fc.addActionListener(this);
451 // bring up simple color chooser
452 colorChooser = new JColorChooser();
453 JDialog dialog = JColorChooser.createDialog(me,
454 "Select new Colour", true, // modal
455 colorChooser, this, // OK button handler
456 null); // no CANCEL button handler
457 colorChooser.setColor(featureColour.getMaxColour());
458 dialog.setVisible(true);
463 if (e.getSource() instanceof FeatureColourChooser)
465 FeatureColourChooser fc = (FeatureColourChooser) e.getSource();
466 table.setValueAt(fc.getLastColour(), selectedRow, 1);
471 // probably the color chooser!
472 table.setValueAt(new FeatureColour(colorChooser.getColor()),
475 me.updateFeatureRenderer(
476 ((FeatureTableModel) table.getModel()).getData(), false);
483 JMenuItem selCols = new JMenuItem(
484 MessageManager.getString("label.select_columns_containing"));
485 selCols.addActionListener(new ActionListener()
488 public void actionPerformed(ActionEvent arg0)
490 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
494 JMenuItem clearCols = new JMenuItem(MessageManager
495 .getString("label.select_columns_not_containing"));
496 clearCols.addActionListener(new ActionListener()
499 public void actionPerformed(ActionEvent arg0)
501 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
505 JMenuItem hideCols = new JMenuItem(
506 MessageManager.getString("label.hide_columns_containing"));
507 hideCols.addActionListener(new ActionListener()
510 public void actionPerformed(ActionEvent arg0)
512 fr.ap.alignFrame.hideFeatureColumns(type, true);
515 JMenuItem hideOtherCols = new JMenuItem(
516 MessageManager.getString("label.hide_columns_not_containing"));
517 hideOtherCols.addActionListener(new ActionListener()
520 public void actionPerformed(ActionEvent arg0)
522 fr.ap.alignFrame.hideFeatureColumns(type, false);
528 men.add(hideOtherCols);
529 men.show(table, x, y);
533 synchronized public void discoverAllFeatureData()
535 Set<String> allGroups = new HashSet<>();
536 AlignmentI alignment = af.getViewport().getAlignment();
538 for (int i = 0; i < alignment.getHeight(); i++)
540 SequenceI seq = alignment.getSequenceAt(i);
541 for (String group : seq.getFeatures().getFeatureGroups(true))
543 if (group != null && !allGroups.contains(group))
545 allGroups.add(group);
546 checkGroupState(group);
551 populateFilterableFeatures();
559 * Synchronise gui group list and check visibility of group
562 * @return true if group is visible
564 private boolean checkGroupState(String group)
566 boolean visible = fr.checkGroupVisibility(group, true);
568 for (int g = 0; g < groupPanel.getComponentCount(); g++)
570 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
572 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
577 final String grp = group;
578 final JCheckBox check = new JCheckBox(group, visible);
579 check.setFont(new Font("Serif", Font.BOLD, 12));
580 check.setToolTipText(group);
581 check.addItemListener(new ItemListener()
584 public void itemStateChanged(ItemEvent evt)
586 fr.setGroupVisibility(check.getText(), check.isSelected());
587 resetTable(new String[] { grp });
588 af.alignPanel.paintAlignment(true, true);
591 groupPanel.add(check);
595 synchronized void resetTable(String[] groupChanged)
601 resettingTable = true;
602 typeWidth = new Hashtable<>();
603 // TODO: change avWidth calculation to 'per-sequence' average and use long
606 Set<String> displayableTypes = new HashSet<>();
607 Set<String> foundGroups = new HashSet<>();
610 * determine which feature types may be visible depending on
611 * which groups are selected, and recompute average width data
613 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
616 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
619 * get the sequence's groups for positional features
620 * and keep track of which groups are visible
622 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
623 Set<String> visibleGroups = new HashSet<>();
624 for (String group : groups)
626 if (group == null || checkGroupState(group))
628 visibleGroups.add(group);
631 foundGroups.addAll(groups);
634 * get distinct feature types for visible groups
635 * record distinct visible types, and their count and total length
637 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
638 visibleGroups.toArray(new String[visibleGroups.size()]));
639 for (String type : types)
641 displayableTypes.add(type);
642 float[] avWidth = typeWidth.get(type);
645 avWidth = new float[2];
646 typeWidth.put(type, avWidth);
648 // todo this could include features with a non-visible group
649 // - do we greatly care?
650 // todo should we include non-displayable features here, and only
651 // update when features are added?
652 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
653 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
657 Object[][] data = new Object[displayableTypes.size()][3];
660 if (fr.hasRenderOrder())
664 fr.findAllFeatures(groupChanged != null); // prod to update
665 // colourschemes. but don't
667 // First add the checks in the previous render order,
668 // in case the window has been closed and reopened
670 List<String> frl = fr.getRenderOrder();
671 for (int ro = frl.size() - 1; ro > -1; ro--)
673 String type = frl.get(ro);
675 if (!displayableTypes.contains(type))
680 data[dataIndex][0] = type;
681 data[dataIndex][1] = fr.getFeatureStyle(type);
682 data[dataIndex][2] = new Boolean(
683 af.getViewport().getFeaturesDisplayed().isVisible(type));
685 displayableTypes.remove(type);
690 * process any extra features belonging only to
691 * a group which was just selected
693 while (!displayableTypes.isEmpty())
695 String type = displayableTypes.iterator().next();
696 data[dataIndex][0] = type;
698 data[dataIndex][1] = fr.getFeatureStyle(type);
699 if (data[dataIndex][1] == null)
701 // "Colour has been updated in another view!!"
702 fr.clearRenderOrder();
706 data[dataIndex][2] = new Boolean(true);
708 displayableTypes.remove(type);
711 if (originalData == null)
713 originalData = new Object[data.length][3];
714 for (int i = 0; i < data.length; i++)
716 System.arraycopy(data[i], 0, originalData[i], 0, 3);
721 updateOriginalData(data);
724 table.setModel(new FeatureTableModel(data));
725 table.getColumnModel().getColumn(0).setPreferredWidth(200);
727 groupPanel.setLayout(
728 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
729 pruneGroups(foundGroups);
730 groupPanel.validate();
732 updateFeatureRenderer(data, groupChanged != null);
733 resettingTable = false;
737 * Updates 'originalData' (used for restore on Cancel) if we detect that
738 * changes have been made outwith this dialog
740 * <li>a new feature type added (and made visible)</li>
741 * <li>a feature colour changed (in the Amend Features dialog)</li>
746 protected void updateOriginalData(Object[][] foundData)
748 // todo LinkedHashMap instead of Object[][] would be nice
750 Object[][] currentData = ((FeatureTableModel) table.getModel())
752 for (Object[] row : foundData)
754 String type = (String) row[0];
755 boolean found = false;
756 for (Object[] current : currentData)
758 if (type.equals(current[0]))
762 * currently dependent on object equality here;
763 * really need an equals method on FeatureColour
765 if (!row[1].equals(current[1]))
768 * feature colour has changed externally - update originalData
770 for (Object[] original : originalData)
772 if (type.equals(original[0]))
774 original[1] = row[1];
785 * new feature detected - add to original data (on top)
787 Object[][] newData = new Object[originalData.length + 1][3];
788 for (int i = 0; i < originalData.length; i++)
790 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
793 originalData = newData;
799 * Remove from the groups panel any checkboxes for groups that are not in the
800 * foundGroups set. This enables removing a group from the display when the
801 * last feature in that group is deleted.
805 protected void pruneGroups(Set<String> foundGroups)
807 for (int g = 0; g < groupPanel.getComponentCount(); g++)
809 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
810 if (!foundGroups.contains(checkbox.getText()))
812 groupPanel.remove(checkbox);
818 * reorder data based on the featureRenderers global priority list.
822 private void ensureOrder(Object[][] data)
824 boolean sort = false;
825 float[] order = new float[data.length];
826 for (int i = 0; i < order.length; i++)
828 order[i] = fr.getOrder(data[i][0].toString());
831 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
835 sort = sort || order[i - 1] > order[i];
840 jalview.util.QuickSort.sort(order, data);
846 JalviewFileChooser chooser = new JalviewFileChooser("fc",
847 "Sequence Feature Colours");
848 chooser.setFileView(new JalviewFileView());
849 chooser.setDialogTitle(
850 MessageManager.getString("label.load_feature_colours"));
851 chooser.setToolTipText(MessageManager.getString("action.load"));
853 int value = chooser.showOpenDialog(this);
855 if (value == JalviewFileChooser.APPROVE_OPTION)
857 File file = chooser.getSelectedFile();
861 InputStreamReader in = new InputStreamReader(
862 new FileInputStream(file), "UTF-8");
864 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
866 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
869 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
872 Color mincol = null, maxcol = null;
875 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
876 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
878 } catch (Exception e)
880 Cache.log.warn("Couldn't parse out graduated feature color.",
883 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
884 newcol.getMin(), newcol.getMax());
885 if (newcol.hasAutoScale())
887 gcol.setAutoScaled(newcol.getAutoScale());
889 if (newcol.hasColourByLabel())
891 gcol.setColourByLabel(newcol.getColourByLabel());
893 if (newcol.hasThreshold())
895 gcol.setThreshold(newcol.getThreshold());
897 if (newcol.getThreshType().length() > 0)
899 String ttyp = newcol.getThreshType();
900 if (ttyp.equalsIgnoreCase("ABOVE"))
902 gcol.setAboveThreshold(true);
904 if (ttyp.equalsIgnoreCase("BELOW"))
906 gcol.setBelowThreshold(true);
909 fr.setColour(name = newcol.getName(), gcol);
913 Color color = new Color(
914 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
915 fr.setColour(name = jucs.getColour(i).getName(),
916 new FeatureColour(color));
918 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
923 Object[][] data = ((FeatureTableModel) table.getModel())
926 updateFeatureRenderer(data, false);
929 } catch (Exception ex)
931 System.out.println("Error loading User Colour File\n" + ex);
938 JalviewFileChooser chooser = new JalviewFileChooser("fc",
939 "Sequence Feature Colours");
940 chooser.setFileView(new JalviewFileView());
941 chooser.setDialogTitle(
942 MessageManager.getString("label.save_feature_colours"));
943 chooser.setToolTipText(MessageManager.getString("action.save"));
945 int value = chooser.showSaveDialog(this);
947 if (value == JalviewFileChooser.APPROVE_OPTION)
949 String choice = chooser.getSelectedFile().getPath();
950 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
951 ucs.setSchemeName("Sequence Features");
954 PrintWriter out = new PrintWriter(new OutputStreamWriter(
955 new FileOutputStream(choice), "UTF-8"));
957 Set<String> fr_colours = fr.getAllFeatureColours();
958 Iterator<String> e = fr_colours.iterator();
959 float[] sortOrder = new float[fr_colours.size()];
960 String[] sortTypes = new String[fr_colours.size()];
964 sortTypes[i] = e.next();
965 sortOrder[i] = fr.getOrder(sortTypes[i]);
968 QuickSort.sort(sortOrder, sortTypes);
970 for (i = 0; i < sortTypes.length; i++)
972 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
973 col.setName(sortTypes[i]);
974 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
975 if (fcol.isSimpleColour())
977 col.setRGB(Format.getHexString(fcol.getColour()));
981 col.setRGB(Format.getHexString(fcol.getMaxColour()));
982 col.setMin(fcol.getMin());
983 col.setMax(fcol.getMax());
985 jalview.util.Format.getHexString(fcol.getMinColour()));
986 col.setAutoScale(fcol.isAutoScaled());
987 col.setThreshold(fcol.getThreshold());
988 col.setColourByLabel(fcol.isColourByLabel());
989 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
990 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
996 } catch (Exception ex)
998 ex.printStackTrace();
1003 public void invertSelection()
1005 for (int i = 0; i < table.getRowCount(); i++)
1007 Boolean value = (Boolean) table.getValueAt(i, 2);
1009 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
1013 public void orderByAvWidth()
1015 if (table == null || table.getModel() == null)
1019 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1020 float[] width = new float[data.length];
1024 for (int i = 0; i < data.length; i++)
1026 awidth = typeWidth.get(data[i][0]);
1029 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1030 // weight - but have to make per
1031 // sequence, too (awidth[2])
1032 // if (width[i]==1) // hack to distinguish single width sequences.
1044 boolean sort = false;
1045 for (int i = 0; i < width.length; i++)
1047 // awidth = (float[]) typeWidth.get(data[i][0]);
1050 width[i] = fr.getOrder(data[i][0].toString());
1053 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1058 width[i] /= max; // normalize
1059 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1063 sort = sort || width[i - 1] > width[i];
1068 jalview.util.QuickSort.sort(width, data);
1069 // update global priority order
1072 updateFeatureRenderer(data, false);
1080 frame.setClosed(true);
1081 } catch (Exception exe)
1087 public void updateFeatureRenderer(Object[][] data)
1089 updateFeatureRenderer(data, true);
1093 * Update the priority order of features; only repaint if this changed the
1094 * order of visible features
1099 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1101 if (fr.setFeaturePriority(data, visibleNew))
1103 af.alignPanel.paintAlignment(true, true);
1107 private void jbInit() throws Exception
1109 this.setLayout(new BorderLayout());
1111 JPanel settingsPane = new JPanel();
1112 settingsPane.setLayout(new BorderLayout());
1114 filtersPane = new JPanel();
1116 dasSettingsPane.setLayout(new BorderLayout());
1118 JPanel bigPanel = new JPanel();
1119 bigPanel.setLayout(new BorderLayout());
1121 groupPanel = new JPanel();
1122 bigPanel.add(groupPanel, BorderLayout.NORTH);
1124 JButton invert = new JButton(
1125 MessageManager.getString("label.invert_selection"));
1126 invert.setFont(JvSwingUtils.getLabelFont());
1127 invert.addActionListener(new ActionListener()
1130 public void actionPerformed(ActionEvent e)
1136 JButton optimizeOrder = new JButton(
1137 MessageManager.getString("label.optimise_order"));
1138 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1139 optimizeOrder.addActionListener(new ActionListener()
1142 public void actionPerformed(ActionEvent e)
1148 JButton sortByScore = new JButton(
1149 MessageManager.getString("label.seq_sort_by_score"));
1150 sortByScore.setFont(JvSwingUtils.getLabelFont());
1151 sortByScore.addActionListener(new ActionListener()
1154 public void actionPerformed(ActionEvent e)
1156 af.avc.sortAlignmentByFeatureScore(null);
1159 JButton sortByDens = new JButton(
1160 MessageManager.getString("label.sequence_sort_by_density"));
1161 sortByDens.setFont(JvSwingUtils.getLabelFont());
1162 sortByDens.addActionListener(new ActionListener()
1165 public void actionPerformed(ActionEvent e)
1167 af.avc.sortAlignmentByFeatureDensity(null);
1171 JButton help = new JButton(MessageManager.getString("action.help"));
1172 help.setFont(JvSwingUtils.getLabelFont());
1173 help.addActionListener(new ActionListener()
1176 public void actionPerformed(ActionEvent e)
1180 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1181 } catch (HelpSetException e1)
1183 e1.printStackTrace();
1187 help.setFont(JvSwingUtils.getLabelFont());
1188 help.setText(MessageManager.getString("action.help"));
1189 help.addActionListener(new ActionListener()
1192 public void actionPerformed(ActionEvent e)
1196 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1197 } catch (HelpSetException e1)
1199 e1.printStackTrace();
1204 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1205 cancel.setFont(JvSwingUtils.getLabelFont());
1206 cancel.addActionListener(new ActionListener()
1209 public void actionPerformed(ActionEvent e)
1211 fr.setTransparency(originalTransparency);
1212 fr.setFeatureFilters(originalFilters);
1213 updateFeatureRenderer(originalData);
1218 JButton ok = new JButton(MessageManager.getString("action.ok"));
1219 ok.setFont(JvSwingUtils.getLabelFont());
1220 ok.addActionListener(new ActionListener()
1223 public void actionPerformed(ActionEvent e)
1229 JButton loadColours = new JButton(
1230 MessageManager.getString("label.load_colours"));
1231 loadColours.setFont(JvSwingUtils.getLabelFont());
1232 loadColours.addActionListener(new ActionListener()
1235 public void actionPerformed(ActionEvent e)
1241 JButton saveColours = new JButton(
1242 MessageManager.getString("label.save_colours"));
1243 saveColours.setFont(JvSwingUtils.getLabelFont());
1244 saveColours.addActionListener(new ActionListener()
1247 public void actionPerformed(ActionEvent e)
1252 transparency.addChangeListener(new ChangeListener()
1255 public void stateChanged(ChangeEvent evt)
1257 if (!inConstruction)
1259 fr.setTransparency((100 - transparency.getValue()) / 100f);
1260 af.alignPanel.paintAlignment(true,true);
1265 transparency.setMaximum(70);
1266 transparency.setToolTipText(
1267 MessageManager.getString("label.transparency_tip"));
1268 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1269 fetchDAS.addActionListener(new ActionListener()
1272 public void actionPerformed(ActionEvent e)
1274 fetchDAS_actionPerformed(e);
1277 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1278 saveDAS.addActionListener(new ActionListener()
1281 public void actionPerformed(ActionEvent e)
1283 saveDAS_actionPerformed(e);
1287 JPanel dasButtonPanel = new JPanel();
1288 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1289 dasSettingsPane.setBorder(null);
1290 cancelDAS.setEnabled(false);
1291 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1292 cancelDAS.addActionListener(new ActionListener()
1295 public void actionPerformed(ActionEvent e)
1297 cancelDAS_actionPerformed(e);
1301 JTabbedPane tabbedPane = new JTabbedPane();
1302 this.add(tabbedPane, BorderLayout.CENTER);
1303 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1305 tabbedPane.addTab(MessageManager.getString("label.filters"),
1307 // tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1308 // dasSettingsPane);
1310 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1311 bigPanel.add(transPanel, BorderLayout.SOUTH);
1313 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1314 transbuttons.add(optimizeOrder);
1315 transbuttons.add(invert);
1316 transbuttons.add(sortByScore);
1317 transbuttons.add(sortByDens);
1318 transbuttons.add(help);
1319 transPanel.add(transparency);
1320 transPanel.add(transbuttons);
1322 JPanel buttonPanel = new JPanel();
1323 buttonPanel.add(ok);
1324 buttonPanel.add(cancel);
1325 buttonPanel.add(loadColours);
1326 buttonPanel.add(saveColours);
1327 bigPanel.add(scrollPane, BorderLayout.CENTER);
1328 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1329 dasButtonPanel.add(fetchDAS);
1330 dasButtonPanel.add(cancelDAS);
1331 dasButtonPanel.add(saveDAS);
1332 settingsPane.add(bigPanel, BorderLayout.CENTER);
1333 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1339 * Populates initial layout of the feature attribute filters panel
1341 protected void initFiltersTab()
1343 filters = new ArrayList<>();
1346 * choose feature type
1348 JPanel chooseTypePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1349 chooseTypePanel.setBackground(Color.white);
1350 JvSwingUtils.createItalicTitledBorder(chooseTypePanel,
1351 MessageManager.getString("label.feature_type"), true);
1352 filteredFeatureChoice = new JComboBox<>();
1353 filteredFeatureChoice.addItemListener(new ItemListener()
1356 public void itemStateChanged(ItemEvent e)
1358 refreshFiltersDisplay();
1361 chooseTypePanel.add(new JLabel(MessageManager
1362 .getString("label.feature_to_filter")));
1363 chooseTypePanel.add(filteredFeatureChoice);
1364 populateFilterableFeatures();
1367 * the panel with the filters for the selected feature type
1369 JPanel filtersPanel = new JPanel();
1370 filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
1371 filtersPanel.setBackground(Color.white);
1372 JvSwingUtils.createItalicTitledBorder(filtersPanel,
1373 MessageManager.getString("label.filters"), true);
1376 * add AND or OR radio buttons
1378 JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1379 andOrPanel.setBackground(Color.white);
1380 andOrPanel.setBorder(BorderFactory.createLineBorder(debugBorderColour));
1381 andFilters = new JRadioButton("And");
1382 orFilters = new JRadioButton("Or");
1383 ActionListener actionListener = new ActionListener()
1386 public void actionPerformed(ActionEvent e)
1391 andFilters.addActionListener(actionListener);
1392 orFilters.addActionListener(actionListener);
1393 ButtonGroup andOr = new ButtonGroup();
1394 andOr.add(andFilters);
1395 andOr.add(orFilters);
1396 andFilters.setSelected(true);
1397 andOrPanel.add(new JLabel(MessageManager
1398 .getString("label.join_conditions")));
1399 andOrPanel.add(andFilters);
1400 andOrPanel.add(orFilters);
1401 filtersPanel.add(andOrPanel);
1404 * panel with filters - populated by refreshFiltersDisplay
1406 chooseFiltersPanel = new JPanel();
1407 LayoutManager box = new BoxLayout(chooseFiltersPanel,
1409 chooseFiltersPanel.setLayout(box);
1410 filtersPanel.add(chooseFiltersPanel);
1413 * a read-only text view of the current filters
1415 JPanel showFiltersPanel = new JPanel(new BorderLayout(5, 5));
1416 showFiltersPanel.setBackground(Color.white);
1417 JvSwingUtils.createItalicTitledBorder(showFiltersPanel,
1418 MessageManager.getString("label.match_condition"), true);
1419 filtersAsText = new JTextArea();
1420 filtersAsText.setLineWrap(true);
1421 filtersAsText.setWrapStyleWord(true);
1422 showFiltersPanel.add(filtersAsText);
1424 filtersPane.setLayout(new BorderLayout());
1425 filtersPane.add(chooseTypePanel, BorderLayout.NORTH);
1426 filtersPane.add(filtersPanel, BorderLayout.CENTER);
1427 filtersPane.add(showFiltersPanel, BorderLayout.SOUTH);
1430 * update display for initial feature type selection
1432 refreshFiltersDisplay();
1436 * Adds entries to the 'choose feature to filter' drop-down choice. Only
1437 * feature types which have known attributes (so can be filtered) are
1438 * included, so recall this method to update the list (check for newly added
1441 protected void populateFilterableFeatures()
1444 * suppress action handler while updating the list
1446 ItemListener listener = filteredFeatureChoice.getItemListeners()[0];
1447 filteredFeatureChoice.removeItemListener(listener);
1449 filteredFeatureChoice.removeAllItems();
1450 ReverseListIterator<String> types = new ReverseListIterator<>(
1451 fr.getRenderOrder());
1453 boolean found = false;
1454 while (types.hasNext())
1456 String type = types.next();
1457 if (FeatureAttributes.getInstance().hasAttributes(type))
1459 filteredFeatureChoice.addItem(type);
1465 filteredFeatureChoice.addItem(MessageManager
1466 .getString("label.no_feature_attributes"));
1467 filteredFeatureChoice.setEnabled(false);
1470 filteredFeatureChoice.addItemListener(listener);
1474 * Refreshes the display to show any filters currently configured for the
1475 * selected feature type (editable, with 'remove' option), plus one extra row
1476 * for adding a condition. This should be called on change of selected feature
1477 * type, or after a filter has been removed, added or amended.
1479 protected void refreshFiltersDisplay()
1482 * clear the panel and list of filter conditions
1484 chooseFiltersPanel.removeAll();
1488 * look up attributes known for feature type
1490 String selectedType = (String) filteredFeatureChoice.getSelectedItem();
1491 List<String[]> attNames = FeatureAttributes.getInstance()
1492 .getAttributes(selectedType);
1495 * if this feature type has filters set, load them first
1497 KeyedMatcherSetI featureFilters = fr.getFeatureFilter(selectedType);
1498 filtersAsText.setText("");
1499 if (featureFilters != null)
1501 filtersAsText.setText(featureFilters.toString());
1502 if (!featureFilters.isAnded())
1504 orFilters.setSelected(true);
1506 featureFilters.getMatchers().forEach(matcher -> filters.add(matcher));
1510 * and an empty filter for the user to populate (add)
1512 KeyedMatcherI noFilter = new KeyedMatcher(Condition.values()[0], "",
1514 filters.add(noFilter);
1517 * render the conditions in rows, each in its own JPanel
1519 int filterIndex = 0;
1520 for (KeyedMatcherI filter : filters)
1522 String[] attName = filter.getKey();
1523 Condition condition = filter.getMatcher()
1525 String pattern = filter.getMatcher().getPattern();
1526 JPanel row = addFilter(attName, attNames, condition, pattern, filterIndex);
1527 row.setBorder(BorderFactory.createLineBorder(debugBorderColour));
1528 chooseFiltersPanel.add(row);
1531 // chooseFiltersPanel.add(Box.createVerticalGlue());
1533 filtersPane.validate();
1534 filtersPane.repaint();
1538 * A helper method that constructs a panel with one filter condition:
1540 * <li>a drop-down list of attribute names to choose from</li>
1541 * <li>a drop-down list of conditions to choose from</li>
1542 * <li>a text field for input of a match pattern</li>
1543 * <li>optionally, a 'remove' button</li>
1545 * If attribute, condition or pattern are not null, they are set as defaults for
1546 * the input fields. The 'remove' button is added unless the pattern is null or
1547 * empty (incomplete filter condition).
1553 * @param filterIndex
1556 protected JPanel addFilter(String[] attName, List<String[]> attNames,
1557 Condition cond, String pattern, int filterIndex)
1559 JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
1560 filterRow.setBackground(Color.white);
1563 * drop-down choice of attribute, with description as a tooltip
1564 * if we can obtain it
1566 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1567 final JComboBox<String> attCombo = populateAttributesDropdown(
1568 featureType, attNames);
1569 JComboBox<Condition> condCombo = new JComboBox<>();
1570 JTextField patternField = new JTextField(8);
1573 * action handlers that validate and (if valid) apply changes
1575 ActionListener actionListener = new ActionListener()
1578 public void actionPerformed(ActionEvent e)
1580 if (attCombo.getSelectedItem() != null)
1582 if (validateFilter(patternField, condCombo))
1584 updateFilter(attCombo, condCombo, patternField, filterIndex);
1590 ItemListener itemListener = new ItemListener()
1593 public void itemStateChanged(ItemEvent e)
1595 actionListener.actionPerformed(null);
1599 if (attName == null) // the 'add a condition' row
1601 attCombo.setSelectedItem(null);
1605 attCombo.setSelectedItem(String.join(COLON, attName));
1607 attCombo.addItemListener(itemListener);
1609 filterRow.add(attCombo);
1612 * drop-down choice of test condition
1614 for (Condition c : Condition.values())
1616 condCombo.addItem(c);
1620 condCombo.setSelectedItem(cond);
1622 condCombo.addItemListener(itemListener);
1623 filterRow.add(condCombo);
1626 * pattern to match against
1628 patternField.setText(pattern);
1629 patternField.addActionListener(actionListener);
1630 patternField.addFocusListener(new FocusAdapter()
1633 public void focusLost(FocusEvent e)
1635 actionListener.actionPerformed(null);
1638 filterRow.add(patternField);
1641 * add remove button if filter is populated (non-empty pattern)
1643 if (pattern != null && pattern.trim().length() > 0)
1645 // todo: gif for button drawing '-' or 'x'
1646 JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
1647 removeCondition.setToolTipText(MessageManager
1648 .getString("label.delete_row"));
1649 removeCondition.addActionListener(new ActionListener()
1652 public void actionPerformed(ActionEvent e)
1654 filters.remove(filterIndex);
1658 filterRow.add(removeCondition);
1665 * A helper method to build the drop-down choice of attributes for a feature.
1666 * Where metadata is available with a description for an attribute, that is
1667 * added as a tooltip.
1669 * @param featureType
1672 protected JComboBox<String> populateAttributesDropdown(
1673 String featureType, List<String[]> attNames)
1675 List<String> displayNames = new ArrayList<>();
1676 List<String> tooltips = new ArrayList<>();
1677 FeatureAttributes fa = FeatureAttributes.getInstance();
1678 for (String[] attName : attNames)
1680 String desc = fa.getDescription(featureType, attName);
1681 if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
1683 desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "...";
1685 displayNames.add(String.join(COLON, attName));
1686 tooltips.add(desc == null ? "" : desc);
1689 JComboBox<String> attCombo = JvSwingUtils.buildComboWithTooltips(
1690 displayNames, tooltips);
1691 if (attNames.isEmpty())
1693 attCombo.setToolTipText(MessageManager
1694 .getString("label.no_attributes"));
1700 * Action on any change to feature filtering, namely
1702 * <li>change of selected attribute</li>
1703 * <li>change of selected condition</li>
1704 * <li>change of match pattern</li>
1705 * <li>removal of a condition</li>
1707 * The action should be to
1709 * <li>parse and validate the filters</li>
1710 * <li>if valid, update the filter text box</li>
1711 * <li>and apply the filters to the viewport</li>
1714 protected void filtersChanged()
1717 * update the filter conditions for the feature type
1719 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1720 boolean anded = andFilters.isSelected();
1721 KeyedMatcherSetI combined = new KeyedMatcherSet();
1723 for (KeyedMatcherI filter : filters)
1725 String pattern = filter.getMatcher().getPattern();
1726 if (pattern.trim().length() > 0)
1730 combined.and(filter);
1734 combined.or(filter);
1740 * save the filter conditions in the FeatureRenderer
1741 * (note this might now be an empty filter with no conditions)
1743 fr.setFeatureFilter(featureType, combined);
1745 filtersAsText.setText(combined.toString());
1747 refreshFiltersDisplay();
1749 af.alignPanel.paintAlignment(true, true);
1753 * Constructs a filter condition from the given input fields, and replaces the
1754 * condition at filterIndex with the new one
1759 * @param filterIndex
1761 protected void updateFilter(JComboBox<String> attCombo,
1762 JComboBox<Condition> condCombo, JTextField valueField,
1765 String attName = (String) attCombo.getSelectedItem();
1766 Condition cond = (Condition) condCombo.getSelectedItem();
1767 String pattern = valueField.getText();
1768 KeyedMatcherI km = new KeyedMatcher(cond, pattern,
1769 attName.split(COLON));
1771 filters.set(filterIndex, km);
1774 public void fetchDAS_actionPerformed(ActionEvent e)
1776 fetchDAS.setEnabled(false);
1777 cancelDAS.setEnabled(true);
1778 dassourceBrowser.setGuiEnabled(false);
1779 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1780 .getSelectedSources();
1781 doDasFeatureFetch(selectedSources, true, true);
1785 * get the features from selectedSources for all or the current selection
1787 * @param selectedSources
1788 * @param checkDbRefs
1789 * @param promptFetchDbRefs
1791 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1792 boolean checkDbRefs, boolean promptFetchDbRefs)
1794 SequenceI[] dataset, seqs;
1796 AlignmentViewport vp = af.getViewport();
1797 if (vp.getSelectionGroup() != null
1798 && vp.getSelectionGroup().getSize() > 0)
1800 iSize = vp.getSelectionGroup().getSize();
1801 dataset = new SequenceI[iSize];
1802 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1806 iSize = vp.getAlignment().getHeight();
1807 seqs = vp.getAlignment().getSequencesArray();
1810 dataset = new SequenceI[iSize];
1811 for (int i = 0; i < iSize; i++)
1813 dataset[i] = seqs[i].getDatasetSequence();
1816 cancelDAS.setEnabled(true);
1817 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1818 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1819 af.getViewport().setShowSequenceFeatures(true);
1820 af.showSeqFeatures.setSelected(true);
1824 * blocking call to initialise the das source browser
1826 public void initDasSources()
1828 dassourceBrowser.initDasSources();
1832 * examine the current list of das sources and return any matching the given
1833 * nicknames in sources
1836 * Vector of Strings to resolve to DAS source nicknames.
1837 * @return sources that are present in source list.
1839 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1841 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1845 * get currently selected das sources. ensure you have called initDasSources
1846 * before calling this.
1848 * @return vector of selected das source nicknames
1850 public Vector<jalviewSourceI> getSelectedSources()
1852 return dassourceBrowser.getSelectedSources();
1856 * properly initialise DAS fetcher and then initiate a new thread to fetch
1857 * features from the named sources (rather than any turned on by default)
1861 * if true then runs in same thread, otherwise passes to the Swing
1864 public void fetchDasFeatures(Vector<String> sources, boolean block)
1867 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1868 .resolveSourceNicknames(sources);
1869 if (resolved.size() == 0)
1871 resolved = dassourceBrowser.getSelectedSources();
1873 if (resolved.size() > 0)
1875 final List<jalviewSourceI> dassources = resolved;
1876 fetchDAS.setEnabled(false);
1877 // cancelDAS.setEnabled(true); doDasFetch does this.
1878 Runnable fetcher = new Runnable()
1884 doDasFeatureFetch(dassources, true, false);
1894 SwingUtilities.invokeLater(fetcher);
1899 public void saveDAS_actionPerformed(ActionEvent e)
1902 .saveProperties(jalview.bin.Cache.applicationProperties);
1905 public void complete()
1907 fetchDAS.setEnabled(true);
1908 cancelDAS.setEnabled(false);
1909 dassourceBrowser.setGuiEnabled(true);
1913 public void cancelDAS_actionPerformed(ActionEvent e)
1915 if (dasFeatureFetcher != null)
1917 dasFeatureFetcher.cancel();
1922 public void noDasSourceActive()
1925 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1926 MessageManager.getString("label.no_das_sources_selected_warn"),
1927 MessageManager.getString("label.no_das_sources_selected_title"),
1928 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1932 * Answers true unless a numeric condition has been selected with a
1933 * non-numeric value. Sets the value field to RED with a tooltip if in error.
1935 * If the pattern entered is empty, this method returns false, but does not
1936 * mark the field as invalid. This supports selecting an attribute for a new
1937 * condition before a match pattern has been entered.
1942 protected boolean validateFilter(JTextField value,
1943 JComboBox<Condition> condCombo)
1945 if (value == null || condCombo == null)
1947 return true; // fields not populated
1950 Condition cond = (Condition) condCombo.getSelectedItem();
1951 value.setBackground(Color.white);
1952 value.setToolTipText("");
1953 String v1 = value.getText().trim();
1954 if (v1.length() == 0)
1959 if (cond.isNumeric())
1964 } catch (NumberFormatException e)
1966 value.setBackground(Color.red);
1967 value.setToolTipText(MessageManager
1968 .getString("label.numeric_required"));
1976 // ///////////////////////////////////////////////////////////////////////
1977 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1978 // ///////////////////////////////////////////////////////////////////////
1979 class FeatureTableModel extends AbstractTableModel
1981 FeatureTableModel(Object[][] data)
1986 private String[] columnNames = {
1987 MessageManager.getString("label.feature_type"),
1988 MessageManager.getString("action.colour"),
1989 MessageManager.getString("label.display") };
1991 private Object[][] data;
1993 public Object[][] getData()
1998 public void setData(Object[][] data)
2004 public int getColumnCount()
2006 return columnNames.length;
2009 public Object[] getRow(int row)
2015 public int getRowCount()
2021 public String getColumnName(int col)
2023 return columnNames[col];
2027 public Object getValueAt(int row, int col)
2029 return data[row][col];
2033 public Class getColumnClass(int c)
2035 return getValueAt(0, c).getClass();
2039 public boolean isCellEditable(int row, int col)
2041 return col == 0 ? false : true;
2045 public void setValueAt(Object value, int row, int col)
2047 data[row][col] = value;
2048 fireTableCellUpdated(row, col);
2049 updateFeatureRenderer(data);
2054 class ColorRenderer extends JLabel implements TableCellRenderer
2056 javax.swing.border.Border unselectedBorder = null;
2058 javax.swing.border.Border selectedBorder = null;
2060 final String baseTT = "Click to edit, right/apple click for menu.";
2062 public ColorRenderer()
2064 setOpaque(true); // MUST do this for background to show up.
2065 setHorizontalTextPosition(SwingConstants.CENTER);
2066 setVerticalTextPosition(SwingConstants.CENTER);
2070 public Component getTableCellRendererComponent(JTable tbl, Object color,
2071 boolean isSelected, boolean hasFocus, int row, int column)
2073 FeatureColourI cellColour = (FeatureColourI) color;
2075 setToolTipText(baseTT);
2076 setBackground(tbl.getBackground());
2077 if (!cellColour.isSimpleColour())
2079 Rectangle cr = tbl.getCellRect(row, column, false);
2080 FeatureSettings.renderGraduatedColor(this, cellColour,
2081 (int) cr.getWidth(), (int) cr.getHeight());
2087 setBackground(cellColour.getColour());
2091 if (selectedBorder == null)
2093 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2094 tbl.getSelectionBackground());
2096 setBorder(selectedBorder);
2100 if (unselectedBorder == null)
2102 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2103 tbl.getBackground());
2105 setBorder(unselectedBorder);
2113 * update comp using rendering settings from gcol
2118 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
2120 int w = comp.getWidth(), h = comp.getHeight();
2123 w = (int) comp.getPreferredSize().getWidth();
2124 h = (int) comp.getPreferredSize().getHeight();
2131 renderGraduatedColor(comp, gcol, w, h);
2134 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
2137 boolean thr = false;
2138 StringBuilder tt = new StringBuilder();
2139 StringBuilder tx = new StringBuilder();
2141 if (gcol.isColourByAttribute())
2143 tx.append(String.join(":", gcol.getAttributeName()));
2145 else if (!gcol.isColourByLabel())
2147 tx.append(MessageManager.getString("label.score"));
2150 if (gcol.isAboveThreshold())
2154 tt.append("Thresholded (Above ").append(gcol.getThreshold())
2157 if (gcol.isBelowThreshold())
2161 tt.append("Thresholded (Below ").append(gcol.getThreshold())
2164 if (gcol.isColourByLabel())
2166 tt.append("Coloured by label text. ").append(tt);
2171 if (!gcol.isColourByAttribute())
2179 Color newColor = gcol.getMaxColour();
2180 comp.setBackground(newColor);
2181 // System.err.println("Width is " + w / 2);
2182 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
2183 comp.setIcon(ficon);
2184 // tt+="RGB value: Max (" + newColor.getRed() + ", "
2185 // + newColor.getGreen() + ", " + newColor.getBlue()
2186 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
2187 // + ", " + minCol.getBlue() + ")");
2189 comp.setHorizontalAlignment(SwingConstants.CENTER);
2190 comp.setText(tx.toString());
2191 if (tt.length() > 0)
2193 if (comp.getToolTipText() == null)
2195 comp.setToolTipText(tt.toString());
2199 comp.setToolTipText(tt.append(" ").append(comp.getToolTipText())
2206 class FeatureIcon implements Icon
2208 FeatureColourI gcol;
2212 boolean midspace = false;
2214 int width = 50, height = 20;
2216 int s1, e1; // start and end of midpoint band for thresholded symbol
2218 Color mpcolour = Color.white;
2220 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2240 public int getIconWidth()
2246 public int getIconHeight()
2252 public void paintIcon(Component c, Graphics g, int x, int y)
2255 if (gcol.isColourByLabel())
2258 g.fillRect(0, 0, width, height);
2259 // need an icon here.
2260 g.setColor(gcol.getMaxColour());
2262 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2264 // g.setFont(g.getFont().deriveFont(
2265 // AffineTransform.getScaleInstance(
2266 // width/g.getFontMetrics().stringWidth("Label"),
2267 // height/g.getFontMetrics().getHeight())));
2269 g.drawString(MessageManager.getString("label.label"), 0, 0);
2274 Color minCol = gcol.getMinColour();
2276 g.fillRect(0, 0, s1, height);
2279 g.setColor(Color.white);
2280 g.fillRect(s1, 0, e1 - s1, height);
2282 g.setColor(gcol.getMaxColour());
2283 g.fillRect(0, e1, width - e1, height);
2288 class ColorEditor extends AbstractCellEditor
2289 implements TableCellEditor, ActionListener
2293 FeatureColourI currentColor;
2295 FeatureColourChooser chooser;
2301 JColorChooser colorChooser;
2305 protected static final String EDIT = "edit";
2307 int selectedRow = 0;
2309 public ColorEditor(FeatureSettings me)
2312 // Set up the editor (from the table's point of view),
2313 // which is a button.
2314 // This button brings up the color chooser dialog,
2315 // which is the editor from the user's point of view.
2316 button = new JButton();
2317 button.setActionCommand(EDIT);
2318 button.addActionListener(this);
2319 button.setBorderPainted(false);
2320 // Set up the dialog that the button brings up.
2321 colorChooser = new JColorChooser();
2322 dialog = JColorChooser.createDialog(button,
2323 MessageManager.getString("label.select_new_colour"), true, // modal
2324 colorChooser, this, // OK button handler
2325 null); // no CANCEL button handler
2329 * Handles events from the editor button and from the dialog's OK button.
2332 public void actionPerformed(ActionEvent e)
2335 if (EDIT.equals(e.getActionCommand()))
2337 // The user has clicked the cell, so
2338 // bring up the dialog.
2339 if (currentColor.isSimpleColour())
2341 // bring up simple color chooser
2342 button.setBackground(currentColor.getColour());
2343 colorChooser.setColor(currentColor.getColour());
2344 dialog.setVisible(true);
2348 // bring up graduated chooser.
2349 chooser = new FeatureColourChooser(me.fr, type);
2350 chooser.setRequestFocusEnabled(true);
2351 chooser.requestFocus();
2352 chooser.addActionListener(this);
2354 // Make the renderer reappear.
2355 fireEditingStopped();
2359 { // User pressed dialog's "OK" button.
2360 if (currentColor.isSimpleColour())
2362 currentColor = new FeatureColour(colorChooser.getColor());
2366 currentColor = chooser.getLastColour();
2368 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
2369 fireEditingStopped();
2370 me.table.validate();
2374 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2376 public Object getCellEditorValue()
2378 return currentColor;
2381 // Implement the one method defined by TableCellEditor.
2383 public Component getTableCellEditorComponent(JTable table, Object value,
2384 boolean isSelected, int row, int column)
2386 currentColor = (FeatureColourI) value;
2387 this.selectedRow = row;
2388 type = me.table.getValueAt(row, 0).toString();
2389 button.setOpaque(true);
2390 button.setBackground(me.getBackground());
2391 if (!currentColor.isSimpleColour())
2393 JLabel btn = new JLabel();
2394 btn.setSize(button.getSize());
2395 FeatureSettings.renderGraduatedColor(btn, currentColor);
2396 button.setBackground(btn.getBackground());
2397 button.setIcon(btn.getIcon());
2398 button.setText(btn.getText());
2403 button.setIcon(null);
2404 button.setBackground(currentColor.getColour());