2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.SequenceI;
27 import jalview.datamodel.features.FeatureMatcher;
28 import jalview.datamodel.features.FeatureMatcherI;
29 import jalview.datamodel.features.FeatureMatcherSet;
30 import jalview.datamodel.features.FeatureMatcherSetI;
31 import jalview.gui.Help.HelpId;
32 import jalview.gui.JalviewColourChooser.ColourChooserListener;
33 import jalview.io.JalviewFileChooser;
34 import jalview.io.JalviewFileView;
35 import jalview.schemes.FeatureColour;
36 import jalview.util.MessageManager;
37 import jalview.util.Platform;
38 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
39 import jalview.xml.binding.jalview.JalviewUserColours;
40 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
41 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
42 import jalview.xml.binding.jalview.ObjectFactory;
44 import java.awt.BorderLayout;
45 import java.awt.Color;
46 import java.awt.Component;
47 import java.awt.Dimension;
49 import java.awt.Graphics;
50 import java.awt.GridLayout;
51 import java.awt.Point;
52 import java.awt.Rectangle;
53 import java.awt.event.ActionEvent;
54 import java.awt.event.ActionListener;
55 import java.awt.event.ItemEvent;
56 import java.awt.event.ItemListener;
57 import java.awt.event.MouseAdapter;
58 import java.awt.event.MouseEvent;
59 import java.awt.event.MouseMotionAdapter;
60 import java.beans.PropertyChangeEvent;
61 import java.beans.PropertyChangeListener;
63 import java.io.FileInputStream;
64 import java.io.FileOutputStream;
65 import java.io.InputStreamReader;
66 import java.io.OutputStreamWriter;
67 import java.io.PrintWriter;
68 import java.util.Arrays;
69 import java.util.Comparator;
70 import java.util.HashMap;
71 import java.util.HashSet;
72 import java.util.Hashtable;
73 import java.util.Iterator;
74 import java.util.List;
78 import javax.help.HelpSetException;
79 import javax.swing.AbstractCellEditor;
80 import javax.swing.BorderFactory;
81 import javax.swing.Icon;
82 import javax.swing.JButton;
83 import javax.swing.JCheckBox;
84 import javax.swing.JCheckBoxMenuItem;
85 import javax.swing.JInternalFrame;
86 import javax.swing.JLabel;
87 import javax.swing.JLayeredPane;
88 import javax.swing.JMenuItem;
89 import javax.swing.JPanel;
90 import javax.swing.JPopupMenu;
91 import javax.swing.JScrollPane;
92 import javax.swing.JSlider;
93 import javax.swing.JTable;
94 import javax.swing.ListSelectionModel;
95 import javax.swing.SwingConstants;
96 import javax.swing.ToolTipManager;
97 import javax.swing.border.Border;
98 import javax.swing.event.ChangeEvent;
99 import javax.swing.event.ChangeListener;
100 import javax.swing.table.AbstractTableModel;
101 import javax.swing.table.JTableHeader;
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
135 .getString("label.click_to_edit");
137 final FeatureRenderer fr;
139 public final AlignFrame af;
142 * 'original' fields hold settings to restore on Cancel
144 Object[][] originalData;
146 float originalTransparency;
148 Map<String, FeatureMatcherSetI> originalFilters;
150 final JInternalFrame frame;
152 JScrollPane scrollPane = new JScrollPane();
158 JSlider transparency = new JSlider();
161 * when true, constructor is still executing - so ignore UI events
163 protected volatile boolean inConstruction = true;
165 int selectedRow = -1;
167 boolean resettingTable = false;
170 * true when Feature Settings are updating from feature renderer
172 boolean handlingUpdate = false;
175 * holds {featureCount, totalExtent} for each feature type
177 Map<String, float[]> typeWidth = null;
184 public FeatureSettings(AlignFrame alignFrame)
186 this.af = alignFrame;
187 fr = af.getFeatureRenderer();
189 // save transparency for restore on Cancel
190 originalTransparency = fr.getTransparency();
191 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
192 transparency.setMaximum(100 - originalTransparencyAsPercent);
194 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
199 } catch (Exception ex)
201 ex.printStackTrace();
207 public String getToolTipText(MouseEvent e)
210 int column = table.columnAtPoint(e.getPoint());
211 int row = table.rowAtPoint(e.getPoint());
216 tip = JvSwingUtils.wrapTooltip(true, MessageManager
217 .getString("label.feature_settings_click_drag"));
220 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
222 tip = getColorTooltip(colour, true);
225 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
229 .getString("label.configure_feature_tooltip")
240 * Position the tooltip near the bottom edge of, and half way across, the
244 public Point getToolTipLocation(MouseEvent e)
246 Point point = e.getPoint();
247 int column = table.columnAtPoint(point);
248 int row = table.rowAtPoint(point);
249 Rectangle r = getCellRect(row, column, false);
250 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
254 JTableHeader tableHeader = table.getTableHeader();
255 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
256 tableHeader.setReorderingAllowed(false);
257 table.setFont(new Font("Verdana", Font.PLAIN, 12));
258 ToolTipManager.sharedInstance().registerComponent(table);
259 table.setDefaultEditor(FeatureColour.class, new ColorEditor());
260 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
262 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor());
263 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
265 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
266 new ColorRenderer(), new ColorEditor());
267 table.addColumn(colourColumn);
269 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
270 new FilterRenderer(), new FilterEditor());
271 table.addColumn(filterColumn);
273 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
275 table.addMouseListener(new MouseAdapter()
278 public void mousePressed(MouseEvent evt)
280 selectedRow = table.rowAtPoint(evt.getPoint());
281 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
282 if (evt.isPopupTrigger())
284 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
285 showPopupMenu(selectedRow, type, colour, evt.getPoint());
287 else if (evt.getClickCount() == 2)
289 boolean invertSelection = evt.isAltDown();
290 boolean toggleSelection = Platform.isControlDown(evt);
291 boolean extendSelection = evt.isShiftDown();
292 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
293 invertSelection, extendSelection, toggleSelection, type);
297 // isPopupTrigger fires on mouseReleased on Windows
299 public void mouseReleased(MouseEvent evt)
301 selectedRow = table.rowAtPoint(evt.getPoint());
302 if (evt.isPopupTrigger())
304 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
305 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
306 showPopupMenu(selectedRow, type, colour, evt.getPoint());
311 table.addMouseMotionListener(new MouseMotionAdapter()
314 public void mouseDragged(MouseEvent evt)
316 int newRow = table.rowAtPoint(evt.getPoint());
317 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
320 * reposition 'selectedRow' to 'newRow' (the dragged to location)
321 * this could be more than one row away for a very fast drag action
322 * so just swap it with adjacent rows until we get it there
324 Object[][] data = ((FeatureTableModel) table.getModel())
326 int direction = newRow < selectedRow ? -1 : 1;
327 for (int i = selectedRow; i != newRow; i += direction)
329 Object[] temp = data[i];
330 data[i] = data[i + direction];
331 data[i + direction] = temp;
333 updateFeatureRenderer(data);
335 selectedRow = newRow;
339 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
340 // MessageManager.getString("label.feature_settings_click_drag")));
341 scrollPane.setViewportView(table);
343 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
345 fr.findAllFeatures(true); // display everything!
348 discoverAllFeatureData();
349 final PropertyChangeListener change;
350 final FeatureSettings fs = this;
351 fr.addPropertyChangeListener(change = new PropertyChangeListener()
354 public void propertyChange(PropertyChangeEvent evt)
356 if (!fs.resettingTable && !fs.handlingUpdate)
358 fs.handlingUpdate = true;
360 // new groups may be added with new sequence feature types only
361 fs.handlingUpdate = false;
367 frame = new JInternalFrame();
368 frame.setContentPane(this);
369 Desktop.addInternalFrame(frame,
370 MessageManager.getString("label.sequence_feature_settings"),
371 600, Platform.isAMacAndNotJS() ? 480 : 450);
372 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
374 frame.addInternalFrameListener(
375 new javax.swing.event.InternalFrameAdapter()
378 public void internalFrameClosed(
379 javax.swing.event.InternalFrameEvent evt)
381 fr.removePropertyChangeListener(change);
384 frame.setLayer(JLayeredPane.PALETTE_LAYER);
385 inConstruction = false;
389 * Constructs and shows a popup menu of possible actions on the selected row
397 protected void showPopupMenu(final int rowSelected, final String type,
398 final Object typeCol, final Point pt)
400 JPopupMenu men = new JPopupMenu(MessageManager
401 .formatMessage("label.settings_for_param", new String[]
403 final FeatureColourI featureColour = (FeatureColourI) typeCol;
406 * menu option to select (or deselect) variable colour
408 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
409 MessageManager.getString("label.variable_colour"));
410 variableColourCB.setSelected(!featureColour.isSimpleColour());
411 men.add(variableColourCB);
414 * checkbox action listener doubles up as listener to OK
415 * from the variable colour / filters dialog
417 variableColourCB.addActionListener(new ActionListener()
420 public void actionPerformed(ActionEvent e)
422 if (e.getSource() == variableColourCB)
424 men.setVisible(true); // BH 2018 for JavaScript because this is a
426 men.setVisible(false); // BH 2018 for JavaScript because this is a
428 if (featureColour.isSimpleColour())
431 * toggle simple colour to variable colour - show dialog
433 FeatureTypeSettings fc = new FeatureTypeSettings(fr, type);
434 fc.addActionListener(this);
439 * toggle variable to simple colour - show colour chooser
441 String title = MessageManager
442 .formatMessage("label.select_colour_for", type);
443 ColourChooserListener listener = new ColourChooserListener()
446 public void colourSelected(Color c)
448 table.setValueAt(new FeatureColour(c), rowSelected,
451 updateFeatureRenderer(
452 ((FeatureTableModel) table.getModel()).getData(),
456 JalviewColourChooser.showColourChooser(FeatureSettings.this,
457 title, featureColour.getMaxColour(), listener);
462 if (e.getSource() instanceof FeatureTypeSettings)
465 * update after OK in feature colour dialog; the updated
466 * colour will have already been set in the FeatureRenderer
468 FeatureColourI fci = fr.getFeatureColours().get(type);
469 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
470 // BH 2018 setting a table value does not invalidate it.
471 // System.out.println("FeatureSettings is valied" +
480 JMenuItem scr = new JMenuItem(
481 MessageManager.getString("label.sort_by_score"));
483 scr.addActionListener(new ActionListener()
487 public void actionPerformed(ActionEvent e)
489 af.avc.sortAlignmentByFeatureScore(Arrays.asList(new String[]
493 JMenuItem dens = new JMenuItem(
494 MessageManager.getString("label.sort_by_density"));
495 dens.addActionListener(new ActionListener()
499 public void actionPerformed(ActionEvent e)
501 af.avc.sortAlignmentByFeatureDensity(Arrays.asList(new String[]
507 JMenuItem selCols = new JMenuItem(
508 MessageManager.getString("label.select_columns_containing"));
509 selCols.addActionListener(new ActionListener()
512 public void actionPerformed(ActionEvent arg0)
514 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
518 JMenuItem clearCols = new JMenuItem(MessageManager
519 .getString("label.select_columns_not_containing"));
520 clearCols.addActionListener(new ActionListener()
523 public void actionPerformed(ActionEvent arg0)
525 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
529 JMenuItem hideCols = new JMenuItem(
530 MessageManager.getString("label.hide_columns_containing"));
531 hideCols.addActionListener(new ActionListener()
534 public void actionPerformed(ActionEvent arg0)
536 fr.ap.alignFrame.hideFeatureColumns(type, true);
539 JMenuItem hideOtherCols = new JMenuItem(
540 MessageManager.getString("label.hide_columns_not_containing"));
541 hideOtherCols.addActionListener(new ActionListener()
544 public void actionPerformed(ActionEvent arg0)
546 fr.ap.alignFrame.hideFeatureColumns(type, false);
552 men.add(hideOtherCols);
553 men.show(table, pt.x, pt.y);
557 synchronized public void discoverAllFeatureData()
559 Set<String> allGroups = new HashSet<>();
560 AlignmentI alignment = af.getViewport().getAlignment();
562 for (int i = 0; i < alignment.getHeight(); i++)
564 SequenceI seq = alignment.getSequenceAt(i);
565 for (String group : seq.getFeatures().getFeatureGroups(true))
567 if (group != null && !allGroups.contains(group))
569 allGroups.add(group);
570 checkGroupState(group);
581 * Synchronise gui group list and check visibility of group
584 * @return true if group is visible
586 private boolean checkGroupState(String group)
588 boolean visible = fr.checkGroupVisibility(group, true);
590 for (int g = 0; g < groupPanel.getComponentCount(); g++)
592 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
594 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
599 final String grp = group;
600 final JCheckBox check = new JCheckBox(group, visible);
601 check.setFont(new Font("Serif", Font.BOLD, 12));
602 check.setToolTipText(group);
603 check.addItemListener(new ItemListener()
606 public void itemStateChanged(ItemEvent evt)
608 fr.setGroupVisibility(check.getText(), check.isSelected());
609 resetTable(new String[] { grp });
610 af.alignPanel.paintAlignment(true, true);
613 groupPanel.add(check);
617 synchronized void resetTable(String[] groupChanged)
623 resettingTable = true;
624 typeWidth = new Hashtable<>();
625 // TODO: change avWidth calculation to 'per-sequence' average and use long
628 Set<String> displayableTypes = new HashSet<>();
629 Set<String> foundGroups = new HashSet<>();
632 * determine which feature types may be visible depending on
633 * which groups are selected, and recompute average width data
635 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
638 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
641 * get the sequence's groups for positional features
642 * and keep track of which groups are visible
644 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
645 Set<String> visibleGroups = new HashSet<>();
646 for (String group : groups)
648 if (group == null || checkGroupState(group))
650 visibleGroups.add(group);
653 foundGroups.addAll(groups);
656 * get distinct feature types for visible groups
657 * record distinct visible types, and their count and total length
659 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
660 visibleGroups.toArray(new String[visibleGroups.size()]));
661 for (String type : types)
663 displayableTypes.add(type);
664 float[] avWidth = typeWidth.get(type);
667 avWidth = new float[2];
668 typeWidth.put(type, avWidth);
670 // todo this could include features with a non-visible group
671 // - do we greatly care?
672 // todo should we include non-displayable features here, and only
673 // update when features are added?
674 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
675 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
679 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
682 if (fr.hasRenderOrder())
686 fr.findAllFeatures(groupChanged != null); // prod to update
687 // colourschemes. but don't
689 // First add the checks in the previous render order,
690 // in case the window has been closed and reopened
692 List<String> frl = fr.getRenderOrder();
693 for (int ro = frl.size() - 1; ro > -1; ro--)
695 String type = frl.get(ro);
697 if (!displayableTypes.contains(type))
702 data[dataIndex][TYPE_COLUMN] = type;
703 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
704 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
705 data[dataIndex][FILTER_COLUMN] = featureFilter == null
706 ? new FeatureMatcherSet()
708 data[dataIndex][SHOW_COLUMN] = new Boolean(
709 af.getViewport().getFeaturesDisplayed().isVisible(type));
711 displayableTypes.remove(type);
716 * process any extra features belonging only to
717 * a group which was just selected
719 while (!displayableTypes.isEmpty())
721 String type = displayableTypes.iterator().next();
722 data[dataIndex][TYPE_COLUMN] = type;
724 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
725 if (data[dataIndex][COLOUR_COLUMN] == null)
727 // "Colour has been updated in another view!!"
728 fr.clearRenderOrder();
731 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
732 data[dataIndex][FILTER_COLUMN] = featureFilter == null
733 ? new FeatureMatcherSet()
735 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
737 displayableTypes.remove(type);
740 if (originalData == null)
742 originalData = new Object[data.length][COLUMN_COUNT];
743 for (int i = 0; i < data.length; i++)
745 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
750 updateOriginalData(data);
753 table.setModel(new FeatureTableModel(data));
754 table.getColumnModel().getColumn(0).setPreferredWidth(200);
756 groupPanel.setLayout(
757 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
758 pruneGroups(foundGroups);
759 groupPanel.validate();
761 updateFeatureRenderer(data, groupChanged != null);
762 resettingTable = false;
766 * Updates 'originalData' (used for restore on Cancel) if we detect that
767 * changes have been made outwith this dialog
769 * <li>a new feature type added (and made visible)</li>
770 * <li>a feature colour changed (in the Amend Features dialog)</li>
775 protected void updateOriginalData(Object[][] foundData)
777 // todo LinkedHashMap instead of Object[][] would be nice
779 Object[][] currentData = ((FeatureTableModel) table.getModel())
781 for (Object[] row : foundData)
783 String type = (String) row[TYPE_COLUMN];
784 boolean found = false;
785 for (Object[] current : currentData)
787 if (type.equals(current[TYPE_COLUMN]))
791 * currently dependent on object equality here;
792 * really need an equals method on FeatureColour
794 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
797 * feature colour has changed externally - update originalData
799 for (Object[] original : originalData)
801 if (type.equals(original[TYPE_COLUMN]))
803 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
814 * new feature detected - add to original data (on top)
816 Object[][] newData = new Object[originalData.length
818 for (int i = 0; i < originalData.length; i++)
820 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
824 originalData = newData;
830 * Remove from the groups panel any checkboxes for groups that are not in the
831 * foundGroups set. This enables removing a group from the display when the
832 * last feature in that group is deleted.
836 protected void pruneGroups(Set<String> foundGroups)
838 for (int g = 0; g < groupPanel.getComponentCount(); g++)
840 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
841 if (!foundGroups.contains(checkbox.getText()))
843 groupPanel.remove(checkbox);
849 * reorder data based on the featureRenderers global priority list.
853 private void ensureOrder(Object[][] data)
855 boolean sort = false;
856 float[] order = new float[data.length];
857 for (int i = 0; i < order.length; i++)
859 order[i] = fr.getOrder(data[i][0].toString());
862 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
866 sort = sort || order[i - 1] > order[i];
871 jalview.util.QuickSort.sort(order, data);
876 * Offers a file chooser dialog, and then loads the feature colours and
877 * filters from file in XML format and unmarshals to Jalview feature settings
881 JalviewFileChooser chooser = new JalviewFileChooser("fc",
882 SEQUENCE_FEATURE_COLOURS);
883 chooser.setFileView(new JalviewFileView());
884 chooser.setDialogTitle(
885 MessageManager.getString("label.load_feature_colours"));
886 chooser.setToolTipText(MessageManager.getString("action.load"));
887 chooser.setResponseHandler(0, new Runnable()
892 File file = chooser.getSelectedFile();
896 chooser.showOpenDialog(this);
900 * Loads feature colours and filters from XML stored in the given file
908 InputStreamReader in = new InputStreamReader(
909 new FileInputStream(file), "UTF-8");
911 JAXBContext jc = JAXBContext
912 .newInstance("jalview.xml.binding.jalview");
913 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
914 XMLStreamReader streamReader = XMLInputFactory.newInstance()
915 .createXMLStreamReader(in);
916 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
917 JalviewUserColours.class);
918 JalviewUserColours jucs = jbe.getValue();
920 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
923 * load feature colours
925 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
927 Colour newcol = jucs.getColour().get(i);
928 FeatureColourI colour = jalview.project.Jalview2XML
929 .parseColour(newcol);
930 fr.setColour(newcol.getName(), colour);
931 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
935 * load feature filters; loaded filters will replace any that are
936 * currently defined, other defined filters are left unchanged
938 for (int i = 0; i < jucs.getFilter().size(); i++)
940 Filter filterModel = jucs.getFilter().get(i);
941 String featureType = filterModel.getFeatureType();
942 FeatureMatcherSetI filter = jalview.project.Jalview2XML
943 .parseFilter(featureType, filterModel.getMatcherSet());
944 if (!filter.isEmpty())
946 fr.setFeatureFilter(featureType, filter);
951 * update feature settings table
956 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
958 updateFeatureRenderer(data, false);
961 } catch (Exception ex)
963 System.out.println("Error loading User Colour File\n" + ex);
968 * Offers a file chooser dialog, and then saves the current feature colours
969 * and any filters to the selected file in XML format
973 JalviewFileChooser chooser = new JalviewFileChooser("fc",
974 SEQUENCE_FEATURE_COLOURS);
975 chooser.setFileView(new JalviewFileView());
976 chooser.setDialogTitle(
977 MessageManager.getString("label.save_feature_colours"));
978 chooser.setToolTipText(MessageManager.getString("action.save"));
979 int option = chooser.showSaveDialog(this);
980 if (option == JalviewFileChooser.APPROVE_OPTION)
982 File file = chooser.getSelectedFile();
988 * Saves feature colours and filters to the given file
994 JalviewUserColours ucs = new JalviewUserColours();
995 ucs.setSchemeName("Sequence Features");
998 PrintWriter out = new PrintWriter(
999 new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
1002 * sort feature types by colour order, from 0 (highest)
1005 Set<String> fr_colours = fr.getAllFeatureColours();
1006 String[] sortedTypes = fr_colours
1007 .toArray(new String[fr_colours.size()]);
1008 Arrays.sort(sortedTypes, new Comparator<String>()
1011 public int compare(String type1, String type2)
1013 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1018 * save feature colours
1020 for (String featureType : sortedTypes)
1022 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1023 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1025 ucs.getColour().add(col);
1029 * save any feature filters
1031 for (String featureType : sortedTypes)
1033 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1034 if (filter != null && !filter.isEmpty())
1036 Iterator<FeatureMatcherI> iterator = filter.getMatchers()
1038 FeatureMatcherI firstMatcher = iterator.next();
1039 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1040 .marshalFilter(firstMatcher, iterator, filter.isAnded());
1041 Filter filterModel = new Filter();
1042 filterModel.setFeatureType(featureType);
1043 filterModel.setMatcherSet(ms);
1044 ucs.getFilter().add(filterModel);
1047 JAXBContext jaxbContext = JAXBContext
1048 .newInstance(JalviewUserColours.class);
1049 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1050 jaxbMarshaller.marshal(
1051 new ObjectFactory().createJalviewUserColours(ucs), out);
1053 // jaxbMarshaller.marshal(object, pout);
1054 // marshaller.marshal(object);
1057 // ucs.marshal(out);
1059 } catch (Exception ex)
1061 ex.printStackTrace();
1065 public void invertSelection()
1067 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1068 for (int i = 0; i < data.length; i++)
1070 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1072 updateFeatureRenderer(data, true);
1076 public void orderByAvWidth()
1078 if (table == null || table.getModel() == null)
1082 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1083 float[] width = new float[data.length];
1087 for (int i = 0; i < data.length; i++)
1089 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1092 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1093 // weight - but have to make per
1094 // sequence, too (awidth[2])
1095 // if (width[i]==1) // hack to distinguish single width sequences.
1106 boolean sort = false;
1107 for (int i = 0; i < width.length; i++)
1109 // awidth = (float[]) typeWidth.get(data[i][0]);
1112 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1115 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1121 width[i] /= max; // normalize
1122 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for
1127 sort = sort || width[i - 1] > width[i];
1132 jalview.util.QuickSort.sort(width, data);
1133 // update global priority order
1136 updateFeatureRenderer(data, false);
1144 frame.setClosed(true);
1145 } catch (Exception exe)
1152 * Update the priority order of features; only repaint if this changed the
1153 * order of visible features. Any newly discovered feature types are set to
1154 * visible. Returns true if repaint was requested, false if not.
1159 public boolean updateFeatureRenderer(Object[][] data)
1161 return updateFeatureRenderer(data, true);
1165 * Update the priority order of features; only repaint if this changed the
1166 * order of visible features. Returns true if repaint was requested, false if
1173 boolean updateFeatureRenderer(Object[][] data, boolean visibleNew)
1175 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1177 if (fr.setFeaturePriority(rowData, visibleNew))
1179 af.alignPanel.paintAlignment(true, true);
1186 * Converts table data into an array of data beans
1188 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1190 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1191 for (int i = 0; i < data.length; i++)
1193 String type = (String) data[i][TYPE_COLUMN];
1194 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1195 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1196 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1197 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1203 private void jbInit() throws Exception
1205 this.setLayout(new BorderLayout());
1207 JPanel settingsPane = new JPanel();
1208 settingsPane.setLayout(new BorderLayout());
1210 JPanel bigPanel = new JPanel();
1211 bigPanel.setLayout(new BorderLayout());
1213 groupPanel = new JPanel();
1214 bigPanel.add(groupPanel, BorderLayout.NORTH);
1216 JButton invert = new JButton(
1217 MessageManager.getString("label.invert_selection"));
1218 invert.setFont(JvSwingUtils.getLabelFont());
1219 invert.addActionListener(new ActionListener()
1222 public void actionPerformed(ActionEvent e)
1228 JButton optimizeOrder = new JButton(
1229 MessageManager.getString("label.optimise_order"));
1230 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1231 optimizeOrder.addActionListener(new ActionListener()
1234 public void actionPerformed(ActionEvent e)
1240 JButton sortByScore = new JButton(
1241 MessageManager.getString("label.seq_sort_by_score"));
1242 sortByScore.setFont(JvSwingUtils.getLabelFont());
1243 sortByScore.addActionListener(new ActionListener()
1246 public void actionPerformed(ActionEvent e)
1248 af.avc.sortAlignmentByFeatureScore(null);
1251 JButton sortByDens = new JButton(
1252 MessageManager.getString("label.sequence_sort_by_density"));
1253 sortByDens.setFont(JvSwingUtils.getLabelFont());
1254 sortByDens.addActionListener(new ActionListener()
1257 public void actionPerformed(ActionEvent e)
1259 af.avc.sortAlignmentByFeatureDensity(null);
1263 JButton help = new JButton(MessageManager.getString("action.help"));
1264 help.setFont(JvSwingUtils.getLabelFont());
1265 help.addActionListener(new ActionListener()
1268 public void actionPerformed(ActionEvent e)
1272 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1273 } catch (HelpSetException e1)
1275 e1.printStackTrace();
1280 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1281 cancel.setFont(JvSwingUtils.getLabelFont());
1282 cancel.addActionListener(new ActionListener()
1285 public void actionPerformed(ActionEvent e)
1291 JButton ok = new JButton(MessageManager.getString("action.ok"));
1292 ok.setFont(JvSwingUtils.getLabelFont());
1293 ok.addActionListener(new ActionListener()
1296 public void actionPerformed(ActionEvent e)
1302 JButton loadColours = new JButton(
1303 MessageManager.getString("label.load_colours"));
1304 loadColours.setFont(JvSwingUtils.getLabelFont());
1305 loadColours.setToolTipText(
1306 MessageManager.getString("label.load_colours_tooltip"));
1307 loadColours.addActionListener(new ActionListener()
1310 public void actionPerformed(ActionEvent e)
1316 JButton saveColours = new JButton(
1317 MessageManager.getString("label.save_colours"));
1318 saveColours.setFont(JvSwingUtils.getLabelFont());
1319 saveColours.setToolTipText(
1320 MessageManager.getString("label.save_colours_tooltip"));
1321 saveColours.addActionListener(new ActionListener()
1324 public void actionPerformed(ActionEvent e)
1329 transparency.addChangeListener(new ChangeListener()
1332 public void stateChanged(ChangeEvent evt)
1334 if (!inConstruction)
1336 fr.setTransparency((100 - transparency.getValue()) / 100f);
1337 af.alignPanel.paintAlignment(true, true);
1342 transparency.setMaximum(70);
1343 transparency.setToolTipText(
1344 MessageManager.getString("label.transparency_tip"));
1346 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1347 bigPanel.add(transPanel, BorderLayout.SOUTH);
1349 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1350 transbuttons.add(optimizeOrder);
1351 transbuttons.add(invert);
1352 transbuttons.add(sortByScore);
1353 transbuttons.add(sortByDens);
1354 transbuttons.add(help);
1355 transPanel.add(transparency);
1356 transPanel.add(transbuttons);
1358 JPanel buttonPanel = new JPanel();
1359 buttonPanel.add(ok);
1360 buttonPanel.add(cancel);
1361 buttonPanel.add(loadColours);
1362 buttonPanel.add(saveColours);
1363 bigPanel.add(scrollPane, BorderLayout.CENTER);
1364 settingsPane.add(bigPanel, BorderLayout.CENTER);
1365 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1366 this.add(settingsPane);
1370 * On Cancel, restore settings as they were when the dialog was opened (or
1371 * possibly with any new features added while the dialog was open)
1375 fr.setTransparency(originalTransparency);
1376 fr.setFeatureFilters(originalFilters);
1377 boolean repainted = updateFeatureRenderer(originalData);
1380 * ensure alignment (and Overview if visible) are redrawn
1384 af.alignPanel.paintAlignment(true, true);
1391 * Answers a suitable tooltip to show on the colour cell of the table
1395 * if true include 'click to edit' and similar text
1398 public static String getColorTooltip(FeatureColourI fcol,
1405 if (fcol.isSimpleColour())
1407 return withHint ? BASE_TOOLTIP : null;
1409 String description = fcol.getDescription();
1410 description = description.replaceAll("<", "<");
1411 description = description.replaceAll(">", ">");
1412 StringBuilder tt = new StringBuilder(description);
1415 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1417 return JvSwingUtils.wrapTooltip(true, tt.toString());
1420 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1423 boolean thr = false;
1424 StringBuilder tx = new StringBuilder();
1426 if (gcol.isColourByAttribute())
1428 tx.append(FeatureMatcher
1429 .toAttributeDisplayName(gcol.getAttributeName()));
1431 else if (!gcol.isColourByLabel())
1433 tx.append(MessageManager.getString("label.score"));
1436 if (gcol.isAboveThreshold())
1441 if (gcol.isBelowThreshold())
1446 if (gcol.isColourByLabel())
1452 if (!gcol.isColourByAttribute())
1460 Color newColor = gcol.getMaxColour();
1461 comp.setBackground(newColor);
1462 // System.err.println("Width is " + w / 2);
1463 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1464 comp.setIcon(ficon);
1465 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1466 // + newColor.getGreen() + ", " + newColor.getBlue()
1467 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1468 // + ", " + minCol.getBlue() + ")");
1470 comp.setHorizontalAlignment(SwingConstants.CENTER);
1471 comp.setText(tx.toString());
1474 // ///////////////////////////////////////////////////////////////////////
1475 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1476 // ///////////////////////////////////////////////////////////////////////
1477 class FeatureTableModel extends AbstractTableModel
1479 private String[] columnNames = {
1480 MessageManager.getString("label.feature_type"),
1481 MessageManager.getString("action.colour"),
1482 MessageManager.getString("label.configuration"),
1483 MessageManager.getString("label.show") };
1485 private Object[][] data;
1487 FeatureTableModel(Object[][] data)
1492 public Object[][] getData()
1497 public void setData(Object[][] data)
1503 public int getColumnCount()
1505 return columnNames.length;
1508 public Object[] getRow(int row)
1514 public int getRowCount()
1520 public String getColumnName(int col)
1522 return columnNames[col];
1526 public Object getValueAt(int row, int col)
1528 return data[row][col];
1532 * Answers the class of the object in column c of the first row of the table
1535 public Class<?> getColumnClass(int c)
1537 Object v = getValueAt(0, c);
1538 return v == null ? null : v.getClass();
1542 public boolean isCellEditable(int row, int col)
1544 return col == 0 ? false : true;
1548 public void setValueAt(Object value, int row, int col)
1550 data[row][col] = value;
1551 fireTableCellUpdated(row, col);
1552 updateFeatureRenderer(data);
1557 class ColorRenderer extends JLabel implements TableCellRenderer
1559 Border unselectedBorder = null;
1561 Border selectedBorder = null;
1563 public ColorRenderer()
1565 setOpaque(true); // MUST do this for background to show up.
1566 setHorizontalTextPosition(SwingConstants.CENTER);
1567 setVerticalTextPosition(SwingConstants.CENTER);
1571 public Component getTableCellRendererComponent(JTable tbl, Object color,
1572 boolean isSelected, boolean hasFocus, int row, int column)
1574 FeatureColourI cellColour = (FeatureColourI) color;
1576 setBackground(tbl.getBackground());
1577 if (!cellColour.isSimpleColour())
1579 Rectangle cr = tbl.getCellRect(row, column, false);
1580 FeatureSettings.renderGraduatedColor(this, cellColour,
1581 (int) cr.getWidth(), (int) cr.getHeight());
1587 setBackground(cellColour.getColour());
1591 if (selectedBorder == null)
1593 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1594 tbl.getSelectionBackground());
1596 setBorder(selectedBorder);
1600 if (unselectedBorder == null)
1602 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1603 tbl.getBackground());
1605 setBorder(unselectedBorder);
1612 class FilterRenderer extends JLabel implements TableCellRenderer
1614 javax.swing.border.Border unselectedBorder = null;
1616 javax.swing.border.Border selectedBorder = null;
1618 public FilterRenderer()
1620 setOpaque(true); // MUST do this for background to show up.
1621 setHorizontalTextPosition(SwingConstants.CENTER);
1622 setVerticalTextPosition(SwingConstants.CENTER);
1626 public Component getTableCellRendererComponent(JTable tbl,
1627 Object filter, boolean isSelected, boolean hasFocus, int row,
1630 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1632 String asText = theFilter.toString();
1633 setBackground(tbl.getBackground());
1634 this.setText(asText);
1639 if (selectedBorder == null)
1641 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1642 tbl.getSelectionBackground());
1644 setBorder(selectedBorder);
1648 if (unselectedBorder == null)
1650 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1651 tbl.getBackground());
1653 setBorder(unselectedBorder);
1661 * update comp using rendering settings from gcol
1666 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1668 int w = comp.getWidth(), h = comp.getHeight();
1671 w = (int) comp.getPreferredSize().getWidth();
1672 h = (int) comp.getPreferredSize().getHeight();
1679 renderGraduatedColor(comp, gcol, w, h);
1682 @SuppressWarnings("serial")
1683 class ColorEditor extends AbstractCellEditor
1684 implements TableCellEditor, ActionListener
1686 FeatureColourI currentColor;
1688 FeatureTypeSettings chooser;
1694 protected static final String EDIT = "edit";
1696 int rowSelected = 0;
1698 public ColorEditor()
1700 // Set up the editor (from the table's point of view),
1701 // which is a button.
1702 // This button brings up the color chooser dialog,
1703 // which is the editor from the user's point of view.
1704 button = new JButton();
1705 button.setActionCommand(EDIT);
1706 button.addActionListener(this);
1707 button.setBorderPainted(false);
1711 * Handles events from the editor button, and from the colour/filters
1712 * dialog's OK button
1715 public void actionPerformed(ActionEvent e)
1717 if (button == e.getSource())
1719 if (currentColor.isSimpleColour())
1722 * simple colour chooser
1724 String ttl = MessageManager
1725 .formatMessage("label.select_colour_for", type);
1726 ColourChooserListener listener = new ColourChooserListener()
1729 public void colourSelected(Color c)
1731 currentColor = new FeatureColour(c);
1732 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1733 fireEditingStopped();
1737 public void cancel()
1739 fireEditingStopped();
1742 JalviewColourChooser.showColourChooser(button, ttl,
1743 currentColor.getColour(), listener);
1748 * variable colour and filters dialog
1750 chooser = new FeatureTypeSettings(fr, type);
1751 if (!Platform.isJS())
1758 chooser.setRequestFocusEnabled(true);
1759 chooser.requestFocus();
1761 chooser.addActionListener(this);
1762 fireEditingStopped();
1768 * after OK in variable colour dialog, any changes to colour
1769 * (or filters!) are already set in FeatureRenderer, so just
1770 * update table data without triggering updateFeatureRenderer
1772 currentColor = fr.getFeatureColours().get(type);
1773 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1774 if (currentFilter == null)
1776 currentFilter = new FeatureMatcherSet();
1778 Object[] data = ((FeatureTableModel) table.getModel())
1779 .getData()[rowSelected];
1780 data[COLOUR_COLUMN] = currentColor;
1781 data[FILTER_COLUMN] = currentFilter;
1782 fireEditingStopped();
1783 // SwingJS needs an explicit repaint() here,
1784 // rather than relying upon no validation having
1785 // occurred since the stopEditing call was made.
1786 // Its laying out has not been stopped by the modal frame
1793 * Override allows access to this method from anonymous inner classes
1796 protected void fireEditingStopped()
1798 super.fireEditingStopped();
1801 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1803 public Object getCellEditorValue()
1805 return currentColor;
1808 // Implement the one method defined by TableCellEditor.
1810 public Component getTableCellEditorComponent(JTable theTable,
1811 Object value, boolean isSelected, int row, int column)
1813 currentColor = (FeatureColourI) value;
1814 this.rowSelected = row;
1815 type = table.getValueAt(row, TYPE_COLUMN).toString();
1816 button.setOpaque(true);
1817 button.setBackground(FeatureSettings.this.getBackground());
1818 if (!currentColor.isSimpleColour())
1820 JLabel btn = new JLabel();
1821 btn.setSize(button.getSize());
1822 FeatureSettings.renderGraduatedColor(btn, currentColor);
1823 button.setBackground(btn.getBackground());
1824 button.setIcon(btn.getIcon());
1825 button.setText(btn.getText());
1830 button.setIcon(null);
1831 button.setBackground(currentColor.getColour());
1838 * The cell editor for the Filter column. It displays the text of any filters
1839 * for the feature type in that row (in full as a tooltip, possible
1840 * abbreviated as display text). On click in the cell, opens the Feature
1841 * Display Settings dialog at the Filters tab.
1843 @SuppressWarnings("serial")
1844 class FilterEditor extends AbstractCellEditor
1845 implements TableCellEditor, ActionListener
1848 FeatureMatcherSetI currentFilter;
1856 protected static final String EDIT = "edit";
1858 int rowSelected = 0;
1860 public FilterEditor()
1862 button = new JButton();
1863 button.setActionCommand(EDIT);
1864 button.addActionListener(this);
1865 button.setBorderPainted(false);
1869 * Handles events from the editor button
1872 public void actionPerformed(ActionEvent e)
1874 if (button == e.getSource())
1876 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
1877 chooser.addActionListener(this);
1878 chooser.setRequestFocusEnabled(true);
1879 chooser.requestFocus();
1880 if (lastLocation != null)
1882 // todo open at its last position on screen
1883 chooser.setBounds(lastLocation.x, lastLocation.y,
1884 chooser.getWidth(), chooser.getHeight());
1887 fireEditingStopped();
1889 else if (e.getSource() instanceof Component)
1893 * after OK in variable colour dialog, any changes to filter
1894 * (or colours!) are already set in FeatureRenderer, so just
1895 * update table data without triggering updateFeatureRenderer
1897 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1898 currentFilter = fr.getFeatureFilter(type);
1899 if (currentFilter == null)
1901 currentFilter = new FeatureMatcherSet();
1904 Object[] data = ((FeatureTableModel) table.getModel())
1905 .getData()[rowSelected];
1906 data[COLOUR_COLUMN] = currentColor;
1907 data[FILTER_COLUMN] = currentFilter;
1908 fireEditingStopped();
1909 // SwingJS needs an explicit repaint() here,
1910 // rather than relying upon no validation having
1911 // occurred since the stopEditing call was made.
1912 // Its laying out has not been stopped by the modal frame
1919 public Object getCellEditorValue()
1921 return currentFilter;
1925 public Component getTableCellEditorComponent(JTable theTable,
1926 Object value, boolean isSelected, int row, int column)
1928 currentFilter = (FeatureMatcherSetI) value;
1929 this.rowSelected = row;
1930 type = table.getValueAt(row, TYPE_COLUMN).toString();
1931 button.setOpaque(true);
1932 button.setBackground(FeatureSettings.this.getBackground());
1933 button.setText(currentFilter.toString());
1934 button.setIcon(null);
1940 class FeatureIcon implements Icon
1942 FeatureColourI gcol;
1946 boolean midspace = false;
1948 int width = 50, height = 20;
1950 int s1, e1; // start and end of midpoint band for thresholded symbol
1952 Color mpcolour = Color.white;
1954 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1974 public int getIconWidth()
1980 public int getIconHeight()
1986 public void paintIcon(Component c, Graphics g, int x, int y)
1989 if (gcol.isColourByLabel())
1992 g.fillRect(0, 0, width, height);
1993 // need an icon here.
1994 g.setColor(gcol.getMaxColour());
1996 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1998 // g.setFont(g.getFont().deriveFont(
1999 // AffineTransform.getScaleInstance(
2000 // width/g.getFontMetrics().stringWidth("Label"),
2001 // height/g.getFontMetrics().getHeight())));
2003 g.drawString(MessageManager.getString("label.label"), 0, 0);
2008 Color minCol = gcol.getMinColour();
2010 g.fillRect(0, 0, s1, height);
2013 g.setColor(Color.white);
2014 g.fillRect(s1, 0, e1 - s1, height);
2016 g.setColor(gcol.getMaxColour());
2017 // g.fillRect(0, e1, width - e1, height); // BH 2018
2018 g.fillRect(e1, 0, width - e1, height);