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.FeatureMatcher;
28 import jalview.datamodel.features.FeatureMatcherI;
29 import jalview.datamodel.features.FeatureMatcherSet;
30 import jalview.datamodel.features.FeatureMatcherSetI;
31 import jalview.datamodel.ontology.OntologyI;
32 import jalview.gui.Help.HelpId;
33 import jalview.io.JalviewFileChooser;
34 import jalview.io.JalviewFileView;
35 import jalview.io.gff.SequenceOntologyFactory;
36 import jalview.schemes.FeatureColour;
37 import jalview.util.MessageManager;
38 import jalview.util.Platform;
39 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
40 import jalview.xml.binding.jalview.JalviewUserColours;
41 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
42 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
43 import jalview.xml.binding.jalview.ObjectFactory;
45 import java.awt.BorderLayout;
46 import java.awt.Color;
47 import java.awt.Component;
48 import java.awt.Dimension;
50 import java.awt.Graphics;
51 import java.awt.GridLayout;
52 import java.awt.Point;
53 import java.awt.Rectangle;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.ActionListener;
56 import java.awt.event.ItemEvent;
57 import java.awt.event.ItemListener;
58 import java.awt.event.MouseAdapter;
59 import java.awt.event.MouseEvent;
60 import java.awt.event.MouseMotionAdapter;
61 import java.beans.PropertyChangeEvent;
62 import java.beans.PropertyChangeListener;
64 import java.io.FileInputStream;
65 import java.io.FileOutputStream;
66 import java.io.InputStreamReader;
67 import java.io.OutputStreamWriter;
68 import java.io.PrintWriter;
69 import java.util.ArrayList;
70 import java.util.Arrays;
71 import java.util.Comparator;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.Hashtable;
75 import java.util.Iterator;
76 import java.util.List;
80 import javax.help.HelpSetException;
81 import javax.swing.AbstractCellEditor;
82 import javax.swing.BorderFactory;
83 import javax.swing.Icon;
84 import javax.swing.JButton;
85 import javax.swing.JCheckBox;
86 import javax.swing.JColorChooser;
87 import javax.swing.JDialog;
88 import javax.swing.JInternalFrame;
89 import javax.swing.JLabel;
90 import javax.swing.JLayeredPane;
91 import javax.swing.JMenuItem;
92 import javax.swing.JPanel;
93 import javax.swing.JPopupMenu;
94 import javax.swing.JScrollPane;
95 import javax.swing.JSlider;
96 import javax.swing.JTable;
97 import javax.swing.ListSelectionModel;
98 import javax.swing.RowFilter;
99 import javax.swing.SwingConstants;
100 import javax.swing.border.Border;
101 import javax.swing.event.ChangeEvent;
102 import javax.swing.event.ChangeListener;
103 import javax.swing.table.AbstractTableModel;
104 import javax.swing.table.JTableHeader;
105 import javax.swing.table.TableCellEditor;
106 import javax.swing.table.TableCellRenderer;
107 import javax.swing.table.TableColumn;
108 import javax.swing.table.TableRowSorter;
109 import javax.xml.bind.JAXBContext;
110 import javax.xml.bind.JAXBElement;
111 import javax.xml.bind.Marshaller;
112 import javax.xml.stream.XMLInputFactory;
113 import javax.xml.stream.XMLStreamReader;
115 public class FeatureSettings extends JPanel
116 implements FeatureSettingsControllerI
118 private static final Font VERDANA_12 = new Font("Verdana", Font.PLAIN, 12);
120 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
121 .getString("label.sequence_feature_colours");
124 * column indices of fields in Feature Settings table
126 static final int TYPE_COLUMN = 0;
128 static final int COLOUR_COLUMN = 1;
130 static final int FILTER_COLUMN = 2;
132 static final int SHOW_COLUMN = 3;
134 private static final int COLUMN_COUNT = 4;
136 private static final int MIN_WIDTH = 400;
138 private static final int MIN_HEIGHT = 400;
140 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
142 final FeatureRenderer fr;
144 public final AlignFrame af;
147 * 'original' fields hold settings to restore on Cancel
149 Object[][] originalData;
151 private float originalTransparency;
153 private Map<String, FeatureMatcherSetI> originalFilters;
155 final JInternalFrame frame;
157 JScrollPane scrollPane = new JScrollPane();
163 JSlider transparency = new JSlider();
166 * when true, constructor is still executing - so ignore UI events
168 protected volatile boolean inConstruction = true;
170 int selectedRow = -1;
172 boolean resettingTable = false;
175 * true when Feature Settings are updating from feature renderer
177 private boolean handlingUpdate = false;
180 * holds {featureCount, totalExtent} for each feature type
182 Map<String, float[]> typeWidth = null;
185 * if true, 'child' feature types are not displayed
187 JCheckBox summaryView;
190 * those feature types that do not have a parent feature type present
191 * (as determined by an Ontology relationship)
193 List<String> topLevelTypes;
200 public FeatureSettings(AlignFrame alignFrame)
202 this.af = alignFrame;
203 fr = af.getFeatureRenderer();
205 // save transparency for restore on Cancel
206 originalTransparency = fr.getTransparency();
207 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
208 transparency.setMaximum(100 - originalTransparencyAsPercent);
210 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
212 topLevelTypes = new ArrayList<>();
217 } catch (Exception ex)
219 ex.printStackTrace();
224 scrollPane.setViewportView(table);
226 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
228 fr.findAllFeatures(true); // display everything!
231 discoverAllFeatureData();
232 final PropertyChangeListener change;
233 final FeatureSettings fs = this;
234 fr.addPropertyChangeListener(change = new PropertyChangeListener()
237 public void propertyChange(PropertyChangeEvent evt)
239 if (!fs.resettingTable && !fs.handlingUpdate)
241 fs.handlingUpdate = true;
243 // new groups may be added with new sequence feature types only
244 fs.handlingUpdate = false;
249 frame = new JInternalFrame();
250 frame.setContentPane(this);
251 if (Platform.isAMac())
253 Desktop.addInternalFrame(frame,
254 MessageManager.getString("label.sequence_feature_settings"),
259 Desktop.addInternalFrame(frame,
260 MessageManager.getString("label.sequence_feature_settings"),
263 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
265 frame.addInternalFrameListener(
266 new javax.swing.event.InternalFrameAdapter()
269 public void internalFrameClosed(
270 javax.swing.event.InternalFrameEvent evt)
272 fr.removePropertyChangeListener(change);
275 frame.setLayer(JLayeredPane.PALETTE_LAYER);
276 inConstruction = false;
280 * Constructs and configures the JTable which displays columns of data for
283 protected void initTable()
288 public String getToolTipText(MouseEvent e)
291 int column = table.columnAtPoint(e.getPoint());
292 int row = table.rowAtPoint(e.getPoint());
297 * drag to reorder not enabled in Summary View
299 tip = summaryView.isSelected()
300 ? MessageManager.getString(
301 "label.feature_settings_select_columns")
302 : JvSwingUtils.wrapTooltip(true, MessageManager
303 .getString("label.feature_settings_click_drag"));
306 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
308 tip = getColorTooltip(colour, true);
311 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
315 .getString("label.configure_feature_tooltip")
325 * Position the tooltip near the bottom edge of, and half way across, the
329 public Point getToolTipLocation(MouseEvent e)
331 Point point = e.getPoint();
332 int column = table.columnAtPoint(point);
333 int row = table.rowAtPoint(point);
334 Rectangle r = getCellRect(row, column, false);
335 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
340 JTableHeader tableHeader = table.getTableHeader();
341 tableHeader.setFont(VERDANA_12);
342 tableHeader.setReorderingAllowed(false);
343 table.setFont(VERDANA_12);
345 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
346 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
348 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
349 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
351 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
352 new ColorRenderer(), new ColorEditor(this));
353 table.addColumn(colourColumn);
355 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
356 new FilterRenderer(), new FilterEditor(this));
357 table.addColumn(filterColumn);
359 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
361 table.addMouseListener(new MouseAdapter()
364 public void mousePressed(MouseEvent evt)
366 selectedRow = table.rowAtPoint(evt.getPoint());
367 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
368 if (evt.isPopupTrigger())
370 popupMenu(selectedRow, type, evt.getX(), evt.getY());
372 else if (evt.getClickCount() == 2)
374 boolean invertSelection = evt.isAltDown();
375 boolean toggleSelection = Platform.isControlDown(evt);
376 boolean extendSelection = evt.isShiftDown();
377 String[] terms = getTermsInScope(type);
378 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
379 invertSelection, extendSelection, toggleSelection, terms);
383 // isPopupTrigger fires on mouseReleased on Windows
385 public void mouseReleased(MouseEvent evt)
387 selectedRow = table.rowAtPoint(evt.getPoint());
388 if (evt.isPopupTrigger())
390 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
391 popupMenu(selectedRow, type, evt.getX(), evt.getY());
396 table.addMouseMotionListener(new MouseMotionAdapter()
399 public void mouseDragged(MouseEvent evt)
401 int newRow = table.rowAtPoint(evt.getPoint());
408 * Answers an array consisting of the given type, and also (if 'Summary View'
409 * is selected), any feature types which are child terms of it in the Sequence
415 protected String[] getTermsInScope(String type)
417 if (!summaryView.isSelected())
419 return new String[] { type };
422 List<String> terms = new ArrayList<>();
425 OntologyI so = SequenceOntologyFactory.getInstance();
427 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
428 for (Object[] row : data)
430 String type2 = (String) row[TYPE_COLUMN];
431 if (!type2.equals(type) && so.isA(type2, type))
436 return terms.toArray(new String[terms.size()]);
439 protected void popupMenu(final int rowSelected, final String type, int x,
442 JPopupMenu men = new JPopupMenu(MessageManager
443 .formatMessage("label.settings_for_param", new String[]
445 JMenuItem scr = new JMenuItem(
446 MessageManager.getString("label.sort_by_score"));
448 final FeatureSettings me = this;
449 scr.addActionListener(new ActionListener()
452 public void actionPerformed(ActionEvent e)
454 String[] types = getTermsInScope(type);
455 me.af.avc.sortAlignmentByFeatureScore(Arrays.asList(types));
458 JMenuItem dens = new JMenuItem(
459 MessageManager.getString("label.sort_by_density"));
460 dens.addActionListener(new ActionListener()
463 public void actionPerformed(ActionEvent e)
465 String[] types = getTermsInScope(type);
466 me.af.avc.sortAlignmentByFeatureDensity(Arrays.asList(types));
471 JMenuItem selCols = new JMenuItem(
472 MessageManager.getString("label.select_columns_containing"));
473 selCols.addActionListener(new ActionListener()
476 public void actionPerformed(ActionEvent arg0)
478 String[] types = getTermsInScope(type);
479 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
483 JMenuItem clearCols = new JMenuItem(MessageManager
484 .getString("label.select_columns_not_containing"));
485 clearCols.addActionListener(new ActionListener()
488 public void actionPerformed(ActionEvent arg0)
490 String[] types = getTermsInScope(type);
491 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
495 JMenuItem hideCols = new JMenuItem(
496 MessageManager.getString("label.hide_columns_containing"));
497 hideCols.addActionListener(new ActionListener()
500 public void actionPerformed(ActionEvent arg0)
502 String[] types = getTermsInScope(type);
503 fr.ap.alignFrame.hideFeatureColumns(true, types);
506 JMenuItem hideOtherCols = new JMenuItem(
507 MessageManager.getString("label.hide_columns_not_containing"));
508 hideOtherCols.addActionListener(new ActionListener()
511 public void actionPerformed(ActionEvent arg0)
513 String[] types = getTermsInScope(type);
514 fr.ap.alignFrame.hideFeatureColumns(false, types);
520 men.add(hideOtherCols);
521 men.show(table, x, y);
525 synchronized public void discoverAllFeatureData()
527 Set<String> allGroups = new HashSet<>();
528 AlignmentI alignment = af.getViewport().getAlignment();
530 for (int i = 0; i < alignment.getHeight(); i++)
532 SequenceI seq = alignment.getSequenceAt(i);
533 for (String group : seq.getFeatures().getFeatureGroups(true))
535 if (group != null && !allGroups.contains(group))
537 allGroups.add(group);
538 checkGroupState(group);
549 * Synchronise gui group list and check visibility of group
552 * @return true if group is visible
554 private boolean checkGroupState(String group)
556 boolean visible = fr.checkGroupVisibility(group, true);
558 for (int g = 0; g < groupPanel.getComponentCount(); g++)
560 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
562 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
567 final String grp = group;
568 final JCheckBox check = new JCheckBox(group, visible);
569 check.setFont(new Font("Serif", Font.BOLD, 12));
570 check.setToolTipText(group);
571 check.addItemListener(new ItemListener()
574 public void itemStateChanged(ItemEvent evt)
576 fr.setGroupVisibility(check.getText(), check.isSelected());
577 resetTable(new String[] { grp });
578 af.alignPanel.paintAlignment(true, true);
581 groupPanel.add(check);
585 synchronized void resetTable(String[] groupChanged)
591 resettingTable = true;
592 typeWidth = new Hashtable<>();
593 // TODO: change avWidth calculation to 'per-sequence' average and use long
596 Set<String> displayableTypes = new HashSet<>();
597 Set<String> foundGroups = new HashSet<>();
600 * determine which feature types may be visible depending on
601 * which groups are selected, and recompute average width data
603 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
606 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
609 * get the sequence's groups for positional features
610 * and keep track of which groups are visible
612 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
613 Set<String> visibleGroups = new HashSet<>();
614 for (String group : groups)
616 if (group == null || checkGroupState(group))
618 visibleGroups.add(group);
621 foundGroups.addAll(groups);
624 * get distinct feature types for visible groups
625 * record distinct visible types, and their count and total length
627 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
628 visibleGroups.toArray(new String[visibleGroups.size()]));
630 for (String type : types)
632 displayableTypes.add(type);
633 float[] avWidth = typeWidth.get(type);
636 avWidth = new float[2];
637 typeWidth.put(type, avWidth);
639 // todo this could include features with a non-visible group
640 // - do we greatly care?
641 // todo should we include non-displayable features here, and only
642 // update when features are added?
643 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
644 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
649 * enable 'Summary View' if some types are sub-types of others
651 Set<String> parents = SequenceOntologyFactory.getInstance()
652 .getParentTerms(displayableTypes);
653 summaryView.setEnabled(parents.size() < displayableTypes.size());
655 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
658 if (fr.hasRenderOrder())
662 fr.findAllFeatures(groupChanged != null); // prod to update
663 // colourschemes. but don't
665 // First add the checks in the previous render order,
666 // in case the window has been closed and reopened
668 List<String> frl = fr.getRenderOrder();
669 for (int ro = frl.size() - 1; ro > -1; ro--)
671 String type = frl.get(ro);
673 if (!displayableTypes.contains(type))
677 data[dataIndex][TYPE_COLUMN] = type;
678 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
679 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
680 data[dataIndex][FILTER_COLUMN] = featureFilter == null
681 ? new FeatureMatcherSet()
683 data[dataIndex][SHOW_COLUMN] = new Boolean(
684 af.getViewport().getFeaturesDisplayed().isVisible(type));
686 displayableTypes.remove(type);
691 * process any extra features belonging only to
692 * a group which was just selected
694 while (!displayableTypes.isEmpty())
696 String type = displayableTypes.iterator().next();
697 data[dataIndex][TYPE_COLUMN] = type;
699 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
700 if (data[dataIndex][COLOUR_COLUMN] == null)
702 // "Colour has been updated in another view!!"
703 fr.clearRenderOrder();
706 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
707 data[dataIndex][FILTER_COLUMN] = featureFilter == null
708 ? new FeatureMatcherSet()
710 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
712 displayableTypes.remove(type);
715 if (originalData == null)
717 originalData = new Object[data.length][COLUMN_COUNT];
718 for (int i = 0; i < data.length; i++)
720 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
725 updateOriginalData(data);
729 * recreate the table model
731 FeatureTableModel dataModel = new FeatureTableModel(data);
732 table.setModel(dataModel);
735 * we want to be able to filter out rows for sub-types, but not to sort
736 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
738 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
740 for (int i = 0; i < table.getColumnCount(); i++)
742 sorter.setSortable(i, false);
746 * filter rows to only top-level Ontology types if requested
748 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
751 public boolean include(
752 Entry<? extends FeatureTableModel, ? extends Integer> entry)
754 if (!summaryView.isSelected())
758 int row = entry.getIdentifier(); // this is model, not view, row number
759 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
760 return parents.contains(featureType);
763 table.setRowSorter(sorter);
765 table.getColumnModel().getColumn(0).setPreferredWidth(200);
767 groupPanel.setLayout(
768 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
769 pruneGroups(foundGroups);
770 groupPanel.validate();
772 updateFeatureRenderer(data, groupChanged != null);
773 resettingTable = false;
777 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
778 * have been made outwith this dialog
780 * <li>a new feature type added (and made visible)</li>
781 * <li>a feature colour changed (in the Amend Features dialog)</li>
786 protected void updateOriginalData(Object[][] foundData)
788 // todo LinkedHashMap instead of Object[][] would be nice
790 Object[][] currentData = ((FeatureTableModel) table.getModel())
792 for (Object[] row : foundData)
794 String type = (String) row[TYPE_COLUMN];
795 boolean found = false;
796 for (Object[] current : currentData)
798 if (type.equals(current[TYPE_COLUMN]))
802 * currently dependent on object equality here;
803 * really need an equals method on FeatureColour
805 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
808 * feature colour has changed externally - update originalData
810 for (Object[] original : originalData)
812 if (type.equals(original[TYPE_COLUMN]))
814 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
825 * new feature detected - add to original data (on top)
827 Object[][] newData = new Object[originalData.length
829 for (int i = 0; i < originalData.length; i++)
831 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
835 originalData = newData;
841 * Remove from the groups panel any checkboxes for groups that are not in the
842 * foundGroups set. This enables removing a group from the display when the last
843 * feature in that group is deleted.
847 protected void pruneGroups(Set<String> foundGroups)
849 for (int g = 0; g < groupPanel.getComponentCount(); g++)
851 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
852 if (!foundGroups.contains(checkbox.getText()))
854 groupPanel.remove(checkbox);
860 * reorder data based on the featureRenderers global priority list.
864 private void ensureOrder(Object[][] data)
866 boolean sort = false;
867 float[] order = new float[data.length];
868 for (int i = 0; i < order.length; i++)
870 order[i] = fr.getOrder(data[i][0].toString());
873 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
877 sort = sort || order[i - 1] > order[i];
882 jalview.util.QuickSort.sort(order, data);
887 * Offers a file chooser dialog, and then loads the feature colours and
888 * filters from file in XML format and unmarshals to Jalview feature settings
892 JalviewFileChooser chooser = new JalviewFileChooser("fc",
893 SEQUENCE_FEATURE_COLOURS);
894 chooser.setFileView(new JalviewFileView());
895 chooser.setDialogTitle(
896 MessageManager.getString("label.load_feature_colours"));
897 chooser.setToolTipText(MessageManager.getString("action.load"));
899 int value = chooser.showOpenDialog(this);
901 if (value == JalviewFileChooser.APPROVE_OPTION)
903 File file = chooser.getSelectedFile();
909 * Loads feature colours and filters from XML stored in the given file
917 InputStreamReader in = new InputStreamReader(
918 new FileInputStream(file), "UTF-8");
920 JAXBContext jc = JAXBContext
921 .newInstance("jalview.xml.binding.jalview");
922 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
923 XMLStreamReader streamReader = XMLInputFactory.newInstance()
924 .createXMLStreamReader(in);
925 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
926 JalviewUserColours.class);
927 JalviewUserColours jucs = jbe.getValue();
929 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
932 * load feature colours
934 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
936 Colour newcol = jucs.getColour().get(i);
937 FeatureColourI colour = jalview.project.Jalview2XML
938 .parseColour(newcol);
939 fr.setColour(newcol.getName(), colour);
940 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
944 * load feature filters; loaded filters will replace any that are
945 * currently defined, other defined filters are left unchanged
947 for (int i = 0; i < jucs.getFilter().size(); i++)
949 Filter filterModel = jucs.getFilter().get(i);
950 String featureType = filterModel.getFeatureType();
951 FeatureMatcherSetI filter = jalview.project.Jalview2XML
952 .parseFilter(featureType, filterModel.getMatcherSet());
953 if (!filter.isEmpty())
955 fr.setFeatureFilter(featureType, filter);
960 * update feature settings table
965 Object[][] data = ((FeatureTableModel) table.getModel())
968 updateFeatureRenderer(data, false);
971 } catch (Exception ex)
973 System.out.println("Error loading User Colour File\n" + ex);
978 * Offers a file chooser dialog, and then saves the current feature colours
979 * and any filters to the selected file in XML format
983 JalviewFileChooser chooser = new JalviewFileChooser("fc",
984 SEQUENCE_FEATURE_COLOURS);
985 chooser.setFileView(new JalviewFileView());
986 chooser.setDialogTitle(
987 MessageManager.getString("label.save_feature_colours"));
988 chooser.setToolTipText(MessageManager.getString("action.save"));
990 int value = chooser.showSaveDialog(this);
992 if (value == JalviewFileChooser.APPROVE_OPTION)
994 save(chooser.getSelectedFile());
999 * Saves feature colours and filters to the given file
1003 void save(File file)
1005 JalviewUserColours ucs = new JalviewUserColours();
1006 ucs.setSchemeName("Sequence Features");
1009 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1010 new FileOutputStream(file), "UTF-8"));
1013 * sort feature types by colour order, from 0 (highest)
1016 Set<String> fr_colours = fr.getAllFeatureColours();
1017 String[] sortedTypes = fr_colours
1018 .toArray(new String[fr_colours.size()]);
1019 Arrays.sort(sortedTypes, new Comparator<String>()
1022 public int compare(String type1, String type2)
1024 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1029 * save feature colours
1031 for (String featureType : sortedTypes)
1033 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1034 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1036 ucs.getColour().add(col);
1040 * save any feature filters
1042 for (String featureType : sortedTypes)
1044 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1045 if (filter != null && !filter.isEmpty())
1047 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1048 FeatureMatcherI firstMatcher = iterator.next();
1049 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1050 .marshalFilter(firstMatcher, iterator,
1052 Filter filterModel = new Filter();
1053 filterModel.setFeatureType(featureType);
1054 filterModel.setMatcherSet(ms);
1055 ucs.getFilter().add(filterModel);
1058 JAXBContext jaxbContext = JAXBContext
1059 .newInstance(JalviewUserColours.class);
1060 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1061 jaxbMarshaller.marshal(
1062 new ObjectFactory().createJalviewUserColours(ucs), out);
1064 // jaxbMarshaller.marshal(object, pout);
1065 // marshaller.marshal(object);
1068 // ucs.marshal(out);
1070 } catch (Exception ex)
1072 ex.printStackTrace();
1076 public void invertSelection()
1078 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1079 for (int i = 0; i < data.length; i++)
1081 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1083 updateFeatureRenderer(data, true);
1087 public void orderByAvWidth()
1089 if (table == null || table.getModel() == null)
1093 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1094 float[] width = new float[data.length];
1098 for (int i = 0; i < data.length; i++)
1100 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1103 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1104 // weight - but have to make per
1105 // sequence, too (awidth[2])
1106 // if (width[i]==1) // hack to distinguish single width sequences.
1117 boolean sort = false;
1118 for (int i = 0; i < width.length; i++)
1120 // awidth = (float[]) typeWidth.get(data[i][0]);
1123 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1126 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1132 width[i] /= max; // normalize
1133 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1137 sort = sort || width[i - 1] > width[i];
1142 jalview.util.QuickSort.sort(width, data);
1143 // update global priority order
1146 updateFeatureRenderer(data, false);
1154 frame.setClosed(true);
1155 } catch (Exception exe)
1161 public void updateFeatureRenderer(Object[][] data)
1163 updateFeatureRenderer(data, true);
1167 * Update the priority order of features; only repaint if this changed the order
1168 * of visible features
1173 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1175 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1177 if (fr.setFeaturePriority(rowData, visibleNew))
1179 af.alignPanel.paintAlignment(true, true);
1184 * Converts table data into an array of data beans
1186 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1188 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1189 for (int i = 0; i < data.length; i++)
1191 String type = (String) data[i][TYPE_COLUMN];
1192 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1193 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1194 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1195 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1201 private void jbInit() throws Exception
1203 this.setLayout(new BorderLayout());
1205 JPanel settingsPane = new JPanel();
1206 settingsPane.setLayout(new BorderLayout());
1208 JPanel bigPanel = new JPanel();
1209 bigPanel.setLayout(new BorderLayout());
1211 groupPanel = new JPanel();
1212 bigPanel.add(groupPanel, BorderLayout.NORTH);
1214 JButton invert = new JButton(
1215 MessageManager.getString("label.invert_selection"));
1216 invert.setFont(JvSwingUtils.getLabelFont());
1217 invert.addActionListener(new ActionListener()
1220 public void actionPerformed(ActionEvent e)
1226 JButton optimizeOrder = new JButton(
1227 MessageManager.getString("label.optimise_order"));
1228 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1229 optimizeOrder.addActionListener(new ActionListener()
1232 public void actionPerformed(ActionEvent e)
1238 JButton sortByScore = new JButton(
1239 MessageManager.getString("label.seq_sort_by_score"));
1240 sortByScore.setFont(JvSwingUtils.getLabelFont());
1241 sortByScore.addActionListener(new ActionListener()
1244 public void actionPerformed(ActionEvent e)
1246 af.avc.sortAlignmentByFeatureScore(null);
1249 JButton sortByDens = new JButton(
1250 MessageManager.getString("label.sequence_sort_by_density"));
1251 sortByDens.setFont(JvSwingUtils.getLabelFont());
1252 sortByDens.addActionListener(new ActionListener()
1255 public void actionPerformed(ActionEvent e)
1257 af.avc.sortAlignmentByFeatureDensity(null);
1261 JButton help = new JButton(MessageManager.getString("action.help"));
1262 help.setFont(JvSwingUtils.getLabelFont());
1263 help.addActionListener(new ActionListener()
1266 public void actionPerformed(ActionEvent e)
1270 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1271 } catch (HelpSetException e1)
1273 e1.printStackTrace();
1278 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1279 cancel.setFont(JvSwingUtils.getLabelFont());
1280 cancel.addActionListener(new ActionListener()
1283 public void actionPerformed(ActionEvent e)
1285 fr.setTransparency(originalTransparency);
1286 fr.setFeatureFilters(originalFilters);
1287 updateFeatureRenderer(originalData);
1292 JButton ok = new JButton(MessageManager.getString("action.ok"));
1293 ok.setFont(JvSwingUtils.getLabelFont());
1294 ok.addActionListener(new ActionListener()
1297 public void actionPerformed(ActionEvent e)
1303 JButton loadColours = new JButton(
1304 MessageManager.getString("label.load_colours"));
1305 loadColours.setFont(JvSwingUtils.getLabelFont());
1306 loadColours.setToolTipText(
1307 MessageManager.getString("label.load_colours_tooltip"));
1308 loadColours.addActionListener(new ActionListener()
1311 public void actionPerformed(ActionEvent e)
1317 JButton saveColours = new JButton(
1318 MessageManager.getString("label.save_colours"));
1319 saveColours.setFont(JvSwingUtils.getLabelFont());
1320 saveColours.setToolTipText(
1321 MessageManager.getString("label.save_colours_tooltip"));
1322 saveColours.addActionListener(new ActionListener()
1325 public void actionPerformed(ActionEvent e)
1330 transparency.addChangeListener(new ChangeListener()
1333 public void stateChanged(ChangeEvent evt)
1335 if (!inConstruction)
1337 fr.setTransparency((100 - transparency.getValue()) / 100f);
1338 af.alignPanel.paintAlignment(true, true);
1343 summaryView = new JCheckBox(
1344 MessageManager.getString("label.summary_view"));
1347 MessageManager.getString("label.summary_view_tip"));
1348 summaryView.addActionListener(new ActionListener()
1351 public void actionPerformed(ActionEvent e)
1357 transparency.setMaximum(70);
1358 transparency.setToolTipText(
1359 MessageManager.getString("label.transparency_tip"));
1361 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1362 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1364 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1365 transbuttons.add(optimizeOrder);
1366 transbuttons.add(invert);
1367 transbuttons.add(sortByScore);
1368 transbuttons.add(sortByDens);
1369 transbuttons.add(help);
1370 JPanel transPanel = new JPanel(new GridLayout(3, 1));
1371 transPanel.add(summaryView);
1372 transPanel.add(new JLabel(" Colour transparency" + ":"));
1373 transPanel.add(transparency);
1374 lowerPanel.add(transPanel);
1375 lowerPanel.add(transbuttons);
1377 JPanel buttonPanel = new JPanel();
1378 buttonPanel.add(ok);
1379 buttonPanel.add(cancel);
1380 buttonPanel.add(loadColours);
1381 buttonPanel.add(saveColours);
1382 bigPanel.add(scrollPane, BorderLayout.CENTER);
1383 settingsPane.add(bigPanel, BorderLayout.CENTER);
1384 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1385 this.add(settingsPane);
1389 * Reorders features by 'dragging' selectedRow to 'newRow'
1393 protected void dragRow(int newRow)
1395 if (summaryView.isSelected())
1397 // no drag while in summary view
1401 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1404 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1405 * this could be more than one row away for a very fast drag action
1406 * so just swap it with adjacent rows until we get it there
1408 Object[][] data = ((FeatureTableModel) table.getModel())
1410 int direction = newRow < selectedRow ? -1 : 1;
1411 for (int i = selectedRow; i != newRow; i += direction)
1413 Object[] temp = data[i];
1414 data[i] = data[i + direction];
1415 data[i + direction] = temp;
1417 updateFeatureRenderer(data);
1419 selectedRow = newRow;
1423 protected void refreshTable()
1425 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1426 for (Object[] row : data)
1428 String type = (String) row[TYPE_COLUMN];
1429 FeatureColourI colour = fr.getFeatureColours().get(type);
1430 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1433 filter = new FeatureMatcherSet();
1435 row[COLOUR_COLUMN] = colour;
1436 row[FILTER_COLUMN] = filter;
1442 * Answers a suitable tooltip to show on the colour cell of the table
1446 * if true include 'click to edit' and similar text
1449 public static String getColorTooltip(FeatureColourI fcol,
1456 if (fcol.isSimpleColour())
1458 return withHint ? BASE_TOOLTIP : null;
1460 String description = fcol.getDescription();
1461 description = description.replaceAll("<", "<");
1462 description = description.replaceAll(">", ">");
1463 StringBuilder tt = new StringBuilder(description);
1466 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1468 return JvSwingUtils.wrapTooltip(true, tt.toString());
1471 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1474 boolean thr = false;
1475 StringBuilder tx = new StringBuilder();
1477 if (gcol.isColourByAttribute())
1479 tx.append(FeatureMatcher
1480 .toAttributeDisplayName(gcol.getAttributeName()));
1482 else if (!gcol.isColourByLabel())
1484 tx.append(MessageManager.getString("label.score"));
1487 if (gcol.isAboveThreshold())
1492 if (gcol.isBelowThreshold())
1497 if (gcol.isColourByLabel())
1503 if (!gcol.isColourByAttribute())
1511 Color newColor = gcol.getMaxColour();
1512 comp.setBackground(newColor);
1513 // System.err.println("Width is " + w / 2);
1514 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1515 comp.setIcon(ficon);
1516 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1517 // + newColor.getGreen() + ", " + newColor.getBlue()
1518 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1519 // + ", " + minCol.getBlue() + ")");
1521 comp.setHorizontalAlignment(SwingConstants.CENTER);
1522 comp.setText(tx.toString());
1525 // ///////////////////////////////////////////////////////////////////////
1526 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1527 // ///////////////////////////////////////////////////////////////////////
1528 class FeatureTableModel extends AbstractTableModel
1530 private String[] columnNames = {
1531 MessageManager.getString("label.feature_type"),
1532 MessageManager.getString("action.colour"),
1533 MessageManager.getString("label.configuration"),
1534 MessageManager.getString("label.show") };
1536 private Object[][] data;
1538 FeatureTableModel(Object[][] data)
1543 public Object[][] getData()
1548 public void setData(Object[][] data)
1554 public int getColumnCount()
1556 return columnNames.length;
1559 public Object[] getRow(int row)
1565 public int getRowCount()
1571 public String getColumnName(int col)
1573 return columnNames[col];
1577 public Object getValueAt(int row, int col)
1579 return data[row][col];
1583 * Answers the class of column c of the table
1586 public Class<?> getColumnClass(int c)
1591 return String.class;
1593 return FeatureColour.class;
1595 return FeatureMatcherSet.class;
1597 return Boolean.class;
1602 * Answers true for all columns except Feature Type
1605 public boolean isCellEditable(int row, int col)
1607 return col != TYPE_COLUMN;
1611 * Sets the value in the model for a given row and column. If Visibility
1612 * (Show/Hide) is being set, and the table is in Summary View, then it is
1613 * set also on any sub-types of the row's feature type.
1616 public void setValueAt(Object value, int row, int col)
1618 data[row][col] = value;
1619 fireTableCellUpdated(row, col);
1620 if (summaryView.isSelected() && col == SHOW_COLUMN)
1622 setSubtypesVisibility(row, (Boolean) value);
1624 updateFeatureRenderer(data);
1628 * Sets the visibility of any feature types which are sub-types of the type
1629 * in the given row of the table
1634 protected void setSubtypesVisibility(int row, Boolean value)
1636 String type = (String) data[row][TYPE_COLUMN];
1637 OntologyI so = SequenceOntologyFactory.getInstance();
1639 for (int r = 0; r < data.length; r++)
1643 String type2 = (String) data[r][TYPE_COLUMN];
1644 if (so.isA(type2, type))
1646 data[r][SHOW_COLUMN] = value;
1647 fireTableCellUpdated(r, SHOW_COLUMN);
1654 class ColorRenderer extends JLabel implements TableCellRenderer
1656 Border unselectedBorder = null;
1658 Border selectedBorder = null;
1660 public ColorRenderer()
1662 setOpaque(true); // MUST do this for background to show up.
1663 setHorizontalTextPosition(SwingConstants.CENTER);
1664 setVerticalTextPosition(SwingConstants.CENTER);
1668 public Component getTableCellRendererComponent(JTable tbl, Object color,
1669 boolean isSelected, boolean hasFocus, int row, int column)
1671 FeatureColourI cellColour = (FeatureColourI) color;
1673 setBackground(tbl.getBackground());
1674 if (!cellColour.isSimpleColour())
1676 Rectangle cr = tbl.getCellRect(row, column, false);
1677 FeatureSettings.renderGraduatedColor(this, cellColour,
1678 (int) cr.getWidth(), (int) cr.getHeight());
1684 setBackground(cellColour.getColour());
1688 if (selectedBorder == null)
1690 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1691 tbl.getSelectionBackground());
1693 setBorder(selectedBorder);
1697 if (unselectedBorder == null)
1699 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1700 tbl.getBackground());
1702 setBorder(unselectedBorder);
1709 class FilterRenderer extends JLabel implements TableCellRenderer
1711 javax.swing.border.Border unselectedBorder = null;
1713 javax.swing.border.Border selectedBorder = null;
1715 public FilterRenderer()
1717 setOpaque(true); // MUST do this for background to show up.
1718 setHorizontalTextPosition(SwingConstants.CENTER);
1719 setVerticalTextPosition(SwingConstants.CENTER);
1723 public Component getTableCellRendererComponent(JTable tbl,
1724 Object filter, boolean isSelected, boolean hasFocus, int row,
1727 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1729 String asText = theFilter.toString();
1730 setBackground(tbl.getBackground());
1731 this.setText(asText);
1736 if (selectedBorder == null)
1738 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1739 tbl.getSelectionBackground());
1741 setBorder(selectedBorder);
1745 if (unselectedBorder == null)
1747 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1748 tbl.getBackground());
1750 setBorder(unselectedBorder);
1758 * update comp using rendering settings from gcol
1763 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1765 int w = comp.getWidth(), h = comp.getHeight();
1768 w = (int) comp.getPreferredSize().getWidth();
1769 h = (int) comp.getPreferredSize().getHeight();
1776 renderGraduatedColor(comp, gcol, w, h);
1779 class ColorEditor extends AbstractCellEditor
1780 implements TableCellEditor, ActionListener
1784 FeatureColourI currentColor;
1786 FeatureTypeSettings chooser;
1790 JButton colourButton;
1792 JColorChooser colorChooser;
1796 protected static final String EDIT = "edit";
1798 int rowSelected = 0;
1800 public ColorEditor(FeatureSettings me)
1803 // Set up the editor (from the table's point of view),
1804 // which is a button.
1805 // This button brings up the color chooser dialog,
1806 // which is the editor from the user's point of view.
1807 colourButton = new JButton();
1808 colourButton.setActionCommand(EDIT);
1809 colourButton.addActionListener(this);
1810 colourButton.setBorderPainted(false);
1811 // Set up the dialog that the button brings up.
1812 colorChooser = new JColorChooser();
1813 dialog = JColorChooser.createDialog(colourButton,
1814 MessageManager.getString("label.select_colour"), true, // modal
1815 colorChooser, this, // OK button handler
1816 null); // no CANCEL button handler
1820 * Handles events from the editor button and from the dialog's OK button.
1823 public void actionPerformed(ActionEvent e)
1825 // todo test e.getSource() instead here
1826 if (EDIT.equals(e.getActionCommand()))
1828 // The user has clicked the cell, so
1829 // bring up the dialog.
1830 if (currentColor.isSimpleColour())
1832 // bring up simple color chooser
1833 colourButton.setBackground(currentColor.getColour());
1834 colorChooser.setColor(currentColor.getColour());
1835 dialog.setVisible(true);
1839 // bring up graduated chooser.
1840 chooser = new FeatureTypeSettings(me.fr, type);
1841 chooser.setRequestFocusEnabled(true);
1842 chooser.requestFocus();
1843 chooser.addActionListener(this);
1844 // Make the renderer reappear.
1845 fireEditingStopped();
1850 if (currentColor.isSimpleColour())
1853 * read off colour picked in colour chooser after OK pressed
1855 currentColor = new FeatureColour(colorChooser.getColor());
1856 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1861 * after OK in variable colour dialog, any changes to colour
1862 * (or filters!) are already set in FeatureRenderer, so just
1863 * update table data without triggering updateFeatureRenderer
1867 fireEditingStopped();
1868 me.table.validate();
1872 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1874 public Object getCellEditorValue()
1876 return currentColor;
1879 // Implement the one method defined by TableCellEditor.
1881 public Component getTableCellEditorComponent(JTable theTable, Object value,
1882 boolean isSelected, int row, int column)
1884 currentColor = (FeatureColourI) value;
1885 this.rowSelected = row;
1886 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1887 colourButton.setOpaque(true);
1888 colourButton.setBackground(me.getBackground());
1889 if (!currentColor.isSimpleColour())
1891 JLabel btn = new JLabel();
1892 btn.setSize(colourButton.getSize());
1893 FeatureSettings.renderGraduatedColor(btn, currentColor);
1894 colourButton.setBackground(btn.getBackground());
1895 colourButton.setIcon(btn.getIcon());
1896 colourButton.setText(btn.getText());
1900 colourButton.setText("");
1901 colourButton.setIcon(null);
1902 colourButton.setBackground(currentColor.getColour());
1904 return colourButton;
1909 * The cell editor for the Filter column. It displays the text of any filters
1910 * for the feature type in that row (in full as a tooltip, possible abbreviated
1911 * as display text). On click in the cell, opens the Feature Display Settings
1912 * dialog at the Filters tab.
1914 class FilterEditor extends AbstractCellEditor
1915 implements TableCellEditor, ActionListener
1919 FeatureMatcherSetI currentFilter;
1925 JButton filterButton;
1927 protected static final String EDIT = "edit";
1929 int rowSelected = 0;
1931 public FilterEditor(FeatureSettings me)
1934 filterButton = new JButton();
1935 filterButton.setActionCommand(EDIT);
1936 filterButton.addActionListener(this);
1937 filterButton.setBorderPainted(false);
1941 * Handles events from the editor button
1944 public void actionPerformed(ActionEvent e)
1946 if (filterButton == e.getSource())
1948 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1949 chooser.addActionListener(this);
1950 chooser.setRequestFocusEnabled(true);
1951 chooser.requestFocus();
1952 if (lastLocation != null)
1954 // todo open at its last position on screen
1955 chooser.setBounds(lastLocation.x, lastLocation.y,
1956 chooser.getWidth(), chooser.getHeight());
1959 fireEditingStopped();
1961 else if (e.getSource() instanceof Component)
1964 * after OK in variable colour dialog, any changes to filter
1965 * (or colours!) are already set in FeatureRenderer, so just
1966 * update table data without triggering updateFeatureRenderer
1969 fireEditingStopped();
1970 me.table.validate();
1975 public Object getCellEditorValue()
1977 return currentFilter;
1981 public Component getTableCellEditorComponent(JTable theTable, Object value,
1982 boolean isSelected, int row, int column)
1984 currentFilter = (FeatureMatcherSetI) value;
1985 this.rowSelected = row;
1986 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1987 filterButton.setOpaque(true);
1988 filterButton.setBackground(me.getBackground());
1989 filterButton.setText(currentFilter.toString());
1990 filterButton.setToolTipText(currentFilter.toString());
1991 filterButton.setIcon(null);
1992 return filterButton;
1997 class FeatureIcon implements Icon
1999 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2001 FeatureColourI gcol;
2005 boolean midspace = false;
2007 int width = 50, height = 20;
2009 int s1, e1; // start and end of midpoint band for thresholded symbol
2011 Color mpcolour = Color.white;
2013 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2033 public int getIconWidth()
2039 public int getIconHeight()
2045 public void paintIcon(Component c, Graphics g, int x, int y)
2048 if (gcol.isColourByLabel())
2051 g.fillRect(0, 0, width, height);
2052 // need an icon here.
2053 g.setColor(gcol.getMaxColour());
2055 g.setFont(VERDANA_9);
2057 // g.setFont(g.getFont().deriveFont(
2058 // AffineTransform.getScaleInstance(
2059 // width/g.getFontMetrics().stringWidth("Label"),
2060 // height/g.getFontMetrics().getHeight())));
2062 g.drawString(MessageManager.getString("label.label"), 0, 0);
2067 Color minCol = gcol.getMinColour();
2069 g.fillRect(0, 0, s1, height);
2072 g.setColor(Color.white);
2073 g.fillRect(s1, 0, e1 - s1, height);
2075 g.setColor(gcol.getMaxColour());
2076 g.fillRect(0, e1, width - e1, height);