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 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
540 JMenuItem clearCols = new JMenuItem(MessageManager
541 .getString("label.select_columns_not_containing"));
542 clearCols.addActionListener(new ActionListener()
545 public void actionPerformed(ActionEvent arg0)
547 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
551 JMenuItem hideCols = new JMenuItem(
552 MessageManager.getString("label.hide_columns_containing"));
553 hideCols.addActionListener(new ActionListener()
556 public void actionPerformed(ActionEvent arg0)
558 fr.ap.alignFrame.hideFeatureColumns(type, true);
561 JMenuItem hideOtherCols = new JMenuItem(
562 MessageManager.getString("label.hide_columns_not_containing"));
563 hideOtherCols.addActionListener(new ActionListener()
566 public void actionPerformed(ActionEvent arg0)
568 fr.ap.alignFrame.hideFeatureColumns(type, false);
574 men.add(hideOtherCols);
575 men.show(table, x, y);
579 synchronized public void discoverAllFeatureData()
581 Set<String> allGroups = new HashSet<>();
582 AlignmentI alignment = af.getViewport().getAlignment();
584 for (int i = 0; i < alignment.getHeight(); i++)
586 SequenceI seq = alignment.getSequenceAt(i);
587 for (String group : seq.getFeatures().getFeatureGroups(true))
589 if (group != null && !allGroups.contains(group))
591 allGroups.add(group);
592 checkGroupState(group);
603 * Synchronise gui group list and check visibility of group
606 * @return true if group is visible
608 private boolean checkGroupState(String group)
610 boolean visible = fr.checkGroupVisibility(group, true);
612 for (int g = 0; g < groupPanel.getComponentCount(); g++)
614 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
616 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
621 final String grp = group;
622 final JCheckBox check = new JCheckBox(group, visible);
623 check.setFont(new Font("Serif", Font.BOLD, 12));
624 check.setToolTipText(group);
625 check.addItemListener(new ItemListener()
628 public void itemStateChanged(ItemEvent evt)
630 fr.setGroupVisibility(check.getText(), check.isSelected());
631 resetTable(new String[] { grp });
632 af.alignPanel.paintAlignment(true, true);
635 groupPanel.add(check);
639 synchronized void resetTable(String[] groupChanged)
645 resettingTable = true;
646 typeWidth = new Hashtable<>();
647 // TODO: change avWidth calculation to 'per-sequence' average and use long
650 Set<String> displayableTypes = new HashSet<>();
651 Set<String> foundGroups = new HashSet<>();
654 * determine which feature types may be visible depending on
655 * which groups are selected, and recompute average width data
657 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
660 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
663 * get the sequence's groups for positional features
664 * and keep track of which groups are visible
666 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
667 Set<String> visibleGroups = new HashSet<>();
668 for (String group : groups)
670 if (group == null || checkGroupState(group))
672 visibleGroups.add(group);
675 foundGroups.addAll(groups);
678 * get distinct feature types for visible groups
679 * record distinct visible types, and their count and total length
681 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
682 visibleGroups.toArray(new String[visibleGroups.size()]));
684 for (String type : types)
686 displayableTypes.add(type);
687 float[] avWidth = typeWidth.get(type);
690 avWidth = new float[2];
691 typeWidth.put(type, avWidth);
693 // todo this could include features with a non-visible group
694 // - do we greatly care?
695 // todo should we include non-displayable features here, and only
696 // update when features are added?
697 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
698 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
703 * enable 'Summary View' if some types are sub-types of others
705 Set<String> parents = SequenceOntologyFactory.getInstance()
706 .getParentTerms(displayableTypes);
707 summaryView.setEnabled(parents.size() < displayableTypes.size());
709 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
712 if (fr.hasRenderOrder())
716 fr.findAllFeatures(groupChanged != null); // prod to update
717 // colourschemes. but don't
719 // First add the checks in the previous render order,
720 // in case the window has been closed and reopened
722 List<String> frl = fr.getRenderOrder();
723 for (int ro = frl.size() - 1; ro > -1; ro--)
725 String type = frl.get(ro);
727 if (!displayableTypes.contains(type))
731 data[dataIndex][TYPE_COLUMN] = type;
732 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
733 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
734 data[dataIndex][FILTER_COLUMN] = featureFilter == null
735 ? new FeatureMatcherSet()
737 data[dataIndex][SHOW_COLUMN] = new Boolean(
738 af.getViewport().getFeaturesDisplayed().isVisible(type));
740 displayableTypes.remove(type);
745 * process any extra features belonging only to
746 * a group which was just selected
748 while (!displayableTypes.isEmpty())
750 String type = displayableTypes.iterator().next();
751 data[dataIndex][TYPE_COLUMN] = type;
753 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
754 if (data[dataIndex][COLOUR_COLUMN] == null)
756 // "Colour has been updated in another view!!"
757 fr.clearRenderOrder();
760 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
761 data[dataIndex][FILTER_COLUMN] = featureFilter == null
762 ? new FeatureMatcherSet()
764 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
766 displayableTypes.remove(type);
769 if (originalData == null)
771 originalData = new Object[data.length][COLUMN_COUNT];
772 for (int i = 0; i < data.length; i++)
774 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
779 updateOriginalData(data);
783 * recreate the table model
785 FeatureTableModel dataModel = new FeatureTableModel(data);
786 table.setModel(dataModel);
789 * we want to be able to filter out rows for sub-types, but not to sort
790 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
792 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
794 for (int i = 0; i < table.getColumnCount(); i++)
796 sorter.setSortable(i, false);
800 * filter rows to only top-level Ontology types if requested
802 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
805 public boolean include(
806 Entry<? extends FeatureTableModel, ? extends Integer> entry)
808 if (!summaryView.isSelected())
812 int row = entry.getIdentifier(); // this is model, not view, row number
813 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
814 return parents.contains(featureType);
817 table.setRowSorter(sorter);
819 table.getColumnModel().getColumn(0).setPreferredWidth(200);
821 groupPanel.setLayout(
822 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
823 pruneGroups(foundGroups);
824 groupPanel.validate();
826 updateFeatureRenderer(data, groupChanged != null);
827 resettingTable = false;
831 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
832 * have been made outwith this dialog
834 * <li>a new feature type added (and made visible)</li>
835 * <li>a feature colour changed (in the Amend Features dialog)</li>
840 protected void updateOriginalData(Object[][] foundData)
842 // todo LinkedHashMap instead of Object[][] would be nice
844 Object[][] currentData = ((FeatureTableModel) table.getModel())
846 for (Object[] row : foundData)
848 String type = (String) row[TYPE_COLUMN];
849 boolean found = false;
850 for (Object[] current : currentData)
852 if (type.equals(current[TYPE_COLUMN]))
856 * currently dependent on object equality here;
857 * really need an equals method on FeatureColour
859 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
862 * feature colour has changed externally - update originalData
864 for (Object[] original : originalData)
866 if (type.equals(original[TYPE_COLUMN]))
868 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
879 * new feature detected - add to original data (on top)
881 Object[][] newData = new Object[originalData.length
883 for (int i = 0; i < originalData.length; i++)
885 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
889 originalData = newData;
895 * Remove from the groups panel any checkboxes for groups that are not in the
896 * foundGroups set. This enables removing a group from the display when the last
897 * feature in that group is deleted.
901 protected void pruneGroups(Set<String> foundGroups)
903 for (int g = 0; g < groupPanel.getComponentCount(); g++)
905 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
906 if (!foundGroups.contains(checkbox.getText()))
908 groupPanel.remove(checkbox);
914 * reorder data based on the featureRenderers global priority list.
918 private void ensureOrder(Object[][] data)
920 boolean sort = false;
921 float[] order = new float[data.length];
922 for (int i = 0; i < order.length; i++)
924 order[i] = fr.getOrder(data[i][0].toString());
927 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
931 sort = sort || order[i - 1] > order[i];
936 jalview.util.QuickSort.sort(order, data);
941 * Offers a file chooser dialog, and then loads the feature colours and
942 * filters from file in XML format and unmarshals to Jalview feature settings
946 JalviewFileChooser chooser = new JalviewFileChooser("fc",
947 SEQUENCE_FEATURE_COLOURS);
948 chooser.setFileView(new JalviewFileView());
949 chooser.setDialogTitle(
950 MessageManager.getString("label.load_feature_colours"));
951 chooser.setToolTipText(MessageManager.getString("action.load"));
953 int value = chooser.showOpenDialog(this);
955 if (value == JalviewFileChooser.APPROVE_OPTION)
957 File file = chooser.getSelectedFile();
963 * Loads feature colours and filters from XML stored in the given file
971 InputStreamReader in = new InputStreamReader(
972 new FileInputStream(file), "UTF-8");
974 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
977 * load feature colours
979 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
981 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
982 FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
983 fr.setColour(newcol.getName(), colour);
984 fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
988 * load feature filters; loaded filters will replace any that are
989 * currently defined, other defined filters are left unchanged
991 for (int i = 0; i < jucs.getFilterCount(); i++)
993 jalview.schemabinding.version2.Filter filterModel = jucs
995 String featureType = filterModel.getFeatureType();
996 FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
997 filterModel.getMatcherSet());
998 if (!filter.isEmpty())
1000 fr.setFeatureFilter(featureType, filter);
1005 * update feature settings table
1010 Object[][] data = ((FeatureTableModel) table.getModel())
1013 updateFeatureRenderer(data, false);
1016 } catch (Exception ex)
1018 System.out.println("Error loading User Colour File\n" + ex);
1023 * Offers a file chooser dialog, and then saves the current feature colours
1024 * and any filters to the selected file in XML format
1028 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1029 SEQUENCE_FEATURE_COLOURS);
1030 chooser.setFileView(new JalviewFileView());
1031 chooser.setDialogTitle(
1032 MessageManager.getString("label.save_feature_colours"));
1033 chooser.setToolTipText(MessageManager.getString("action.save"));
1035 int value = chooser.showSaveDialog(this);
1037 if (value == JalviewFileChooser.APPROVE_OPTION)
1039 save(chooser.getSelectedFile());
1044 * Saves feature colours and filters to the given file
1048 void save(File file)
1050 JalviewUserColours ucs = new JalviewUserColours();
1051 ucs.setSchemeName("Sequence Features");
1054 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1055 new FileOutputStream(file), "UTF-8"));
1058 * sort feature types by colour order, from 0 (highest)
1061 Set<String> fr_colours = fr.getAllFeatureColours();
1062 String[] sortedTypes = fr_colours
1063 .toArray(new String[fr_colours.size()]);
1064 Arrays.sort(sortedTypes, new Comparator<String>()
1067 public int compare(String type1, String type2)
1069 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1074 * save feature colours
1076 for (String featureType : sortedTypes)
1078 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1079 jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
1085 * save any feature filters
1087 for (String featureType : sortedTypes)
1089 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1090 if (filter != null && !filter.isEmpty())
1092 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1093 FeatureMatcherI firstMatcher = iterator.next();
1094 MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
1096 Filter filterModel = new Filter();
1097 filterModel.setFeatureType(featureType);
1098 filterModel.setMatcherSet(ms);
1099 ucs.addFilter(filterModel);
1105 } catch (Exception ex)
1107 ex.printStackTrace();
1111 public void invertSelection()
1113 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1114 for (int i = 0; i < data.length; i++)
1116 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1118 updateFeatureRenderer(data, true);
1122 public void orderByAvWidth()
1124 if (table == null || table.getModel() == null)
1128 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1129 float[] width = new float[data.length];
1133 for (int i = 0; i < data.length; i++)
1135 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1138 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1139 // weight - but have to make per
1140 // sequence, too (awidth[2])
1141 // if (width[i]==1) // hack to distinguish single width sequences.
1152 boolean sort = false;
1153 for (int i = 0; i < width.length; i++)
1155 // awidth = (float[]) typeWidth.get(data[i][0]);
1158 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1161 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1167 width[i] /= max; // normalize
1168 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1172 sort = sort || width[i - 1] > width[i];
1177 jalview.util.QuickSort.sort(width, data);
1178 // update global priority order
1181 updateFeatureRenderer(data, false);
1189 frame.setClosed(true);
1190 } catch (Exception exe)
1196 public void updateFeatureRenderer(Object[][] data)
1198 updateFeatureRenderer(data, true);
1202 * Update the priority order of features; only repaint if this changed the order
1203 * of visible features
1208 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1210 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1212 if (fr.setFeaturePriority(rowData, visibleNew))
1214 af.alignPanel.paintAlignment(true, true);
1219 * Converts table data into an array of data beans
1221 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1223 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1224 for (int i = 0; i < data.length; i++)
1226 String type = (String) data[i][TYPE_COLUMN];
1227 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1228 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1229 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1230 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1236 private void jbInit() throws Exception
1238 this.setLayout(new BorderLayout());
1240 JPanel settingsPane = new JPanel();
1241 settingsPane.setLayout(new BorderLayout());
1243 dasSettingsPane.setLayout(new BorderLayout());
1245 JPanel bigPanel = new JPanel();
1246 bigPanel.setLayout(new BorderLayout());
1248 groupPanel = new JPanel();
1249 bigPanel.add(groupPanel, BorderLayout.NORTH);
1251 JButton invert = new JButton(
1252 MessageManager.getString("label.invert_selection"));
1253 invert.setFont(JvSwingUtils.getLabelFont());
1254 invert.addActionListener(new ActionListener()
1257 public void actionPerformed(ActionEvent e)
1263 JButton optimizeOrder = new JButton(
1264 MessageManager.getString("label.optimise_order"));
1265 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1266 optimizeOrder.addActionListener(new ActionListener()
1269 public void actionPerformed(ActionEvent e)
1275 JButton sortByScore = new JButton(
1276 MessageManager.getString("label.seq_sort_by_score"));
1277 sortByScore.setFont(JvSwingUtils.getLabelFont());
1278 sortByScore.addActionListener(new ActionListener()
1281 public void actionPerformed(ActionEvent e)
1283 af.avc.sortAlignmentByFeatureScore(null);
1286 JButton sortByDens = new JButton(
1287 MessageManager.getString("label.sequence_sort_by_density"));
1288 sortByDens.setFont(JvSwingUtils.getLabelFont());
1289 sortByDens.addActionListener(new ActionListener()
1292 public void actionPerformed(ActionEvent e)
1294 af.avc.sortAlignmentByFeatureDensity(null);
1298 JButton help = new JButton(MessageManager.getString("action.help"));
1299 help.setFont(JvSwingUtils.getLabelFont());
1300 help.addActionListener(new ActionListener()
1303 public void actionPerformed(ActionEvent e)
1307 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1308 } catch (HelpSetException e1)
1310 e1.printStackTrace();
1314 help.setFont(JvSwingUtils.getLabelFont());
1315 help.setText(MessageManager.getString("action.help"));
1316 help.addActionListener(new ActionListener()
1319 public void actionPerformed(ActionEvent e)
1323 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1324 } catch (HelpSetException e1)
1326 e1.printStackTrace();
1331 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1332 cancel.setFont(JvSwingUtils.getLabelFont());
1333 cancel.addActionListener(new ActionListener()
1336 public void actionPerformed(ActionEvent e)
1338 fr.setTransparency(originalTransparency);
1339 fr.setFeatureFilters(originalFilters);
1340 updateFeatureRenderer(originalData);
1345 JButton ok = new JButton(MessageManager.getString("action.ok"));
1346 ok.setFont(JvSwingUtils.getLabelFont());
1347 ok.addActionListener(new ActionListener()
1350 public void actionPerformed(ActionEvent e)
1356 JButton loadColours = new JButton(
1357 MessageManager.getString("label.load_colours"));
1358 loadColours.setFont(JvSwingUtils.getLabelFont());
1359 loadColours.setToolTipText(
1360 MessageManager.getString("label.load_colours_tooltip"));
1361 loadColours.addActionListener(new ActionListener()
1364 public void actionPerformed(ActionEvent e)
1370 JButton saveColours = new JButton(
1371 MessageManager.getString("label.save_colours"));
1372 saveColours.setFont(JvSwingUtils.getLabelFont());
1373 saveColours.setToolTipText(
1374 MessageManager.getString("label.save_colours_tooltip"));
1375 saveColours.addActionListener(new ActionListener()
1378 public void actionPerformed(ActionEvent e)
1383 transparency.addChangeListener(new ChangeListener()
1386 public void stateChanged(ChangeEvent evt)
1388 if (!inConstruction)
1390 fr.setTransparency((100 - transparency.getValue()) / 100f);
1391 af.alignPanel.paintAlignment(true, true);
1396 summaryView = new JCheckBox(
1397 MessageManager.getString("label.summary_view"));
1400 MessageManager.getString("label.summary_view_tip"));
1401 summaryView.addActionListener(new ActionListener()
1404 public void actionPerformed(ActionEvent e)
1410 transparency.setMaximum(70);
1411 transparency.setToolTipText(
1412 MessageManager.getString("label.transparency_tip"));
1413 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1414 fetchDAS.addActionListener(new ActionListener()
1417 public void actionPerformed(ActionEvent e)
1419 fetchDAS_actionPerformed(e);
1422 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1423 saveDAS.addActionListener(new ActionListener()
1426 public void actionPerformed(ActionEvent e)
1428 saveDAS_actionPerformed(e);
1432 JPanel dasButtonPanel = new JPanel();
1433 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1434 dasSettingsPane.setBorder(null);
1435 cancelDAS.setEnabled(false);
1436 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1437 cancelDAS.addActionListener(new ActionListener()
1440 public void actionPerformed(ActionEvent e)
1442 cancelDAS_actionPerformed(e);
1446 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1447 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1449 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1450 transbuttons.add(optimizeOrder);
1451 transbuttons.add(invert);
1452 transbuttons.add(sortByScore);
1453 transbuttons.add(sortByDens);
1454 transbuttons.add(help);
1455 JPanel transPanel = new JPanel(new GridLayout(3, 1));
1456 transPanel.add(summaryView);
1457 transPanel.add(new JLabel(" Colour transparency" + ":"));
1458 transPanel.add(transparency);
1459 lowerPanel.add(transPanel);
1460 lowerPanel.add(transbuttons);
1462 JPanel buttonPanel = new JPanel();
1463 buttonPanel.add(ok);
1464 buttonPanel.add(cancel);
1465 buttonPanel.add(loadColours);
1466 buttonPanel.add(saveColours);
1467 bigPanel.add(scrollPane, BorderLayout.CENTER);
1468 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1469 dasButtonPanel.add(fetchDAS);
1470 dasButtonPanel.add(cancelDAS);
1471 dasButtonPanel.add(saveDAS);
1472 settingsPane.add(bigPanel, BorderLayout.CENTER);
1473 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1474 this.add(settingsPane);
1477 public void fetchDAS_actionPerformed(ActionEvent e)
1479 fetchDAS.setEnabled(false);
1480 cancelDAS.setEnabled(true);
1481 dassourceBrowser.setGuiEnabled(false);
1482 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1483 .getSelectedSources();
1484 doDasFeatureFetch(selectedSources, true, true);
1488 * get the features from selectedSources for all or the current selection
1490 * @param selectedSources
1491 * @param checkDbRefs
1492 * @param promptFetchDbRefs
1494 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1495 boolean checkDbRefs, boolean promptFetchDbRefs)
1497 SequenceI[] dataset, seqs;
1499 AlignmentViewport vp = af.getViewport();
1500 if (vp.getSelectionGroup() != null
1501 && vp.getSelectionGroup().getSize() > 0)
1503 iSize = vp.getSelectionGroup().getSize();
1504 dataset = new SequenceI[iSize];
1505 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1509 iSize = vp.getAlignment().getHeight();
1510 seqs = vp.getAlignment().getSequencesArray();
1513 dataset = new SequenceI[iSize];
1514 for (int i = 0; i < iSize; i++)
1516 dataset[i] = seqs[i].getDatasetSequence();
1519 cancelDAS.setEnabled(true);
1520 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1521 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1522 af.getViewport().setShowSequenceFeatures(true);
1523 af.showSeqFeatures.setSelected(true);
1527 * blocking call to initialise the das source browser
1529 public void initDasSources()
1531 dassourceBrowser.initDasSources();
1535 * examine the current list of das sources and return any matching the given
1536 * nicknames in sources
1539 * Vector of Strings to resolve to DAS source nicknames.
1540 * @return sources that are present in source list.
1542 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1544 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1548 * get currently selected das sources. ensure you have called initDasSources
1549 * before calling this.
1551 * @return vector of selected das source nicknames
1553 public Vector<jalviewSourceI> getSelectedSources()
1555 return dassourceBrowser.getSelectedSources();
1559 * properly initialise DAS fetcher and then initiate a new thread to fetch
1560 * features from the named sources (rather than any turned on by default)
1564 * if true then runs in same thread, otherwise passes to the Swing
1567 public void fetchDasFeatures(Vector<String> sources, boolean block)
1570 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1571 .resolveSourceNicknames(sources);
1572 if (resolved.size() == 0)
1574 resolved = dassourceBrowser.getSelectedSources();
1576 if (resolved.size() > 0)
1578 final List<jalviewSourceI> dassources = resolved;
1579 fetchDAS.setEnabled(false);
1580 // cancelDAS.setEnabled(true); doDasFetch does this.
1581 Runnable fetcher = new Runnable()
1587 doDasFeatureFetch(dassources, true, false);
1597 SwingUtilities.invokeLater(fetcher);
1602 public void saveDAS_actionPerformed(ActionEvent e)
1605 .saveProperties(jalview.bin.Cache.applicationProperties);
1608 public void complete()
1610 fetchDAS.setEnabled(true);
1611 cancelDAS.setEnabled(false);
1612 dassourceBrowser.setGuiEnabled(true);
1616 public void cancelDAS_actionPerformed(ActionEvent e)
1618 if (dasFeatureFetcher != null)
1620 dasFeatureFetcher.cancel();
1625 public void noDasSourceActive()
1628 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1629 MessageManager.getString("label.no_das_sources_selected_warn"),
1630 MessageManager.getString("label.no_das_sources_selected_title"),
1631 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1635 * Reorders features by 'dragging' selectedRow to 'newRow'
1639 protected void dragRow(int newRow)
1641 if (summaryView.isSelected())
1643 // no drag while in summary view
1647 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1650 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1651 * this could be more than one row away for a very fast drag action
1652 * so just swap it with adjacent rows until we get it there
1654 Object[][] data = ((FeatureTableModel) table.getModel())
1656 int direction = newRow < selectedRow ? -1 : 1;
1657 for (int i = selectedRow; i != newRow; i += direction)
1659 Object[] temp = data[i];
1660 data[i] = data[i + direction];
1661 data[i + direction] = temp;
1663 updateFeatureRenderer(data);
1665 selectedRow = newRow;
1669 protected void refreshTable()
1671 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1672 for (Object[] row : data)
1674 String type = (String) row[TYPE_COLUMN];
1675 FeatureColourI colour = fr.getFeatureColours().get(type);
1676 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1679 filter = new FeatureMatcherSet();
1681 row[COLOUR_COLUMN] = colour;
1682 row[FILTER_COLUMN] = filter;
1687 // ///////////////////////////////////////////////////////////////////////
1688 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1689 // ///////////////////////////////////////////////////////////////////////
1690 class FeatureTableModel extends AbstractTableModel
1692 private String[] columnNames = {
1693 MessageManager.getString("label.feature_type"),
1694 MessageManager.getString("action.colour"),
1695 MessageManager.getString("label.filter"),
1696 MessageManager.getString("label.show") };
1698 private Object[][] data;
1700 FeatureTableModel(Object[][] data)
1705 public Object[][] getData()
1710 public void setData(Object[][] data)
1716 public int getColumnCount()
1718 return columnNames.length;
1721 public Object[] getRow(int row)
1727 public int getRowCount()
1733 public String getColumnName(int col)
1735 return columnNames[col];
1739 public Object getValueAt(int row, int col)
1741 return data[row][col];
1745 * Answers the class of the object in column c of the first row of the table
1748 public Class<?> getColumnClass(int c)
1750 Object v = getValueAt(0, c);
1751 return v == null ? null : v.getClass();
1755 * Answers true for all columns except Feature Type
1758 public boolean isCellEditable(int row, int col)
1760 return col != TYPE_COLUMN;
1764 * Sets the value in the model for a given row and column. If Visibility
1765 * (Show/Hide) is being set, and the table is in Summary View, then it is
1766 * set also on any sub-types of the row's feature type.
1769 public void setValueAt(Object value, int row, int col)
1771 data[row][col] = value;
1772 fireTableCellUpdated(row, col);
1773 if (summaryView.isSelected() && col == SHOW_COLUMN)
1775 setSubtypesVisibility(row, (Boolean) value);
1777 updateFeatureRenderer(data);
1781 * Sets the visibility of any feature types which are sub-types of the type
1782 * in the given row of the table
1787 protected void setSubtypesVisibility(int row, Boolean value)
1789 String type = (String) data[row][TYPE_COLUMN];
1790 OntologyI so = SequenceOntologyFactory.getInstance();
1792 for (int r = 0; r < data.length; r++)
1796 String type2 = (String) data[r][TYPE_COLUMN];
1797 if (so.isA(type2, type))
1799 data[r][SHOW_COLUMN] = value;
1800 fireTableCellUpdated(r, SHOW_COLUMN);
1808 class ColorRenderer extends JLabel implements TableCellRenderer
1810 javax.swing.border.Border unselectedBorder = null;
1812 javax.swing.border.Border selectedBorder = null;
1814 final String baseTT = "Click to edit, right/apple click for menu.";
1816 public ColorRenderer()
1818 setOpaque(true); // MUST do this for background to show up.
1819 setHorizontalTextPosition(SwingConstants.CENTER);
1820 setVerticalTextPosition(SwingConstants.CENTER);
1824 public Component getTableCellRendererComponent(JTable tbl, Object color,
1825 boolean isSelected, boolean hasFocus, int row, int column)
1827 FeatureColourI cellColour = (FeatureColourI) color;
1829 setToolTipText(baseTT);
1830 setBackground(tbl.getBackground());
1831 if (!cellColour.isSimpleColour())
1833 Rectangle cr = tbl.getCellRect(row, column, false);
1834 FeatureSettings.renderGraduatedColor(this, cellColour,
1835 (int) cr.getWidth(), (int) cr.getHeight());
1841 setBackground(cellColour.getColour());
1845 if (selectedBorder == null)
1847 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1848 tbl.getSelectionBackground());
1850 setBorder(selectedBorder);
1854 if (unselectedBorder == null)
1856 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1857 tbl.getBackground());
1859 setBorder(unselectedBorder);
1866 class FilterRenderer extends JLabel implements TableCellRenderer
1868 javax.swing.border.Border unselectedBorder = null;
1870 javax.swing.border.Border selectedBorder = null;
1872 public FilterRenderer()
1874 setOpaque(true); // MUST do this for background to show up.
1875 setHorizontalTextPosition(SwingConstants.CENTER);
1876 setVerticalTextPosition(SwingConstants.CENTER);
1880 public Component getTableCellRendererComponent(JTable tbl,
1881 Object filter, boolean isSelected, boolean hasFocus, int row,
1884 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1886 String asText = theFilter.toString();
1887 setBackground(tbl.getBackground());
1888 this.setText(asText);
1893 if (selectedBorder == null)
1895 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1896 tbl.getSelectionBackground());
1898 setBorder(selectedBorder);
1902 if (unselectedBorder == null)
1904 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1905 tbl.getBackground());
1907 setBorder(unselectedBorder);
1915 * update comp using rendering settings from gcol
1920 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1922 int w = comp.getWidth(), h = comp.getHeight();
1925 w = (int) comp.getPreferredSize().getWidth();
1926 h = (int) comp.getPreferredSize().getHeight();
1933 renderGraduatedColor(comp, gcol, w, h);
1936 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1939 boolean thr = false;
1940 StringBuilder tt = new StringBuilder();
1941 StringBuilder tx = new StringBuilder();
1943 if (gcol.isColourByAttribute())
1945 tx.append(String.join(":", gcol.getAttributeName()));
1947 else if (!gcol.isColourByLabel())
1949 tx.append(MessageManager.getString("label.score"));
1952 if (gcol.isAboveThreshold())
1956 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1959 if (gcol.isBelowThreshold())
1963 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1966 if (gcol.isColourByLabel())
1968 tt.append("Coloured by label text. ").append(tt);
1973 if (!gcol.isColourByAttribute())
1981 Color newColor = gcol.getMaxColour();
1982 comp.setBackground(newColor);
1983 // System.err.println("Width is " + w / 2);
1984 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1985 comp.setIcon(ficon);
1986 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1987 // + newColor.getGreen() + ", " + newColor.getBlue()
1988 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1989 // + ", " + minCol.getBlue() + ")");
1991 comp.setHorizontalAlignment(SwingConstants.CENTER);
1992 comp.setText(tx.toString());
1993 if (tt.length() > 0)
1995 if (comp.getToolTipText() == null)
1997 comp.setToolTipText(tt.toString());
2001 comp.setToolTipText(
2002 tt.append(" ").append(comp.getToolTipText()).toString());
2007 class ColorEditor extends AbstractCellEditor
2008 implements TableCellEditor, ActionListener
2012 FeatureColourI currentColor;
2014 FeatureTypeSettings chooser;
2018 JButton colourButton;
2020 JColorChooser colorChooser;
2024 protected static final String EDIT = "edit";
2026 int rowSelected = 0;
2028 public ColorEditor(FeatureSettings me)
2031 // Set up the editor (from the table's point of view),
2032 // which is a button.
2033 // This button brings up the color chooser dialog,
2034 // which is the editor from the user's point of view.
2035 colourButton = new JButton();
2036 colourButton.setActionCommand(EDIT);
2037 colourButton.addActionListener(this);
2038 colourButton.setBorderPainted(false);
2039 // Set up the dialog that the button brings up.
2040 colorChooser = new JColorChooser();
2041 dialog = JColorChooser.createDialog(colourButton,
2042 MessageManager.getString("label.select_colour"), true, // modal
2043 colorChooser, this, // OK button handler
2044 null); // no CANCEL button handler
2048 * Handles events from the editor button and from the dialog's OK button.
2051 public void actionPerformed(ActionEvent e)
2053 // todo test e.getSource() instead here
2054 if (EDIT.equals(e.getActionCommand()))
2056 // The user has clicked the cell, so
2057 // bring up the dialog.
2058 if (currentColor.isSimpleColour())
2060 // bring up simple color chooser
2061 colourButton.setBackground(currentColor.getColour());
2062 colorChooser.setColor(currentColor.getColour());
2063 dialog.setVisible(true);
2067 // bring up graduated chooser.
2068 chooser = new FeatureTypeSettings(me.fr, type);
2069 chooser.setRequestFocusEnabled(true);
2070 chooser.requestFocus();
2071 chooser.addActionListener(this);
2072 chooser.showTab(true);
2074 // Make the renderer reappear.
2075 fireEditingStopped();
2079 if (currentColor.isSimpleColour())
2082 * read off colour picked in colour chooser after OK pressed
2084 currentColor = new FeatureColour(colorChooser.getColor());
2085 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
2090 * after OK in variable colour dialog, any changes to colour
2091 * (or filters!) are already set in FeatureRenderer, so just
2092 * update table data without triggering updateFeatureRenderer
2096 fireEditingStopped();
2097 me.table.validate();
2101 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2103 public Object getCellEditorValue()
2105 return currentColor;
2108 // Implement the one method defined by TableCellEditor.
2110 public Component getTableCellEditorComponent(JTable theTable, Object value,
2111 boolean isSelected, int row, int column)
2113 currentColor = (FeatureColourI) value;
2114 this.rowSelected = row;
2115 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2116 colourButton.setOpaque(true);
2117 colourButton.setBackground(me.getBackground());
2118 if (!currentColor.isSimpleColour())
2120 JLabel btn = new JLabel();
2121 btn.setSize(colourButton.getSize());
2122 FeatureSettings.renderGraduatedColor(btn, currentColor);
2123 colourButton.setBackground(btn.getBackground());
2124 colourButton.setIcon(btn.getIcon());
2125 colourButton.setText(btn.getText());
2129 colourButton.setText("");
2130 colourButton.setIcon(null);
2131 colourButton.setBackground(currentColor.getColour());
2133 return colourButton;
2138 * The cell editor for the Filter column. It displays the text of any filters
2139 * for the feature type in that row (in full as a tooltip, possible abbreviated
2140 * as display text). On click in the cell, opens the Feature Display Settings
2141 * dialog at the Filters tab.
2143 class FilterEditor extends AbstractCellEditor
2144 implements TableCellEditor, ActionListener
2148 FeatureMatcherSetI currentFilter;
2154 JButton filterButton;
2156 protected static final String EDIT = "edit";
2158 int rowSelected = 0;
2160 public FilterEditor(FeatureSettings me)
2163 filterButton = new JButton();
2164 filterButton.setActionCommand(EDIT);
2165 filterButton.addActionListener(this);
2166 filterButton.setBorderPainted(false);
2170 * Handles events from the editor button
2173 public void actionPerformed(ActionEvent e)
2175 if (filterButton == e.getSource())
2177 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2178 chooser.addActionListener(this);
2179 chooser.setRequestFocusEnabled(true);
2180 chooser.requestFocus();
2181 if (lastLocation != null)
2183 // todo open at its last position on screen
2184 chooser.setBounds(lastLocation.x, lastLocation.y,
2185 chooser.getWidth(), chooser.getHeight());
2188 chooser.showTab(false);
2189 fireEditingStopped();
2191 else if (e.getSource() instanceof Component)
2194 * after OK in variable colour dialog, any changes to filter
2195 * (or colours!) are already set in FeatureRenderer, so just
2196 * update table data without triggering updateFeatureRenderer
2199 fireEditingStopped();
2200 me.table.validate();
2205 public Object getCellEditorValue()
2207 return currentFilter;
2211 public Component getTableCellEditorComponent(JTable theTable, Object value,
2212 boolean isSelected, int row, int column)
2214 currentFilter = (FeatureMatcherSetI) value;
2215 this.rowSelected = row;
2216 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2217 filterButton.setOpaque(true);
2218 filterButton.setBackground(me.getBackground());
2219 filterButton.setText(currentFilter.toString());
2220 filterButton.setToolTipText(currentFilter.toString());
2221 filterButton.setIcon(null);
2222 return filterButton;
2227 class FeatureIcon implements Icon
2229 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2231 FeatureColourI gcol;
2235 boolean midspace = false;
2237 int width = 50, height = 20;
2239 int s1, e1; // start and end of midpoint band for thresholded symbol
2241 Color mpcolour = Color.white;
2243 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2263 public int getIconWidth()
2269 public int getIconHeight()
2275 public void paintIcon(Component c, Graphics g, int x, int y)
2278 if (gcol.isColourByLabel())
2281 g.fillRect(0, 0, width, height);
2282 // need an icon here.
2283 g.setColor(gcol.getMaxColour());
2285 g.setFont(VERDANA_9);
2287 // g.setFont(g.getFont().deriveFont(
2288 // AffineTransform.getScaleInstance(
2289 // width/g.getFontMetrics().stringWidth("Label"),
2290 // height/g.getFontMetrics().getHeight())));
2292 g.drawString(MessageManager.getString("label.label"), 0, 0);
2297 Color minCol = gcol.getMinColour();
2299 g.fillRect(0, 0, s1, height);
2302 g.setColor(Color.white);
2303 g.fillRect(s1, 0, e1 - s1, height);
2305 g.setColor(gcol.getMaxColour());
2306 g.fillRect(0, e1, width - e1, height);