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 column c of the table
1530 public Class<?> getColumnClass(int c)
1535 return String.class;
1537 return FeatureColour.class;
1539 return FeatureMatcherSet.class;
1541 return Boolean.class;
1546 * Answers true for all columns except Feature Type
1549 public boolean isCellEditable(int row, int col)
1551 return col != TYPE_COLUMN;
1555 * Sets the value in the model for a given row and column. If Visibility
1556 * (Show/Hide) is being set, and the table is in Summary View, then it is
1557 * set also on any sub-types of the row's feature type.
1560 public void setValueAt(Object value, int row, int col)
1562 data[row][col] = value;
1563 fireTableCellUpdated(row, col);
1564 if (summaryView.isSelected() && col == SHOW_COLUMN)
1566 setSubtypesVisibility(row, (Boolean) value);
1568 updateFeatureRenderer(data);
1572 * Sets the visibility of any feature types which are sub-types of the type
1573 * in the given row of the table
1578 protected void setSubtypesVisibility(int row, Boolean value)
1580 String type = (String) data[row][TYPE_COLUMN];
1581 OntologyI so = SequenceOntologyFactory.getInstance();
1583 for (int r = 0; r < data.length; r++)
1587 String type2 = (String) data[r][TYPE_COLUMN];
1588 if (so.isA(type2, type))
1590 data[r][SHOW_COLUMN] = value;
1591 fireTableCellUpdated(r, SHOW_COLUMN);
1598 class ColorRenderer extends JLabel implements TableCellRenderer
1600 javax.swing.border.Border unselectedBorder = null;
1602 javax.swing.border.Border selectedBorder = null;
1604 final String baseTT = "Click to edit, right/apple click for menu.";
1606 public ColorRenderer()
1608 setOpaque(true); // MUST do this for background to show up.
1609 setHorizontalTextPosition(SwingConstants.CENTER);
1610 setVerticalTextPosition(SwingConstants.CENTER);
1614 public Component getTableCellRendererComponent(JTable tbl, Object color,
1615 boolean isSelected, boolean hasFocus, int row, int column)
1617 FeatureColourI cellColour = (FeatureColourI) color;
1619 setToolTipText(baseTT);
1620 setBackground(tbl.getBackground());
1621 if (!cellColour.isSimpleColour())
1623 Rectangle cr = tbl.getCellRect(row, column, false);
1624 FeatureSettings.renderGraduatedColor(this, cellColour,
1625 (int) cr.getWidth(), (int) cr.getHeight());
1631 setBackground(cellColour.getColour());
1635 if (selectedBorder == null)
1637 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1638 tbl.getSelectionBackground());
1640 setBorder(selectedBorder);
1644 if (unselectedBorder == null)
1646 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1647 tbl.getBackground());
1649 setBorder(unselectedBorder);
1656 class FilterRenderer extends JLabel implements TableCellRenderer
1658 javax.swing.border.Border unselectedBorder = null;
1660 javax.swing.border.Border selectedBorder = null;
1662 public FilterRenderer()
1664 setOpaque(true); // MUST do this for background to show up.
1665 setHorizontalTextPosition(SwingConstants.CENTER);
1666 setVerticalTextPosition(SwingConstants.CENTER);
1670 public Component getTableCellRendererComponent(JTable tbl,
1671 Object filter, boolean isSelected, boolean hasFocus, int row,
1674 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1676 String asText = theFilter.toString();
1677 setBackground(tbl.getBackground());
1678 this.setText(asText);
1683 if (selectedBorder == null)
1685 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1686 tbl.getSelectionBackground());
1688 setBorder(selectedBorder);
1692 if (unselectedBorder == null)
1694 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1695 tbl.getBackground());
1697 setBorder(unselectedBorder);
1705 * update comp using rendering settings from gcol
1710 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1712 int w = comp.getWidth(), h = comp.getHeight();
1715 w = (int) comp.getPreferredSize().getWidth();
1716 h = (int) comp.getPreferredSize().getHeight();
1723 renderGraduatedColor(comp, gcol, w, h);
1726 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1729 boolean thr = false;
1730 StringBuilder tt = new StringBuilder();
1731 StringBuilder tx = new StringBuilder();
1733 if (gcol.isColourByAttribute())
1735 tx.append(String.join(":", gcol.getAttributeName()));
1737 else if (!gcol.isColourByLabel())
1739 tx.append(MessageManager.getString("label.score"));
1742 if (gcol.isAboveThreshold())
1746 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1749 if (gcol.isBelowThreshold())
1753 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1756 if (gcol.isColourByLabel())
1758 tt.append("Coloured by label text. ").append(tt);
1763 if (!gcol.isColourByAttribute())
1771 Color newColor = gcol.getMaxColour();
1772 comp.setBackground(newColor);
1773 // System.err.println("Width is " + w / 2);
1774 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1775 comp.setIcon(ficon);
1776 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1777 // + newColor.getGreen() + ", " + newColor.getBlue()
1778 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1779 // + ", " + minCol.getBlue() + ")");
1781 comp.setHorizontalAlignment(SwingConstants.CENTER);
1782 comp.setText(tx.toString());
1783 if (tt.length() > 0)
1785 if (comp.getToolTipText() == null)
1787 comp.setToolTipText(tt.toString());
1791 comp.setToolTipText(
1792 tt.append(" ").append(comp.getToolTipText()).toString());
1797 class ColorEditor extends AbstractCellEditor
1798 implements TableCellEditor, ActionListener
1802 FeatureColourI currentColor;
1804 FeatureTypeSettings chooser;
1808 JButton colourButton;
1810 JColorChooser colorChooser;
1814 protected static final String EDIT = "edit";
1816 int rowSelected = 0;
1818 public ColorEditor(FeatureSettings me)
1821 // Set up the editor (from the table's point of view),
1822 // which is a button.
1823 // This button brings up the color chooser dialog,
1824 // which is the editor from the user's point of view.
1825 colourButton = new JButton();
1826 colourButton.setActionCommand(EDIT);
1827 colourButton.addActionListener(this);
1828 colourButton.setBorderPainted(false);
1829 // Set up the dialog that the button brings up.
1830 colorChooser = new JColorChooser();
1831 dialog = JColorChooser.createDialog(colourButton,
1832 MessageManager.getString("label.select_colour"), true, // modal
1833 colorChooser, this, // OK button handler
1834 null); // no CANCEL button handler
1838 * Handles events from the editor button and from the dialog's OK button.
1841 public void actionPerformed(ActionEvent e)
1843 // todo test e.getSource() instead here
1844 if (EDIT.equals(e.getActionCommand()))
1846 // The user has clicked the cell, so
1847 // bring up the dialog.
1848 if (currentColor.isSimpleColour())
1850 // bring up simple color chooser
1851 colourButton.setBackground(currentColor.getColour());
1852 colorChooser.setColor(currentColor.getColour());
1853 dialog.setVisible(true);
1857 // bring up graduated chooser.
1858 chooser = new FeatureTypeSettings(me.fr, type);
1859 chooser.setRequestFocusEnabled(true);
1860 chooser.requestFocus();
1861 chooser.addActionListener(this);
1862 // Make the renderer reappear.
1863 fireEditingStopped();
1868 if (currentColor.isSimpleColour())
1871 * read off colour picked in colour chooser after OK pressed
1873 currentColor = new FeatureColour(colorChooser.getColor());
1874 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1879 * after OK in variable colour dialog, any changes to colour
1880 * (or filters!) are already set in FeatureRenderer, so just
1881 * update table data without triggering updateFeatureRenderer
1885 fireEditingStopped();
1886 me.table.validate();
1890 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1892 public Object getCellEditorValue()
1894 return currentColor;
1897 // Implement the one method defined by TableCellEditor.
1899 public Component getTableCellEditorComponent(JTable theTable, Object value,
1900 boolean isSelected, int row, int column)
1902 currentColor = (FeatureColourI) value;
1903 this.rowSelected = row;
1904 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1905 colourButton.setOpaque(true);
1906 colourButton.setBackground(me.getBackground());
1907 if (!currentColor.isSimpleColour())
1909 JLabel btn = new JLabel();
1910 btn.setSize(colourButton.getSize());
1911 FeatureSettings.renderGraduatedColor(btn, currentColor);
1912 colourButton.setBackground(btn.getBackground());
1913 colourButton.setIcon(btn.getIcon());
1914 colourButton.setText(btn.getText());
1918 colourButton.setText("");
1919 colourButton.setIcon(null);
1920 colourButton.setBackground(currentColor.getColour());
1922 return colourButton;
1927 * The cell editor for the Filter column. It displays the text of any filters
1928 * for the feature type in that row (in full as a tooltip, possible abbreviated
1929 * as display text). On click in the cell, opens the Feature Display Settings
1930 * dialog at the Filters tab.
1932 class FilterEditor extends AbstractCellEditor
1933 implements TableCellEditor, ActionListener
1937 FeatureMatcherSetI currentFilter;
1943 JButton filterButton;
1945 protected static final String EDIT = "edit";
1947 int rowSelected = 0;
1949 public FilterEditor(FeatureSettings me)
1952 filterButton = new JButton();
1953 filterButton.setActionCommand(EDIT);
1954 filterButton.addActionListener(this);
1955 filterButton.setBorderPainted(false);
1959 * Handles events from the editor button
1962 public void actionPerformed(ActionEvent e)
1964 if (filterButton == e.getSource())
1966 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1967 chooser.addActionListener(this);
1968 chooser.setRequestFocusEnabled(true);
1969 chooser.requestFocus();
1970 if (lastLocation != null)
1972 // todo open at its last position on screen
1973 chooser.setBounds(lastLocation.x, lastLocation.y,
1974 chooser.getWidth(), chooser.getHeight());
1977 fireEditingStopped();
1979 else if (e.getSource() instanceof Component)
1982 * after OK in variable colour dialog, any changes to filter
1983 * (or colours!) are already set in FeatureRenderer, so just
1984 * update table data without triggering updateFeatureRenderer
1987 fireEditingStopped();
1988 me.table.validate();
1993 public Object getCellEditorValue()
1995 return currentFilter;
1999 public Component getTableCellEditorComponent(JTable theTable, Object value,
2000 boolean isSelected, int row, int column)
2002 currentFilter = (FeatureMatcherSetI) value;
2003 this.rowSelected = row;
2004 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2005 filterButton.setOpaque(true);
2006 filterButton.setBackground(me.getBackground());
2007 filterButton.setText(currentFilter.toString());
2008 filterButton.setToolTipText(currentFilter.toString());
2009 filterButton.setIcon(null);
2010 return filterButton;
2015 class FeatureIcon implements Icon
2017 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2019 FeatureColourI gcol;
2023 boolean midspace = false;
2025 int width = 50, height = 20;
2027 int s1, e1; // start and end of midpoint band for thresholded symbol
2029 Color mpcolour = Color.white;
2031 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2051 public int getIconWidth()
2057 public int getIconHeight()
2063 public void paintIcon(Component c, Graphics g, int x, int y)
2066 if (gcol.isColourByLabel())
2069 g.fillRect(0, 0, width, height);
2070 // need an icon here.
2071 g.setColor(gcol.getMaxColour());
2073 g.setFont(VERDANA_9);
2075 // g.setFont(g.getFont().deriveFont(
2076 // AffineTransform.getScaleInstance(
2077 // width/g.getFontMetrics().stringWidth("Label"),
2078 // height/g.getFontMetrics().getHeight())));
2080 g.drawString(MessageManager.getString("label.label"), 0, 0);
2085 Color minCol = gcol.getMinColour();
2087 g.fillRect(0, 0, s1, height);
2090 g.setColor(Color.white);
2091 g.fillRect(s1, 0, e1 - s1, height);
2093 g.setColor(gcol.getMaxColour());
2094 g.fillRect(0, e1, width - e1, height);