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.datamodel.AlignmentI;
26 import jalview.datamodel.SequenceI;
27 import jalview.datamodel.features.FeatureMatcherI;
28 import jalview.datamodel.features.FeatureMatcherSet;
29 import jalview.datamodel.features.FeatureMatcherSetI;
30 import jalview.datamodel.ontology.OntologyI;
31 import jalview.gui.Help.HelpId;
32 import jalview.io.JalviewFileChooser;
33 import jalview.io.JalviewFileView;
34 import jalview.io.gff.SequenceOntologyFactory;
35 import jalview.schemabinding.version2.Filter;
36 import jalview.schemabinding.version2.JalviewUserColours;
37 import jalview.schemabinding.version2.MatcherSet;
38 import jalview.schemes.FeatureColour;
39 import jalview.util.MessageManager;
40 import jalview.util.Platform;
41 import jalview.viewmodel.AlignmentViewport;
42 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
43 import jalview.ws.DasSequenceFeatureFetcher;
44 import jalview.ws.dbsources.das.api.jalviewSourceI;
46 import java.awt.BorderLayout;
47 import java.awt.Color;
48 import java.awt.Component;
49 import java.awt.Dimension;
51 import java.awt.Graphics;
52 import java.awt.GridLayout;
53 import java.awt.Point;
54 import java.awt.Rectangle;
55 import java.awt.event.ActionEvent;
56 import java.awt.event.ActionListener;
57 import java.awt.event.ItemEvent;
58 import java.awt.event.ItemListener;
59 import java.awt.event.MouseAdapter;
60 import java.awt.event.MouseEvent;
61 import java.awt.event.MouseMotionAdapter;
62 import java.beans.PropertyChangeEvent;
63 import java.beans.PropertyChangeListener;
65 import java.io.FileInputStream;
66 import java.io.FileOutputStream;
67 import java.io.InputStreamReader;
68 import java.io.OutputStreamWriter;
69 import java.io.PrintWriter;
70 import java.util.ArrayList;
71 import java.util.Arrays;
72 import java.util.Comparator;
73 import java.util.HashMap;
74 import java.util.HashSet;
75 import java.util.Hashtable;
76 import java.util.Iterator;
77 import java.util.List;
80 import java.util.Vector;
82 import javax.help.HelpSetException;
83 import javax.swing.AbstractCellEditor;
84 import javax.swing.BorderFactory;
85 import javax.swing.Icon;
86 import javax.swing.JButton;
87 import javax.swing.JCheckBox;
88 import javax.swing.JCheckBoxMenuItem;
89 import javax.swing.JColorChooser;
90 import javax.swing.JDialog;
91 import javax.swing.JInternalFrame;
92 import javax.swing.JLabel;
93 import javax.swing.JLayeredPane;
94 import javax.swing.JMenuItem;
95 import javax.swing.JPanel;
96 import javax.swing.JPopupMenu;
97 import javax.swing.JScrollPane;
98 import javax.swing.JSlider;
99 import javax.swing.JTable;
100 import javax.swing.ListSelectionModel;
101 import javax.swing.RowFilter;
102 import javax.swing.SwingConstants;
103 import javax.swing.SwingUtilities;
104 import javax.swing.event.ChangeEvent;
105 import javax.swing.event.ChangeListener;
106 import javax.swing.table.AbstractTableModel;
107 import javax.swing.table.TableCellEditor;
108 import javax.swing.table.TableCellRenderer;
109 import javax.swing.table.TableColumn;
110 import javax.swing.table.TableRowSorter;
112 public class FeatureSettings extends JPanel
113 implements FeatureSettingsControllerI
115 private static final Font VERDANA_12 = new Font("Verdana", Font.PLAIN, 12);
117 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
118 .getString("label.sequence_feature_colours");
121 * column indices of fields in Feature Settings table
123 static final int TYPE_COLUMN = 0;
125 static final int COLOUR_COLUMN = 1;
127 static final int FILTER_COLUMN = 2;
129 static final int SHOW_COLUMN = 3;
131 private static final int COLUMN_COUNT = 4;
133 private static final int MIN_WIDTH = 400;
135 private static final int MIN_HEIGHT = 400;
137 DasSourceBrowser dassourceBrowser;
139 DasSequenceFeatureFetcher dasFeatureFetcher;
141 JPanel dasSettingsPane = new JPanel();
143 final FeatureRenderer fr;
145 public final AlignFrame af;
148 * 'original' fields hold settings to restore on Cancel
150 Object[][] originalData;
152 private float originalTransparency;
154 private Map<String, FeatureMatcherSetI> originalFilters;
156 final JInternalFrame frame;
158 JScrollPane scrollPane = new JScrollPane();
164 JSlider transparency = new JSlider();
167 * when true, constructor is still executing - so ignore UI events
169 protected volatile boolean inConstruction = true;
171 int selectedRow = -1;
173 JButton fetchDAS = new JButton();
175 JButton saveDAS = new JButton();
177 JButton cancelDAS = new JButton();
179 boolean resettingTable = false;
182 * true when Feature Settings are updating from feature renderer
184 private boolean handlingUpdate = false;
187 * holds {featureCount, totalExtent} for each feature type
189 Map<String, float[]> typeWidth = null;
192 * if true, 'child' feature types are not displayed
194 JCheckBox summaryView;
197 * those feature types that do not have a parent feature type present
198 * (as determined by an Ontology relationship)
200 List<String> topLevelTypes;
207 public FeatureSettings(AlignFrame alignFrame)
209 this.af = alignFrame;
210 fr = af.getFeatureRenderer();
212 // save transparency for restore on Cancel
213 originalTransparency = fr.getTransparency();
214 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
215 transparency.setMaximum(100 - originalTransparencyAsPercent);
217 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
219 topLevelTypes = new ArrayList<>();
224 } catch (Exception ex)
226 ex.printStackTrace();
231 scrollPane.setViewportView(table);
233 dassourceBrowser = new DasSourceBrowser(this);
234 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
236 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
238 fr.findAllFeatures(true); // display everything!
241 discoverAllFeatureData();
242 final PropertyChangeListener change;
243 final FeatureSettings fs = this;
244 fr.addPropertyChangeListener(change = new PropertyChangeListener()
247 public void propertyChange(PropertyChangeEvent evt)
249 if (!fs.resettingTable && !fs.handlingUpdate)
251 fs.handlingUpdate = true;
253 // new groups may be added with new sequence feature types only
254 fs.handlingUpdate = false;
259 frame = new JInternalFrame();
260 frame.setContentPane(this);
261 if (Platform.isAMac())
263 Desktop.addInternalFrame(frame,
264 MessageManager.getString("label.sequence_feature_settings"),
269 Desktop.addInternalFrame(frame,
270 MessageManager.getString("label.sequence_feature_settings"),
273 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
275 frame.addInternalFrameListener(
276 new javax.swing.event.InternalFrameAdapter()
279 public void internalFrameClosed(
280 javax.swing.event.InternalFrameEvent evt)
282 fr.removePropertyChangeListener(change);
283 dassourceBrowser.fs = null;
286 frame.setLayer(JLayeredPane.PALETTE_LAYER);
287 inConstruction = false;
291 * Constructs and configures the JTable which displays columns of data for
294 protected void initTable()
299 public String getToolTipText(MouseEvent e)
302 int column = table.columnAtPoint(e.getPoint());
307 * drag to reorder not enabled in Summary View
309 tip = summaryView.isSelected()
310 ? MessageManager.getString(
311 "label.feature_settings_select_columns")
312 : JvSwingUtils.wrapTooltip(true, MessageManager
313 .getString("label.feature_settings_click_drag"));
316 int row = table.rowAtPoint(e.getPoint());
317 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
320 ? MessageManager.getString("label.filters_tooltip")
329 table.getTableHeader().setFont(VERDANA_12);
330 table.setFont(VERDANA_12);
332 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
333 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
335 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
336 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
338 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
339 new ColorRenderer(), new ColorEditor(this));
340 table.addColumn(colourColumn);
342 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
343 new FilterRenderer(), new FilterEditor(this));
344 table.addColumn(filterColumn);
346 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
348 table.addMouseListener(new MouseAdapter()
351 public void mousePressed(MouseEvent evt)
353 selectedRow = table.rowAtPoint(evt.getPoint());
354 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
355 if (evt.isPopupTrigger())
357 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
358 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
360 else if (evt.getClickCount() == 2)
362 boolean invertSelection = evt.isAltDown();
363 boolean toggleSelection = Platform.isControlDown(evt);
364 boolean extendSelection = evt.isShiftDown();
365 String[] terms = getTermsInScope(type);
366 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
367 invertSelection, extendSelection, toggleSelection, terms);
371 // isPopupTrigger fires on mouseReleased on Windows
373 public void mouseReleased(MouseEvent evt)
375 selectedRow = table.rowAtPoint(evt.getPoint());
376 if (evt.isPopupTrigger())
378 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
379 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
380 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
385 table.addMouseMotionListener(new MouseMotionAdapter()
388 public void mouseDragged(MouseEvent evt)
390 int newRow = table.rowAtPoint(evt.getPoint());
397 * Answers an array consisting of the given type, and also (if 'Summary View'
398 * is selected), any child terms in the sequence ontology
403 protected String[] getTermsInScope(String type)
405 if (!summaryView.isSelected())
407 return new String[] { type };
410 List<String> terms = new ArrayList<>();
413 OntologyI so = SequenceOntologyFactory.getInstance();
415 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
416 for (Object[] row : data)
418 String type2 = (String) row[TYPE_COLUMN];
419 if (!type2.equals(type) && so.isA(type2, type))
424 return terms.toArray(new String[terms.size()]);
427 protected void popupMenu(final int rowSelected, final String type,
428 final Object typeCol, int x, int y)
430 final FeatureColourI featureColour = (FeatureColourI) typeCol;
432 JPopupMenu men = new JPopupMenu(MessageManager
433 .formatMessage("label.settings_for_param", new String[]
435 JMenuItem scr = new JMenuItem(
436 MessageManager.getString("label.sort_by_score"));
438 final FeatureSettings me = this;
439 scr.addActionListener(new ActionListener()
443 public void actionPerformed(ActionEvent e)
446 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
451 JMenuItem dens = new JMenuItem(
452 MessageManager.getString("label.sort_by_density"));
453 dens.addActionListener(new ActionListener()
457 public void actionPerformed(ActionEvent e)
460 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
468 * variable colour options include colour by label, by score,
469 * by selected attribute text, or attribute value
471 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
472 MessageManager.getString("label.variable_colour"));
473 mxcol.setSelected(!featureColour.isSimpleColour());
475 mxcol.addActionListener(new ActionListener()
477 JColorChooser colorChooser;
480 public void actionPerformed(ActionEvent e)
482 if (e.getSource() == mxcol)
484 if (featureColour.isSimpleColour())
486 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
487 fc.addActionListener(this);
491 // bring up simple color chooser
492 colorChooser = new JColorChooser();
493 String title = MessageManager
494 .getString("label.select_colour");
495 JDialog dialog = JColorChooser.createDialog(me,
496 title, true, // modal
497 colorChooser, this, // OK button handler
498 null); // no CANCEL button handler
499 colorChooser.setColor(featureColour.getMaxColour());
500 dialog.setVisible(true);
505 if (e.getSource() instanceof FeatureTypeSettings)
508 * update after OK in feature colour dialog; the updated
509 * colour will have already been set in the FeatureRenderer
511 FeatureColourI fci = fr.getFeatureColours().get(type);
512 table.setValueAt(fci, rowSelected, 1);
517 // probably the color chooser!
518 table.setValueAt(new FeatureColour(colorChooser.getColor()),
521 me.updateFeatureRenderer(
522 ((FeatureTableModel) table.getModel()).getData(),
529 JMenuItem selCols = new JMenuItem(
530 MessageManager.getString("label.select_columns_containing"));
531 selCols.addActionListener(new ActionListener()
534 public void actionPerformed(ActionEvent arg0)
536 String[] types = getTermsInScope(type);
537 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
541 JMenuItem clearCols = new JMenuItem(MessageManager
542 .getString("label.select_columns_not_containing"));
543 clearCols.addActionListener(new ActionListener()
546 public void actionPerformed(ActionEvent arg0)
548 String[] types = getTermsInScope(type);
549 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
553 JMenuItem hideCols = new JMenuItem(
554 MessageManager.getString("label.hide_columns_containing"));
555 hideCols.addActionListener(new ActionListener()
558 public void actionPerformed(ActionEvent arg0)
560 String[] types = getTermsInScope(type);
561 fr.ap.alignFrame.hideFeatureColumns(true, types);
564 JMenuItem hideOtherCols = new JMenuItem(
565 MessageManager.getString("label.hide_columns_not_containing"));
566 hideOtherCols.addActionListener(new ActionListener()
569 public void actionPerformed(ActionEvent arg0)
571 String[] types = getTermsInScope(type);
572 fr.ap.alignFrame.hideFeatureColumns(false, types);
578 men.add(hideOtherCols);
579 men.show(table, x, y);
583 synchronized public void discoverAllFeatureData()
585 Set<String> allGroups = new HashSet<>();
586 AlignmentI alignment = af.getViewport().getAlignment();
588 for (int i = 0; i < alignment.getHeight(); i++)
590 SequenceI seq = alignment.getSequenceAt(i);
591 for (String group : seq.getFeatures().getFeatureGroups(true))
593 if (group != null && !allGroups.contains(group))
595 allGroups.add(group);
596 checkGroupState(group);
607 * Synchronise gui group list and check visibility of group
610 * @return true if group is visible
612 private boolean checkGroupState(String group)
614 boolean visible = fr.checkGroupVisibility(group, true);
616 for (int g = 0; g < groupPanel.getComponentCount(); g++)
618 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
620 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
625 final String grp = group;
626 final JCheckBox check = new JCheckBox(group, visible);
627 check.setFont(new Font("Serif", Font.BOLD, 12));
628 check.setToolTipText(group);
629 check.addItemListener(new ItemListener()
632 public void itemStateChanged(ItemEvent evt)
634 fr.setGroupVisibility(check.getText(), check.isSelected());
635 resetTable(new String[] { grp });
636 af.alignPanel.paintAlignment(true, true);
639 groupPanel.add(check);
643 synchronized void resetTable(String[] groupChanged)
649 resettingTable = true;
650 typeWidth = new Hashtable<>();
651 // TODO: change avWidth calculation to 'per-sequence' average and use long
654 Set<String> displayableTypes = new HashSet<>();
655 Set<String> foundGroups = new HashSet<>();
658 * determine which feature types may be visible depending on
659 * which groups are selected, and recompute average width data
661 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
664 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
667 * get the sequence's groups for positional features
668 * and keep track of which groups are visible
670 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
671 Set<String> visibleGroups = new HashSet<>();
672 for (String group : groups)
674 if (group == null || checkGroupState(group))
676 visibleGroups.add(group);
679 foundGroups.addAll(groups);
682 * get distinct feature types for visible groups
683 * record distinct visible types, and their count and total length
685 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
686 visibleGroups.toArray(new String[visibleGroups.size()]));
688 for (String type : types)
690 displayableTypes.add(type);
691 float[] avWidth = typeWidth.get(type);
694 avWidth = new float[2];
695 typeWidth.put(type, avWidth);
697 // todo this could include features with a non-visible group
698 // - do we greatly care?
699 // todo should we include non-displayable features here, and only
700 // update when features are added?
701 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
702 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
707 * enable 'Summary View' if some types are sub-types of others
709 Set<String> parents = SequenceOntologyFactory.getInstance()
710 .getParentTerms(displayableTypes);
711 summaryView.setEnabled(parents.size() < displayableTypes.size());
713 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
716 if (fr.hasRenderOrder())
720 fr.findAllFeatures(groupChanged != null); // prod to update
721 // colourschemes. but don't
723 // First add the checks in the previous render order,
724 // in case the window has been closed and reopened
726 List<String> frl = fr.getRenderOrder();
727 for (int ro = frl.size() - 1; ro > -1; ro--)
729 String type = frl.get(ro);
731 if (!displayableTypes.contains(type))
735 data[dataIndex][TYPE_COLUMN] = type;
736 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
737 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
738 data[dataIndex][FILTER_COLUMN] = featureFilter == null
739 ? new FeatureMatcherSet()
741 data[dataIndex][SHOW_COLUMN] = new Boolean(
742 af.getViewport().getFeaturesDisplayed().isVisible(type));
744 displayableTypes.remove(type);
749 * process any extra features belonging only to
750 * a group which was just selected
752 while (!displayableTypes.isEmpty())
754 String type = displayableTypes.iterator().next();
755 data[dataIndex][TYPE_COLUMN] = type;
757 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
758 if (data[dataIndex][COLOUR_COLUMN] == null)
760 // "Colour has been updated in another view!!"
761 fr.clearRenderOrder();
764 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
765 data[dataIndex][FILTER_COLUMN] = featureFilter == null
766 ? new FeatureMatcherSet()
768 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
770 displayableTypes.remove(type);
773 if (originalData == null)
775 originalData = new Object[data.length][COLUMN_COUNT];
776 for (int i = 0; i < data.length; i++)
778 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
783 updateOriginalData(data);
787 * recreate the table model
789 FeatureTableModel dataModel = new FeatureTableModel(data);
790 table.setModel(dataModel);
793 * we want to be able to filter out rows for sub-types, but not to sort
794 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
796 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
798 for (int i = 0; i < table.getColumnCount(); i++)
800 sorter.setSortable(i, false);
804 * filter rows to only top-level Ontology types if requested
806 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
809 public boolean include(
810 Entry<? extends FeatureTableModel, ? extends Integer> entry)
812 if (!summaryView.isSelected())
816 int row = entry.getIdentifier(); // this is model, not view, row number
817 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
818 return parents.contains(featureType);
821 table.setRowSorter(sorter);
823 table.getColumnModel().getColumn(0).setPreferredWidth(200);
825 groupPanel.setLayout(
826 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
827 pruneGroups(foundGroups);
828 groupPanel.validate();
830 updateFeatureRenderer(data, groupChanged != null);
831 resettingTable = false;
835 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
836 * have been made outwith this dialog
838 * <li>a new feature type added (and made visible)</li>
839 * <li>a feature colour changed (in the Amend Features dialog)</li>
844 protected void updateOriginalData(Object[][] foundData)
846 // todo LinkedHashMap instead of Object[][] would be nice
848 Object[][] currentData = ((FeatureTableModel) table.getModel())
850 for (Object[] row : foundData)
852 String type = (String) row[TYPE_COLUMN];
853 boolean found = false;
854 for (Object[] current : currentData)
856 if (type.equals(current[TYPE_COLUMN]))
860 * currently dependent on object equality here;
861 * really need an equals method on FeatureColour
863 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
866 * feature colour has changed externally - update originalData
868 for (Object[] original : originalData)
870 if (type.equals(original[TYPE_COLUMN]))
872 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
883 * new feature detected - add to original data (on top)
885 Object[][] newData = new Object[originalData.length
887 for (int i = 0; i < originalData.length; i++)
889 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
893 originalData = newData;
899 * Remove from the groups panel any checkboxes for groups that are not in the
900 * foundGroups set. This enables removing a group from the display when the last
901 * feature in that group is deleted.
905 protected void pruneGroups(Set<String> foundGroups)
907 for (int g = 0; g < groupPanel.getComponentCount(); g++)
909 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
910 if (!foundGroups.contains(checkbox.getText()))
912 groupPanel.remove(checkbox);
918 * reorder data based on the featureRenderers global priority list.
922 private void ensureOrder(Object[][] data)
924 boolean sort = false;
925 float[] order = new float[data.length];
926 for (int i = 0; i < order.length; i++)
928 order[i] = fr.getOrder(data[i][0].toString());
931 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
935 sort = sort || order[i - 1] > order[i];
940 jalview.util.QuickSort.sort(order, data);
945 * Offers a file chooser dialog, and then loads the feature colours and
946 * filters from file in XML format and unmarshals to Jalview feature settings
950 JalviewFileChooser chooser = new JalviewFileChooser("fc",
951 SEQUENCE_FEATURE_COLOURS);
952 chooser.setFileView(new JalviewFileView());
953 chooser.setDialogTitle(
954 MessageManager.getString("label.load_feature_colours"));
955 chooser.setToolTipText(MessageManager.getString("action.load"));
957 int value = chooser.showOpenDialog(this);
959 if (value == JalviewFileChooser.APPROVE_OPTION)
961 File file = chooser.getSelectedFile();
967 * Loads feature colours and filters from XML stored in the given file
975 InputStreamReader in = new InputStreamReader(
976 new FileInputStream(file), "UTF-8");
978 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
981 * load feature colours
983 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
985 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
986 FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
987 fr.setColour(newcol.getName(), colour);
988 fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
992 * load feature filters; loaded filters will replace any that are
993 * currently defined, other defined filters are left unchanged
995 for (int i = 0; i < jucs.getFilterCount(); i++)
997 jalview.schemabinding.version2.Filter filterModel = jucs
999 String featureType = filterModel.getFeatureType();
1000 FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
1001 filterModel.getMatcherSet());
1002 if (!filter.isEmpty())
1004 fr.setFeatureFilter(featureType, filter);
1009 * update feature settings table
1014 Object[][] data = ((FeatureTableModel) table.getModel())
1017 updateFeatureRenderer(data, false);
1020 } catch (Exception ex)
1022 System.out.println("Error loading User Colour File\n" + ex);
1027 * Offers a file chooser dialog, and then saves the current feature colours
1028 * and any filters to the selected file in XML format
1032 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1033 SEQUENCE_FEATURE_COLOURS);
1034 chooser.setFileView(new JalviewFileView());
1035 chooser.setDialogTitle(
1036 MessageManager.getString("label.save_feature_colours"));
1037 chooser.setToolTipText(MessageManager.getString("action.save"));
1039 int value = chooser.showSaveDialog(this);
1041 if (value == JalviewFileChooser.APPROVE_OPTION)
1043 save(chooser.getSelectedFile());
1048 * Saves feature colours and filters to the given file
1052 void save(File file)
1054 JalviewUserColours ucs = new JalviewUserColours();
1055 ucs.setSchemeName("Sequence Features");
1058 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1059 new FileOutputStream(file), "UTF-8"));
1062 * sort feature types by colour order, from 0 (highest)
1065 Set<String> fr_colours = fr.getAllFeatureColours();
1066 String[] sortedTypes = fr_colours
1067 .toArray(new String[fr_colours.size()]);
1068 Arrays.sort(sortedTypes, new Comparator<String>()
1071 public int compare(String type1, String type2)
1073 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1078 * save feature colours
1080 for (String featureType : sortedTypes)
1082 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1083 jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
1089 * save any feature filters
1091 for (String featureType : sortedTypes)
1093 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1094 if (filter != null && !filter.isEmpty())
1096 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1097 FeatureMatcherI firstMatcher = iterator.next();
1098 MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
1100 Filter filterModel = new Filter();
1101 filterModel.setFeatureType(featureType);
1102 filterModel.setMatcherSet(ms);
1103 ucs.addFilter(filterModel);
1109 } catch (Exception ex)
1111 ex.printStackTrace();
1115 public void invertSelection()
1117 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1118 for (int i = 0; i < data.length; i++)
1120 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1122 updateFeatureRenderer(data, true);
1126 public void orderByAvWidth()
1128 if (table == null || table.getModel() == null)
1132 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1133 float[] width = new float[data.length];
1137 for (int i = 0; i < data.length; i++)
1139 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1142 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1143 // weight - but have to make per
1144 // sequence, too (awidth[2])
1145 // if (width[i]==1) // hack to distinguish single width sequences.
1156 boolean sort = false;
1157 for (int i = 0; i < width.length; i++)
1159 // awidth = (float[]) typeWidth.get(data[i][0]);
1162 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1165 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1171 width[i] /= max; // normalize
1172 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1176 sort = sort || width[i - 1] > width[i];
1181 jalview.util.QuickSort.sort(width, data);
1182 // update global priority order
1185 updateFeatureRenderer(data, false);
1193 frame.setClosed(true);
1194 } catch (Exception exe)
1200 public void updateFeatureRenderer(Object[][] data)
1202 updateFeatureRenderer(data, true);
1206 * Update the priority order of features; only repaint if this changed the order
1207 * of visible features
1212 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1214 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1216 if (fr.setFeaturePriority(rowData, visibleNew))
1218 af.alignPanel.paintAlignment(true, true);
1223 * Converts table data into an array of data beans
1225 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1227 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1228 for (int i = 0; i < data.length; i++)
1230 String type = (String) data[i][TYPE_COLUMN];
1231 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1232 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1233 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1234 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1240 private void jbInit() throws Exception
1242 this.setLayout(new BorderLayout());
1244 JPanel settingsPane = new JPanel();
1245 settingsPane.setLayout(new BorderLayout());
1247 dasSettingsPane.setLayout(new BorderLayout());
1249 JPanel bigPanel = new JPanel();
1250 bigPanel.setLayout(new BorderLayout());
1252 groupPanel = new JPanel();
1253 bigPanel.add(groupPanel, BorderLayout.NORTH);
1255 JButton invert = new JButton(
1256 MessageManager.getString("label.invert_selection"));
1257 invert.setFont(JvSwingUtils.getLabelFont());
1258 invert.addActionListener(new ActionListener()
1261 public void actionPerformed(ActionEvent e)
1267 JButton optimizeOrder = new JButton(
1268 MessageManager.getString("label.optimise_order"));
1269 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1270 optimizeOrder.addActionListener(new ActionListener()
1273 public void actionPerformed(ActionEvent e)
1279 JButton sortByScore = new JButton(
1280 MessageManager.getString("label.seq_sort_by_score"));
1281 sortByScore.setFont(JvSwingUtils.getLabelFont());
1282 sortByScore.addActionListener(new ActionListener()
1285 public void actionPerformed(ActionEvent e)
1287 af.avc.sortAlignmentByFeatureScore(null);
1290 JButton sortByDens = new JButton(
1291 MessageManager.getString("label.sequence_sort_by_density"));
1292 sortByDens.setFont(JvSwingUtils.getLabelFont());
1293 sortByDens.addActionListener(new ActionListener()
1296 public void actionPerformed(ActionEvent e)
1298 af.avc.sortAlignmentByFeatureDensity(null);
1302 JButton help = new JButton(MessageManager.getString("action.help"));
1303 help.setFont(JvSwingUtils.getLabelFont());
1304 help.addActionListener(new ActionListener()
1307 public void actionPerformed(ActionEvent e)
1311 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1312 } catch (HelpSetException e1)
1314 e1.printStackTrace();
1318 help.setFont(JvSwingUtils.getLabelFont());
1319 help.setText(MessageManager.getString("action.help"));
1320 help.addActionListener(new ActionListener()
1323 public void actionPerformed(ActionEvent e)
1327 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1328 } catch (HelpSetException e1)
1330 e1.printStackTrace();
1335 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1336 cancel.setFont(JvSwingUtils.getLabelFont());
1337 cancel.addActionListener(new ActionListener()
1340 public void actionPerformed(ActionEvent e)
1342 fr.setTransparency(originalTransparency);
1343 fr.setFeatureFilters(originalFilters);
1344 updateFeatureRenderer(originalData);
1349 JButton ok = new JButton(MessageManager.getString("action.ok"));
1350 ok.setFont(JvSwingUtils.getLabelFont());
1351 ok.addActionListener(new ActionListener()
1354 public void actionPerformed(ActionEvent e)
1360 JButton loadColours = new JButton(
1361 MessageManager.getString("label.load_colours"));
1362 loadColours.setFont(JvSwingUtils.getLabelFont());
1363 loadColours.setToolTipText(
1364 MessageManager.getString("label.load_colours_tooltip"));
1365 loadColours.addActionListener(new ActionListener()
1368 public void actionPerformed(ActionEvent e)
1374 JButton saveColours = new JButton(
1375 MessageManager.getString("label.save_colours"));
1376 saveColours.setFont(JvSwingUtils.getLabelFont());
1377 saveColours.setToolTipText(
1378 MessageManager.getString("label.save_colours_tooltip"));
1379 saveColours.addActionListener(new ActionListener()
1382 public void actionPerformed(ActionEvent e)
1387 transparency.addChangeListener(new ChangeListener()
1390 public void stateChanged(ChangeEvent evt)
1392 if (!inConstruction)
1394 fr.setTransparency((100 - transparency.getValue()) / 100f);
1395 af.alignPanel.paintAlignment(true, true);
1400 summaryView = new JCheckBox(
1401 MessageManager.getString("label.summary_view"));
1404 MessageManager.getString("label.summary_view_tip"));
1405 summaryView.addActionListener(new ActionListener()
1408 public void actionPerformed(ActionEvent e)
1414 transparency.setMaximum(70);
1415 transparency.setToolTipText(
1416 MessageManager.getString("label.transparency_tip"));
1417 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1418 fetchDAS.addActionListener(new ActionListener()
1421 public void actionPerformed(ActionEvent e)
1423 fetchDAS_actionPerformed(e);
1426 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1427 saveDAS.addActionListener(new ActionListener()
1430 public void actionPerformed(ActionEvent e)
1432 saveDAS_actionPerformed(e);
1436 JPanel dasButtonPanel = new JPanel();
1437 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1438 dasSettingsPane.setBorder(null);
1439 cancelDAS.setEnabled(false);
1440 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1441 cancelDAS.addActionListener(new ActionListener()
1444 public void actionPerformed(ActionEvent e)
1446 cancelDAS_actionPerformed(e);
1450 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1451 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1453 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1454 transbuttons.add(optimizeOrder);
1455 transbuttons.add(invert);
1456 transbuttons.add(sortByScore);
1457 transbuttons.add(sortByDens);
1458 transbuttons.add(help);
1459 JPanel transPanel = new JPanel(new GridLayout(3, 1));
1460 transPanel.add(summaryView);
1461 transPanel.add(new JLabel(" Colour transparency" + ":"));
1462 transPanel.add(transparency);
1463 lowerPanel.add(transPanel);
1464 lowerPanel.add(transbuttons);
1466 JPanel buttonPanel = new JPanel();
1467 buttonPanel.add(ok);
1468 buttonPanel.add(cancel);
1469 buttonPanel.add(loadColours);
1470 buttonPanel.add(saveColours);
1471 bigPanel.add(scrollPane, BorderLayout.CENTER);
1472 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1473 dasButtonPanel.add(fetchDAS);
1474 dasButtonPanel.add(cancelDAS);
1475 dasButtonPanel.add(saveDAS);
1476 settingsPane.add(bigPanel, BorderLayout.CENTER);
1477 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1478 this.add(settingsPane);
1481 public void fetchDAS_actionPerformed(ActionEvent e)
1483 fetchDAS.setEnabled(false);
1484 cancelDAS.setEnabled(true);
1485 dassourceBrowser.setGuiEnabled(false);
1486 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1487 .getSelectedSources();
1488 doDasFeatureFetch(selectedSources, true, true);
1492 * get the features from selectedSources for all or the current selection
1494 * @param selectedSources
1495 * @param checkDbRefs
1496 * @param promptFetchDbRefs
1498 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1499 boolean checkDbRefs, boolean promptFetchDbRefs)
1501 SequenceI[] dataset, seqs;
1503 AlignmentViewport vp = af.getViewport();
1504 if (vp.getSelectionGroup() != null
1505 && vp.getSelectionGroup().getSize() > 0)
1507 iSize = vp.getSelectionGroup().getSize();
1508 dataset = new SequenceI[iSize];
1509 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1513 iSize = vp.getAlignment().getHeight();
1514 seqs = vp.getAlignment().getSequencesArray();
1517 dataset = new SequenceI[iSize];
1518 for (int i = 0; i < iSize; i++)
1520 dataset[i] = seqs[i].getDatasetSequence();
1523 cancelDAS.setEnabled(true);
1524 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1525 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1526 af.getViewport().setShowSequenceFeatures(true);
1527 af.showSeqFeatures.setSelected(true);
1531 * blocking call to initialise the das source browser
1533 public void initDasSources()
1535 dassourceBrowser.initDasSources();
1539 * examine the current list of das sources and return any matching the given
1540 * nicknames in sources
1543 * Vector of Strings to resolve to DAS source nicknames.
1544 * @return sources that are present in source list.
1546 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1548 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1552 * get currently selected das sources. ensure you have called initDasSources
1553 * before calling this.
1555 * @return vector of selected das source nicknames
1557 public Vector<jalviewSourceI> getSelectedSources()
1559 return dassourceBrowser.getSelectedSources();
1563 * properly initialise DAS fetcher and then initiate a new thread to fetch
1564 * features from the named sources (rather than any turned on by default)
1568 * if true then runs in same thread, otherwise passes to the Swing
1571 public void fetchDasFeatures(Vector<String> sources, boolean block)
1574 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1575 .resolveSourceNicknames(sources);
1576 if (resolved.size() == 0)
1578 resolved = dassourceBrowser.getSelectedSources();
1580 if (resolved.size() > 0)
1582 final List<jalviewSourceI> dassources = resolved;
1583 fetchDAS.setEnabled(false);
1584 // cancelDAS.setEnabled(true); doDasFetch does this.
1585 Runnable fetcher = new Runnable()
1591 doDasFeatureFetch(dassources, true, false);
1601 SwingUtilities.invokeLater(fetcher);
1606 public void saveDAS_actionPerformed(ActionEvent e)
1609 .saveProperties(jalview.bin.Cache.applicationProperties);
1612 public void complete()
1614 fetchDAS.setEnabled(true);
1615 cancelDAS.setEnabled(false);
1616 dassourceBrowser.setGuiEnabled(true);
1620 public void cancelDAS_actionPerformed(ActionEvent e)
1622 if (dasFeatureFetcher != null)
1624 dasFeatureFetcher.cancel();
1629 public void noDasSourceActive()
1632 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1633 MessageManager.getString("label.no_das_sources_selected_warn"),
1634 MessageManager.getString("label.no_das_sources_selected_title"),
1635 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1639 * Reorders features by 'dragging' selectedRow to 'newRow'
1643 protected void dragRow(int newRow)
1645 if (summaryView.isSelected())
1647 // no drag while in summary view
1651 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1654 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1655 * this could be more than one row away for a very fast drag action
1656 * so just swap it with adjacent rows until we get it there
1658 Object[][] data = ((FeatureTableModel) table.getModel())
1660 int direction = newRow < selectedRow ? -1 : 1;
1661 for (int i = selectedRow; i != newRow; i += direction)
1663 Object[] temp = data[i];
1664 data[i] = data[i + direction];
1665 data[i + direction] = temp;
1667 updateFeatureRenderer(data);
1669 selectedRow = newRow;
1673 protected void refreshTable()
1675 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1676 for (Object[] row : data)
1678 String type = (String) row[TYPE_COLUMN];
1679 FeatureColourI colour = fr.getFeatureColours().get(type);
1680 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1683 filter = new FeatureMatcherSet();
1685 row[COLOUR_COLUMN] = colour;
1686 row[FILTER_COLUMN] = filter;
1691 // ///////////////////////////////////////////////////////////////////////
1692 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1693 // ///////////////////////////////////////////////////////////////////////
1694 class FeatureTableModel extends AbstractTableModel
1696 private String[] columnNames = {
1697 MessageManager.getString("label.feature_type"),
1698 MessageManager.getString("action.colour"),
1699 MessageManager.getString("label.filter"),
1700 MessageManager.getString("label.show") };
1702 private Object[][] data;
1704 FeatureTableModel(Object[][] data)
1709 public Object[][] getData()
1714 public void setData(Object[][] data)
1720 public int getColumnCount()
1722 return columnNames.length;
1725 public Object[] getRow(int row)
1731 public int getRowCount()
1737 public String getColumnName(int col)
1739 return columnNames[col];
1743 public Object getValueAt(int row, int col)
1745 return data[row][col];
1749 * Answers the class of the object in column c of the first row of the table
1752 public Class<?> getColumnClass(int c)
1754 Object v = getValueAt(0, c);
1755 return v == null ? null : v.getClass();
1759 * Answers true for all columns except Feature Type
1762 public boolean isCellEditable(int row, int col)
1764 return col != TYPE_COLUMN;
1768 * Sets the value in the model for a given row and column. If Visibility
1769 * (Show/Hide) is being set, and the table is in Summary View, then it is
1770 * set also on any sub-types of the row's feature type.
1773 public void setValueAt(Object value, int row, int col)
1775 data[row][col] = value;
1776 fireTableCellUpdated(row, col);
1777 if (summaryView.isSelected() && col == SHOW_COLUMN)
1779 setSubtypesVisibility(row, (Boolean) value);
1781 updateFeatureRenderer(data);
1785 * Sets the visibility of any feature types which are sub-types of the type
1786 * in the given row of the table
1791 protected void setSubtypesVisibility(int row, Boolean value)
1793 String type = (String) data[row][TYPE_COLUMN];
1794 OntologyI so = SequenceOntologyFactory.getInstance();
1796 for (int r = 0; r < data.length; r++)
1800 String type2 = (String) data[r][TYPE_COLUMN];
1801 if (so.isA(type2, type))
1803 data[r][SHOW_COLUMN] = value;
1804 fireTableCellUpdated(r, SHOW_COLUMN);
1812 class ColorRenderer extends JLabel implements TableCellRenderer
1814 javax.swing.border.Border unselectedBorder = null;
1816 javax.swing.border.Border selectedBorder = null;
1818 final String baseTT = "Click to edit, right/apple click for menu.";
1820 public ColorRenderer()
1822 setOpaque(true); // MUST do this for background to show up.
1823 setHorizontalTextPosition(SwingConstants.CENTER);
1824 setVerticalTextPosition(SwingConstants.CENTER);
1828 public Component getTableCellRendererComponent(JTable tbl, Object color,
1829 boolean isSelected, boolean hasFocus, int row, int column)
1831 FeatureColourI cellColour = (FeatureColourI) color;
1833 setToolTipText(baseTT);
1834 setBackground(tbl.getBackground());
1835 if (!cellColour.isSimpleColour())
1837 Rectangle cr = tbl.getCellRect(row, column, false);
1838 FeatureSettings.renderGraduatedColor(this, cellColour,
1839 (int) cr.getWidth(), (int) cr.getHeight());
1845 setBackground(cellColour.getColour());
1849 if (selectedBorder == null)
1851 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1852 tbl.getSelectionBackground());
1854 setBorder(selectedBorder);
1858 if (unselectedBorder == null)
1860 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1861 tbl.getBackground());
1863 setBorder(unselectedBorder);
1870 class FilterRenderer extends JLabel implements TableCellRenderer
1872 javax.swing.border.Border unselectedBorder = null;
1874 javax.swing.border.Border selectedBorder = null;
1876 public FilterRenderer()
1878 setOpaque(true); // MUST do this for background to show up.
1879 setHorizontalTextPosition(SwingConstants.CENTER);
1880 setVerticalTextPosition(SwingConstants.CENTER);
1884 public Component getTableCellRendererComponent(JTable tbl,
1885 Object filter, boolean isSelected, boolean hasFocus, int row,
1888 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1890 String asText = theFilter.toString();
1891 setBackground(tbl.getBackground());
1892 this.setText(asText);
1897 if (selectedBorder == null)
1899 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1900 tbl.getSelectionBackground());
1902 setBorder(selectedBorder);
1906 if (unselectedBorder == null)
1908 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1909 tbl.getBackground());
1911 setBorder(unselectedBorder);
1919 * update comp using rendering settings from gcol
1924 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1926 int w = comp.getWidth(), h = comp.getHeight();
1929 w = (int) comp.getPreferredSize().getWidth();
1930 h = (int) comp.getPreferredSize().getHeight();
1937 renderGraduatedColor(comp, gcol, w, h);
1940 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1943 boolean thr = false;
1944 StringBuilder tt = new StringBuilder();
1945 StringBuilder tx = new StringBuilder();
1947 if (gcol.isColourByAttribute())
1949 tx.append(String.join(":", gcol.getAttributeName()));
1951 else if (!gcol.isColourByLabel())
1953 tx.append(MessageManager.getString("label.score"));
1956 if (gcol.isAboveThreshold())
1960 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1963 if (gcol.isBelowThreshold())
1967 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1970 if (gcol.isColourByLabel())
1972 tt.append("Coloured by label text. ").append(tt);
1977 if (!gcol.isColourByAttribute())
1985 Color newColor = gcol.getMaxColour();
1986 comp.setBackground(newColor);
1987 // System.err.println("Width is " + w / 2);
1988 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1989 comp.setIcon(ficon);
1990 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1991 // + newColor.getGreen() + ", " + newColor.getBlue()
1992 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1993 // + ", " + minCol.getBlue() + ")");
1995 comp.setHorizontalAlignment(SwingConstants.CENTER);
1996 comp.setText(tx.toString());
1997 if (tt.length() > 0)
1999 if (comp.getToolTipText() == null)
2001 comp.setToolTipText(tt.toString());
2005 comp.setToolTipText(
2006 tt.append(" ").append(comp.getToolTipText()).toString());
2011 class ColorEditor extends AbstractCellEditor
2012 implements TableCellEditor, ActionListener
2016 FeatureColourI currentColor;
2018 FeatureTypeSettings chooser;
2022 JButton colourButton;
2024 JColorChooser colorChooser;
2028 protected static final String EDIT = "edit";
2030 int rowSelected = 0;
2032 public ColorEditor(FeatureSettings me)
2035 // Set up the editor (from the table's point of view),
2036 // which is a button.
2037 // This button brings up the color chooser dialog,
2038 // which is the editor from the user's point of view.
2039 colourButton = new JButton();
2040 colourButton.setActionCommand(EDIT);
2041 colourButton.addActionListener(this);
2042 colourButton.setBorderPainted(false);
2043 // Set up the dialog that the button brings up.
2044 colorChooser = new JColorChooser();
2045 dialog = JColorChooser.createDialog(colourButton,
2046 MessageManager.getString("label.select_colour"), true, // modal
2047 colorChooser, this, // OK button handler
2048 null); // no CANCEL button handler
2052 * Handles events from the editor button and from the dialog's OK button.
2055 public void actionPerformed(ActionEvent e)
2057 // todo test e.getSource() instead here
2058 if (EDIT.equals(e.getActionCommand()))
2060 // The user has clicked the cell, so
2061 // bring up the dialog.
2062 if (currentColor.isSimpleColour())
2064 // bring up simple color chooser
2065 colourButton.setBackground(currentColor.getColour());
2066 colorChooser.setColor(currentColor.getColour());
2067 dialog.setVisible(true);
2071 // bring up graduated chooser.
2072 chooser = new FeatureTypeSettings(me.fr, type);
2073 chooser.setRequestFocusEnabled(true);
2074 chooser.requestFocus();
2075 chooser.addActionListener(this);
2076 chooser.showTab(true);
2078 // Make the renderer reappear.
2079 fireEditingStopped();
2083 if (currentColor.isSimpleColour())
2086 * read off colour picked in colour chooser after OK pressed
2088 currentColor = new FeatureColour(colorChooser.getColor());
2089 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
2094 * after OK in variable colour dialog, any changes to colour
2095 * (or filters!) are already set in FeatureRenderer, so just
2096 * update table data without triggering updateFeatureRenderer
2100 fireEditingStopped();
2101 me.table.validate();
2105 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2107 public Object getCellEditorValue()
2109 return currentColor;
2112 // Implement the one method defined by TableCellEditor.
2114 public Component getTableCellEditorComponent(JTable theTable, Object value,
2115 boolean isSelected, int row, int column)
2117 currentColor = (FeatureColourI) value;
2118 this.rowSelected = row;
2119 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2120 colourButton.setOpaque(true);
2121 colourButton.setBackground(me.getBackground());
2122 if (!currentColor.isSimpleColour())
2124 JLabel btn = new JLabel();
2125 btn.setSize(colourButton.getSize());
2126 FeatureSettings.renderGraduatedColor(btn, currentColor);
2127 colourButton.setBackground(btn.getBackground());
2128 colourButton.setIcon(btn.getIcon());
2129 colourButton.setText(btn.getText());
2133 colourButton.setText("");
2134 colourButton.setIcon(null);
2135 colourButton.setBackground(currentColor.getColour());
2137 return colourButton;
2142 * The cell editor for the Filter column. It displays the text of any filters
2143 * for the feature type in that row (in full as a tooltip, possible abbreviated
2144 * as display text). On click in the cell, opens the Feature Display Settings
2145 * dialog at the Filters tab.
2147 class FilterEditor extends AbstractCellEditor
2148 implements TableCellEditor, ActionListener
2152 FeatureMatcherSetI currentFilter;
2158 JButton filterButton;
2160 protected static final String EDIT = "edit";
2162 int rowSelected = 0;
2164 public FilterEditor(FeatureSettings me)
2167 filterButton = new JButton();
2168 filterButton.setActionCommand(EDIT);
2169 filterButton.addActionListener(this);
2170 filterButton.setBorderPainted(false);
2174 * Handles events from the editor button
2177 public void actionPerformed(ActionEvent e)
2179 if (filterButton == e.getSource())
2181 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2182 chooser.addActionListener(this);
2183 chooser.setRequestFocusEnabled(true);
2184 chooser.requestFocus();
2185 if (lastLocation != null)
2187 // todo open at its last position on screen
2188 chooser.setBounds(lastLocation.x, lastLocation.y,
2189 chooser.getWidth(), chooser.getHeight());
2192 chooser.showTab(false);
2193 fireEditingStopped();
2195 else if (e.getSource() instanceof Component)
2198 * after OK in variable colour dialog, any changes to filter
2199 * (or colours!) are already set in FeatureRenderer, so just
2200 * update table data without triggering updateFeatureRenderer
2203 fireEditingStopped();
2204 me.table.validate();
2209 public Object getCellEditorValue()
2211 return currentFilter;
2215 public Component getTableCellEditorComponent(JTable theTable, Object value,
2216 boolean isSelected, int row, int column)
2218 currentFilter = (FeatureMatcherSetI) value;
2219 this.rowSelected = row;
2220 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2221 filterButton.setOpaque(true);
2222 filterButton.setBackground(me.getBackground());
2223 filterButton.setText(currentFilter.toString());
2224 filterButton.setToolTipText(currentFilter.toString());
2225 filterButton.setIcon(null);
2226 return filterButton;
2231 class FeatureIcon implements Icon
2233 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2235 FeatureColourI gcol;
2239 boolean midspace = false;
2241 int width = 50, height = 20;
2243 int s1, e1; // start and end of midpoint band for thresholded symbol
2245 Color mpcolour = Color.white;
2247 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2267 public int getIconWidth()
2273 public int getIconHeight()
2279 public void paintIcon(Component c, Graphics g, int x, int y)
2282 if (gcol.isColourByLabel())
2285 g.fillRect(0, 0, width, height);
2286 // need an icon here.
2287 g.setColor(gcol.getMaxColour());
2289 g.setFont(VERDANA_9);
2291 // g.setFont(g.getFont().deriveFont(
2292 // AffineTransform.getScaleInstance(
2293 // width/g.getFontMetrics().stringWidth("Label"),
2294 // height/g.getFontMetrics().getHeight())));
2296 g.drawString(MessageManager.getString("label.label"), 0, 0);
2301 Color minCol = gcol.getMinColour();
2303 g.fillRect(0, 0, s1, height);
2306 g.setColor(Color.white);
2307 g.fillRect(s1, 0, e1 - s1, height);
2309 g.setColor(gcol.getMaxColour());
2310 g.fillRect(0, e1, width - e1, height);