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.ButtonGroup;
88 import javax.swing.Icon;
89 import javax.swing.JButton;
90 import javax.swing.JCheckBox;
91 import javax.swing.JCheckBoxMenuItem;
92 import javax.swing.JColorChooser;
93 import javax.swing.JComboBox;
94 import javax.swing.JDialog;
95 import javax.swing.JInternalFrame;
96 import javax.swing.JLabel;
97 import javax.swing.JLayeredPane;
98 import javax.swing.JMenuItem;
99 import javax.swing.JPanel;
100 import javax.swing.JPopupMenu;
101 import javax.swing.JRadioButton;
102 import javax.swing.JScrollPane;
103 import javax.swing.JSlider;
104 import javax.swing.JTabbedPane;
105 import javax.swing.JTable;
106 import javax.swing.JTextArea;
107 import javax.swing.JTextField;
108 import javax.swing.ListSelectionModel;
109 import javax.swing.SwingConstants;
110 import javax.swing.SwingUtilities;
111 import javax.swing.event.ChangeEvent;
112 import javax.swing.event.ChangeListener;
113 import javax.swing.plaf.basic.BasicArrowButton;
114 import javax.swing.table.AbstractTableModel;
115 import javax.swing.table.TableCellEditor;
116 import javax.swing.table.TableCellRenderer;
118 public class FeatureSettings extends JPanel
119 implements FeatureSettingsControllerI
121 private static final int MIN_WIDTH = 400;
123 private static final int MIN_HEIGHT = 400;
125 DasSourceBrowser dassourceBrowser;
127 DasSequenceFeatureFetcher dasFeatureFetcher;
129 JPanel dasSettingsPane = new JPanel();
131 final FeatureRenderer fr;
133 public final AlignFrame af;
136 * 'original' fields hold settings to restore on Cancel
138 Object[][] originalData;
140 private float originalTransparency;
142 private Map<String, KeyedMatcherSetI> originalFilters;
144 final JInternalFrame frame;
146 JScrollPane scrollPane = new JScrollPane();
152 JSlider transparency = new JSlider();
155 * when true, constructor is still executing - so ignore UI events
157 protected volatile boolean inConstruction = true;
159 int selectedRow = -1;
161 JButton fetchDAS = new JButton();
163 JButton saveDAS = new JButton();
165 JButton cancelDAS = new JButton();
167 boolean resettingTable = false;
170 * true when Feature Settings are updating from feature renderer
172 private boolean handlingUpdate = false;
175 * holds {featureCount, totalExtent} for each feature type
177 Map<String, float[]> typeWidth = null;
180 * fields of the feature filters tab
182 private JPanel filtersPane;
184 private JPanel chooseFiltersPanel;
186 private JComboBox<String> filteredFeatureChoice;
188 private JRadioButton andFilters;
190 private JRadioButton orFilters;
193 * filters for the currently selected feature type
195 private List<KeyedMatcherI> filters;
197 private JTextArea filtersAsText;
204 public FeatureSettings(AlignFrame alignFrame)
206 this.af = alignFrame;
207 fr = af.getFeatureRenderer();
209 // save transparency for restore on Cancel
210 originalTransparency = fr.getTransparency();
211 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
212 transparency.setMaximum(100 - originalTransparencyAsPercent);
214 originalFilters = fr.getFeatureFilters();
219 } catch (Exception ex)
221 ex.printStackTrace();
227 public String getToolTipText(MouseEvent e)
229 if (table.columnAtPoint(e.getPoint()) == 0)
232 * Tooltip for feature name only
234 return JvSwingUtils.wrapTooltip(true, MessageManager
235 .getString("label.feature_settings_click_drag"));
240 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
241 table.setFont(new Font("Verdana", Font.PLAIN, 12));
242 table.setDefaultRenderer(Color.class, new ColorRenderer());
244 table.setDefaultEditor(Color.class, new ColorEditor(this));
246 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
247 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
248 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
250 table.addMouseListener(new MouseAdapter()
253 public void mousePressed(MouseEvent evt)
255 selectedRow = table.rowAtPoint(evt.getPoint());
256 if (evt.isPopupTrigger())
258 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
259 table.getValueAt(selectedRow, 1), fr.getMinMax(),
260 evt.getX(), evt.getY());
262 else if (evt.getClickCount() == 2)
264 boolean invertSelection = evt.isAltDown();
265 boolean toggleSelection = Platform.isControlDown(evt);
266 boolean extendSelection = evt.isShiftDown();
267 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
268 invertSelection, extendSelection, toggleSelection,
269 (String) table.getValueAt(selectedRow, 0));
273 // isPopupTrigger fires on mouseReleased on Windows
275 public void mouseReleased(MouseEvent evt)
277 selectedRow = table.rowAtPoint(evt.getPoint());
278 if (evt.isPopupTrigger())
280 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
281 table.getValueAt(selectedRow, 1), fr.getMinMax(),
282 evt.getX(), evt.getY());
287 table.addMouseMotionListener(new MouseMotionAdapter()
290 public void mouseDragged(MouseEvent evt)
292 int newRow = table.rowAtPoint(evt.getPoint());
293 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
296 * reposition 'selectedRow' to 'newRow' (the dragged to location)
297 * this could be more than one row away for a very fast drag action
298 * so just swap it with adjacent rows until we get it there
300 Object[][] data = ((FeatureTableModel) table.getModel())
302 int direction = newRow < selectedRow ? -1 : 1;
303 for (int i = selectedRow; i != newRow; i += direction)
305 Object[] temp = data[i];
306 data[i] = data[i + direction];
307 data[i + direction] = temp;
309 updateFeatureRenderer(data);
311 selectedRow = newRow;
315 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
316 // MessageManager.getString("label.feature_settings_click_drag")));
317 scrollPane.setViewportView(table);
319 dassourceBrowser = new DasSourceBrowser(this);
320 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
322 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
324 fr.findAllFeatures(true); // display everything!
327 discoverAllFeatureData();
328 final PropertyChangeListener change;
329 final FeatureSettings fs = this;
330 fr.addPropertyChangeListener(change = new PropertyChangeListener()
333 public void propertyChange(PropertyChangeEvent evt)
335 if (!fs.resettingTable && !fs.handlingUpdate)
337 fs.handlingUpdate = true;
338 fs.resetTable(null); // new groups may be added with new seuqence
339 // feature types only
340 fs.handlingUpdate = false;
346 frame = new JInternalFrame();
347 frame.setContentPane(this);
348 if (Platform.isAMac())
350 Desktop.addInternalFrame(frame,
351 MessageManager.getString("label.sequence_feature_settings"),
356 Desktop.addInternalFrame(frame,
357 MessageManager.getString("label.sequence_feature_settings"),
360 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
362 frame.addInternalFrameListener(
363 new javax.swing.event.InternalFrameAdapter()
366 public void internalFrameClosed(
367 javax.swing.event.InternalFrameEvent evt)
369 fr.removePropertyChangeListener(change);
370 dassourceBrowser.fs = null;
373 frame.setLayer(JLayeredPane.PALETTE_LAYER);
374 inConstruction = false;
377 protected void popupSort(final int selectedRow, final String type,
378 final Object typeCol, final Map<String, float[][]> minmax, int x,
381 final FeatureColourI featureColour = (FeatureColourI) typeCol;
383 JPopupMenu men = new JPopupMenu(MessageManager
384 .formatMessage("label.settings_for_param", new String[]
386 JMenuItem scr = new JMenuItem(
387 MessageManager.getString("label.sort_by_score"));
389 final FeatureSettings me = this;
390 scr.addActionListener(new ActionListener()
394 public void actionPerformed(ActionEvent e)
397 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
402 JMenuItem dens = new JMenuItem(
403 MessageManager.getString("label.sort_by_density"));
404 dens.addActionListener(new ActionListener()
408 public void actionPerformed(ActionEvent e)
411 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
419 final float[][] typeMinMax = minmax.get(type);
421 * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
422 * this is broken at the moment and isn't that useful anyway!
423 * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
426 * public void actionPerformed(ActionEvent e) {
427 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
428 * null); } else { minmax.put(type, typeMinMax); } }
434 if (typeMinMax != null && typeMinMax[0] != null)
436 // if (table.getValueAt(row, column));
437 // graduated colourschemes for those where minmax exists for the
438 // positional features
439 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
441 mxcol.setSelected(!featureColour.isSimpleColour());
443 mxcol.addActionListener(new ActionListener()
445 JColorChooser colorChooser;
448 public void actionPerformed(ActionEvent e)
450 if (e.getSource() == mxcol)
452 if (featureColour.isSimpleColour())
454 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
456 fc.addActionListener(this);
460 // bring up simple color chooser
461 colorChooser = new JColorChooser();
462 JDialog dialog = JColorChooser.createDialog(me,
463 "Select new Colour", true, // modal
464 colorChooser, this, // OK button handler
465 null); // no CANCEL button handler
466 colorChooser.setColor(featureColour.getMaxColour());
467 dialog.setVisible(true);
472 if (e.getSource() instanceof FeatureColourChooser)
474 FeatureColourChooser fc = (FeatureColourChooser) e
476 table.setValueAt(fc.getLastColour(), selectedRow, 1);
481 // probably the color chooser!
482 table.setValueAt(new FeatureColour(colorChooser.getColor()),
485 me.updateFeatureRenderer(
486 ((FeatureTableModel) table.getModel()).getData(),
495 JMenuItem selCols = new JMenuItem(
496 MessageManager.getString("label.select_columns_containing"));
497 selCols.addActionListener(new ActionListener()
500 public void actionPerformed(ActionEvent arg0)
502 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
506 JMenuItem clearCols = new JMenuItem(MessageManager
507 .getString("label.select_columns_not_containing"));
508 clearCols.addActionListener(new ActionListener()
511 public void actionPerformed(ActionEvent arg0)
513 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
517 JMenuItem hideCols = new JMenuItem(
518 MessageManager.getString("label.hide_columns_containing"));
519 hideCols.addActionListener(new ActionListener()
522 public void actionPerformed(ActionEvent arg0)
524 fr.ap.alignFrame.hideFeatureColumns(type, true);
527 JMenuItem hideOtherCols = new JMenuItem(
528 MessageManager.getString("label.hide_columns_not_containing"));
529 hideOtherCols.addActionListener(new ActionListener()
532 public void actionPerformed(ActionEvent arg0)
534 fr.ap.alignFrame.hideFeatureColumns(type, false);
540 men.add(hideOtherCols);
541 men.show(table, x, y);
545 synchronized public void discoverAllFeatureData()
547 Set<String> allGroups = new HashSet<>();
548 AlignmentI alignment = af.getViewport().getAlignment();
550 for (int i = 0; i < alignment.getHeight(); i++)
552 SequenceI seq = alignment.getSequenceAt(i);
553 for (String group : seq.getFeatures().getFeatureGroups(true))
555 if (group != null && !allGroups.contains(group))
557 allGroups.add(group);
558 checkGroupState(group);
563 populateFilterableFeatures();
571 * Synchronise gui group list and check visibility of group
574 * @return true if group is visible
576 private boolean checkGroupState(String group)
578 boolean visible = fr.checkGroupVisibility(group, true);
580 for (int g = 0; g < groupPanel.getComponentCount(); g++)
582 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
584 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
589 final String grp = group;
590 final JCheckBox check = new JCheckBox(group, visible);
591 check.setFont(new Font("Serif", Font.BOLD, 12));
592 check.setToolTipText(group);
593 check.addItemListener(new ItemListener()
596 public void itemStateChanged(ItemEvent evt)
598 fr.setGroupVisibility(check.getText(), check.isSelected());
599 resetTable(new String[] { grp });
600 af.alignPanel.paintAlignment(true, true);
603 groupPanel.add(check);
607 synchronized void resetTable(String[] groupChanged)
613 resettingTable = true;
614 typeWidth = new Hashtable<>();
615 // TODO: change avWidth calculation to 'per-sequence' average and use long
618 Set<String> displayableTypes = new HashSet<>();
619 Set<String> foundGroups = new HashSet<>();
622 * determine which feature types may be visible depending on
623 * which groups are selected, and recompute average width data
625 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
628 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
631 * get the sequence's groups for positional features
632 * and keep track of which groups are visible
634 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
635 Set<String> visibleGroups = new HashSet<>();
636 for (String group : groups)
638 if (group == null || checkGroupState(group))
640 visibleGroups.add(group);
643 foundGroups.addAll(groups);
646 * get distinct feature types for visible groups
647 * record distinct visible types, and their count and total length
649 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
650 visibleGroups.toArray(new String[visibleGroups.size()]));
651 for (String type : types)
653 displayableTypes.add(type);
654 float[] avWidth = typeWidth.get(type);
657 avWidth = new float[2];
658 typeWidth.put(type, avWidth);
660 // todo this could include features with a non-visible group
661 // - do we greatly care?
662 // todo should we include non-displayable features here, and only
663 // update when features are added?
664 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
665 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
669 Object[][] data = new Object[displayableTypes.size()][3];
672 if (fr.hasRenderOrder())
676 fr.findAllFeatures(groupChanged != null); // prod to update
677 // colourschemes. but don't
679 // First add the checks in the previous render order,
680 // in case the window has been closed and reopened
682 List<String> frl = fr.getRenderOrder();
683 for (int ro = frl.size() - 1; ro > -1; ro--)
685 String type = frl.get(ro);
687 if (!displayableTypes.contains(type))
692 data[dataIndex][0] = type;
693 data[dataIndex][1] = fr.getFeatureStyle(type);
694 data[dataIndex][2] = new Boolean(
695 af.getViewport().getFeaturesDisplayed().isVisible(type));
697 displayableTypes.remove(type);
702 * process any extra features belonging only to
703 * a group which was just selected
705 while (!displayableTypes.isEmpty())
707 String type = displayableTypes.iterator().next();
708 data[dataIndex][0] = type;
710 data[dataIndex][1] = fr.getFeatureStyle(type);
711 if (data[dataIndex][1] == null)
713 // "Colour has been updated in another view!!"
714 fr.clearRenderOrder();
718 data[dataIndex][2] = new Boolean(true);
720 displayableTypes.remove(type);
723 if (originalData == null)
725 originalData = new Object[data.length][3];
726 for (int i = 0; i < data.length; i++)
728 System.arraycopy(data[i], 0, originalData[i], 0, 3);
733 updateOriginalData(data);
736 table.setModel(new FeatureTableModel(data));
737 table.getColumnModel().getColumn(0).setPreferredWidth(200);
739 groupPanel.setLayout(
740 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
741 pruneGroups(foundGroups);
742 groupPanel.validate();
744 updateFeatureRenderer(data, groupChanged != null);
745 resettingTable = false;
749 * Updates 'originalData' (used for restore on Cancel) if we detect that
750 * changes have been made outwith this dialog
752 * <li>a new feature type added (and made visible)</li>
753 * <li>a feature colour changed (in the Amend Features dialog)</li>
758 protected void updateOriginalData(Object[][] foundData)
760 // todo LinkedHashMap instead of Object[][] would be nice
762 Object[][] currentData = ((FeatureTableModel) table.getModel())
764 for (Object[] row : foundData)
766 String type = (String) row[0];
767 boolean found = false;
768 for (Object[] current : currentData)
770 if (type.equals(current[0]))
774 * currently dependent on object equality here;
775 * really need an equals method on FeatureColour
777 if (!row[1].equals(current[1]))
780 * feature colour has changed externally - update originalData
782 for (Object[] original : originalData)
784 if (type.equals(original[0]))
786 original[1] = row[1];
797 * new feature detected - add to original data (on top)
799 Object[][] newData = new Object[originalData.length + 1][3];
800 for (int i = 0; i < originalData.length; i++)
802 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
805 originalData = newData;
811 * Remove from the groups panel any checkboxes for groups that are not in the
812 * foundGroups set. This enables removing a group from the display when the
813 * last feature in that group is deleted.
817 protected void pruneGroups(Set<String> foundGroups)
819 for (int g = 0; g < groupPanel.getComponentCount(); g++)
821 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
822 if (!foundGroups.contains(checkbox.getText()))
824 groupPanel.remove(checkbox);
830 * reorder data based on the featureRenderers global priority list.
834 private void ensureOrder(Object[][] data)
836 boolean sort = false;
837 float[] order = new float[data.length];
838 for (int i = 0; i < order.length; i++)
840 order[i] = fr.getOrder(data[i][0].toString());
843 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
847 sort = sort || order[i - 1] > order[i];
852 jalview.util.QuickSort.sort(order, data);
858 JalviewFileChooser chooser = new JalviewFileChooser("fc",
859 "Sequence Feature Colours");
860 chooser.setFileView(new JalviewFileView());
861 chooser.setDialogTitle(
862 MessageManager.getString("label.load_feature_colours"));
863 chooser.setToolTipText(MessageManager.getString("action.load"));
865 int value = chooser.showOpenDialog(this);
867 if (value == JalviewFileChooser.APPROVE_OPTION)
869 File file = chooser.getSelectedFile();
873 InputStreamReader in = new InputStreamReader(
874 new FileInputStream(file), "UTF-8");
876 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
878 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
881 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
884 Color mincol = null, maxcol = null;
887 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
888 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
890 } catch (Exception e)
892 Cache.log.warn("Couldn't parse out graduated feature color.",
895 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
896 newcol.getMin(), newcol.getMax());
897 if (newcol.hasAutoScale())
899 gcol.setAutoScaled(newcol.getAutoScale());
901 if (newcol.hasColourByLabel())
903 gcol.setColourByLabel(newcol.getColourByLabel());
905 if (newcol.hasThreshold())
907 gcol.setThreshold(newcol.getThreshold());
909 if (newcol.getThreshType().length() > 0)
911 String ttyp = newcol.getThreshType();
912 if (ttyp.equalsIgnoreCase("ABOVE"))
914 gcol.setAboveThreshold(true);
916 if (ttyp.equalsIgnoreCase("BELOW"))
918 gcol.setBelowThreshold(true);
921 fr.setColour(name = newcol.getName(), gcol);
925 Color color = new Color(
926 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
927 fr.setColour(name = jucs.getColour(i).getName(),
928 new FeatureColour(color));
930 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
935 Object[][] data = ((FeatureTableModel) table.getModel())
938 updateFeatureRenderer(data, false);
941 } catch (Exception ex)
943 System.out.println("Error loading User Colour File\n" + ex);
950 JalviewFileChooser chooser = new JalviewFileChooser("fc",
951 "Sequence Feature Colours");
952 chooser.setFileView(new JalviewFileView());
953 chooser.setDialogTitle(
954 MessageManager.getString("label.save_feature_colours"));
955 chooser.setToolTipText(MessageManager.getString("action.save"));
957 int value = chooser.showSaveDialog(this);
959 if (value == JalviewFileChooser.APPROVE_OPTION)
961 String choice = chooser.getSelectedFile().getPath();
962 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
963 ucs.setSchemeName("Sequence Features");
966 PrintWriter out = new PrintWriter(new OutputStreamWriter(
967 new FileOutputStream(choice), "UTF-8"));
969 Set<String> fr_colours = fr.getAllFeatureColours();
970 Iterator<String> e = fr_colours.iterator();
971 float[] sortOrder = new float[fr_colours.size()];
972 String[] sortTypes = new String[fr_colours.size()];
976 sortTypes[i] = e.next();
977 sortOrder[i] = fr.getOrder(sortTypes[i]);
980 QuickSort.sort(sortOrder, sortTypes);
982 for (i = 0; i < sortTypes.length; i++)
984 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
985 col.setName(sortTypes[i]);
986 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
987 if (fcol.isSimpleColour())
989 col.setRGB(Format.getHexString(fcol.getColour()));
993 col.setRGB(Format.getHexString(fcol.getMaxColour()));
994 col.setMin(fcol.getMin());
995 col.setMax(fcol.getMax());
997 jalview.util.Format.getHexString(fcol.getMinColour()));
998 col.setAutoScale(fcol.isAutoScaled());
999 col.setThreshold(fcol.getThreshold());
1000 col.setColourByLabel(fcol.isColourByLabel());
1001 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
1002 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
1008 } catch (Exception ex)
1010 ex.printStackTrace();
1015 public void invertSelection()
1017 for (int i = 0; i < table.getRowCount(); i++)
1019 Boolean value = (Boolean) table.getValueAt(i, 2);
1021 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
1025 public void orderByAvWidth()
1027 if (table == null || table.getModel() == null)
1031 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1032 float[] width = new float[data.length];
1036 for (int i = 0; i < data.length; i++)
1038 awidth = typeWidth.get(data[i][0]);
1041 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1042 // weight - but have to make per
1043 // sequence, too (awidth[2])
1044 // if (width[i]==1) // hack to distinguish single width sequences.
1056 boolean sort = false;
1057 for (int i = 0; i < width.length; i++)
1059 // awidth = (float[]) typeWidth.get(data[i][0]);
1062 width[i] = fr.getOrder(data[i][0].toString());
1065 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1070 width[i] /= max; // normalize
1071 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1075 sort = sort || width[i - 1] > width[i];
1080 jalview.util.QuickSort.sort(width, data);
1081 // update global priority order
1084 updateFeatureRenderer(data, false);
1092 frame.setClosed(true);
1093 } catch (Exception exe)
1099 public void updateFeatureRenderer(Object[][] data)
1101 updateFeatureRenderer(data, true);
1105 * Update the priority order of features; only repaint if this changed the
1106 * order of visible features
1111 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1113 if (fr.setFeaturePriority(data, visibleNew))
1115 af.alignPanel.paintAlignment(true, true);
1119 private void jbInit() throws Exception
1121 this.setLayout(new BorderLayout());
1123 JPanel settingsPane = new JPanel();
1124 settingsPane.setLayout(new BorderLayout());
1126 filtersPane = new JPanel();
1128 dasSettingsPane.setLayout(new BorderLayout());
1130 JPanel bigPanel = new JPanel();
1131 bigPanel.setLayout(new BorderLayout());
1133 groupPanel = new JPanel();
1134 bigPanel.add(groupPanel, BorderLayout.NORTH);
1136 JButton invert = new JButton(
1137 MessageManager.getString("label.invert_selection"));
1138 invert.setFont(JvSwingUtils.getLabelFont());
1139 invert.addActionListener(new ActionListener()
1142 public void actionPerformed(ActionEvent e)
1148 JButton optimizeOrder = new JButton(
1149 MessageManager.getString("label.optimise_order"));
1150 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1151 optimizeOrder.addActionListener(new ActionListener()
1154 public void actionPerformed(ActionEvent e)
1160 JButton sortByScore = new JButton(
1161 MessageManager.getString("label.seq_sort_by_score"));
1162 sortByScore.setFont(JvSwingUtils.getLabelFont());
1163 sortByScore.addActionListener(new ActionListener()
1166 public void actionPerformed(ActionEvent e)
1168 af.avc.sortAlignmentByFeatureScore(null);
1171 JButton sortByDens = new JButton(
1172 MessageManager.getString("label.sequence_sort_by_density"));
1173 sortByDens.setFont(JvSwingUtils.getLabelFont());
1174 sortByDens.addActionListener(new ActionListener()
1177 public void actionPerformed(ActionEvent e)
1179 af.avc.sortAlignmentByFeatureDensity(null);
1183 JButton help = new JButton(MessageManager.getString("action.help"));
1184 help.setFont(JvSwingUtils.getLabelFont());
1185 help.addActionListener(new ActionListener()
1188 public void actionPerformed(ActionEvent e)
1192 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1193 } catch (HelpSetException e1)
1195 e1.printStackTrace();
1199 help.setFont(JvSwingUtils.getLabelFont());
1200 help.setText(MessageManager.getString("action.help"));
1201 help.addActionListener(new ActionListener()
1204 public void actionPerformed(ActionEvent e)
1208 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1209 } catch (HelpSetException e1)
1211 e1.printStackTrace();
1216 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1217 cancel.setFont(JvSwingUtils.getLabelFont());
1218 cancel.addActionListener(new ActionListener()
1221 public void actionPerformed(ActionEvent e)
1223 fr.setTransparency(originalTransparency);
1224 fr.setFeatureFilters(originalFilters);
1225 updateFeatureRenderer(originalData);
1230 JButton ok = new JButton(MessageManager.getString("action.ok"));
1231 ok.setFont(JvSwingUtils.getLabelFont());
1232 ok.addActionListener(new ActionListener()
1235 public void actionPerformed(ActionEvent e)
1241 JButton loadColours = new JButton(
1242 MessageManager.getString("label.load_colours"));
1243 loadColours.setFont(JvSwingUtils.getLabelFont());
1244 loadColours.addActionListener(new ActionListener()
1247 public void actionPerformed(ActionEvent e)
1253 JButton saveColours = new JButton(
1254 MessageManager.getString("label.save_colours"));
1255 saveColours.setFont(JvSwingUtils.getLabelFont());
1256 saveColours.addActionListener(new ActionListener()
1259 public void actionPerformed(ActionEvent e)
1264 transparency.addChangeListener(new ChangeListener()
1267 public void stateChanged(ChangeEvent evt)
1269 if (!inConstruction)
1271 fr.setTransparency((100 - transparency.getValue()) / 100f);
1272 af.alignPanel.paintAlignment(true,true);
1277 transparency.setMaximum(70);
1278 transparency.setToolTipText(
1279 MessageManager.getString("label.transparency_tip"));
1280 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1281 fetchDAS.addActionListener(new ActionListener()
1284 public void actionPerformed(ActionEvent e)
1286 fetchDAS_actionPerformed(e);
1289 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1290 saveDAS.addActionListener(new ActionListener()
1293 public void actionPerformed(ActionEvent e)
1295 saveDAS_actionPerformed(e);
1299 JPanel dasButtonPanel = new JPanel();
1300 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1301 dasSettingsPane.setBorder(null);
1302 cancelDAS.setEnabled(false);
1303 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1304 cancelDAS.addActionListener(new ActionListener()
1307 public void actionPerformed(ActionEvent e)
1309 cancelDAS_actionPerformed(e);
1313 JTabbedPane tabbedPane = new JTabbedPane();
1314 this.add(tabbedPane, BorderLayout.CENTER);
1315 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1317 tabbedPane.addTab(MessageManager.getString("label.filters"),
1319 // tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1320 // dasSettingsPane);
1322 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1323 bigPanel.add(transPanel, BorderLayout.SOUTH);
1325 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1326 transbuttons.add(optimizeOrder);
1327 transbuttons.add(invert);
1328 transbuttons.add(sortByScore);
1329 transbuttons.add(sortByDens);
1330 transbuttons.add(help);
1331 transPanel.add(transparency);
1332 transPanel.add(transbuttons);
1334 JPanel buttonPanel = new JPanel();
1335 buttonPanel.add(ok);
1336 buttonPanel.add(cancel);
1337 buttonPanel.add(loadColours);
1338 buttonPanel.add(saveColours);
1339 bigPanel.add(scrollPane, BorderLayout.CENTER);
1340 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1341 dasButtonPanel.add(fetchDAS);
1342 dasButtonPanel.add(cancelDAS);
1343 dasButtonPanel.add(saveDAS);
1344 settingsPane.add(bigPanel, BorderLayout.CENTER);
1345 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1351 * Populates initial layout of the feature attribute filters panel
1353 protected void initFiltersTab()
1355 filters = new ArrayList<>();
1358 * choose feature type
1360 JPanel chooseTypePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1361 chooseTypePanel.setBackground(Color.white);
1362 chooseTypePanel.setBorder(BorderFactory
1363 .createTitledBorder(MessageManager
1364 .getString("label.feature_type")));
1365 filteredFeatureChoice = new JComboBox<>();
1366 filteredFeatureChoice.addItemListener(new ItemListener()
1369 public void itemStateChanged(ItemEvent e)
1371 refreshFiltersDisplay();
1374 chooseTypePanel.add(new JLabel(MessageManager
1375 .getString("label.feature_to_filter")));
1376 chooseTypePanel.add(filteredFeatureChoice);
1377 populateFilterableFeatures();
1380 * the panel with the filters for the selected feature type
1382 JPanel filtersPanel = new JPanel(new GridLayout(0, 1));
1383 filtersPanel.setBackground(Color.white);
1384 filtersPanel.setBorder(BorderFactory
1385 .createTitledBorder(MessageManager.getString("label.filters")));
1388 * add AND or OR radio buttons
1390 JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1391 andOrPanel.setBackground(Color.white);
1392 andFilters = new JRadioButton("And");
1393 orFilters = new JRadioButton("Or");
1394 ActionListener actionListener = new ActionListener()
1397 public void actionPerformed(ActionEvent e)
1402 andFilters.addActionListener(actionListener);
1403 orFilters.addActionListener(actionListener);
1404 ButtonGroup andOr = new ButtonGroup();
1405 andOr.add(andFilters);
1406 andOr.add(orFilters);
1407 andFilters.setSelected(true);
1408 andOrPanel.add(new JLabel(MessageManager
1409 .getString("label.join_conditions")));
1410 andOrPanel.add(andFilters);
1411 andOrPanel.add(orFilters);
1412 filtersPanel.add(andOrPanel);
1415 * panel with filters - populated by refreshFiltersDisplay
1417 chooseFiltersPanel = new JPanel(new GridLayout(0, 1));
1418 filtersPanel.add(chooseFiltersPanel);
1421 * a read-only text view of the current filters
1423 JPanel showFiltersPanel = new JPanel(new BorderLayout(5, 5));
1424 showFiltersPanel.setBackground(Color.white);
1425 showFiltersPanel.setBorder(BorderFactory
1426 .createTitledBorder(MessageManager
1427 .getString("label.match_condition")));
1428 filtersAsText = new JTextArea();
1429 filtersAsText.setLineWrap(true);
1430 filtersAsText.setWrapStyleWord(true);
1431 showFiltersPanel.add(filtersAsText);
1433 filtersPane.setLayout(new BorderLayout());
1434 filtersPane.add(chooseTypePanel, BorderLayout.NORTH);
1435 filtersPane.add(filtersPanel, BorderLayout.CENTER);
1436 filtersPane.add(showFiltersPanel, BorderLayout.SOUTH);
1439 * update display for initial feature type selection
1441 refreshFiltersDisplay();
1445 * Adds entries to the 'choose feature to filter' drop-down choice. Only
1446 * feature types which have known attributes (so can be filtered) are
1447 * included, so recall this method to update the list (check for newly added
1450 protected void populateFilterableFeatures()
1453 * suppress action handler while updating the list
1455 ItemListener listener = filteredFeatureChoice.getItemListeners()[0];
1456 filteredFeatureChoice.removeItemListener(listener);
1458 filteredFeatureChoice.removeAllItems();
1459 ReverseListIterator<String> types = new ReverseListIterator<>(
1460 fr.getRenderOrder());
1462 boolean found = false;
1463 while (types.hasNext())
1465 String type = types.next();
1466 if (FeatureAttributes.getInstance().hasAttributes(type))
1468 filteredFeatureChoice.addItem(type);
1474 filteredFeatureChoice
1475 .addItem("No filterable feature attributes known");
1478 filteredFeatureChoice.addItemListener(listener);
1483 * Refreshes the display to show any filters currently configured for the
1484 * selected feature type (editable, with 'remove' option), plus one extra row
1485 * for adding a condition. This should be called on change of selected feature
1486 * type, or after a filter has been removed, added or amended.
1488 protected void refreshFiltersDisplay()
1491 * clear the panel and list of filter conditions
1493 chooseFiltersPanel.removeAll();
1495 String selectedType = (String) filteredFeatureChoice.getSelectedItem();
1500 * look up attributes known for feature type
1502 List<String> attNames = FeatureAttributes.getInstance().getAttributes(
1506 * if this feature type has filters set, load them first
1508 KeyedMatcherSetI featureFilters = fr.getFeatureFilter(selectedType);
1509 filtersAsText.setText("");
1510 if (featureFilters != null)
1512 filtersAsText.setText(featureFilters.toString());
1513 if (!featureFilters.isAnded())
1515 orFilters.setSelected(true);
1517 Iterator<KeyedMatcherI> matchers = featureFilters.getMatchers();
1518 while (matchers.hasNext())
1520 filters.add(matchers.next());
1525 * and an empty filter for the user to populate (add)
1527 KeyedMatcherI noFilter = new KeyedMatcher("", Condition.values()[0], "");
1528 filters.add(noFilter);
1531 * render the conditions in rows, each in its own JPanel
1534 for (KeyedMatcherI filter : filters)
1536 String key = filter.getKey();
1537 Condition condition = filter.getMatcher()
1539 String pattern = filter.getMatcher().getPattern();
1540 JPanel row = addFilter(key, attNames, condition, pattern, i);
1541 chooseFiltersPanel.add(row);
1545 filtersPane.validate();
1546 filtersPane.repaint();
1550 * A helper method that constructs a panel with one filter condition:
1552 * <li>a drop-down list of attribute names to choose from</li>
1553 * <li>a drop-down list of conditions to choose from</li>
1554 * <li>a text field for input of a match pattern</li>
1555 * <li>optionally, a 'remove' button</li>
1557 * If attribute, condition or pattern are not null, they are set as defaults
1558 * for the input fields. The 'remove' button is added unless the pattern is
1559 * null or empty (incomplete filter condition).
1565 * @param filterIndex
1568 protected JPanel addFilter(String attribute, List<String> attNames,
1569 Condition cond, String pattern, int filterIndex)
1571 JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
1572 filterRow.setBackground(Color.white);
1575 * inputs for attribute, condition, pattern
1577 final JComboBox<String> attCombo = new JComboBox<>();
1578 JComboBox<Condition> condCombo = new JComboBox<>();
1579 JTextField patternField = new JTextField(8);
1582 * action handlers that validate and (if valid) apply changes
1584 ActionListener actionListener = new ActionListener()
1587 public void actionPerformed(ActionEvent e)
1589 if (attCombo.getSelectedItem() != null)
1591 if (validateFilter(patternField, condCombo))
1593 updateFilter(attCombo, condCombo, patternField, filterIndex);
1599 ItemListener itemListener = new ItemListener()
1602 public void itemStateChanged(ItemEvent e)
1604 actionListener.actionPerformed(null);
1609 * drop-down choice of attribute, with description as a tooltip
1610 * if we can obtain it
1612 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1613 populateAttributesDropdown(attCombo, featureType, attNames);
1614 if ("".equals(attribute))
1616 attCombo.setSelectedItem(null);
1620 attCombo.setSelectedItem(attribute);
1622 attCombo.addItemListener(itemListener);
1624 filterRow.add(attCombo);
1627 * drop-down choice of test condition
1629 for (Condition c : Condition.values())
1631 condCombo.addItem(c);
1635 condCombo.setSelectedItem(cond);
1637 condCombo.addItemListener(itemListener);
1638 filterRow.add(condCombo);
1641 * pattern to match against
1643 patternField.setText(pattern);
1644 patternField.addActionListener(actionListener);
1645 patternField.addFocusListener(new FocusAdapter()
1648 public void focusLost(FocusEvent e)
1650 actionListener.actionPerformed(null);
1653 filterRow.add(patternField);
1656 * add remove button if filter is populated (non-empty pattern)
1658 if (pattern != null && pattern.trim().length() > 0)
1660 // todo: gif for - button
1661 JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
1662 removeCondition.setToolTipText(MessageManager
1663 .getString("label.delete_row"));
1664 removeCondition.addActionListener(new ActionListener()
1667 public void actionPerformed(ActionEvent e)
1669 filters.remove(filterIndex);
1673 filterRow.add(removeCondition);
1680 * A helper method to build the drop-down choice of attributes for a feature.
1681 * Where metadata is available with a description for an attribute, that is
1682 * added as a tooltip.
1685 * @param featureType
1688 protected void populateAttributesDropdown(
1689 final JComboBox<String> attCombo, String featureType,
1690 List<String> attNames)
1692 final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
1693 attCombo.setRenderer(renderer);
1694 List<String> tips = new ArrayList<String>();
1695 if (attNames.isEmpty())
1697 attCombo.addItem("---");
1698 attCombo.setToolTipText(MessageManager
1699 .getString("label.no_attributes_known"));
1703 attCombo.setToolTipText("");
1704 FeatureAttributes fs = FeatureAttributes.getInstance();
1705 for (String attName : attNames)
1707 attCombo.addItem(attName);
1708 String desc = fs.getDescription(featureType, attName);
1709 tips.add(desc == null ? "" : desc);
1712 renderer.setTooltips(tips);
1713 final MouseAdapter mouseListener = new MouseAdapter()
1716 public void mouseEntered(MouseEvent e)
1718 int j = attCombo.getSelectedIndex();
1721 attCombo.setToolTipText(tips.get(j));
1726 public void mouseExited(MouseEvent e)
1728 attCombo.setToolTipText(null);
1731 for (Component c : attCombo.getComponents())
1733 c.addMouseListener(mouseListener);
1738 * Action on any change to feature filtering, namely
1740 * <li>change of selected attribute</li>
1741 * <li>change of selected condition</li>
1742 * <li>change of match pattern</li>
1743 * <li>removal of a condition</li>
1745 * The action should be to
1747 * <li>parse and validate the filters</li>
1748 * <li>if valid, update the filter text box</li>
1749 * <li>and apply the filters to the viewport</li>
1752 protected void filtersChanged()
1755 * update the filter conditions for the feature type
1757 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1758 boolean anded = andFilters.isSelected();
1759 KeyedMatcherSetI combined = new KeyedMatcherSet();
1761 for (KeyedMatcherI filter : filters)
1763 String pattern = filter.getMatcher().getPattern();
1764 if (pattern.trim().length() > 0)
1768 combined.and(filter);
1772 combined.or(filter);
1778 * save the filter conditions in the FeatureRenderer
1779 * (note this might now be an empty filter with no conditions)
1781 fr.setFeatureFilter(featureType, combined);
1783 filtersAsText.setText(combined.toString());
1785 refreshFiltersDisplay();
1787 af.alignPanel.paintAlignment(true, true);
1791 * Constructs a filter condition from the given input fields, and replaces the
1792 * condition at filterIndex with the new one
1797 * @param filterIndex
1799 protected void updateFilter(JComboBox<String> attCombo,
1800 JComboBox<Condition> condCombo, JTextField valueField,
1803 String attName = (String) attCombo.getSelectedItem();
1804 Condition cond = (Condition) condCombo.getSelectedItem();
1805 String pattern = valueField.getText();
1806 KeyedMatcherI km = new KeyedMatcher(attName, cond, pattern);
1808 filters.set(filterIndex, km);
1811 public void fetchDAS_actionPerformed(ActionEvent e)
1813 fetchDAS.setEnabled(false);
1814 cancelDAS.setEnabled(true);
1815 dassourceBrowser.setGuiEnabled(false);
1816 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1817 .getSelectedSources();
1818 doDasFeatureFetch(selectedSources, true, true);
1822 * get the features from selectedSources for all or the current selection
1824 * @param selectedSources
1825 * @param checkDbRefs
1826 * @param promptFetchDbRefs
1828 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1829 boolean checkDbRefs, boolean promptFetchDbRefs)
1831 SequenceI[] dataset, seqs;
1833 AlignmentViewport vp = af.getViewport();
1834 if (vp.getSelectionGroup() != null
1835 && vp.getSelectionGroup().getSize() > 0)
1837 iSize = vp.getSelectionGroup().getSize();
1838 dataset = new SequenceI[iSize];
1839 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1843 iSize = vp.getAlignment().getHeight();
1844 seqs = vp.getAlignment().getSequencesArray();
1847 dataset = new SequenceI[iSize];
1848 for (int i = 0; i < iSize; i++)
1850 dataset[i] = seqs[i].getDatasetSequence();
1853 cancelDAS.setEnabled(true);
1854 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1855 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1856 af.getViewport().setShowSequenceFeatures(true);
1857 af.showSeqFeatures.setSelected(true);
1861 * blocking call to initialise the das source browser
1863 public void initDasSources()
1865 dassourceBrowser.initDasSources();
1869 * examine the current list of das sources and return any matching the given
1870 * nicknames in sources
1873 * Vector of Strings to resolve to DAS source nicknames.
1874 * @return sources that are present in source list.
1876 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1878 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1882 * get currently selected das sources. ensure you have called initDasSources
1883 * before calling this.
1885 * @return vector of selected das source nicknames
1887 public Vector<jalviewSourceI> getSelectedSources()
1889 return dassourceBrowser.getSelectedSources();
1893 * properly initialise DAS fetcher and then initiate a new thread to fetch
1894 * features from the named sources (rather than any turned on by default)
1898 * if true then runs in same thread, otherwise passes to the Swing
1901 public void fetchDasFeatures(Vector<String> sources, boolean block)
1904 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1905 .resolveSourceNicknames(sources);
1906 if (resolved.size() == 0)
1908 resolved = dassourceBrowser.getSelectedSources();
1910 if (resolved.size() > 0)
1912 final List<jalviewSourceI> dassources = resolved;
1913 fetchDAS.setEnabled(false);
1914 // cancelDAS.setEnabled(true); doDasFetch does this.
1915 Runnable fetcher = new Runnable()
1921 doDasFeatureFetch(dassources, true, false);
1931 SwingUtilities.invokeLater(fetcher);
1936 public void saveDAS_actionPerformed(ActionEvent e)
1939 .saveProperties(jalview.bin.Cache.applicationProperties);
1942 public void complete()
1944 fetchDAS.setEnabled(true);
1945 cancelDAS.setEnabled(false);
1946 dassourceBrowser.setGuiEnabled(true);
1950 public void cancelDAS_actionPerformed(ActionEvent e)
1952 if (dasFeatureFetcher != null)
1954 dasFeatureFetcher.cancel();
1959 public void noDasSourceActive()
1962 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1963 MessageManager.getString("label.no_das_sources_selected_warn"),
1964 MessageManager.getString("label.no_das_sources_selected_title"),
1965 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1969 * Answers true unless a numeric condition has been selected with a
1970 * non-numeric value. Sets the value field to RED with a tooltip if in error.
1972 * If the pattern entered is empty, this method returns false, but does not
1973 * mark the field as invalid. This supports selecting an attribute for a new
1974 * condition before a match pattern has been entered.
1979 protected boolean validateFilter(JTextField value,
1980 JComboBox<Condition> condCombo)
1982 if (value == null || condCombo == null)
1984 return true; // fields not populated
1987 Condition cond = (Condition) condCombo.getSelectedItem();
1988 value.setBackground(Color.white);
1989 value.setToolTipText("");
1990 String v1 = value.getText().trim();
1991 if (v1.length() == 0)
1996 if (cond.isNumeric())
2001 } catch (NumberFormatException e)
2003 value.setBackground(Color.red);
2004 value.setToolTipText(MessageManager
2005 .getString("label.numeric_required"));
2013 // ///////////////////////////////////////////////////////////////////////
2014 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
2015 // ///////////////////////////////////////////////////////////////////////
2016 class FeatureTableModel extends AbstractTableModel
2018 FeatureTableModel(Object[][] data)
2023 private String[] columnNames = {
2024 MessageManager.getString("label.feature_type"),
2025 MessageManager.getString("action.colour"),
2026 MessageManager.getString("label.display") };
2028 private Object[][] data;
2030 public Object[][] getData()
2035 public void setData(Object[][] data)
2041 public int getColumnCount()
2043 return columnNames.length;
2046 public Object[] getRow(int row)
2052 public int getRowCount()
2058 public String getColumnName(int col)
2060 return columnNames[col];
2064 public Object getValueAt(int row, int col)
2066 return data[row][col];
2070 public Class getColumnClass(int c)
2072 return getValueAt(0, c).getClass();
2076 public boolean isCellEditable(int row, int col)
2078 return col == 0 ? false : true;
2082 public void setValueAt(Object value, int row, int col)
2084 data[row][col] = value;
2085 fireTableCellUpdated(row, col);
2086 updateFeatureRenderer(data);
2091 class ColorRenderer extends JLabel implements TableCellRenderer
2093 javax.swing.border.Border unselectedBorder = null;
2095 javax.swing.border.Border selectedBorder = null;
2097 final String baseTT = "Click to edit, right/apple click for menu.";
2099 public ColorRenderer()
2101 setOpaque(true); // MUST do this for background to show up.
2102 setHorizontalTextPosition(SwingConstants.CENTER);
2103 setVerticalTextPosition(SwingConstants.CENTER);
2107 public Component getTableCellRendererComponent(JTable tbl, Object color,
2108 boolean isSelected, boolean hasFocus, int row, int column)
2110 FeatureColourI cellColour = (FeatureColourI) color;
2111 // JLabel comp = new JLabel();
2115 // setBounds(getBounds());
2117 setToolTipText(baseTT);
2118 setBackground(tbl.getBackground());
2119 if (!cellColour.isSimpleColour())
2121 Rectangle cr = tbl.getCellRect(row, column, false);
2122 FeatureSettings.renderGraduatedColor(this, cellColour,
2123 (int) cr.getWidth(), (int) cr.getHeight());
2130 newColor = cellColour.getColour();
2131 setBackground(newColor);
2135 if (selectedBorder == null)
2137 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2138 tbl.getSelectionBackground());
2140 setBorder(selectedBorder);
2144 if (unselectedBorder == null)
2146 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2147 tbl.getBackground());
2149 setBorder(unselectedBorder);
2157 * update comp using rendering settings from gcol
2162 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
2164 int w = comp.getWidth(), h = comp.getHeight();
2167 w = (int) comp.getPreferredSize().getWidth();
2168 h = (int) comp.getPreferredSize().getHeight();
2175 renderGraduatedColor(comp, gcol, w, h);
2178 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
2181 boolean thr = false;
2184 if (gcol.isAboveThreshold())
2188 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
2190 if (gcol.isBelowThreshold())
2194 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
2196 if (gcol.isColourByLabel())
2198 tt = "Coloured by label text. " + tt;
2208 Color newColor = gcol.getMaxColour();
2209 comp.setBackground(newColor);
2210 // System.err.println("Width is " + w / 2);
2211 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
2212 comp.setIcon(ficon);
2213 // tt+="RGB value: Max (" + newColor.getRed() + ", "
2214 // + newColor.getGreen() + ", " + newColor.getBlue()
2215 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
2216 // + ", " + minCol.getBlue() + ")");
2218 comp.setHorizontalAlignment(SwingConstants.CENTER);
2220 if (tt.length() > 0)
2222 if (comp.getToolTipText() == null)
2224 comp.setToolTipText(tt);
2228 comp.setToolTipText(tt + " " + comp.getToolTipText());
2234 class FeatureIcon implements Icon
2236 FeatureColourI gcol;
2240 boolean midspace = false;
2242 int width = 50, height = 20;
2244 int s1, e1; // start and end of midpoint band for thresholded symbol
2246 Color mpcolour = Color.white;
2248 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2268 public int getIconWidth()
2274 public int getIconHeight()
2280 public void paintIcon(Component c, Graphics g, int x, int y)
2283 if (gcol.isColourByLabel())
2286 g.fillRect(0, 0, width, height);
2287 // need an icon here.
2288 g.setColor(gcol.getMaxColour());
2290 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2292 // g.setFont(g.getFont().deriveFont(
2293 // AffineTransform.getScaleInstance(
2294 // width/g.getFontMetrics().stringWidth("Label"),
2295 // height/g.getFontMetrics().getHeight())));
2297 g.drawString(MessageManager.getString("label.label"), 0, 0);
2302 Color minCol = gcol.getMinColour();
2304 g.fillRect(0, 0, s1, height);
2307 g.setColor(Color.white);
2308 g.fillRect(s1, 0, e1 - s1, height);
2310 g.setColor(gcol.getMaxColour());
2311 g.fillRect(0, e1, width - e1, height);
2316 class ColorEditor extends AbstractCellEditor
2317 implements TableCellEditor, ActionListener
2321 FeatureColourI currentColor;
2323 FeatureColourChooser chooser;
2329 JColorChooser colorChooser;
2333 protected static final String EDIT = "edit";
2335 int selectedRow = 0;
2337 public ColorEditor(FeatureSettings me)
2340 // Set up the editor (from the table's point of view),
2341 // which is a button.
2342 // This button brings up the color chooser dialog,
2343 // which is the editor from the user's point of view.
2344 button = new JButton();
2345 button.setActionCommand(EDIT);
2346 button.addActionListener(this);
2347 button.setBorderPainted(false);
2348 // Set up the dialog that the button brings up.
2349 colorChooser = new JColorChooser();
2350 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
2351 colorChooser, this, // OK button handler
2352 null); // no CANCEL button handler
2356 * Handles events from the editor button and from the dialog's OK button.
2359 public void actionPerformed(ActionEvent e)
2362 if (EDIT.equals(e.getActionCommand()))
2364 // The user has clicked the cell, so
2365 // bring up the dialog.
2366 if (currentColor.isSimpleColour())
2368 // bring up simple color chooser
2369 button.setBackground(currentColor.getColour());
2370 colorChooser.setColor(currentColor.getColour());
2371 dialog.setVisible(true);
2375 // bring up graduated chooser.
2376 chooser = new FeatureColourChooser(me.fr, type);
2377 chooser.setRequestFocusEnabled(true);
2378 chooser.requestFocus();
2379 chooser.addActionListener(this);
2381 // Make the renderer reappear.
2382 fireEditingStopped();
2386 { // User pressed dialog's "OK" button.
2387 if (currentColor.isSimpleColour())
2389 currentColor = new FeatureColour(colorChooser.getColor());
2393 currentColor = chooser.getLastColour();
2395 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
2396 fireEditingStopped();
2397 me.table.validate();
2401 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2403 public Object getCellEditorValue()
2405 return currentColor;
2408 // Implement the one method defined by TableCellEditor.
2410 public Component getTableCellEditorComponent(JTable table, Object value,
2411 boolean isSelected, int row, int column)
2413 currentColor = (FeatureColourI) value;
2414 this.selectedRow = row;
2415 type = me.table.getValueAt(row, 0).toString();
2416 button.setOpaque(true);
2417 button.setBackground(me.getBackground());
2418 if (!currentColor.isSimpleColour())
2420 JLabel btn = new JLabel();
2421 btn.setSize(button.getSize());
2422 FeatureSettings.renderGraduatedColor(btn, currentColor);
2423 button.setBackground(btn.getBackground());
2424 button.setIcon(btn.getIcon());
2425 button.setText(btn.getText());
2430 button.setIcon(null);
2431 button.setBackground(currentColor.getColour());