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.gui.Help.HelpId;
32 import jalview.gui.JalviewColourChooser.ColourChooserListener;
33 import jalview.io.JalviewFileChooser;
34 import jalview.io.JalviewFileView;
35 import jalview.schemes.FeatureColour;
36 import jalview.util.MessageManager;
37 import jalview.util.Platform;
38 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
39 import jalview.xml.binding.jalview.JalviewUserColours;
40 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
41 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
42 import jalview.xml.binding.jalview.ObjectFactory;
44 import java.awt.BorderLayout;
45 import java.awt.Color;
46 import java.awt.Component;
47 import java.awt.Dimension;
49 import java.awt.Graphics;
50 import java.awt.GridLayout;
51 import java.awt.Point;
52 import java.awt.Rectangle;
53 import java.awt.event.ActionEvent;
54 import java.awt.event.ActionListener;
55 import java.awt.event.ItemEvent;
56 import java.awt.event.ItemListener;
57 import java.awt.event.MouseAdapter;
58 import java.awt.event.MouseEvent;
59 import java.awt.event.MouseMotionAdapter;
60 import java.beans.PropertyChangeEvent;
61 import java.beans.PropertyChangeListener;
63 import java.io.FileInputStream;
64 import java.io.FileOutputStream;
65 import java.io.InputStreamReader;
66 import java.io.OutputStreamWriter;
67 import java.io.PrintWriter;
68 import java.util.Arrays;
69 import java.util.Comparator;
70 import java.util.HashMap;
71 import java.util.HashSet;
72 import java.util.Hashtable;
73 import java.util.Iterator;
74 import java.util.List;
78 import javax.help.HelpSetException;
79 import javax.swing.AbstractCellEditor;
80 import javax.swing.BorderFactory;
81 import javax.swing.Icon;
82 import javax.swing.JButton;
83 import javax.swing.JCheckBox;
84 import javax.swing.JCheckBoxMenuItem;
85 import javax.swing.JInternalFrame;
86 import javax.swing.JLabel;
87 import javax.swing.JLayeredPane;
88 import javax.swing.JMenuItem;
89 import javax.swing.JPanel;
90 import javax.swing.JPopupMenu;
91 import javax.swing.JScrollPane;
92 import javax.swing.JSlider;
93 import javax.swing.JTable;
94 import javax.swing.ListSelectionModel;
95 import javax.swing.SwingConstants;
96 import javax.swing.ToolTipManager;
97 import javax.swing.border.Border;
98 import javax.swing.event.ChangeEvent;
99 import javax.swing.event.ChangeListener;
100 import javax.swing.table.AbstractTableModel;
101 import javax.swing.table.TableCellEditor;
102 import javax.swing.table.TableCellRenderer;
103 import javax.swing.table.TableColumn;
104 import javax.xml.bind.JAXBContext;
105 import javax.xml.bind.JAXBElement;
106 import javax.xml.bind.Marshaller;
107 import javax.xml.stream.XMLInputFactory;
108 import javax.xml.stream.XMLStreamReader;
110 public class FeatureSettings extends JPanel
111 implements FeatureSettingsControllerI
113 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
114 .getString("label.sequence_feature_colours");
117 * column indices of fields in Feature Settings table
119 static final int TYPE_COLUMN = 0;
121 static final int COLOUR_COLUMN = 1;
123 static final int FILTER_COLUMN = 2;
125 static final int SHOW_COLUMN = 3;
127 private static final int COLUMN_COUNT = 4;
129 private static final int MIN_WIDTH = 400;
131 private static final int MIN_HEIGHT = 400;
133 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
135 final FeatureRenderer fr;
137 public final AlignFrame af;
140 * 'original' fields hold settings to restore on Cancel
142 Object[][] originalData;
144 float originalTransparency;
146 Map<String, FeatureMatcherSetI> originalFilters;
148 final JInternalFrame frame;
150 JScrollPane scrollPane = new JScrollPane();
156 JSlider transparency = new JSlider();
159 * when true, constructor is still executing - so ignore UI events
161 protected volatile boolean inConstruction = true;
163 int selectedRow = -1;
165 JButton fetchDAS = new JButton();
167 JButton saveDAS = new JButton();
169 JButton cancelDAS = new JButton();
171 boolean resettingTable = false;
174 * true when Feature Settings are updating from feature renderer
176 boolean handlingUpdate = false;
179 * holds {featureCount, totalExtent} for each feature type
181 Map<String, float[]> typeWidth = null;
188 public FeatureSettings(AlignFrame alignFrame)
190 this.af = alignFrame;
191 fr = af.getFeatureRenderer();
193 // save transparency for restore on Cancel
194 originalTransparency = fr.getTransparency();
195 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
196 transparency.setMaximum(100 - originalTransparencyAsPercent);
198 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
203 } catch (Exception ex)
205 ex.printStackTrace();
212 // public void repaint() {
213 // System.out.println("FS repaint");
219 // public void repaint(long tm, int x, int y, int width, int height) {
220 // System.out.println("FS repaint " + x + " " + y + " " + width + " " + height);
221 // super.repaint(tm, x, y, width, height);
225 // public void invalidate() {
227 // System.out.println("FS invalidating ");
228 // super.invalidate();
232 // public void validate() {
233 // System.out.println("FS validating " + isValid());
238 public String getToolTipText(MouseEvent e)
241 int column = table.columnAtPoint(e.getPoint());
242 int row = table.rowAtPoint(e.getPoint());
247 tip = JvSwingUtils.wrapTooltip(true, MessageManager
248 .getString("label.feature_settings_click_drag"));
251 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
253 tip = getColorTooltip(colour, true);
256 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
260 .getString("label.configure_feature_tooltip")
272 * Position the tooltip near the bottom edge of, and half way across, the
276 public Point getToolTipLocation(MouseEvent e)
278 Point point = e.getPoint();
279 int column = table.columnAtPoint(point);
280 int row = table.rowAtPoint(point);
281 Rectangle r = getCellRect(row, column, false);
282 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
287 // next line is needed to avoid (quiet) exceptions thrown
288 // when column ordering changes so that the above constants
290 table.getTableHeader().setReorderingAllowed(false); // BH 2018
292 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
293 ToolTipManager.sharedInstance().registerComponent(table);
295 table.setDefaultEditor(FeatureColour.class, new ColorEditor());
296 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
298 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor());
299 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
301 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
302 new ColorRenderer(), new ColorEditor());
303 table.addColumn(colourColumn);
305 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
306 new FilterRenderer(), new FilterEditor());
307 table.addColumn(filterColumn);
309 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
311 table.addMouseListener(new MouseAdapter()
314 public void mousePressed(MouseEvent evt)
316 selectedRow = table.rowAtPoint(evt.getPoint());
317 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
318 if (evt.isPopupTrigger())
320 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
321 showPopupMenu(selectedRow, type, colour, evt.getPoint());
323 else if (evt.getClickCount() == 2)
325 boolean invertSelection = evt.isAltDown();
326 boolean toggleSelection = Platform.isControlDown(evt);
327 boolean extendSelection = evt.isShiftDown();
328 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
329 invertSelection, extendSelection, toggleSelection, type);
333 // isPopupTrigger fires on mouseReleased on Windows
335 public void mouseReleased(MouseEvent evt)
337 selectedRow = table.rowAtPoint(evt.getPoint());
338 if (evt.isPopupTrigger())
340 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
341 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
342 showPopupMenu(selectedRow, type, colour, evt.getPoint());
347 table.addMouseMotionListener(new MouseMotionAdapter()
350 public void mouseDragged(MouseEvent evt)
352 int newRow = table.rowAtPoint(evt.getPoint());
353 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
356 * reposition 'selectedRow' to 'newRow' (the dragged to location)
357 * this could be more than one row away for a very fast drag action
358 * so just swap it with adjacent rows until we get it there
360 Object[][] data = ((FeatureTableModel) table.getModel())
362 int direction = newRow < selectedRow ? -1 : 1;
363 for (int i = selectedRow; i != newRow; i += direction)
365 Object[] temp = data[i];
366 data[i] = data[i + direction];
367 data[i + direction] = temp;
369 updateFeatureRenderer(data);
371 selectedRow = newRow;
375 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
376 // MessageManager.getString("label.feature_settings_click_drag")));
377 scrollPane.setViewportView(table);
379 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
381 fr.findAllFeatures(true); // display everything!
384 discoverAllFeatureData();
385 final PropertyChangeListener change;
386 final FeatureSettings fs = this;
387 fr.addPropertyChangeListener(change = new PropertyChangeListener()
390 public void propertyChange(PropertyChangeEvent evt)
392 if (!fs.resettingTable && !fs.handlingUpdate)
394 fs.handlingUpdate = true;
396 // new groups may be added with new sequence feature types only
397 fs.handlingUpdate = false;
403 frame = new JInternalFrame();
404 frame.setContentPane(this);
405 Desktop.addInternalFrame(frame,
406 MessageManager.getString("label.sequence_feature_settings"),
407 600, Platform.isAMacAndNotJS() ? 480 : 450);
408 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
410 frame.addInternalFrameListener(
411 new javax.swing.event.InternalFrameAdapter()
414 public void internalFrameClosed(
415 javax.swing.event.InternalFrameEvent evt)
417 fr.removePropertyChangeListener(change);
420 frame.setLayer(JLayeredPane.PALETTE_LAYER);
421 inConstruction = false;
425 * Constructs and shows a popup menu of possible actions on the selected row and
433 protected void showPopupMenu(final int rowSelected, final String type,
434 final Object typeCol, final Point pt)
436 JPopupMenu men = new JPopupMenu(MessageManager
437 .formatMessage("label.settings_for_param", new String[]
439 final FeatureColourI featureColour = (FeatureColourI) typeCol;
442 * menu option to select (or deselect) variable colour
444 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
445 MessageManager.getString("label.variable_colour"));
446 variableColourCB.setSelected(!featureColour.isSimpleColour());
447 men.add(variableColourCB);
450 * checkbox action listener doubles up as listener to OK
451 * from the variable colour / filters dialog
453 variableColourCB.addActionListener(new ActionListener()
456 public void actionPerformed(ActionEvent e)
458 if (e.getSource() == variableColourCB)
460 men.setVisible(true); // BH 2018 for JavaScript because this is a checkbox
461 men.setVisible(false); // BH 2018 for JavaScript because this is a checkbox
462 if (featureColour.isSimpleColour())
465 * toggle simple colour to variable colour - show dialog
467 FeatureTypeSettings fc = new FeatureTypeSettings(fr, type);
468 fc.addActionListener(this);
473 * toggle variable to simple colour - show colour chooser
475 String title = MessageManager.formatMessage("label.select_colour_for", type);
476 ColourChooserListener listener = new ColourChooserListener()
479 public void colourSelected(Color c)
481 table.setValueAt(new FeatureColour(c), rowSelected,
484 updateFeatureRenderer(
485 ((FeatureTableModel) table.getModel()).getData(),
489 JalviewColourChooser.showColourChooser(FeatureSettings.this, title,
490 featureColour.getMaxColour(), listener);
495 if (e.getSource() instanceof FeatureTypeSettings)
498 * update after OK in feature colour dialog; the updated
499 * colour will have already been set in the FeatureRenderer
501 FeatureColourI fci = fr.getFeatureColours().get(type);
502 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
503 // BH 2018 setting a table value does not invalidate it.
504 // System.out.println("FeatureSettings is valied" + table.isValid());
513 JMenuItem scr = new JMenuItem(
514 MessageManager.getString("label.sort_by_score"));
516 scr.addActionListener(new ActionListener()
520 public void actionPerformed(ActionEvent e)
522 af.avc.sortAlignmentByFeatureScore(Arrays.asList(new String[]
526 JMenuItem dens = new JMenuItem(
527 MessageManager.getString("label.sort_by_density"));
528 dens.addActionListener(new ActionListener()
532 public void actionPerformed(ActionEvent e)
534 af.avc.sortAlignmentByFeatureDensity(Arrays.asList(new String[]
540 JMenuItem selCols = new JMenuItem(
541 MessageManager.getString("label.select_columns_containing"));
542 selCols.addActionListener(new ActionListener()
545 public void actionPerformed(ActionEvent arg0)
547 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
551 JMenuItem clearCols = new JMenuItem(MessageManager
552 .getString("label.select_columns_not_containing"));
553 clearCols.addActionListener(new ActionListener()
556 public void actionPerformed(ActionEvent arg0)
558 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
562 JMenuItem hideCols = new JMenuItem(
563 MessageManager.getString("label.hide_columns_containing"));
564 hideCols.addActionListener(new ActionListener()
567 public void actionPerformed(ActionEvent arg0)
569 fr.ap.alignFrame.hideFeatureColumns(type, true);
572 JMenuItem hideOtherCols = new JMenuItem(
573 MessageManager.getString("label.hide_columns_not_containing"));
574 hideOtherCols.addActionListener(new ActionListener()
577 public void actionPerformed(ActionEvent arg0)
579 fr.ap.alignFrame.hideFeatureColumns(type, false);
585 men.add(hideOtherCols);
586 men.show(table, pt.x, pt.y);
590 synchronized public void discoverAllFeatureData()
592 Set<String> allGroups = new HashSet<>();
593 AlignmentI alignment = af.getViewport().getAlignment();
595 for (int i = 0; i < alignment.getHeight(); i++)
597 SequenceI seq = alignment.getSequenceAt(i);
598 for (String group : seq.getFeatures().getFeatureGroups(true))
600 if (group != null && !allGroups.contains(group))
602 allGroups.add(group);
603 checkGroupState(group);
614 * Synchronise gui group list and check visibility of group
617 * @return true if group is visible
619 private boolean checkGroupState(String group)
621 boolean visible = fr.checkGroupVisibility(group, true);
623 for (int g = 0; g < groupPanel.getComponentCount(); g++)
625 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
627 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
632 final String grp = group;
633 final JCheckBox check = new JCheckBox(group, visible);
634 check.setFont(new Font("Serif", Font.BOLD, 12));
635 check.setToolTipText(group);
636 check.addItemListener(new ItemListener()
639 public void itemStateChanged(ItemEvent evt)
641 fr.setGroupVisibility(check.getText(), check.isSelected());
642 resetTable(new String[] { grp });
643 af.alignPanel.paintAlignment(true, true);
646 groupPanel.add(check);
650 synchronized void resetTable(String[] groupChanged)
656 resettingTable = true;
657 typeWidth = new Hashtable<>();
658 // TODO: change avWidth calculation to 'per-sequence' average and use long
661 Set<String> displayableTypes = new HashSet<>();
662 Set<String> foundGroups = new HashSet<>();
665 * determine which feature types may be visible depending on
666 * which groups are selected, and recompute average width data
668 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
671 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
674 * get the sequence's groups for positional features
675 * and keep track of which groups are visible
677 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
678 Set<String> visibleGroups = new HashSet<>();
679 for (String group : groups)
681 if (group == null || checkGroupState(group))
683 visibleGroups.add(group);
686 foundGroups.addAll(groups);
689 * get distinct feature types for visible groups
690 * record distinct visible types, and their count and total length
692 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
693 visibleGroups.toArray(new String[visibleGroups.size()]));
694 for (String type : types)
696 displayableTypes.add(type);
697 float[] avWidth = typeWidth.get(type);
700 avWidth = new float[2];
701 typeWidth.put(type, avWidth);
703 // todo this could include features with a non-visible group
704 // - do we greatly care?
705 // todo should we include non-displayable features here, and only
706 // update when features are added?
707 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
708 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
712 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
715 if (fr.hasRenderOrder())
719 fr.findAllFeatures(groupChanged != null); // prod to update
720 // colourschemes. but don't
722 // First add the checks in the previous render order,
723 // in case the window has been closed and reopened
725 List<String> frl = fr.getRenderOrder();
726 for (int ro = frl.size() - 1; ro > -1; ro--)
728 String type = frl.get(ro);
730 if (!displayableTypes.contains(type))
735 data[dataIndex][TYPE_COLUMN] = type;
736 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
737 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
738 data[dataIndex][FILTER_COLUMN] = featureFilter == null
739 ? new FeatureMatcherSet()
741 data[dataIndex][SHOW_COLUMN] = new Boolean(
742 af.getViewport().getFeaturesDisplayed().isVisible(type));
744 displayableTypes.remove(type);
749 * process any extra features belonging only to
750 * a group which was just selected
752 while (!displayableTypes.isEmpty())
754 String type = displayableTypes.iterator().next();
755 data[dataIndex][TYPE_COLUMN] = type;
757 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
758 if (data[dataIndex][COLOUR_COLUMN] == null)
760 // "Colour has been updated in another view!!"
761 fr.clearRenderOrder();
764 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
765 data[dataIndex][FILTER_COLUMN] = featureFilter == null
766 ? new FeatureMatcherSet()
768 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
770 displayableTypes.remove(type);
773 if (originalData == null)
775 originalData = new Object[data.length][COLUMN_COUNT];
776 for (int i = 0; i < data.length; i++)
778 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
783 updateOriginalData(data);
786 table.setModel(new FeatureTableModel(data));
787 table.getColumnModel().getColumn(0).setPreferredWidth(200);
789 groupPanel.setLayout(
790 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
791 pruneGroups(foundGroups);
792 groupPanel.validate();
794 updateFeatureRenderer(data, groupChanged != null);
795 resettingTable = false;
799 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
800 * have been made outwith this dialog
802 * <li>a new feature type added (and made visible)</li>
803 * <li>a feature colour changed (in the Amend Features dialog)</li>
808 protected void updateOriginalData(Object[][] foundData)
810 // todo LinkedHashMap instead of Object[][] would be nice
812 Object[][] currentData = ((FeatureTableModel) table.getModel())
814 for (Object[] row : foundData)
816 String type = (String) row[TYPE_COLUMN];
817 boolean found = false;
818 for (Object[] current : currentData)
820 if (type.equals(current[TYPE_COLUMN]))
824 * currently dependent on object equality here;
825 * really need an equals method on FeatureColour
827 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
830 * feature colour has changed externally - update originalData
832 for (Object[] original : originalData)
834 if (type.equals(original[TYPE_COLUMN]))
836 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
847 * new feature detected - add to original data (on top)
849 Object[][] newData = new Object[originalData.length
851 for (int i = 0; i < originalData.length; i++)
853 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
857 originalData = newData;
863 * Remove from the groups panel any checkboxes for groups that are not in the
864 * foundGroups set. This enables removing a group from the display when the last
865 * feature in that group is deleted.
869 protected void pruneGroups(Set<String> foundGroups)
871 for (int g = 0; g < groupPanel.getComponentCount(); g++)
873 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
874 if (!foundGroups.contains(checkbox.getText()))
876 groupPanel.remove(checkbox);
882 * reorder data based on the featureRenderers global priority list.
886 private void ensureOrder(Object[][] data)
888 boolean sort = false;
889 float[] order = new float[data.length];
890 for (int i = 0; i < order.length; i++)
892 order[i] = fr.getOrder(data[i][0].toString());
895 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
899 sort = sort || order[i - 1] > order[i];
904 jalview.util.QuickSort.sort(order, data);
909 * Offers a file chooser dialog, and then loads the feature colours and
910 * filters from file in XML format and unmarshals to Jalview feature settings
914 JalviewFileChooser chooser = new JalviewFileChooser("fc",
915 SEQUENCE_FEATURE_COLOURS);
916 chooser.setFileView(new JalviewFileView());
917 chooser.setDialogTitle(
918 MessageManager.getString("label.load_feature_colours"));
919 chooser.setToolTipText(MessageManager.getString("action.load"));
920 chooser.setResponseHandler(0, new Runnable()
925 File file = chooser.getSelectedFile();
929 chooser.showOpenDialog(this);
933 * Loads feature colours and filters from XML stored in the given file
941 InputStreamReader in = new InputStreamReader(
942 new FileInputStream(file), "UTF-8");
944 JAXBContext jc = JAXBContext
945 .newInstance("jalview.xml.binding.jalview");
946 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
947 XMLStreamReader streamReader = XMLInputFactory.newInstance()
948 .createXMLStreamReader(in);
949 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
950 JalviewUserColours.class);
951 JalviewUserColours jucs = jbe.getValue();
953 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
956 * load feature colours
958 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
960 Colour newcol = jucs.getColour().get(i);
961 FeatureColourI colour = jalview.project.Jalview2XML
962 .parseColour(newcol);
963 fr.setColour(newcol.getName(), colour);
964 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
968 * load feature filters; loaded filters will replace any that are
969 * currently defined, other defined filters are left unchanged
971 for (int i = 0; i < jucs.getFilter().size(); i++)
973 Filter filterModel = jucs.getFilter().get(i);
974 String featureType = filterModel.getFeatureType();
975 FeatureMatcherSetI filter = jalview.project.Jalview2XML
976 .parseFilter(featureType, filterModel.getMatcherSet());
977 if (!filter.isEmpty())
979 fr.setFeatureFilter(featureType, filter);
984 * update feature settings table
989 Object[][] data = ((FeatureTableModel) table.getModel())
992 updateFeatureRenderer(data, false);
995 } catch (Exception ex)
997 System.out.println("Error loading User Colour File\n" + ex);
1002 * Offers a file chooser dialog, and then saves the current feature colours
1003 * and any filters to the selected file in XML format
1007 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1008 SEQUENCE_FEATURE_COLOURS);
1009 chooser.setFileView(new JalviewFileView());
1010 chooser.setDialogTitle(
1011 MessageManager.getString("label.save_feature_colours"));
1012 chooser.setToolTipText(MessageManager.getString("action.save"));
1013 int option = chooser.showSaveDialog(this);
1014 if (option == JalviewFileChooser.APPROVE_OPTION)
1016 File file = chooser.getSelectedFile();
1022 * Saves feature colours and filters to the given file
1026 void save(File file)
1028 JalviewUserColours ucs = new JalviewUserColours();
1029 ucs.setSchemeName("Sequence Features");
1032 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1033 new FileOutputStream(file), "UTF-8"));
1036 * sort feature types by colour order, from 0 (highest)
1039 Set<String> fr_colours = fr.getAllFeatureColours();
1040 String[] sortedTypes = fr_colours
1041 .toArray(new String[fr_colours.size()]);
1042 Arrays.sort(sortedTypes, new Comparator<String>()
1045 public int compare(String type1, String type2)
1047 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1052 * save feature colours
1054 for (String featureType : sortedTypes)
1056 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1057 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1059 ucs.getColour().add(col);
1063 * save any feature filters
1065 for (String featureType : sortedTypes)
1067 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1068 if (filter != null && !filter.isEmpty())
1070 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1071 FeatureMatcherI firstMatcher = iterator.next();
1072 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1073 .marshalFilter(firstMatcher, iterator,
1075 Filter filterModel = new Filter();
1076 filterModel.setFeatureType(featureType);
1077 filterModel.setMatcherSet(ms);
1078 ucs.getFilter().add(filterModel);
1081 JAXBContext jaxbContext = JAXBContext
1082 .newInstance(JalviewUserColours.class);
1083 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1084 jaxbMarshaller.marshal(
1085 new ObjectFactory().createJalviewUserColours(ucs), out);
1087 // jaxbMarshaller.marshal(object, pout);
1088 // marshaller.marshal(object);
1091 // ucs.marshal(out);
1093 } catch (Exception ex)
1095 ex.printStackTrace();
1099 public void invertSelection()
1101 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1102 for (int i = 0; i < data.length; i++)
1104 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1106 updateFeatureRenderer(data, true);
1110 public void orderByAvWidth()
1112 if (table == null || table.getModel() == null)
1116 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1117 float[] width = new float[data.length];
1121 for (int i = 0; i < data.length; i++)
1123 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1126 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1127 // weight - but have to make per
1128 // sequence, too (awidth[2])
1129 // if (width[i]==1) // hack to distinguish single width sequences.
1140 boolean sort = false;
1141 for (int i = 0; i < width.length; i++)
1143 // awidth = (float[]) typeWidth.get(data[i][0]);
1146 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1149 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1155 width[i] /= max; // normalize
1156 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1160 sort = sort || width[i - 1] > width[i];
1165 jalview.util.QuickSort.sort(width, data);
1166 // update global priority order
1169 updateFeatureRenderer(data, false);
1177 frame.setClosed(true);
1178 } catch (Exception exe)
1184 public void updateFeatureRenderer(Object[][] data)
1186 updateFeatureRenderer(data, true);
1190 * Update the priority order of features; only repaint if this changed the order
1191 * of visible features
1196 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1198 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1200 if (fr.setFeaturePriority(rowData, visibleNew))
1202 af.alignPanel.paintAlignment(true, true);
1207 * Converts table data into an array of data beans
1209 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1211 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1212 for (int i = 0; i < data.length; i++)
1214 String type = (String) data[i][TYPE_COLUMN];
1215 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1216 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1217 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1218 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1224 private void jbInit() throws Exception
1226 this.setLayout(new BorderLayout());
1228 JPanel settingsPane = new JPanel();
1229 settingsPane.setLayout(new BorderLayout());
1231 JPanel bigPanel = new JPanel();
1232 bigPanel.setLayout(new BorderLayout());
1234 groupPanel = new JPanel();
1235 bigPanel.add(groupPanel, BorderLayout.NORTH);
1237 JButton invert = new JButton(
1238 MessageManager.getString("label.invert_selection"));
1239 invert.setFont(JvSwingUtils.getLabelFont());
1240 invert.addActionListener(new ActionListener()
1243 public void actionPerformed(ActionEvent e)
1249 JButton optimizeOrder = new JButton(
1250 MessageManager.getString("label.optimise_order"));
1251 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1252 optimizeOrder.addActionListener(new ActionListener()
1255 public void actionPerformed(ActionEvent e)
1261 JButton sortByScore = new JButton(
1262 MessageManager.getString("label.seq_sort_by_score"));
1263 sortByScore.setFont(JvSwingUtils.getLabelFont());
1264 sortByScore.addActionListener(new ActionListener()
1267 public void actionPerformed(ActionEvent e)
1269 af.avc.sortAlignmentByFeatureScore(null);
1272 JButton sortByDens = new JButton(
1273 MessageManager.getString("label.sequence_sort_by_density"));
1274 sortByDens.setFont(JvSwingUtils.getLabelFont());
1275 sortByDens.addActionListener(new ActionListener()
1278 public void actionPerformed(ActionEvent e)
1280 af.avc.sortAlignmentByFeatureDensity(null);
1284 JButton help = new JButton(MessageManager.getString("action.help"));
1285 help.setFont(JvSwingUtils.getLabelFont());
1286 help.addActionListener(new ActionListener()
1289 public void actionPerformed(ActionEvent e)
1293 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1294 } catch (HelpSetException e1)
1296 e1.printStackTrace();
1300 help.setFont(JvSwingUtils.getLabelFont());
1301 help.setText(MessageManager.getString("action.help"));
1302 help.addActionListener(new ActionListener()
1305 public void actionPerformed(ActionEvent e)
1309 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1310 } catch (HelpSetException e1)
1312 e1.printStackTrace();
1317 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1318 cancel.setFont(JvSwingUtils.getLabelFont());
1319 cancel.addActionListener(new ActionListener()
1322 public void actionPerformed(ActionEvent e)
1324 fr.setTransparency(originalTransparency);
1325 fr.setFeatureFilters(originalFilters);
1326 updateFeatureRenderer(originalData);
1331 JButton ok = new JButton(MessageManager.getString("action.ok"));
1332 ok.setFont(JvSwingUtils.getLabelFont());
1333 ok.addActionListener(new ActionListener()
1336 public void actionPerformed(ActionEvent e)
1342 JButton loadColours = new JButton(
1343 MessageManager.getString("label.load_colours"));
1344 loadColours.setFont(JvSwingUtils.getLabelFont());
1345 loadColours.setToolTipText(
1346 MessageManager.getString("label.load_colours_tooltip"));
1347 loadColours.addActionListener(new ActionListener()
1350 public void actionPerformed(ActionEvent e)
1356 JButton saveColours = new JButton(
1357 MessageManager.getString("label.save_colours"));
1358 saveColours.setFont(JvSwingUtils.getLabelFont());
1359 saveColours.setToolTipText(
1360 MessageManager.getString("label.save_colours_tooltip"));
1361 saveColours.addActionListener(new ActionListener()
1364 public void actionPerformed(ActionEvent e)
1369 transparency.addChangeListener(new ChangeListener()
1372 public void stateChanged(ChangeEvent evt)
1374 if (!inConstruction)
1376 fr.setTransparency((100 - transparency.getValue()) / 100f);
1377 af.alignPanel.paintAlignment(true, true);
1382 transparency.setMaximum(70);
1383 transparency.setToolTipText(
1384 MessageManager.getString("label.transparency_tip"));
1386 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1387 bigPanel.add(transPanel, BorderLayout.SOUTH);
1389 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1390 transbuttons.add(optimizeOrder);
1391 transbuttons.add(invert);
1392 transbuttons.add(sortByScore);
1393 transbuttons.add(sortByDens);
1394 transbuttons.add(help);
1395 transPanel.add(transparency);
1396 transPanel.add(transbuttons);
1398 JPanel buttonPanel = new JPanel();
1399 buttonPanel.add(ok);
1400 buttonPanel.add(cancel);
1401 buttonPanel.add(loadColours);
1402 buttonPanel.add(saveColours);
1403 bigPanel.add(scrollPane, BorderLayout.CENTER);
1404 settingsPane.add(bigPanel, BorderLayout.CENTER);
1405 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1406 this.add(settingsPane);
1410 * Answers a suitable tooltip to show on the colour cell of the table
1414 * if true include 'click to edit' and similar text
1417 public static String getColorTooltip(FeatureColourI fcol,
1424 if (fcol.isSimpleColour())
1426 return withHint ? BASE_TOOLTIP : null;
1428 String description = fcol.getDescription();
1429 description = description.replaceAll("<", "<");
1430 description = description.replaceAll(">", ">");
1431 StringBuilder tt = new StringBuilder(description);
1434 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1436 return JvSwingUtils.wrapTooltip(true, tt.toString());
1439 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1442 boolean thr = false;
1443 StringBuilder tx = new StringBuilder();
1445 if (gcol.isColourByAttribute())
1447 tx.append(FeatureMatcher
1448 .toAttributeDisplayName(gcol.getAttributeName()));
1450 else if (!gcol.isColourByLabel())
1452 tx.append(MessageManager.getString("label.score"));
1455 if (gcol.isAboveThreshold())
1460 if (gcol.isBelowThreshold())
1465 if (gcol.isColourByLabel())
1471 if (!gcol.isColourByAttribute())
1479 Color newColor = gcol.getMaxColour();
1480 comp.setBackground(newColor);
1481 // System.err.println("Width is " + w / 2);
1482 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1483 comp.setIcon(ficon);
1484 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1485 // + newColor.getGreen() + ", " + newColor.getBlue()
1486 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1487 // + ", " + minCol.getBlue() + ")");
1489 comp.setHorizontalAlignment(SwingConstants.CENTER);
1490 comp.setText(tx.toString());
1493 // ///////////////////////////////////////////////////////////////////////
1494 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1495 // ///////////////////////////////////////////////////////////////////////
1496 class FeatureTableModel extends AbstractTableModel
1498 private String[] columnNames = {
1499 MessageManager.getString("label.feature_type"),
1500 MessageManager.getString("action.colour"),
1501 MessageManager.getString("label.configuration"),
1502 MessageManager.getString("label.show") };
1504 private Object[][] data;
1506 FeatureTableModel(Object[][] data)
1511 public Object[][] getData()
1516 public void setData(Object[][] data)
1522 public int getColumnCount()
1524 return columnNames.length;
1527 public Object[] getRow(int row)
1533 public int getRowCount()
1539 public String getColumnName(int col)
1541 return columnNames[col];
1545 public Object getValueAt(int row, int col)
1547 return data[row][col];
1551 * Answers the class of the object in column c of the first row of the table
1554 public Class<?> getColumnClass(int c)
1556 Object v = getValueAt(0, c);
1557 return v == null ? null : v.getClass();
1561 public boolean isCellEditable(int row, int col)
1563 return col == 0 ? false : true;
1567 public void setValueAt(Object value, int row, int col)
1569 data[row][col] = value;
1570 fireTableCellUpdated(row, col);
1571 updateFeatureRenderer(data);
1576 class ColorRenderer extends JLabel implements TableCellRenderer
1578 Border unselectedBorder = null;
1580 Border selectedBorder = null;
1582 public ColorRenderer()
1584 setOpaque(true); // MUST do this for background to show up.
1585 setHorizontalTextPosition(SwingConstants.CENTER);
1586 setVerticalTextPosition(SwingConstants.CENTER);
1590 public Component getTableCellRendererComponent(JTable tbl, Object color,
1591 boolean isSelected, boolean hasFocus, int row, int column)
1593 FeatureColourI cellColour = (FeatureColourI) color;
1595 setBackground(tbl.getBackground());
1596 if (!cellColour.isSimpleColour())
1598 Rectangle cr = tbl.getCellRect(row, column, false);
1599 FeatureSettings.renderGraduatedColor(this, cellColour,
1600 (int) cr.getWidth(), (int) cr.getHeight());
1606 setBackground(cellColour.getColour());
1610 if (selectedBorder == null)
1612 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1613 tbl.getSelectionBackground());
1615 setBorder(selectedBorder);
1619 if (unselectedBorder == null)
1621 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1622 tbl.getBackground());
1624 setBorder(unselectedBorder);
1631 class FilterRenderer extends JLabel implements TableCellRenderer
1633 javax.swing.border.Border unselectedBorder = null;
1635 javax.swing.border.Border selectedBorder = null;
1637 public FilterRenderer()
1639 setOpaque(true); // MUST do this for background to show up.
1640 setHorizontalTextPosition(SwingConstants.CENTER);
1641 setVerticalTextPosition(SwingConstants.CENTER);
1645 public Component getTableCellRendererComponent(JTable tbl,
1646 Object filter, boolean isSelected, boolean hasFocus, int row,
1649 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1651 String asText = theFilter.toString();
1652 setBackground(tbl.getBackground());
1653 this.setText(asText);
1658 if (selectedBorder == null)
1660 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1661 tbl.getSelectionBackground());
1663 setBorder(selectedBorder);
1667 if (unselectedBorder == null)
1669 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1670 tbl.getBackground());
1672 setBorder(unselectedBorder);
1680 * update comp using rendering settings from gcol
1685 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1687 int w = comp.getWidth(), h = comp.getHeight();
1690 w = (int) comp.getPreferredSize().getWidth();
1691 h = (int) comp.getPreferredSize().getHeight();
1698 renderGraduatedColor(comp, gcol, w, h);
1701 @SuppressWarnings("serial")
1702 class ColorEditor extends AbstractCellEditor
1703 implements TableCellEditor, ActionListener
1705 FeatureColourI currentColor;
1707 FeatureTypeSettings chooser;
1713 protected static final String EDIT = "edit";
1715 int rowSelected = 0;
1717 public ColorEditor()
1719 // Set up the editor (from the table's point of view),
1720 // which is a button.
1721 // This button brings up the color chooser dialog,
1722 // which is the editor from the user's point of view.
1723 button = new JButton();
1724 button.setActionCommand(EDIT);
1725 button.addActionListener(this);
1726 button.setBorderPainted(false);
1730 * Handles events from the editor button, and from the colour/filters
1731 * dialog's OK button
1734 public void actionPerformed(ActionEvent e)
1736 if (button == e.getSource())
1738 if (currentColor.isSimpleColour())
1741 * simple colour chooser
1743 String ttl = MessageManager.formatMessage("label.select_colour_for", type);
1744 ColourChooserListener listener = new ColourChooserListener()
1747 public void colourSelected(Color c)
1749 currentColor = new FeatureColour(c);
1750 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1751 fireEditingStopped();
1754 public void cancel()
1756 fireEditingStopped();
1759 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1764 * variable colour and filters dialog
1766 chooser = new FeatureTypeSettings(fr, type);
1767 if (!Platform.isJS())
1769 chooser.setRequestFocusEnabled(true);
1770 chooser.requestFocus();
1772 chooser.addActionListener(this);
1773 fireEditingStopped();
1779 * after OK in variable colour dialog, any changes to colour
1780 * (or filters!) are already set in FeatureRenderer, so just
1781 * update table data without triggering updateFeatureRenderer
1783 currentColor = fr.getFeatureColours().get(type);
1784 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1785 if (currentFilter == null)
1787 currentFilter = new FeatureMatcherSet();
1789 Object[] data = ((FeatureTableModel) table.getModel())
1790 .getData()[rowSelected];
1791 data[COLOUR_COLUMN] = currentColor;
1792 data[FILTER_COLUMN] = currentFilter;
1793 fireEditingStopped();
1794 // SwingJS needs an explicit repaint() here,
1795 // rather than relying upon no validation having
1796 // occurred since the stopEditing call was made.
1797 // Its laying out has not been stopped by the modal frame
1804 * Override allows access to this method from anonymous inner classes
1807 protected void fireEditingStopped()
1809 super.fireEditingStopped();
1812 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1814 public Object getCellEditorValue()
1816 return currentColor;
1819 // Implement the one method defined by TableCellEditor.
1821 public Component getTableCellEditorComponent(JTable theTable, Object value,
1822 boolean isSelected, int row, int column)
1824 currentColor = (FeatureColourI) value;
1825 this.rowSelected = row;
1826 type = table.getValueAt(row, TYPE_COLUMN).toString();
1827 button.setOpaque(true);
1828 button.setBackground(FeatureSettings.this.getBackground());
1829 if (!currentColor.isSimpleColour())
1831 JLabel btn = new JLabel();
1832 btn.setSize(button.getSize());
1833 FeatureSettings.renderGraduatedColor(btn, currentColor);
1834 button.setBackground(btn.getBackground());
1835 button.setIcon(btn.getIcon());
1836 button.setText(btn.getText());
1841 button.setIcon(null);
1842 button.setBackground(currentColor.getColour());
1849 * The cell editor for the Filter column. It displays the text of any filters
1850 * for the feature type in that row (in full as a tooltip, possible abbreviated
1851 * as display text). On click in the cell, opens the Feature Display Settings
1852 * dialog at the Filters tab.
1854 @SuppressWarnings("serial")
1855 class FilterEditor extends AbstractCellEditor
1856 implements TableCellEditor, ActionListener
1859 FeatureMatcherSetI currentFilter;
1867 protected static final String EDIT = "edit";
1869 int rowSelected = 0;
1871 public FilterEditor()
1873 button = new JButton();
1874 button.setActionCommand(EDIT);
1875 button.addActionListener(this);
1876 button.setBorderPainted(false);
1880 * Handles events from the editor button
1883 public void actionPerformed(ActionEvent e)
1885 if (button == e.getSource())
1887 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
1888 chooser.addActionListener(this);
1889 chooser.setRequestFocusEnabled(true);
1890 chooser.requestFocus();
1891 if (lastLocation != null)
1893 // todo open at its last position on screen
1894 chooser.setBounds(lastLocation.x, lastLocation.y,
1895 chooser.getWidth(), chooser.getHeight());
1898 fireEditingStopped();
1900 else if (e.getSource() instanceof Component)
1904 * after OK in variable colour dialog, any changes to filter
1905 * (or colours!) are already set in FeatureRenderer, so just
1906 * update table data without triggering updateFeatureRenderer
1908 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1909 currentFilter = fr.getFeatureFilter(type);
1910 if (currentFilter == null)
1912 currentFilter = new FeatureMatcherSet();
1915 Object[] data = ((FeatureTableModel) table.getModel())
1916 .getData()[rowSelected];
1917 data[COLOUR_COLUMN] = currentColor;
1918 data[FILTER_COLUMN] = currentFilter;
1919 fireEditingStopped();
1920 // SwingJS needs an explicit repaint() here,
1921 // rather than relying upon no validation having
1922 // occurred since the stopEditing call was made.
1923 // Its laying out has not been stopped by the modal frame
1930 public Object getCellEditorValue()
1932 return currentFilter;
1936 public Component getTableCellEditorComponent(JTable theTable, Object value,
1937 boolean isSelected, int row, int column)
1939 currentFilter = (FeatureMatcherSetI) value;
1940 this.rowSelected = row;
1941 type = table.getValueAt(row, TYPE_COLUMN).toString();
1942 button.setOpaque(true);
1943 button.setBackground(FeatureSettings.this.getBackground());
1944 button.setText(currentFilter.toString());
1945 button.setIcon(null);
1951 class FeatureIcon implements Icon
1953 FeatureColourI gcol;
1957 boolean midspace = false;
1959 int width = 50, height = 20;
1961 int s1, e1; // start and end of midpoint band for thresholded symbol
1963 Color mpcolour = Color.white;
1965 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1985 public int getIconWidth()
1991 public int getIconHeight()
1997 public void paintIcon(Component c, Graphics g, int x, int y)
2000 if (gcol.isColourByLabel())
2003 g.fillRect(0, 0, width, height);
2004 // need an icon here.
2005 g.setColor(gcol.getMaxColour());
2007 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2009 // g.setFont(g.getFont().deriveFont(
2010 // AffineTransform.getScaleInstance(
2011 // width/g.getFontMetrics().stringWidth("Label"),
2012 // height/g.getFontMetrics().getHeight())));
2014 g.drawString(MessageManager.getString("label.label"), 0, 0);
2019 Color minCol = gcol.getMinColour();
2021 g.fillRect(0, 0, s1, height);
2024 g.setColor(Color.white);
2025 g.fillRect(s1, 0, e1 - s1, height);
2027 g.setColor(gcol.getMaxColour());
2028 // g.fillRect(0, e1, width - e1, height); // BH 2018
2029 g.fillRect(e1, 0, width - e1, height);