2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.bin.Jalview;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.SequenceI;
28 import jalview.datamodel.features.FeatureMatcher;
29 import jalview.datamodel.features.FeatureMatcherI;
30 import jalview.datamodel.features.FeatureMatcherSet;
31 import jalview.datamodel.features.FeatureMatcherSetI;
32 import jalview.gui.Help.HelpId;
33 import jalview.gui.JalviewColourChooser.ColourChooserListener;
34 import jalview.io.JalviewFileChooser;
35 import jalview.io.JalviewFileView;
36 import jalview.schemes.FeatureColour;
37 import jalview.util.MessageManager;
38 import jalview.util.Platform;
39 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
40 import jalview.xml.binding.jalview.JalviewUserColours;
41 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
42 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
43 import jalview.xml.binding.jalview.ObjectFactory;
45 import java.awt.BorderLayout;
46 import java.awt.Color;
47 import java.awt.Component;
48 import java.awt.Dimension;
50 import java.awt.Graphics;
51 import java.awt.GridLayout;
52 import java.awt.Point;
53 import java.awt.Rectangle;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.ActionListener;
56 import java.awt.event.ItemEvent;
57 import java.awt.event.ItemListener;
58 import java.awt.event.MouseAdapter;
59 import java.awt.event.MouseEvent;
60 import java.awt.event.MouseMotionAdapter;
61 import java.beans.PropertyChangeEvent;
62 import java.beans.PropertyChangeListener;
64 import java.io.FileInputStream;
65 import java.io.FileOutputStream;
66 import java.io.InputStreamReader;
67 import java.io.OutputStreamWriter;
68 import java.io.PrintWriter;
69 import java.util.Arrays;
70 import java.util.Comparator;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.Hashtable;
74 import java.util.Iterator;
75 import java.util.List;
79 import javax.help.HelpSetException;
80 import javax.swing.AbstractCellEditor;
81 import javax.swing.BorderFactory;
82 import javax.swing.Icon;
83 import javax.swing.JButton;
84 import javax.swing.JCheckBox;
85 import javax.swing.JCheckBoxMenuItem;
86 import javax.swing.JInternalFrame;
87 import javax.swing.JLabel;
88 import javax.swing.JLayeredPane;
89 import javax.swing.JMenuItem;
90 import javax.swing.JPanel;
91 import javax.swing.JPopupMenu;
92 import javax.swing.JScrollPane;
93 import javax.swing.JSlider;
94 import javax.swing.JTable;
95 import javax.swing.ListSelectionModel;
96 import javax.swing.SwingConstants;
97 import javax.swing.ToolTipManager;
98 import javax.swing.border.Border;
99 import javax.swing.event.ChangeEvent;
100 import javax.swing.event.ChangeListener;
101 import javax.swing.plaf.TableUI;
102 import javax.swing.table.AbstractTableModel;
103 import javax.swing.table.TableCellEditor;
104 import javax.swing.table.TableCellRenderer;
105 import javax.swing.table.TableColumn;
106 import javax.xml.bind.JAXBContext;
107 import javax.xml.bind.JAXBElement;
108 import javax.xml.bind.Marshaller;
109 import javax.xml.stream.XMLInputFactory;
110 import javax.xml.stream.XMLStreamReader;
112 public class FeatureSettings extends JPanel
113 implements FeatureSettingsControllerI
115 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
116 .getString("label.sequence_feature_colours");
119 * column indices of fields in Feature Settings table
121 static final int TYPE_COLUMN = 0;
123 static final int COLOUR_COLUMN = 1;
125 static final int FILTER_COLUMN = 2;
127 static final int SHOW_COLUMN = 3;
129 private static final int COLUMN_COUNT = 4;
131 private static final int MIN_WIDTH = 400;
133 private static final int MIN_HEIGHT = 400;
135 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
137 final FeatureRenderer fr;
139 public final AlignFrame af;
142 * 'original' fields hold settings to restore on Cancel
144 Object[][] originalData;
146 float originalTransparency;
148 Map<String, FeatureMatcherSetI> originalFilters;
150 final JInternalFrame frame;
152 JScrollPane scrollPane = new JScrollPane();
158 JSlider transparency = new JSlider();
161 * when true, constructor is still executing - so ignore UI events
163 protected volatile boolean inConstruction = true;
165 int selectedRow = -1;
167 JButton fetchDAS = new JButton();
169 JButton saveDAS = new JButton();
171 JButton cancelDAS = new JButton();
173 boolean resettingTable = false;
176 * true when Feature Settings are updating from feature renderer
178 boolean handlingUpdate = false;
181 * holds {featureCount, totalExtent} for each feature type
183 Map<String, float[]> typeWidth = null;
190 public FeatureSettings(AlignFrame alignFrame)
192 this.af = alignFrame;
193 fr = af.getFeatureRenderer();
195 // save transparency for restore on Cancel
196 originalTransparency = fr.getTransparency();
197 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
198 transparency.setMaximum(100 - originalTransparencyAsPercent);
200 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
205 } catch (Exception ex)
207 ex.printStackTrace();
213 public String getToolTipText(MouseEvent e)
216 int column = table.columnAtPoint(e.getPoint());
217 int row = table.rowAtPoint(e.getPoint());
222 tip = JvSwingUtils.wrapTooltip(true, MessageManager
223 .getString("label.feature_settings_click_drag"));
226 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
228 tip = getColorTooltip(colour, true);
231 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
234 ? MessageManager.getString("label.filters_tooltip")
246 * Position the tooltip near the bottom edge of, and half way across, the
250 public Point getToolTipLocation(MouseEvent e)
252 Point point = e.getPoint();
253 int column = table.columnAtPoint(point);
254 int row = table.rowAtPoint(point);
255 Rectangle r = getCellRect(row, column, false);
256 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
261 // next line is needed to avoid (quiet) exceptions thrown
262 // when column ordering changes so that the above constants
264 table.getTableHeader().setReorderingAllowed(false); // BH 2018
266 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
267 ToolTipManager.sharedInstance().registerComponent(table);
269 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
270 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
272 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
273 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
275 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
276 new ColorRenderer(), new ColorEditor(this));
277 table.addColumn(colourColumn);
279 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
280 new FilterRenderer(), new FilterEditor(this));
281 table.addColumn(filterColumn);
283 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
285 table.addMouseListener(new MouseAdapter()
288 public void mousePressed(MouseEvent evt)
290 selectedRow = table.rowAtPoint(evt.getPoint());
291 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
292 if (evt.isPopupTrigger())
294 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
295 showPopupMenu(selectedRow, type, colour, evt.getPoint());
297 else if (evt.getClickCount() == 2)
299 boolean invertSelection = evt.isAltDown();
300 boolean toggleSelection = Platform.isControlDown(evt);
301 boolean extendSelection = evt.isShiftDown();
302 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
303 invertSelection, extendSelection, toggleSelection, type);
307 // isPopupTrigger fires on mouseReleased on Windows
309 public void mouseReleased(MouseEvent evt)
311 selectedRow = table.rowAtPoint(evt.getPoint());
312 if (evt.isPopupTrigger())
314 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
315 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
316 showPopupMenu(selectedRow, type, colour, evt.getPoint());
321 table.addMouseMotionListener(new MouseMotionAdapter()
324 public void mouseDragged(MouseEvent evt)
326 int newRow = table.rowAtPoint(evt.getPoint());
327 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
330 * reposition 'selectedRow' to 'newRow' (the dragged to location)
331 * this could be more than one row away for a very fast drag action
332 * so just swap it with adjacent rows until we get it there
334 Object[][] data = ((FeatureTableModel) table.getModel())
336 int direction = newRow < selectedRow ? -1 : 1;
337 for (int i = selectedRow; i != newRow; i += direction)
339 Object[] temp = data[i];
340 data[i] = data[i + direction];
341 data[i + direction] = temp;
343 updateFeatureRenderer(data);
345 selectedRow = newRow;
349 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
350 // MessageManager.getString("label.feature_settings_click_drag")));
351 scrollPane.setViewportView(table);
353 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
355 fr.findAllFeatures(true); // display everything!
358 discoverAllFeatureData();
359 final PropertyChangeListener change;
360 final FeatureSettings fs = this;
361 fr.addPropertyChangeListener(change = new PropertyChangeListener()
364 public void propertyChange(PropertyChangeEvent evt)
366 if (!fs.resettingTable && !fs.handlingUpdate)
368 fs.handlingUpdate = true;
370 // new groups may be added with new sequence feature types only
371 fs.handlingUpdate = false;
377 frame = new JInternalFrame();
378 frame.setContentPane(this);
379 if (Platform.isAMac())
381 Desktop.addInternalFrame(frame,
382 MessageManager.getString("label.sequence_feature_settings"),
387 Desktop.addInternalFrame(frame,
388 MessageManager.getString("label.sequence_feature_settings"),
391 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
393 frame.addInternalFrameListener(
394 new javax.swing.event.InternalFrameAdapter()
397 public void internalFrameClosed(
398 javax.swing.event.InternalFrameEvent evt)
400 fr.removePropertyChangeListener(change);
403 frame.setLayer(JLayeredPane.PALETTE_LAYER);
404 inConstruction = false;
408 * Constructs and shows a popup menu of possible actions on the selected row and
416 protected void showPopupMenu(final int rowSelected, final String type,
417 final Object typeCol, final Point pt)
419 final FeatureColourI featureColour = (FeatureColourI) typeCol;
421 JPopupMenu men = new JPopupMenu(MessageManager
422 .formatMessage("label.settings_for_param", new String[]
424 JMenuItem scr = new JMenuItem(
425 MessageManager.getString("label.sort_by_score"));
427 final FeatureSettings me = this;
428 scr.addActionListener(new ActionListener()
432 public void actionPerformed(ActionEvent e)
435 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
439 JMenuItem dens = new JMenuItem(
440 MessageManager.getString("label.sort_by_density"));
441 dens.addActionListener(new ActionListener()
445 public void actionPerformed(ActionEvent e)
448 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
455 * variable colour options include colour by label, by score,
456 * by selected attribute text, or attribute value
458 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
459 MessageManager.getString("label.variable_colour"));
460 variableColourCB.setSelected(!featureColour.isSimpleColour());
461 men.add(variableColourCB);
464 * checkbox action listener doubles up as listener to OK
465 * from the variable colour / filters dialog
467 variableColourCB.addActionListener(new ActionListener()
470 public void actionPerformed(ActionEvent e)
472 if (e.getSource() == variableColourCB)
474 men.setVisible(true); // BH 2018 for JavaScript because this is a checkbox
475 men.setVisible(false); // BH 2018 for JavaScript because this is a checkbox
476 if (featureColour.isSimpleColour())
479 * toggle simple colour to variable colour - show dialog
481 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
482 fc.addActionListener(this);
487 * toggle variable to simple colour - show colour chooser
489 String title = MessageManager.formatMessage("label.select_colour_for", type);
490 ColourChooserListener listener = new ColourChooserListener()
493 public void colourSelected(Color c)
495 table.setValueAt(new FeatureColour(c), rowSelected,
498 me.updateFeatureRenderer(
499 ((FeatureTableModel) table.getModel()).getData(),
503 JalviewColourChooser.showColourChooser(me, title, featureColour.getMaxColour(), listener);
508 if (e.getSource() instanceof FeatureTypeSettings)
511 * update after OK in feature colour dialog; the updated
512 * colour will have already been set in the FeatureRenderer
514 FeatureColourI fci = fr.getFeatureColours().get(type);
515 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
516 // BH 2018 setting a table value does not invalidate it.
517 System.out.println("FeatureSettings is valied" + table.isValid());
524 JMenuItem selCols = new JMenuItem(
525 MessageManager.getString("label.select_columns_containing"));
526 selCols.addActionListener(new ActionListener()
529 public void actionPerformed(ActionEvent arg0)
531 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
535 JMenuItem clearCols = new JMenuItem(MessageManager
536 .getString("label.select_columns_not_containing"));
537 clearCols.addActionListener(new ActionListener()
540 public void actionPerformed(ActionEvent arg0)
542 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
546 JMenuItem hideCols = new JMenuItem(
547 MessageManager.getString("label.hide_columns_containing"));
548 hideCols.addActionListener(new ActionListener()
551 public void actionPerformed(ActionEvent arg0)
553 fr.ap.alignFrame.hideFeatureColumns(type, true);
556 JMenuItem hideOtherCols = new JMenuItem(
557 MessageManager.getString("label.hide_columns_not_containing"));
558 hideOtherCols.addActionListener(new ActionListener()
561 public void actionPerformed(ActionEvent arg0)
563 fr.ap.alignFrame.hideFeatureColumns(type, false);
569 men.add(hideOtherCols);
570 men.show(table, pt.x, pt.y);
574 synchronized public void discoverAllFeatureData()
576 Set<String> allGroups = new HashSet<>();
577 AlignmentI alignment = af.getViewport().getAlignment();
579 for (int i = 0; i < alignment.getHeight(); i++)
581 SequenceI seq = alignment.getSequenceAt(i);
582 for (String group : seq.getFeatures().getFeatureGroups(true))
584 if (group != null && !allGroups.contains(group))
586 allGroups.add(group);
587 checkGroupState(group);
598 * Synchronise gui group list and check visibility of group
601 * @return true if group is visible
603 private boolean checkGroupState(String group)
605 boolean visible = fr.checkGroupVisibility(group, true);
607 for (int g = 0; g < groupPanel.getComponentCount(); g++)
609 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
611 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
616 final String grp = group;
617 final JCheckBox check = new JCheckBox(group, visible);
618 check.setFont(new Font("Serif", Font.BOLD, 12));
619 check.setToolTipText(group);
620 check.addItemListener(new ItemListener()
623 public void itemStateChanged(ItemEvent evt)
625 fr.setGroupVisibility(check.getText(), check.isSelected());
626 resetTable(new String[] { grp });
627 af.alignPanel.paintAlignment(true, true);
630 groupPanel.add(check);
634 synchronized void resetTable(String[] groupChanged)
640 resettingTable = true;
641 typeWidth = new Hashtable<>();
642 // TODO: change avWidth calculation to 'per-sequence' average and use long
645 Set<String> displayableTypes = new HashSet<>();
646 Set<String> foundGroups = new HashSet<>();
649 * determine which feature types may be visible depending on
650 * which groups are selected, and recompute average width data
652 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
655 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
658 * get the sequence's groups for positional features
659 * and keep track of which groups are visible
661 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
662 Set<String> visibleGroups = new HashSet<>();
663 for (String group : groups)
665 if (group == null || checkGroupState(group))
667 visibleGroups.add(group);
670 foundGroups.addAll(groups);
673 * get distinct feature types for visible groups
674 * record distinct visible types, and their count and total length
676 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
677 visibleGroups.toArray(new String[visibleGroups.size()]));
678 for (String type : types)
680 displayableTypes.add(type);
681 float[] avWidth = typeWidth.get(type);
684 avWidth = new float[2];
685 typeWidth.put(type, avWidth);
687 // todo this could include features with a non-visible group
688 // - do we greatly care?
689 // todo should we include non-displayable features here, and only
690 // update when features are added?
691 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
692 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
696 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
699 if (fr.hasRenderOrder())
703 fr.findAllFeatures(groupChanged != null); // prod to update
704 // colourschemes. but don't
706 // First add the checks in the previous render order,
707 // in case the window has been closed and reopened
709 List<String> frl = fr.getRenderOrder();
710 for (int ro = frl.size() - 1; ro > -1; ro--)
712 String type = frl.get(ro);
714 if (!displayableTypes.contains(type))
719 data[dataIndex][TYPE_COLUMN] = type;
720 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
721 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
722 data[dataIndex][FILTER_COLUMN] = featureFilter == null
723 ? new FeatureMatcherSet()
725 data[dataIndex][SHOW_COLUMN] = new Boolean(
726 af.getViewport().getFeaturesDisplayed().isVisible(type));
728 displayableTypes.remove(type);
733 * process any extra features belonging only to
734 * a group which was just selected
736 while (!displayableTypes.isEmpty())
738 String type = displayableTypes.iterator().next();
739 data[dataIndex][TYPE_COLUMN] = type;
741 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
742 if (data[dataIndex][COLOUR_COLUMN] == null)
744 // "Colour has been updated in another view!!"
745 fr.clearRenderOrder();
748 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
749 data[dataIndex][FILTER_COLUMN] = featureFilter == null
750 ? new FeatureMatcherSet()
752 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
754 displayableTypes.remove(type);
757 if (originalData == null)
759 originalData = new Object[data.length][COLUMN_COUNT];
760 for (int i = 0; i < data.length; i++)
762 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
767 updateOriginalData(data);
770 table.setModel(new FeatureTableModel(data));
771 table.getColumnModel().getColumn(0).setPreferredWidth(200);
773 groupPanel.setLayout(
774 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
775 pruneGroups(foundGroups);
776 groupPanel.validate();
778 updateFeatureRenderer(data, groupChanged != null);
779 resettingTable = false;
783 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
784 * have been made outwith this dialog
786 * <li>a new feature type added (and made visible)</li>
787 * <li>a feature colour changed (in the Amend Features dialog)</li>
792 protected void updateOriginalData(Object[][] foundData)
794 // todo LinkedHashMap instead of Object[][] would be nice
796 Object[][] currentData = ((FeatureTableModel) table.getModel())
798 for (Object[] row : foundData)
800 String type = (String) row[TYPE_COLUMN];
801 boolean found = false;
802 for (Object[] current : currentData)
804 if (type.equals(current[TYPE_COLUMN]))
808 * currently dependent on object equality here;
809 * really need an equals method on FeatureColour
811 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
814 * feature colour has changed externally - update originalData
816 for (Object[] original : originalData)
818 if (type.equals(original[TYPE_COLUMN]))
820 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
831 * new feature detected - add to original data (on top)
833 Object[][] newData = new Object[originalData.length
835 for (int i = 0; i < originalData.length; i++)
837 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
841 originalData = newData;
847 * Remove from the groups panel any checkboxes for groups that are not in the
848 * foundGroups set. This enables removing a group from the display when the last
849 * feature in that group is deleted.
853 protected void pruneGroups(Set<String> foundGroups)
855 for (int g = 0; g < groupPanel.getComponentCount(); g++)
857 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
858 if (!foundGroups.contains(checkbox.getText()))
860 groupPanel.remove(checkbox);
866 * reorder data based on the featureRenderers global priority list.
870 private void ensureOrder(Object[][] data)
872 boolean sort = false;
873 float[] order = new float[data.length];
874 for (int i = 0; i < order.length; i++)
876 order[i] = fr.getOrder(data[i][0].toString());
879 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
883 sort = sort || order[i - 1] > order[i];
888 jalview.util.QuickSort.sort(order, data);
893 * Offers a file chooser dialog, and then loads the feature colours and
894 * filters from file in XML format and unmarshals to Jalview feature settings
898 JalviewFileChooser chooser = new JalviewFileChooser("fc",
899 SEQUENCE_FEATURE_COLOURS);
900 chooser.setFileView(new JalviewFileView());
901 chooser.setDialogTitle(
902 MessageManager.getString("label.load_feature_colours"));
903 chooser.setToolTipText(MessageManager.getString("action.load"));
904 chooser.setResponseHandler(0, new Runnable()
909 File file = chooser.getSelectedFile();
913 chooser.showOpenDialog(this);
917 * Loads feature colours and filters from XML stored in the given file
925 InputStreamReader in = new InputStreamReader(
926 new FileInputStream(file), "UTF-8");
928 JAXBContext jc = JAXBContext
929 .newInstance("jalview.xml.binding.jalview");
930 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
931 XMLStreamReader streamReader = XMLInputFactory.newInstance()
932 .createXMLStreamReader(in);
933 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
934 JalviewUserColours.class);
935 JalviewUserColours jucs = jbe.getValue();
937 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
940 * load feature colours
942 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
944 Colour newcol = jucs.getColour().get(i);
945 FeatureColourI colour = jalview.project.Jalview2XML
946 .parseColour(newcol);
947 fr.setColour(newcol.getName(), colour);
948 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
952 * load feature filters; loaded filters will replace any that are
953 * currently defined, other defined filters are left unchanged
955 for (int i = 0; i < jucs.getFilter().size(); i++)
957 Filter filterModel = jucs.getFilter().get(i);
958 String featureType = filterModel.getFeatureType();
959 FeatureMatcherSetI filter = jalview.project.Jalview2XML
960 .parseFilter(featureType, filterModel.getMatcherSet());
961 if (!filter.isEmpty())
963 fr.setFeatureFilter(featureType, filter);
968 * update feature settings table
973 Object[][] data = ((FeatureTableModel) table.getModel())
976 updateFeatureRenderer(data, false);
979 } catch (Exception ex)
981 System.out.println("Error loading User Colour File\n" + ex);
986 * Offers a file chooser dialog, and then saves the current feature colours
987 * and any filters to the selected file in XML format
991 JalviewFileChooser chooser = new JalviewFileChooser("fc",
992 SEQUENCE_FEATURE_COLOURS);
993 chooser.setFileView(new JalviewFileView());
994 chooser.setDialogTitle(
995 MessageManager.getString("label.save_feature_colours"));
996 chooser.setToolTipText(MessageManager.getString("action.save"));
997 int option = chooser.showSaveDialog(this);
998 if (option == JalviewFileChooser.APPROVE_OPTION)
1000 File file = chooser.getSelectedFile();
1006 * Saves feature colours and filters to the given file
1010 void save(File file)
1012 JalviewUserColours ucs = new JalviewUserColours();
1013 ucs.setSchemeName("Sequence Features");
1016 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1017 new FileOutputStream(file), "UTF-8"));
1020 * sort feature types by colour order, from 0 (highest)
1023 Set<String> fr_colours = fr.getAllFeatureColours();
1024 String[] sortedTypes = fr_colours
1025 .toArray(new String[fr_colours.size()]);
1026 Arrays.sort(sortedTypes, new Comparator<String>()
1029 public int compare(String type1, String type2)
1031 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1036 * save feature colours
1038 for (String featureType : sortedTypes)
1040 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1041 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1043 ucs.getColour().add(col);
1047 * save any feature filters
1049 for (String featureType : sortedTypes)
1051 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1052 if (filter != null && !filter.isEmpty())
1054 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1055 FeatureMatcherI firstMatcher = iterator.next();
1056 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1057 .marshalFilter(firstMatcher, iterator,
1059 Filter filterModel = new Filter();
1060 filterModel.setFeatureType(featureType);
1061 filterModel.setMatcherSet(ms);
1062 ucs.getFilter().add(filterModel);
1065 JAXBContext jaxbContext = JAXBContext
1066 .newInstance(JalviewUserColours.class);
1067 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1068 jaxbMarshaller.marshal(
1069 new ObjectFactory().createJalviewUserColours(ucs), out);
1071 // jaxbMarshaller.marshal(object, pout);
1072 // marshaller.marshal(object);
1075 // ucs.marshal(out);
1077 } catch (Exception ex)
1079 ex.printStackTrace();
1083 public void invertSelection()
1085 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1086 for (int i = 0; i < data.length; i++)
1088 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1090 updateFeatureRenderer(data, true);
1094 public void orderByAvWidth()
1096 if (table == null || table.getModel() == null)
1100 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1101 float[] width = new float[data.length];
1105 for (int i = 0; i < data.length; i++)
1107 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1110 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1111 // weight - but have to make per
1112 // sequence, too (awidth[2])
1113 // if (width[i]==1) // hack to distinguish single width sequences.
1124 boolean sort = false;
1125 for (int i = 0; i < width.length; i++)
1127 // awidth = (float[]) typeWidth.get(data[i][0]);
1130 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1133 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1139 width[i] /= max; // normalize
1140 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1144 sort = sort || width[i - 1] > width[i];
1149 jalview.util.QuickSort.sort(width, data);
1150 // update global priority order
1153 updateFeatureRenderer(data, false);
1161 frame.setClosed(true);
1162 } catch (Exception exe)
1168 public void updateFeatureRenderer(Object[][] data)
1170 updateFeatureRenderer(data, true);
1174 * Update the priority order of features; only repaint if this changed the order
1175 * of visible features
1180 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1182 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1184 if (fr.setFeaturePriority(rowData, visibleNew))
1186 af.alignPanel.paintAlignment(true, true);
1191 * Converts table data into an array of data beans
1193 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1195 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1196 for (int i = 0; i < data.length; i++)
1198 String type = (String) data[i][TYPE_COLUMN];
1199 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1200 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1201 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1202 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1208 private void jbInit() throws Exception
1210 this.setLayout(new BorderLayout());
1212 JPanel settingsPane = new JPanel();
1213 settingsPane.setLayout(new BorderLayout());
1215 JPanel bigPanel = new JPanel();
1216 bigPanel.setLayout(new BorderLayout());
1218 groupPanel = new JPanel();
1219 bigPanel.add(groupPanel, BorderLayout.NORTH);
1221 JButton invert = new JButton(
1222 MessageManager.getString("label.invert_selection"));
1223 invert.setFont(JvSwingUtils.getLabelFont());
1224 invert.addActionListener(new ActionListener()
1227 public void actionPerformed(ActionEvent e)
1233 JButton optimizeOrder = new JButton(
1234 MessageManager.getString("label.optimise_order"));
1235 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1236 optimizeOrder.addActionListener(new ActionListener()
1239 public void actionPerformed(ActionEvent e)
1245 JButton sortByScore = new JButton(
1246 MessageManager.getString("label.seq_sort_by_score"));
1247 sortByScore.setFont(JvSwingUtils.getLabelFont());
1248 sortByScore.addActionListener(new ActionListener()
1251 public void actionPerformed(ActionEvent e)
1253 af.avc.sortAlignmentByFeatureScore(null);
1256 JButton sortByDens = new JButton(
1257 MessageManager.getString("label.sequence_sort_by_density"));
1258 sortByDens.setFont(JvSwingUtils.getLabelFont());
1259 sortByDens.addActionListener(new ActionListener()
1262 public void actionPerformed(ActionEvent e)
1264 af.avc.sortAlignmentByFeatureDensity(null);
1268 JButton help = new JButton(MessageManager.getString("action.help"));
1269 help.setFont(JvSwingUtils.getLabelFont());
1270 help.addActionListener(new ActionListener()
1273 public void actionPerformed(ActionEvent e)
1277 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1278 } catch (HelpSetException e1)
1280 e1.printStackTrace();
1284 help.setFont(JvSwingUtils.getLabelFont());
1285 help.setText(MessageManager.getString("action.help"));
1286 help.addActionListener(new ActionListener()
1289 public void actionPerformed(ActionEvent e)
1293 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1294 } catch (HelpSetException e1)
1296 e1.printStackTrace();
1301 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1302 cancel.setFont(JvSwingUtils.getLabelFont());
1303 cancel.addActionListener(new ActionListener()
1306 public void actionPerformed(ActionEvent e)
1308 fr.setTransparency(originalTransparency);
1309 fr.setFeatureFilters(originalFilters);
1310 updateFeatureRenderer(originalData);
1315 JButton ok = new JButton(MessageManager.getString("action.ok"));
1316 ok.setFont(JvSwingUtils.getLabelFont());
1317 ok.addActionListener(new ActionListener()
1320 public void actionPerformed(ActionEvent e)
1326 JButton loadColours = new JButton(
1327 MessageManager.getString("label.load_colours"));
1328 loadColours.setFont(JvSwingUtils.getLabelFont());
1329 loadColours.setToolTipText(
1330 MessageManager.getString("label.load_colours_tooltip"));
1331 loadColours.addActionListener(new ActionListener()
1334 public void actionPerformed(ActionEvent e)
1340 JButton saveColours = new JButton(
1341 MessageManager.getString("label.save_colours"));
1342 saveColours.setFont(JvSwingUtils.getLabelFont());
1343 saveColours.setToolTipText(
1344 MessageManager.getString("label.save_colours_tooltip"));
1345 saveColours.addActionListener(new ActionListener()
1348 public void actionPerformed(ActionEvent e)
1353 transparency.addChangeListener(new ChangeListener()
1356 public void stateChanged(ChangeEvent evt)
1358 if (!inConstruction)
1360 fr.setTransparency((100 - transparency.getValue()) / 100f);
1361 af.alignPanel.paintAlignment(true, true);
1366 transparency.setMaximum(70);
1367 transparency.setToolTipText(
1368 MessageManager.getString("label.transparency_tip"));
1370 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1371 bigPanel.add(transPanel, BorderLayout.SOUTH);
1373 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1374 transbuttons.add(optimizeOrder);
1375 transbuttons.add(invert);
1376 transbuttons.add(sortByScore);
1377 transbuttons.add(sortByDens);
1378 transbuttons.add(help);
1379 transPanel.add(transparency);
1380 transPanel.add(transbuttons);
1382 JPanel buttonPanel = new JPanel();
1383 buttonPanel.add(ok);
1384 buttonPanel.add(cancel);
1385 buttonPanel.add(loadColours);
1386 buttonPanel.add(saveColours);
1387 bigPanel.add(scrollPane, BorderLayout.CENTER);
1388 settingsPane.add(bigPanel, BorderLayout.CENTER);
1389 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1390 this.add(settingsPane);
1394 * Answers a suitable tooltip to show on the colour cell of the table
1398 * if true include 'click to edit' and similar text
1401 public static String getColorTooltip(FeatureColourI fcol,
1408 if (fcol.isSimpleColour())
1410 return withHint ? BASE_TOOLTIP : null;
1412 String description = fcol.getDescription();
1413 description = description.replaceAll("<", "<");
1414 description = description.replaceAll(">", ">");
1415 StringBuilder tt = new StringBuilder(description);
1418 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1420 return JvSwingUtils.wrapTooltip(true, tt.toString());
1423 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1426 boolean thr = false;
1427 StringBuilder tx = new StringBuilder();
1429 if (gcol.isColourByAttribute())
1431 tx.append(FeatureMatcher
1432 .toAttributeDisplayName(gcol.getAttributeName()));
1434 else if (!gcol.isColourByLabel())
1436 tx.append(MessageManager.getString("label.score"));
1439 if (gcol.isAboveThreshold())
1444 if (gcol.isBelowThreshold())
1449 if (gcol.isColourByLabel())
1455 if (!gcol.isColourByAttribute())
1463 Color newColor = gcol.getMaxColour();
1464 comp.setBackground(newColor);
1465 // System.err.println("Width is " + w / 2);
1466 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1467 comp.setIcon(ficon);
1468 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1469 // + newColor.getGreen() + ", " + newColor.getBlue()
1470 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1471 // + ", " + minCol.getBlue() + ")");
1473 comp.setHorizontalAlignment(SwingConstants.CENTER);
1474 comp.setText(tx.toString());
1477 // ///////////////////////////////////////////////////////////////////////
1478 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1479 // ///////////////////////////////////////////////////////////////////////
1480 class FeatureTableModel extends AbstractTableModel
1482 private String[] columnNames = {
1483 MessageManager.getString("label.feature_type"),
1484 MessageManager.getString("action.colour"),
1485 MessageManager.getString("label.filter"),
1486 MessageManager.getString("label.show") };
1488 private Object[][] data;
1490 FeatureTableModel(Object[][] data)
1495 public Object[][] getData()
1500 public void setData(Object[][] data)
1506 public int getColumnCount()
1508 return columnNames.length;
1511 public Object[] getRow(int row)
1517 public int getRowCount()
1523 public String getColumnName(int col)
1525 return columnNames[col];
1529 public Object getValueAt(int row, int col)
1531 return data[row][col];
1535 * Answers the class of the object in column c of the first row of the table
1538 public Class<?> getColumnClass(int c)
1540 Object v = getValueAt(0, c);
1541 return v == null ? null : v.getClass();
1545 public boolean isCellEditable(int row, int col)
1547 return col == 0 ? false : true;
1551 public void setValueAt(Object value, int row, int col)
1553 data[row][col] = value;
1554 fireTableCellUpdated(row, col);
1555 updateFeatureRenderer(data);
1560 class ColorRenderer extends JLabel implements TableCellRenderer
1562 Border unselectedBorder = null;
1564 Border selectedBorder = null;
1566 public ColorRenderer()
1568 setOpaque(true); // MUST do this for background to show up.
1569 setHorizontalTextPosition(SwingConstants.CENTER);
1570 setVerticalTextPosition(SwingConstants.CENTER);
1574 public Component getTableCellRendererComponent(JTable tbl, Object color,
1575 boolean isSelected, boolean hasFocus, int row, int column)
1577 FeatureColourI cellColour = (FeatureColourI) color;
1579 setBackground(tbl.getBackground());
1580 if (!cellColour.isSimpleColour())
1582 Rectangle cr = tbl.getCellRect(row, column, false);
1583 FeatureSettings.renderGraduatedColor(this, cellColour,
1584 (int) cr.getWidth(), (int) cr.getHeight());
1590 setBackground(cellColour.getColour());
1594 if (selectedBorder == null)
1596 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1597 tbl.getSelectionBackground());
1599 setBorder(selectedBorder);
1603 if (unselectedBorder == null)
1605 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1606 tbl.getBackground());
1608 setBorder(unselectedBorder);
1615 class FilterRenderer extends JLabel implements TableCellRenderer
1617 javax.swing.border.Border unselectedBorder = null;
1619 javax.swing.border.Border selectedBorder = null;
1621 public FilterRenderer()
1623 setOpaque(true); // MUST do this for background to show up.
1624 setHorizontalTextPosition(SwingConstants.CENTER);
1625 setVerticalTextPosition(SwingConstants.CENTER);
1629 public Component getTableCellRendererComponent(JTable tbl,
1630 Object filter, boolean isSelected, boolean hasFocus, int row,
1633 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1635 String asText = theFilter.toString();
1636 setBackground(tbl.getBackground());
1637 this.setText(asText);
1642 if (selectedBorder == null)
1644 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1645 tbl.getSelectionBackground());
1647 setBorder(selectedBorder);
1651 if (unselectedBorder == null)
1653 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1654 tbl.getBackground());
1656 setBorder(unselectedBorder);
1664 * update comp using rendering settings from gcol
1669 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1671 int w = comp.getWidth(), h = comp.getHeight();
1674 w = (int) comp.getPreferredSize().getWidth();
1675 h = (int) comp.getPreferredSize().getHeight();
1682 renderGraduatedColor(comp, gcol, w, h);
1685 class ColorEditor extends AbstractCellEditor
1686 implements TableCellEditor, ActionListener
1690 FeatureColourI currentColor;
1692 FeatureTypeSettings chooser;
1698 protected static final String EDIT = "edit";
1700 int rowSelected = 0;
1702 public ColorEditor(FeatureSettings fs)
1705 // Set up the editor (from the table's point of view),
1706 // which is a button.
1707 // This button brings up the color chooser dialog,
1708 // which is the editor from the user's point of view.
1709 button = new JButton();
1710 button.setActionCommand(EDIT);
1711 button.addActionListener(this);
1712 button.setBorderPainted(false);
1716 * Handles events from the editor button, and from the colour/filters
1717 * dialog's OK button
1720 public void actionPerformed(ActionEvent e)
1722 if (button == e.getSource())
1724 if (currentColor.isSimpleColour())
1727 * simple colour chooser
1729 String ttl = MessageManager.formatMessage("label.select_colour_for", type);
1730 ColourChooserListener listener = new ColourChooserListener()
1733 public void colourSelected(Color c)
1735 currentColor = new FeatureColour(c);
1736 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1737 fireEditingStopped();
1740 public void cancel()
1742 fireEditingStopped();
1745 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1750 * variable colour and filters dialog
1752 chooser = new FeatureTypeSettings(me.fr, type);
1753 if (!Jalview.isJS())
1755 chooser.setRequestFocusEnabled(true);
1756 chooser.requestFocus();
1758 chooser.addActionListener(this);
1759 fireEditingStopped();
1765 * after OK in variable colour dialog, any changes to colour
1766 * (or filters!) are already set in FeatureRenderer, so just
1767 * update table data without triggering updateFeatureRenderer
1769 currentColor = fr.getFeatureColours().get(type);
1770 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1771 if (currentFilter == null)
1773 currentFilter = new FeatureMatcherSet();
1775 Object[] data = ((FeatureTableModel) table.getModel())
1776 .getData()[rowSelected];
1777 data[COLOUR_COLUMN] = currentColor;
1778 data[FILTER_COLUMN] = currentFilter;
1780 fireEditingStopped();
1781 me.table.validate();
1785 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1787 public Object getCellEditorValue()
1789 return currentColor;
1792 // Implement the one method defined by TableCellEditor.
1794 public Component getTableCellEditorComponent(JTable theTable, Object value,
1795 boolean isSelected, int row, int column)
1797 currentColor = (FeatureColourI) value;
1798 this.rowSelected = row;
1799 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1800 button.setOpaque(true);
1801 button.setBackground(me.getBackground());
1802 if (!currentColor.isSimpleColour())
1804 JLabel btn = new JLabel();
1805 btn.setSize(button.getSize());
1806 FeatureSettings.renderGraduatedColor(btn, currentColor);
1807 button.setBackground(btn.getBackground());
1808 button.setIcon(btn.getIcon());
1809 button.setText(btn.getText());
1814 button.setIcon(null);
1815 button.setBackground(currentColor.getColour());
1822 * The cell editor for the Filter column. It displays the text of any filters
1823 * for the feature type in that row (in full as a tooltip, possible abbreviated
1824 * as display text). On click in the cell, opens the Feature Display Settings
1825 * dialog at the Filters tab.
1827 class FilterEditor extends AbstractCellEditor
1828 implements TableCellEditor, ActionListener
1832 FeatureMatcherSetI currentFilter;
1840 protected static final String EDIT = "edit";
1842 int rowSelected = 0;
1844 public FilterEditor(FeatureSettings me)
1847 button = new JButton();
1848 button.setActionCommand(EDIT);
1849 button.addActionListener(this);
1850 button.setBorderPainted(false);
1854 * Handles events from the editor button
1857 public void actionPerformed(ActionEvent e)
1859 if (button == e.getSource())
1861 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1862 chooser.addActionListener(this);
1863 chooser.setRequestFocusEnabled(true);
1864 chooser.requestFocus();
1865 if (lastLocation != null)
1867 // todo open at its last position on screen
1868 chooser.setBounds(lastLocation.x, lastLocation.y,
1869 chooser.getWidth(), chooser.getHeight());
1872 fireEditingStopped();
1874 else if (e.getSource() instanceof Component)
1878 * after OK in variable colour dialog, any changes to filter
1879 * (or colours!) are already set in FeatureRenderer, so just
1880 * update table data without triggering updateFeatureRenderer
1882 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1883 currentFilter = me.fr.getFeatureFilter(type);
1884 if (currentFilter == null)
1886 currentFilter = new FeatureMatcherSet();
1888 Object[] data = ((FeatureTableModel) table.getModel())
1889 .getData()[rowSelected];
1890 data[COLOUR_COLUMN] = currentColor;
1891 data[FILTER_COLUMN] = currentFilter;
1892 fireEditingStopped();
1893 me.table.validate();
1898 public Object getCellEditorValue()
1900 return currentFilter;
1904 public Component getTableCellEditorComponent(JTable theTable, Object value,
1905 boolean isSelected, int row, int column)
1907 currentFilter = (FeatureMatcherSetI) value;
1908 this.rowSelected = row;
1909 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1910 button.setOpaque(true);
1911 button.setBackground(me.getBackground());
1912 button.setText(currentFilter.toString());
1913 button.setIcon(null);
1919 class FeatureIcon implements Icon
1921 FeatureColourI gcol;
1925 boolean midspace = false;
1927 int width = 50, height = 20;
1929 int s1, e1; // start and end of midpoint band for thresholded symbol
1931 Color mpcolour = Color.white;
1933 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1953 public int getIconWidth()
1959 public int getIconHeight()
1965 public void paintIcon(Component c, Graphics g, int x, int y)
1968 if (gcol.isColourByLabel())
1971 g.fillRect(0, 0, width, height);
1972 // need an icon here.
1973 g.setColor(gcol.getMaxColour());
1975 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1977 // g.setFont(g.getFont().deriveFont(
1978 // AffineTransform.getScaleInstance(
1979 // width/g.getFontMetrics().stringWidth("Label"),
1980 // height/g.getFontMetrics().getHeight())));
1982 g.drawString(MessageManager.getString("label.label"), 0, 0);
1987 Color minCol = gcol.getMinColour();
1989 g.fillRect(0, 0, s1, height);
1992 g.setColor(Color.white);
1993 g.fillRect(s1, 0, e1 - s1, height);
1995 g.setColor(gcol.getMaxColour());
1996 // g.fillRect(0, e1, width - e1, height); // BH 2018
1997 g.fillRect(e1, 0, width - e1, height);