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(
490 Arrays.asList(new String[]
494 JMenuItem dens = new JMenuItem(
495 MessageManager.getString("label.sort_by_density"));
496 dens.addActionListener(new ActionListener()
500 public void actionPerformed(ActionEvent e)
502 af.avc.sortAlignmentByFeatureDensity(
503 Arrays.asList(new String[]
509 JMenuItem selCols = new JMenuItem(
510 MessageManager.getString("label.select_columns_containing"));
511 selCols.addActionListener(new ActionListener()
514 public void actionPerformed(ActionEvent arg0)
516 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
520 JMenuItem clearCols = new JMenuItem(MessageManager
521 .getString("label.select_columns_not_containing"));
522 clearCols.addActionListener(new ActionListener()
525 public void actionPerformed(ActionEvent arg0)
527 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
531 JMenuItem hideCols = new JMenuItem(
532 MessageManager.getString("label.hide_columns_containing"));
533 hideCols.addActionListener(new ActionListener()
536 public void actionPerformed(ActionEvent arg0)
538 fr.ap.alignFrame.hideFeatureColumns(type, true);
541 JMenuItem hideOtherCols = new JMenuItem(
542 MessageManager.getString("label.hide_columns_not_containing"));
543 hideOtherCols.addActionListener(new ActionListener()
546 public void actionPerformed(ActionEvent arg0)
548 fr.ap.alignFrame.hideFeatureColumns(type, false);
554 men.add(hideOtherCols);
555 men.show(table, pt.x, pt.y);
559 synchronized public void discoverAllFeatureData()
561 Set<String> allGroups = new HashSet<>();
562 AlignmentI alignment = af.getViewport().getAlignment();
564 for (int i = 0; i < alignment.getHeight(); i++)
566 SequenceI seq = alignment.getSequenceAt(i);
567 for (String group : seq.getFeatures().getFeatureGroups(true))
569 if (group != null && !allGroups.contains(group))
571 allGroups.add(group);
572 checkGroupState(group);
583 * Synchronise gui group list and check visibility of group
586 * @return true if group is visible
588 private boolean checkGroupState(String group)
590 boolean visible = fr.checkGroupVisibility(group, true);
592 for (int g = 0; g < groupPanel.getComponentCount(); g++)
594 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
596 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
601 final String grp = group;
602 final JCheckBox check = new JCheckBox(group, visible);
603 check.setFont(new Font("Serif", Font.BOLD, 12));
604 check.setToolTipText(group);
605 check.addItemListener(new ItemListener()
608 public void itemStateChanged(ItemEvent evt)
610 fr.setGroupVisibility(check.getText(), check.isSelected());
611 resetTable(new String[] { grp });
612 af.alignPanel.paintAlignment(true, true);
615 groupPanel.add(check);
619 synchronized void resetTable(String[] groupChanged)
625 resettingTable = true;
626 typeWidth = new Hashtable<>();
627 // TODO: change avWidth calculation to 'per-sequence' average and use long
630 Set<String> displayableTypes = new HashSet<>();
631 Set<String> foundGroups = new HashSet<>();
634 * determine which feature types may be visible depending on
635 * which groups are selected, and recompute average width data
637 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
640 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
643 * get the sequence's groups for positional features
644 * and keep track of which groups are visible
646 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
647 Set<String> visibleGroups = new HashSet<>();
648 for (String group : groups)
650 if (group == null || checkGroupState(group))
652 visibleGroups.add(group);
655 foundGroups.addAll(groups);
658 * get distinct feature types for visible groups
659 * record distinct visible types, and their count and total length
661 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
662 visibleGroups.toArray(new String[visibleGroups.size()]));
663 for (String type : types)
665 displayableTypes.add(type);
666 float[] avWidth = typeWidth.get(type);
669 avWidth = new float[2];
670 typeWidth.put(type, avWidth);
672 // todo this could include features with a non-visible group
673 // - do we greatly care?
674 // todo should we include non-displayable features here, and only
675 // update when features are added?
676 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
677 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
681 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
684 if (fr.hasRenderOrder())
688 fr.findAllFeatures(groupChanged != null); // prod to update
689 // colourschemes. but don't
691 // First add the checks in the previous render order,
692 // in case the window has been closed and reopened
694 List<String> frl = fr.getRenderOrder();
695 for (int ro = frl.size() - 1; ro > -1; ro--)
697 String type = frl.get(ro);
699 if (!displayableTypes.contains(type))
704 data[dataIndex][TYPE_COLUMN] = type;
705 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
706 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
707 data[dataIndex][FILTER_COLUMN] = featureFilter == null
708 ? new FeatureMatcherSet()
710 data[dataIndex][SHOW_COLUMN] = new Boolean(
711 af.getViewport().getFeaturesDisplayed().isVisible(type));
713 displayableTypes.remove(type);
718 * process any extra features belonging only to
719 * a group which was just selected
721 while (!displayableTypes.isEmpty())
723 String type = displayableTypes.iterator().next();
724 data[dataIndex][TYPE_COLUMN] = type;
726 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
727 if (data[dataIndex][COLOUR_COLUMN] == null)
729 // "Colour has been updated in another view!!"
730 fr.clearRenderOrder();
733 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
734 data[dataIndex][FILTER_COLUMN] = featureFilter == null
735 ? new FeatureMatcherSet()
737 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
739 displayableTypes.remove(type);
742 if (originalData == null)
744 originalData = new Object[data.length][COLUMN_COUNT];
745 for (int i = 0; i < data.length; i++)
747 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
752 updateOriginalData(data);
755 table.setModel(new FeatureTableModel(data));
756 table.getColumnModel().getColumn(0).setPreferredWidth(200);
758 groupPanel.setLayout(
759 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
760 pruneGroups(foundGroups);
761 groupPanel.validate();
763 updateFeatureRenderer(data, groupChanged != null);
764 resettingTable = false;
768 * Updates 'originalData' (used for restore on Cancel) if we detect that
769 * changes have been made outwith this dialog
771 * <li>a new feature type added (and made visible)</li>
772 * <li>a feature colour changed (in the Amend Features dialog)</li>
777 protected void updateOriginalData(Object[][] foundData)
779 // todo LinkedHashMap instead of Object[][] would be nice
781 Object[][] currentData = ((FeatureTableModel) table.getModel())
783 for (Object[] row : foundData)
785 String type = (String) row[TYPE_COLUMN];
786 boolean found = false;
787 for (Object[] current : currentData)
789 if (type.equals(current[TYPE_COLUMN]))
793 * currently dependent on object equality here;
794 * really need an equals method on FeatureColour
796 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
799 * feature colour has changed externally - update originalData
801 for (Object[] original : originalData)
803 if (type.equals(original[TYPE_COLUMN]))
805 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
816 * new feature detected - add to original data (on top)
818 Object[][] newData = new Object[originalData.length
820 for (int i = 0; i < originalData.length; i++)
822 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
826 originalData = newData;
832 * Remove from the groups panel any checkboxes for groups that are not in the
833 * foundGroups set. This enables removing a group from the display when the
834 * last feature in that group is deleted.
838 protected void pruneGroups(Set<String> foundGroups)
840 for (int g = 0; g < groupPanel.getComponentCount(); g++)
842 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
843 if (!foundGroups.contains(checkbox.getText()))
845 groupPanel.remove(checkbox);
851 * reorder data based on the featureRenderers global priority list.
855 private void ensureOrder(Object[][] data)
857 boolean sort = false;
858 float[] order = new float[data.length];
859 for (int i = 0; i < order.length; i++)
861 order[i] = fr.getOrder(data[i][0].toString());
864 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
868 sort = sort || order[i - 1] > order[i];
873 jalview.util.QuickSort.sort(order, data);
878 * Offers a file chooser dialog, and then loads the feature colours and
879 * filters from file in XML format and unmarshals to Jalview feature settings
883 JalviewFileChooser chooser = new JalviewFileChooser("fc",
884 SEQUENCE_FEATURE_COLOURS);
885 chooser.setFileView(new JalviewFileView());
886 chooser.setDialogTitle(
887 MessageManager.getString("label.load_feature_colours"));
888 chooser.setToolTipText(MessageManager.getString("action.load"));
889 chooser.setResponseHandler(0, new Runnable()
894 File file = chooser.getSelectedFile();
898 chooser.showOpenDialog(this);
902 * Loads feature colours and filters from XML stored in the given file
910 InputStreamReader in = new InputStreamReader(
911 new FileInputStream(file), "UTF-8");
913 JAXBContext jc = JAXBContext
914 .newInstance("jalview.xml.binding.jalview");
915 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
916 XMLStreamReader streamReader = XMLInputFactory.newInstance()
917 .createXMLStreamReader(in);
918 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
919 JalviewUserColours.class);
920 JalviewUserColours jucs = jbe.getValue();
922 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
925 * load feature colours
927 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
929 Colour newcol = jucs.getColour().get(i);
930 FeatureColourI colour = jalview.project.Jalview2XML
931 .parseColour(newcol);
932 fr.setColour(newcol.getName(), colour);
933 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
937 * load feature filters; loaded filters will replace any that are
938 * currently defined, other defined filters are left unchanged
940 for (int i = 0; i < jucs.getFilter().size(); i++)
942 Filter filterModel = jucs.getFilter().get(i);
943 String featureType = filterModel.getFeatureType();
944 FeatureMatcherSetI filter = jalview.project.Jalview2XML
945 .parseFilter(featureType, filterModel.getMatcherSet());
946 if (!filter.isEmpty())
948 fr.setFeatureFilter(featureType, filter);
953 * update feature settings table
958 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
960 updateFeatureRenderer(data, false);
963 } catch (Exception ex)
965 System.out.println("Error loading User Colour File\n" + ex);
970 * Offers a file chooser dialog, and then saves the current feature colours
971 * and any filters to the selected file in XML format
975 JalviewFileChooser chooser = new JalviewFileChooser("fc",
976 SEQUENCE_FEATURE_COLOURS);
977 chooser.setFileView(new JalviewFileView());
978 chooser.setDialogTitle(
979 MessageManager.getString("label.save_feature_colours"));
980 chooser.setToolTipText(MessageManager.getString("action.save"));
981 int option = chooser.showSaveDialog(this);
982 if (option == JalviewFileChooser.APPROVE_OPTION)
984 File file = chooser.getSelectedFile();
990 * Saves feature colours and filters to the given file
996 JalviewUserColours ucs = new JalviewUserColours();
997 ucs.setSchemeName("Sequence Features");
1000 PrintWriter out = new PrintWriter(
1001 new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
1004 * sort feature types by colour order, from 0 (highest)
1007 Set<String> fr_colours = fr.getAllFeatureColours();
1008 String[] sortedTypes = fr_colours
1009 .toArray(new String[fr_colours.size()]);
1010 Arrays.sort(sortedTypes, new Comparator<String>()
1013 public int compare(String type1, String type2)
1015 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1020 * save feature colours
1022 for (String featureType : sortedTypes)
1024 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1025 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1027 ucs.getColour().add(col);
1031 * save any feature filters
1033 for (String featureType : sortedTypes)
1035 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1036 if (filter != null && !filter.isEmpty())
1038 Iterator<FeatureMatcherI> iterator = filter.getMatchers()
1040 FeatureMatcherI firstMatcher = iterator.next();
1041 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1042 .marshalFilter(firstMatcher, iterator, filter.isAnded());
1043 Filter filterModel = new Filter();
1044 filterModel.setFeatureType(featureType);
1045 filterModel.setMatcherSet(ms);
1046 ucs.getFilter().add(filterModel);
1049 JAXBContext jaxbContext = JAXBContext
1050 .newInstance(JalviewUserColours.class);
1051 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1052 jaxbMarshaller.marshal(
1053 new ObjectFactory().createJalviewUserColours(ucs), out);
1055 // jaxbMarshaller.marshal(object, pout);
1056 // marshaller.marshal(object);
1059 // ucs.marshal(out);
1061 } catch (Exception ex)
1063 ex.printStackTrace();
1067 public void invertSelection()
1069 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1070 for (int i = 0; i < data.length; i++)
1072 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1074 updateFeatureRenderer(data, true);
1078 public void orderByAvWidth()
1080 if (table == null || table.getModel() == null)
1084 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1085 float[] width = new float[data.length];
1089 for (int i = 0; i < data.length; i++)
1091 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1094 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1095 // weight - but have to make per
1096 // sequence, too (awidth[2])
1097 // if (width[i]==1) // hack to distinguish single width sequences.
1108 boolean sort = false;
1109 for (int i = 0; i < width.length; i++)
1111 // awidth = (float[]) typeWidth.get(data[i][0]);
1114 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1117 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1123 width[i] /= max; // normalize
1124 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for
1129 sort = sort || width[i - 1] > width[i];
1134 jalview.util.QuickSort.sort(width, data);
1135 // update global priority order
1138 updateFeatureRenderer(data, false);
1146 frame.setClosed(true);
1147 } catch (Exception exe)
1154 * Update the priority order of features; only repaint if this changed the
1155 * order of visible features. Any newly discovered feature types are set to
1156 * visible. Returns true if repaint was requested, false if not.
1161 public boolean updateFeatureRenderer(Object[][] data)
1163 return updateFeatureRenderer(data, true);
1167 * Update the priority order of features; only repaint if this changed the
1168 * order of visible features. Returns true if repaint was requested, false if
1175 boolean updateFeatureRenderer(Object[][] data, boolean visibleNew)
1177 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1179 if (fr.setFeaturePriority(rowData, visibleNew))
1181 af.alignPanel.paintAlignment(true, true);
1188 * Converts table data into an array of data beans
1190 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1192 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1193 for (int i = 0; i < data.length; i++)
1195 String type = (String) data[i][TYPE_COLUMN];
1196 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1197 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1198 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1199 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1205 private void jbInit() throws Exception
1207 this.setLayout(new BorderLayout());
1209 JPanel settingsPane = new JPanel();
1210 settingsPane.setLayout(new BorderLayout());
1212 JPanel bigPanel = new JPanel();
1213 bigPanel.setLayout(new BorderLayout());
1215 groupPanel = new JPanel();
1216 bigPanel.add(groupPanel, BorderLayout.NORTH);
1218 JButton invert = new JButton(
1219 MessageManager.getString("label.invert_selection"));
1220 invert.setFont(JvSwingUtils.getLabelFont());
1221 invert.addActionListener(new ActionListener()
1224 public void actionPerformed(ActionEvent e)
1230 JButton optimizeOrder = new JButton(
1231 MessageManager.getString("label.optimise_order"));
1232 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1233 optimizeOrder.addActionListener(new ActionListener()
1236 public void actionPerformed(ActionEvent e)
1242 JButton sortByScore = new JButton(
1243 MessageManager.getString("label.seq_sort_by_score"));
1244 sortByScore.setFont(JvSwingUtils.getLabelFont());
1245 sortByScore.addActionListener(new ActionListener()
1248 public void actionPerformed(ActionEvent e)
1250 af.avc.sortAlignmentByFeatureScore(null);
1253 JButton sortByDens = new JButton(
1254 MessageManager.getString("label.sequence_sort_by_density"));
1255 sortByDens.setFont(JvSwingUtils.getLabelFont());
1256 sortByDens.addActionListener(new ActionListener()
1259 public void actionPerformed(ActionEvent e)
1261 af.avc.sortAlignmentByFeatureDensity(null);
1265 JButton help = new JButton(MessageManager.getString("action.help"));
1266 help.setFont(JvSwingUtils.getLabelFont());
1267 help.addActionListener(new ActionListener()
1270 public void actionPerformed(ActionEvent e)
1274 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1275 } catch (HelpSetException e1)
1277 e1.printStackTrace();
1282 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1283 cancel.setFont(JvSwingUtils.getLabelFont());
1284 cancel.addActionListener(new ActionListener()
1287 public void actionPerformed(ActionEvent e)
1293 JButton ok = new JButton(MessageManager.getString("action.ok"));
1294 ok.setFont(JvSwingUtils.getLabelFont());
1295 ok.addActionListener(new ActionListener()
1298 public void actionPerformed(ActionEvent e)
1304 JButton loadColours = new JButton(
1305 MessageManager.getString("label.load_colours"));
1306 loadColours.setFont(JvSwingUtils.getLabelFont());
1307 loadColours.setToolTipText(
1308 MessageManager.getString("label.load_colours_tooltip"));
1309 loadColours.addActionListener(new ActionListener()
1312 public void actionPerformed(ActionEvent e)
1318 JButton saveColours = new JButton(
1319 MessageManager.getString("label.save_colours"));
1320 saveColours.setFont(JvSwingUtils.getLabelFont());
1321 saveColours.setToolTipText(
1322 MessageManager.getString("label.save_colours_tooltip"));
1323 saveColours.addActionListener(new ActionListener()
1326 public void actionPerformed(ActionEvent e)
1331 transparency.addChangeListener(new ChangeListener()
1334 public void stateChanged(ChangeEvent evt)
1336 if (!inConstruction)
1338 fr.setTransparency((100 - transparency.getValue()) / 100f);
1339 af.alignPanel.paintAlignment(true, true);
1344 transparency.setMaximum(70);
1345 transparency.setToolTipText(
1346 MessageManager.getString("label.transparency_tip"));
1348 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1349 bigPanel.add(transPanel, BorderLayout.SOUTH);
1351 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1352 transbuttons.add(optimizeOrder);
1353 transbuttons.add(invert);
1354 transbuttons.add(sortByScore);
1355 transbuttons.add(sortByDens);
1356 transbuttons.add(help);
1357 transPanel.add(transparency);
1358 transPanel.add(transbuttons);
1360 JPanel buttonPanel = new JPanel();
1361 buttonPanel.add(ok);
1362 buttonPanel.add(cancel);
1363 buttonPanel.add(loadColours);
1364 buttonPanel.add(saveColours);
1365 bigPanel.add(scrollPane, BorderLayout.CENTER);
1366 settingsPane.add(bigPanel, BorderLayout.CENTER);
1367 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1368 this.add(settingsPane);
1372 * On Cancel, restore settings as they were when the dialog was opened (or
1373 * possibly with any new features added while the dialog was open)
1377 fr.setTransparency(originalTransparency);
1378 fr.setFeatureFilters(originalFilters);
1379 boolean repainted = updateFeatureRenderer(originalData);
1382 * ensure alignment (and Overview if visible) are redrawn
1386 af.alignPanel.paintAlignment(true, true);
1393 * Answers a suitable tooltip to show on the colour cell of the table
1397 * if true include 'click to edit' and similar text
1400 public static String getColorTooltip(FeatureColourI fcol,
1407 if (fcol.isSimpleColour())
1409 return withHint ? BASE_TOOLTIP : null;
1411 String description = fcol.getDescription();
1412 description = description.replaceAll("<", "<");
1413 description = description.replaceAll(">", ">");
1414 StringBuilder tt = new StringBuilder(description);
1417 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1419 return JvSwingUtils.wrapTooltip(true, tt.toString());
1422 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1425 boolean thr = false;
1426 StringBuilder tx = new StringBuilder();
1428 if (gcol.isColourByAttribute())
1430 tx.append(FeatureMatcher
1431 .toAttributeDisplayName(gcol.getAttributeName()));
1433 else if (!gcol.isColourByLabel())
1435 tx.append(MessageManager.getString("label.score"));
1438 if (gcol.isAboveThreshold())
1443 if (gcol.isBelowThreshold())
1448 if (gcol.isColourByLabel())
1454 if (!gcol.isColourByAttribute())
1462 Color newColor = gcol.getMaxColour();
1463 comp.setBackground(newColor);
1464 // System.err.println("Width is " + w / 2);
1465 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1466 comp.setIcon(ficon);
1467 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1468 // + newColor.getGreen() + ", " + newColor.getBlue()
1469 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1470 // + ", " + minCol.getBlue() + ")");
1472 comp.setHorizontalAlignment(SwingConstants.CENTER);
1473 comp.setText(tx.toString());
1476 // ///////////////////////////////////////////////////////////////////////
1477 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1478 // ///////////////////////////////////////////////////////////////////////
1479 class FeatureTableModel extends AbstractTableModel
1481 private String[] columnNames = {
1482 MessageManager.getString("label.feature_type"),
1483 MessageManager.getString("action.colour"),
1484 MessageManager.getString("label.configuration"),
1485 MessageManager.getString("label.show") };
1487 private Object[][] data;
1489 FeatureTableModel(Object[][] data)
1494 public Object[][] getData()
1499 public void setData(Object[][] data)
1505 public int getColumnCount()
1507 return columnNames.length;
1510 public Object[] getRow(int row)
1516 public int getRowCount()
1522 public String getColumnName(int col)
1524 return columnNames[col];
1528 public Object getValueAt(int row, int col)
1530 return data[row][col];
1534 * Answers the class of the object in column c of the first row of the table
1537 public Class<?> getColumnClass(int c)
1539 Object v = getValueAt(0, c);
1540 return v == null ? null : v.getClass();
1544 public boolean isCellEditable(int row, int col)
1546 return col == 0 ? false : true;
1550 public void setValueAt(Object value, int row, int col)
1552 data[row][col] = value;
1553 fireTableCellUpdated(row, col);
1554 updateFeatureRenderer(data);
1559 class ColorRenderer extends JLabel implements TableCellRenderer
1561 Border unselectedBorder = null;
1563 Border selectedBorder = null;
1565 public ColorRenderer()
1567 setOpaque(true); // MUST do this for background to show up.
1568 setHorizontalTextPosition(SwingConstants.CENTER);
1569 setVerticalTextPosition(SwingConstants.CENTER);
1573 public Component getTableCellRendererComponent(JTable tbl, Object color,
1574 boolean isSelected, boolean hasFocus, int row, int column)
1576 FeatureColourI cellColour = (FeatureColourI) color;
1578 setBackground(tbl.getBackground());
1579 if (!cellColour.isSimpleColour())
1581 Rectangle cr = tbl.getCellRect(row, column, false);
1582 FeatureSettings.renderGraduatedColor(this, cellColour,
1583 (int) cr.getWidth(), (int) cr.getHeight());
1589 setBackground(cellColour.getColour());
1593 if (selectedBorder == null)
1595 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1596 tbl.getSelectionBackground());
1598 setBorder(selectedBorder);
1602 if (unselectedBorder == null)
1604 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1605 tbl.getBackground());
1607 setBorder(unselectedBorder);
1614 class FilterRenderer extends JLabel implements TableCellRenderer
1616 javax.swing.border.Border unselectedBorder = null;
1618 javax.swing.border.Border selectedBorder = null;
1620 public FilterRenderer()
1622 setOpaque(true); // MUST do this for background to show up.
1623 setHorizontalTextPosition(SwingConstants.CENTER);
1624 setVerticalTextPosition(SwingConstants.CENTER);
1628 public Component getTableCellRendererComponent(JTable tbl,
1629 Object filter, boolean isSelected, boolean hasFocus, int row,
1632 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1634 String asText = theFilter.toString();
1635 setBackground(tbl.getBackground());
1636 this.setText(asText);
1641 if (selectedBorder == null)
1643 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1644 tbl.getSelectionBackground());
1646 setBorder(selectedBorder);
1650 if (unselectedBorder == null)
1652 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1653 tbl.getBackground());
1655 setBorder(unselectedBorder);
1663 * update comp using rendering settings from gcol
1668 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1670 int w = comp.getWidth(), h = comp.getHeight();
1673 w = (int) comp.getPreferredSize().getWidth();
1674 h = (int) comp.getPreferredSize().getHeight();
1681 renderGraduatedColor(comp, gcol, w, h);
1684 @SuppressWarnings("serial")
1685 class ColorEditor extends AbstractCellEditor
1686 implements TableCellEditor, ActionListener
1688 FeatureColourI currentColor;
1690 FeatureTypeSettings chooser;
1696 protected static final String EDIT = "edit";
1698 int rowSelected = 0;
1700 public ColorEditor()
1702 // Set up the editor (from the table's point of view),
1703 // which is a button.
1704 // This button brings up the color chooser dialog,
1705 // which is the editor from the user's point of view.
1706 button = new JButton();
1707 button.setActionCommand(EDIT);
1708 button.addActionListener(this);
1709 button.setBorderPainted(false);
1713 * Handles events from the editor button, and from the colour/filters
1714 * dialog's OK button
1717 public void actionPerformed(ActionEvent e)
1719 if (button == e.getSource())
1721 if (currentColor.isSimpleColour())
1724 * simple colour chooser
1726 String ttl = MessageManager
1727 .formatMessage("label.select_colour_for", type);
1728 ColourChooserListener listener = new ColourChooserListener()
1731 public void colourSelected(Color c)
1733 currentColor = new FeatureColour(c);
1734 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1735 fireEditingStopped();
1739 public void cancel()
1741 fireEditingStopped();
1744 JalviewColourChooser.showColourChooser(button, ttl,
1745 currentColor.getColour(), listener);
1750 * variable colour and filters dialog
1752 chooser = new FeatureTypeSettings(fr, type);
1753 if (!Platform.isJS())
1760 chooser.setRequestFocusEnabled(true);
1761 chooser.requestFocus();
1763 chooser.addActionListener(this);
1764 fireEditingStopped();
1770 * after OK in variable colour dialog, any changes to colour
1771 * (or filters!) are already set in FeatureRenderer, so just
1772 * update table data without triggering updateFeatureRenderer
1774 currentColor = fr.getFeatureColours().get(type);
1775 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1776 if (currentFilter == null)
1778 currentFilter = new FeatureMatcherSet();
1780 Object[] data = ((FeatureTableModel) table.getModel())
1781 .getData()[rowSelected];
1782 data[COLOUR_COLUMN] = currentColor;
1783 data[FILTER_COLUMN] = currentFilter;
1784 fireEditingStopped();
1785 // SwingJS needs an explicit repaint() here,
1786 // rather than relying upon no validation having
1787 // occurred since the stopEditing call was made.
1788 // Its laying out has not been stopped by the modal frame
1795 * Override allows access to this method from anonymous inner classes
1798 protected void fireEditingStopped()
1800 super.fireEditingStopped();
1803 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1805 public Object getCellEditorValue()
1807 return currentColor;
1810 // Implement the one method defined by TableCellEditor.
1812 public Component getTableCellEditorComponent(JTable theTable,
1813 Object value, boolean isSelected, int row, int column)
1815 currentColor = (FeatureColourI) value;
1816 this.rowSelected = row;
1817 type = table.getValueAt(row, TYPE_COLUMN).toString();
1818 button.setOpaque(true);
1819 button.setBackground(FeatureSettings.this.getBackground());
1820 if (!currentColor.isSimpleColour())
1822 JLabel btn = new JLabel();
1823 btn.setSize(button.getSize());
1824 FeatureSettings.renderGraduatedColor(btn, currentColor);
1825 button.setBackground(btn.getBackground());
1826 button.setIcon(btn.getIcon());
1827 button.setText(btn.getText());
1832 button.setIcon(null);
1833 button.setBackground(currentColor.getColour());
1840 * The cell editor for the Filter column. It displays the text of any filters
1841 * for the feature type in that row (in full as a tooltip, possible
1842 * abbreviated as display text). On click in the cell, opens the Feature
1843 * Display Settings dialog at the Filters tab.
1845 @SuppressWarnings("serial")
1846 class FilterEditor extends AbstractCellEditor
1847 implements TableCellEditor, ActionListener
1850 FeatureMatcherSetI currentFilter;
1858 protected static final String EDIT = "edit";
1860 int rowSelected = 0;
1862 public FilterEditor()
1864 button = new JButton();
1865 button.setActionCommand(EDIT);
1866 button.addActionListener(this);
1867 button.setBorderPainted(false);
1871 * Handles events from the editor button
1874 public void actionPerformed(ActionEvent e)
1876 if (button == e.getSource())
1878 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
1879 chooser.addActionListener(this);
1880 chooser.setRequestFocusEnabled(true);
1881 chooser.requestFocus();
1882 if (lastLocation != null)
1884 // todo open at its last position on screen
1885 chooser.setBounds(lastLocation.x, lastLocation.y,
1886 chooser.getWidth(), chooser.getHeight());
1889 fireEditingStopped();
1891 else if (e.getSource() instanceof Component)
1895 * after OK in variable colour dialog, any changes to filter
1896 * (or colours!) are already set in FeatureRenderer, so just
1897 * update table data without triggering updateFeatureRenderer
1899 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1900 currentFilter = fr.getFeatureFilter(type);
1901 if (currentFilter == null)
1903 currentFilter = new FeatureMatcherSet();
1906 Object[] data = ((FeatureTableModel) table.getModel())
1907 .getData()[rowSelected];
1908 data[COLOUR_COLUMN] = currentColor;
1909 data[FILTER_COLUMN] = currentFilter;
1910 fireEditingStopped();
1911 // SwingJS needs an explicit repaint() here,
1912 // rather than relying upon no validation having
1913 // occurred since the stopEditing call was made.
1914 // Its laying out has not been stopped by the modal frame
1921 public Object getCellEditorValue()
1923 return currentFilter;
1927 public Component getTableCellEditorComponent(JTable theTable,
1928 Object value, boolean isSelected, int row, int column)
1930 currentFilter = (FeatureMatcherSetI) value;
1931 this.rowSelected = row;
1932 type = table.getValueAt(row, TYPE_COLUMN).toString();
1933 button.setOpaque(true);
1934 button.setBackground(FeatureSettings.this.getBackground());
1935 button.setText(currentFilter.toString());
1936 button.setIcon(null);
1942 class FeatureIcon implements Icon
1944 FeatureColourI gcol;
1948 boolean midspace = false;
1950 int width = 50, height = 20;
1952 int s1, e1; // start and end of midpoint band for thresholded symbol
1954 Color mpcolour = Color.white;
1956 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1976 public int getIconWidth()
1982 public int getIconHeight()
1988 public void paintIcon(Component c, Graphics g, int x, int y)
1991 if (gcol.isColourByLabel())
1994 g.fillRect(0, 0, width, height);
1995 // need an icon here.
1996 g.setColor(gcol.getMaxColour());
1998 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2000 // g.setFont(g.getFont().deriveFont(
2001 // AffineTransform.getScaleInstance(
2002 // width/g.getFontMetrics().stringWidth("Label"),
2003 // height/g.getFontMetrics().getHeight())));
2005 g.drawString(MessageManager.getString("label.label"), 0, 0);
2010 Color minCol = gcol.getMinColour();
2012 g.fillRect(0, 0, s1, height);
2015 g.setColor(Color.white);
2016 g.fillRect(s1, 0, e1 - s1, height);
2018 g.setColor(gcol.getMaxColour());
2019 // g.fillRect(0, e1, width - e1, height); // BH 2018
2020 g.fillRect(e1, 0, width - e1, height);