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.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
43 import java.awt.BorderLayout;
44 import java.awt.Color;
45 import java.awt.Component;
46 import java.awt.Dimension;
48 import java.awt.Graphics;
49 import java.awt.GridLayout;
50 import java.awt.Point;
51 import java.awt.Rectangle;
52 import java.awt.event.ActionEvent;
53 import java.awt.event.ActionListener;
54 import java.awt.event.ItemEvent;
55 import java.awt.event.ItemListener;
56 import java.awt.event.MouseAdapter;
57 import java.awt.event.MouseEvent;
58 import java.awt.event.MouseMotionAdapter;
59 import java.beans.PropertyChangeEvent;
60 import java.beans.PropertyChangeListener;
62 import java.io.FileInputStream;
63 import java.io.FileOutputStream;
64 import java.io.InputStreamReader;
65 import java.io.OutputStreamWriter;
66 import java.io.PrintWriter;
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.Comparator;
70 import java.util.HashMap;
71 import java.util.HashSet;
72 import java.util.Hashtable;
73 import java.util.Iterator;
74 import java.util.List;
78 import javax.help.HelpSetException;
79 import javax.swing.AbstractCellEditor;
80 import javax.swing.BorderFactory;
81 import javax.swing.Icon;
82 import javax.swing.JButton;
83 import javax.swing.JCheckBox;
84 import javax.swing.JCheckBoxMenuItem;
85 import javax.swing.JColorChooser;
86 import javax.swing.JDialog;
87 import javax.swing.JInternalFrame;
88 import javax.swing.JLabel;
89 import javax.swing.JLayeredPane;
90 import javax.swing.JMenuItem;
91 import javax.swing.JPanel;
92 import javax.swing.JPopupMenu;
93 import javax.swing.JScrollPane;
94 import javax.swing.JSlider;
95 import javax.swing.JTable;
96 import javax.swing.ListSelectionModel;
97 import javax.swing.RowFilter;
98 import javax.swing.SwingConstants;
99 import javax.swing.event.ChangeEvent;
100 import javax.swing.event.ChangeListener;
101 import javax.swing.table.AbstractTableModel;
102 import javax.swing.table.TableCellEditor;
103 import javax.swing.table.TableCellRenderer;
104 import javax.swing.table.TableColumn;
105 import javax.swing.table.TableRowSorter;
107 public class FeatureSettings extends JPanel
108 implements FeatureSettingsControllerI
110 private static final Font VERDANA_12 = new Font("Verdana", Font.PLAIN, 12);
112 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
113 .getString("label.sequence_feature_colours");
116 * column indices of fields in Feature Settings table
118 static final int TYPE_COLUMN = 0;
120 static final int COLOUR_COLUMN = 1;
122 static final int FILTER_COLUMN = 2;
124 static final int SHOW_COLUMN = 3;
126 private static final int COLUMN_COUNT = 4;
128 private static final int MIN_WIDTH = 400;
130 private static final int MIN_HEIGHT = 400;
132 final FeatureRenderer fr;
134 public final AlignFrame af;
137 * 'original' fields hold settings to restore on Cancel
139 Object[][] originalData;
141 private float originalTransparency;
143 private Map<String, FeatureMatcherSetI> originalFilters;
145 final JInternalFrame frame;
147 JScrollPane scrollPane = new JScrollPane();
153 JSlider transparency = new JSlider();
156 * when true, constructor is still executing - so ignore UI events
158 protected volatile boolean inConstruction = true;
160 int selectedRow = -1;
162 boolean resettingTable = false;
165 * true when Feature Settings are updating from feature renderer
167 private boolean handlingUpdate = false;
170 * holds {featureCount, totalExtent} for each feature type
172 Map<String, float[]> typeWidth = null;
175 * if true, 'child' feature types are not displayed
177 JCheckBox summaryView;
180 * those feature types that do not have a parent feature type present
181 * (as determined by an Ontology relationship)
183 List<String> topLevelTypes;
190 public FeatureSettings(AlignFrame alignFrame)
192 this.af = alignFrame;
193 fr = af.getFeatureRenderer();
195 // save transparency for restore on Cancel
196 originalTransparency = fr.getTransparency();
197 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
198 transparency.setMaximum(100 - originalTransparencyAsPercent);
200 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
202 topLevelTypes = new ArrayList<>();
207 } catch (Exception ex)
209 ex.printStackTrace();
214 scrollPane.setViewportView(table);
216 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
218 fr.findAllFeatures(true); // display everything!
221 discoverAllFeatureData();
222 final PropertyChangeListener change;
223 final FeatureSettings fs = this;
224 fr.addPropertyChangeListener(change = new PropertyChangeListener()
227 public void propertyChange(PropertyChangeEvent evt)
229 if (!fs.resettingTable && !fs.handlingUpdate)
231 fs.handlingUpdate = true;
233 // new groups may be added with new sequence feature types only
234 fs.handlingUpdate = false;
239 frame = new JInternalFrame();
240 frame.setContentPane(this);
241 if (Platform.isAMac())
243 Desktop.addInternalFrame(frame,
244 MessageManager.getString("label.sequence_feature_settings"),
249 Desktop.addInternalFrame(frame,
250 MessageManager.getString("label.sequence_feature_settings"),
253 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
255 frame.addInternalFrameListener(
256 new javax.swing.event.InternalFrameAdapter()
259 public void internalFrameClosed(
260 javax.swing.event.InternalFrameEvent evt)
262 fr.removePropertyChangeListener(change);
265 frame.setLayer(JLayeredPane.PALETTE_LAYER);
266 inConstruction = false;
270 * Constructs and configures the JTable which displays columns of data for
273 protected void initTable()
278 public String getToolTipText(MouseEvent e)
281 int column = table.columnAtPoint(e.getPoint());
286 * drag to reorder not enabled in Summary View
288 tip = summaryView.isSelected()
289 ? MessageManager.getString(
290 "label.feature_settings_select_columns")
291 : JvSwingUtils.wrapTooltip(true, MessageManager
292 .getString("label.feature_settings_click_drag"));
295 int row = table.rowAtPoint(e.getPoint());
296 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
299 ? MessageManager.getString("label.filters_tooltip")
308 table.getTableHeader().setFont(VERDANA_12);
309 table.setFont(VERDANA_12);
311 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
312 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
314 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
315 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
317 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
318 new ColorRenderer(), new ColorEditor(this));
319 table.addColumn(colourColumn);
321 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
322 new FilterRenderer(), new FilterEditor(this));
323 table.addColumn(filterColumn);
325 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
327 table.addMouseListener(new MouseAdapter()
330 public void mousePressed(MouseEvent evt)
332 selectedRow = table.rowAtPoint(evt.getPoint());
333 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
334 if (evt.isPopupTrigger())
336 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
337 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
339 else if (evt.getClickCount() == 2)
341 boolean invertSelection = evt.isAltDown();
342 boolean toggleSelection = Platform.isControlDown(evt);
343 boolean extendSelection = evt.isShiftDown();
344 String[] terms = getTermsInScope(type);
345 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
346 invertSelection, extendSelection, toggleSelection, terms);
350 // isPopupTrigger fires on mouseReleased on Windows
352 public void mouseReleased(MouseEvent evt)
354 selectedRow = table.rowAtPoint(evt.getPoint());
355 if (evt.isPopupTrigger())
357 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
358 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
359 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
364 table.addMouseMotionListener(new MouseMotionAdapter()
367 public void mouseDragged(MouseEvent evt)
369 int newRow = table.rowAtPoint(evt.getPoint());
376 * Answers an array consisting of the given type, and also (if 'Summary View'
377 * is selected), any child terms in the sequence ontology
382 protected String[] getTermsInScope(String type)
384 if (!summaryView.isSelected())
386 return new String[] { type };
389 List<String> terms = new ArrayList<>();
392 OntologyI so = SequenceOntologyFactory.getInstance();
394 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
395 for (Object[] row : data)
397 String type2 = (String) row[TYPE_COLUMN];
398 if (!type2.equals(type) && so.isA(type2, type))
403 return terms.toArray(new String[terms.size()]);
406 protected void popupMenu(final int rowSelected, final String type,
407 final Object typeCol, int x, int y)
409 final FeatureColourI featureColour = (FeatureColourI) typeCol;
411 JPopupMenu men = new JPopupMenu(MessageManager
412 .formatMessage("label.settings_for_param", new String[]
414 JMenuItem scr = new JMenuItem(
415 MessageManager.getString("label.sort_by_score"));
417 final FeatureSettings me = this;
418 scr.addActionListener(new ActionListener()
421 public void actionPerformed(ActionEvent e)
423 String[] types = getTermsInScope(type);
424 me.af.avc.sortAlignmentByFeatureScore(Arrays.asList(types));
427 JMenuItem dens = new JMenuItem(
428 MessageManager.getString("label.sort_by_density"));
429 dens.addActionListener(new ActionListener()
432 public void actionPerformed(ActionEvent e)
434 String[] types = getTermsInScope(type);
435 me.af.avc.sortAlignmentByFeatureDensity(Arrays.asList(types));
441 * variable colour options include colour by label, by score,
442 * by selected attribute text, or attribute value
444 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
445 MessageManager.getString("label.variable_colour"));
446 mxcol.setSelected(!featureColour.isSimpleColour());
448 mxcol.addActionListener(new ActionListener()
450 JColorChooser colorChooser;
453 public void actionPerformed(ActionEvent e)
455 if (e.getSource() == mxcol)
457 if (featureColour.isSimpleColour())
459 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
460 fc.addActionListener(this);
464 // bring up simple color chooser
465 colorChooser = new JColorChooser();
466 String title = MessageManager
467 .getString("label.select_colour");
468 JDialog dialog = JColorChooser.createDialog(me,
469 title, true, // modal
470 colorChooser, this, // OK button handler
471 null); // no CANCEL button handler
472 colorChooser.setColor(featureColour.getMaxColour());
473 dialog.setVisible(true);
478 if (e.getSource() instanceof FeatureTypeSettings)
481 * update after OK in feature colour dialog; the updated
482 * colour will have already been set in the FeatureRenderer
484 FeatureColourI fci = fr.getFeatureColours().get(type);
485 table.setValueAt(fci, rowSelected, 1);
490 // probably the color chooser!
491 table.setValueAt(new FeatureColour(colorChooser.getColor()),
494 me.updateFeatureRenderer(
495 ((FeatureTableModel) table.getModel()).getData(),
502 JMenuItem selCols = new JMenuItem(
503 MessageManager.getString("label.select_columns_containing"));
504 selCols.addActionListener(new ActionListener()
507 public void actionPerformed(ActionEvent arg0)
509 String[] types = getTermsInScope(type);
510 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
514 JMenuItem clearCols = new JMenuItem(MessageManager
515 .getString("label.select_columns_not_containing"));
516 clearCols.addActionListener(new ActionListener()
519 public void actionPerformed(ActionEvent arg0)
521 String[] types = getTermsInScope(type);
522 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
526 JMenuItem hideCols = new JMenuItem(
527 MessageManager.getString("label.hide_columns_containing"));
528 hideCols.addActionListener(new ActionListener()
531 public void actionPerformed(ActionEvent arg0)
533 String[] types = getTermsInScope(type);
534 fr.ap.alignFrame.hideFeatureColumns(true, types);
537 JMenuItem hideOtherCols = new JMenuItem(
538 MessageManager.getString("label.hide_columns_not_containing"));
539 hideOtherCols.addActionListener(new ActionListener()
542 public void actionPerformed(ActionEvent arg0)
544 String[] types = getTermsInScope(type);
545 fr.ap.alignFrame.hideFeatureColumns(false, types);
551 men.add(hideOtherCols);
552 men.show(table, x, y);
556 synchronized public void discoverAllFeatureData()
558 Set<String> allGroups = new HashSet<>();
559 AlignmentI alignment = af.getViewport().getAlignment();
561 for (int i = 0; i < alignment.getHeight(); i++)
563 SequenceI seq = alignment.getSequenceAt(i);
564 for (String group : seq.getFeatures().getFeatureGroups(true))
566 if (group != null && !allGroups.contains(group))
568 allGroups.add(group);
569 checkGroupState(group);
580 * Synchronise gui group list and check visibility of group
583 * @return true if group is visible
585 private boolean checkGroupState(String group)
587 boolean visible = fr.checkGroupVisibility(group, true);
589 for (int g = 0; g < groupPanel.getComponentCount(); g++)
591 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
593 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
598 final String grp = group;
599 final JCheckBox check = new JCheckBox(group, visible);
600 check.setFont(new Font("Serif", Font.BOLD, 12));
601 check.setToolTipText(group);
602 check.addItemListener(new ItemListener()
605 public void itemStateChanged(ItemEvent evt)
607 fr.setGroupVisibility(check.getText(), check.isSelected());
608 resetTable(new String[] { grp });
609 af.alignPanel.paintAlignment(true, true);
612 groupPanel.add(check);
616 synchronized void resetTable(String[] groupChanged)
622 resettingTable = true;
623 typeWidth = new Hashtable<>();
624 // TODO: change avWidth calculation to 'per-sequence' average and use long
627 Set<String> displayableTypes = new HashSet<>();
628 Set<String> foundGroups = new HashSet<>();
631 * determine which feature types may be visible depending on
632 * which groups are selected, and recompute average width data
634 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
637 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
640 * get the sequence's groups for positional features
641 * and keep track of which groups are visible
643 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
644 Set<String> visibleGroups = new HashSet<>();
645 for (String group : groups)
647 if (group == null || checkGroupState(group))
649 visibleGroups.add(group);
652 foundGroups.addAll(groups);
655 * get distinct feature types for visible groups
656 * record distinct visible types, and their count and total length
658 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
659 visibleGroups.toArray(new String[visibleGroups.size()]));
661 for (String type : types)
663 displayableTypes.add(type);
664 float[] avWidth = typeWidth.get(type);
667 avWidth = new float[2];
668 typeWidth.put(type, avWidth);
670 // todo this could include features with a non-visible group
671 // - do we greatly care?
672 // todo should we include non-displayable features here, and only
673 // update when features are added?
674 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
675 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
680 * enable 'Summary View' if some types are sub-types of others
682 Set<String> parents = SequenceOntologyFactory.getInstance()
683 .getParentTerms(displayableTypes);
684 summaryView.setEnabled(parents.size() < displayableTypes.size());
686 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
689 if (fr.hasRenderOrder())
693 fr.findAllFeatures(groupChanged != null); // prod to update
694 // colourschemes. but don't
696 // First add the checks in the previous render order,
697 // in case the window has been closed and reopened
699 List<String> frl = fr.getRenderOrder();
700 for (int ro = frl.size() - 1; ro > -1; ro--)
702 String type = frl.get(ro);
704 if (!displayableTypes.contains(type))
708 data[dataIndex][TYPE_COLUMN] = type;
709 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
710 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
711 data[dataIndex][FILTER_COLUMN] = featureFilter == null
712 ? new FeatureMatcherSet()
714 data[dataIndex][SHOW_COLUMN] = new Boolean(
715 af.getViewport().getFeaturesDisplayed().isVisible(type));
717 displayableTypes.remove(type);
722 * process any extra features belonging only to
723 * a group which was just selected
725 while (!displayableTypes.isEmpty())
727 String type = displayableTypes.iterator().next();
728 data[dataIndex][TYPE_COLUMN] = type;
730 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
731 if (data[dataIndex][COLOUR_COLUMN] == null)
733 // "Colour has been updated in another view!!"
734 fr.clearRenderOrder();
737 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
738 data[dataIndex][FILTER_COLUMN] = featureFilter == null
739 ? new FeatureMatcherSet()
741 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
743 displayableTypes.remove(type);
746 if (originalData == null)
748 originalData = new Object[data.length][COLUMN_COUNT];
749 for (int i = 0; i < data.length; i++)
751 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
756 updateOriginalData(data);
760 * recreate the table model
762 FeatureTableModel dataModel = new FeatureTableModel(data);
763 table.setModel(dataModel);
766 * we want to be able to filter out rows for sub-types, but not to sort
767 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
769 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
771 for (int i = 0; i < table.getColumnCount(); i++)
773 sorter.setSortable(i, false);
777 * filter rows to only top-level Ontology types if requested
779 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
782 public boolean include(
783 Entry<? extends FeatureTableModel, ? extends Integer> entry)
785 if (!summaryView.isSelected())
789 int row = entry.getIdentifier(); // this is model, not view, row number
790 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
791 return parents.contains(featureType);
794 table.setRowSorter(sorter);
796 table.getColumnModel().getColumn(0).setPreferredWidth(200);
798 groupPanel.setLayout(
799 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
800 pruneGroups(foundGroups);
801 groupPanel.validate();
803 updateFeatureRenderer(data, groupChanged != null);
804 resettingTable = false;
808 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
809 * have been made outwith this dialog
811 * <li>a new feature type added (and made visible)</li>
812 * <li>a feature colour changed (in the Amend Features dialog)</li>
817 protected void updateOriginalData(Object[][] foundData)
819 // todo LinkedHashMap instead of Object[][] would be nice
821 Object[][] currentData = ((FeatureTableModel) table.getModel())
823 for (Object[] row : foundData)
825 String type = (String) row[TYPE_COLUMN];
826 boolean found = false;
827 for (Object[] current : currentData)
829 if (type.equals(current[TYPE_COLUMN]))
833 * currently dependent on object equality here;
834 * really need an equals method on FeatureColour
836 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
839 * feature colour has changed externally - update originalData
841 for (Object[] original : originalData)
843 if (type.equals(original[TYPE_COLUMN]))
845 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
856 * new feature detected - add to original data (on top)
858 Object[][] newData = new Object[originalData.length
860 for (int i = 0; i < originalData.length; i++)
862 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
866 originalData = newData;
872 * Remove from the groups panel any checkboxes for groups that are not in the
873 * foundGroups set. This enables removing a group from the display when the last
874 * feature in that group is deleted.
878 protected void pruneGroups(Set<String> foundGroups)
880 for (int g = 0; g < groupPanel.getComponentCount(); g++)
882 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
883 if (!foundGroups.contains(checkbox.getText()))
885 groupPanel.remove(checkbox);
891 * reorder data based on the featureRenderers global priority list.
895 private void ensureOrder(Object[][] data)
897 boolean sort = false;
898 float[] order = new float[data.length];
899 for (int i = 0; i < order.length; i++)
901 order[i] = fr.getOrder(data[i][0].toString());
904 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
908 sort = sort || order[i - 1] > order[i];
913 jalview.util.QuickSort.sort(order, data);
918 * Offers a file chooser dialog, and then loads the feature colours and
919 * filters from file in XML format and unmarshals to Jalview feature settings
923 JalviewFileChooser chooser = new JalviewFileChooser("fc",
924 SEQUENCE_FEATURE_COLOURS);
925 chooser.setFileView(new JalviewFileView());
926 chooser.setDialogTitle(
927 MessageManager.getString("label.load_feature_colours"));
928 chooser.setToolTipText(MessageManager.getString("action.load"));
930 int value = chooser.showOpenDialog(this);
932 if (value == JalviewFileChooser.APPROVE_OPTION)
934 File file = chooser.getSelectedFile();
940 * Loads feature colours and filters from XML stored in the given file
948 InputStreamReader in = new InputStreamReader(
949 new FileInputStream(file), "UTF-8");
951 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
954 * load feature colours
956 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
958 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
959 FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
960 fr.setColour(newcol.getName(), colour);
961 fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
965 * load feature filters; loaded filters will replace any that are
966 * currently defined, other defined filters are left unchanged
968 for (int i = 0; i < jucs.getFilterCount(); i++)
970 jalview.schemabinding.version2.Filter filterModel = jucs
972 String featureType = filterModel.getFeatureType();
973 FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
974 filterModel.getMatcherSet());
975 if (!filter.isEmpty())
977 fr.setFeatureFilter(featureType, filter);
982 * update feature settings table
987 Object[][] data = ((FeatureTableModel) table.getModel())
990 updateFeatureRenderer(data, false);
993 } catch (Exception ex)
995 System.out.println("Error loading User Colour File\n" + ex);
1000 * Offers a file chooser dialog, and then saves the current feature colours
1001 * and any filters to the selected file in XML format
1005 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1006 SEQUENCE_FEATURE_COLOURS);
1007 chooser.setFileView(new JalviewFileView());
1008 chooser.setDialogTitle(
1009 MessageManager.getString("label.save_feature_colours"));
1010 chooser.setToolTipText(MessageManager.getString("action.save"));
1012 int value = chooser.showSaveDialog(this);
1014 if (value == JalviewFileChooser.APPROVE_OPTION)
1016 save(chooser.getSelectedFile());
1021 * Saves feature colours and filters to the given file
1025 void save(File file)
1027 JalviewUserColours ucs = new JalviewUserColours();
1028 ucs.setSchemeName("Sequence Features");
1031 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1032 new FileOutputStream(file), "UTF-8"));
1035 * sort feature types by colour order, from 0 (highest)
1038 Set<String> fr_colours = fr.getAllFeatureColours();
1039 String[] sortedTypes = fr_colours
1040 .toArray(new String[fr_colours.size()]);
1041 Arrays.sort(sortedTypes, new Comparator<String>()
1044 public int compare(String type1, String type2)
1046 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1051 * save feature colours
1053 for (String featureType : sortedTypes)
1055 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1056 jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
1062 * save any feature filters
1064 for (String featureType : sortedTypes)
1066 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1067 if (filter != null && !filter.isEmpty())
1069 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1070 FeatureMatcherI firstMatcher = iterator.next();
1071 MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
1073 Filter filterModel = new Filter();
1074 filterModel.setFeatureType(featureType);
1075 filterModel.setMatcherSet(ms);
1076 ucs.addFilter(filterModel);
1082 } catch (Exception ex)
1084 ex.printStackTrace();
1088 public void invertSelection()
1090 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1091 for (int i = 0; i < data.length; i++)
1093 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1095 updateFeatureRenderer(data, true);
1099 public void orderByAvWidth()
1101 if (table == null || table.getModel() == null)
1105 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1106 float[] width = new float[data.length];
1110 for (int i = 0; i < data.length; i++)
1112 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1115 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1116 // weight - but have to make per
1117 // sequence, too (awidth[2])
1118 // if (width[i]==1) // hack to distinguish single width sequences.
1129 boolean sort = false;
1130 for (int i = 0; i < width.length; i++)
1132 // awidth = (float[]) typeWidth.get(data[i][0]);
1135 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1138 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1144 width[i] /= max; // normalize
1145 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1149 sort = sort || width[i - 1] > width[i];
1154 jalview.util.QuickSort.sort(width, data);
1155 // update global priority order
1158 updateFeatureRenderer(data, false);
1166 frame.setClosed(true);
1167 } catch (Exception exe)
1173 public void updateFeatureRenderer(Object[][] data)
1175 updateFeatureRenderer(data, true);
1179 * Update the priority order of features; only repaint if this changed the order
1180 * of visible features
1185 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1187 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1189 if (fr.setFeaturePriority(rowData, visibleNew))
1191 af.alignPanel.paintAlignment(true, true);
1196 * Converts table data into an array of data beans
1198 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1200 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1201 for (int i = 0; i < data.length; i++)
1203 String type = (String) data[i][TYPE_COLUMN];
1204 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1205 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1206 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1207 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1213 private void jbInit() throws Exception
1215 this.setLayout(new BorderLayout());
1217 JPanel settingsPane = new JPanel();
1218 settingsPane.setLayout(new BorderLayout());
1220 JPanel bigPanel = new JPanel();
1221 bigPanel.setLayout(new BorderLayout());
1223 groupPanel = new JPanel();
1224 bigPanel.add(groupPanel, BorderLayout.NORTH);
1226 JButton invert = new JButton(
1227 MessageManager.getString("label.invert_selection"));
1228 invert.setFont(JvSwingUtils.getLabelFont());
1229 invert.addActionListener(new ActionListener()
1232 public void actionPerformed(ActionEvent e)
1238 JButton optimizeOrder = new JButton(
1239 MessageManager.getString("label.optimise_order"));
1240 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1241 optimizeOrder.addActionListener(new ActionListener()
1244 public void actionPerformed(ActionEvent e)
1250 JButton sortByScore = new JButton(
1251 MessageManager.getString("label.seq_sort_by_score"));
1252 sortByScore.setFont(JvSwingUtils.getLabelFont());
1253 sortByScore.addActionListener(new ActionListener()
1256 public void actionPerformed(ActionEvent e)
1258 af.avc.sortAlignmentByFeatureScore(null);
1261 JButton sortByDens = new JButton(
1262 MessageManager.getString("label.sequence_sort_by_density"));
1263 sortByDens.setFont(JvSwingUtils.getLabelFont());
1264 sortByDens.addActionListener(new ActionListener()
1267 public void actionPerformed(ActionEvent e)
1269 af.avc.sortAlignmentByFeatureDensity(null);
1273 JButton help = new JButton(MessageManager.getString("action.help"));
1274 help.setFont(JvSwingUtils.getLabelFont());
1275 help.addActionListener(new ActionListener()
1278 public void actionPerformed(ActionEvent e)
1282 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1283 } catch (HelpSetException e1)
1285 e1.printStackTrace();
1289 help.setFont(JvSwingUtils.getLabelFont());
1290 help.setText(MessageManager.getString("action.help"));
1291 help.addActionListener(new ActionListener()
1294 public void actionPerformed(ActionEvent e)
1298 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1299 } catch (HelpSetException e1)
1301 e1.printStackTrace();
1306 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1307 cancel.setFont(JvSwingUtils.getLabelFont());
1308 cancel.addActionListener(new ActionListener()
1311 public void actionPerformed(ActionEvent e)
1313 fr.setTransparency(originalTransparency);
1314 fr.setFeatureFilters(originalFilters);
1315 updateFeatureRenderer(originalData);
1320 JButton ok = new JButton(MessageManager.getString("action.ok"));
1321 ok.setFont(JvSwingUtils.getLabelFont());
1322 ok.addActionListener(new ActionListener()
1325 public void actionPerformed(ActionEvent e)
1331 JButton loadColours = new JButton(
1332 MessageManager.getString("label.load_colours"));
1333 loadColours.setFont(JvSwingUtils.getLabelFont());
1334 loadColours.setToolTipText(
1335 MessageManager.getString("label.load_colours_tooltip"));
1336 loadColours.addActionListener(new ActionListener()
1339 public void actionPerformed(ActionEvent e)
1345 JButton saveColours = new JButton(
1346 MessageManager.getString("label.save_colours"));
1347 saveColours.setFont(JvSwingUtils.getLabelFont());
1348 saveColours.setToolTipText(
1349 MessageManager.getString("label.save_colours_tooltip"));
1350 saveColours.addActionListener(new ActionListener()
1353 public void actionPerformed(ActionEvent e)
1358 transparency.addChangeListener(new ChangeListener()
1361 public void stateChanged(ChangeEvent evt)
1363 if (!inConstruction)
1365 fr.setTransparency((100 - transparency.getValue()) / 100f);
1366 af.alignPanel.paintAlignment(true, true);
1371 summaryView = new JCheckBox(
1372 MessageManager.getString("label.summary_view"));
1375 MessageManager.getString("label.summary_view_tip"));
1376 summaryView.addActionListener(new ActionListener()
1379 public void actionPerformed(ActionEvent e)
1385 transparency.setMaximum(70);
1386 transparency.setToolTipText(
1387 MessageManager.getString("label.transparency_tip"));
1389 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1390 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1392 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1393 transbuttons.add(optimizeOrder);
1394 transbuttons.add(invert);
1395 transbuttons.add(sortByScore);
1396 transbuttons.add(sortByDens);
1397 transbuttons.add(help);
1398 JPanel transPanel = new JPanel(new GridLayout(3, 1));
1399 transPanel.add(summaryView);
1400 transPanel.add(new JLabel(" Colour transparency" + ":"));
1401 transPanel.add(transparency);
1402 lowerPanel.add(transPanel);
1403 lowerPanel.add(transbuttons);
1405 JPanel buttonPanel = new JPanel();
1406 buttonPanel.add(ok);
1407 buttonPanel.add(cancel);
1408 buttonPanel.add(loadColours);
1409 buttonPanel.add(saveColours);
1410 bigPanel.add(scrollPane, BorderLayout.CENTER);
1411 settingsPane.add(bigPanel, BorderLayout.CENTER);
1412 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1413 this.add(settingsPane);
1417 * Reorders features by 'dragging' selectedRow to 'newRow'
1421 protected void dragRow(int newRow)
1423 if (summaryView.isSelected())
1425 // no drag while in summary view
1429 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1432 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1433 * this could be more than one row away for a very fast drag action
1434 * so just swap it with adjacent rows until we get it there
1436 Object[][] data = ((FeatureTableModel) table.getModel())
1438 int direction = newRow < selectedRow ? -1 : 1;
1439 for (int i = selectedRow; i != newRow; i += direction)
1441 Object[] temp = data[i];
1442 data[i] = data[i + direction];
1443 data[i + direction] = temp;
1445 updateFeatureRenderer(data);
1447 selectedRow = newRow;
1451 protected void refreshTable()
1453 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1454 for (Object[] row : data)
1456 String type = (String) row[TYPE_COLUMN];
1457 FeatureColourI colour = fr.getFeatureColours().get(type);
1458 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1461 filter = new FeatureMatcherSet();
1463 row[COLOUR_COLUMN] = colour;
1464 row[FILTER_COLUMN] = filter;
1469 // ///////////////////////////////////////////////////////////////////////
1470 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1471 // ///////////////////////////////////////////////////////////////////////
1472 class FeatureTableModel extends AbstractTableModel
1474 private String[] columnNames = {
1475 MessageManager.getString("label.feature_type"),
1476 MessageManager.getString("action.colour"),
1477 MessageManager.getString("label.filter"),
1478 MessageManager.getString("label.show") };
1480 private Object[][] data;
1482 FeatureTableModel(Object[][] data)
1487 public Object[][] getData()
1492 public void setData(Object[][] data)
1498 public int getColumnCount()
1500 return columnNames.length;
1503 public Object[] getRow(int row)
1509 public int getRowCount()
1515 public String getColumnName(int col)
1517 return columnNames[col];
1521 public Object getValueAt(int row, int col)
1523 return data[row][col];
1527 * Answers the class of the object in column c of the first row of the table
1530 public Class<?> getColumnClass(int c)
1532 Object v = getValueAt(0, c);
1533 return v == null ? null : v.getClass();
1537 * Answers true for all columns except Feature Type
1540 public boolean isCellEditable(int row, int col)
1542 return col != TYPE_COLUMN;
1546 * Sets the value in the model for a given row and column. If Visibility
1547 * (Show/Hide) is being set, and the table is in Summary View, then it is
1548 * set also on any sub-types of the row's feature type.
1551 public void setValueAt(Object value, int row, int col)
1553 data[row][col] = value;
1554 fireTableCellUpdated(row, col);
1555 if (summaryView.isSelected() && col == SHOW_COLUMN)
1557 setSubtypesVisibility(row, (Boolean) value);
1559 updateFeatureRenderer(data);
1563 * Sets the visibility of any feature types which are sub-types of the type
1564 * in the given row of the table
1569 protected void setSubtypesVisibility(int row, Boolean value)
1571 String type = (String) data[row][TYPE_COLUMN];
1572 OntologyI so = SequenceOntologyFactory.getInstance();
1574 for (int r = 0; r < data.length; r++)
1578 String type2 = (String) data[r][TYPE_COLUMN];
1579 if (so.isA(type2, type))
1581 data[r][SHOW_COLUMN] = value;
1582 fireTableCellUpdated(r, SHOW_COLUMN);
1589 class ColorRenderer extends JLabel implements TableCellRenderer
1591 javax.swing.border.Border unselectedBorder = null;
1593 javax.swing.border.Border selectedBorder = null;
1595 final String baseTT = "Click to edit, right/apple click for menu.";
1597 public ColorRenderer()
1599 setOpaque(true); // MUST do this for background to show up.
1600 setHorizontalTextPosition(SwingConstants.CENTER);
1601 setVerticalTextPosition(SwingConstants.CENTER);
1605 public Component getTableCellRendererComponent(JTable tbl, Object color,
1606 boolean isSelected, boolean hasFocus, int row, int column)
1608 FeatureColourI cellColour = (FeatureColourI) color;
1610 setToolTipText(baseTT);
1611 setBackground(tbl.getBackground());
1612 if (!cellColour.isSimpleColour())
1614 Rectangle cr = tbl.getCellRect(row, column, false);
1615 FeatureSettings.renderGraduatedColor(this, cellColour,
1616 (int) cr.getWidth(), (int) cr.getHeight());
1622 setBackground(cellColour.getColour());
1626 if (selectedBorder == null)
1628 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1629 tbl.getSelectionBackground());
1631 setBorder(selectedBorder);
1635 if (unselectedBorder == null)
1637 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1638 tbl.getBackground());
1640 setBorder(unselectedBorder);
1647 class FilterRenderer extends JLabel implements TableCellRenderer
1649 javax.swing.border.Border unselectedBorder = null;
1651 javax.swing.border.Border selectedBorder = null;
1653 public FilterRenderer()
1655 setOpaque(true); // MUST do this for background to show up.
1656 setHorizontalTextPosition(SwingConstants.CENTER);
1657 setVerticalTextPosition(SwingConstants.CENTER);
1661 public Component getTableCellRendererComponent(JTable tbl,
1662 Object filter, boolean isSelected, boolean hasFocus, int row,
1665 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1667 String asText = theFilter.toString();
1668 setBackground(tbl.getBackground());
1669 this.setText(asText);
1674 if (selectedBorder == null)
1676 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1677 tbl.getSelectionBackground());
1679 setBorder(selectedBorder);
1683 if (unselectedBorder == null)
1685 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1686 tbl.getBackground());
1688 setBorder(unselectedBorder);
1696 * update comp using rendering settings from gcol
1701 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1703 int w = comp.getWidth(), h = comp.getHeight();
1706 w = (int) comp.getPreferredSize().getWidth();
1707 h = (int) comp.getPreferredSize().getHeight();
1714 renderGraduatedColor(comp, gcol, w, h);
1717 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1720 boolean thr = false;
1721 StringBuilder tt = new StringBuilder();
1722 StringBuilder tx = new StringBuilder();
1724 if (gcol.isColourByAttribute())
1726 tx.append(String.join(":", gcol.getAttributeName()));
1728 else if (!gcol.isColourByLabel())
1730 tx.append(MessageManager.getString("label.score"));
1733 if (gcol.isAboveThreshold())
1737 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1740 if (gcol.isBelowThreshold())
1744 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1747 if (gcol.isColourByLabel())
1749 tt.append("Coloured by label text. ").append(tt);
1754 if (!gcol.isColourByAttribute())
1762 Color newColor = gcol.getMaxColour();
1763 comp.setBackground(newColor);
1764 // System.err.println("Width is " + w / 2);
1765 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1766 comp.setIcon(ficon);
1767 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1768 // + newColor.getGreen() + ", " + newColor.getBlue()
1769 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1770 // + ", " + minCol.getBlue() + ")");
1772 comp.setHorizontalAlignment(SwingConstants.CENTER);
1773 comp.setText(tx.toString());
1774 if (tt.length() > 0)
1776 if (comp.getToolTipText() == null)
1778 comp.setToolTipText(tt.toString());
1782 comp.setToolTipText(
1783 tt.append(" ").append(comp.getToolTipText()).toString());
1788 class ColorEditor extends AbstractCellEditor
1789 implements TableCellEditor, ActionListener
1793 FeatureColourI currentColor;
1795 FeatureTypeSettings chooser;
1799 JButton colourButton;
1801 JColorChooser colorChooser;
1805 protected static final String EDIT = "edit";
1807 int rowSelected = 0;
1809 public ColorEditor(FeatureSettings me)
1812 // Set up the editor (from the table's point of view),
1813 // which is a button.
1814 // This button brings up the color chooser dialog,
1815 // which is the editor from the user's point of view.
1816 colourButton = new JButton();
1817 colourButton.setActionCommand(EDIT);
1818 colourButton.addActionListener(this);
1819 colourButton.setBorderPainted(false);
1820 // Set up the dialog that the button brings up.
1821 colorChooser = new JColorChooser();
1822 dialog = JColorChooser.createDialog(colourButton,
1823 MessageManager.getString("label.select_colour"), true, // modal
1824 colorChooser, this, // OK button handler
1825 null); // no CANCEL button handler
1829 * Handles events from the editor button and from the dialog's OK button.
1832 public void actionPerformed(ActionEvent e)
1834 // todo test e.getSource() instead here
1835 if (EDIT.equals(e.getActionCommand()))
1837 // The user has clicked the cell, so
1838 // bring up the dialog.
1839 if (currentColor.isSimpleColour())
1841 // bring up simple color chooser
1842 colourButton.setBackground(currentColor.getColour());
1843 colorChooser.setColor(currentColor.getColour());
1844 dialog.setVisible(true);
1848 // bring up graduated chooser.
1849 chooser = new FeatureTypeSettings(me.fr, type);
1850 chooser.setRequestFocusEnabled(true);
1851 chooser.requestFocus();
1852 chooser.addActionListener(this);
1853 // Make the renderer reappear.
1854 fireEditingStopped();
1859 if (currentColor.isSimpleColour())
1862 * read off colour picked in colour chooser after OK pressed
1864 currentColor = new FeatureColour(colorChooser.getColor());
1865 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1870 * after OK in variable colour dialog, any changes to colour
1871 * (or filters!) are already set in FeatureRenderer, so just
1872 * update table data without triggering updateFeatureRenderer
1876 fireEditingStopped();
1877 me.table.validate();
1881 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1883 public Object getCellEditorValue()
1885 return currentColor;
1888 // Implement the one method defined by TableCellEditor.
1890 public Component getTableCellEditorComponent(JTable theTable, Object value,
1891 boolean isSelected, int row, int column)
1893 currentColor = (FeatureColourI) value;
1894 this.rowSelected = row;
1895 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1896 colourButton.setOpaque(true);
1897 colourButton.setBackground(me.getBackground());
1898 if (!currentColor.isSimpleColour())
1900 JLabel btn = new JLabel();
1901 btn.setSize(colourButton.getSize());
1902 FeatureSettings.renderGraduatedColor(btn, currentColor);
1903 colourButton.setBackground(btn.getBackground());
1904 colourButton.setIcon(btn.getIcon());
1905 colourButton.setText(btn.getText());
1909 colourButton.setText("");
1910 colourButton.setIcon(null);
1911 colourButton.setBackground(currentColor.getColour());
1913 return colourButton;
1918 * The cell editor for the Filter column. It displays the text of any filters
1919 * for the feature type in that row (in full as a tooltip, possible abbreviated
1920 * as display text). On click in the cell, opens the Feature Display Settings
1921 * dialog at the Filters tab.
1923 class FilterEditor extends AbstractCellEditor
1924 implements TableCellEditor, ActionListener
1928 FeatureMatcherSetI currentFilter;
1934 JButton filterButton;
1936 protected static final String EDIT = "edit";
1938 int rowSelected = 0;
1940 public FilterEditor(FeatureSettings me)
1943 filterButton = new JButton();
1944 filterButton.setActionCommand(EDIT);
1945 filterButton.addActionListener(this);
1946 filterButton.setBorderPainted(false);
1950 * Handles events from the editor button
1953 public void actionPerformed(ActionEvent e)
1955 if (filterButton == e.getSource())
1957 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1958 chooser.addActionListener(this);
1959 chooser.setRequestFocusEnabled(true);
1960 chooser.requestFocus();
1961 if (lastLocation != null)
1963 // todo open at its last position on screen
1964 chooser.setBounds(lastLocation.x, lastLocation.y,
1965 chooser.getWidth(), chooser.getHeight());
1968 fireEditingStopped();
1970 else if (e.getSource() instanceof Component)
1973 * after OK in variable colour dialog, any changes to filter
1974 * (or colours!) are already set in FeatureRenderer, so just
1975 * update table data without triggering updateFeatureRenderer
1978 fireEditingStopped();
1979 me.table.validate();
1984 public Object getCellEditorValue()
1986 return currentFilter;
1990 public Component getTableCellEditorComponent(JTable theTable, Object value,
1991 boolean isSelected, int row, int column)
1993 currentFilter = (FeatureMatcherSetI) value;
1994 this.rowSelected = row;
1995 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1996 filterButton.setOpaque(true);
1997 filterButton.setBackground(me.getBackground());
1998 filterButton.setText(currentFilter.toString());
1999 filterButton.setToolTipText(currentFilter.toString());
2000 filterButton.setIcon(null);
2001 return filterButton;
2006 class FeatureIcon implements Icon
2008 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2010 FeatureColourI gcol;
2014 boolean midspace = false;
2016 int width = 50, height = 20;
2018 int s1, e1; // start and end of midpoint band for thresholded symbol
2020 Color mpcolour = Color.white;
2022 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2042 public int getIconWidth()
2048 public int getIconHeight()
2054 public void paintIcon(Component c, Graphics g, int x, int y)
2057 if (gcol.isColourByLabel())
2060 g.fillRect(0, 0, width, height);
2061 // need an icon here.
2062 g.setColor(gcol.getMaxColour());
2064 g.setFont(VERDANA_9);
2066 // g.setFont(g.getFont().deriveFont(
2067 // AffineTransform.getScaleInstance(
2068 // width/g.getFontMetrics().stringWidth("Label"),
2069 // height/g.getFontMetrics().getHeight())));
2071 g.drawString(MessageManager.getString("label.label"), 0, 0);
2076 Color minCol = gcol.getMinColour();
2078 g.fillRect(0, 0, s1, height);
2081 g.setColor(Color.white);
2082 g.fillRect(s1, 0, e1 - s1, height);
2084 g.setColor(gcol.getMaxColour());
2085 g.fillRect(0, e1, width - e1, height);