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 child terms in the sequence ontology
414 protected String[] getTermsInScope(String type)
416 if (!summaryView.isSelected())
418 return new String[] { type };
421 List<String> terms = new ArrayList<>();
424 OntologyI so = SequenceOntologyFactory.getInstance();
426 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
427 for (Object[] row : data)
429 String type2 = (String) row[TYPE_COLUMN];
430 if (!type2.equals(type) && so.isA(type2, type))
435 return terms.toArray(new String[terms.size()]);
438 protected void popupMenu(final int rowSelected, final String type, int x,
441 JPopupMenu men = new JPopupMenu(MessageManager
442 .formatMessage("label.settings_for_param", new String[]
444 JMenuItem scr = new JMenuItem(
445 MessageManager.getString("label.sort_by_score"));
447 final FeatureSettings me = this;
448 scr.addActionListener(new ActionListener()
451 public void actionPerformed(ActionEvent e)
453 String[] types = getTermsInScope(type);
454 me.af.avc.sortAlignmentByFeatureScore(Arrays.asList(types));
457 JMenuItem dens = new JMenuItem(
458 MessageManager.getString("label.sort_by_density"));
459 dens.addActionListener(new ActionListener()
462 public void actionPerformed(ActionEvent e)
464 String[] types = getTermsInScope(type);
465 me.af.avc.sortAlignmentByFeatureDensity(Arrays.asList(types));
470 JMenuItem selCols = new JMenuItem(
471 MessageManager.getString("label.select_columns_containing"));
472 selCols.addActionListener(new ActionListener()
475 public void actionPerformed(ActionEvent arg0)
477 String[] types = getTermsInScope(type);
478 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
482 JMenuItem clearCols = new JMenuItem(MessageManager
483 .getString("label.select_columns_not_containing"));
484 clearCols.addActionListener(new ActionListener()
487 public void actionPerformed(ActionEvent arg0)
489 String[] types = getTermsInScope(type);
490 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
494 JMenuItem hideCols = new JMenuItem(
495 MessageManager.getString("label.hide_columns_containing"));
496 hideCols.addActionListener(new ActionListener()
499 public void actionPerformed(ActionEvent arg0)
501 String[] types = getTermsInScope(type);
502 fr.ap.alignFrame.hideFeatureColumns(true, types);
505 JMenuItem hideOtherCols = new JMenuItem(
506 MessageManager.getString("label.hide_columns_not_containing"));
507 hideOtherCols.addActionListener(new ActionListener()
510 public void actionPerformed(ActionEvent arg0)
512 String[] types = getTermsInScope(type);
513 fr.ap.alignFrame.hideFeatureColumns(false, types);
519 men.add(hideOtherCols);
520 men.show(table, x, y);
524 synchronized public void discoverAllFeatureData()
526 Set<String> allGroups = new HashSet<>();
527 AlignmentI alignment = af.getViewport().getAlignment();
529 for (int i = 0; i < alignment.getHeight(); i++)
531 SequenceI seq = alignment.getSequenceAt(i);
532 for (String group : seq.getFeatures().getFeatureGroups(true))
534 if (group != null && !allGroups.contains(group))
536 allGroups.add(group);
537 checkGroupState(group);
548 * Synchronise gui group list and check visibility of group
551 * @return true if group is visible
553 private boolean checkGroupState(String group)
555 boolean visible = fr.checkGroupVisibility(group, true);
557 for (int g = 0; g < groupPanel.getComponentCount(); g++)
559 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
561 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
566 final String grp = group;
567 final JCheckBox check = new JCheckBox(group, visible);
568 check.setFont(new Font("Serif", Font.BOLD, 12));
569 check.setToolTipText(group);
570 check.addItemListener(new ItemListener()
573 public void itemStateChanged(ItemEvent evt)
575 fr.setGroupVisibility(check.getText(), check.isSelected());
576 resetTable(new String[] { grp });
577 af.alignPanel.paintAlignment(true, true);
580 groupPanel.add(check);
584 synchronized void resetTable(String[] groupChanged)
590 resettingTable = true;
591 typeWidth = new Hashtable<>();
592 // TODO: change avWidth calculation to 'per-sequence' average and use long
595 Set<String> displayableTypes = new HashSet<>();
596 Set<String> foundGroups = new HashSet<>();
599 * determine which feature types may be visible depending on
600 * which groups are selected, and recompute average width data
602 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
605 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
608 * get the sequence's groups for positional features
609 * and keep track of which groups are visible
611 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
612 Set<String> visibleGroups = new HashSet<>();
613 for (String group : groups)
615 if (group == null || checkGroupState(group))
617 visibleGroups.add(group);
620 foundGroups.addAll(groups);
623 * get distinct feature types for visible groups
624 * record distinct visible types, and their count and total length
626 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
627 visibleGroups.toArray(new String[visibleGroups.size()]));
629 for (String type : types)
631 displayableTypes.add(type);
632 float[] avWidth = typeWidth.get(type);
635 avWidth = new float[2];
636 typeWidth.put(type, avWidth);
638 // todo this could include features with a non-visible group
639 // - do we greatly care?
640 // todo should we include non-displayable features here, and only
641 // update when features are added?
642 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
643 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
648 * enable 'Summary View' if some types are sub-types of others
650 Set<String> parents = SequenceOntologyFactory.getInstance()
651 .getParentTerms(displayableTypes);
652 summaryView.setEnabled(parents.size() < displayableTypes.size());
654 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
657 if (fr.hasRenderOrder())
661 fr.findAllFeatures(groupChanged != null); // prod to update
662 // colourschemes. but don't
664 // First add the checks in the previous render order,
665 // in case the window has been closed and reopened
667 List<String> frl = fr.getRenderOrder();
668 for (int ro = frl.size() - 1; ro > -1; ro--)
670 String type = frl.get(ro);
672 if (!displayableTypes.contains(type))
676 data[dataIndex][TYPE_COLUMN] = type;
677 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
678 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
679 data[dataIndex][FILTER_COLUMN] = featureFilter == null
680 ? new FeatureMatcherSet()
682 data[dataIndex][SHOW_COLUMN] = new Boolean(
683 af.getViewport().getFeaturesDisplayed().isVisible(type));
685 displayableTypes.remove(type);
690 * process any extra features belonging only to
691 * a group which was just selected
693 while (!displayableTypes.isEmpty())
695 String type = displayableTypes.iterator().next();
696 data[dataIndex][TYPE_COLUMN] = type;
698 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
699 if (data[dataIndex][COLOUR_COLUMN] == null)
701 // "Colour has been updated in another view!!"
702 fr.clearRenderOrder();
705 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
706 data[dataIndex][FILTER_COLUMN] = featureFilter == null
707 ? new FeatureMatcherSet()
709 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
711 displayableTypes.remove(type);
714 if (originalData == null)
716 originalData = new Object[data.length][COLUMN_COUNT];
717 for (int i = 0; i < data.length; i++)
719 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
724 updateOriginalData(data);
728 * recreate the table model
730 FeatureTableModel dataModel = new FeatureTableModel(data);
731 table.setModel(dataModel);
734 * we want to be able to filter out rows for sub-types, but not to sort
735 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
737 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
739 for (int i = 0; i < table.getColumnCount(); i++)
741 sorter.setSortable(i, false);
745 * filter rows to only top-level Ontology types if requested
747 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
750 public boolean include(
751 Entry<? extends FeatureTableModel, ? extends Integer> entry)
753 if (!summaryView.isSelected())
757 int row = entry.getIdentifier(); // this is model, not view, row number
758 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
759 return parents.contains(featureType);
762 table.setRowSorter(sorter);
764 table.getColumnModel().getColumn(0).setPreferredWidth(200);
766 groupPanel.setLayout(
767 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
768 pruneGroups(foundGroups);
769 groupPanel.validate();
771 updateFeatureRenderer(data, groupChanged != null);
772 resettingTable = false;
776 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
777 * have been made outwith this dialog
779 * <li>a new feature type added (and made visible)</li>
780 * <li>a feature colour changed (in the Amend Features dialog)</li>
785 protected void updateOriginalData(Object[][] foundData)
787 // todo LinkedHashMap instead of Object[][] would be nice
789 Object[][] currentData = ((FeatureTableModel) table.getModel())
791 for (Object[] row : foundData)
793 String type = (String) row[TYPE_COLUMN];
794 boolean found = false;
795 for (Object[] current : currentData)
797 if (type.equals(current[TYPE_COLUMN]))
801 * currently dependent on object equality here;
802 * really need an equals method on FeatureColour
804 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
807 * feature colour has changed externally - update originalData
809 for (Object[] original : originalData)
811 if (type.equals(original[TYPE_COLUMN]))
813 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
824 * new feature detected - add to original data (on top)
826 Object[][] newData = new Object[originalData.length
828 for (int i = 0; i < originalData.length; i++)
830 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
834 originalData = newData;
840 * Remove from the groups panel any checkboxes for groups that are not in the
841 * foundGroups set. This enables removing a group from the display when the last
842 * feature in that group is deleted.
846 protected void pruneGroups(Set<String> foundGroups)
848 for (int g = 0; g < groupPanel.getComponentCount(); g++)
850 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
851 if (!foundGroups.contains(checkbox.getText()))
853 groupPanel.remove(checkbox);
859 * reorder data based on the featureRenderers global priority list.
863 private void ensureOrder(Object[][] data)
865 boolean sort = false;
866 float[] order = new float[data.length];
867 for (int i = 0; i < order.length; i++)
869 order[i] = fr.getOrder(data[i][0].toString());
872 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
876 sort = sort || order[i - 1] > order[i];
881 jalview.util.QuickSort.sort(order, data);
886 * Offers a file chooser dialog, and then loads the feature colours and
887 * filters from file in XML format and unmarshals to Jalview feature settings
891 JalviewFileChooser chooser = new JalviewFileChooser("fc",
892 SEQUENCE_FEATURE_COLOURS);
893 chooser.setFileView(new JalviewFileView());
894 chooser.setDialogTitle(
895 MessageManager.getString("label.load_feature_colours"));
896 chooser.setToolTipText(MessageManager.getString("action.load"));
898 int value = chooser.showOpenDialog(this);
900 if (value == JalviewFileChooser.APPROVE_OPTION)
902 File file = chooser.getSelectedFile();
908 * Loads feature colours and filters from XML stored in the given file
916 InputStreamReader in = new InputStreamReader(
917 new FileInputStream(file), "UTF-8");
919 JAXBContext jc = JAXBContext
920 .newInstance("jalview.xml.binding.jalview");
921 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
922 XMLStreamReader streamReader = XMLInputFactory.newInstance()
923 .createXMLStreamReader(in);
924 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
925 JalviewUserColours.class);
926 JalviewUserColours jucs = jbe.getValue();
928 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
931 * load feature colours
933 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
935 Colour newcol = jucs.getColour().get(i);
936 FeatureColourI colour = jalview.project.Jalview2XML
937 .parseColour(newcol);
938 fr.setColour(newcol.getName(), colour);
939 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
943 * load feature filters; loaded filters will replace any that are
944 * currently defined, other defined filters are left unchanged
946 for (int i = 0; i < jucs.getFilter().size(); i++)
948 Filter filterModel = jucs.getFilter().get(i);
949 String featureType = filterModel.getFeatureType();
950 FeatureMatcherSetI filter = jalview.project.Jalview2XML
951 .parseFilter(featureType, filterModel.getMatcherSet());
952 if (!filter.isEmpty())
954 fr.setFeatureFilter(featureType, filter);
959 * update feature settings table
964 Object[][] data = ((FeatureTableModel) table.getModel())
967 updateFeatureRenderer(data, false);
970 } catch (Exception ex)
972 System.out.println("Error loading User Colour File\n" + ex);
977 * Offers a file chooser dialog, and then saves the current feature colours
978 * and any filters to the selected file in XML format
982 JalviewFileChooser chooser = new JalviewFileChooser("fc",
983 SEQUENCE_FEATURE_COLOURS);
984 chooser.setFileView(new JalviewFileView());
985 chooser.setDialogTitle(
986 MessageManager.getString("label.save_feature_colours"));
987 chooser.setToolTipText(MessageManager.getString("action.save"));
989 int value = chooser.showSaveDialog(this);
991 if (value == JalviewFileChooser.APPROVE_OPTION)
993 save(chooser.getSelectedFile());
998 * Saves feature colours and filters to the given file
1002 void save(File file)
1004 JalviewUserColours ucs = new JalviewUserColours();
1005 ucs.setSchemeName("Sequence Features");
1008 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1009 new FileOutputStream(file), "UTF-8"));
1012 * sort feature types by colour order, from 0 (highest)
1015 Set<String> fr_colours = fr.getAllFeatureColours();
1016 String[] sortedTypes = fr_colours
1017 .toArray(new String[fr_colours.size()]);
1018 Arrays.sort(sortedTypes, new Comparator<String>()
1021 public int compare(String type1, String type2)
1023 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1028 * save feature colours
1030 for (String featureType : sortedTypes)
1032 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1033 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1035 ucs.getColour().add(col);
1039 * save any feature filters
1041 for (String featureType : sortedTypes)
1043 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1044 if (filter != null && !filter.isEmpty())
1046 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1047 FeatureMatcherI firstMatcher = iterator.next();
1048 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1049 .marshalFilter(firstMatcher, iterator,
1051 Filter filterModel = new Filter();
1052 filterModel.setFeatureType(featureType);
1053 filterModel.setMatcherSet(ms);
1054 ucs.getFilter().add(filterModel);
1057 JAXBContext jaxbContext = JAXBContext
1058 .newInstance(JalviewUserColours.class);
1059 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1060 jaxbMarshaller.marshal(
1061 new ObjectFactory().createJalviewUserColours(ucs), out);
1063 // jaxbMarshaller.marshal(object, pout);
1064 // marshaller.marshal(object);
1067 // ucs.marshal(out);
1069 } catch (Exception ex)
1071 ex.printStackTrace();
1075 public void invertSelection()
1077 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1078 for (int i = 0; i < data.length; i++)
1080 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1082 updateFeatureRenderer(data, true);
1086 public void orderByAvWidth()
1088 if (table == null || table.getModel() == null)
1092 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1093 float[] width = new float[data.length];
1097 for (int i = 0; i < data.length; i++)
1099 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1102 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1103 // weight - but have to make per
1104 // sequence, too (awidth[2])
1105 // if (width[i]==1) // hack to distinguish single width sequences.
1116 boolean sort = false;
1117 for (int i = 0; i < width.length; i++)
1119 // awidth = (float[]) typeWidth.get(data[i][0]);
1122 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1125 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1131 width[i] /= max; // normalize
1132 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1136 sort = sort || width[i - 1] > width[i];
1141 jalview.util.QuickSort.sort(width, data);
1142 // update global priority order
1145 updateFeatureRenderer(data, false);
1153 frame.setClosed(true);
1154 } catch (Exception exe)
1160 public void updateFeatureRenderer(Object[][] data)
1162 updateFeatureRenderer(data, true);
1166 * Update the priority order of features; only repaint if this changed the order
1167 * of visible features
1172 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1174 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1176 if (fr.setFeaturePriority(rowData, visibleNew))
1178 af.alignPanel.paintAlignment(true, true);
1183 * Converts table data into an array of data beans
1185 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1187 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1188 for (int i = 0; i < data.length; i++)
1190 String type = (String) data[i][TYPE_COLUMN];
1191 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1192 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1193 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1194 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1200 private void jbInit() throws Exception
1202 this.setLayout(new BorderLayout());
1204 JPanel settingsPane = new JPanel();
1205 settingsPane.setLayout(new BorderLayout());
1207 JPanel bigPanel = new JPanel();
1208 bigPanel.setLayout(new BorderLayout());
1210 groupPanel = new JPanel();
1211 bigPanel.add(groupPanel, BorderLayout.NORTH);
1213 JButton invert = new JButton(
1214 MessageManager.getString("label.invert_selection"));
1215 invert.setFont(JvSwingUtils.getLabelFont());
1216 invert.addActionListener(new ActionListener()
1219 public void actionPerformed(ActionEvent e)
1225 JButton optimizeOrder = new JButton(
1226 MessageManager.getString("label.optimise_order"));
1227 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1228 optimizeOrder.addActionListener(new ActionListener()
1231 public void actionPerformed(ActionEvent e)
1237 JButton sortByScore = new JButton(
1238 MessageManager.getString("label.seq_sort_by_score"));
1239 sortByScore.setFont(JvSwingUtils.getLabelFont());
1240 sortByScore.addActionListener(new ActionListener()
1243 public void actionPerformed(ActionEvent e)
1245 af.avc.sortAlignmentByFeatureScore(null);
1248 JButton sortByDens = new JButton(
1249 MessageManager.getString("label.sequence_sort_by_density"));
1250 sortByDens.setFont(JvSwingUtils.getLabelFont());
1251 sortByDens.addActionListener(new ActionListener()
1254 public void actionPerformed(ActionEvent e)
1256 af.avc.sortAlignmentByFeatureDensity(null);
1260 JButton help = new JButton(MessageManager.getString("action.help"));
1261 help.setFont(JvSwingUtils.getLabelFont());
1262 help.addActionListener(new ActionListener()
1265 public void actionPerformed(ActionEvent e)
1269 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1270 } catch (HelpSetException e1)
1272 e1.printStackTrace();
1277 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1278 cancel.setFont(JvSwingUtils.getLabelFont());
1279 cancel.addActionListener(new ActionListener()
1282 public void actionPerformed(ActionEvent e)
1284 fr.setTransparency(originalTransparency);
1285 fr.setFeatureFilters(originalFilters);
1286 updateFeatureRenderer(originalData);
1291 JButton ok = new JButton(MessageManager.getString("action.ok"));
1292 ok.setFont(JvSwingUtils.getLabelFont());
1293 ok.addActionListener(new ActionListener()
1296 public void actionPerformed(ActionEvent e)
1302 JButton loadColours = new JButton(
1303 MessageManager.getString("label.load_colours"));
1304 loadColours.setFont(JvSwingUtils.getLabelFont());
1305 loadColours.setToolTipText(
1306 MessageManager.getString("label.load_colours_tooltip"));
1307 loadColours.addActionListener(new ActionListener()
1310 public void actionPerformed(ActionEvent e)
1316 JButton saveColours = new JButton(
1317 MessageManager.getString("label.save_colours"));
1318 saveColours.setFont(JvSwingUtils.getLabelFont());
1319 saveColours.setToolTipText(
1320 MessageManager.getString("label.save_colours_tooltip"));
1321 saveColours.addActionListener(new ActionListener()
1324 public void actionPerformed(ActionEvent e)
1329 transparency.addChangeListener(new ChangeListener()
1332 public void stateChanged(ChangeEvent evt)
1334 if (!inConstruction)
1336 fr.setTransparency((100 - transparency.getValue()) / 100f);
1337 af.alignPanel.paintAlignment(true, true);
1342 summaryView = new JCheckBox(
1343 MessageManager.getString("label.summary_view"));
1346 MessageManager.getString("label.summary_view_tip"));
1347 summaryView.addActionListener(new ActionListener()
1350 public void actionPerformed(ActionEvent e)
1356 transparency.setMaximum(70);
1357 transparency.setToolTipText(
1358 MessageManager.getString("label.transparency_tip"));
1360 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1361 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1363 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1364 transbuttons.add(optimizeOrder);
1365 transbuttons.add(invert);
1366 transbuttons.add(sortByScore);
1367 transbuttons.add(sortByDens);
1368 transbuttons.add(help);
1369 JPanel transPanel = new JPanel(new GridLayout(3, 1));
1370 transPanel.add(summaryView);
1371 transPanel.add(new JLabel(" Colour transparency" + ":"));
1372 transPanel.add(transparency);
1373 lowerPanel.add(transPanel);
1374 lowerPanel.add(transbuttons);
1376 JPanel buttonPanel = new JPanel();
1377 buttonPanel.add(ok);
1378 buttonPanel.add(cancel);
1379 buttonPanel.add(loadColours);
1380 buttonPanel.add(saveColours);
1381 bigPanel.add(scrollPane, BorderLayout.CENTER);
1382 settingsPane.add(bigPanel, BorderLayout.CENTER);
1383 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1384 this.add(settingsPane);
1388 * Reorders features by 'dragging' selectedRow to 'newRow'
1392 protected void dragRow(int newRow)
1394 if (summaryView.isSelected())
1396 // no drag while in summary view
1400 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1403 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1404 * this could be more than one row away for a very fast drag action
1405 * so just swap it with adjacent rows until we get it there
1407 Object[][] data = ((FeatureTableModel) table.getModel())
1409 int direction = newRow < selectedRow ? -1 : 1;
1410 for (int i = selectedRow; i != newRow; i += direction)
1412 Object[] temp = data[i];
1413 data[i] = data[i + direction];
1414 data[i + direction] = temp;
1416 updateFeatureRenderer(data);
1418 selectedRow = newRow;
1422 protected void refreshTable()
1424 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1425 for (Object[] row : data)
1427 String type = (String) row[TYPE_COLUMN];
1428 FeatureColourI colour = fr.getFeatureColours().get(type);
1429 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1432 filter = new FeatureMatcherSet();
1434 row[COLOUR_COLUMN] = colour;
1435 row[FILTER_COLUMN] = filter;
1441 * Answers a suitable tooltip to show on the colour cell of the table
1445 * if true include 'click to edit' and similar text
1448 public static String getColorTooltip(FeatureColourI fcol,
1455 if (fcol.isSimpleColour())
1457 return withHint ? BASE_TOOLTIP : null;
1459 String description = fcol.getDescription();
1460 description = description.replaceAll("<", "<");
1461 description = description.replaceAll(">", ">");
1462 StringBuilder tt = new StringBuilder(description);
1465 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1467 return JvSwingUtils.wrapTooltip(true, tt.toString());
1470 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1473 boolean thr = false;
1474 StringBuilder tx = new StringBuilder();
1476 if (gcol.isColourByAttribute())
1478 tx.append(FeatureMatcher
1479 .toAttributeDisplayName(gcol.getAttributeName()));
1481 else if (!gcol.isColourByLabel())
1483 tx.append(MessageManager.getString("label.score"));
1486 if (gcol.isAboveThreshold())
1491 if (gcol.isBelowThreshold())
1496 if (gcol.isColourByLabel())
1502 if (!gcol.isColourByAttribute())
1510 Color newColor = gcol.getMaxColour();
1511 comp.setBackground(newColor);
1512 // System.err.println("Width is " + w / 2);
1513 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1514 comp.setIcon(ficon);
1515 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1516 // + newColor.getGreen() + ", " + newColor.getBlue()
1517 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1518 // + ", " + minCol.getBlue() + ")");
1520 comp.setHorizontalAlignment(SwingConstants.CENTER);
1521 comp.setText(tx.toString());
1524 // ///////////////////////////////////////////////////////////////////////
1525 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1526 // ///////////////////////////////////////////////////////////////////////
1527 class FeatureTableModel extends AbstractTableModel
1529 private String[] columnNames = {
1530 MessageManager.getString("label.feature_type"),
1531 MessageManager.getString("action.colour"),
1532 MessageManager.getString("label.configuration"),
1533 MessageManager.getString("label.show") };
1535 private Object[][] data;
1537 FeatureTableModel(Object[][] data)
1542 public Object[][] getData()
1547 public void setData(Object[][] data)
1553 public int getColumnCount()
1555 return columnNames.length;
1558 public Object[] getRow(int row)
1564 public int getRowCount()
1570 public String getColumnName(int col)
1572 return columnNames[col];
1576 public Object getValueAt(int row, int col)
1578 return data[row][col];
1582 * Answers the class of column c of the table
1585 public Class<?> getColumnClass(int c)
1590 return String.class;
1592 return FeatureColour.class;
1594 return FeatureMatcherSet.class;
1596 return Boolean.class;
1601 * Answers true for all columns except Feature Type
1604 public boolean isCellEditable(int row, int col)
1606 return col != TYPE_COLUMN;
1610 * Sets the value in the model for a given row and column. If Visibility
1611 * (Show/Hide) is being set, and the table is in Summary View, then it is
1612 * set also on any sub-types of the row's feature type.
1615 public void setValueAt(Object value, int row, int col)
1617 data[row][col] = value;
1618 fireTableCellUpdated(row, col);
1619 if (summaryView.isSelected() && col == SHOW_COLUMN)
1621 setSubtypesVisibility(row, (Boolean) value);
1623 updateFeatureRenderer(data);
1627 * Sets the visibility of any feature types which are sub-types of the type
1628 * in the given row of the table
1633 protected void setSubtypesVisibility(int row, Boolean value)
1635 String type = (String) data[row][TYPE_COLUMN];
1636 OntologyI so = SequenceOntologyFactory.getInstance();
1638 for (int r = 0; r < data.length; r++)
1642 String type2 = (String) data[r][TYPE_COLUMN];
1643 if (so.isA(type2, type))
1645 data[r][SHOW_COLUMN] = value;
1646 fireTableCellUpdated(r, SHOW_COLUMN);
1653 class ColorRenderer extends JLabel implements TableCellRenderer
1655 Border unselectedBorder = null;
1657 Border selectedBorder = null;
1659 public ColorRenderer()
1661 setOpaque(true); // MUST do this for background to show up.
1662 setHorizontalTextPosition(SwingConstants.CENTER);
1663 setVerticalTextPosition(SwingConstants.CENTER);
1667 public Component getTableCellRendererComponent(JTable tbl, Object color,
1668 boolean isSelected, boolean hasFocus, int row, int column)
1670 FeatureColourI cellColour = (FeatureColourI) color;
1672 setBackground(tbl.getBackground());
1673 if (!cellColour.isSimpleColour())
1675 Rectangle cr = tbl.getCellRect(row, column, false);
1676 FeatureSettings.renderGraduatedColor(this, cellColour,
1677 (int) cr.getWidth(), (int) cr.getHeight());
1683 setBackground(cellColour.getColour());
1687 if (selectedBorder == null)
1689 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1690 tbl.getSelectionBackground());
1692 setBorder(selectedBorder);
1696 if (unselectedBorder == null)
1698 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1699 tbl.getBackground());
1701 setBorder(unselectedBorder);
1708 class FilterRenderer extends JLabel implements TableCellRenderer
1710 javax.swing.border.Border unselectedBorder = null;
1712 javax.swing.border.Border selectedBorder = null;
1714 public FilterRenderer()
1716 setOpaque(true); // MUST do this for background to show up.
1717 setHorizontalTextPosition(SwingConstants.CENTER);
1718 setVerticalTextPosition(SwingConstants.CENTER);
1722 public Component getTableCellRendererComponent(JTable tbl,
1723 Object filter, boolean isSelected, boolean hasFocus, int row,
1726 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1728 String asText = theFilter.toString();
1729 setBackground(tbl.getBackground());
1730 this.setText(asText);
1735 if (selectedBorder == null)
1737 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1738 tbl.getSelectionBackground());
1740 setBorder(selectedBorder);
1744 if (unselectedBorder == null)
1746 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1747 tbl.getBackground());
1749 setBorder(unselectedBorder);
1757 * update comp using rendering settings from gcol
1762 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1764 int w = comp.getWidth(), h = comp.getHeight();
1767 w = (int) comp.getPreferredSize().getWidth();
1768 h = (int) comp.getPreferredSize().getHeight();
1775 renderGraduatedColor(comp, gcol, w, h);
1778 class ColorEditor extends AbstractCellEditor
1779 implements TableCellEditor, ActionListener
1783 FeatureColourI currentColor;
1785 FeatureTypeSettings chooser;
1789 JButton colourButton;
1791 JColorChooser colorChooser;
1795 protected static final String EDIT = "edit";
1797 int rowSelected = 0;
1799 public ColorEditor(FeatureSettings me)
1802 // Set up the editor (from the table's point of view),
1803 // which is a button.
1804 // This button brings up the color chooser dialog,
1805 // which is the editor from the user's point of view.
1806 colourButton = new JButton();
1807 colourButton.setActionCommand(EDIT);
1808 colourButton.addActionListener(this);
1809 colourButton.setBorderPainted(false);
1810 // Set up the dialog that the button brings up.
1811 colorChooser = new JColorChooser();
1812 dialog = JColorChooser.createDialog(colourButton,
1813 MessageManager.getString("label.select_colour"), true, // modal
1814 colorChooser, this, // OK button handler
1815 null); // no CANCEL button handler
1819 * Handles events from the editor button and from the dialog's OK button.
1822 public void actionPerformed(ActionEvent e)
1824 // todo test e.getSource() instead here
1825 if (EDIT.equals(e.getActionCommand()))
1827 // The user has clicked the cell, so
1828 // bring up the dialog.
1829 if (currentColor.isSimpleColour())
1831 // bring up simple color chooser
1832 colourButton.setBackground(currentColor.getColour());
1833 colorChooser.setColor(currentColor.getColour());
1834 dialog.setVisible(true);
1838 // bring up graduated chooser.
1839 chooser = new FeatureTypeSettings(me.fr, type);
1840 chooser.setRequestFocusEnabled(true);
1841 chooser.requestFocus();
1842 chooser.addActionListener(this);
1843 // Make the renderer reappear.
1844 fireEditingStopped();
1849 if (currentColor.isSimpleColour())
1852 * read off colour picked in colour chooser after OK pressed
1854 currentColor = new FeatureColour(colorChooser.getColor());
1855 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1860 * after OK in variable colour dialog, any changes to colour
1861 * (or filters!) are already set in FeatureRenderer, so just
1862 * update table data without triggering updateFeatureRenderer
1866 fireEditingStopped();
1867 me.table.validate();
1871 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1873 public Object getCellEditorValue()
1875 return currentColor;
1878 // Implement the one method defined by TableCellEditor.
1880 public Component getTableCellEditorComponent(JTable theTable, Object value,
1881 boolean isSelected, int row, int column)
1883 currentColor = (FeatureColourI) value;
1884 this.rowSelected = row;
1885 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1886 colourButton.setOpaque(true);
1887 colourButton.setBackground(me.getBackground());
1888 if (!currentColor.isSimpleColour())
1890 JLabel btn = new JLabel();
1891 btn.setSize(colourButton.getSize());
1892 FeatureSettings.renderGraduatedColor(btn, currentColor);
1893 colourButton.setBackground(btn.getBackground());
1894 colourButton.setIcon(btn.getIcon());
1895 colourButton.setText(btn.getText());
1899 colourButton.setText("");
1900 colourButton.setIcon(null);
1901 colourButton.setBackground(currentColor.getColour());
1903 return colourButton;
1908 * The cell editor for the Filter column. It displays the text of any filters
1909 * for the feature type in that row (in full as a tooltip, possible abbreviated
1910 * as display text). On click in the cell, opens the Feature Display Settings
1911 * dialog at the Filters tab.
1913 class FilterEditor extends AbstractCellEditor
1914 implements TableCellEditor, ActionListener
1918 FeatureMatcherSetI currentFilter;
1924 JButton filterButton;
1926 protected static final String EDIT = "edit";
1928 int rowSelected = 0;
1930 public FilterEditor(FeatureSettings me)
1933 filterButton = new JButton();
1934 filterButton.setActionCommand(EDIT);
1935 filterButton.addActionListener(this);
1936 filterButton.setBorderPainted(false);
1940 * Handles events from the editor button
1943 public void actionPerformed(ActionEvent e)
1945 if (filterButton == e.getSource())
1947 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1948 chooser.addActionListener(this);
1949 chooser.setRequestFocusEnabled(true);
1950 chooser.requestFocus();
1951 if (lastLocation != null)
1953 // todo open at its last position on screen
1954 chooser.setBounds(lastLocation.x, lastLocation.y,
1955 chooser.getWidth(), chooser.getHeight());
1958 fireEditingStopped();
1960 else if (e.getSource() instanceof Component)
1963 * after OK in variable colour dialog, any changes to filter
1964 * (or colours!) are already set in FeatureRenderer, so just
1965 * update table data without triggering updateFeatureRenderer
1968 fireEditingStopped();
1969 me.table.validate();
1974 public Object getCellEditorValue()
1976 return currentFilter;
1980 public Component getTableCellEditorComponent(JTable theTable, Object value,
1981 boolean isSelected, int row, int column)
1983 currentFilter = (FeatureMatcherSetI) value;
1984 this.rowSelected = row;
1985 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1986 filterButton.setOpaque(true);
1987 filterButton.setBackground(me.getBackground());
1988 filterButton.setText(currentFilter.toString());
1989 filterButton.setToolTipText(currentFilter.toString());
1990 filterButton.setIcon(null);
1991 return filterButton;
1996 class FeatureIcon implements Icon
1998 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2000 FeatureColourI gcol;
2004 boolean midspace = false;
2006 int width = 50, height = 20;
2008 int s1, e1; // start and end of midpoint band for thresholded symbol
2010 Color mpcolour = Color.white;
2012 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2032 public int getIconWidth()
2038 public int getIconHeight()
2044 public void paintIcon(Component c, Graphics g, int x, int y)
2047 if (gcol.isColourByLabel())
2050 g.fillRect(0, 0, width, height);
2051 // need an icon here.
2052 g.setColor(gcol.getMaxColour());
2054 g.setFont(VERDANA_9);
2056 // g.setFont(g.getFont().deriveFont(
2057 // AffineTransform.getScaleInstance(
2058 // width/g.getFontMetrics().stringWidth("Label"),
2059 // height/g.getFontMetrics().getHeight())));
2061 g.drawString(MessageManager.getString("label.label"), 0, 0);
2066 Color minCol = gcol.getMinColour();
2068 g.fillRect(0, 0, s1, height);
2071 g.setColor(Color.white);
2072 g.fillRect(s1, 0, e1 - s1, height);
2074 g.setColor(gcol.getMaxColour());
2075 g.fillRect(0, e1, width - e1, height);