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.bin.Jalview;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.SequenceI;
28 import jalview.datamodel.features.FeatureMatcher;
29 import jalview.datamodel.features.FeatureMatcherI;
30 import jalview.datamodel.features.FeatureMatcherSet;
31 import jalview.datamodel.features.FeatureMatcherSetI;
32 import jalview.gui.Help.HelpId;
33 import jalview.gui.JalviewColourChooser.ColourChooserListener;
34 import jalview.io.JalviewFileChooser;
35 import jalview.io.JalviewFileView;
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.Arrays;
70 import java.util.Comparator;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.Hashtable;
74 import java.util.Iterator;
75 import java.util.List;
79 import javax.help.HelpSetException;
80 import javax.swing.AbstractCellEditor;
81 import javax.swing.BorderFactory;
82 import javax.swing.Icon;
83 import javax.swing.JButton;
84 import javax.swing.JCheckBox;
85 import javax.swing.JCheckBoxMenuItem;
86 import javax.swing.JInternalFrame;
87 import javax.swing.JLabel;
88 import javax.swing.JLayeredPane;
89 import javax.swing.JMenuItem;
90 import javax.swing.JPanel;
91 import javax.swing.JPopupMenu;
92 import javax.swing.JScrollPane;
93 import javax.swing.JSlider;
94 import javax.swing.JTable;
95 import javax.swing.ListSelectionModel;
96 import javax.swing.SwingConstants;
97 import javax.swing.ToolTipManager;
98 import javax.swing.border.Border;
99 import javax.swing.event.ChangeEvent;
100 import javax.swing.event.ChangeListener;
101 import javax.swing.table.AbstractTableModel;
102 import javax.swing.table.TableCellEditor;
103 import javax.swing.table.TableCellRenderer;
104 import javax.swing.table.TableColumn;
105 import javax.xml.bind.JAXBContext;
106 import javax.xml.bind.JAXBElement;
107 import javax.xml.bind.Marshaller;
108 import javax.xml.stream.XMLInputFactory;
109 import javax.xml.stream.XMLStreamReader;
111 public class FeatureSettings extends JPanel
112 implements FeatureSettingsControllerI
114 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
115 .getString("label.sequence_feature_colours");
118 * column indices of fields in Feature Settings table
120 static final int TYPE_COLUMN = 0;
122 static final int COLOUR_COLUMN = 1;
124 static final int FILTER_COLUMN = 2;
126 static final int SHOW_COLUMN = 3;
128 private static final int COLUMN_COUNT = 4;
130 private static final int MIN_WIDTH = 400;
132 private static final int MIN_HEIGHT = 400;
134 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
136 final FeatureRenderer fr;
138 public final AlignFrame af;
141 * 'original' fields hold settings to restore on Cancel
143 Object[][] originalData;
145 float originalTransparency;
147 Map<String, FeatureMatcherSetI> originalFilters;
149 final JInternalFrame frame;
151 JScrollPane scrollPane = new JScrollPane();
157 JSlider transparency = new JSlider();
160 * when true, constructor is still executing - so ignore UI events
162 protected volatile boolean inConstruction = true;
164 int selectedRow = -1;
166 JButton fetchDAS = new JButton();
168 JButton saveDAS = new JButton();
170 JButton cancelDAS = new JButton();
172 boolean resettingTable = false;
175 * true when Feature Settings are updating from feature renderer
177 boolean handlingUpdate = false;
180 * holds {featureCount, totalExtent} for each feature type
182 Map<String, float[]> typeWidth = null;
189 public FeatureSettings(AlignFrame alignFrame)
191 this.af = alignFrame;
192 fr = af.getFeatureRenderer();
194 // save transparency for restore on Cancel
195 originalTransparency = fr.getTransparency();
196 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
197 transparency.setMaximum(100 - originalTransparencyAsPercent);
199 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
204 } catch (Exception ex)
206 ex.printStackTrace();
213 // public void repaint() {
214 // System.out.println("FS repaint");
220 // public void repaint(long tm, int x, int y, int width, int height) {
221 // System.out.println("FS repaint " + x + " " + y + " " + width + " " + height);
222 // super.repaint(tm, x, y, width, height);
226 // public void invalidate() {
228 // System.out.println("FS invalidating ");
229 // super.invalidate();
233 // public void validate() {
234 // System.out.println("FS validating " + isValid());
239 public String getToolTipText(MouseEvent e)
242 int column = table.columnAtPoint(e.getPoint());
243 int row = table.rowAtPoint(e.getPoint());
248 tip = JvSwingUtils.wrapTooltip(true, MessageManager
249 .getString("label.feature_settings_click_drag"));
252 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
254 tip = getColorTooltip(colour, true);
257 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
261 .getString("label.configure_feature_tooltip")
273 * Position the tooltip near the bottom edge of, and half way across, the
277 public Point getToolTipLocation(MouseEvent e)
279 Point point = e.getPoint();
280 int column = table.columnAtPoint(point);
281 int row = table.rowAtPoint(point);
282 Rectangle r = getCellRect(row, column, false);
283 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
288 // next line is needed to avoid (quiet) exceptions thrown
289 // when column ordering changes so that the above constants
291 table.getTableHeader().setReorderingAllowed(false); // BH 2018
293 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
294 ToolTipManager.sharedInstance().registerComponent(table);
296 table.setDefaultEditor(FeatureColour.class, new ColorEditor());
297 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
299 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor());
300 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
302 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
303 new ColorRenderer(), new ColorEditor());
304 table.addColumn(colourColumn);
306 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
307 new FilterRenderer(), new FilterEditor());
308 table.addColumn(filterColumn);
310 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
312 table.addMouseListener(new MouseAdapter()
315 public void mousePressed(MouseEvent evt)
317 selectedRow = table.rowAtPoint(evt.getPoint());
318 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
319 if (evt.isPopupTrigger())
321 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
322 showPopupMenu(selectedRow, type, colour, evt.getPoint());
324 else if (evt.getClickCount() == 2)
326 boolean invertSelection = evt.isAltDown();
327 boolean toggleSelection = Platform.isControlDown(evt);
328 boolean extendSelection = evt.isShiftDown();
329 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
330 invertSelection, extendSelection, toggleSelection, type);
334 // isPopupTrigger fires on mouseReleased on Windows
336 public void mouseReleased(MouseEvent evt)
338 selectedRow = table.rowAtPoint(evt.getPoint());
339 if (evt.isPopupTrigger())
341 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
342 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
343 showPopupMenu(selectedRow, type, colour, evt.getPoint());
348 table.addMouseMotionListener(new MouseMotionAdapter()
351 public void mouseDragged(MouseEvent evt)
353 int newRow = table.rowAtPoint(evt.getPoint());
354 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
357 * reposition 'selectedRow' to 'newRow' (the dragged to location)
358 * this could be more than one row away for a very fast drag action
359 * so just swap it with adjacent rows until we get it there
361 Object[][] data = ((FeatureTableModel) table.getModel())
363 int direction = newRow < selectedRow ? -1 : 1;
364 for (int i = selectedRow; i != newRow; i += direction)
366 Object[] temp = data[i];
367 data[i] = data[i + direction];
368 data[i + direction] = temp;
370 updateFeatureRenderer(data);
372 selectedRow = newRow;
376 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
377 // MessageManager.getString("label.feature_settings_click_drag")));
378 scrollPane.setViewportView(table);
380 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
382 fr.findAllFeatures(true); // display everything!
385 discoverAllFeatureData();
386 final PropertyChangeListener change;
387 final FeatureSettings fs = this;
388 fr.addPropertyChangeListener(change = new PropertyChangeListener()
391 public void propertyChange(PropertyChangeEvent evt)
393 if (!fs.resettingTable && !fs.handlingUpdate)
395 fs.handlingUpdate = true;
397 // new groups may be added with new sequence feature types only
398 fs.handlingUpdate = false;
404 frame = new JInternalFrame();
405 frame.setContentPane(this);
406 Desktop.addInternalFrame(frame,
407 MessageManager.getString("label.sequence_feature_settings"),
408 600, Platform.isAMacAndNotJS() ? 480 : 450);
409 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
411 frame.addInternalFrameListener(
412 new javax.swing.event.InternalFrameAdapter()
415 public void internalFrameClosed(
416 javax.swing.event.InternalFrameEvent evt)
418 fr.removePropertyChangeListener(change);
421 frame.setLayer(JLayeredPane.PALETTE_LAYER);
422 inConstruction = false;
426 * Constructs and shows a popup menu of possible actions on the selected row and
434 protected void showPopupMenu(final int rowSelected, final String type,
435 final Object typeCol, final Point pt)
437 JPopupMenu men = new JPopupMenu(MessageManager
438 .formatMessage("label.settings_for_param", new String[]
440 final FeatureColourI featureColour = (FeatureColourI) typeCol;
443 * menu option to select (or deselect) variable colour
445 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
446 MessageManager.getString("label.variable_colour"));
447 variableColourCB.setSelected(!featureColour.isSimpleColour());
448 men.add(variableColourCB);
451 * checkbox action listener doubles up as listener to OK
452 * from the variable colour / filters dialog
454 variableColourCB.addActionListener(new ActionListener()
457 public void actionPerformed(ActionEvent e)
459 if (e.getSource() == variableColourCB)
461 men.setVisible(true); // BH 2018 for JavaScript because this is a checkbox
462 men.setVisible(false); // BH 2018 for JavaScript because this is a checkbox
463 if (featureColour.isSimpleColour())
466 * toggle simple colour to variable colour - show dialog
468 FeatureTypeSettings fc = new FeatureTypeSettings(fr, type);
469 fc.addActionListener(this);
474 * toggle variable to simple colour - show colour chooser
476 String title = MessageManager.formatMessage("label.select_colour_for", type);
477 ColourChooserListener listener = new ColourChooserListener()
480 public void colourSelected(Color c)
482 table.setValueAt(new FeatureColour(c), rowSelected,
485 updateFeatureRenderer(
486 ((FeatureTableModel) table.getModel()).getData(),
490 JalviewColourChooser.showColourChooser(FeatureSettings.this, title,
491 featureColour.getMaxColour(), listener);
496 if (e.getSource() instanceof FeatureTypeSettings)
499 * update after OK in feature colour dialog; the updated
500 * colour will have already been set in the FeatureRenderer
502 FeatureColourI fci = fr.getFeatureColours().get(type);
503 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
504 // BH 2018 setting a table value does not invalidate it.
505 // System.out.println("FeatureSettings is valied" + table.isValid());
514 JMenuItem scr = new JMenuItem(
515 MessageManager.getString("label.sort_by_score"));
517 scr.addActionListener(new ActionListener()
521 public void actionPerformed(ActionEvent e)
523 af.avc.sortAlignmentByFeatureScore(Arrays.asList(new String[]
527 JMenuItem dens = new JMenuItem(
528 MessageManager.getString("label.sort_by_density"));
529 dens.addActionListener(new ActionListener()
533 public void actionPerformed(ActionEvent e)
535 af.avc.sortAlignmentByFeatureDensity(Arrays.asList(new String[]
541 JMenuItem selCols = new JMenuItem(
542 MessageManager.getString("label.select_columns_containing"));
543 selCols.addActionListener(new ActionListener()
546 public void actionPerformed(ActionEvent arg0)
548 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
552 JMenuItem clearCols = new JMenuItem(MessageManager
553 .getString("label.select_columns_not_containing"));
554 clearCols.addActionListener(new ActionListener()
557 public void actionPerformed(ActionEvent arg0)
559 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
563 JMenuItem hideCols = new JMenuItem(
564 MessageManager.getString("label.hide_columns_containing"));
565 hideCols.addActionListener(new ActionListener()
568 public void actionPerformed(ActionEvent arg0)
570 fr.ap.alignFrame.hideFeatureColumns(type, true);
573 JMenuItem hideOtherCols = new JMenuItem(
574 MessageManager.getString("label.hide_columns_not_containing"));
575 hideOtherCols.addActionListener(new ActionListener()
578 public void actionPerformed(ActionEvent arg0)
580 fr.ap.alignFrame.hideFeatureColumns(type, false);
586 men.add(hideOtherCols);
587 men.show(table, pt.x, pt.y);
591 synchronized public void discoverAllFeatureData()
593 Set<String> allGroups = new HashSet<>();
594 AlignmentI alignment = af.getViewport().getAlignment();
596 for (int i = 0; i < alignment.getHeight(); i++)
598 SequenceI seq = alignment.getSequenceAt(i);
599 for (String group : seq.getFeatures().getFeatureGroups(true))
601 if (group != null && !allGroups.contains(group))
603 allGroups.add(group);
604 checkGroupState(group);
615 * Synchronise gui group list and check visibility of group
618 * @return true if group is visible
620 private boolean checkGroupState(String group)
622 boolean visible = fr.checkGroupVisibility(group, true);
624 for (int g = 0; g < groupPanel.getComponentCount(); g++)
626 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
628 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
633 final String grp = group;
634 final JCheckBox check = new JCheckBox(group, visible);
635 check.setFont(new Font("Serif", Font.BOLD, 12));
636 check.setToolTipText(group);
637 check.addItemListener(new ItemListener()
640 public void itemStateChanged(ItemEvent evt)
642 fr.setGroupVisibility(check.getText(), check.isSelected());
643 resetTable(new String[] { grp });
644 af.alignPanel.paintAlignment(true, true);
647 groupPanel.add(check);
651 synchronized void resetTable(String[] groupChanged)
657 resettingTable = true;
658 typeWidth = new Hashtable<>();
659 // TODO: change avWidth calculation to 'per-sequence' average and use long
662 Set<String> displayableTypes = new HashSet<>();
663 Set<String> foundGroups = new HashSet<>();
666 * determine which feature types may be visible depending on
667 * which groups are selected, and recompute average width data
669 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
672 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
675 * get the sequence's groups for positional features
676 * and keep track of which groups are visible
678 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
679 Set<String> visibleGroups = new HashSet<>();
680 for (String group : groups)
682 if (group == null || checkGroupState(group))
684 visibleGroups.add(group);
687 foundGroups.addAll(groups);
690 * get distinct feature types for visible groups
691 * record distinct visible types, and their count and total length
693 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
694 visibleGroups.toArray(new String[visibleGroups.size()]));
695 for (String type : types)
697 displayableTypes.add(type);
698 float[] avWidth = typeWidth.get(type);
701 avWidth = new float[2];
702 typeWidth.put(type, avWidth);
704 // todo this could include features with a non-visible group
705 // - do we greatly care?
706 // todo should we include non-displayable features here, and only
707 // update when features are added?
708 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
709 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
713 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
716 if (fr.hasRenderOrder())
720 fr.findAllFeatures(groupChanged != null); // prod to update
721 // colourschemes. but don't
723 // First add the checks in the previous render order,
724 // in case the window has been closed and reopened
726 List<String> frl = fr.getRenderOrder();
727 for (int ro = frl.size() - 1; ro > -1; ro--)
729 String type = frl.get(ro);
731 if (!displayableTypes.contains(type))
736 data[dataIndex][TYPE_COLUMN] = type;
737 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
738 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
739 data[dataIndex][FILTER_COLUMN] = featureFilter == null
740 ? new FeatureMatcherSet()
742 data[dataIndex][SHOW_COLUMN] = new Boolean(
743 af.getViewport().getFeaturesDisplayed().isVisible(type));
745 displayableTypes.remove(type);
750 * process any extra features belonging only to
751 * a group which was just selected
753 while (!displayableTypes.isEmpty())
755 String type = displayableTypes.iterator().next();
756 data[dataIndex][TYPE_COLUMN] = type;
758 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
759 if (data[dataIndex][COLOUR_COLUMN] == null)
761 // "Colour has been updated in another view!!"
762 fr.clearRenderOrder();
765 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
766 data[dataIndex][FILTER_COLUMN] = featureFilter == null
767 ? new FeatureMatcherSet()
769 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
771 displayableTypes.remove(type);
774 if (originalData == null)
776 originalData = new Object[data.length][COLUMN_COUNT];
777 for (int i = 0; i < data.length; i++)
779 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
784 updateOriginalData(data);
787 table.setModel(new FeatureTableModel(data));
788 table.getColumnModel().getColumn(0).setPreferredWidth(200);
790 groupPanel.setLayout(
791 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
792 pruneGroups(foundGroups);
793 groupPanel.validate();
795 updateFeatureRenderer(data, groupChanged != null);
796 resettingTable = false;
800 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
801 * have been made outwith this dialog
803 * <li>a new feature type added (and made visible)</li>
804 * <li>a feature colour changed (in the Amend Features dialog)</li>
809 protected void updateOriginalData(Object[][] foundData)
811 // todo LinkedHashMap instead of Object[][] would be nice
813 Object[][] currentData = ((FeatureTableModel) table.getModel())
815 for (Object[] row : foundData)
817 String type = (String) row[TYPE_COLUMN];
818 boolean found = false;
819 for (Object[] current : currentData)
821 if (type.equals(current[TYPE_COLUMN]))
825 * currently dependent on object equality here;
826 * really need an equals method on FeatureColour
828 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
831 * feature colour has changed externally - update originalData
833 for (Object[] original : originalData)
835 if (type.equals(original[TYPE_COLUMN]))
837 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
848 * new feature detected - add to original data (on top)
850 Object[][] newData = new Object[originalData.length
852 for (int i = 0; i < originalData.length; i++)
854 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
858 originalData = newData;
864 * Remove from the groups panel any checkboxes for groups that are not in the
865 * foundGroups set. This enables removing a group from the display when the last
866 * feature in that group is deleted.
870 protected void pruneGroups(Set<String> foundGroups)
872 for (int g = 0; g < groupPanel.getComponentCount(); g++)
874 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
875 if (!foundGroups.contains(checkbox.getText()))
877 groupPanel.remove(checkbox);
883 * reorder data based on the featureRenderers global priority list.
887 private void ensureOrder(Object[][] data)
889 boolean sort = false;
890 float[] order = new float[data.length];
891 for (int i = 0; i < order.length; i++)
893 order[i] = fr.getOrder(data[i][0].toString());
896 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
900 sort = sort || order[i - 1] > order[i];
905 jalview.util.QuickSort.sort(order, data);
910 * Offers a file chooser dialog, and then loads the feature colours and
911 * filters from file in XML format and unmarshals to Jalview feature settings
915 JalviewFileChooser chooser = new JalviewFileChooser("fc",
916 SEQUENCE_FEATURE_COLOURS);
917 chooser.setFileView(new JalviewFileView());
918 chooser.setDialogTitle(
919 MessageManager.getString("label.load_feature_colours"));
920 chooser.setToolTipText(MessageManager.getString("action.load"));
921 chooser.setResponseHandler(0, new Runnable()
926 File file = chooser.getSelectedFile();
930 chooser.showOpenDialog(this);
934 * Loads feature colours and filters from XML stored in the given file
942 InputStreamReader in = new InputStreamReader(
943 new FileInputStream(file), "UTF-8");
945 JAXBContext jc = JAXBContext
946 .newInstance("jalview.xml.binding.jalview");
947 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
948 XMLStreamReader streamReader = XMLInputFactory.newInstance()
949 .createXMLStreamReader(in);
950 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
951 JalviewUserColours.class);
952 JalviewUserColours jucs = jbe.getValue();
954 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
957 * load feature colours
959 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
961 Colour newcol = jucs.getColour().get(i);
962 FeatureColourI colour = jalview.project.Jalview2XML
963 .parseColour(newcol);
964 fr.setColour(newcol.getName(), colour);
965 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
969 * load feature filters; loaded filters will replace any that are
970 * currently defined, other defined filters are left unchanged
972 for (int i = 0; i < jucs.getFilter().size(); i++)
974 Filter filterModel = jucs.getFilter().get(i);
975 String featureType = filterModel.getFeatureType();
976 FeatureMatcherSetI filter = jalview.project.Jalview2XML
977 .parseFilter(featureType, filterModel.getMatcherSet());
978 if (!filter.isEmpty())
980 fr.setFeatureFilter(featureType, filter);
985 * update feature settings table
990 Object[][] data = ((FeatureTableModel) table.getModel())
993 updateFeatureRenderer(data, false);
996 } catch (Exception ex)
998 System.out.println("Error loading User Colour File\n" + ex);
1003 * Offers a file chooser dialog, and then saves the current feature colours
1004 * and any filters to the selected file in XML format
1008 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1009 SEQUENCE_FEATURE_COLOURS);
1010 chooser.setFileView(new JalviewFileView());
1011 chooser.setDialogTitle(
1012 MessageManager.getString("label.save_feature_colours"));
1013 chooser.setToolTipText(MessageManager.getString("action.save"));
1014 int option = chooser.showSaveDialog(this);
1015 if (option == JalviewFileChooser.APPROVE_OPTION)
1017 File file = chooser.getSelectedFile();
1023 * Saves feature colours and filters to the given file
1027 void save(File file)
1029 JalviewUserColours ucs = new JalviewUserColours();
1030 ucs.setSchemeName("Sequence Features");
1033 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1034 new FileOutputStream(file), "UTF-8"));
1037 * sort feature types by colour order, from 0 (highest)
1040 Set<String> fr_colours = fr.getAllFeatureColours();
1041 String[] sortedTypes = fr_colours
1042 .toArray(new String[fr_colours.size()]);
1043 Arrays.sort(sortedTypes, new Comparator<String>()
1046 public int compare(String type1, String type2)
1048 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1053 * save feature colours
1055 for (String featureType : sortedTypes)
1057 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1058 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1060 ucs.getColour().add(col);
1064 * save any feature filters
1066 for (String featureType : sortedTypes)
1068 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1069 if (filter != null && !filter.isEmpty())
1071 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1072 FeatureMatcherI firstMatcher = iterator.next();
1073 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1074 .marshalFilter(firstMatcher, iterator,
1076 Filter filterModel = new Filter();
1077 filterModel.setFeatureType(featureType);
1078 filterModel.setMatcherSet(ms);
1079 ucs.getFilter().add(filterModel);
1082 JAXBContext jaxbContext = JAXBContext
1083 .newInstance(JalviewUserColours.class);
1084 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1085 jaxbMarshaller.marshal(
1086 new ObjectFactory().createJalviewUserColours(ucs), out);
1088 // jaxbMarshaller.marshal(object, pout);
1089 // marshaller.marshal(object);
1092 // ucs.marshal(out);
1094 } catch (Exception ex)
1096 ex.printStackTrace();
1100 public void invertSelection()
1102 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1103 for (int i = 0; i < data.length; i++)
1105 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1107 updateFeatureRenderer(data, true);
1111 public void orderByAvWidth()
1113 if (table == null || table.getModel() == null)
1117 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1118 float[] width = new float[data.length];
1122 for (int i = 0; i < data.length; i++)
1124 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1127 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1128 // weight - but have to make per
1129 // sequence, too (awidth[2])
1130 // if (width[i]==1) // hack to distinguish single width sequences.
1141 boolean sort = false;
1142 for (int i = 0; i < width.length; i++)
1144 // awidth = (float[]) typeWidth.get(data[i][0]);
1147 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1150 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1156 width[i] /= max; // normalize
1157 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1161 sort = sort || width[i - 1] > width[i];
1166 jalview.util.QuickSort.sort(width, data);
1167 // update global priority order
1170 updateFeatureRenderer(data, false);
1178 frame.setClosed(true);
1179 } catch (Exception exe)
1185 public void updateFeatureRenderer(Object[][] data)
1187 updateFeatureRenderer(data, true);
1191 * Update the priority order of features; only repaint if this changed the order
1192 * of visible features
1197 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1199 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1201 if (fr.setFeaturePriority(rowData, visibleNew))
1203 af.alignPanel.paintAlignment(true, true);
1208 * Converts table data into an array of data beans
1210 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1212 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1213 for (int i = 0; i < data.length; i++)
1215 String type = (String) data[i][TYPE_COLUMN];
1216 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1217 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1218 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1219 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1225 private void jbInit() throws Exception
1227 this.setLayout(new BorderLayout());
1229 JPanel settingsPane = new JPanel();
1230 settingsPane.setLayout(new BorderLayout());
1232 JPanel bigPanel = new JPanel();
1233 bigPanel.setLayout(new BorderLayout());
1235 groupPanel = new JPanel();
1236 bigPanel.add(groupPanel, BorderLayout.NORTH);
1238 JButton invert = new JButton(
1239 MessageManager.getString("label.invert_selection"));
1240 invert.setFont(JvSwingUtils.getLabelFont());
1241 invert.addActionListener(new ActionListener()
1244 public void actionPerformed(ActionEvent e)
1250 JButton optimizeOrder = new JButton(
1251 MessageManager.getString("label.optimise_order"));
1252 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1253 optimizeOrder.addActionListener(new ActionListener()
1256 public void actionPerformed(ActionEvent e)
1262 JButton sortByScore = new JButton(
1263 MessageManager.getString("label.seq_sort_by_score"));
1264 sortByScore.setFont(JvSwingUtils.getLabelFont());
1265 sortByScore.addActionListener(new ActionListener()
1268 public void actionPerformed(ActionEvent e)
1270 af.avc.sortAlignmentByFeatureScore(null);
1273 JButton sortByDens = new JButton(
1274 MessageManager.getString("label.sequence_sort_by_density"));
1275 sortByDens.setFont(JvSwingUtils.getLabelFont());
1276 sortByDens.addActionListener(new ActionListener()
1279 public void actionPerformed(ActionEvent e)
1281 af.avc.sortAlignmentByFeatureDensity(null);
1285 JButton help = new JButton(MessageManager.getString("action.help"));
1286 help.setFont(JvSwingUtils.getLabelFont());
1287 help.addActionListener(new ActionListener()
1290 public void actionPerformed(ActionEvent e)
1294 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1295 } catch (HelpSetException e1)
1297 e1.printStackTrace();
1301 help.setFont(JvSwingUtils.getLabelFont());
1302 help.setText(MessageManager.getString("action.help"));
1303 help.addActionListener(new ActionListener()
1306 public void actionPerformed(ActionEvent e)
1310 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1311 } catch (HelpSetException e1)
1313 e1.printStackTrace();
1318 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1319 cancel.setFont(JvSwingUtils.getLabelFont());
1320 cancel.addActionListener(new ActionListener()
1323 public void actionPerformed(ActionEvent e)
1325 fr.setTransparency(originalTransparency);
1326 fr.setFeatureFilters(originalFilters);
1327 updateFeatureRenderer(originalData);
1332 JButton ok = new JButton(MessageManager.getString("action.ok"));
1333 ok.setFont(JvSwingUtils.getLabelFont());
1334 ok.addActionListener(new ActionListener()
1337 public void actionPerformed(ActionEvent e)
1343 JButton loadColours = new JButton(
1344 MessageManager.getString("label.load_colours"));
1345 loadColours.setFont(JvSwingUtils.getLabelFont());
1346 loadColours.setToolTipText(
1347 MessageManager.getString("label.load_colours_tooltip"));
1348 loadColours.addActionListener(new ActionListener()
1351 public void actionPerformed(ActionEvent e)
1357 JButton saveColours = new JButton(
1358 MessageManager.getString("label.save_colours"));
1359 saveColours.setFont(JvSwingUtils.getLabelFont());
1360 saveColours.setToolTipText(
1361 MessageManager.getString("label.save_colours_tooltip"));
1362 saveColours.addActionListener(new ActionListener()
1365 public void actionPerformed(ActionEvent e)
1370 transparency.addChangeListener(new ChangeListener()
1373 public void stateChanged(ChangeEvent evt)
1375 if (!inConstruction)
1377 fr.setTransparency((100 - transparency.getValue()) / 100f);
1378 af.alignPanel.paintAlignment(true, true);
1383 transparency.setMaximum(70);
1384 transparency.setToolTipText(
1385 MessageManager.getString("label.transparency_tip"));
1387 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1388 bigPanel.add(transPanel, BorderLayout.SOUTH);
1390 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1391 transbuttons.add(optimizeOrder);
1392 transbuttons.add(invert);
1393 transbuttons.add(sortByScore);
1394 transbuttons.add(sortByDens);
1395 transbuttons.add(help);
1396 transPanel.add(transparency);
1397 transPanel.add(transbuttons);
1399 JPanel buttonPanel = new JPanel();
1400 buttonPanel.add(ok);
1401 buttonPanel.add(cancel);
1402 buttonPanel.add(loadColours);
1403 buttonPanel.add(saveColours);
1404 bigPanel.add(scrollPane, BorderLayout.CENTER);
1405 settingsPane.add(bigPanel, BorderLayout.CENTER);
1406 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1407 this.add(settingsPane);
1411 * Answers a suitable tooltip to show on the colour cell of the table
1415 * if true include 'click to edit' and similar text
1418 public static String getColorTooltip(FeatureColourI fcol,
1425 if (fcol.isSimpleColour())
1427 return withHint ? BASE_TOOLTIP : null;
1429 String description = fcol.getDescription();
1430 description = description.replaceAll("<", "<");
1431 description = description.replaceAll(">", ">");
1432 StringBuilder tt = new StringBuilder(description);
1435 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1437 return JvSwingUtils.wrapTooltip(true, tt.toString());
1440 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1443 boolean thr = false;
1444 StringBuilder tx = new StringBuilder();
1446 if (gcol.isColourByAttribute())
1448 tx.append(FeatureMatcher
1449 .toAttributeDisplayName(gcol.getAttributeName()));
1451 else if (!gcol.isColourByLabel())
1453 tx.append(MessageManager.getString("label.score"));
1456 if (gcol.isAboveThreshold())
1461 if (gcol.isBelowThreshold())
1466 if (gcol.isColourByLabel())
1472 if (!gcol.isColourByAttribute())
1480 Color newColor = gcol.getMaxColour();
1481 comp.setBackground(newColor);
1482 // System.err.println("Width is " + w / 2);
1483 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1484 comp.setIcon(ficon);
1485 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1486 // + newColor.getGreen() + ", " + newColor.getBlue()
1487 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1488 // + ", " + minCol.getBlue() + ")");
1490 comp.setHorizontalAlignment(SwingConstants.CENTER);
1491 comp.setText(tx.toString());
1494 // ///////////////////////////////////////////////////////////////////////
1495 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1496 // ///////////////////////////////////////////////////////////////////////
1497 class FeatureTableModel extends AbstractTableModel
1499 private String[] columnNames = {
1500 MessageManager.getString("label.feature_type"),
1501 MessageManager.getString("action.colour"),
1502 MessageManager.getString("label.configuration"),
1503 MessageManager.getString("label.show") };
1505 private Object[][] data;
1507 FeatureTableModel(Object[][] data)
1512 public Object[][] getData()
1517 public void setData(Object[][] data)
1523 public int getColumnCount()
1525 return columnNames.length;
1528 public Object[] getRow(int row)
1534 public int getRowCount()
1540 public String getColumnName(int col)
1542 return columnNames[col];
1546 public Object getValueAt(int row, int col)
1548 return data[row][col];
1552 * Answers the class of the object in column c of the first row of the table
1555 public Class<?> getColumnClass(int c)
1557 Object v = getValueAt(0, c);
1558 return v == null ? null : v.getClass();
1562 public boolean isCellEditable(int row, int col)
1564 return col == 0 ? false : true;
1568 public void setValueAt(Object value, int row, int col)
1570 data[row][col] = value;
1571 fireTableCellUpdated(row, col);
1572 updateFeatureRenderer(data);
1577 class ColorRenderer extends JLabel implements TableCellRenderer
1579 Border unselectedBorder = null;
1581 Border selectedBorder = null;
1583 public ColorRenderer()
1585 setOpaque(true); // MUST do this for background to show up.
1586 setHorizontalTextPosition(SwingConstants.CENTER);
1587 setVerticalTextPosition(SwingConstants.CENTER);
1591 public Component getTableCellRendererComponent(JTable tbl, Object color,
1592 boolean isSelected, boolean hasFocus, int row, int column)
1594 FeatureColourI cellColour = (FeatureColourI) color;
1596 setBackground(tbl.getBackground());
1597 if (!cellColour.isSimpleColour())
1599 Rectangle cr = tbl.getCellRect(row, column, false);
1600 FeatureSettings.renderGraduatedColor(this, cellColour,
1601 (int) cr.getWidth(), (int) cr.getHeight());
1607 setBackground(cellColour.getColour());
1611 if (selectedBorder == null)
1613 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1614 tbl.getSelectionBackground());
1616 setBorder(selectedBorder);
1620 if (unselectedBorder == null)
1622 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1623 tbl.getBackground());
1625 setBorder(unselectedBorder);
1632 class FilterRenderer extends JLabel implements TableCellRenderer
1634 javax.swing.border.Border unselectedBorder = null;
1636 javax.swing.border.Border selectedBorder = null;
1638 public FilterRenderer()
1640 setOpaque(true); // MUST do this for background to show up.
1641 setHorizontalTextPosition(SwingConstants.CENTER);
1642 setVerticalTextPosition(SwingConstants.CENTER);
1646 public Component getTableCellRendererComponent(JTable tbl,
1647 Object filter, boolean isSelected, boolean hasFocus, int row,
1650 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1652 String asText = theFilter.toString();
1653 setBackground(tbl.getBackground());
1654 this.setText(asText);
1659 if (selectedBorder == null)
1661 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1662 tbl.getSelectionBackground());
1664 setBorder(selectedBorder);
1668 if (unselectedBorder == null)
1670 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1671 tbl.getBackground());
1673 setBorder(unselectedBorder);
1681 * update comp using rendering settings from gcol
1686 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1688 int w = comp.getWidth(), h = comp.getHeight();
1691 w = (int) comp.getPreferredSize().getWidth();
1692 h = (int) comp.getPreferredSize().getHeight();
1699 renderGraduatedColor(comp, gcol, w, h);
1702 @SuppressWarnings("serial")
1703 class ColorEditor extends AbstractCellEditor
1704 implements TableCellEditor, ActionListener
1706 FeatureColourI currentColor;
1708 FeatureTypeSettings chooser;
1714 protected static final String EDIT = "edit";
1716 int rowSelected = 0;
1718 public ColorEditor()
1720 // Set up the editor (from the table's point of view),
1721 // which is a button.
1722 // This button brings up the color chooser dialog,
1723 // which is the editor from the user's point of view.
1724 button = new JButton();
1725 button.setActionCommand(EDIT);
1726 button.addActionListener(this);
1727 button.setBorderPainted(false);
1731 * Handles events from the editor button, and from the colour/filters
1732 * dialog's OK button
1735 public void actionPerformed(ActionEvent e)
1737 if (button == e.getSource())
1739 if (currentColor.isSimpleColour())
1742 * simple colour chooser
1744 String ttl = MessageManager.formatMessage("label.select_colour_for", type);
1745 ColourChooserListener listener = new ColourChooserListener()
1748 public void colourSelected(Color c)
1750 currentColor = new FeatureColour(c);
1751 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1752 fireEditingStopped();
1755 public void cancel()
1757 fireEditingStopped();
1760 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1765 * variable colour and filters dialog
1767 chooser = new FeatureTypeSettings(fr, type);
1768 if (!Jalview.isJS())
1770 chooser.setRequestFocusEnabled(true);
1771 chooser.requestFocus();
1773 chooser.addActionListener(this);
1774 fireEditingStopped();
1780 * after OK in variable colour dialog, any changes to colour
1781 * (or filters!) are already set in FeatureRenderer, so just
1782 * update table data without triggering updateFeatureRenderer
1784 currentColor = fr.getFeatureColours().get(type);
1785 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1786 if (currentFilter == null)
1788 currentFilter = new FeatureMatcherSet();
1790 Object[] data = ((FeatureTableModel) table.getModel())
1791 .getData()[rowSelected];
1792 data[COLOUR_COLUMN] = currentColor;
1793 data[FILTER_COLUMN] = currentFilter;
1794 fireEditingStopped();
1795 // SwingJS needs an explicit repaint() here,
1796 // rather than relying upon no validation having
1797 // occurred since the stopEditing call was made.
1798 // Its laying out has not been stopped by the modal frame
1805 * Override allows access to this method from anonymous inner classes
1808 protected void fireEditingStopped()
1810 super.fireEditingStopped();
1813 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1815 public Object getCellEditorValue()
1817 return currentColor;
1820 // Implement the one method defined by TableCellEditor.
1822 public Component getTableCellEditorComponent(JTable theTable, Object value,
1823 boolean isSelected, int row, int column)
1825 currentColor = (FeatureColourI) value;
1826 this.rowSelected = row;
1827 type = table.getValueAt(row, TYPE_COLUMN).toString();
1828 button.setOpaque(true);
1829 button.setBackground(FeatureSettings.this.getBackground());
1830 if (!currentColor.isSimpleColour())
1832 JLabel btn = new JLabel();
1833 btn.setSize(button.getSize());
1834 FeatureSettings.renderGraduatedColor(btn, currentColor);
1835 button.setBackground(btn.getBackground());
1836 button.setIcon(btn.getIcon());
1837 button.setText(btn.getText());
1842 button.setIcon(null);
1843 button.setBackground(currentColor.getColour());
1850 * The cell editor for the Filter column. It displays the text of any filters
1851 * for the feature type in that row (in full as a tooltip, possible abbreviated
1852 * as display text). On click in the cell, opens the Feature Display Settings
1853 * dialog at the Filters tab.
1855 @SuppressWarnings("serial")
1856 class FilterEditor extends AbstractCellEditor
1857 implements TableCellEditor, ActionListener
1860 FeatureMatcherSetI currentFilter;
1868 protected static final String EDIT = "edit";
1870 int rowSelected = 0;
1872 public FilterEditor()
1874 button = new JButton();
1875 button.setActionCommand(EDIT);
1876 button.addActionListener(this);
1877 button.setBorderPainted(false);
1881 * Handles events from the editor button
1884 public void actionPerformed(ActionEvent e)
1886 if (button == e.getSource())
1888 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
1889 chooser.addActionListener(this);
1890 chooser.setRequestFocusEnabled(true);
1891 chooser.requestFocus();
1892 if (lastLocation != null)
1894 // todo open at its last position on screen
1895 chooser.setBounds(lastLocation.x, lastLocation.y,
1896 chooser.getWidth(), chooser.getHeight());
1899 fireEditingStopped();
1901 else if (e.getSource() instanceof Component)
1905 * after OK in variable colour dialog, any changes to filter
1906 * (or colours!) are already set in FeatureRenderer, so just
1907 * update table data without triggering updateFeatureRenderer
1909 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1910 currentFilter = fr.getFeatureFilter(type);
1911 if (currentFilter == null)
1913 currentFilter = new FeatureMatcherSet();
1916 Object[] data = ((FeatureTableModel) table.getModel())
1917 .getData()[rowSelected];
1918 data[COLOUR_COLUMN] = currentColor;
1919 data[FILTER_COLUMN] = currentFilter;
1920 fireEditingStopped();
1921 // SwingJS needs an explicit repaint() here,
1922 // rather than relying upon no validation having
1923 // occurred since the stopEditing call was made.
1924 // Its laying out has not been stopped by the modal frame
1931 public Object getCellEditorValue()
1933 return currentFilter;
1937 public Component getTableCellEditorComponent(JTable theTable, Object value,
1938 boolean isSelected, int row, int column)
1940 currentFilter = (FeatureMatcherSetI) value;
1941 this.rowSelected = row;
1942 type = table.getValueAt(row, TYPE_COLUMN).toString();
1943 button.setOpaque(true);
1944 button.setBackground(FeatureSettings.this.getBackground());
1945 button.setText(currentFilter.toString());
1946 button.setIcon(null);
1952 class FeatureIcon implements Icon
1954 FeatureColourI gcol;
1958 boolean midspace = false;
1960 int width = 50, height = 20;
1962 int s1, e1; // start and end of midpoint band for thresholded symbol
1964 Color mpcolour = Color.white;
1966 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1986 public int getIconWidth()
1992 public int getIconHeight()
1998 public void paintIcon(Component c, Graphics g, int x, int y)
2001 if (gcol.isColourByLabel())
2004 g.fillRect(0, 0, width, height);
2005 // need an icon here.
2006 g.setColor(gcol.getMaxColour());
2008 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2010 // g.setFont(g.getFont().deriveFont(
2011 // AffineTransform.getScaleInstance(
2012 // width/g.getFontMetrics().stringWidth("Label"),
2013 // height/g.getFontMetrics().getHeight())));
2015 g.drawString(MessageManager.getString("label.label"), 0, 0);
2020 Color minCol = gcol.getMinColour();
2022 g.fillRect(0, 0, s1, height);
2025 g.setColor(Color.white);
2026 g.fillRect(s1, 0, e1 - s1, height);
2028 g.setColor(gcol.getMaxColour());
2029 // g.fillRect(0, e1, width - e1, height); // BH 2018
2030 g.fillRect(e1, 0, width - e1, height);