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.plaf.TableUI;
102 import javax.swing.table.AbstractTableModel;
103 import javax.swing.table.TableCellEditor;
104 import javax.swing.table.TableCellRenderer;
105 import javax.swing.table.TableColumn;
106 import javax.xml.bind.JAXBContext;
107 import javax.xml.bind.JAXBElement;
108 import javax.xml.bind.Marshaller;
109 import javax.xml.stream.XMLInputFactory;
110 import javax.xml.stream.XMLStreamReader;
112 public class FeatureSettings extends JPanel
113 implements FeatureSettingsControllerI
115 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
116 .getString("label.sequence_feature_colours");
119 * column indices of fields in Feature Settings table
121 static final int TYPE_COLUMN = 0;
123 static final int COLOUR_COLUMN = 1;
125 static final int FILTER_COLUMN = 2;
127 static final int SHOW_COLUMN = 3;
129 private static final int COLUMN_COUNT = 4;
131 private static final int MIN_WIDTH = 400;
133 private static final int MIN_HEIGHT = 400;
135 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
137 final FeatureRenderer fr;
139 public final AlignFrame af;
142 * 'original' fields hold settings to restore on Cancel
144 Object[][] originalData;
146 float originalTransparency;
148 Map<String, FeatureMatcherSetI> originalFilters;
150 final JInternalFrame frame;
152 JScrollPane scrollPane = new JScrollPane();
158 JSlider transparency = new JSlider();
161 * when true, constructor is still executing - so ignore UI events
163 protected volatile boolean inConstruction = true;
165 int selectedRow = -1;
167 JButton fetchDAS = new JButton();
169 JButton saveDAS = new JButton();
171 JButton cancelDAS = new JButton();
173 boolean resettingTable = false;
176 * true when Feature Settings are updating from feature renderer
178 boolean handlingUpdate = false;
181 * holds {featureCount, totalExtent} for each feature type
183 Map<String, float[]> typeWidth = null;
190 public FeatureSettings(AlignFrame alignFrame)
192 this.af = alignFrame;
193 fr = af.getFeatureRenderer();
195 // save transparency for restore on Cancel
196 originalTransparency = fr.getTransparency();
197 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
198 transparency.setMaximum(100 - originalTransparencyAsPercent);
200 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
205 } catch (Exception ex)
207 ex.printStackTrace();
214 // public void repaint() {
215 // System.out.println("FS repaint");
221 // public void repaint(long tm, int x, int y, int width, int height) {
222 // System.out.println("FS repaint " + x + " " + y + " " + width + " " + height);
223 // super.repaint(tm, x, y, width, height);
227 // public void invalidate() {
229 // System.out.println("FS invalidating ");
230 // super.invalidate();
234 // public void validate() {
235 // System.out.println("FS validating " + isValid());
240 public String getToolTipText(MouseEvent e)
243 int column = table.columnAtPoint(e.getPoint());
244 int row = table.rowAtPoint(e.getPoint());
249 tip = JvSwingUtils.wrapTooltip(true, MessageManager
250 .getString("label.feature_settings_click_drag"));
253 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
255 tip = getColorTooltip(colour, true);
258 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
261 ? MessageManager.getString("label.filters_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 if (Platform.isAMac())
408 Desktop.addInternalFrame(frame,
409 MessageManager.getString("label.sequence_feature_settings"),
414 Desktop.addInternalFrame(frame,
415 MessageManager.getString("label.sequence_feature_settings"),
418 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
420 frame.addInternalFrameListener(
421 new javax.swing.event.InternalFrameAdapter()
424 public void internalFrameClosed(
425 javax.swing.event.InternalFrameEvent evt)
427 fr.removePropertyChangeListener(change);
430 frame.setLayer(JLayeredPane.PALETTE_LAYER);
431 inConstruction = false;
435 * Constructs and shows a popup menu of possible actions on the selected row and
443 protected void showPopupMenu(final int rowSelected, final String type,
444 final Object typeCol, final Point pt)
446 final FeatureColourI featureColour = (FeatureColourI) typeCol;
448 JPopupMenu men = new JPopupMenu(MessageManager
449 .formatMessage("label.settings_for_param", new String[]
451 JMenuItem scr = new JMenuItem(
452 MessageManager.getString("label.sort_by_score"));
454 final FeatureSettings me = this;
455 scr.addActionListener(new ActionListener()
459 public void actionPerformed(ActionEvent e)
462 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
466 JMenuItem dens = new JMenuItem(
467 MessageManager.getString("label.sort_by_density"));
468 dens.addActionListener(new ActionListener()
472 public void actionPerformed(ActionEvent e)
475 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
482 * variable colour options include colour by label, by score,
483 * by selected attribute text, or attribute value
485 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
486 MessageManager.getString("label.variable_colour"));
487 variableColourCB.setSelected(!featureColour.isSimpleColour());
488 men.add(variableColourCB);
491 * checkbox action listener doubles up as listener to OK
492 * from the variable colour / filters dialog
494 variableColourCB.addActionListener(new ActionListener()
497 public void actionPerformed(ActionEvent e)
499 if (e.getSource() == variableColourCB)
501 men.setVisible(true); // BH 2018 for JavaScript because this is a checkbox
502 men.setVisible(false); // BH 2018 for JavaScript because this is a checkbox
503 if (featureColour.isSimpleColour())
506 * toggle simple colour to variable colour - show dialog
508 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
509 fc.addActionListener(this);
514 * toggle variable to simple colour - show colour chooser
516 String title = MessageManager.formatMessage("label.select_colour_for", type);
517 ColourChooserListener listener = new ColourChooserListener()
520 public void colourSelected(Color c)
522 table.setValueAt(new FeatureColour(c), rowSelected,
525 me.updateFeatureRenderer(
526 ((FeatureTableModel) table.getModel()).getData(),
530 JalviewColourChooser.showColourChooser(me, title, featureColour.getMaxColour(), listener);
535 if (e.getSource() instanceof FeatureTypeSettings)
538 * update after OK in feature colour dialog; the updated
539 * colour will have already been set in the FeatureRenderer
541 FeatureColourI fci = fr.getFeatureColours().get(type);
542 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
543 // BH 2018 setting a table value does not invalidate it.
544 System.out.println("FeatureSettings is valied" + table.isValid());
551 JMenuItem selCols = new JMenuItem(
552 MessageManager.getString("label.select_columns_containing"));
553 selCols.addActionListener(new ActionListener()
556 public void actionPerformed(ActionEvent arg0)
558 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
562 JMenuItem clearCols = new JMenuItem(MessageManager
563 .getString("label.select_columns_not_containing"));
564 clearCols.addActionListener(new ActionListener()
567 public void actionPerformed(ActionEvent arg0)
569 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
573 JMenuItem hideCols = new JMenuItem(
574 MessageManager.getString("label.hide_columns_containing"));
575 hideCols.addActionListener(new ActionListener()
578 public void actionPerformed(ActionEvent arg0)
580 fr.ap.alignFrame.hideFeatureColumns(type, true);
583 JMenuItem hideOtherCols = new JMenuItem(
584 MessageManager.getString("label.hide_columns_not_containing"));
585 hideOtherCols.addActionListener(new ActionListener()
588 public void actionPerformed(ActionEvent arg0)
590 fr.ap.alignFrame.hideFeatureColumns(type, false);
596 men.add(hideOtherCols);
597 men.show(table, pt.x, pt.y);
601 synchronized public void discoverAllFeatureData()
603 Set<String> allGroups = new HashSet<>();
604 AlignmentI alignment = af.getViewport().getAlignment();
606 for (int i = 0; i < alignment.getHeight(); i++)
608 SequenceI seq = alignment.getSequenceAt(i);
609 for (String group : seq.getFeatures().getFeatureGroups(true))
611 if (group != null && !allGroups.contains(group))
613 allGroups.add(group);
614 checkGroupState(group);
625 * Synchronise gui group list and check visibility of group
628 * @return true if group is visible
630 private boolean checkGroupState(String group)
632 boolean visible = fr.checkGroupVisibility(group, true);
634 for (int g = 0; g < groupPanel.getComponentCount(); g++)
636 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
638 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
643 final String grp = group;
644 final JCheckBox check = new JCheckBox(group, visible);
645 check.setFont(new Font("Serif", Font.BOLD, 12));
646 check.setToolTipText(group);
647 check.addItemListener(new ItemListener()
650 public void itemStateChanged(ItemEvent evt)
652 fr.setGroupVisibility(check.getText(), check.isSelected());
653 resetTable(new String[] { grp });
654 af.alignPanel.paintAlignment(true, true);
657 groupPanel.add(check);
661 synchronized void resetTable(String[] groupChanged)
667 resettingTable = true;
668 typeWidth = new Hashtable<>();
669 // TODO: change avWidth calculation to 'per-sequence' average and use long
672 Set<String> displayableTypes = new HashSet<>();
673 Set<String> foundGroups = new HashSet<>();
676 * determine which feature types may be visible depending on
677 * which groups are selected, and recompute average width data
679 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
682 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
685 * get the sequence's groups for positional features
686 * and keep track of which groups are visible
688 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
689 Set<String> visibleGroups = new HashSet<>();
690 for (String group : groups)
692 if (group == null || checkGroupState(group))
694 visibleGroups.add(group);
697 foundGroups.addAll(groups);
700 * get distinct feature types for visible groups
701 * record distinct visible types, and their count and total length
703 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
704 visibleGroups.toArray(new String[visibleGroups.size()]));
705 for (String type : types)
707 displayableTypes.add(type);
708 float[] avWidth = typeWidth.get(type);
711 avWidth = new float[2];
712 typeWidth.put(type, avWidth);
714 // todo this could include features with a non-visible group
715 // - do we greatly care?
716 // todo should we include non-displayable features here, and only
717 // update when features are added?
718 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
719 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
723 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
726 if (fr.hasRenderOrder())
730 fr.findAllFeatures(groupChanged != null); // prod to update
731 // colourschemes. but don't
733 // First add the checks in the previous render order,
734 // in case the window has been closed and reopened
736 List<String> frl = fr.getRenderOrder();
737 for (int ro = frl.size() - 1; ro > -1; ro--)
739 String type = frl.get(ro);
741 if (!displayableTypes.contains(type))
746 data[dataIndex][TYPE_COLUMN] = type;
747 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
748 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
749 data[dataIndex][FILTER_COLUMN] = featureFilter == null
750 ? new FeatureMatcherSet()
752 data[dataIndex][SHOW_COLUMN] = new Boolean(
753 af.getViewport().getFeaturesDisplayed().isVisible(type));
755 displayableTypes.remove(type);
760 * process any extra features belonging only to
761 * a group which was just selected
763 while (!displayableTypes.isEmpty())
765 String type = displayableTypes.iterator().next();
766 data[dataIndex][TYPE_COLUMN] = type;
768 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
769 if (data[dataIndex][COLOUR_COLUMN] == null)
771 // "Colour has been updated in another view!!"
772 fr.clearRenderOrder();
775 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
776 data[dataIndex][FILTER_COLUMN] = featureFilter == null
777 ? new FeatureMatcherSet()
779 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
781 displayableTypes.remove(type);
784 if (originalData == null)
786 originalData = new Object[data.length][COLUMN_COUNT];
787 for (int i = 0; i < data.length; i++)
789 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
794 updateOriginalData(data);
797 table.setModel(new FeatureTableModel(data));
798 table.getColumnModel().getColumn(0).setPreferredWidth(200);
800 groupPanel.setLayout(
801 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
802 pruneGroups(foundGroups);
803 groupPanel.validate();
805 updateFeatureRenderer(data, groupChanged != null);
806 resettingTable = false;
810 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
811 * have been made outwith this dialog
813 * <li>a new feature type added (and made visible)</li>
814 * <li>a feature colour changed (in the Amend Features dialog)</li>
819 protected void updateOriginalData(Object[][] foundData)
821 // todo LinkedHashMap instead of Object[][] would be nice
823 Object[][] currentData = ((FeatureTableModel) table.getModel())
825 for (Object[] row : foundData)
827 String type = (String) row[TYPE_COLUMN];
828 boolean found = false;
829 for (Object[] current : currentData)
831 if (type.equals(current[TYPE_COLUMN]))
835 * currently dependent on object equality here;
836 * really need an equals method on FeatureColour
838 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
841 * feature colour has changed externally - update originalData
843 for (Object[] original : originalData)
845 if (type.equals(original[TYPE_COLUMN]))
847 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
858 * new feature detected - add to original data (on top)
860 Object[][] newData = new Object[originalData.length
862 for (int i = 0; i < originalData.length; i++)
864 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
868 originalData = newData;
874 * Remove from the groups panel any checkboxes for groups that are not in the
875 * foundGroups set. This enables removing a group from the display when the last
876 * feature in that group is deleted.
880 protected void pruneGroups(Set<String> foundGroups)
882 for (int g = 0; g < groupPanel.getComponentCount(); g++)
884 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
885 if (!foundGroups.contains(checkbox.getText()))
887 groupPanel.remove(checkbox);
893 * reorder data based on the featureRenderers global priority list.
897 private void ensureOrder(Object[][] data)
899 boolean sort = false;
900 float[] order = new float[data.length];
901 for (int i = 0; i < order.length; i++)
903 order[i] = fr.getOrder(data[i][0].toString());
906 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
910 sort = sort || order[i - 1] > order[i];
915 jalview.util.QuickSort.sort(order, data);
920 * Offers a file chooser dialog, and then loads the feature colours and
921 * filters from file in XML format and unmarshals to Jalview feature settings
925 JalviewFileChooser chooser = new JalviewFileChooser("fc",
926 SEQUENCE_FEATURE_COLOURS);
927 chooser.setFileView(new JalviewFileView());
928 chooser.setDialogTitle(
929 MessageManager.getString("label.load_feature_colours"));
930 chooser.setToolTipText(MessageManager.getString("action.load"));
931 chooser.setResponseHandler(0, new Runnable()
936 File file = chooser.getSelectedFile();
940 chooser.showOpenDialog(this);
944 * Loads feature colours and filters from XML stored in the given file
952 InputStreamReader in = new InputStreamReader(
953 new FileInputStream(file), "UTF-8");
955 JAXBContext jc = JAXBContext
956 .newInstance("jalview.xml.binding.jalview");
957 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
958 XMLStreamReader streamReader = XMLInputFactory.newInstance()
959 .createXMLStreamReader(in);
960 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
961 JalviewUserColours.class);
962 JalviewUserColours jucs = jbe.getValue();
964 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
967 * load feature colours
969 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
971 Colour newcol = jucs.getColour().get(i);
972 FeatureColourI colour = jalview.project.Jalview2XML
973 .parseColour(newcol);
974 fr.setColour(newcol.getName(), colour);
975 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
979 * load feature filters; loaded filters will replace any that are
980 * currently defined, other defined filters are left unchanged
982 for (int i = 0; i < jucs.getFilter().size(); i++)
984 Filter filterModel = jucs.getFilter().get(i);
985 String featureType = filterModel.getFeatureType();
986 FeatureMatcherSetI filter = jalview.project.Jalview2XML
987 .parseFilter(featureType, filterModel.getMatcherSet());
988 if (!filter.isEmpty())
990 fr.setFeatureFilter(featureType, filter);
995 * update feature settings table
1000 Object[][] data = ((FeatureTableModel) table.getModel())
1003 updateFeatureRenderer(data, false);
1006 } catch (Exception ex)
1008 System.out.println("Error loading User Colour File\n" + ex);
1013 * Offers a file chooser dialog, and then saves the current feature colours
1014 * and any filters to the selected file in XML format
1018 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1019 SEQUENCE_FEATURE_COLOURS);
1020 chooser.setFileView(new JalviewFileView());
1021 chooser.setDialogTitle(
1022 MessageManager.getString("label.save_feature_colours"));
1023 chooser.setToolTipText(MessageManager.getString("action.save"));
1024 int option = chooser.showSaveDialog(this);
1025 if (option == JalviewFileChooser.APPROVE_OPTION)
1027 File file = chooser.getSelectedFile();
1033 * Saves feature colours and filters to the given file
1037 void save(File file)
1039 JalviewUserColours ucs = new JalviewUserColours();
1040 ucs.setSchemeName("Sequence Features");
1043 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1044 new FileOutputStream(file), "UTF-8"));
1047 * sort feature types by colour order, from 0 (highest)
1050 Set<String> fr_colours = fr.getAllFeatureColours();
1051 String[] sortedTypes = fr_colours
1052 .toArray(new String[fr_colours.size()]);
1053 Arrays.sort(sortedTypes, new Comparator<String>()
1056 public int compare(String type1, String type2)
1058 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1063 * save feature colours
1065 for (String featureType : sortedTypes)
1067 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1068 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1070 ucs.getColour().add(col);
1074 * save any feature filters
1076 for (String featureType : sortedTypes)
1078 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1079 if (filter != null && !filter.isEmpty())
1081 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1082 FeatureMatcherI firstMatcher = iterator.next();
1083 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1084 .marshalFilter(firstMatcher, iterator,
1086 Filter filterModel = new Filter();
1087 filterModel.setFeatureType(featureType);
1088 filterModel.setMatcherSet(ms);
1089 ucs.getFilter().add(filterModel);
1092 JAXBContext jaxbContext = JAXBContext
1093 .newInstance(JalviewUserColours.class);
1094 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1095 jaxbMarshaller.marshal(
1096 new ObjectFactory().createJalviewUserColours(ucs), out);
1098 // jaxbMarshaller.marshal(object, pout);
1099 // marshaller.marshal(object);
1102 // ucs.marshal(out);
1104 } catch (Exception ex)
1106 ex.printStackTrace();
1110 public void invertSelection()
1112 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1113 for (int i = 0; i < data.length; i++)
1115 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1117 updateFeatureRenderer(data, true);
1121 public void orderByAvWidth()
1123 if (table == null || table.getModel() == null)
1127 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1128 float[] width = new float[data.length];
1132 for (int i = 0; i < data.length; i++)
1134 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1137 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1138 // weight - but have to make per
1139 // sequence, too (awidth[2])
1140 // if (width[i]==1) // hack to distinguish single width sequences.
1151 boolean sort = false;
1152 for (int i = 0; i < width.length; i++)
1154 // awidth = (float[]) typeWidth.get(data[i][0]);
1157 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1160 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1166 width[i] /= max; // normalize
1167 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1171 sort = sort || width[i - 1] > width[i];
1176 jalview.util.QuickSort.sort(width, data);
1177 // update global priority order
1180 updateFeatureRenderer(data, false);
1188 frame.setClosed(true);
1189 } catch (Exception exe)
1195 public void updateFeatureRenderer(Object[][] data)
1197 updateFeatureRenderer(data, true);
1201 * Update the priority order of features; only repaint if this changed the order
1202 * of visible features
1207 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1209 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1211 if (fr.setFeaturePriority(rowData, visibleNew))
1213 af.alignPanel.paintAlignment(true, true);
1218 * Converts table data into an array of data beans
1220 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1222 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1223 for (int i = 0; i < data.length; i++)
1225 String type = (String) data[i][TYPE_COLUMN];
1226 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1227 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1228 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1229 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1235 private void jbInit() throws Exception
1237 this.setLayout(new BorderLayout());
1239 JPanel settingsPane = new JPanel();
1240 settingsPane.setLayout(new BorderLayout());
1242 JPanel bigPanel = new JPanel();
1243 bigPanel.setLayout(new BorderLayout());
1245 groupPanel = new JPanel();
1246 bigPanel.add(groupPanel, BorderLayout.NORTH);
1248 JButton invert = new JButton(
1249 MessageManager.getString("label.invert_selection"));
1250 invert.setFont(JvSwingUtils.getLabelFont());
1251 invert.addActionListener(new ActionListener()
1254 public void actionPerformed(ActionEvent e)
1260 JButton optimizeOrder = new JButton(
1261 MessageManager.getString("label.optimise_order"));
1262 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1263 optimizeOrder.addActionListener(new ActionListener()
1266 public void actionPerformed(ActionEvent e)
1272 JButton sortByScore = new JButton(
1273 MessageManager.getString("label.seq_sort_by_score"));
1274 sortByScore.setFont(JvSwingUtils.getLabelFont());
1275 sortByScore.addActionListener(new ActionListener()
1278 public void actionPerformed(ActionEvent e)
1280 af.avc.sortAlignmentByFeatureScore(null);
1283 JButton sortByDens = new JButton(
1284 MessageManager.getString("label.sequence_sort_by_density"));
1285 sortByDens.setFont(JvSwingUtils.getLabelFont());
1286 sortByDens.addActionListener(new ActionListener()
1289 public void actionPerformed(ActionEvent e)
1291 af.avc.sortAlignmentByFeatureDensity(null);
1295 JButton help = new JButton(MessageManager.getString("action.help"));
1296 help.setFont(JvSwingUtils.getLabelFont());
1297 help.addActionListener(new ActionListener()
1300 public void actionPerformed(ActionEvent e)
1304 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1305 } catch (HelpSetException e1)
1307 e1.printStackTrace();
1311 help.setFont(JvSwingUtils.getLabelFont());
1312 help.setText(MessageManager.getString("action.help"));
1313 help.addActionListener(new ActionListener()
1316 public void actionPerformed(ActionEvent e)
1320 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1321 } catch (HelpSetException e1)
1323 e1.printStackTrace();
1328 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1329 cancel.setFont(JvSwingUtils.getLabelFont());
1330 cancel.addActionListener(new ActionListener()
1333 public void actionPerformed(ActionEvent e)
1335 fr.setTransparency(originalTransparency);
1336 fr.setFeatureFilters(originalFilters);
1337 updateFeatureRenderer(originalData);
1342 JButton ok = new JButton(MessageManager.getString("action.ok"));
1343 ok.setFont(JvSwingUtils.getLabelFont());
1344 ok.addActionListener(new ActionListener()
1347 public void actionPerformed(ActionEvent e)
1353 JButton loadColours = new JButton(
1354 MessageManager.getString("label.load_colours"));
1355 loadColours.setFont(JvSwingUtils.getLabelFont());
1356 loadColours.setToolTipText(
1357 MessageManager.getString("label.load_colours_tooltip"));
1358 loadColours.addActionListener(new ActionListener()
1361 public void actionPerformed(ActionEvent e)
1367 JButton saveColours = new JButton(
1368 MessageManager.getString("label.save_colours"));
1369 saveColours.setFont(JvSwingUtils.getLabelFont());
1370 saveColours.setToolTipText(
1371 MessageManager.getString("label.save_colours_tooltip"));
1372 saveColours.addActionListener(new ActionListener()
1375 public void actionPerformed(ActionEvent e)
1380 transparency.addChangeListener(new ChangeListener()
1383 public void stateChanged(ChangeEvent evt)
1385 if (!inConstruction)
1387 fr.setTransparency((100 - transparency.getValue()) / 100f);
1388 af.alignPanel.paintAlignment(true, true);
1393 transparency.setMaximum(70);
1394 transparency.setToolTipText(
1395 MessageManager.getString("label.transparency_tip"));
1397 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1398 bigPanel.add(transPanel, BorderLayout.SOUTH);
1400 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1401 transbuttons.add(optimizeOrder);
1402 transbuttons.add(invert);
1403 transbuttons.add(sortByScore);
1404 transbuttons.add(sortByDens);
1405 transbuttons.add(help);
1406 transPanel.add(transparency);
1407 transPanel.add(transbuttons);
1409 JPanel buttonPanel = new JPanel();
1410 buttonPanel.add(ok);
1411 buttonPanel.add(cancel);
1412 buttonPanel.add(loadColours);
1413 buttonPanel.add(saveColours);
1414 bigPanel.add(scrollPane, BorderLayout.CENTER);
1415 settingsPane.add(bigPanel, BorderLayout.CENTER);
1416 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1417 this.add(settingsPane);
1421 * Answers a suitable tooltip to show on the colour cell of the table
1425 * if true include 'click to edit' and similar text
1428 public static String getColorTooltip(FeatureColourI fcol,
1435 if (fcol.isSimpleColour())
1437 return withHint ? BASE_TOOLTIP : null;
1439 String description = fcol.getDescription();
1440 description = description.replaceAll("<", "<");
1441 description = description.replaceAll(">", ">");
1442 StringBuilder tt = new StringBuilder(description);
1445 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1447 return JvSwingUtils.wrapTooltip(true, tt.toString());
1450 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1453 boolean thr = false;
1454 StringBuilder tx = new StringBuilder();
1456 if (gcol.isColourByAttribute())
1458 tx.append(FeatureMatcher
1459 .toAttributeDisplayName(gcol.getAttributeName()));
1461 else if (!gcol.isColourByLabel())
1463 tx.append(MessageManager.getString("label.score"));
1466 if (gcol.isAboveThreshold())
1471 if (gcol.isBelowThreshold())
1476 if (gcol.isColourByLabel())
1482 if (!gcol.isColourByAttribute())
1490 Color newColor = gcol.getMaxColour();
1491 comp.setBackground(newColor);
1492 // System.err.println("Width is " + w / 2);
1493 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1494 comp.setIcon(ficon);
1495 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1496 // + newColor.getGreen() + ", " + newColor.getBlue()
1497 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1498 // + ", " + minCol.getBlue() + ")");
1500 comp.setHorizontalAlignment(SwingConstants.CENTER);
1501 comp.setText(tx.toString());
1504 // ///////////////////////////////////////////////////////////////////////
1505 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1506 // ///////////////////////////////////////////////////////////////////////
1507 class FeatureTableModel extends AbstractTableModel
1509 private String[] columnNames = {
1510 MessageManager.getString("label.feature_type"),
1511 MessageManager.getString("action.colour"),
1512 MessageManager.getString("label.filter"),
1513 MessageManager.getString("label.show") };
1515 private Object[][] data;
1517 FeatureTableModel(Object[][] data)
1522 public Object[][] getData()
1527 public void setData(Object[][] data)
1533 public int getColumnCount()
1535 return columnNames.length;
1538 public Object[] getRow(int row)
1544 public int getRowCount()
1550 public String getColumnName(int col)
1552 return columnNames[col];
1556 public Object getValueAt(int row, int col)
1558 return data[row][col];
1562 * Answers the class of the object in column c of the first row of the table
1565 public Class<?> getColumnClass(int c)
1567 Object v = getValueAt(0, c);
1568 return v == null ? null : v.getClass();
1572 public boolean isCellEditable(int row, int col)
1574 return col == 0 ? false : true;
1578 public void setValueAt(Object value, int row, int col)
1580 data[row][col] = value;
1581 fireTableCellUpdated(row, col);
1582 updateFeatureRenderer(data);
1587 class ColorRenderer extends JLabel implements TableCellRenderer
1589 Border unselectedBorder = null;
1591 Border selectedBorder = null;
1593 public ColorRenderer()
1595 setOpaque(true); // MUST do this for background to show up.
1596 setHorizontalTextPosition(SwingConstants.CENTER);
1597 setVerticalTextPosition(SwingConstants.CENTER);
1601 public Component getTableCellRendererComponent(JTable tbl, Object color,
1602 boolean isSelected, boolean hasFocus, int row, int column)
1604 FeatureColourI cellColour = (FeatureColourI) color;
1606 setBackground(tbl.getBackground());
1607 if (!cellColour.isSimpleColour())
1609 Rectangle cr = tbl.getCellRect(row, column, false);
1610 FeatureSettings.renderGraduatedColor(this, cellColour,
1611 (int) cr.getWidth(), (int) cr.getHeight());
1617 setBackground(cellColour.getColour());
1621 if (selectedBorder == null)
1623 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1624 tbl.getSelectionBackground());
1626 setBorder(selectedBorder);
1630 if (unselectedBorder == null)
1632 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1633 tbl.getBackground());
1635 setBorder(unselectedBorder);
1642 class FilterRenderer extends JLabel implements TableCellRenderer
1644 javax.swing.border.Border unselectedBorder = null;
1646 javax.swing.border.Border selectedBorder = null;
1648 public FilterRenderer()
1650 setOpaque(true); // MUST do this for background to show up.
1651 setHorizontalTextPosition(SwingConstants.CENTER);
1652 setVerticalTextPosition(SwingConstants.CENTER);
1656 public Component getTableCellRendererComponent(JTable tbl,
1657 Object filter, boolean isSelected, boolean hasFocus, int row,
1660 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1662 String asText = theFilter.toString();
1663 setBackground(tbl.getBackground());
1664 this.setText(asText);
1669 if (selectedBorder == null)
1671 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1672 tbl.getSelectionBackground());
1674 setBorder(selectedBorder);
1678 if (unselectedBorder == null)
1680 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1681 tbl.getBackground());
1683 setBorder(unselectedBorder);
1691 * update comp using rendering settings from gcol
1696 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1698 int w = comp.getWidth(), h = comp.getHeight();
1701 w = (int) comp.getPreferredSize().getWidth();
1702 h = (int) comp.getPreferredSize().getHeight();
1709 renderGraduatedColor(comp, gcol, w, h);
1712 @SuppressWarnings("serial")
1713 class ColorEditor extends AbstractCellEditor
1714 implements TableCellEditor, ActionListener
1716 // FeatureSettings me;
1718 FeatureColourI currentColor;
1720 FeatureTypeSettings chooser;
1726 protected static final String EDIT = "edit";
1728 int rowSelected = 0;
1730 public ColorEditor()
1732 // Set up the editor (from the table's point of view),
1733 // which is a button.
1734 // This button brings up the color chooser dialog,
1735 // which is the editor from the user's point of view.
1736 button = new JButton();
1737 button.setActionCommand(EDIT);
1738 button.addActionListener(this);
1739 button.setBorderPainted(false);
1743 * Handles events from the editor button, and from the colour/filters
1744 * dialog's OK button
1747 public void actionPerformed(ActionEvent e)
1749 if (button == e.getSource())
1751 if (currentColor.isSimpleColour())
1754 * simple colour chooser
1756 String ttl = MessageManager.formatMessage("label.select_colour_for", type);
1757 ColourChooserListener listener = new ColourChooserListener()
1760 public void colourSelected(Color c)
1762 currentColor = new FeatureColour(c);
1763 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1764 fireEditingStopped();
1767 public void cancel()
1769 fireEditingStopped();
1772 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1777 * variable colour and filters dialog
1779 chooser = new FeatureTypeSettings(fr, type);
1780 if (!Jalview.isJS())
1782 chooser.setRequestFocusEnabled(true);
1783 chooser.requestFocus();
1785 chooser.addActionListener(this);
1786 fireEditingStopped();
1792 * after OK in variable colour dialog, any changes to colour
1793 * (or filters!) are already set in FeatureRenderer, so just
1794 * update table data without triggering updateFeatureRenderer
1796 currentColor = fr.getFeatureColours().get(type);
1797 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1798 if (currentFilter == null)
1800 currentFilter = new FeatureMatcherSet();
1802 Object[] data = ((FeatureTableModel) table.getModel())
1803 .getData()[rowSelected];
1804 data[COLOUR_COLUMN] = currentColor;
1805 data[FILTER_COLUMN] = currentFilter;
1806 fireEditingStopped();
1807 // SwingJS needs an explicit repaint() here,
1808 // rather than relying upon no validation having
1809 // occurred since the stopEditing call was made.
1810 // Its laying out has not been stopped by the modal frame
1816 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1818 public Object getCellEditorValue()
1820 return currentColor;
1823 // Implement the one method defined by TableCellEditor.
1825 public Component getTableCellEditorComponent(JTable theTable, Object value,
1826 boolean isSelected, int row, int column)
1828 currentColor = (FeatureColourI) value;
1829 this.rowSelected = row;
1830 type = table.getValueAt(row, TYPE_COLUMN).toString();
1831 button.setOpaque(true);
1832 button.setBackground(getBackground());
1833 if (!currentColor.isSimpleColour())
1835 JLabel btn = new JLabel();
1836 btn.setSize(button.getSize());
1837 FeatureSettings.renderGraduatedColor(btn, currentColor);
1838 button.setBackground(btn.getBackground());
1839 button.setIcon(btn.getIcon());
1840 button.setText(btn.getText());
1845 button.setIcon(null);
1846 button.setBackground(currentColor.getColour());
1853 * The cell editor for the Filter column. It displays the text of any filters
1854 * for the feature type in that row (in full as a tooltip, possible abbreviated
1855 * as display text). On click in the cell, opens the Feature Display Settings
1856 * dialog at the Filters tab.
1858 @SuppressWarnings("serial")
1859 class FilterEditor extends AbstractCellEditor
1860 implements TableCellEditor, ActionListener
1863 FeatureMatcherSetI currentFilter;
1871 protected static final String EDIT = "edit";
1873 int rowSelected = 0;
1875 public FilterEditor()
1877 button = new JButton();
1878 button.setActionCommand(EDIT);
1879 button.addActionListener(this);
1880 button.setBorderPainted(false);
1884 * Handles events from the editor button
1887 public void actionPerformed(ActionEvent e)
1889 if (button == e.getSource())
1891 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
1892 chooser.addActionListener(this);
1893 chooser.setRequestFocusEnabled(true);
1894 chooser.requestFocus();
1895 if (lastLocation != null)
1897 // todo open at its last position on screen
1898 chooser.setBounds(lastLocation.x, lastLocation.y,
1899 chooser.getWidth(), chooser.getHeight());
1902 fireEditingStopped();
1904 else if (e.getSource() instanceof Component)
1908 * after OK in variable colour dialog, any changes to filter
1909 * (or colours!) are already set in FeatureRenderer, so just
1910 * update table data without triggering updateFeatureRenderer
1912 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1913 currentFilter = fr.getFeatureFilter(type);
1914 if (currentFilter == null)
1916 currentFilter = new FeatureMatcherSet();
1919 Object[] data = ((FeatureTableModel) table.getModel())
1920 .getData()[rowSelected];
1921 data[COLOUR_COLUMN] = currentColor;
1922 data[FILTER_COLUMN] = currentFilter;
1923 fireEditingStopped();
1924 // SwingJS needs an explicit repaint() here,
1925 // rather than relying upon no validation having
1926 // occurred since the stopEditing call was made.
1927 // Its laying out has not been stopped by the modal frame
1934 public Object getCellEditorValue()
1936 return currentFilter;
1940 public Component getTableCellEditorComponent(JTable theTable, Object value,
1941 boolean isSelected, int row, int column)
1943 currentFilter = (FeatureMatcherSetI) value;
1944 this.rowSelected = row;
1945 type = table.getValueAt(row, TYPE_COLUMN).toString();
1946 button.setOpaque(true);
1947 button.setBackground(getBackground());
1948 button.setText(currentFilter.toString());
1949 button.setIcon(null);
1955 class FeatureIcon implements Icon
1957 FeatureColourI gcol;
1961 boolean midspace = false;
1963 int width = 50, height = 20;
1965 int s1, e1; // start and end of midpoint band for thresholded symbol
1967 Color mpcolour = Color.white;
1969 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1989 public int getIconWidth()
1995 public int getIconHeight()
2001 public void paintIcon(Component c, Graphics g, int x, int y)
2004 if (gcol.isColourByLabel())
2007 g.fillRect(0, 0, width, height);
2008 // need an icon here.
2009 g.setColor(gcol.getMaxColour());
2011 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2013 // g.setFont(g.getFont().deriveFont(
2014 // AffineTransform.getScaleInstance(
2015 // width/g.getFontMetrics().stringWidth("Label"),
2016 // height/g.getFontMetrics().getHeight())));
2018 g.drawString(MessageManager.getString("label.label"), 0, 0);
2023 Color minCol = gcol.getMinColour();
2025 g.fillRect(0, 0, s1, height);
2028 g.setColor(Color.white);
2029 g.fillRect(s1, 0, e1 - s1, height);
2031 g.setColor(gcol.getMaxColour());
2032 // g.fillRect(0, e1, width - e1, height); // BH 2018
2033 g.fillRect(e1, 0, width - e1, height);