2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.bin.Jalview;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.SequenceI;
28 import jalview.datamodel.features.FeatureMatcher;
29 import jalview.datamodel.features.FeatureMatcherI;
30 import jalview.datamodel.features.FeatureMatcherSet;
31 import jalview.datamodel.features.FeatureMatcherSetI;
32 import jalview.gui.Help.HelpId;
33 import jalview.gui.JalviewColourChooser.ColourChooserListener;
34 import jalview.io.JalviewFileChooser;
35 import jalview.io.JalviewFileView;
36 import jalview.schemes.FeatureColour;
37 import jalview.util.MessageManager;
38 import jalview.util.Platform;
39 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
40 import jalview.xml.binding.jalview.JalviewUserColours;
41 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
42 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
43 import jalview.xml.binding.jalview.ObjectFactory;
45 import java.awt.BorderLayout;
46 import java.awt.Color;
47 import java.awt.Component;
48 import java.awt.Dimension;
50 import java.awt.Graphics;
51 import java.awt.GridLayout;
52 import java.awt.Point;
53 import java.awt.Rectangle;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.ActionListener;
56 import java.awt.event.ItemEvent;
57 import java.awt.event.ItemListener;
58 import java.awt.event.MouseAdapter;
59 import java.awt.event.MouseEvent;
60 import java.awt.event.MouseMotionAdapter;
61 import java.beans.PropertyChangeEvent;
62 import java.beans.PropertyChangeListener;
64 import java.io.FileInputStream;
65 import java.io.FileOutputStream;
66 import java.io.InputStreamReader;
67 import java.io.OutputStreamWriter;
68 import java.io.PrintWriter;
69 import java.util.Arrays;
70 import java.util.Comparator;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.Hashtable;
74 import java.util.Iterator;
75 import java.util.List;
79 import javax.help.HelpSetException;
80 import javax.swing.AbstractCellEditor;
81 import javax.swing.BorderFactory;
82 import javax.swing.Icon;
83 import javax.swing.JButton;
84 import javax.swing.JCheckBox;
85 import javax.swing.JCheckBoxMenuItem;
86 import javax.swing.JInternalFrame;
87 import javax.swing.JLabel;
88 import javax.swing.JLayeredPane;
89 import javax.swing.JMenuItem;
90 import javax.swing.JPanel;
91 import javax.swing.JPopupMenu;
92 import javax.swing.JScrollPane;
93 import javax.swing.JSlider;
94 import javax.swing.JTable;
95 import javax.swing.ListSelectionModel;
96 import javax.swing.SwingConstants;
97 import javax.swing.ToolTipManager;
98 import javax.swing.border.Border;
99 import javax.swing.event.ChangeEvent;
100 import javax.swing.event.ChangeListener;
101 import javax.swing.table.AbstractTableModel;
102 import javax.swing.table.TableCellEditor;
103 import javax.swing.table.TableCellRenderer;
104 import javax.swing.table.TableColumn;
105 import javax.xml.bind.JAXBContext;
106 import javax.xml.bind.JAXBElement;
107 import javax.xml.bind.Marshaller;
108 import javax.xml.stream.XMLInputFactory;
109 import javax.xml.stream.XMLStreamReader;
111 public class FeatureSettings extends JPanel
112 implements FeatureSettingsControllerI
114 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
115 .getString("label.sequence_feature_colours");
118 * column indices of fields in Feature Settings table
120 static final int TYPE_COLUMN = 0;
122 static final int COLOUR_COLUMN = 1;
124 static final int FILTER_COLUMN = 2;
126 static final int SHOW_COLUMN = 3;
128 private static final int COLUMN_COUNT = 4;
130 private static final int MIN_WIDTH = 400;
132 private static final int MIN_HEIGHT = 400;
134 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
136 final FeatureRenderer fr;
138 public final AlignFrame af;
141 * 'original' fields hold settings to restore on Cancel
143 Object[][] originalData;
145 float originalTransparency;
147 Map<String, FeatureMatcherSetI> originalFilters;
149 final JInternalFrame frame;
151 JScrollPane scrollPane = new JScrollPane();
157 JSlider transparency = new JSlider();
160 * when true, constructor is still executing - so ignore UI events
162 protected volatile boolean inConstruction = true;
164 int selectedRow = -1;
166 JButton fetchDAS = new JButton();
168 JButton saveDAS = new JButton();
170 JButton cancelDAS = new JButton();
172 boolean resettingTable = false;
175 * true when Feature Settings are updating from feature renderer
177 boolean handlingUpdate = false;
180 * holds {featureCount, totalExtent} for each feature type
182 Map<String, float[]> typeWidth = null;
189 public FeatureSettings(AlignFrame alignFrame)
191 this.af = alignFrame;
192 fr = af.getFeatureRenderer();
194 // save transparency for restore on Cancel
195 originalTransparency = fr.getTransparency();
196 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
197 transparency.setMaximum(100 - originalTransparencyAsPercent);
199 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
204 } catch (Exception ex)
206 ex.printStackTrace();
213 // public void repaint() {
214 // System.out.println("FS repaint");
220 // public void repaint(long tm, int x, int y, int width, int height) {
221 // System.out.println("FS repaint " + x + " " + y + " " + width + " " + height);
222 // super.repaint(tm, x, y, width, height);
226 // public void invalidate() {
228 // System.out.println("FS invalidating ");
229 // super.invalidate();
233 // public void validate() {
234 // System.out.println("FS validating " + isValid());
239 public String getToolTipText(MouseEvent e)
242 int column = table.columnAtPoint(e.getPoint());
243 int row = table.rowAtPoint(e.getPoint());
248 tip = JvSwingUtils.wrapTooltip(true, MessageManager
249 .getString("label.feature_settings_click_drag"));
252 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
254 tip = getColorTooltip(colour, true);
257 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
260 ? MessageManager.getString("label.filters_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 if (Platform.isAMac())
407 Desktop.addInternalFrame(frame,
408 MessageManager.getString("label.sequence_feature_settings"),
413 Desktop.addInternalFrame(frame,
414 MessageManager.getString("label.sequence_feature_settings"),
417 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
419 frame.addInternalFrameListener(
420 new javax.swing.event.InternalFrameAdapter()
423 public void internalFrameClosed(
424 javax.swing.event.InternalFrameEvent evt)
426 fr.removePropertyChangeListener(change);
429 frame.setLayer(JLayeredPane.PALETTE_LAYER);
430 inConstruction = false;
434 * Constructs and shows a popup menu of possible actions on the selected row and
442 protected void showPopupMenu(final int rowSelected, final String type,
443 final Object typeCol, final Point pt)
445 JPopupMenu men = new JPopupMenu(MessageManager
446 .formatMessage("label.settings_for_param", new String[]
448 final FeatureColourI featureColour = (FeatureColourI) typeCol;
451 * menu option to select (or deselect) variable colour
453 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
454 MessageManager.getString("label.variable_colour"));
455 variableColourCB.setSelected(!featureColour.isSimpleColour());
456 men.add(variableColourCB);
459 * checkbox action listener doubles up as listener to OK
460 * from the variable colour / filters dialog
462 variableColourCB.addActionListener(new ActionListener()
465 public void actionPerformed(ActionEvent e)
467 if (e.getSource() == variableColourCB)
469 men.setVisible(true); // BH 2018 for JavaScript because this is a checkbox
470 men.setVisible(false); // BH 2018 for JavaScript because this is a checkbox
471 if (featureColour.isSimpleColour())
474 * toggle simple colour to variable colour - show dialog
476 FeatureTypeSettings fc = new FeatureTypeSettings(fr, type);
477 fc.addActionListener(this);
482 * toggle variable to simple colour - show colour chooser
484 String title = MessageManager.formatMessage("label.select_colour_for", type);
485 ColourChooserListener listener = new ColourChooserListener()
488 public void colourSelected(Color c)
490 table.setValueAt(new FeatureColour(c), rowSelected,
493 updateFeatureRenderer(
494 ((FeatureTableModel) table.getModel()).getData(),
498 JalviewColourChooser.showColourChooser(FeatureSettings.this, title,
499 featureColour.getMaxColour(), listener);
504 if (e.getSource() instanceof FeatureTypeSettings)
507 * update after OK in feature colour dialog; the updated
508 * colour will have already been set in the FeatureRenderer
510 FeatureColourI fci = fr.getFeatureColours().get(type);
511 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
512 // BH 2018 setting a table value does not invalidate it.
513 // System.out.println("FeatureSettings is valied" + table.isValid());
522 JMenuItem scr = new JMenuItem(
523 MessageManager.getString("label.sort_by_score"));
525 scr.addActionListener(new ActionListener()
529 public void actionPerformed(ActionEvent e)
531 af.avc.sortAlignmentByFeatureScore(Arrays.asList(new String[]
535 JMenuItem dens = new JMenuItem(
536 MessageManager.getString("label.sort_by_density"));
537 dens.addActionListener(new ActionListener()
541 public void actionPerformed(ActionEvent e)
543 af.avc.sortAlignmentByFeatureDensity(Arrays.asList(new String[]
549 JMenuItem selCols = new JMenuItem(
550 MessageManager.getString("label.select_columns_containing"));
551 selCols.addActionListener(new ActionListener()
554 public void actionPerformed(ActionEvent arg0)
556 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
560 JMenuItem clearCols = new JMenuItem(MessageManager
561 .getString("label.select_columns_not_containing"));
562 clearCols.addActionListener(new ActionListener()
565 public void actionPerformed(ActionEvent arg0)
567 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
571 JMenuItem hideCols = new JMenuItem(
572 MessageManager.getString("label.hide_columns_containing"));
573 hideCols.addActionListener(new ActionListener()
576 public void actionPerformed(ActionEvent arg0)
578 fr.ap.alignFrame.hideFeatureColumns(type, true);
581 JMenuItem hideOtherCols = new JMenuItem(
582 MessageManager.getString("label.hide_columns_not_containing"));
583 hideOtherCols.addActionListener(new ActionListener()
586 public void actionPerformed(ActionEvent arg0)
588 fr.ap.alignFrame.hideFeatureColumns(type, false);
594 men.add(hideOtherCols);
595 men.show(table, pt.x, pt.y);
599 synchronized public void discoverAllFeatureData()
601 Set<String> allGroups = new HashSet<>();
602 AlignmentI alignment = af.getViewport().getAlignment();
604 for (int i = 0; i < alignment.getHeight(); i++)
606 SequenceI seq = alignment.getSequenceAt(i);
607 for (String group : seq.getFeatures().getFeatureGroups(true))
609 if (group != null && !allGroups.contains(group))
611 allGroups.add(group);
612 checkGroupState(group);
623 * Synchronise gui group list and check visibility of group
626 * @return true if group is visible
628 private boolean checkGroupState(String group)
630 boolean visible = fr.checkGroupVisibility(group, true);
632 for (int g = 0; g < groupPanel.getComponentCount(); g++)
634 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
636 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
641 final String grp = group;
642 final JCheckBox check = new JCheckBox(group, visible);
643 check.setFont(new Font("Serif", Font.BOLD, 12));
644 check.setToolTipText(group);
645 check.addItemListener(new ItemListener()
648 public void itemStateChanged(ItemEvent evt)
650 fr.setGroupVisibility(check.getText(), check.isSelected());
651 resetTable(new String[] { grp });
652 af.alignPanel.paintAlignment(true, true);
655 groupPanel.add(check);
659 synchronized void resetTable(String[] groupChanged)
665 resettingTable = true;
666 typeWidth = new Hashtable<>();
667 // TODO: change avWidth calculation to 'per-sequence' average and use long
670 Set<String> displayableTypes = new HashSet<>();
671 Set<String> foundGroups = new HashSet<>();
674 * determine which feature types may be visible depending on
675 * which groups are selected, and recompute average width data
677 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
680 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
683 * get the sequence's groups for positional features
684 * and keep track of which groups are visible
686 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
687 Set<String> visibleGroups = new HashSet<>();
688 for (String group : groups)
690 if (group == null || checkGroupState(group))
692 visibleGroups.add(group);
695 foundGroups.addAll(groups);
698 * get distinct feature types for visible groups
699 * record distinct visible types, and their count and total length
701 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
702 visibleGroups.toArray(new String[visibleGroups.size()]));
703 for (String type : types)
705 displayableTypes.add(type);
706 float[] avWidth = typeWidth.get(type);
709 avWidth = new float[2];
710 typeWidth.put(type, avWidth);
712 // todo this could include features with a non-visible group
713 // - do we greatly care?
714 // todo should we include non-displayable features here, and only
715 // update when features are added?
716 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
717 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
721 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
724 if (fr.hasRenderOrder())
728 fr.findAllFeatures(groupChanged != null); // prod to update
729 // colourschemes. but don't
731 // First add the checks in the previous render order,
732 // in case the window has been closed and reopened
734 List<String> frl = fr.getRenderOrder();
735 for (int ro = frl.size() - 1; ro > -1; ro--)
737 String type = frl.get(ro);
739 if (!displayableTypes.contains(type))
744 data[dataIndex][TYPE_COLUMN] = type;
745 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
746 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
747 data[dataIndex][FILTER_COLUMN] = featureFilter == null
748 ? new FeatureMatcherSet()
750 data[dataIndex][SHOW_COLUMN] = new Boolean(
751 af.getViewport().getFeaturesDisplayed().isVisible(type));
753 displayableTypes.remove(type);
758 * process any extra features belonging only to
759 * a group which was just selected
761 while (!displayableTypes.isEmpty())
763 String type = displayableTypes.iterator().next();
764 data[dataIndex][TYPE_COLUMN] = type;
766 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
767 if (data[dataIndex][COLOUR_COLUMN] == null)
769 // "Colour has been updated in another view!!"
770 fr.clearRenderOrder();
773 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
774 data[dataIndex][FILTER_COLUMN] = featureFilter == null
775 ? new FeatureMatcherSet()
777 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
779 displayableTypes.remove(type);
782 if (originalData == null)
784 originalData = new Object[data.length][COLUMN_COUNT];
785 for (int i = 0; i < data.length; i++)
787 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
792 updateOriginalData(data);
795 table.setModel(new FeatureTableModel(data));
796 table.getColumnModel().getColumn(0).setPreferredWidth(200);
798 groupPanel.setLayout(
799 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
800 pruneGroups(foundGroups);
801 groupPanel.validate();
803 updateFeatureRenderer(data, groupChanged != null);
804 resettingTable = false;
808 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
809 * have been made outwith this dialog
811 * <li>a new feature type added (and made visible)</li>
812 * <li>a feature colour changed (in the Amend Features dialog)</li>
817 protected void updateOriginalData(Object[][] foundData)
819 // todo LinkedHashMap instead of Object[][] would be nice
821 Object[][] currentData = ((FeatureTableModel) table.getModel())
823 for (Object[] row : foundData)
825 String type = (String) row[TYPE_COLUMN];
826 boolean found = false;
827 for (Object[] current : currentData)
829 if (type.equals(current[TYPE_COLUMN]))
833 * currently dependent on object equality here;
834 * really need an equals method on FeatureColour
836 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
839 * feature colour has changed externally - update originalData
841 for (Object[] original : originalData)
843 if (type.equals(original[TYPE_COLUMN]))
845 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
856 * new feature detected - add to original data (on top)
858 Object[][] newData = new Object[originalData.length
860 for (int i = 0; i < originalData.length; i++)
862 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
866 originalData = newData;
872 * Remove from the groups panel any checkboxes for groups that are not in the
873 * foundGroups set. This enables removing a group from the display when the last
874 * feature in that group is deleted.
878 protected void pruneGroups(Set<String> foundGroups)
880 for (int g = 0; g < groupPanel.getComponentCount(); g++)
882 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
883 if (!foundGroups.contains(checkbox.getText()))
885 groupPanel.remove(checkbox);
891 * reorder data based on the featureRenderers global priority list.
895 private void ensureOrder(Object[][] data)
897 boolean sort = false;
898 float[] order = new float[data.length];
899 for (int i = 0; i < order.length; i++)
901 order[i] = fr.getOrder(data[i][0].toString());
904 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
908 sort = sort || order[i - 1] > order[i];
913 jalview.util.QuickSort.sort(order, data);
918 * Offers a file chooser dialog, and then loads the feature colours and
919 * filters from file in XML format and unmarshals to Jalview feature settings
923 JalviewFileChooser chooser = new JalviewFileChooser("fc",
924 SEQUENCE_FEATURE_COLOURS);
925 chooser.setFileView(new JalviewFileView());
926 chooser.setDialogTitle(
927 MessageManager.getString("label.load_feature_colours"));
928 chooser.setToolTipText(MessageManager.getString("action.load"));
929 chooser.setResponseHandler(0, new Runnable()
934 File file = chooser.getSelectedFile();
938 chooser.showOpenDialog(this);
942 * Loads feature colours and filters from XML stored in the given file
950 InputStreamReader in = new InputStreamReader(
951 new FileInputStream(file), "UTF-8");
953 JAXBContext jc = JAXBContext
954 .newInstance("jalview.xml.binding.jalview");
955 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
956 XMLStreamReader streamReader = XMLInputFactory.newInstance()
957 .createXMLStreamReader(in);
958 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
959 JalviewUserColours.class);
960 JalviewUserColours jucs = jbe.getValue();
962 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
965 * load feature colours
967 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
969 Colour newcol = jucs.getColour().get(i);
970 FeatureColourI colour = jalview.project.Jalview2XML
971 .parseColour(newcol);
972 fr.setColour(newcol.getName(), colour);
973 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
977 * load feature filters; loaded filters will replace any that are
978 * currently defined, other defined filters are left unchanged
980 for (int i = 0; i < jucs.getFilter().size(); i++)
982 Filter filterModel = jucs.getFilter().get(i);
983 String featureType = filterModel.getFeatureType();
984 FeatureMatcherSetI filter = jalview.project.Jalview2XML
985 .parseFilter(featureType, filterModel.getMatcherSet());
986 if (!filter.isEmpty())
988 fr.setFeatureFilter(featureType, filter);
993 * update feature settings table
998 Object[][] data = ((FeatureTableModel) table.getModel())
1001 updateFeatureRenderer(data, false);
1004 } catch (Exception ex)
1006 System.out.println("Error loading User Colour File\n" + ex);
1011 * Offers a file chooser dialog, and then saves the current feature colours
1012 * and any filters to the selected file in XML format
1016 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1017 SEQUENCE_FEATURE_COLOURS);
1018 chooser.setFileView(new JalviewFileView());
1019 chooser.setDialogTitle(
1020 MessageManager.getString("label.save_feature_colours"));
1021 chooser.setToolTipText(MessageManager.getString("action.save"));
1022 int option = chooser.showSaveDialog(this);
1023 if (option == JalviewFileChooser.APPROVE_OPTION)
1025 File file = chooser.getSelectedFile();
1031 * Saves feature colours and filters to the given file
1035 void save(File file)
1037 JalviewUserColours ucs = new JalviewUserColours();
1038 ucs.setSchemeName("Sequence Features");
1041 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1042 new FileOutputStream(file), "UTF-8"));
1045 * sort feature types by colour order, from 0 (highest)
1048 Set<String> fr_colours = fr.getAllFeatureColours();
1049 String[] sortedTypes = fr_colours
1050 .toArray(new String[fr_colours.size()]);
1051 Arrays.sort(sortedTypes, new Comparator<String>()
1054 public int compare(String type1, String type2)
1056 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1061 * save feature colours
1063 for (String featureType : sortedTypes)
1065 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1066 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1068 ucs.getColour().add(col);
1072 * save any feature filters
1074 for (String featureType : sortedTypes)
1076 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1077 if (filter != null && !filter.isEmpty())
1079 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1080 FeatureMatcherI firstMatcher = iterator.next();
1081 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1082 .marshalFilter(firstMatcher, iterator,
1084 Filter filterModel = new Filter();
1085 filterModel.setFeatureType(featureType);
1086 filterModel.setMatcherSet(ms);
1087 ucs.getFilter().add(filterModel);
1090 JAXBContext jaxbContext = JAXBContext
1091 .newInstance(JalviewUserColours.class);
1092 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1093 jaxbMarshaller.marshal(
1094 new ObjectFactory().createJalviewUserColours(ucs), out);
1096 // jaxbMarshaller.marshal(object, pout);
1097 // marshaller.marshal(object);
1100 // ucs.marshal(out);
1102 } catch (Exception ex)
1104 ex.printStackTrace();
1108 public void invertSelection()
1110 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1111 for (int i = 0; i < data.length; i++)
1113 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1115 updateFeatureRenderer(data, true);
1119 public void orderByAvWidth()
1121 if (table == null || table.getModel() == null)
1125 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1126 float[] width = new float[data.length];
1130 for (int i = 0; i < data.length; i++)
1132 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1135 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1136 // weight - but have to make per
1137 // sequence, too (awidth[2])
1138 // if (width[i]==1) // hack to distinguish single width sequences.
1149 boolean sort = false;
1150 for (int i = 0; i < width.length; i++)
1152 // awidth = (float[]) typeWidth.get(data[i][0]);
1155 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1158 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1164 width[i] /= max; // normalize
1165 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1169 sort = sort || width[i - 1] > width[i];
1174 jalview.util.QuickSort.sort(width, data);
1175 // update global priority order
1178 updateFeatureRenderer(data, false);
1186 frame.setClosed(true);
1187 } catch (Exception exe)
1193 public void updateFeatureRenderer(Object[][] data)
1195 updateFeatureRenderer(data, true);
1199 * Update the priority order of features; only repaint if this changed the order
1200 * of visible features
1205 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1207 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1209 if (fr.setFeaturePriority(rowData, visibleNew))
1211 af.alignPanel.paintAlignment(true, true);
1216 * Converts table data into an array of data beans
1218 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1220 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1221 for (int i = 0; i < data.length; i++)
1223 String type = (String) data[i][TYPE_COLUMN];
1224 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1225 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1226 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1227 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1233 private void jbInit() throws Exception
1235 this.setLayout(new BorderLayout());
1237 JPanel settingsPane = new JPanel();
1238 settingsPane.setLayout(new BorderLayout());
1240 JPanel bigPanel = new JPanel();
1241 bigPanel.setLayout(new BorderLayout());
1243 groupPanel = new JPanel();
1244 bigPanel.add(groupPanel, BorderLayout.NORTH);
1246 JButton invert = new JButton(
1247 MessageManager.getString("label.invert_selection"));
1248 invert.setFont(JvSwingUtils.getLabelFont());
1249 invert.addActionListener(new ActionListener()
1252 public void actionPerformed(ActionEvent e)
1258 JButton optimizeOrder = new JButton(
1259 MessageManager.getString("label.optimise_order"));
1260 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1261 optimizeOrder.addActionListener(new ActionListener()
1264 public void actionPerformed(ActionEvent e)
1270 JButton sortByScore = new JButton(
1271 MessageManager.getString("label.seq_sort_by_score"));
1272 sortByScore.setFont(JvSwingUtils.getLabelFont());
1273 sortByScore.addActionListener(new ActionListener()
1276 public void actionPerformed(ActionEvent e)
1278 af.avc.sortAlignmentByFeatureScore(null);
1281 JButton sortByDens = new JButton(
1282 MessageManager.getString("label.sequence_sort_by_density"));
1283 sortByDens.setFont(JvSwingUtils.getLabelFont());
1284 sortByDens.addActionListener(new ActionListener()
1287 public void actionPerformed(ActionEvent e)
1289 af.avc.sortAlignmentByFeatureDensity(null);
1293 JButton help = new JButton(MessageManager.getString("action.help"));
1294 help.setFont(JvSwingUtils.getLabelFont());
1295 help.addActionListener(new ActionListener()
1298 public void actionPerformed(ActionEvent e)
1302 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1303 } catch (HelpSetException e1)
1305 e1.printStackTrace();
1309 help.setFont(JvSwingUtils.getLabelFont());
1310 help.setText(MessageManager.getString("action.help"));
1311 help.addActionListener(new ActionListener()
1314 public void actionPerformed(ActionEvent e)
1318 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1319 } catch (HelpSetException e1)
1321 e1.printStackTrace();
1326 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1327 cancel.setFont(JvSwingUtils.getLabelFont());
1328 cancel.addActionListener(new ActionListener()
1331 public void actionPerformed(ActionEvent e)
1333 fr.setTransparency(originalTransparency);
1334 fr.setFeatureFilters(originalFilters);
1335 updateFeatureRenderer(originalData);
1340 JButton ok = new JButton(MessageManager.getString("action.ok"));
1341 ok.setFont(JvSwingUtils.getLabelFont());
1342 ok.addActionListener(new ActionListener()
1345 public void actionPerformed(ActionEvent e)
1351 JButton loadColours = new JButton(
1352 MessageManager.getString("label.load_colours"));
1353 loadColours.setFont(JvSwingUtils.getLabelFont());
1354 loadColours.setToolTipText(
1355 MessageManager.getString("label.load_colours_tooltip"));
1356 loadColours.addActionListener(new ActionListener()
1359 public void actionPerformed(ActionEvent e)
1365 JButton saveColours = new JButton(
1366 MessageManager.getString("label.save_colours"));
1367 saveColours.setFont(JvSwingUtils.getLabelFont());
1368 saveColours.setToolTipText(
1369 MessageManager.getString("label.save_colours_tooltip"));
1370 saveColours.addActionListener(new ActionListener()
1373 public void actionPerformed(ActionEvent e)
1378 transparency.addChangeListener(new ChangeListener()
1381 public void stateChanged(ChangeEvent evt)
1383 if (!inConstruction)
1385 fr.setTransparency((100 - transparency.getValue()) / 100f);
1386 af.alignPanel.paintAlignment(true, true);
1391 transparency.setMaximum(70);
1392 transparency.setToolTipText(
1393 MessageManager.getString("label.transparency_tip"));
1395 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1396 bigPanel.add(transPanel, BorderLayout.SOUTH);
1398 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1399 transbuttons.add(optimizeOrder);
1400 transbuttons.add(invert);
1401 transbuttons.add(sortByScore);
1402 transbuttons.add(sortByDens);
1403 transbuttons.add(help);
1404 transPanel.add(transparency);
1405 transPanel.add(transbuttons);
1407 JPanel buttonPanel = new JPanel();
1408 buttonPanel.add(ok);
1409 buttonPanel.add(cancel);
1410 buttonPanel.add(loadColours);
1411 buttonPanel.add(saveColours);
1412 bigPanel.add(scrollPane, BorderLayout.CENTER);
1413 settingsPane.add(bigPanel, BorderLayout.CENTER);
1414 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1415 this.add(settingsPane);
1419 * Answers a suitable tooltip to show on the colour cell of the table
1423 * if true include 'click to edit' and similar text
1426 public static String getColorTooltip(FeatureColourI fcol,
1433 if (fcol.isSimpleColour())
1435 return withHint ? BASE_TOOLTIP : null;
1437 String description = fcol.getDescription();
1438 description = description.replaceAll("<", "<");
1439 description = description.replaceAll(">", ">");
1440 StringBuilder tt = new StringBuilder(description);
1443 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1445 return JvSwingUtils.wrapTooltip(true, tt.toString());
1448 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1451 boolean thr = false;
1452 StringBuilder tx = new StringBuilder();
1454 if (gcol.isColourByAttribute())
1456 tx.append(FeatureMatcher
1457 .toAttributeDisplayName(gcol.getAttributeName()));
1459 else if (!gcol.isColourByLabel())
1461 tx.append(MessageManager.getString("label.score"));
1464 if (gcol.isAboveThreshold())
1469 if (gcol.isBelowThreshold())
1474 if (gcol.isColourByLabel())
1480 if (!gcol.isColourByAttribute())
1488 Color newColor = gcol.getMaxColour();
1489 comp.setBackground(newColor);
1490 // System.err.println("Width is " + w / 2);
1491 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1492 comp.setIcon(ficon);
1493 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1494 // + newColor.getGreen() + ", " + newColor.getBlue()
1495 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1496 // + ", " + minCol.getBlue() + ")");
1498 comp.setHorizontalAlignment(SwingConstants.CENTER);
1499 comp.setText(tx.toString());
1502 // ///////////////////////////////////////////////////////////////////////
1503 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1504 // ///////////////////////////////////////////////////////////////////////
1505 class FeatureTableModel extends AbstractTableModel
1507 private String[] columnNames = {
1508 MessageManager.getString("label.feature_type"),
1509 MessageManager.getString("action.colour"),
1510 MessageManager.getString("label.filter"),
1511 MessageManager.getString("label.show") };
1513 private Object[][] data;
1515 FeatureTableModel(Object[][] data)
1520 public Object[][] getData()
1525 public void setData(Object[][] data)
1531 public int getColumnCount()
1533 return columnNames.length;
1536 public Object[] getRow(int row)
1542 public int getRowCount()
1548 public String getColumnName(int col)
1550 return columnNames[col];
1554 public Object getValueAt(int row, int col)
1556 return data[row][col];
1560 * Answers the class of the object in column c of the first row of the table
1563 public Class<?> getColumnClass(int c)
1565 Object v = getValueAt(0, c);
1566 return v == null ? null : v.getClass();
1570 public boolean isCellEditable(int row, int col)
1572 return col == 0 ? false : true;
1576 public void setValueAt(Object value, int row, int col)
1578 data[row][col] = value;
1579 fireTableCellUpdated(row, col);
1580 updateFeatureRenderer(data);
1585 class ColorRenderer extends JLabel implements TableCellRenderer
1587 Border unselectedBorder = null;
1589 Border selectedBorder = null;
1591 public ColorRenderer()
1593 setOpaque(true); // MUST do this for background to show up.
1594 setHorizontalTextPosition(SwingConstants.CENTER);
1595 setVerticalTextPosition(SwingConstants.CENTER);
1599 public Component getTableCellRendererComponent(JTable tbl, Object color,
1600 boolean isSelected, boolean hasFocus, int row, int column)
1602 FeatureColourI cellColour = (FeatureColourI) color;
1604 setBackground(tbl.getBackground());
1605 if (!cellColour.isSimpleColour())
1607 Rectangle cr = tbl.getCellRect(row, column, false);
1608 FeatureSettings.renderGraduatedColor(this, cellColour,
1609 (int) cr.getWidth(), (int) cr.getHeight());
1615 setBackground(cellColour.getColour());
1619 if (selectedBorder == null)
1621 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1622 tbl.getSelectionBackground());
1624 setBorder(selectedBorder);
1628 if (unselectedBorder == null)
1630 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1631 tbl.getBackground());
1633 setBorder(unselectedBorder);
1640 class FilterRenderer extends JLabel implements TableCellRenderer
1642 javax.swing.border.Border unselectedBorder = null;
1644 javax.swing.border.Border selectedBorder = null;
1646 public FilterRenderer()
1648 setOpaque(true); // MUST do this for background to show up.
1649 setHorizontalTextPosition(SwingConstants.CENTER);
1650 setVerticalTextPosition(SwingConstants.CENTER);
1654 public Component getTableCellRendererComponent(JTable tbl,
1655 Object filter, boolean isSelected, boolean hasFocus, int row,
1658 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1660 String asText = theFilter.toString();
1661 setBackground(tbl.getBackground());
1662 this.setText(asText);
1667 if (selectedBorder == null)
1669 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1670 tbl.getSelectionBackground());
1672 setBorder(selectedBorder);
1676 if (unselectedBorder == null)
1678 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1679 tbl.getBackground());
1681 setBorder(unselectedBorder);
1689 * update comp using rendering settings from gcol
1694 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1696 int w = comp.getWidth(), h = comp.getHeight();
1699 w = (int) comp.getPreferredSize().getWidth();
1700 h = (int) comp.getPreferredSize().getHeight();
1707 renderGraduatedColor(comp, gcol, w, h);
1710 @SuppressWarnings("serial")
1711 class ColorEditor extends AbstractCellEditor
1712 implements TableCellEditor, ActionListener
1714 FeatureColourI currentColor;
1716 FeatureTypeSettings chooser;
1722 protected static final String EDIT = "edit";
1724 int rowSelected = 0;
1726 public ColorEditor()
1728 // Set up the editor (from the table's point of view),
1729 // which is a button.
1730 // This button brings up the color chooser dialog,
1731 // which is the editor from the user's point of view.
1732 button = new JButton();
1733 button.setActionCommand(EDIT);
1734 button.addActionListener(this);
1735 button.setBorderPainted(false);
1739 * Handles events from the editor button, and from the colour/filters
1740 * dialog's OK button
1743 public void actionPerformed(ActionEvent e)
1745 if (button == e.getSource())
1747 if (currentColor.isSimpleColour())
1750 * simple colour chooser
1752 String ttl = MessageManager.formatMessage("label.select_colour_for", type);
1753 ColourChooserListener listener = new ColourChooserListener()
1756 public void colourSelected(Color c)
1758 currentColor = new FeatureColour(c);
1759 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1760 fireEditingStopped();
1763 public void cancel()
1765 fireEditingStopped();
1768 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1773 * variable colour and filters dialog
1775 chooser = new FeatureTypeSettings(fr, type);
1776 if (!Jalview.isJS())
1778 chooser.setRequestFocusEnabled(true);
1779 chooser.requestFocus();
1781 chooser.addActionListener(this);
1782 fireEditingStopped();
1788 * after OK in variable colour dialog, any changes to colour
1789 * (or filters!) are already set in FeatureRenderer, so just
1790 * update table data without triggering updateFeatureRenderer
1792 currentColor = fr.getFeatureColours().get(type);
1793 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1794 if (currentFilter == null)
1796 currentFilter = new FeatureMatcherSet();
1798 Object[] data = ((FeatureTableModel) table.getModel())
1799 .getData()[rowSelected];
1800 data[COLOUR_COLUMN] = currentColor;
1801 data[FILTER_COLUMN] = currentFilter;
1802 fireEditingStopped();
1803 // SwingJS needs an explicit repaint() here,
1804 // rather than relying upon no validation having
1805 // occurred since the stopEditing call was made.
1806 // Its laying out has not been stopped by the modal frame
1813 * Override allows access to this method from anonymous inner classes
1816 protected void fireEditingStopped()
1818 super.fireEditingStopped();
1821 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1823 public Object getCellEditorValue()
1825 return currentColor;
1828 // Implement the one method defined by TableCellEditor.
1830 public Component getTableCellEditorComponent(JTable theTable, Object value,
1831 boolean isSelected, int row, int column)
1833 currentColor = (FeatureColourI) value;
1834 this.rowSelected = row;
1835 type = table.getValueAt(row, TYPE_COLUMN).toString();
1836 button.setOpaque(true);
1837 button.setBackground(FeatureSettings.this.getBackground());
1838 if (!currentColor.isSimpleColour())
1840 JLabel btn = new JLabel();
1841 btn.setSize(button.getSize());
1842 FeatureSettings.renderGraduatedColor(btn, currentColor);
1843 button.setBackground(btn.getBackground());
1844 button.setIcon(btn.getIcon());
1845 button.setText(btn.getText());
1850 button.setIcon(null);
1851 button.setBackground(currentColor.getColour());
1858 * The cell editor for the Filter column. It displays the text of any filters
1859 * for the feature type in that row (in full as a tooltip, possible abbreviated
1860 * as display text). On click in the cell, opens the Feature Display Settings
1861 * dialog at the Filters tab.
1863 @SuppressWarnings("serial")
1864 class FilterEditor extends AbstractCellEditor
1865 implements TableCellEditor, ActionListener
1868 FeatureMatcherSetI currentFilter;
1876 protected static final String EDIT = "edit";
1878 int rowSelected = 0;
1880 public FilterEditor()
1882 button = new JButton();
1883 button.setActionCommand(EDIT);
1884 button.addActionListener(this);
1885 button.setBorderPainted(false);
1889 * Handles events from the editor button
1892 public void actionPerformed(ActionEvent e)
1894 if (button == e.getSource())
1896 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
1897 chooser.addActionListener(this);
1898 chooser.setRequestFocusEnabled(true);
1899 chooser.requestFocus();
1900 if (lastLocation != null)
1902 // todo open at its last position on screen
1903 chooser.setBounds(lastLocation.x, lastLocation.y,
1904 chooser.getWidth(), chooser.getHeight());
1907 fireEditingStopped();
1909 else if (e.getSource() instanceof Component)
1913 * after OK in variable colour dialog, any changes to filter
1914 * (or colours!) are already set in FeatureRenderer, so just
1915 * update table data without triggering updateFeatureRenderer
1917 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1918 currentFilter = fr.getFeatureFilter(type);
1919 if (currentFilter == null)
1921 currentFilter = new FeatureMatcherSet();
1924 Object[] data = ((FeatureTableModel) table.getModel())
1925 .getData()[rowSelected];
1926 data[COLOUR_COLUMN] = currentColor;
1927 data[FILTER_COLUMN] = currentFilter;
1928 fireEditingStopped();
1929 // SwingJS needs an explicit repaint() here,
1930 // rather than relying upon no validation having
1931 // occurred since the stopEditing call was made.
1932 // Its laying out has not been stopped by the modal frame
1939 public Object getCellEditorValue()
1941 return currentFilter;
1945 public Component getTableCellEditorComponent(JTable theTable, Object value,
1946 boolean isSelected, int row, int column)
1948 currentFilter = (FeatureMatcherSetI) value;
1949 this.rowSelected = row;
1950 type = table.getValueAt(row, TYPE_COLUMN).toString();
1951 button.setOpaque(true);
1952 button.setBackground(FeatureSettings.this.getBackground());
1953 button.setText(currentFilter.toString());
1954 button.setIcon(null);
1960 class FeatureIcon implements Icon
1962 FeatureColourI gcol;
1966 boolean midspace = false;
1968 int width = 50, height = 20;
1970 int s1, e1; // start and end of midpoint band for thresholded symbol
1972 Color mpcolour = Color.white;
1974 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1994 public int getIconWidth()
2000 public int getIconHeight()
2006 public void paintIcon(Component c, Graphics g, int x, int y)
2009 if (gcol.isColourByLabel())
2012 g.fillRect(0, 0, width, height);
2013 // need an icon here.
2014 g.setColor(gcol.getMaxColour());
2016 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2018 // g.setFont(g.getFont().deriveFont(
2019 // AffineTransform.getScaleInstance(
2020 // width/g.getFontMetrics().stringWidth("Label"),
2021 // height/g.getFontMetrics().getHeight())));
2023 g.drawString(MessageManager.getString("label.label"), 0, 0);
2028 Color minCol = gcol.getMinColour();
2030 g.fillRect(0, 0, s1, height);
2033 g.setColor(Color.white);
2034 g.fillRect(s1, 0, e1 - s1, height);
2036 g.setColor(gcol.getMaxColour());
2037 // g.fillRect(0, e1, width - e1, height); // BH 2018
2038 g.fillRect(e1, 0, width - e1, height);