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 int MIN_WIDTH = 400;
125 private static final int MIN_HEIGHT = 400;
127 private static final int MAX_TOOLTIP_LENGTH = 50;
129 DasSourceBrowser dassourceBrowser;
131 DasSequenceFeatureFetcher dasFeatureFetcher;
133 JPanel dasSettingsPane = new JPanel();
135 final FeatureRenderer fr;
137 public final AlignFrame af;
140 * 'original' fields hold settings to restore on Cancel
142 Object[][] originalData;
144 private float originalTransparency;
146 private Map<String, KeyedMatcherSetI> originalFilters;
148 final JInternalFrame frame;
150 JScrollPane scrollPane = new JScrollPane();
156 JSlider transparency = new JSlider();
159 * when true, constructor is still executing - so ignore UI events
161 protected volatile boolean inConstruction = true;
163 int selectedRow = -1;
165 JButton fetchDAS = new JButton();
167 JButton saveDAS = new JButton();
169 JButton cancelDAS = new JButton();
171 boolean resettingTable = false;
174 * true when Feature Settings are updating from feature renderer
176 private boolean handlingUpdate = false;
179 * holds {featureCount, totalExtent} for each feature type
181 Map<String, float[]> typeWidth = null;
184 * fields of the feature filters tab
186 private JPanel filtersPane;
188 private JPanel chooseFiltersPanel;
190 private JComboBox<String> filteredFeatureChoice;
192 private JRadioButton andFilters;
194 private JRadioButton orFilters;
197 * filters for the currently selected feature type
199 private List<KeyedMatcherI> filters;
201 private JTextArea filtersAsText;
203 // set white normally, black to debug layout
204 private Color debugBorderColour = Color.white;
211 public FeatureSettings(AlignFrame alignFrame)
213 this.af = alignFrame;
214 fr = af.getFeatureRenderer();
216 // save transparency for restore on Cancel
217 originalTransparency = fr.getTransparency();
218 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
219 transparency.setMaximum(100 - originalTransparencyAsPercent);
221 originalFilters = fr.getFeatureFilters();
226 } catch (Exception ex)
228 ex.printStackTrace();
234 public String getToolTipText(MouseEvent e)
236 if (table.columnAtPoint(e.getPoint()) == 0)
239 * Tooltip for feature name only
241 return JvSwingUtils.wrapTooltip(true, MessageManager
242 .getString("label.feature_settings_click_drag"));
247 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
248 table.setFont(new Font("Verdana", Font.PLAIN, 12));
249 table.setDefaultRenderer(Color.class, new ColorRenderer());
251 table.setDefaultEditor(Color.class, new ColorEditor(this));
253 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
254 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
255 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
257 table.addMouseListener(new MouseAdapter()
260 public void mousePressed(MouseEvent evt)
262 selectedRow = table.rowAtPoint(evt.getPoint());
263 if (evt.isPopupTrigger())
265 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
266 table.getValueAt(selectedRow, 1), fr.getMinMax(),
267 evt.getX(), evt.getY());
269 else if (evt.getClickCount() == 2)
271 boolean invertSelection = evt.isAltDown();
272 boolean toggleSelection = Platform.isControlDown(evt);
273 boolean extendSelection = evt.isShiftDown();
274 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
275 invertSelection, extendSelection, toggleSelection,
276 (String) table.getValueAt(selectedRow, 0));
280 // isPopupTrigger fires on mouseReleased on Windows
282 public void mouseReleased(MouseEvent evt)
284 selectedRow = table.rowAtPoint(evt.getPoint());
285 if (evt.isPopupTrigger())
287 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
288 table.getValueAt(selectedRow, 1), fr.getMinMax(),
289 evt.getX(), evt.getY());
294 table.addMouseMotionListener(new MouseMotionAdapter()
297 public void mouseDragged(MouseEvent evt)
299 int newRow = table.rowAtPoint(evt.getPoint());
300 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
303 * reposition 'selectedRow' to 'newRow' (the dragged to location)
304 * this could be more than one row away for a very fast drag action
305 * so just swap it with adjacent rows until we get it there
307 Object[][] data = ((FeatureTableModel) table.getModel())
309 int direction = newRow < selectedRow ? -1 : 1;
310 for (int i = selectedRow; i != newRow; i += direction)
312 Object[] temp = data[i];
313 data[i] = data[i + direction];
314 data[i + direction] = temp;
316 updateFeatureRenderer(data);
318 selectedRow = newRow;
322 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
323 // MessageManager.getString("label.feature_settings_click_drag")));
324 scrollPane.setViewportView(table);
326 dassourceBrowser = new DasSourceBrowser(this);
327 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
329 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
331 fr.findAllFeatures(true); // display everything!
334 discoverAllFeatureData();
335 final PropertyChangeListener change;
336 final FeatureSettings fs = this;
337 fr.addPropertyChangeListener(change = new PropertyChangeListener()
340 public void propertyChange(PropertyChangeEvent evt)
342 if (!fs.resettingTable && !fs.handlingUpdate)
344 fs.handlingUpdate = true;
345 fs.resetTable(null); // new groups may be added with new seuqence
346 // feature types only
347 fs.handlingUpdate = false;
353 frame = new JInternalFrame();
354 frame.setContentPane(this);
355 if (Platform.isAMac())
357 Desktop.addInternalFrame(frame,
358 MessageManager.getString("label.sequence_feature_settings"),
363 Desktop.addInternalFrame(frame,
364 MessageManager.getString("label.sequence_feature_settings"),
367 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
369 frame.addInternalFrameListener(
370 new javax.swing.event.InternalFrameAdapter()
373 public void internalFrameClosed(
374 javax.swing.event.InternalFrameEvent evt)
376 fr.removePropertyChangeListener(change);
377 dassourceBrowser.fs = null;
380 frame.setLayer(JLayeredPane.PALETTE_LAYER);
381 inConstruction = false;
384 protected void popupSort(final int selectedRow, final String type,
385 final Object typeCol, final Map<String, float[][]> minmax, int x,
388 final FeatureColourI featureColour = (FeatureColourI) typeCol;
390 JPopupMenu men = new JPopupMenu(MessageManager
391 .formatMessage("label.settings_for_param", new String[]
393 JMenuItem scr = new JMenuItem(
394 MessageManager.getString("label.sort_by_score"));
396 final FeatureSettings me = this;
397 scr.addActionListener(new ActionListener()
401 public void actionPerformed(ActionEvent e)
404 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
409 JMenuItem dens = new JMenuItem(
410 MessageManager.getString("label.sort_by_density"));
411 dens.addActionListener(new ActionListener()
415 public void actionPerformed(ActionEvent e)
418 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
426 * variable colour options include colour by label, by score,
427 * by selected attribute text, or attribute value
429 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
430 MessageManager.getString("label.variable_colour"));
431 mxcol.setSelected(!featureColour.isSimpleColour());
433 mxcol.addActionListener(new ActionListener()
435 JColorChooser colorChooser;
438 public void actionPerformed(ActionEvent e)
440 if (e.getSource() == mxcol)
442 if (featureColour.isSimpleColour())
444 FeatureColourChooser fc = new FeatureColourChooser(me.fr, type);
445 fc.addActionListener(this);
449 // bring up simple color chooser
450 colorChooser = new JColorChooser();
451 JDialog dialog = JColorChooser.createDialog(me,
452 "Select new Colour", true, // modal
453 colorChooser, this, // OK button handler
454 null); // no CANCEL button handler
455 colorChooser.setColor(featureColour.getMaxColour());
456 dialog.setVisible(true);
461 if (e.getSource() instanceof FeatureColourChooser)
463 FeatureColourChooser fc = (FeatureColourChooser) e.getSource();
464 table.setValueAt(fc.getLastColour(), selectedRow, 1);
469 // probably the color chooser!
470 table.setValueAt(new FeatureColour(colorChooser.getColor()),
473 me.updateFeatureRenderer(
474 ((FeatureTableModel) table.getModel()).getData(), false);
481 JMenuItem selCols = new JMenuItem(
482 MessageManager.getString("label.select_columns_containing"));
483 selCols.addActionListener(new ActionListener()
486 public void actionPerformed(ActionEvent arg0)
488 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
492 JMenuItem clearCols = new JMenuItem(MessageManager
493 .getString("label.select_columns_not_containing"));
494 clearCols.addActionListener(new ActionListener()
497 public void actionPerformed(ActionEvent arg0)
499 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
503 JMenuItem hideCols = new JMenuItem(
504 MessageManager.getString("label.hide_columns_containing"));
505 hideCols.addActionListener(new ActionListener()
508 public void actionPerformed(ActionEvent arg0)
510 fr.ap.alignFrame.hideFeatureColumns(type, true);
513 JMenuItem hideOtherCols = new JMenuItem(
514 MessageManager.getString("label.hide_columns_not_containing"));
515 hideOtherCols.addActionListener(new ActionListener()
518 public void actionPerformed(ActionEvent arg0)
520 fr.ap.alignFrame.hideFeatureColumns(type, false);
526 men.add(hideOtherCols);
527 men.show(table, x, y);
531 synchronized public void discoverAllFeatureData()
533 Set<String> allGroups = new HashSet<>();
534 AlignmentI alignment = af.getViewport().getAlignment();
536 for (int i = 0; i < alignment.getHeight(); i++)
538 SequenceI seq = alignment.getSequenceAt(i);
539 for (String group : seq.getFeatures().getFeatureGroups(true))
541 if (group != null && !allGroups.contains(group))
543 allGroups.add(group);
544 checkGroupState(group);
549 populateFilterableFeatures();
557 * Synchronise gui group list and check visibility of group
560 * @return true if group is visible
562 private boolean checkGroupState(String group)
564 boolean visible = fr.checkGroupVisibility(group, true);
566 for (int g = 0; g < groupPanel.getComponentCount(); g++)
568 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
570 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
575 final String grp = group;
576 final JCheckBox check = new JCheckBox(group, visible);
577 check.setFont(new Font("Serif", Font.BOLD, 12));
578 check.setToolTipText(group);
579 check.addItemListener(new ItemListener()
582 public void itemStateChanged(ItemEvent evt)
584 fr.setGroupVisibility(check.getText(), check.isSelected());
585 resetTable(new String[] { grp });
586 af.alignPanel.paintAlignment(true, true);
589 groupPanel.add(check);
593 synchronized void resetTable(String[] groupChanged)
599 resettingTable = true;
600 typeWidth = new Hashtable<>();
601 // TODO: change avWidth calculation to 'per-sequence' average and use long
604 Set<String> displayableTypes = new HashSet<>();
605 Set<String> foundGroups = new HashSet<>();
608 * determine which feature types may be visible depending on
609 * which groups are selected, and recompute average width data
611 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
614 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
617 * get the sequence's groups for positional features
618 * and keep track of which groups are visible
620 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
621 Set<String> visibleGroups = new HashSet<>();
622 for (String group : groups)
624 if (group == null || checkGroupState(group))
626 visibleGroups.add(group);
629 foundGroups.addAll(groups);
632 * get distinct feature types for visible groups
633 * record distinct visible types, and their count and total length
635 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
636 visibleGroups.toArray(new String[visibleGroups.size()]));
637 for (String type : types)
639 displayableTypes.add(type);
640 float[] avWidth = typeWidth.get(type);
643 avWidth = new float[2];
644 typeWidth.put(type, avWidth);
646 // todo this could include features with a non-visible group
647 // - do we greatly care?
648 // todo should we include non-displayable features here, and only
649 // update when features are added?
650 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
651 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
655 Object[][] data = new Object[displayableTypes.size()][3];
658 if (fr.hasRenderOrder())
662 fr.findAllFeatures(groupChanged != null); // prod to update
663 // colourschemes. but don't
665 // First add the checks in the previous render order,
666 // in case the window has been closed and reopened
668 List<String> frl = fr.getRenderOrder();
669 for (int ro = frl.size() - 1; ro > -1; ro--)
671 String type = frl.get(ro);
673 if (!displayableTypes.contains(type))
678 data[dataIndex][0] = type;
679 data[dataIndex][1] = fr.getFeatureStyle(type);
680 data[dataIndex][2] = new Boolean(
681 af.getViewport().getFeaturesDisplayed().isVisible(type));
683 displayableTypes.remove(type);
688 * process any extra features belonging only to
689 * a group which was just selected
691 while (!displayableTypes.isEmpty())
693 String type = displayableTypes.iterator().next();
694 data[dataIndex][0] = type;
696 data[dataIndex][1] = fr.getFeatureStyle(type);
697 if (data[dataIndex][1] == null)
699 // "Colour has been updated in another view!!"
700 fr.clearRenderOrder();
704 data[dataIndex][2] = new Boolean(true);
706 displayableTypes.remove(type);
709 if (originalData == null)
711 originalData = new Object[data.length][3];
712 for (int i = 0; i < data.length; i++)
714 System.arraycopy(data[i], 0, originalData[i], 0, 3);
719 updateOriginalData(data);
722 table.setModel(new FeatureTableModel(data));
723 table.getColumnModel().getColumn(0).setPreferredWidth(200);
725 groupPanel.setLayout(
726 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
727 pruneGroups(foundGroups);
728 groupPanel.validate();
730 updateFeatureRenderer(data, groupChanged != null);
731 resettingTable = false;
735 * Updates 'originalData' (used for restore on Cancel) if we detect that
736 * changes have been made outwith this dialog
738 * <li>a new feature type added (and made visible)</li>
739 * <li>a feature colour changed (in the Amend Features dialog)</li>
744 protected void updateOriginalData(Object[][] foundData)
746 // todo LinkedHashMap instead of Object[][] would be nice
748 Object[][] currentData = ((FeatureTableModel) table.getModel())
750 for (Object[] row : foundData)
752 String type = (String) row[0];
753 boolean found = false;
754 for (Object[] current : currentData)
756 if (type.equals(current[0]))
760 * currently dependent on object equality here;
761 * really need an equals method on FeatureColour
763 if (!row[1].equals(current[1]))
766 * feature colour has changed externally - update originalData
768 for (Object[] original : originalData)
770 if (type.equals(original[0]))
772 original[1] = row[1];
783 * new feature detected - add to original data (on top)
785 Object[][] newData = new Object[originalData.length + 1][3];
786 for (int i = 0; i < originalData.length; i++)
788 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
791 originalData = newData;
797 * Remove from the groups panel any checkboxes for groups that are not in the
798 * foundGroups set. This enables removing a group from the display when the
799 * last feature in that group is deleted.
803 protected void pruneGroups(Set<String> foundGroups)
805 for (int g = 0; g < groupPanel.getComponentCount(); g++)
807 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
808 if (!foundGroups.contains(checkbox.getText()))
810 groupPanel.remove(checkbox);
816 * reorder data based on the featureRenderers global priority list.
820 private void ensureOrder(Object[][] data)
822 boolean sort = false;
823 float[] order = new float[data.length];
824 for (int i = 0; i < order.length; i++)
826 order[i] = fr.getOrder(data[i][0].toString());
829 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
833 sort = sort || order[i - 1] > order[i];
838 jalview.util.QuickSort.sort(order, data);
844 JalviewFileChooser chooser = new JalviewFileChooser("fc",
845 "Sequence Feature Colours");
846 chooser.setFileView(new JalviewFileView());
847 chooser.setDialogTitle(
848 MessageManager.getString("label.load_feature_colours"));
849 chooser.setToolTipText(MessageManager.getString("action.load"));
851 int value = chooser.showOpenDialog(this);
853 if (value == JalviewFileChooser.APPROVE_OPTION)
855 File file = chooser.getSelectedFile();
859 InputStreamReader in = new InputStreamReader(
860 new FileInputStream(file), "UTF-8");
862 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
864 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
867 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
870 Color mincol = null, maxcol = null;
873 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
874 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
876 } catch (Exception e)
878 Cache.log.warn("Couldn't parse out graduated feature color.",
881 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
882 newcol.getMin(), newcol.getMax());
883 if (newcol.hasAutoScale())
885 gcol.setAutoScaled(newcol.getAutoScale());
887 if (newcol.hasColourByLabel())
889 gcol.setColourByLabel(newcol.getColourByLabel());
891 if (newcol.hasThreshold())
893 gcol.setThreshold(newcol.getThreshold());
895 if (newcol.getThreshType().length() > 0)
897 String ttyp = newcol.getThreshType();
898 if (ttyp.equalsIgnoreCase("ABOVE"))
900 gcol.setAboveThreshold(true);
902 if (ttyp.equalsIgnoreCase("BELOW"))
904 gcol.setBelowThreshold(true);
907 fr.setColour(name = newcol.getName(), gcol);
911 Color color = new Color(
912 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
913 fr.setColour(name = jucs.getColour(i).getName(),
914 new FeatureColour(color));
916 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
921 Object[][] data = ((FeatureTableModel) table.getModel())
924 updateFeatureRenderer(data, false);
927 } catch (Exception ex)
929 System.out.println("Error loading User Colour File\n" + ex);
936 JalviewFileChooser chooser = new JalviewFileChooser("fc",
937 "Sequence Feature Colours");
938 chooser.setFileView(new JalviewFileView());
939 chooser.setDialogTitle(
940 MessageManager.getString("label.save_feature_colours"));
941 chooser.setToolTipText(MessageManager.getString("action.save"));
943 int value = chooser.showSaveDialog(this);
945 if (value == JalviewFileChooser.APPROVE_OPTION)
947 String choice = chooser.getSelectedFile().getPath();
948 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
949 ucs.setSchemeName("Sequence Features");
952 PrintWriter out = new PrintWriter(new OutputStreamWriter(
953 new FileOutputStream(choice), "UTF-8"));
955 Set<String> fr_colours = fr.getAllFeatureColours();
956 Iterator<String> e = fr_colours.iterator();
957 float[] sortOrder = new float[fr_colours.size()];
958 String[] sortTypes = new String[fr_colours.size()];
962 sortTypes[i] = e.next();
963 sortOrder[i] = fr.getOrder(sortTypes[i]);
966 QuickSort.sort(sortOrder, sortTypes);
968 for (i = 0; i < sortTypes.length; i++)
970 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
971 col.setName(sortTypes[i]);
972 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
973 if (fcol.isSimpleColour())
975 col.setRGB(Format.getHexString(fcol.getColour()));
979 col.setRGB(Format.getHexString(fcol.getMaxColour()));
980 col.setMin(fcol.getMin());
981 col.setMax(fcol.getMax());
983 jalview.util.Format.getHexString(fcol.getMinColour()));
984 col.setAutoScale(fcol.isAutoScaled());
985 col.setThreshold(fcol.getThreshold());
986 col.setColourByLabel(fcol.isColourByLabel());
987 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
988 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
994 } catch (Exception ex)
996 ex.printStackTrace();
1001 public void invertSelection()
1003 for (int i = 0; i < table.getRowCount(); i++)
1005 Boolean value = (Boolean) table.getValueAt(i, 2);
1007 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
1011 public void orderByAvWidth()
1013 if (table == null || table.getModel() == null)
1017 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1018 float[] width = new float[data.length];
1022 for (int i = 0; i < data.length; i++)
1024 awidth = typeWidth.get(data[i][0]);
1027 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1028 // weight - but have to make per
1029 // sequence, too (awidth[2])
1030 // if (width[i]==1) // hack to distinguish single width sequences.
1042 boolean sort = false;
1043 for (int i = 0; i < width.length; i++)
1045 // awidth = (float[]) typeWidth.get(data[i][0]);
1048 width[i] = fr.getOrder(data[i][0].toString());
1051 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1056 width[i] /= max; // normalize
1057 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1061 sort = sort || width[i - 1] > width[i];
1066 jalview.util.QuickSort.sort(width, data);
1067 // update global priority order
1070 updateFeatureRenderer(data, false);
1078 frame.setClosed(true);
1079 } catch (Exception exe)
1085 public void updateFeatureRenderer(Object[][] data)
1087 updateFeatureRenderer(data, true);
1091 * Update the priority order of features; only repaint if this changed the
1092 * order of visible features
1097 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1099 if (fr.setFeaturePriority(data, visibleNew))
1101 af.alignPanel.paintAlignment(true, true);
1105 private void jbInit() throws Exception
1107 this.setLayout(new BorderLayout());
1109 JPanel settingsPane = new JPanel();
1110 settingsPane.setLayout(new BorderLayout());
1112 filtersPane = new JPanel();
1114 dasSettingsPane.setLayout(new BorderLayout());
1116 JPanel bigPanel = new JPanel();
1117 bigPanel.setLayout(new BorderLayout());
1119 groupPanel = new JPanel();
1120 bigPanel.add(groupPanel, BorderLayout.NORTH);
1122 JButton invert = new JButton(
1123 MessageManager.getString("label.invert_selection"));
1124 invert.setFont(JvSwingUtils.getLabelFont());
1125 invert.addActionListener(new ActionListener()
1128 public void actionPerformed(ActionEvent e)
1134 JButton optimizeOrder = new JButton(
1135 MessageManager.getString("label.optimise_order"));
1136 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1137 optimizeOrder.addActionListener(new ActionListener()
1140 public void actionPerformed(ActionEvent e)
1146 JButton sortByScore = new JButton(
1147 MessageManager.getString("label.seq_sort_by_score"));
1148 sortByScore.setFont(JvSwingUtils.getLabelFont());
1149 sortByScore.addActionListener(new ActionListener()
1152 public void actionPerformed(ActionEvent e)
1154 af.avc.sortAlignmentByFeatureScore(null);
1157 JButton sortByDens = new JButton(
1158 MessageManager.getString("label.sequence_sort_by_density"));
1159 sortByDens.setFont(JvSwingUtils.getLabelFont());
1160 sortByDens.addActionListener(new ActionListener()
1163 public void actionPerformed(ActionEvent e)
1165 af.avc.sortAlignmentByFeatureDensity(null);
1169 JButton help = new JButton(MessageManager.getString("action.help"));
1170 help.setFont(JvSwingUtils.getLabelFont());
1171 help.addActionListener(new ActionListener()
1174 public void actionPerformed(ActionEvent e)
1178 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1179 } catch (HelpSetException e1)
1181 e1.printStackTrace();
1185 help.setFont(JvSwingUtils.getLabelFont());
1186 help.setText(MessageManager.getString("action.help"));
1187 help.addActionListener(new ActionListener()
1190 public void actionPerformed(ActionEvent e)
1194 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1195 } catch (HelpSetException e1)
1197 e1.printStackTrace();
1202 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1203 cancel.setFont(JvSwingUtils.getLabelFont());
1204 cancel.addActionListener(new ActionListener()
1207 public void actionPerformed(ActionEvent e)
1209 fr.setTransparency(originalTransparency);
1210 fr.setFeatureFilters(originalFilters);
1211 updateFeatureRenderer(originalData);
1216 JButton ok = new JButton(MessageManager.getString("action.ok"));
1217 ok.setFont(JvSwingUtils.getLabelFont());
1218 ok.addActionListener(new ActionListener()
1221 public void actionPerformed(ActionEvent e)
1227 JButton loadColours = new JButton(
1228 MessageManager.getString("label.load_colours"));
1229 loadColours.setFont(JvSwingUtils.getLabelFont());
1230 loadColours.addActionListener(new ActionListener()
1233 public void actionPerformed(ActionEvent e)
1239 JButton saveColours = new JButton(
1240 MessageManager.getString("label.save_colours"));
1241 saveColours.setFont(JvSwingUtils.getLabelFont());
1242 saveColours.addActionListener(new ActionListener()
1245 public void actionPerformed(ActionEvent e)
1250 transparency.addChangeListener(new ChangeListener()
1253 public void stateChanged(ChangeEvent evt)
1255 if (!inConstruction)
1257 fr.setTransparency((100 - transparency.getValue()) / 100f);
1258 af.alignPanel.paintAlignment(true,true);
1263 transparency.setMaximum(70);
1264 transparency.setToolTipText(
1265 MessageManager.getString("label.transparency_tip"));
1266 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1267 fetchDAS.addActionListener(new ActionListener()
1270 public void actionPerformed(ActionEvent e)
1272 fetchDAS_actionPerformed(e);
1275 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1276 saveDAS.addActionListener(new ActionListener()
1279 public void actionPerformed(ActionEvent e)
1281 saveDAS_actionPerformed(e);
1285 JPanel dasButtonPanel = new JPanel();
1286 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1287 dasSettingsPane.setBorder(null);
1288 cancelDAS.setEnabled(false);
1289 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1290 cancelDAS.addActionListener(new ActionListener()
1293 public void actionPerformed(ActionEvent e)
1295 cancelDAS_actionPerformed(e);
1299 JTabbedPane tabbedPane = new JTabbedPane();
1300 this.add(tabbedPane, BorderLayout.CENTER);
1301 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1303 tabbedPane.addTab(MessageManager.getString("label.filters"),
1305 // tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1306 // dasSettingsPane);
1308 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1309 bigPanel.add(transPanel, BorderLayout.SOUTH);
1311 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1312 transbuttons.add(optimizeOrder);
1313 transbuttons.add(invert);
1314 transbuttons.add(sortByScore);
1315 transbuttons.add(sortByDens);
1316 transbuttons.add(help);
1317 transPanel.add(transparency);
1318 transPanel.add(transbuttons);
1320 JPanel buttonPanel = new JPanel();
1321 buttonPanel.add(ok);
1322 buttonPanel.add(cancel);
1323 buttonPanel.add(loadColours);
1324 buttonPanel.add(saveColours);
1325 bigPanel.add(scrollPane, BorderLayout.CENTER);
1326 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1327 dasButtonPanel.add(fetchDAS);
1328 dasButtonPanel.add(cancelDAS);
1329 dasButtonPanel.add(saveDAS);
1330 settingsPane.add(bigPanel, BorderLayout.CENTER);
1331 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1337 * Populates initial layout of the feature attribute filters panel
1339 protected void initFiltersTab()
1341 filters = new ArrayList<>();
1344 * choose feature type
1346 JPanel chooseTypePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1347 chooseTypePanel.setBackground(Color.white);
1348 JvSwingUtils.createItalicTitledBorder(chooseTypePanel,
1349 MessageManager.getString("label.feature_type"), true);
1350 filteredFeatureChoice = new JComboBox<>();
1351 filteredFeatureChoice.addItemListener(new ItemListener()
1354 public void itemStateChanged(ItemEvent e)
1356 refreshFiltersDisplay();
1359 chooseTypePanel.add(new JLabel(MessageManager
1360 .getString("label.feature_to_filter")));
1361 chooseTypePanel.add(filteredFeatureChoice);
1362 populateFilterableFeatures();
1365 * the panel with the filters for the selected feature type
1367 JPanel filtersPanel = new JPanel();
1368 filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
1369 filtersPanel.setBackground(Color.white);
1370 JvSwingUtils.createItalicTitledBorder(filtersPanel,
1371 MessageManager.getString("label.filters"), true);
1374 * add AND or OR radio buttons
1376 JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1377 andOrPanel.setBackground(Color.white);
1378 andOrPanel.setBorder(BorderFactory.createLineBorder(debugBorderColour));
1379 andFilters = new JRadioButton("And");
1380 orFilters = new JRadioButton("Or");
1381 ActionListener actionListener = new ActionListener()
1384 public void actionPerformed(ActionEvent e)
1389 andFilters.addActionListener(actionListener);
1390 orFilters.addActionListener(actionListener);
1391 ButtonGroup andOr = new ButtonGroup();
1392 andOr.add(andFilters);
1393 andOr.add(orFilters);
1394 andFilters.setSelected(true);
1395 andOrPanel.add(new JLabel(MessageManager
1396 .getString("label.join_conditions")));
1397 andOrPanel.add(andFilters);
1398 andOrPanel.add(orFilters);
1399 filtersPanel.add(andOrPanel);
1402 * panel with filters - populated by refreshFiltersDisplay
1404 chooseFiltersPanel = new JPanel();
1405 LayoutManager box = new BoxLayout(chooseFiltersPanel,
1407 chooseFiltersPanel.setLayout(box);
1408 filtersPanel.add(chooseFiltersPanel);
1411 * a read-only text view of the current filters
1413 JPanel showFiltersPanel = new JPanel(new BorderLayout(5, 5));
1414 showFiltersPanel.setBackground(Color.white);
1415 JvSwingUtils.createItalicTitledBorder(showFiltersPanel,
1416 MessageManager.getString("label.match_condition"), true);
1417 filtersAsText = new JTextArea();
1418 filtersAsText.setLineWrap(true);
1419 filtersAsText.setWrapStyleWord(true);
1420 showFiltersPanel.add(filtersAsText);
1422 filtersPane.setLayout(new BorderLayout());
1423 filtersPane.add(chooseTypePanel, BorderLayout.NORTH);
1424 filtersPane.add(filtersPanel, BorderLayout.CENTER);
1425 filtersPane.add(showFiltersPanel, BorderLayout.SOUTH);
1428 * update display for initial feature type selection
1430 refreshFiltersDisplay();
1434 * Adds entries to the 'choose feature to filter' drop-down choice. Only
1435 * feature types which have known attributes (so can be filtered) are
1436 * included, so recall this method to update the list (check for newly added
1439 protected void populateFilterableFeatures()
1442 * suppress action handler while updating the list
1444 ItemListener listener = filteredFeatureChoice.getItemListeners()[0];
1445 filteredFeatureChoice.removeItemListener(listener);
1447 filteredFeatureChoice.removeAllItems();
1448 ReverseListIterator<String> types = new ReverseListIterator<>(
1449 fr.getRenderOrder());
1451 boolean found = false;
1452 while (types.hasNext())
1454 String type = types.next();
1455 if (FeatureAttributes.getInstance().hasAttributes(type))
1457 filteredFeatureChoice.addItem(type);
1463 filteredFeatureChoice.addItem(MessageManager
1464 .getString("label.no_feature_attributes"));
1465 filteredFeatureChoice.setEnabled(false);
1468 filteredFeatureChoice.addItemListener(listener);
1472 * Refreshes the display to show any filters currently configured for the
1473 * selected feature type (editable, with 'remove' option), plus one extra row
1474 * for adding a condition. This should be called on change of selected feature
1475 * type, or after a filter has been removed, added or amended.
1477 protected void refreshFiltersDisplay()
1480 * clear the panel and list of filter conditions
1482 chooseFiltersPanel.removeAll();
1486 * look up attributes known for feature type
1488 String selectedType = (String) filteredFeatureChoice.getSelectedItem();
1489 List<String> attNames = FeatureAttributes.getInstance().getAttributes(
1493 * if this feature type has filters set, load them first
1495 KeyedMatcherSetI featureFilters = fr.getFeatureFilter(selectedType);
1496 filtersAsText.setText("");
1497 if (featureFilters != null)
1499 filtersAsText.setText(featureFilters.toString());
1500 if (!featureFilters.isAnded())
1502 orFilters.setSelected(true);
1504 featureFilters.getMatchers().forEach(matcher -> filters.add(matcher));
1508 * and an empty filter for the user to populate (add)
1510 KeyedMatcherI noFilter = new KeyedMatcher("", Condition.values()[0], "");
1511 filters.add(noFilter);
1514 * render the conditions in rows, each in its own JPanel
1516 int filterIndex = 0;
1517 for (KeyedMatcherI filter : filters)
1519 String key = filter.getKey();
1520 Condition condition = filter.getMatcher()
1522 String pattern = filter.getMatcher().getPattern();
1523 JPanel row = addFilter(key, attNames, condition, pattern, filterIndex);
1524 row.setBorder(BorderFactory.createLineBorder(debugBorderColour));
1525 chooseFiltersPanel.add(row);
1528 // chooseFiltersPanel.add(Box.createVerticalGlue());
1530 filtersPane.validate();
1531 filtersPane.repaint();
1535 * A helper method that constructs a panel with one filter condition:
1537 * <li>a drop-down list of attribute names to choose from</li>
1538 * <li>a drop-down list of conditions to choose from</li>
1539 * <li>a text field for input of a match pattern</li>
1540 * <li>optionally, a 'remove' button</li>
1542 * If attribute, condition or pattern are not null, they are set as defaults
1543 * for the input fields. The 'remove' button is added unless the pattern is
1544 * null or empty (incomplete filter condition).
1550 * @param filterIndex
1553 protected JPanel addFilter(String attribute, List<String> attNames,
1554 Condition cond, String pattern, int filterIndex)
1556 JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
1557 filterRow.setBackground(Color.white);
1560 * drop-down choice of attribute, with description as a tooltip
1561 * if we can obtain it
1563 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1564 final JComboBox<String> attCombo = populateAttributesDropdown(
1565 featureType, attNames);
1566 JComboBox<Condition> condCombo = new JComboBox<>();
1567 JTextField patternField = new JTextField(8);
1570 * action handlers that validate and (if valid) apply changes
1572 ActionListener actionListener = new ActionListener()
1575 public void actionPerformed(ActionEvent e)
1577 if (attCombo.getSelectedItem() != null)
1579 if (validateFilter(patternField, condCombo))
1581 updateFilter(attCombo, condCombo, patternField, filterIndex);
1587 ItemListener itemListener = new ItemListener()
1590 public void itemStateChanged(ItemEvent e)
1592 actionListener.actionPerformed(null);
1596 if ("".equals(attribute))
1598 attCombo.setSelectedItem(null);
1602 attCombo.setSelectedItem(attribute);
1604 attCombo.addItemListener(itemListener);
1606 filterRow.add(attCombo);
1609 * drop-down choice of test condition
1611 for (Condition c : Condition.values())
1613 condCombo.addItem(c);
1617 condCombo.setSelectedItem(cond);
1619 condCombo.addItemListener(itemListener);
1620 filterRow.add(condCombo);
1623 * pattern to match against
1625 patternField.setText(pattern);
1626 patternField.addActionListener(actionListener);
1627 patternField.addFocusListener(new FocusAdapter()
1630 public void focusLost(FocusEvent e)
1632 actionListener.actionPerformed(null);
1635 filterRow.add(patternField);
1638 * add remove button if filter is populated (non-empty pattern)
1640 if (pattern != null && pattern.trim().length() > 0)
1642 // todo: gif for button drawing '-' or 'x'
1643 JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
1644 removeCondition.setToolTipText(MessageManager
1645 .getString("label.delete_row"));
1646 removeCondition.addActionListener(new ActionListener()
1649 public void actionPerformed(ActionEvent e)
1651 filters.remove(filterIndex);
1655 filterRow.add(removeCondition);
1662 * A helper method to build the drop-down choice of attributes for a feature.
1663 * Where metadata is available with a description for an attribute, that is
1664 * added as a tooltip.
1666 * @param featureType
1669 protected JComboBox<String> populateAttributesDropdown(
1670 String featureType, List<String> attNames)
1672 List<String> tooltips = new ArrayList<>();
1673 FeatureAttributes fa = FeatureAttributes.getInstance();
1674 for (String attName : attNames)
1676 String desc = fa.getDescription(featureType, attName);
1677 if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
1679 desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "...";
1681 tooltips.add(desc == null ? "" : desc);
1684 JComboBox<String> attCombo = JvSwingUtils.buildComboWithTooltips(
1685 attNames, tooltips);
1686 if (attNames.isEmpty())
1688 attCombo.setToolTipText(MessageManager
1689 .getString("label.no_attributes"));
1695 * Action on any change to feature filtering, namely
1697 * <li>change of selected attribute</li>
1698 * <li>change of selected condition</li>
1699 * <li>change of match pattern</li>
1700 * <li>removal of a condition</li>
1702 * The action should be to
1704 * <li>parse and validate the filters</li>
1705 * <li>if valid, update the filter text box</li>
1706 * <li>and apply the filters to the viewport</li>
1709 protected void filtersChanged()
1712 * update the filter conditions for the feature type
1714 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1715 boolean anded = andFilters.isSelected();
1716 KeyedMatcherSetI combined = new KeyedMatcherSet();
1718 for (KeyedMatcherI filter : filters)
1720 String pattern = filter.getMatcher().getPattern();
1721 if (pattern.trim().length() > 0)
1725 combined.and(filter);
1729 combined.or(filter);
1735 * save the filter conditions in the FeatureRenderer
1736 * (note this might now be an empty filter with no conditions)
1738 fr.setFeatureFilter(featureType, combined);
1740 filtersAsText.setText(combined.toString());
1742 refreshFiltersDisplay();
1744 af.alignPanel.paintAlignment(true, true);
1748 * Constructs a filter condition from the given input fields, and replaces the
1749 * condition at filterIndex with the new one
1754 * @param filterIndex
1756 protected void updateFilter(JComboBox<String> attCombo,
1757 JComboBox<Condition> condCombo, JTextField valueField,
1760 String attName = (String) attCombo.getSelectedItem();
1761 Condition cond = (Condition) condCombo.getSelectedItem();
1762 String pattern = valueField.getText();
1763 KeyedMatcherI km = new KeyedMatcher(attName, cond, pattern);
1765 filters.set(filterIndex, km);
1768 public void fetchDAS_actionPerformed(ActionEvent e)
1770 fetchDAS.setEnabled(false);
1771 cancelDAS.setEnabled(true);
1772 dassourceBrowser.setGuiEnabled(false);
1773 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1774 .getSelectedSources();
1775 doDasFeatureFetch(selectedSources, true, true);
1779 * get the features from selectedSources for all or the current selection
1781 * @param selectedSources
1782 * @param checkDbRefs
1783 * @param promptFetchDbRefs
1785 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1786 boolean checkDbRefs, boolean promptFetchDbRefs)
1788 SequenceI[] dataset, seqs;
1790 AlignmentViewport vp = af.getViewport();
1791 if (vp.getSelectionGroup() != null
1792 && vp.getSelectionGroup().getSize() > 0)
1794 iSize = vp.getSelectionGroup().getSize();
1795 dataset = new SequenceI[iSize];
1796 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1800 iSize = vp.getAlignment().getHeight();
1801 seqs = vp.getAlignment().getSequencesArray();
1804 dataset = new SequenceI[iSize];
1805 for (int i = 0; i < iSize; i++)
1807 dataset[i] = seqs[i].getDatasetSequence();
1810 cancelDAS.setEnabled(true);
1811 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1812 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1813 af.getViewport().setShowSequenceFeatures(true);
1814 af.showSeqFeatures.setSelected(true);
1818 * blocking call to initialise the das source browser
1820 public void initDasSources()
1822 dassourceBrowser.initDasSources();
1826 * examine the current list of das sources and return any matching the given
1827 * nicknames in sources
1830 * Vector of Strings to resolve to DAS source nicknames.
1831 * @return sources that are present in source list.
1833 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1835 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1839 * get currently selected das sources. ensure you have called initDasSources
1840 * before calling this.
1842 * @return vector of selected das source nicknames
1844 public Vector<jalviewSourceI> getSelectedSources()
1846 return dassourceBrowser.getSelectedSources();
1850 * properly initialise DAS fetcher and then initiate a new thread to fetch
1851 * features from the named sources (rather than any turned on by default)
1855 * if true then runs in same thread, otherwise passes to the Swing
1858 public void fetchDasFeatures(Vector<String> sources, boolean block)
1861 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1862 .resolveSourceNicknames(sources);
1863 if (resolved.size() == 0)
1865 resolved = dassourceBrowser.getSelectedSources();
1867 if (resolved.size() > 0)
1869 final List<jalviewSourceI> dassources = resolved;
1870 fetchDAS.setEnabled(false);
1871 // cancelDAS.setEnabled(true); doDasFetch does this.
1872 Runnable fetcher = new Runnable()
1878 doDasFeatureFetch(dassources, true, false);
1888 SwingUtilities.invokeLater(fetcher);
1893 public void saveDAS_actionPerformed(ActionEvent e)
1896 .saveProperties(jalview.bin.Cache.applicationProperties);
1899 public void complete()
1901 fetchDAS.setEnabled(true);
1902 cancelDAS.setEnabled(false);
1903 dassourceBrowser.setGuiEnabled(true);
1907 public void cancelDAS_actionPerformed(ActionEvent e)
1909 if (dasFeatureFetcher != null)
1911 dasFeatureFetcher.cancel();
1916 public void noDasSourceActive()
1919 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1920 MessageManager.getString("label.no_das_sources_selected_warn"),
1921 MessageManager.getString("label.no_das_sources_selected_title"),
1922 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1926 * Answers true unless a numeric condition has been selected with a
1927 * non-numeric value. Sets the value field to RED with a tooltip if in error.
1929 * If the pattern entered is empty, this method returns false, but does not
1930 * mark the field as invalid. This supports selecting an attribute for a new
1931 * condition before a match pattern has been entered.
1936 protected boolean validateFilter(JTextField value,
1937 JComboBox<Condition> condCombo)
1939 if (value == null || condCombo == null)
1941 return true; // fields not populated
1944 Condition cond = (Condition) condCombo.getSelectedItem();
1945 value.setBackground(Color.white);
1946 value.setToolTipText("");
1947 String v1 = value.getText().trim();
1948 if (v1.length() == 0)
1953 if (cond.isNumeric())
1958 } catch (NumberFormatException e)
1960 value.setBackground(Color.red);
1961 value.setToolTipText(MessageManager
1962 .getString("label.numeric_required"));
1970 // ///////////////////////////////////////////////////////////////////////
1971 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1972 // ///////////////////////////////////////////////////////////////////////
1973 class FeatureTableModel extends AbstractTableModel
1975 FeatureTableModel(Object[][] data)
1980 private String[] columnNames = {
1981 MessageManager.getString("label.feature_type"),
1982 MessageManager.getString("action.colour"),
1983 MessageManager.getString("label.display") };
1985 private Object[][] data;
1987 public Object[][] getData()
1992 public void setData(Object[][] data)
1998 public int getColumnCount()
2000 return columnNames.length;
2003 public Object[] getRow(int row)
2009 public int getRowCount()
2015 public String getColumnName(int col)
2017 return columnNames[col];
2021 public Object getValueAt(int row, int col)
2023 return data[row][col];
2027 public Class getColumnClass(int c)
2029 return getValueAt(0, c).getClass();
2033 public boolean isCellEditable(int row, int col)
2035 return col == 0 ? false : true;
2039 public void setValueAt(Object value, int row, int col)
2041 data[row][col] = value;
2042 fireTableCellUpdated(row, col);
2043 updateFeatureRenderer(data);
2048 class ColorRenderer extends JLabel implements TableCellRenderer
2050 javax.swing.border.Border unselectedBorder = null;
2052 javax.swing.border.Border selectedBorder = null;
2054 final String baseTT = "Click to edit, right/apple click for menu.";
2056 public ColorRenderer()
2058 setOpaque(true); // MUST do this for background to show up.
2059 setHorizontalTextPosition(SwingConstants.CENTER);
2060 setVerticalTextPosition(SwingConstants.CENTER);
2064 public Component getTableCellRendererComponent(JTable tbl, Object color,
2065 boolean isSelected, boolean hasFocus, int row, int column)
2067 FeatureColourI cellColour = (FeatureColourI) color;
2069 setToolTipText(baseTT);
2070 setBackground(tbl.getBackground());
2071 if (!cellColour.isSimpleColour())
2073 Rectangle cr = tbl.getCellRect(row, column, false);
2074 FeatureSettings.renderGraduatedColor(this, cellColour,
2075 (int) cr.getWidth(), (int) cr.getHeight());
2081 setBackground(cellColour.getColour());
2085 if (selectedBorder == null)
2087 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2088 tbl.getSelectionBackground());
2090 setBorder(selectedBorder);
2094 if (unselectedBorder == null)
2096 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2097 tbl.getBackground());
2099 setBorder(unselectedBorder);
2107 * update comp using rendering settings from gcol
2112 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
2114 int w = comp.getWidth(), h = comp.getHeight();
2117 w = (int) comp.getPreferredSize().getWidth();
2118 h = (int) comp.getPreferredSize().getHeight();
2125 renderGraduatedColor(comp, gcol, w, h);
2128 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
2131 boolean thr = false;
2132 StringBuilder tt = new StringBuilder();
2133 StringBuilder tx = new StringBuilder();
2135 if (gcol.isColourByAttribute())
2137 tx.append(gcol.getAttributeName());
2139 else if (!gcol.isColourByLabel())
2141 tx.append(MessageManager.getString("label.score"));
2144 if (gcol.isAboveThreshold())
2148 tt.append("Thresholded (Above ").append(gcol.getThreshold())
2151 if (gcol.isBelowThreshold())
2155 tt.append("Thresholded (Below ").append(gcol.getThreshold())
2158 if (gcol.isColourByLabel())
2160 tt.append("Coloured by label text. ").append(tt);
2165 if (!gcol.isColourByAttribute())
2173 Color newColor = gcol.getMaxColour();
2174 comp.setBackground(newColor);
2175 // System.err.println("Width is " + w / 2);
2176 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
2177 comp.setIcon(ficon);
2178 // tt+="RGB value: Max (" + newColor.getRed() + ", "
2179 // + newColor.getGreen() + ", " + newColor.getBlue()
2180 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
2181 // + ", " + minCol.getBlue() + ")");
2183 comp.setHorizontalAlignment(SwingConstants.CENTER);
2184 comp.setText(tx.toString());
2185 if (tt.length() > 0)
2187 if (comp.getToolTipText() == null)
2189 comp.setToolTipText(tt.toString());
2193 comp.setToolTipText(tt.append(" ").append(comp.getToolTipText())
2200 class FeatureIcon implements Icon
2202 FeatureColourI gcol;
2206 boolean midspace = false;
2208 int width = 50, height = 20;
2210 int s1, e1; // start and end of midpoint band for thresholded symbol
2212 Color mpcolour = Color.white;
2214 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2234 public int getIconWidth()
2240 public int getIconHeight()
2246 public void paintIcon(Component c, Graphics g, int x, int y)
2249 if (gcol.isColourByLabel())
2252 g.fillRect(0, 0, width, height);
2253 // need an icon here.
2254 g.setColor(gcol.getMaxColour());
2256 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2258 // g.setFont(g.getFont().deriveFont(
2259 // AffineTransform.getScaleInstance(
2260 // width/g.getFontMetrics().stringWidth("Label"),
2261 // height/g.getFontMetrics().getHeight())));
2263 g.drawString(MessageManager.getString("label.label"), 0, 0);
2268 Color minCol = gcol.getMinColour();
2270 g.fillRect(0, 0, s1, height);
2273 g.setColor(Color.white);
2274 g.fillRect(s1, 0, e1 - s1, height);
2276 g.setColor(gcol.getMaxColour());
2277 g.fillRect(0, e1, width - e1, height);
2282 class ColorEditor extends AbstractCellEditor
2283 implements TableCellEditor, ActionListener
2287 FeatureColourI currentColor;
2289 FeatureColourChooser chooser;
2295 JColorChooser colorChooser;
2299 protected static final String EDIT = "edit";
2301 int selectedRow = 0;
2303 public ColorEditor(FeatureSettings me)
2306 // Set up the editor (from the table's point of view),
2307 // which is a button.
2308 // This button brings up the color chooser dialog,
2309 // which is the editor from the user's point of view.
2310 button = new JButton();
2311 button.setActionCommand(EDIT);
2312 button.addActionListener(this);
2313 button.setBorderPainted(false);
2314 // Set up the dialog that the button brings up.
2315 colorChooser = new JColorChooser();
2316 dialog = JColorChooser.createDialog(button,
2317 MessageManager.getString("label.select_new_colour"), true, // modal
2318 colorChooser, this, // OK button handler
2319 null); // no CANCEL button handler
2323 * Handles events from the editor button and from the dialog's OK button.
2326 public void actionPerformed(ActionEvent e)
2329 if (EDIT.equals(e.getActionCommand()))
2331 // The user has clicked the cell, so
2332 // bring up the dialog.
2333 if (currentColor.isSimpleColour())
2335 // bring up simple color chooser
2336 button.setBackground(currentColor.getColour());
2337 colorChooser.setColor(currentColor.getColour());
2338 dialog.setVisible(true);
2342 // bring up graduated chooser.
2343 chooser = new FeatureColourChooser(me.fr, type);
2344 chooser.setRequestFocusEnabled(true);
2345 chooser.requestFocus();
2346 chooser.addActionListener(this);
2348 // Make the renderer reappear.
2349 fireEditingStopped();
2353 { // User pressed dialog's "OK" button.
2354 if (currentColor.isSimpleColour())
2356 currentColor = new FeatureColour(colorChooser.getColor());
2360 currentColor = chooser.getLastColour();
2362 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
2363 fireEditingStopped();
2364 me.table.validate();
2368 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2370 public Object getCellEditorValue()
2372 return currentColor;
2375 // Implement the one method defined by TableCellEditor.
2377 public Component getTableCellEditorComponent(JTable table, Object value,
2378 boolean isSelected, int row, int column)
2380 currentColor = (FeatureColourI) value;
2381 this.selectedRow = row;
2382 type = me.table.getValueAt(row, 0).toString();
2383 button.setOpaque(true);
2384 button.setBackground(me.getBackground());
2385 if (!currentColor.isSimpleColour())
2387 JLabel btn = new JLabel();
2388 btn.setSize(button.getSize());
2389 FeatureSettings.renderGraduatedColor(btn, currentColor);
2390 button.setBackground(btn.getBackground());
2391 button.setIcon(btn.getIcon());
2392 button.setText(btn.getText());
2397 button.setIcon(null);
2398 button.setBackground(currentColor.getColour());