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.util.dialogrunner.RunResponse;
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 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
298 else if (evt.getClickCount() == 2)
300 boolean invertSelection = evt.isAltDown();
301 boolean toggleSelection = Platform.isControlDown(evt);
302 boolean extendSelection = evt.isShiftDown();
303 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
304 invertSelection, extendSelection, toggleSelection, type);
308 // isPopupTrigger fires on mouseReleased on Windows
310 public void mouseReleased(MouseEvent evt)
312 selectedRow = table.rowAtPoint(evt.getPoint());
313 if (evt.isPopupTrigger())
315 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
316 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
317 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
323 table.addMouseMotionListener(new MouseMotionAdapter()
326 public void mouseDragged(MouseEvent evt)
328 int newRow = table.rowAtPoint(evt.getPoint());
329 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
332 * reposition 'selectedRow' to 'newRow' (the dragged to location)
333 * this could be more than one row away for a very fast drag action
334 * so just swap it with adjacent rows until we get it there
336 Object[][] data = ((FeatureTableModel) table.getModel())
338 int direction = newRow < selectedRow ? -1 : 1;
339 for (int i = selectedRow; i != newRow; i += direction)
341 Object[] temp = data[i];
342 data[i] = data[i + direction];
343 data[i + direction] = temp;
345 updateFeatureRenderer(data);
347 selectedRow = newRow;
351 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
352 // MessageManager.getString("label.feature_settings_click_drag")));
353 scrollPane.setViewportView(table);
355 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
357 fr.findAllFeatures(true); // display everything!
360 discoverAllFeatureData();
361 final PropertyChangeListener change;
362 final FeatureSettings fs = this;
363 fr.addPropertyChangeListener(change = new PropertyChangeListener()
366 public void propertyChange(PropertyChangeEvent evt)
368 if (!fs.resettingTable && !fs.handlingUpdate)
370 fs.handlingUpdate = true;
372 // new groups may be added with new sequence feature types only
373 fs.handlingUpdate = false;
379 frame = new JInternalFrame();
380 frame.setContentPane(this);
381 if (Platform.isAMac())
383 Desktop.addInternalFrame(frame,
384 MessageManager.getString("label.sequence_feature_settings"),
389 Desktop.addInternalFrame(frame,
390 MessageManager.getString("label.sequence_feature_settings"),
393 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
395 frame.addInternalFrameListener(
396 new javax.swing.event.InternalFrameAdapter()
399 public void internalFrameClosed(
400 javax.swing.event.InternalFrameEvent evt)
402 fr.removePropertyChangeListener(change);
405 frame.setLayer(JLayeredPane.PALETTE_LAYER);
406 inConstruction = false;
409 protected void popupSort(final int rowSelected, final String type,
410 final Object typeCol, final Map<String, float[][]> minmax, int x,
413 final FeatureColourI featureColour = (FeatureColourI) typeCol;
415 JPopupMenu men = new JPopupMenu(MessageManager
416 .formatMessage("label.settings_for_param", new String[]
418 JMenuItem scr = new JMenuItem(
419 MessageManager.getString("label.sort_by_score"));
421 final FeatureSettings me = this;
422 scr.addActionListener(new ActionListener()
426 public void actionPerformed(ActionEvent e)
429 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
434 JMenuItem dens = new JMenuItem(
435 MessageManager.getString("label.sort_by_density"));
436 dens.addActionListener(new ActionListener()
440 public void actionPerformed(ActionEvent e)
443 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
451 * variable colour options include colour by label, by score,
452 * by selected attribute text, or attribute value
454 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
455 MessageManager.getString("label.variable_colour"));
456 variableColourCB.setSelected(!featureColour.isSimpleColour());
457 men.add(variableColourCB);
460 * checkbox action listener doubles up as listener to OK
461 * from the variable colour / filters dialog
463 variableColourCB.addActionListener(new ActionListener()
466 public void actionPerformed(ActionEvent e)
468 if (e.getSource() == variableColourCB)
470 if (featureColour.isSimpleColour())
473 * toggle simple colour to variable colour - show dialog
475 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
476 fc.addActionListener(this);
481 * toggle variable to simple colour - show colour chooser
483 String title = MessageManager.getString("label.select_colour");
484 ColourChooserListener listener = new ColourChooserListener()
487 public void colourSelected(Color c)
489 table.setValueAt(new FeatureColour(c), rowSelected,
492 me.updateFeatureRenderer(
493 ((FeatureTableModel) table.getModel()).getData(),
497 JalviewColourChooser.showColourChooser(me, title, featureColour.getMaxColour(), listener);
501 if (e.getSource() instanceof FeatureTypeSettings)
504 * update after OK in feature colour dialog; the updated
505 * colour will have already been set in the FeatureRenderer
507 FeatureColourI fci = fr.getFeatureColours().get(type);
508 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
516 JMenuItem selCols = new JMenuItem(
517 MessageManager.getString("label.select_columns_containing"));
518 selCols.addActionListener(new ActionListener()
521 public void actionPerformed(ActionEvent arg0)
523 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
527 JMenuItem clearCols = new JMenuItem(MessageManager
528 .getString("label.select_columns_not_containing"));
529 clearCols.addActionListener(new ActionListener()
532 public void actionPerformed(ActionEvent arg0)
534 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
538 JMenuItem hideCols = new JMenuItem(
539 MessageManager.getString("label.hide_columns_containing"));
540 hideCols.addActionListener(new ActionListener()
543 public void actionPerformed(ActionEvent arg0)
545 fr.ap.alignFrame.hideFeatureColumns(type, true);
548 JMenuItem hideOtherCols = new JMenuItem(
549 MessageManager.getString("label.hide_columns_not_containing"));
550 hideOtherCols.addActionListener(new ActionListener()
553 public void actionPerformed(ActionEvent arg0)
555 fr.ap.alignFrame.hideFeatureColumns(type, false);
561 men.add(hideOtherCols);
562 men.show(table, x, y);
566 synchronized public void discoverAllFeatureData()
568 Set<String> allGroups = new HashSet<>();
569 AlignmentI alignment = af.getViewport().getAlignment();
571 for (int i = 0; i < alignment.getHeight(); i++)
573 SequenceI seq = alignment.getSequenceAt(i);
574 for (String group : seq.getFeatures().getFeatureGroups(true))
576 if (group != null && !allGroups.contains(group))
578 allGroups.add(group);
579 checkGroupState(group);
590 * Synchronise gui group list and check visibility of group
593 * @return true if group is visible
595 private boolean checkGroupState(String group)
597 boolean visible = fr.checkGroupVisibility(group, true);
599 for (int g = 0; g < groupPanel.getComponentCount(); g++)
601 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
603 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
608 final String grp = group;
609 final JCheckBox check = new JCheckBox(group, visible);
610 check.setFont(new Font("Serif", Font.BOLD, 12));
611 check.setToolTipText(group);
612 check.addItemListener(new ItemListener()
615 public void itemStateChanged(ItemEvent evt)
617 fr.setGroupVisibility(check.getText(), check.isSelected());
618 resetTable(new String[] { grp });
619 af.alignPanel.paintAlignment(true, true);
622 groupPanel.add(check);
626 synchronized void resetTable(String[] groupChanged)
632 resettingTable = true;
633 typeWidth = new Hashtable<>();
634 // TODO: change avWidth calculation to 'per-sequence' average and use long
637 Set<String> displayableTypes = new HashSet<>();
638 Set<String> foundGroups = new HashSet<>();
641 * determine which feature types may be visible depending on
642 * which groups are selected, and recompute average width data
644 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
647 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
650 * get the sequence's groups for positional features
651 * and keep track of which groups are visible
653 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
654 Set<String> visibleGroups = new HashSet<>();
655 for (String group : groups)
657 if (group == null || checkGroupState(group))
659 visibleGroups.add(group);
662 foundGroups.addAll(groups);
665 * get distinct feature types for visible groups
666 * record distinct visible types, and their count and total length
668 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
669 visibleGroups.toArray(new String[visibleGroups.size()]));
670 for (String type : types)
672 displayableTypes.add(type);
673 float[] avWidth = typeWidth.get(type);
676 avWidth = new float[2];
677 typeWidth.put(type, avWidth);
679 // todo this could include features with a non-visible group
680 // - do we greatly care?
681 // todo should we include non-displayable features here, and only
682 // update when features are added?
683 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
684 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
688 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
691 if (fr.hasRenderOrder())
695 fr.findAllFeatures(groupChanged != null); // prod to update
696 // colourschemes. but don't
698 // First add the checks in the previous render order,
699 // in case the window has been closed and reopened
701 List<String> frl = fr.getRenderOrder();
702 for (int ro = frl.size() - 1; ro > -1; ro--)
704 String type = frl.get(ro);
706 if (!displayableTypes.contains(type))
711 data[dataIndex][TYPE_COLUMN] = type;
712 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
713 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
714 data[dataIndex][FILTER_COLUMN] = featureFilter == null
715 ? new FeatureMatcherSet()
717 data[dataIndex][SHOW_COLUMN] = new Boolean(
718 af.getViewport().getFeaturesDisplayed().isVisible(type));
720 displayableTypes.remove(type);
725 * process any extra features belonging only to
726 * a group which was just selected
728 while (!displayableTypes.isEmpty())
730 String type = displayableTypes.iterator().next();
731 data[dataIndex][TYPE_COLUMN] = type;
733 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
734 if (data[dataIndex][COLOUR_COLUMN] == null)
736 // "Colour has been updated in another view!!"
737 fr.clearRenderOrder();
740 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
741 data[dataIndex][FILTER_COLUMN] = featureFilter == null
742 ? new FeatureMatcherSet()
744 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
746 displayableTypes.remove(type);
749 if (originalData == null)
751 originalData = new Object[data.length][COLUMN_COUNT];
752 for (int i = 0; i < data.length; i++)
754 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
759 updateOriginalData(data);
762 table.setModel(new FeatureTableModel(data));
763 table.getColumnModel().getColumn(0).setPreferredWidth(200);
765 groupPanel.setLayout(
766 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
767 pruneGroups(foundGroups);
768 groupPanel.validate();
770 updateFeatureRenderer(data, groupChanged != null);
771 resettingTable = false;
775 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
776 * have been made outwith this dialog
778 * <li>a new feature type added (and made visible)</li>
779 * <li>a feature colour changed (in the Amend Features dialog)</li>
784 protected void updateOriginalData(Object[][] foundData)
786 // todo LinkedHashMap instead of Object[][] would be nice
788 Object[][] currentData = ((FeatureTableModel) table.getModel())
790 for (Object[] row : foundData)
792 String type = (String) row[TYPE_COLUMN];
793 boolean found = false;
794 for (Object[] current : currentData)
796 if (type.equals(current[TYPE_COLUMN]))
800 * currently dependent on object equality here;
801 * really need an equals method on FeatureColour
803 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
806 * feature colour has changed externally - update originalData
808 for (Object[] original : originalData)
810 if (type.equals(original[TYPE_COLUMN]))
812 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
823 * new feature detected - add to original data (on top)
825 Object[][] newData = new Object[originalData.length
827 for (int i = 0; i < originalData.length; i++)
829 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
833 originalData = newData;
839 * Remove from the groups panel any checkboxes for groups that are not in the
840 * foundGroups set. This enables removing a group from the display when the last
841 * feature in that group is deleted.
845 protected void pruneGroups(Set<String> foundGroups)
847 for (int g = 0; g < groupPanel.getComponentCount(); g++)
849 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
850 if (!foundGroups.contains(checkbox.getText()))
852 groupPanel.remove(checkbox);
858 * reorder data based on the featureRenderers global priority list.
862 private void ensureOrder(Object[][] data)
864 boolean sort = false;
865 float[] order = new float[data.length];
866 for (int i = 0; i < order.length; i++)
868 order[i] = fr.getOrder(data[i][0].toString());
871 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
875 sort = sort || order[i - 1] > order[i];
880 jalview.util.QuickSort.sort(order, data);
885 * Offers a file chooser dialog, and then loads the feature colours and
886 * filters from file in XML format and unmarshals to Jalview feature settings
890 JalviewFileChooser chooser = new JalviewFileChooser("fc",
891 SEQUENCE_FEATURE_COLOURS);
892 chooser.setFileView(new JalviewFileView());
893 chooser.setDialogTitle(
894 MessageManager.getString("label.load_feature_colours"));
895 chooser.setToolTipText(MessageManager.getString("action.load"));
896 chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION){
900 File file = chooser.getSelectedFile();
903 chooser.showOpenDialog(this);
907 * Loads feature colours and filters from XML stored in the given file
915 InputStreamReader in = new InputStreamReader(
916 new FileInputStream(file), "UTF-8");
918 JAXBContext jc = JAXBContext
919 .newInstance("jalview.xml.binding.jalview");
920 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
921 XMLStreamReader streamReader = XMLInputFactory.newInstance()
922 .createXMLStreamReader(in);
923 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
924 JalviewUserColours.class);
925 JalviewUserColours jucs = jbe.getValue();
927 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
930 * load feature colours
932 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
934 Colour newcol = jucs.getColour().get(i);
935 FeatureColourI colour = jalview.project.Jalview2XML
936 .parseColour(newcol);
937 fr.setColour(newcol.getName(), colour);
938 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
942 * load feature filters; loaded filters will replace any that are
943 * currently defined, other defined filters are left unchanged
945 for (int i = 0; i < jucs.getFilter().size(); i++)
947 Filter filterModel = jucs.getFilter().get(i);
948 String featureType = filterModel.getFeatureType();
949 FeatureMatcherSetI filter = jalview.project.Jalview2XML
950 .parseFilter(featureType, filterModel.getMatcherSet());
951 if (!filter.isEmpty())
953 fr.setFeatureFilter(featureType, filter);
958 * update feature settings table
963 Object[][] data = ((FeatureTableModel) table.getModel())
966 updateFeatureRenderer(data, false);
969 } catch (Exception ex)
971 System.out.println("Error loading User Colour File\n" + ex);
976 * Offers a file chooser dialog, and then saves the current feature colours
977 * and any filters to the selected file in XML format
981 JalviewFileChooser chooser = new JalviewFileChooser("fc",
982 SEQUENCE_FEATURE_COLOURS);
983 chooser.setFileView(new JalviewFileView());
984 chooser.setDialogTitle(
985 MessageManager.getString("label.save_feature_colours"));
986 chooser.setToolTipText(MessageManager.getString("action.save"));
987 int option = chooser.showSaveDialog(this);
988 if (option == JalviewFileChooser.APPROVE_OPTION)
990 File file = chooser.getSelectedFile();
996 * Saves feature colours and filters to the given file
1000 void save(File file)
1002 JalviewUserColours ucs = new JalviewUserColours();
1003 ucs.setSchemeName("Sequence Features");
1006 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1007 new FileOutputStream(file), "UTF-8"));
1010 * sort feature types by colour order, from 0 (highest)
1013 Set<String> fr_colours = fr.getAllFeatureColours();
1014 String[] sortedTypes = fr_colours
1015 .toArray(new String[fr_colours.size()]);
1016 Arrays.sort(sortedTypes, new Comparator<String>()
1019 public int compare(String type1, String type2)
1021 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1026 * save feature colours
1028 for (String featureType : sortedTypes)
1030 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1031 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1033 ucs.getColour().add(col);
1037 * save any feature filters
1039 for (String featureType : sortedTypes)
1041 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1042 if (filter != null && !filter.isEmpty())
1044 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1045 FeatureMatcherI firstMatcher = iterator.next();
1046 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1047 .marshalFilter(firstMatcher, iterator,
1049 Filter filterModel = new Filter();
1050 filterModel.setFeatureType(featureType);
1051 filterModel.setMatcherSet(ms);
1052 ucs.getFilter().add(filterModel);
1055 JAXBContext jaxbContext = JAXBContext
1056 .newInstance(JalviewUserColours.class);
1057 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1058 jaxbMarshaller.marshal(
1059 new ObjectFactory().createJalviewUserColours(ucs), out);
1061 // jaxbMarshaller.marshal(object, pout);
1062 // marshaller.marshal(object);
1065 // ucs.marshal(out);
1067 } catch (Exception ex)
1069 ex.printStackTrace();
1073 public void invertSelection()
1075 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1076 for (int i = 0; i < data.length; i++)
1078 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1080 updateFeatureRenderer(data, true);
1084 public void orderByAvWidth()
1086 if (table == null || table.getModel() == null)
1090 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1091 float[] width = new float[data.length];
1095 for (int i = 0; i < data.length; i++)
1097 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1100 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1101 // weight - but have to make per
1102 // sequence, too (awidth[2])
1103 // if (width[i]==1) // hack to distinguish single width sequences.
1114 boolean sort = false;
1115 for (int i = 0; i < width.length; i++)
1117 // awidth = (float[]) typeWidth.get(data[i][0]);
1120 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1123 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1129 width[i] /= max; // normalize
1130 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1134 sort = sort || width[i - 1] > width[i];
1139 jalview.util.QuickSort.sort(width, data);
1140 // update global priority order
1143 updateFeatureRenderer(data, false);
1148 private void repaintTable() {
1150 // Here is a needed intervention
1151 // because generally we don't "repaint"
1152 // the table. We re-create the HTML divs
1153 // that is associated with it. A better
1154 // way to do this would be to fire a property change.
1156 @SuppressWarnings("unused")
1157 TableUI ui = table.getUI();
1159 * @j2sNative ui.repaintTable$();
1170 frame.setClosed(true);
1171 } catch (Exception exe)
1177 public void updateFeatureRenderer(Object[][] data)
1179 updateFeatureRenderer(data, true);
1183 * Update the priority order of features; only repaint if this changed the order
1184 * of visible features
1189 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1191 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1193 if (fr.setFeaturePriority(rowData, visibleNew))
1195 af.alignPanel.paintAlignment(true, true);
1200 * Converts table data into an array of data beans
1202 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1204 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1205 for (int i = 0; i < data.length; i++)
1207 String type = (String) data[i][TYPE_COLUMN];
1208 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1209 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1210 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1211 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1217 private void jbInit() throws Exception
1219 this.setLayout(new BorderLayout());
1221 JPanel settingsPane = new JPanel();
1222 settingsPane.setLayout(new BorderLayout());
1224 JPanel bigPanel = new JPanel();
1225 bigPanel.setLayout(new BorderLayout());
1227 groupPanel = new JPanel();
1228 bigPanel.add(groupPanel, BorderLayout.NORTH);
1230 JButton invert = new JButton(
1231 MessageManager.getString("label.invert_selection"));
1232 invert.setFont(JvSwingUtils.getLabelFont());
1233 invert.addActionListener(new ActionListener()
1236 public void actionPerformed(ActionEvent e)
1242 JButton optimizeOrder = new JButton(
1243 MessageManager.getString("label.optimise_order"));
1244 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1245 optimizeOrder.addActionListener(new ActionListener()
1248 public void actionPerformed(ActionEvent e)
1254 JButton sortByScore = new JButton(
1255 MessageManager.getString("label.seq_sort_by_score"));
1256 sortByScore.setFont(JvSwingUtils.getLabelFont());
1257 sortByScore.addActionListener(new ActionListener()
1260 public void actionPerformed(ActionEvent e)
1262 af.avc.sortAlignmentByFeatureScore(null);
1265 JButton sortByDens = new JButton(
1266 MessageManager.getString("label.sequence_sort_by_density"));
1267 sortByDens.setFont(JvSwingUtils.getLabelFont());
1268 sortByDens.addActionListener(new ActionListener()
1271 public void actionPerformed(ActionEvent e)
1273 af.avc.sortAlignmentByFeatureDensity(null);
1277 JButton help = new JButton(MessageManager.getString("action.help"));
1278 help.setFont(JvSwingUtils.getLabelFont());
1279 help.addActionListener(new ActionListener()
1282 public void actionPerformed(ActionEvent e)
1286 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1287 } catch (HelpSetException e1)
1289 e1.printStackTrace();
1293 help.setFont(JvSwingUtils.getLabelFont());
1294 help.setText(MessageManager.getString("action.help"));
1295 help.addActionListener(new ActionListener()
1298 public void actionPerformed(ActionEvent e)
1302 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1303 } catch (HelpSetException e1)
1305 e1.printStackTrace();
1310 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1311 cancel.setFont(JvSwingUtils.getLabelFont());
1312 cancel.addActionListener(new ActionListener()
1315 public void actionPerformed(ActionEvent e)
1317 fr.setTransparency(originalTransparency);
1318 fr.setFeatureFilters(originalFilters);
1319 updateFeatureRenderer(originalData);
1324 JButton ok = new JButton(MessageManager.getString("action.ok"));
1325 ok.setFont(JvSwingUtils.getLabelFont());
1326 ok.addActionListener(new ActionListener()
1329 public void actionPerformed(ActionEvent e)
1335 JButton loadColours = new JButton(
1336 MessageManager.getString("label.load_colours"));
1337 loadColours.setFont(JvSwingUtils.getLabelFont());
1338 loadColours.setToolTipText(
1339 MessageManager.getString("label.load_colours_tooltip"));
1340 loadColours.addActionListener(new ActionListener()
1343 public void actionPerformed(ActionEvent e)
1349 JButton saveColours = new JButton(
1350 MessageManager.getString("label.save_colours"));
1351 saveColours.setFont(JvSwingUtils.getLabelFont());
1352 saveColours.setToolTipText(
1353 MessageManager.getString("label.save_colours_tooltip"));
1354 saveColours.addActionListener(new ActionListener()
1357 public void actionPerformed(ActionEvent e)
1362 transparency.addChangeListener(new ChangeListener()
1365 public void stateChanged(ChangeEvent evt)
1367 if (!inConstruction)
1369 fr.setTransparency((100 - transparency.getValue()) / 100f);
1370 af.alignPanel.paintAlignment(true, true);
1375 transparency.setMaximum(70);
1376 transparency.setToolTipText(
1377 MessageManager.getString("label.transparency_tip"));
1379 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1380 bigPanel.add(transPanel, BorderLayout.SOUTH);
1382 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1383 transbuttons.add(optimizeOrder);
1384 transbuttons.add(invert);
1385 transbuttons.add(sortByScore);
1386 transbuttons.add(sortByDens);
1387 transbuttons.add(help);
1388 transPanel.add(transparency);
1389 transPanel.add(transbuttons);
1391 JPanel buttonPanel = new JPanel();
1392 buttonPanel.add(ok);
1393 buttonPanel.add(cancel);
1394 buttonPanel.add(loadColours);
1395 buttonPanel.add(saveColours);
1396 bigPanel.add(scrollPane, BorderLayout.CENTER);
1397 settingsPane.add(bigPanel, BorderLayout.CENTER);
1398 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1399 this.add(settingsPane);
1403 * Answers a suitable tooltip to show on the colour cell of the table
1407 * if true include 'click to edit' and similar text
1410 public static String getColorTooltip(FeatureColourI fcol,
1417 if (fcol.isSimpleColour())
1419 return withHint ? BASE_TOOLTIP : null;
1421 String description = fcol.getDescription();
1422 description = description.replaceAll("<", "<");
1423 description = description.replaceAll(">", ">");
1424 StringBuilder tt = new StringBuilder(description);
1427 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1429 return JvSwingUtils.wrapTooltip(true, tt.toString());
1432 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1435 boolean thr = false;
1436 StringBuilder tx = new StringBuilder();
1438 if (gcol.isColourByAttribute())
1440 tx.append(FeatureMatcher
1441 .toAttributeDisplayName(gcol.getAttributeName()));
1443 else if (!gcol.isColourByLabel())
1445 tx.append(MessageManager.getString("label.score"));
1448 if (gcol.isAboveThreshold())
1453 if (gcol.isBelowThreshold())
1458 if (gcol.isColourByLabel())
1464 if (!gcol.isColourByAttribute())
1472 Color newColor = gcol.getMaxColour();
1473 comp.setBackground(newColor);
1474 // System.err.println("Width is " + w / 2);
1475 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1476 comp.setIcon(ficon);
1477 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1478 // + newColor.getGreen() + ", " + newColor.getBlue()
1479 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1480 // + ", " + minCol.getBlue() + ")");
1482 comp.setHorizontalAlignment(SwingConstants.CENTER);
1483 comp.setText(tx.toString());
1486 // ///////////////////////////////////////////////////////////////////////
1487 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1488 // ///////////////////////////////////////////////////////////////////////
1489 class FeatureTableModel extends AbstractTableModel
1491 private String[] columnNames = {
1492 MessageManager.getString("label.feature_type"),
1493 MessageManager.getString("action.colour"),
1494 MessageManager.getString("label.filter"),
1495 MessageManager.getString("label.show") };
1497 private Object[][] data;
1499 FeatureTableModel(Object[][] data)
1504 public Object[][] getData()
1509 public void setData(Object[][] data)
1515 public int getColumnCount()
1517 return columnNames.length;
1520 public Object[] getRow(int row)
1526 public int getRowCount()
1532 public String getColumnName(int col)
1534 return columnNames[col];
1538 public Object getValueAt(int row, int col)
1540 return data[row][col];
1544 * Answers the class of the object in column c of the first row of the table
1547 public Class<?> getColumnClass(int c)
1549 Object v = getValueAt(0, c);
1550 return v == null ? null : v.getClass();
1554 public boolean isCellEditable(int row, int col)
1556 return col == 0 ? false : true;
1560 public void setValueAt(Object value, int row, int col)
1562 data[row][col] = value;
1563 fireTableCellUpdated(row, col);
1564 updateFeatureRenderer(data);
1569 class ColorRenderer extends JLabel implements TableCellRenderer
1571 Border unselectedBorder = null;
1573 Border selectedBorder = null;
1575 public ColorRenderer()
1577 setOpaque(true); // MUST do this for background to show up.
1578 setHorizontalTextPosition(SwingConstants.CENTER);
1579 setVerticalTextPosition(SwingConstants.CENTER);
1583 public Component getTableCellRendererComponent(JTable tbl, Object color,
1584 boolean isSelected, boolean hasFocus, int row, int column)
1586 FeatureColourI cellColour = (FeatureColourI) color;
1588 setBackground(tbl.getBackground());
1589 if (!cellColour.isSimpleColour())
1591 Rectangle cr = tbl.getCellRect(row, column, false);
1592 FeatureSettings.renderGraduatedColor(this, cellColour,
1593 (int) cr.getWidth(), (int) cr.getHeight());
1599 setBackground(cellColour.getColour());
1603 if (selectedBorder == null)
1605 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1606 tbl.getSelectionBackground());
1608 setBorder(selectedBorder);
1612 if (unselectedBorder == null)
1614 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1615 tbl.getBackground());
1617 setBorder(unselectedBorder);
1624 class FilterRenderer extends JLabel implements TableCellRenderer
1626 javax.swing.border.Border unselectedBorder = null;
1628 javax.swing.border.Border selectedBorder = null;
1630 public FilterRenderer()
1632 setOpaque(true); // MUST do this for background to show up.
1633 setHorizontalTextPosition(SwingConstants.CENTER);
1634 setVerticalTextPosition(SwingConstants.CENTER);
1638 public Component getTableCellRendererComponent(JTable tbl,
1639 Object filter, boolean isSelected, boolean hasFocus, int row,
1642 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1644 String asText = theFilter.toString();
1645 setBackground(tbl.getBackground());
1646 this.setText(asText);
1651 if (selectedBorder == null)
1653 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1654 tbl.getSelectionBackground());
1656 setBorder(selectedBorder);
1660 if (unselectedBorder == null)
1662 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1663 tbl.getBackground());
1665 setBorder(unselectedBorder);
1673 * update comp using rendering settings from gcol
1678 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1680 int w = comp.getWidth(), h = comp.getHeight();
1683 w = (int) comp.getPreferredSize().getWidth();
1684 h = (int) comp.getPreferredSize().getHeight();
1691 renderGraduatedColor(comp, gcol, w, h);
1694 class ColorEditor extends AbstractCellEditor
1695 implements TableCellEditor, ActionListener
1699 FeatureColourI currentColor;
1701 FeatureTypeSettings chooser;
1707 protected static final String EDIT = "edit";
1709 int rowSelected = 0;
1711 public ColorEditor(FeatureSettings fs)
1714 // Set up the editor (from the table's point of view),
1715 // which is a button.
1716 // This button brings up the color chooser dialog,
1717 // which is the editor from the user's point of view.
1718 button = new JButton();
1719 button.setActionCommand(EDIT);
1720 button.addActionListener(this);
1721 button.setBorderPainted(false);
1725 * Handles events from the editor button, and from the colour/filters
1726 * dialog's OK button
1729 public void actionPerformed(ActionEvent e)
1731 if (button == e.getSource())
1733 if (currentColor.isSimpleColour())
1736 * simple colour chooser
1738 String ttl = MessageManager.getString("label.select_colour");
1739 ColourChooserListener listener = new ColourChooserListener() {
1741 public void colourSelected(Color c)
1743 currentColor = new FeatureColour(c);
1744 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1747 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1752 * variable colour and filters dialog
1754 chooser = new FeatureTypeSettings(me.fr, type);
1759 chooser.setRequestFocusEnabled(true);
1760 chooser.requestFocus();
1762 chooser.addActionListener(this);
1763 // Make the renderer reappear.
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 = me.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;
1785 fireEditingStopped();
1786 me.table.validate();
1790 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1792 public Object getCellEditorValue()
1794 return currentColor;
1797 // Implement the one method defined by TableCellEditor.
1799 public Component getTableCellEditorComponent(JTable theTable, Object value,
1800 boolean isSelected, int row, int column)
1802 currentColor = (FeatureColourI) value;
1803 this.rowSelected = row;
1804 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1805 button.setOpaque(true);
1806 button.setBackground(me.getBackground());
1807 if (!currentColor.isSimpleColour())
1809 JLabel btn = new JLabel();
1810 btn.setSize(button.getSize());
1811 FeatureSettings.renderGraduatedColor(btn, currentColor);
1812 button.setBackground(btn.getBackground());
1813 button.setIcon(btn.getIcon());
1814 button.setText(btn.getText());
1819 button.setIcon(null);
1820 button.setBackground(currentColor.getColour());
1827 * The cell editor for the Filter column. It displays the text of any filters
1828 * for the feature type in that row (in full as a tooltip, possible abbreviated
1829 * as display text). On click in the cell, opens the Feature Display Settings
1830 * dialog at the Filters tab.
1832 class FilterEditor extends AbstractCellEditor
1833 implements TableCellEditor, ActionListener
1837 FeatureMatcherSetI currentFilter;
1845 protected static final String EDIT = "edit";
1847 int rowSelected = 0;
1849 public FilterEditor(FeatureSettings me)
1852 button = new JButton();
1853 button.setActionCommand(EDIT);
1854 button.addActionListener(this);
1855 button.setBorderPainted(false);
1859 * Handles events from the editor button
1862 public void actionPerformed(ActionEvent e)
1864 if (button == e.getSource())
1866 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1867 chooser.addActionListener(this);
1868 chooser.setRequestFocusEnabled(true);
1869 chooser.requestFocus();
1870 if (lastLocation != null)
1872 // todo open at its last position on screen
1873 chooser.setBounds(lastLocation.x, lastLocation.y,
1874 chooser.getWidth(), chooser.getHeight());
1877 fireEditingStopped();
1879 else if (e.getSource() instanceof Component)
1883 * after OK in variable colour dialog, any changes to filter
1884 * (or colours!) are already set in FeatureRenderer, so just
1885 * update table data without triggering updateFeatureRenderer
1887 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1888 currentFilter = me.fr.getFeatureFilter(type);
1889 if (currentFilter == null)
1891 currentFilter = new FeatureMatcherSet();
1893 Object[] data = ((FeatureTableModel) table.getModel())
1894 .getData()[rowSelected];
1895 data[COLOUR_COLUMN] = currentColor;
1896 data[FILTER_COLUMN] = currentFilter;
1897 fireEditingStopped();
1898 me.table.validate();
1903 public Object getCellEditorValue()
1905 return currentFilter;
1909 public Component getTableCellEditorComponent(JTable theTable, Object value,
1910 boolean isSelected, int row, int column)
1912 currentFilter = (FeatureMatcherSetI) value;
1913 this.rowSelected = row;
1914 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1915 button.setOpaque(true);
1916 button.setBackground(me.getBackground());
1917 button.setText(currentFilter.toString());
1918 button.setIcon(null);
1924 class FeatureIcon implements Icon
1926 FeatureColourI gcol;
1930 boolean midspace = false;
1932 int width = 50, height = 20;
1934 int s1, e1; // start and end of midpoint band for thresholded symbol
1936 Color mpcolour = Color.white;
1938 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1958 public int getIconWidth()
1964 public int getIconHeight()
1970 public void paintIcon(Component c, Graphics g, int x, int y)
1973 if (gcol.isColourByLabel())
1976 g.fillRect(0, 0, width, height);
1977 // need an icon here.
1978 g.setColor(gcol.getMaxColour());
1980 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1982 // g.setFont(g.getFont().deriveFont(
1983 // AffineTransform.getScaleInstance(
1984 // width/g.getFontMetrics().stringWidth("Label"),
1985 // height/g.getFontMetrics().getHeight())));
1987 g.drawString(MessageManager.getString("label.label"), 0, 0);
1992 Color minCol = gcol.getMinColour();
1994 g.fillRect(0, 0, s1, height);
1997 g.setColor(Color.white);
1998 g.fillRect(s1, 0, e1 - s1, height);
2000 g.setColor(gcol.getMaxColour());
2001 g.fillRect(0, e1, width - e1, height);