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 final FeatureColourI featureColour = (FeatureColourI) typeCol;
447 JPopupMenu men = new JPopupMenu(MessageManager
448 .formatMessage("label.settings_for_param", new String[]
450 JMenuItem scr = new JMenuItem(
451 MessageManager.getString("label.sort_by_score"));
453 scr.addActionListener(new ActionListener()
457 public void actionPerformed(ActionEvent e)
459 af.avc.sortAlignmentByFeatureScore(Arrays.asList(new String[]
463 JMenuItem dens = new JMenuItem(
464 MessageManager.getString("label.sort_by_density"));
465 dens.addActionListener(new ActionListener()
469 public void actionPerformed(ActionEvent e)
471 af.avc.sortAlignmentByFeatureDensity(Arrays.asList(new String[]
478 * variable colour options include colour by label, by score,
479 * by selected attribute text, or attribute value
481 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
482 MessageManager.getString("label.variable_colour"));
483 variableColourCB.setSelected(!featureColour.isSimpleColour());
484 men.add(variableColourCB);
487 * checkbox action listener doubles up as listener to OK
488 * from the variable colour / filters dialog
490 variableColourCB.addActionListener(new ActionListener()
493 public void actionPerformed(ActionEvent e)
495 if (e.getSource() == variableColourCB)
497 men.setVisible(true); // BH 2018 for JavaScript because this is a checkbox
498 men.setVisible(false); // BH 2018 for JavaScript because this is a checkbox
499 if (featureColour.isSimpleColour())
502 * toggle simple colour to variable colour - show dialog
504 FeatureTypeSettings fc = new FeatureTypeSettings(fr, type);
505 fc.addActionListener(this);
510 * toggle variable to simple colour - show colour chooser
512 String title = MessageManager.formatMessage("label.select_colour_for", type);
513 ColourChooserListener listener = new ColourChooserListener()
516 public void colourSelected(Color c)
518 table.setValueAt(new FeatureColour(c), rowSelected,
521 updateFeatureRenderer(
522 ((FeatureTableModel) table.getModel()).getData(),
526 JalviewColourChooser.showColourChooser(FeatureSettings.this, title,
527 featureColour.getMaxColour(), listener);
532 if (e.getSource() instanceof FeatureTypeSettings)
535 * update after OK in feature colour dialog; the updated
536 * colour will have already been set in the FeatureRenderer
538 FeatureColourI fci = fr.getFeatureColours().get(type);
539 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
540 // BH 2018 setting a table value does not invalidate it.
541 // System.out.println("FeatureSettings is valied" + table.isValid());
548 JMenuItem selCols = new JMenuItem(
549 MessageManager.getString("label.select_columns_containing"));
550 selCols.addActionListener(new ActionListener()
553 public void actionPerformed(ActionEvent arg0)
555 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
559 JMenuItem clearCols = new JMenuItem(MessageManager
560 .getString("label.select_columns_not_containing"));
561 clearCols.addActionListener(new ActionListener()
564 public void actionPerformed(ActionEvent arg0)
566 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
570 JMenuItem hideCols = new JMenuItem(
571 MessageManager.getString("label.hide_columns_containing"));
572 hideCols.addActionListener(new ActionListener()
575 public void actionPerformed(ActionEvent arg0)
577 fr.ap.alignFrame.hideFeatureColumns(type, true);
580 JMenuItem hideOtherCols = new JMenuItem(
581 MessageManager.getString("label.hide_columns_not_containing"));
582 hideOtherCols.addActionListener(new ActionListener()
585 public void actionPerformed(ActionEvent arg0)
587 fr.ap.alignFrame.hideFeatureColumns(type, false);
593 men.add(hideOtherCols);
594 men.show(table, pt.x, pt.y);
598 synchronized public void discoverAllFeatureData()
600 Set<String> allGroups = new HashSet<>();
601 AlignmentI alignment = af.getViewport().getAlignment();
603 for (int i = 0; i < alignment.getHeight(); i++)
605 SequenceI seq = alignment.getSequenceAt(i);
606 for (String group : seq.getFeatures().getFeatureGroups(true))
608 if (group != null && !allGroups.contains(group))
610 allGroups.add(group);
611 checkGroupState(group);
622 * Synchronise gui group list and check visibility of group
625 * @return true if group is visible
627 private boolean checkGroupState(String group)
629 boolean visible = fr.checkGroupVisibility(group, true);
631 for (int g = 0; g < groupPanel.getComponentCount(); g++)
633 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
635 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
640 final String grp = group;
641 final JCheckBox check = new JCheckBox(group, visible);
642 check.setFont(new Font("Serif", Font.BOLD, 12));
643 check.setToolTipText(group);
644 check.addItemListener(new ItemListener()
647 public void itemStateChanged(ItemEvent evt)
649 fr.setGroupVisibility(check.getText(), check.isSelected());
650 resetTable(new String[] { grp });
651 af.alignPanel.paintAlignment(true, true);
654 groupPanel.add(check);
658 synchronized void resetTable(String[] groupChanged)
664 resettingTable = true;
665 typeWidth = new Hashtable<>();
666 // TODO: change avWidth calculation to 'per-sequence' average and use long
669 Set<String> displayableTypes = new HashSet<>();
670 Set<String> foundGroups = new HashSet<>();
673 * determine which feature types may be visible depending on
674 * which groups are selected, and recompute average width data
676 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
679 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
682 * get the sequence's groups for positional features
683 * and keep track of which groups are visible
685 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
686 Set<String> visibleGroups = new HashSet<>();
687 for (String group : groups)
689 if (group == null || checkGroupState(group))
691 visibleGroups.add(group);
694 foundGroups.addAll(groups);
697 * get distinct feature types for visible groups
698 * record distinct visible types, and their count and total length
700 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
701 visibleGroups.toArray(new String[visibleGroups.size()]));
702 for (String type : types)
704 displayableTypes.add(type);
705 float[] avWidth = typeWidth.get(type);
708 avWidth = new float[2];
709 typeWidth.put(type, avWidth);
711 // todo this could include features with a non-visible group
712 // - do we greatly care?
713 // todo should we include non-displayable features here, and only
714 // update when features are added?
715 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
716 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
720 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
723 if (fr.hasRenderOrder())
727 fr.findAllFeatures(groupChanged != null); // prod to update
728 // colourschemes. but don't
730 // First add the checks in the previous render order,
731 // in case the window has been closed and reopened
733 List<String> frl = fr.getRenderOrder();
734 for (int ro = frl.size() - 1; ro > -1; ro--)
736 String type = frl.get(ro);
738 if (!displayableTypes.contains(type))
743 data[dataIndex][TYPE_COLUMN] = type;
744 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
745 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
746 data[dataIndex][FILTER_COLUMN] = featureFilter == null
747 ? new FeatureMatcherSet()
749 data[dataIndex][SHOW_COLUMN] = new Boolean(
750 af.getViewport().getFeaturesDisplayed().isVisible(type));
752 displayableTypes.remove(type);
757 * process any extra features belonging only to
758 * a group which was just selected
760 while (!displayableTypes.isEmpty())
762 String type = displayableTypes.iterator().next();
763 data[dataIndex][TYPE_COLUMN] = type;
765 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
766 if (data[dataIndex][COLOUR_COLUMN] == null)
768 // "Colour has been updated in another view!!"
769 fr.clearRenderOrder();
772 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
773 data[dataIndex][FILTER_COLUMN] = featureFilter == null
774 ? new FeatureMatcherSet()
776 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
778 displayableTypes.remove(type);
781 if (originalData == null)
783 originalData = new Object[data.length][COLUMN_COUNT];
784 for (int i = 0; i < data.length; i++)
786 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
791 updateOriginalData(data);
794 table.setModel(new FeatureTableModel(data));
795 table.getColumnModel().getColumn(0).setPreferredWidth(200);
797 groupPanel.setLayout(
798 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
799 pruneGroups(foundGroups);
800 groupPanel.validate();
802 updateFeatureRenderer(data, groupChanged != null);
803 resettingTable = false;
807 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
808 * have been made outwith this dialog
810 * <li>a new feature type added (and made visible)</li>
811 * <li>a feature colour changed (in the Amend Features dialog)</li>
816 protected void updateOriginalData(Object[][] foundData)
818 // todo LinkedHashMap instead of Object[][] would be nice
820 Object[][] currentData = ((FeatureTableModel) table.getModel())
822 for (Object[] row : foundData)
824 String type = (String) row[TYPE_COLUMN];
825 boolean found = false;
826 for (Object[] current : currentData)
828 if (type.equals(current[TYPE_COLUMN]))
832 * currently dependent on object equality here;
833 * really need an equals method on FeatureColour
835 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
838 * feature colour has changed externally - update originalData
840 for (Object[] original : originalData)
842 if (type.equals(original[TYPE_COLUMN]))
844 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
855 * new feature detected - add to original data (on top)
857 Object[][] newData = new Object[originalData.length
859 for (int i = 0; i < originalData.length; i++)
861 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
865 originalData = newData;
871 * Remove from the groups panel any checkboxes for groups that are not in the
872 * foundGroups set. This enables removing a group from the display when the last
873 * feature in that group is deleted.
877 protected void pruneGroups(Set<String> foundGroups)
879 for (int g = 0; g < groupPanel.getComponentCount(); g++)
881 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
882 if (!foundGroups.contains(checkbox.getText()))
884 groupPanel.remove(checkbox);
890 * reorder data based on the featureRenderers global priority list.
894 private void ensureOrder(Object[][] data)
896 boolean sort = false;
897 float[] order = new float[data.length];
898 for (int i = 0; i < order.length; i++)
900 order[i] = fr.getOrder(data[i][0].toString());
903 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
907 sort = sort || order[i - 1] > order[i];
912 jalview.util.QuickSort.sort(order, data);
917 * Offers a file chooser dialog, and then loads the feature colours and
918 * filters from file in XML format and unmarshals to Jalview feature settings
922 JalviewFileChooser chooser = new JalviewFileChooser("fc",
923 SEQUENCE_FEATURE_COLOURS);
924 chooser.setFileView(new JalviewFileView());
925 chooser.setDialogTitle(
926 MessageManager.getString("label.load_feature_colours"));
927 chooser.setToolTipText(MessageManager.getString("action.load"));
928 chooser.setResponseHandler(0, new Runnable()
933 File file = chooser.getSelectedFile();
937 chooser.showOpenDialog(this);
941 * Loads feature colours and filters from XML stored in the given file
949 InputStreamReader in = new InputStreamReader(
950 new FileInputStream(file), "UTF-8");
952 JAXBContext jc = JAXBContext
953 .newInstance("jalview.xml.binding.jalview");
954 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
955 XMLStreamReader streamReader = XMLInputFactory.newInstance()
956 .createXMLStreamReader(in);
957 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
958 JalviewUserColours.class);
959 JalviewUserColours jucs = jbe.getValue();
961 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
964 * load feature colours
966 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
968 Colour newcol = jucs.getColour().get(i);
969 FeatureColourI colour = jalview.project.Jalview2XML
970 .parseColour(newcol);
971 fr.setColour(newcol.getName(), colour);
972 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
976 * load feature filters; loaded filters will replace any that are
977 * currently defined, other defined filters are left unchanged
979 for (int i = 0; i < jucs.getFilter().size(); i++)
981 Filter filterModel = jucs.getFilter().get(i);
982 String featureType = filterModel.getFeatureType();
983 FeatureMatcherSetI filter = jalview.project.Jalview2XML
984 .parseFilter(featureType, filterModel.getMatcherSet());
985 if (!filter.isEmpty())
987 fr.setFeatureFilter(featureType, filter);
992 * update feature settings table
997 Object[][] data = ((FeatureTableModel) table.getModel())
1000 updateFeatureRenderer(data, false);
1003 } catch (Exception ex)
1005 System.out.println("Error loading User Colour File\n" + ex);
1010 * Offers a file chooser dialog, and then saves the current feature colours
1011 * and any filters to the selected file in XML format
1015 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1016 SEQUENCE_FEATURE_COLOURS);
1017 chooser.setFileView(new JalviewFileView());
1018 chooser.setDialogTitle(
1019 MessageManager.getString("label.save_feature_colours"));
1020 chooser.setToolTipText(MessageManager.getString("action.save"));
1021 int option = chooser.showSaveDialog(this);
1022 if (option == JalviewFileChooser.APPROVE_OPTION)
1024 File file = chooser.getSelectedFile();
1030 * Saves feature colours and filters to the given file
1034 void save(File file)
1036 JalviewUserColours ucs = new JalviewUserColours();
1037 ucs.setSchemeName("Sequence Features");
1040 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1041 new FileOutputStream(file), "UTF-8"));
1044 * sort feature types by colour order, from 0 (highest)
1047 Set<String> fr_colours = fr.getAllFeatureColours();
1048 String[] sortedTypes = fr_colours
1049 .toArray(new String[fr_colours.size()]);
1050 Arrays.sort(sortedTypes, new Comparator<String>()
1053 public int compare(String type1, String type2)
1055 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1060 * save feature colours
1062 for (String featureType : sortedTypes)
1064 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1065 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1067 ucs.getColour().add(col);
1071 * save any feature filters
1073 for (String featureType : sortedTypes)
1075 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1076 if (filter != null && !filter.isEmpty())
1078 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1079 FeatureMatcherI firstMatcher = iterator.next();
1080 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1081 .marshalFilter(firstMatcher, iterator,
1083 Filter filterModel = new Filter();
1084 filterModel.setFeatureType(featureType);
1085 filterModel.setMatcherSet(ms);
1086 ucs.getFilter().add(filterModel);
1089 JAXBContext jaxbContext = JAXBContext
1090 .newInstance(JalviewUserColours.class);
1091 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1092 jaxbMarshaller.marshal(
1093 new ObjectFactory().createJalviewUserColours(ucs), out);
1095 // jaxbMarshaller.marshal(object, pout);
1096 // marshaller.marshal(object);
1099 // ucs.marshal(out);
1101 } catch (Exception ex)
1103 ex.printStackTrace();
1107 public void invertSelection()
1109 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1110 for (int i = 0; i < data.length; i++)
1112 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1114 updateFeatureRenderer(data, true);
1118 public void orderByAvWidth()
1120 if (table == null || table.getModel() == null)
1124 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1125 float[] width = new float[data.length];
1129 for (int i = 0; i < data.length; i++)
1131 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1134 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1135 // weight - but have to make per
1136 // sequence, too (awidth[2])
1137 // if (width[i]==1) // hack to distinguish single width sequences.
1148 boolean sort = false;
1149 for (int i = 0; i < width.length; i++)
1151 // awidth = (float[]) typeWidth.get(data[i][0]);
1154 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1157 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1163 width[i] /= max; // normalize
1164 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1168 sort = sort || width[i - 1] > width[i];
1173 jalview.util.QuickSort.sort(width, data);
1174 // update global priority order
1177 updateFeatureRenderer(data, false);
1185 frame.setClosed(true);
1186 } catch (Exception exe)
1192 public void updateFeatureRenderer(Object[][] data)
1194 updateFeatureRenderer(data, true);
1198 * Update the priority order of features; only repaint if this changed the order
1199 * of visible features
1204 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1206 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1208 if (fr.setFeaturePriority(rowData, visibleNew))
1210 af.alignPanel.paintAlignment(true, true);
1215 * Converts table data into an array of data beans
1217 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1219 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1220 for (int i = 0; i < data.length; i++)
1222 String type = (String) data[i][TYPE_COLUMN];
1223 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1224 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1225 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1226 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1232 private void jbInit() throws Exception
1234 this.setLayout(new BorderLayout());
1236 JPanel settingsPane = new JPanel();
1237 settingsPane.setLayout(new BorderLayout());
1239 JPanel bigPanel = new JPanel();
1240 bigPanel.setLayout(new BorderLayout());
1242 groupPanel = new JPanel();
1243 bigPanel.add(groupPanel, BorderLayout.NORTH);
1245 JButton invert = new JButton(
1246 MessageManager.getString("label.invert_selection"));
1247 invert.setFont(JvSwingUtils.getLabelFont());
1248 invert.addActionListener(new ActionListener()
1251 public void actionPerformed(ActionEvent e)
1257 JButton optimizeOrder = new JButton(
1258 MessageManager.getString("label.optimise_order"));
1259 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1260 optimizeOrder.addActionListener(new ActionListener()
1263 public void actionPerformed(ActionEvent e)
1269 JButton sortByScore = new JButton(
1270 MessageManager.getString("label.seq_sort_by_score"));
1271 sortByScore.setFont(JvSwingUtils.getLabelFont());
1272 sortByScore.addActionListener(new ActionListener()
1275 public void actionPerformed(ActionEvent e)
1277 af.avc.sortAlignmentByFeatureScore(null);
1280 JButton sortByDens = new JButton(
1281 MessageManager.getString("label.sequence_sort_by_density"));
1282 sortByDens.setFont(JvSwingUtils.getLabelFont());
1283 sortByDens.addActionListener(new ActionListener()
1286 public void actionPerformed(ActionEvent e)
1288 af.avc.sortAlignmentByFeatureDensity(null);
1292 JButton help = new JButton(MessageManager.getString("action.help"));
1293 help.setFont(JvSwingUtils.getLabelFont());
1294 help.addActionListener(new ActionListener()
1297 public void actionPerformed(ActionEvent e)
1301 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1302 } catch (HelpSetException e1)
1304 e1.printStackTrace();
1308 help.setFont(JvSwingUtils.getLabelFont());
1309 help.setText(MessageManager.getString("action.help"));
1310 help.addActionListener(new ActionListener()
1313 public void actionPerformed(ActionEvent e)
1317 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1318 } catch (HelpSetException e1)
1320 e1.printStackTrace();
1325 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1326 cancel.setFont(JvSwingUtils.getLabelFont());
1327 cancel.addActionListener(new ActionListener()
1330 public void actionPerformed(ActionEvent e)
1332 fr.setTransparency(originalTransparency);
1333 fr.setFeatureFilters(originalFilters);
1334 updateFeatureRenderer(originalData);
1339 JButton ok = new JButton(MessageManager.getString("action.ok"));
1340 ok.setFont(JvSwingUtils.getLabelFont());
1341 ok.addActionListener(new ActionListener()
1344 public void actionPerformed(ActionEvent e)
1350 JButton loadColours = new JButton(
1351 MessageManager.getString("label.load_colours"));
1352 loadColours.setFont(JvSwingUtils.getLabelFont());
1353 loadColours.setToolTipText(
1354 MessageManager.getString("label.load_colours_tooltip"));
1355 loadColours.addActionListener(new ActionListener()
1358 public void actionPerformed(ActionEvent e)
1364 JButton saveColours = new JButton(
1365 MessageManager.getString("label.save_colours"));
1366 saveColours.setFont(JvSwingUtils.getLabelFont());
1367 saveColours.setToolTipText(
1368 MessageManager.getString("label.save_colours_tooltip"));
1369 saveColours.addActionListener(new ActionListener()
1372 public void actionPerformed(ActionEvent e)
1377 transparency.addChangeListener(new ChangeListener()
1380 public void stateChanged(ChangeEvent evt)
1382 if (!inConstruction)
1384 fr.setTransparency((100 - transparency.getValue()) / 100f);
1385 af.alignPanel.paintAlignment(true, true);
1390 transparency.setMaximum(70);
1391 transparency.setToolTipText(
1392 MessageManager.getString("label.transparency_tip"));
1394 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1395 bigPanel.add(transPanel, BorderLayout.SOUTH);
1397 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1398 transbuttons.add(optimizeOrder);
1399 transbuttons.add(invert);
1400 transbuttons.add(sortByScore);
1401 transbuttons.add(sortByDens);
1402 transbuttons.add(help);
1403 transPanel.add(transparency);
1404 transPanel.add(transbuttons);
1406 JPanel buttonPanel = new JPanel();
1407 buttonPanel.add(ok);
1408 buttonPanel.add(cancel);
1409 buttonPanel.add(loadColours);
1410 buttonPanel.add(saveColours);
1411 bigPanel.add(scrollPane, BorderLayout.CENTER);
1412 settingsPane.add(bigPanel, BorderLayout.CENTER);
1413 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1414 this.add(settingsPane);
1418 * Answers a suitable tooltip to show on the colour cell of the table
1422 * if true include 'click to edit' and similar text
1425 public static String getColorTooltip(FeatureColourI fcol,
1432 if (fcol.isSimpleColour())
1434 return withHint ? BASE_TOOLTIP : null;
1436 String description = fcol.getDescription();
1437 description = description.replaceAll("<", "<");
1438 description = description.replaceAll(">", ">");
1439 StringBuilder tt = new StringBuilder(description);
1442 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1444 return JvSwingUtils.wrapTooltip(true, tt.toString());
1447 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1450 boolean thr = false;
1451 StringBuilder tx = new StringBuilder();
1453 if (gcol.isColourByAttribute())
1455 tx.append(FeatureMatcher
1456 .toAttributeDisplayName(gcol.getAttributeName()));
1458 else if (!gcol.isColourByLabel())
1460 tx.append(MessageManager.getString("label.score"));
1463 if (gcol.isAboveThreshold())
1468 if (gcol.isBelowThreshold())
1473 if (gcol.isColourByLabel())
1479 if (!gcol.isColourByAttribute())
1487 Color newColor = gcol.getMaxColour();
1488 comp.setBackground(newColor);
1489 // System.err.println("Width is " + w / 2);
1490 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1491 comp.setIcon(ficon);
1492 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1493 // + newColor.getGreen() + ", " + newColor.getBlue()
1494 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1495 // + ", " + minCol.getBlue() + ")");
1497 comp.setHorizontalAlignment(SwingConstants.CENTER);
1498 comp.setText(tx.toString());
1501 // ///////////////////////////////////////////////////////////////////////
1502 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1503 // ///////////////////////////////////////////////////////////////////////
1504 class FeatureTableModel extends AbstractTableModel
1506 private String[] columnNames = {
1507 MessageManager.getString("label.feature_type"),
1508 MessageManager.getString("action.colour"),
1509 MessageManager.getString("label.filter"),
1510 MessageManager.getString("label.show") };
1512 private Object[][] data;
1514 FeatureTableModel(Object[][] data)
1519 public Object[][] getData()
1524 public void setData(Object[][] data)
1530 public int getColumnCount()
1532 return columnNames.length;
1535 public Object[] getRow(int row)
1541 public int getRowCount()
1547 public String getColumnName(int col)
1549 return columnNames[col];
1553 public Object getValueAt(int row, int col)
1555 return data[row][col];
1559 * Answers the class of the object in column c of the first row of the table
1562 public Class<?> getColumnClass(int c)
1564 Object v = getValueAt(0, c);
1565 return v == null ? null : v.getClass();
1569 public boolean isCellEditable(int row, int col)
1571 return col == 0 ? false : true;
1575 public void setValueAt(Object value, int row, int col)
1577 data[row][col] = value;
1578 fireTableCellUpdated(row, col);
1579 updateFeatureRenderer(data);
1584 class ColorRenderer extends JLabel implements TableCellRenderer
1586 Border unselectedBorder = null;
1588 Border selectedBorder = null;
1590 public ColorRenderer()
1592 setOpaque(true); // MUST do this for background to show up.
1593 setHorizontalTextPosition(SwingConstants.CENTER);
1594 setVerticalTextPosition(SwingConstants.CENTER);
1598 public Component getTableCellRendererComponent(JTable tbl, Object color,
1599 boolean isSelected, boolean hasFocus, int row, int column)
1601 FeatureColourI cellColour = (FeatureColourI) color;
1603 setBackground(tbl.getBackground());
1604 if (!cellColour.isSimpleColour())
1606 Rectangle cr = tbl.getCellRect(row, column, false);
1607 FeatureSettings.renderGraduatedColor(this, cellColour,
1608 (int) cr.getWidth(), (int) cr.getHeight());
1614 setBackground(cellColour.getColour());
1618 if (selectedBorder == null)
1620 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1621 tbl.getSelectionBackground());
1623 setBorder(selectedBorder);
1627 if (unselectedBorder == null)
1629 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1630 tbl.getBackground());
1632 setBorder(unselectedBorder);
1639 class FilterRenderer extends JLabel implements TableCellRenderer
1641 javax.swing.border.Border unselectedBorder = null;
1643 javax.swing.border.Border selectedBorder = null;
1645 public FilterRenderer()
1647 setOpaque(true); // MUST do this for background to show up.
1648 setHorizontalTextPosition(SwingConstants.CENTER);
1649 setVerticalTextPosition(SwingConstants.CENTER);
1653 public Component getTableCellRendererComponent(JTable tbl,
1654 Object filter, boolean isSelected, boolean hasFocus, int row,
1657 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1659 String asText = theFilter.toString();
1660 setBackground(tbl.getBackground());
1661 this.setText(asText);
1666 if (selectedBorder == null)
1668 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1669 tbl.getSelectionBackground());
1671 setBorder(selectedBorder);
1675 if (unselectedBorder == null)
1677 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1678 tbl.getBackground());
1680 setBorder(unselectedBorder);
1688 * update comp using rendering settings from gcol
1693 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1695 int w = comp.getWidth(), h = comp.getHeight();
1698 w = (int) comp.getPreferredSize().getWidth();
1699 h = (int) comp.getPreferredSize().getHeight();
1706 renderGraduatedColor(comp, gcol, w, h);
1709 @SuppressWarnings("serial")
1710 class ColorEditor extends AbstractCellEditor
1711 implements TableCellEditor, ActionListener
1713 FeatureColourI currentColor;
1715 FeatureTypeSettings chooser;
1721 protected static final String EDIT = "edit";
1723 int rowSelected = 0;
1725 public ColorEditor()
1727 // Set up the editor (from the table's point of view),
1728 // which is a button.
1729 // This button brings up the color chooser dialog,
1730 // which is the editor from the user's point of view.
1731 button = new JButton();
1732 button.setActionCommand(EDIT);
1733 button.addActionListener(this);
1734 button.setBorderPainted(false);
1738 * Handles events from the editor button, and from the colour/filters
1739 * dialog's OK button
1742 public void actionPerformed(ActionEvent e)
1744 if (button == e.getSource())
1746 if (currentColor.isSimpleColour())
1749 * simple colour chooser
1751 String ttl = MessageManager.formatMessage("label.select_colour_for", type);
1752 ColourChooserListener listener = new ColourChooserListener()
1755 public void colourSelected(Color c)
1757 currentColor = new FeatureColour(c);
1758 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1759 fireEditingStopped();
1762 public void cancel()
1764 fireEditingStopped();
1767 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1772 * variable colour and filters dialog
1774 chooser = new FeatureTypeSettings(fr, type);
1775 if (!Jalview.isJS())
1777 chooser.setRequestFocusEnabled(true);
1778 chooser.requestFocus();
1780 chooser.addActionListener(this);
1781 fireEditingStopped();
1787 * after OK in variable colour dialog, any changes to colour
1788 * (or filters!) are already set in FeatureRenderer, so just
1789 * update table data without triggering updateFeatureRenderer
1791 currentColor = fr.getFeatureColours().get(type);
1792 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1793 if (currentFilter == null)
1795 currentFilter = new FeatureMatcherSet();
1797 Object[] data = ((FeatureTableModel) table.getModel())
1798 .getData()[rowSelected];
1799 data[COLOUR_COLUMN] = currentColor;
1800 data[FILTER_COLUMN] = currentFilter;
1801 fireEditingStopped();
1802 // SwingJS needs an explicit repaint() here,
1803 // rather than relying upon no validation having
1804 // occurred since the stopEditing call was made.
1805 // Its laying out has not been stopped by the modal frame
1812 * Override allows access to this method from anonymous inner classes
1815 protected void fireEditingStopped()
1817 super.fireEditingStopped();
1820 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1822 public Object getCellEditorValue()
1824 return currentColor;
1827 // Implement the one method defined by TableCellEditor.
1829 public Component getTableCellEditorComponent(JTable theTable, Object value,
1830 boolean isSelected, int row, int column)
1832 currentColor = (FeatureColourI) value;
1833 this.rowSelected = row;
1834 type = table.getValueAt(row, TYPE_COLUMN).toString();
1835 button.setOpaque(true);
1836 button.setBackground(FeatureSettings.this.getBackground());
1837 if (!currentColor.isSimpleColour())
1839 JLabel btn = new JLabel();
1840 btn.setSize(button.getSize());
1841 FeatureSettings.renderGraduatedColor(btn, currentColor);
1842 button.setBackground(btn.getBackground());
1843 button.setIcon(btn.getIcon());
1844 button.setText(btn.getText());
1849 button.setIcon(null);
1850 button.setBackground(currentColor.getColour());
1857 * The cell editor for the Filter column. It displays the text of any filters
1858 * for the feature type in that row (in full as a tooltip, possible abbreviated
1859 * as display text). On click in the cell, opens the Feature Display Settings
1860 * dialog at the Filters tab.
1862 @SuppressWarnings("serial")
1863 class FilterEditor extends AbstractCellEditor
1864 implements TableCellEditor, ActionListener
1867 FeatureMatcherSetI currentFilter;
1875 protected static final String EDIT = "edit";
1877 int rowSelected = 0;
1879 public FilterEditor()
1881 button = new JButton();
1882 button.setActionCommand(EDIT);
1883 button.addActionListener(this);
1884 button.setBorderPainted(false);
1888 * Handles events from the editor button
1891 public void actionPerformed(ActionEvent e)
1893 if (button == e.getSource())
1895 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
1896 chooser.addActionListener(this);
1897 chooser.setRequestFocusEnabled(true);
1898 chooser.requestFocus();
1899 if (lastLocation != null)
1901 // todo open at its last position on screen
1902 chooser.setBounds(lastLocation.x, lastLocation.y,
1903 chooser.getWidth(), chooser.getHeight());
1906 fireEditingStopped();
1908 else if (e.getSource() instanceof Component)
1912 * after OK in variable colour dialog, any changes to filter
1913 * (or colours!) are already set in FeatureRenderer, so just
1914 * update table data without triggering updateFeatureRenderer
1916 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1917 currentFilter = fr.getFeatureFilter(type);
1918 if (currentFilter == null)
1920 currentFilter = new FeatureMatcherSet();
1923 Object[] data = ((FeatureTableModel) table.getModel())
1924 .getData()[rowSelected];
1925 data[COLOUR_COLUMN] = currentColor;
1926 data[FILTER_COLUMN] = currentFilter;
1927 fireEditingStopped();
1928 // SwingJS needs an explicit repaint() here,
1929 // rather than relying upon no validation having
1930 // occurred since the stopEditing call was made.
1931 // Its laying out has not been stopped by the modal frame
1938 public Object getCellEditorValue()
1940 return currentFilter;
1944 public Component getTableCellEditorComponent(JTable theTable, Object value,
1945 boolean isSelected, int row, int column)
1947 currentFilter = (FeatureMatcherSetI) value;
1948 this.rowSelected = row;
1949 type = table.getValueAt(row, TYPE_COLUMN).toString();
1950 button.setOpaque(true);
1951 button.setBackground(FeatureSettings.this.getBackground());
1952 button.setText(currentFilter.toString());
1953 button.setIcon(null);
1959 class FeatureIcon implements Icon
1961 FeatureColourI gcol;
1965 boolean midspace = false;
1967 int width = 50, height = 20;
1969 int s1, e1; // start and end of midpoint band for thresholded symbol
1971 Color mpcolour = Color.white;
1973 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1993 public int getIconWidth()
1999 public int getIconHeight()
2005 public void paintIcon(Component c, Graphics g, int x, int y)
2008 if (gcol.isColourByLabel())
2011 g.fillRect(0, 0, width, height);
2012 // need an icon here.
2013 g.setColor(gcol.getMaxColour());
2015 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2017 // g.setFont(g.getFont().deriveFont(
2018 // AffineTransform.getScaleInstance(
2019 // width/g.getFontMetrics().stringWidth("Label"),
2020 // height/g.getFontMetrics().getHeight())));
2022 g.drawString(MessageManager.getString("label.label"), 0, 0);
2027 Color minCol = gcol.getMinColour();
2029 g.fillRect(0, 0, s1, height);
2032 g.setColor(Color.white);
2033 g.fillRect(s1, 0, e1 - s1, height);
2035 g.setColor(gcol.getMaxColour());
2036 // g.fillRect(0, e1, width - e1, height); // BH 2018
2037 g.fillRect(e1, 0, width - e1, height);