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.util.dialogrunner.RunResponse;
40 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
41 import jalview.xml.binding.jalview.JalviewUserColours;
42 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
43 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
44 import jalview.xml.binding.jalview.ObjectFactory;
46 import java.awt.BorderLayout;
47 import java.awt.Color;
48 import java.awt.Component;
49 import java.awt.Dimension;
51 import java.awt.Graphics;
52 import java.awt.GridLayout;
53 import java.awt.Point;
54 import java.awt.Rectangle;
55 import java.awt.event.ActionEvent;
56 import java.awt.event.ActionListener;
57 import java.awt.event.ItemEvent;
58 import java.awt.event.ItemListener;
59 import java.awt.event.MouseAdapter;
60 import java.awt.event.MouseEvent;
61 import java.awt.event.MouseMotionAdapter;
62 import java.beans.PropertyChangeEvent;
63 import java.beans.PropertyChangeListener;
65 import java.io.FileInputStream;
66 import java.io.FileOutputStream;
67 import java.io.InputStreamReader;
68 import java.io.OutputStreamWriter;
69 import java.io.PrintWriter;
70 import java.util.Arrays;
71 import java.util.Comparator;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.Hashtable;
75 import java.util.Iterator;
76 import java.util.List;
80 import javax.help.HelpSetException;
81 import javax.swing.AbstractCellEditor;
82 import javax.swing.BorderFactory;
83 import javax.swing.Icon;
84 import javax.swing.JButton;
85 import javax.swing.JCheckBox;
86 import javax.swing.JCheckBoxMenuItem;
87 import javax.swing.JInternalFrame;
88 import javax.swing.JLabel;
89 import javax.swing.JLayeredPane;
90 import javax.swing.JMenuItem;
91 import javax.swing.JPanel;
92 import javax.swing.JPopupMenu;
93 import javax.swing.JScrollPane;
94 import javax.swing.JSlider;
95 import javax.swing.JTable;
96 import javax.swing.ListSelectionModel;
97 import javax.swing.SwingConstants;
98 import javax.swing.ToolTipManager;
99 import javax.swing.border.Border;
100 import javax.swing.event.ChangeEvent;
101 import javax.swing.event.ChangeListener;
102 import javax.swing.plaf.TableUI;
103 import javax.swing.table.AbstractTableModel;
104 import javax.swing.table.TableCellEditor;
105 import javax.swing.table.TableCellRenderer;
106 import javax.swing.table.TableColumn;
107 import javax.xml.bind.JAXBContext;
108 import javax.xml.bind.JAXBElement;
109 import javax.xml.bind.Marshaller;
110 import javax.xml.stream.XMLInputFactory;
111 import javax.xml.stream.XMLStreamReader;
113 public class FeatureSettings extends JPanel
114 implements FeatureSettingsControllerI
116 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
117 .getString("label.sequence_feature_colours");
120 * column indices of fields in Feature Settings table
122 static final int TYPE_COLUMN = 0;
124 static final int COLOUR_COLUMN = 1;
126 static final int FILTER_COLUMN = 2;
128 static final int SHOW_COLUMN = 3;
130 private static final int COLUMN_COUNT = 4;
132 private static final int MIN_WIDTH = 400;
134 private static final int MIN_HEIGHT = 400;
136 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
138 final FeatureRenderer fr;
140 public final AlignFrame af;
143 * 'original' fields hold settings to restore on Cancel
145 Object[][] originalData;
147 float originalTransparency;
149 Map<String, FeatureMatcherSetI> originalFilters;
151 final JInternalFrame frame;
153 JScrollPane scrollPane = new JScrollPane();
159 JSlider transparency = new JSlider();
162 * when true, constructor is still executing - so ignore UI events
164 protected volatile boolean inConstruction = true;
166 int selectedRow = -1;
168 JButton fetchDAS = new JButton();
170 JButton saveDAS = new JButton();
172 JButton cancelDAS = new JButton();
174 boolean resettingTable = false;
177 * true when Feature Settings are updating from feature renderer
179 boolean handlingUpdate = false;
182 * holds {featureCount, totalExtent} for each feature type
184 Map<String, float[]> typeWidth = null;
191 public FeatureSettings(AlignFrame alignFrame)
193 this.af = alignFrame;
194 fr = af.getFeatureRenderer();
196 // save transparency for restore on Cancel
197 originalTransparency = fr.getTransparency();
198 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
199 transparency.setMaximum(100 - originalTransparencyAsPercent);
201 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
206 } catch (Exception ex)
208 ex.printStackTrace();
214 public String getToolTipText(MouseEvent e)
217 int column = table.columnAtPoint(e.getPoint());
218 int row = table.rowAtPoint(e.getPoint());
223 tip = JvSwingUtils.wrapTooltip(true, MessageManager
224 .getString("label.feature_settings_click_drag"));
227 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
229 tip = getColorTooltip(colour, true);
232 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
235 ? MessageManager.getString("label.filters_tooltip")
247 * Position the tooltip near the bottom edge of, and half way across, the
251 public Point getToolTipLocation(MouseEvent e)
253 Point point = e.getPoint();
254 int column = table.columnAtPoint(point);
255 int row = table.rowAtPoint(point);
256 Rectangle r = getCellRect(row, column, false);
257 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
262 // next line is needed to avoid (quiet) exceptions thrown
263 // when column ordering changes so that the above constants
265 table.getTableHeader().setReorderingAllowed(false); // BH 2018
267 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
268 ToolTipManager.sharedInstance().registerComponent(table);
270 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
271 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
273 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
274 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
276 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
277 new ColorRenderer(), new ColorEditor(this));
278 table.addColumn(colourColumn);
280 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
281 new FilterRenderer(), new FilterEditor(this));
282 table.addColumn(filterColumn);
284 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
286 table.addMouseListener(new MouseAdapter()
289 public void mousePressed(MouseEvent evt)
291 selectedRow = table.rowAtPoint(evt.getPoint());
292 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
293 if (evt.isPopupTrigger())
295 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
296 showPopupMenu(selectedRow, type, colour, evt.getPoint());
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 showPopupMenu(selectedRow, type, colour, evt.getPoint());
322 table.addMouseMotionListener(new MouseMotionAdapter()
325 public void mouseDragged(MouseEvent evt)
327 int newRow = table.rowAtPoint(evt.getPoint());
328 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
331 * reposition 'selectedRow' to 'newRow' (the dragged to location)
332 * this could be more than one row away for a very fast drag action
333 * so just swap it with adjacent rows until we get it there
335 Object[][] data = ((FeatureTableModel) table.getModel())
337 int direction = newRow < selectedRow ? -1 : 1;
338 for (int i = selectedRow; i != newRow; i += direction)
340 Object[] temp = data[i];
341 data[i] = data[i + direction];
342 data[i + direction] = temp;
344 updateFeatureRenderer(data);
346 selectedRow = newRow;
350 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
351 // MessageManager.getString("label.feature_settings_click_drag")));
352 scrollPane.setViewportView(table);
354 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
356 fr.findAllFeatures(true); // display everything!
359 discoverAllFeatureData();
360 final PropertyChangeListener change;
361 final FeatureSettings fs = this;
362 fr.addPropertyChangeListener(change = new PropertyChangeListener()
365 public void propertyChange(PropertyChangeEvent evt)
367 if (!fs.resettingTable && !fs.handlingUpdate)
369 fs.handlingUpdate = true;
371 // new groups may be added with new sequence feature types only
372 fs.handlingUpdate = false;
378 frame = new JInternalFrame();
379 frame.setContentPane(this);
380 if (Platform.isAMac())
382 Desktop.addInternalFrame(frame,
383 MessageManager.getString("label.sequence_feature_settings"),
388 Desktop.addInternalFrame(frame,
389 MessageManager.getString("label.sequence_feature_settings"),
392 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
394 frame.addInternalFrameListener(
395 new javax.swing.event.InternalFrameAdapter()
398 public void internalFrameClosed(
399 javax.swing.event.InternalFrameEvent evt)
401 fr.removePropertyChangeListener(change);
404 frame.setLayer(JLayeredPane.PALETTE_LAYER);
405 inConstruction = false;
409 * Constructs and shows a popup menu of possible actions on the selected row and
417 protected void showPopupMenu(final int rowSelected, final String type,
418 final Object typeCol, final Point pt)
420 final FeatureColourI featureColour = (FeatureColourI) typeCol;
422 JPopupMenu men = new JPopupMenu(MessageManager
423 .formatMessage("label.settings_for_param", new String[]
425 JMenuItem scr = new JMenuItem(
426 MessageManager.getString("label.sort_by_score"));
428 final FeatureSettings me = this;
429 scr.addActionListener(new ActionListener()
433 public void actionPerformed(ActionEvent e)
436 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
440 JMenuItem dens = new JMenuItem(
441 MessageManager.getString("label.sort_by_density"));
442 dens.addActionListener(new ActionListener()
446 public void actionPerformed(ActionEvent e)
449 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
456 * variable colour options include colour by label, by score,
457 * by selected attribute text, or attribute value
459 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
460 MessageManager.getString("label.variable_colour"));
461 variableColourCB.setSelected(!featureColour.isSimpleColour());
462 men.add(variableColourCB);
465 * checkbox action listener doubles up as listener to OK
466 * from the variable colour / filters dialog
468 variableColourCB.addActionListener(new ActionListener()
471 public void actionPerformed(ActionEvent e)
473 if (e.getSource() == variableColourCB)
475 if (featureColour.isSimpleColour())
478 * toggle simple colour to variable colour - show dialog
480 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
481 fc.addActionListener(this);
486 * toggle variable to simple colour - show colour chooser
488 String title = MessageManager.formatMessage("label.select_colour_for", type);
489 ColourChooserListener listener = new ColourChooserListener()
492 public void colourSelected(Color c)
494 table.setValueAt(new FeatureColour(c), rowSelected,
497 me.updateFeatureRenderer(
498 ((FeatureTableModel) table.getModel()).getData(),
502 JalviewColourChooser.showColourChooser(me, title, featureColour.getMaxColour(), listener);
507 if (e.getSource() instanceof FeatureTypeSettings)
510 * update after OK in feature colour dialog; the updated
511 * colour will have already been set in the FeatureRenderer
513 FeatureColourI fci = fr.getFeatureColours().get(type);
514 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
521 JMenuItem selCols = new JMenuItem(
522 MessageManager.getString("label.select_columns_containing"));
523 selCols.addActionListener(new ActionListener()
526 public void actionPerformed(ActionEvent arg0)
528 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
532 JMenuItem clearCols = new JMenuItem(MessageManager
533 .getString("label.select_columns_not_containing"));
534 clearCols.addActionListener(new ActionListener()
537 public void actionPerformed(ActionEvent arg0)
539 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
543 JMenuItem hideCols = new JMenuItem(
544 MessageManager.getString("label.hide_columns_containing"));
545 hideCols.addActionListener(new ActionListener()
548 public void actionPerformed(ActionEvent arg0)
550 fr.ap.alignFrame.hideFeatureColumns(type, true);
553 JMenuItem hideOtherCols = new JMenuItem(
554 MessageManager.getString("label.hide_columns_not_containing"));
555 hideOtherCols.addActionListener(new ActionListener()
558 public void actionPerformed(ActionEvent arg0)
560 fr.ap.alignFrame.hideFeatureColumns(type, false);
566 men.add(hideOtherCols);
567 men.show(table, pt.x, pt.y);
571 synchronized public void discoverAllFeatureData()
573 Set<String> allGroups = new HashSet<>();
574 AlignmentI alignment = af.getViewport().getAlignment();
576 for (int i = 0; i < alignment.getHeight(); i++)
578 SequenceI seq = alignment.getSequenceAt(i);
579 for (String group : seq.getFeatures().getFeatureGroups(true))
581 if (group != null && !allGroups.contains(group))
583 allGroups.add(group);
584 checkGroupState(group);
595 * Synchronise gui group list and check visibility of group
598 * @return true if group is visible
600 private boolean checkGroupState(String group)
602 boolean visible = fr.checkGroupVisibility(group, true);
604 for (int g = 0; g < groupPanel.getComponentCount(); g++)
606 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
608 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
613 final String grp = group;
614 final JCheckBox check = new JCheckBox(group, visible);
615 check.setFont(new Font("Serif", Font.BOLD, 12));
616 check.setToolTipText(group);
617 check.addItemListener(new ItemListener()
620 public void itemStateChanged(ItemEvent evt)
622 fr.setGroupVisibility(check.getText(), check.isSelected());
623 resetTable(new String[] { grp });
624 af.alignPanel.paintAlignment(true, true);
627 groupPanel.add(check);
631 synchronized void resetTable(String[] groupChanged)
637 resettingTable = true;
638 typeWidth = new Hashtable<>();
639 // TODO: change avWidth calculation to 'per-sequence' average and use long
642 Set<String> displayableTypes = new HashSet<>();
643 Set<String> foundGroups = new HashSet<>();
646 * determine which feature types may be visible depending on
647 * which groups are selected, and recompute average width data
649 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
652 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
655 * get the sequence's groups for positional features
656 * and keep track of which groups are visible
658 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
659 Set<String> visibleGroups = new HashSet<>();
660 for (String group : groups)
662 if (group == null || checkGroupState(group))
664 visibleGroups.add(group);
667 foundGroups.addAll(groups);
670 * get distinct feature types for visible groups
671 * record distinct visible types, and their count and total length
673 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
674 visibleGroups.toArray(new String[visibleGroups.size()]));
675 for (String type : types)
677 displayableTypes.add(type);
678 float[] avWidth = typeWidth.get(type);
681 avWidth = new float[2];
682 typeWidth.put(type, avWidth);
684 // todo this could include features with a non-visible group
685 // - do we greatly care?
686 // todo should we include non-displayable features here, and only
687 // update when features are added?
688 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
689 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
693 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
696 if (fr.hasRenderOrder())
700 fr.findAllFeatures(groupChanged != null); // prod to update
701 // colourschemes. but don't
703 // First add the checks in the previous render order,
704 // in case the window has been closed and reopened
706 List<String> frl = fr.getRenderOrder();
707 for (int ro = frl.size() - 1; ro > -1; ro--)
709 String type = frl.get(ro);
711 if (!displayableTypes.contains(type))
716 data[dataIndex][TYPE_COLUMN] = type;
717 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
718 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
719 data[dataIndex][FILTER_COLUMN] = featureFilter == null
720 ? new FeatureMatcherSet()
722 data[dataIndex][SHOW_COLUMN] = new Boolean(
723 af.getViewport().getFeaturesDisplayed().isVisible(type));
725 displayableTypes.remove(type);
730 * process any extra features belonging only to
731 * a group which was just selected
733 while (!displayableTypes.isEmpty())
735 String type = displayableTypes.iterator().next();
736 data[dataIndex][TYPE_COLUMN] = type;
738 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
739 if (data[dataIndex][COLOUR_COLUMN] == null)
741 // "Colour has been updated in another view!!"
742 fr.clearRenderOrder();
745 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
746 data[dataIndex][FILTER_COLUMN] = featureFilter == null
747 ? new FeatureMatcherSet()
749 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
751 displayableTypes.remove(type);
754 if (originalData == null)
756 originalData = new Object[data.length][COLUMN_COUNT];
757 for (int i = 0; i < data.length; i++)
759 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
764 updateOriginalData(data);
767 table.setModel(new FeatureTableModel(data));
768 table.getColumnModel().getColumn(0).setPreferredWidth(200);
770 groupPanel.setLayout(
771 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
772 pruneGroups(foundGroups);
773 groupPanel.validate();
775 updateFeatureRenderer(data, groupChanged != null);
776 resettingTable = false;
780 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
781 * have been made outwith this dialog
783 * <li>a new feature type added (and made visible)</li>
784 * <li>a feature colour changed (in the Amend Features dialog)</li>
789 protected void updateOriginalData(Object[][] foundData)
791 // todo LinkedHashMap instead of Object[][] would be nice
793 Object[][] currentData = ((FeatureTableModel) table.getModel())
795 for (Object[] row : foundData)
797 String type = (String) row[TYPE_COLUMN];
798 boolean found = false;
799 for (Object[] current : currentData)
801 if (type.equals(current[TYPE_COLUMN]))
805 * currently dependent on object equality here;
806 * really need an equals method on FeatureColour
808 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
811 * feature colour has changed externally - update originalData
813 for (Object[] original : originalData)
815 if (type.equals(original[TYPE_COLUMN]))
817 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
828 * new feature detected - add to original data (on top)
830 Object[][] newData = new Object[originalData.length
832 for (int i = 0; i < originalData.length; i++)
834 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
838 originalData = newData;
844 * Remove from the groups panel any checkboxes for groups that are not in the
845 * foundGroups set. This enables removing a group from the display when the last
846 * feature in that group is deleted.
850 protected void pruneGroups(Set<String> foundGroups)
852 for (int g = 0; g < groupPanel.getComponentCount(); g++)
854 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
855 if (!foundGroups.contains(checkbox.getText()))
857 groupPanel.remove(checkbox);
863 * reorder data based on the featureRenderers global priority list.
867 private void ensureOrder(Object[][] data)
869 boolean sort = false;
870 float[] order = new float[data.length];
871 for (int i = 0; i < order.length; i++)
873 order[i] = fr.getOrder(data[i][0].toString());
876 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
880 sort = sort || order[i - 1] > order[i];
885 jalview.util.QuickSort.sort(order, data);
890 * Offers a file chooser dialog, and then loads the feature colours and
891 * filters from file in XML format and unmarshals to Jalview feature settings
895 JalviewFileChooser chooser = new JalviewFileChooser("fc",
896 SEQUENCE_FEATURE_COLOURS);
897 chooser.setFileView(new JalviewFileView());
898 chooser.setDialogTitle(
899 MessageManager.getString("label.load_feature_colours"));
900 chooser.setToolTipText(MessageManager.getString("action.load"));
901 chooser.addResponse(new RunResponse(JalviewFileChooser.APPROVE_OPTION){
905 File file = chooser.getSelectedFile();
908 chooser.showOpenDialog(this);
912 * Loads feature colours and filters from XML stored in the given file
920 InputStreamReader in = new InputStreamReader(
921 new FileInputStream(file), "UTF-8");
923 JAXBContext jc = JAXBContext
924 .newInstance("jalview.xml.binding.jalview");
925 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
926 XMLStreamReader streamReader = XMLInputFactory.newInstance()
927 .createXMLStreamReader(in);
928 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
929 JalviewUserColours.class);
930 JalviewUserColours jucs = jbe.getValue();
932 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
935 * load feature colours
937 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
939 Colour newcol = jucs.getColour().get(i);
940 FeatureColourI colour = jalview.project.Jalview2XML
941 .parseColour(newcol);
942 fr.setColour(newcol.getName(), colour);
943 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
947 * load feature filters; loaded filters will replace any that are
948 * currently defined, other defined filters are left unchanged
950 for (int i = 0; i < jucs.getFilter().size(); i++)
952 Filter filterModel = jucs.getFilter().get(i);
953 String featureType = filterModel.getFeatureType();
954 FeatureMatcherSetI filter = jalview.project.Jalview2XML
955 .parseFilter(featureType, filterModel.getMatcherSet());
956 if (!filter.isEmpty())
958 fr.setFeatureFilter(featureType, filter);
963 * update feature settings table
968 Object[][] data = ((FeatureTableModel) table.getModel())
971 updateFeatureRenderer(data, false);
974 } catch (Exception ex)
976 System.out.println("Error loading User Colour File\n" + ex);
981 * Offers a file chooser dialog, and then saves the current feature colours
982 * and any filters to the selected file in XML format
986 JalviewFileChooser chooser = new JalviewFileChooser("fc",
987 SEQUENCE_FEATURE_COLOURS);
988 chooser.setFileView(new JalviewFileView());
989 chooser.setDialogTitle(
990 MessageManager.getString("label.save_feature_colours"));
991 chooser.setToolTipText(MessageManager.getString("action.save"));
992 int option = chooser.showSaveDialog(this);
993 if (option == JalviewFileChooser.APPROVE_OPTION)
995 File file = chooser.getSelectedFile();
1001 * Saves feature colours and filters to the given file
1005 void save(File file)
1007 JalviewUserColours ucs = new JalviewUserColours();
1008 ucs.setSchemeName("Sequence Features");
1011 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1012 new FileOutputStream(file), "UTF-8"));
1015 * sort feature types by colour order, from 0 (highest)
1018 Set<String> fr_colours = fr.getAllFeatureColours();
1019 String[] sortedTypes = fr_colours
1020 .toArray(new String[fr_colours.size()]);
1021 Arrays.sort(sortedTypes, new Comparator<String>()
1024 public int compare(String type1, String type2)
1026 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1031 * save feature colours
1033 for (String featureType : sortedTypes)
1035 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1036 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1038 ucs.getColour().add(col);
1042 * save any feature filters
1044 for (String featureType : sortedTypes)
1046 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1047 if (filter != null && !filter.isEmpty())
1049 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1050 FeatureMatcherI firstMatcher = iterator.next();
1051 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1052 .marshalFilter(firstMatcher, iterator,
1054 Filter filterModel = new Filter();
1055 filterModel.setFeatureType(featureType);
1056 filterModel.setMatcherSet(ms);
1057 ucs.getFilter().add(filterModel);
1060 JAXBContext jaxbContext = JAXBContext
1061 .newInstance(JalviewUserColours.class);
1062 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1063 jaxbMarshaller.marshal(
1064 new ObjectFactory().createJalviewUserColours(ucs), out);
1066 // jaxbMarshaller.marshal(object, pout);
1067 // marshaller.marshal(object);
1070 // ucs.marshal(out);
1072 } catch (Exception ex)
1074 ex.printStackTrace();
1078 public void invertSelection()
1080 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1081 for (int i = 0; i < data.length; i++)
1083 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1085 updateFeatureRenderer(data, true);
1089 public void orderByAvWidth()
1091 if (table == null || table.getModel() == null)
1095 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1096 float[] width = new float[data.length];
1100 for (int i = 0; i < data.length; i++)
1102 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1105 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1106 // weight - but have to make per
1107 // sequence, too (awidth[2])
1108 // if (width[i]==1) // hack to distinguish single width sequences.
1119 boolean sort = false;
1120 for (int i = 0; i < width.length; i++)
1122 // awidth = (float[]) typeWidth.get(data[i][0]);
1125 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1128 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1134 width[i] /= max; // normalize
1135 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1139 sort = sort || width[i - 1] > width[i];
1144 jalview.util.QuickSort.sort(width, data);
1145 // update global priority order
1148 updateFeatureRenderer(data, false);
1153 * Repaints the table using alternative code for Java and J2S
1155 private void repaintTable()
1160 // Here is a needed intervention
1161 // because generally we don't "repaint"
1162 // the table. We re-create the HTML divs
1163 // that is associated with it. A better
1164 // way to do this would be to fire a property change.
1165 @SuppressWarnings("unused")
1166 TableUI ui = table.getUI();
1168 * @j2sNative ui.repaintTable$();
1179 frame.setClosed(true);
1180 } catch (Exception exe)
1186 public void updateFeatureRenderer(Object[][] data)
1188 updateFeatureRenderer(data, true);
1192 * Update the priority order of features; only repaint if this changed the order
1193 * of visible features
1198 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1200 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1202 if (fr.setFeaturePriority(rowData, visibleNew))
1204 af.alignPanel.paintAlignment(true, true);
1209 * Converts table data into an array of data beans
1211 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1213 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1214 for (int i = 0; i < data.length; i++)
1216 String type = (String) data[i][TYPE_COLUMN];
1217 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1218 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1219 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1220 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1226 private void jbInit() throws Exception
1228 this.setLayout(new BorderLayout());
1230 JPanel settingsPane = new JPanel();
1231 settingsPane.setLayout(new BorderLayout());
1233 JPanel bigPanel = new JPanel();
1234 bigPanel.setLayout(new BorderLayout());
1236 groupPanel = new JPanel();
1237 bigPanel.add(groupPanel, BorderLayout.NORTH);
1239 JButton invert = new JButton(
1240 MessageManager.getString("label.invert_selection"));
1241 invert.setFont(JvSwingUtils.getLabelFont());
1242 invert.addActionListener(new ActionListener()
1245 public void actionPerformed(ActionEvent e)
1251 JButton optimizeOrder = new JButton(
1252 MessageManager.getString("label.optimise_order"));
1253 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1254 optimizeOrder.addActionListener(new ActionListener()
1257 public void actionPerformed(ActionEvent e)
1263 JButton sortByScore = new JButton(
1264 MessageManager.getString("label.seq_sort_by_score"));
1265 sortByScore.setFont(JvSwingUtils.getLabelFont());
1266 sortByScore.addActionListener(new ActionListener()
1269 public void actionPerformed(ActionEvent e)
1271 af.avc.sortAlignmentByFeatureScore(null);
1274 JButton sortByDens = new JButton(
1275 MessageManager.getString("label.sequence_sort_by_density"));
1276 sortByDens.setFont(JvSwingUtils.getLabelFont());
1277 sortByDens.addActionListener(new ActionListener()
1280 public void actionPerformed(ActionEvent e)
1282 af.avc.sortAlignmentByFeatureDensity(null);
1286 JButton help = new JButton(MessageManager.getString("action.help"));
1287 help.setFont(JvSwingUtils.getLabelFont());
1288 help.addActionListener(new ActionListener()
1291 public void actionPerformed(ActionEvent e)
1295 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1296 } catch (HelpSetException e1)
1298 e1.printStackTrace();
1302 help.setFont(JvSwingUtils.getLabelFont());
1303 help.setText(MessageManager.getString("action.help"));
1304 help.addActionListener(new ActionListener()
1307 public void actionPerformed(ActionEvent e)
1311 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1312 } catch (HelpSetException e1)
1314 e1.printStackTrace();
1319 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1320 cancel.setFont(JvSwingUtils.getLabelFont());
1321 cancel.addActionListener(new ActionListener()
1324 public void actionPerformed(ActionEvent e)
1326 fr.setTransparency(originalTransparency);
1327 fr.setFeatureFilters(originalFilters);
1328 updateFeatureRenderer(originalData);
1333 JButton ok = new JButton(MessageManager.getString("action.ok"));
1334 ok.setFont(JvSwingUtils.getLabelFont());
1335 ok.addActionListener(new ActionListener()
1338 public void actionPerformed(ActionEvent e)
1344 JButton loadColours = new JButton(
1345 MessageManager.getString("label.load_colours"));
1346 loadColours.setFont(JvSwingUtils.getLabelFont());
1347 loadColours.setToolTipText(
1348 MessageManager.getString("label.load_colours_tooltip"));
1349 loadColours.addActionListener(new ActionListener()
1352 public void actionPerformed(ActionEvent e)
1358 JButton saveColours = new JButton(
1359 MessageManager.getString("label.save_colours"));
1360 saveColours.setFont(JvSwingUtils.getLabelFont());
1361 saveColours.setToolTipText(
1362 MessageManager.getString("label.save_colours_tooltip"));
1363 saveColours.addActionListener(new ActionListener()
1366 public void actionPerformed(ActionEvent e)
1371 transparency.addChangeListener(new ChangeListener()
1374 public void stateChanged(ChangeEvent evt)
1376 if (!inConstruction)
1378 fr.setTransparency((100 - transparency.getValue()) / 100f);
1379 af.alignPanel.paintAlignment(true, true);
1384 transparency.setMaximum(70);
1385 transparency.setToolTipText(
1386 MessageManager.getString("label.transparency_tip"));
1388 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1389 bigPanel.add(transPanel, BorderLayout.SOUTH);
1391 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1392 transbuttons.add(optimizeOrder);
1393 transbuttons.add(invert);
1394 transbuttons.add(sortByScore);
1395 transbuttons.add(sortByDens);
1396 transbuttons.add(help);
1397 transPanel.add(transparency);
1398 transPanel.add(transbuttons);
1400 JPanel buttonPanel = new JPanel();
1401 buttonPanel.add(ok);
1402 buttonPanel.add(cancel);
1403 buttonPanel.add(loadColours);
1404 buttonPanel.add(saveColours);
1405 bigPanel.add(scrollPane, BorderLayout.CENTER);
1406 settingsPane.add(bigPanel, BorderLayout.CENTER);
1407 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1408 this.add(settingsPane);
1412 * Answers a suitable tooltip to show on the colour cell of the table
1416 * if true include 'click to edit' and similar text
1419 public static String getColorTooltip(FeatureColourI fcol,
1426 if (fcol.isSimpleColour())
1428 return withHint ? BASE_TOOLTIP : null;
1430 String description = fcol.getDescription();
1431 description = description.replaceAll("<", "<");
1432 description = description.replaceAll(">", ">");
1433 StringBuilder tt = new StringBuilder(description);
1436 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1438 return JvSwingUtils.wrapTooltip(true, tt.toString());
1441 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1444 boolean thr = false;
1445 StringBuilder tx = new StringBuilder();
1447 if (gcol.isColourByAttribute())
1449 tx.append(FeatureMatcher
1450 .toAttributeDisplayName(gcol.getAttributeName()));
1452 else if (!gcol.isColourByLabel())
1454 tx.append(MessageManager.getString("label.score"));
1457 if (gcol.isAboveThreshold())
1462 if (gcol.isBelowThreshold())
1467 if (gcol.isColourByLabel())
1473 if (!gcol.isColourByAttribute())
1481 Color newColor = gcol.getMaxColour();
1482 comp.setBackground(newColor);
1483 // System.err.println("Width is " + w / 2);
1484 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1485 comp.setIcon(ficon);
1486 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1487 // + newColor.getGreen() + ", " + newColor.getBlue()
1488 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1489 // + ", " + minCol.getBlue() + ")");
1491 comp.setHorizontalAlignment(SwingConstants.CENTER);
1492 comp.setText(tx.toString());
1495 // ///////////////////////////////////////////////////////////////////////
1496 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1497 // ///////////////////////////////////////////////////////////////////////
1498 class FeatureTableModel extends AbstractTableModel
1500 private String[] columnNames = {
1501 MessageManager.getString("label.feature_type"),
1502 MessageManager.getString("action.colour"),
1503 MessageManager.getString("label.filter"),
1504 MessageManager.getString("label.show") };
1506 private Object[][] data;
1508 FeatureTableModel(Object[][] data)
1513 public Object[][] getData()
1518 public void setData(Object[][] data)
1524 public int getColumnCount()
1526 return columnNames.length;
1529 public Object[] getRow(int row)
1535 public int getRowCount()
1541 public String getColumnName(int col)
1543 return columnNames[col];
1547 public Object getValueAt(int row, int col)
1549 return data[row][col];
1553 * Answers the class of the object in column c of the first row of the table
1556 public Class<?> getColumnClass(int c)
1558 Object v = getValueAt(0, c);
1559 return v == null ? null : v.getClass();
1563 public boolean isCellEditable(int row, int col)
1565 return col == 0 ? false : true;
1569 public void setValueAt(Object value, int row, int col)
1571 data[row][col] = value;
1572 fireTableCellUpdated(row, col);
1573 updateFeatureRenderer(data);
1578 class ColorRenderer extends JLabel implements TableCellRenderer
1580 Border unselectedBorder = null;
1582 Border selectedBorder = null;
1584 public ColorRenderer()
1586 setOpaque(true); // MUST do this for background to show up.
1587 setHorizontalTextPosition(SwingConstants.CENTER);
1588 setVerticalTextPosition(SwingConstants.CENTER);
1592 public Component getTableCellRendererComponent(JTable tbl, Object color,
1593 boolean isSelected, boolean hasFocus, int row, int column)
1595 FeatureColourI cellColour = (FeatureColourI) color;
1597 setBackground(tbl.getBackground());
1598 if (!cellColour.isSimpleColour())
1600 Rectangle cr = tbl.getCellRect(row, column, false);
1601 FeatureSettings.renderGraduatedColor(this, cellColour,
1602 (int) cr.getWidth(), (int) cr.getHeight());
1608 setBackground(cellColour.getColour());
1612 if (selectedBorder == null)
1614 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1615 tbl.getSelectionBackground());
1617 setBorder(selectedBorder);
1621 if (unselectedBorder == null)
1623 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1624 tbl.getBackground());
1626 setBorder(unselectedBorder);
1633 class FilterRenderer extends JLabel implements TableCellRenderer
1635 javax.swing.border.Border unselectedBorder = null;
1637 javax.swing.border.Border selectedBorder = null;
1639 public FilterRenderer()
1641 setOpaque(true); // MUST do this for background to show up.
1642 setHorizontalTextPosition(SwingConstants.CENTER);
1643 setVerticalTextPosition(SwingConstants.CENTER);
1647 public Component getTableCellRendererComponent(JTable tbl,
1648 Object filter, boolean isSelected, boolean hasFocus, int row,
1651 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1653 String asText = theFilter.toString();
1654 setBackground(tbl.getBackground());
1655 this.setText(asText);
1660 if (selectedBorder == null)
1662 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1663 tbl.getSelectionBackground());
1665 setBorder(selectedBorder);
1669 if (unselectedBorder == null)
1671 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1672 tbl.getBackground());
1674 setBorder(unselectedBorder);
1682 * update comp using rendering settings from gcol
1687 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1689 int w = comp.getWidth(), h = comp.getHeight();
1692 w = (int) comp.getPreferredSize().getWidth();
1693 h = (int) comp.getPreferredSize().getHeight();
1700 renderGraduatedColor(comp, gcol, w, h);
1703 class ColorEditor extends AbstractCellEditor
1704 implements TableCellEditor, ActionListener
1708 FeatureColourI currentColor;
1710 FeatureTypeSettings chooser;
1716 protected static final String EDIT = "edit";
1718 int rowSelected = 0;
1720 public ColorEditor(FeatureSettings fs)
1723 // Set up the editor (from the table's point of view),
1724 // which is a button.
1725 // This button brings up the color chooser dialog,
1726 // which is the editor from the user's point of view.
1727 button = new JButton();
1728 button.setActionCommand(EDIT);
1729 button.addActionListener(this);
1730 button.setBorderPainted(false);
1734 * Handles events from the editor button, and from the colour/filters
1735 * dialog's OK button
1738 public void actionPerformed(ActionEvent e)
1740 if (button == e.getSource())
1742 if (currentColor.isSimpleColour())
1745 * simple colour chooser
1747 String ttl = MessageManager.formatMessage("label.select_colour_for", type);
1748 ColourChooserListener listener = new ColourChooserListener()
1751 public void colourSelected(Color c)
1753 currentColor = new FeatureColour(c);
1754 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1755 fireEditingStopped();
1758 public void cancel()
1760 fireEditingStopped();
1763 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1768 * variable colour and filters dialog
1770 chooser = new FeatureTypeSettings(me.fr, type);
1771 if (!Jalview.isJS())
1773 chooser.setRequestFocusEnabled(true);
1774 chooser.requestFocus();
1776 chooser.addActionListener(this);
1777 fireEditingStopped();
1783 * after OK in variable colour dialog, any changes to colour
1784 * (or filters!) are already set in FeatureRenderer, so just
1785 * update table data without triggering updateFeatureRenderer
1787 currentColor = fr.getFeatureColours().get(type);
1788 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1789 if (currentFilter == null)
1791 currentFilter = new FeatureMatcherSet();
1793 Object[] data = ((FeatureTableModel) table.getModel())
1794 .getData()[rowSelected];
1795 data[COLOUR_COLUMN] = currentColor;
1796 data[FILTER_COLUMN] = currentFilter;
1798 fireEditingStopped();
1799 me.table.validate();
1803 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1805 public Object getCellEditorValue()
1807 return currentColor;
1810 // Implement the one method defined by TableCellEditor.
1812 public Component getTableCellEditorComponent(JTable theTable, Object value,
1813 boolean isSelected, int row, int column)
1815 currentColor = (FeatureColourI) value;
1816 this.rowSelected = row;
1817 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1818 button.setOpaque(true);
1819 button.setBackground(me.getBackground());
1820 if (!currentColor.isSimpleColour())
1822 JLabel btn = new JLabel();
1823 btn.setSize(button.getSize());
1824 FeatureSettings.renderGraduatedColor(btn, currentColor);
1825 button.setBackground(btn.getBackground());
1826 button.setIcon(btn.getIcon());
1827 button.setText(btn.getText());
1832 button.setIcon(null);
1833 button.setBackground(currentColor.getColour());
1840 * The cell editor for the Filter column. It displays the text of any filters
1841 * for the feature type in that row (in full as a tooltip, possible abbreviated
1842 * as display text). On click in the cell, opens the Feature Display Settings
1843 * dialog at the Filters tab.
1845 class FilterEditor extends AbstractCellEditor
1846 implements TableCellEditor, ActionListener
1850 FeatureMatcherSetI currentFilter;
1858 protected static final String EDIT = "edit";
1860 int rowSelected = 0;
1862 public FilterEditor(FeatureSettings me)
1865 button = new JButton();
1866 button.setActionCommand(EDIT);
1867 button.addActionListener(this);
1868 button.setBorderPainted(false);
1872 * Handles events from the editor button
1875 public void actionPerformed(ActionEvent e)
1877 if (button == e.getSource())
1879 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1880 chooser.addActionListener(this);
1881 chooser.setRequestFocusEnabled(true);
1882 chooser.requestFocus();
1883 if (lastLocation != null)
1885 // todo open at its last position on screen
1886 chooser.setBounds(lastLocation.x, lastLocation.y,
1887 chooser.getWidth(), chooser.getHeight());
1890 fireEditingStopped();
1892 else if (e.getSource() instanceof Component)
1896 * after OK in variable colour dialog, any changes to filter
1897 * (or colours!) are already set in FeatureRenderer, so just
1898 * update table data without triggering updateFeatureRenderer
1900 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1901 currentFilter = me.fr.getFeatureFilter(type);
1902 if (currentFilter == null)
1904 currentFilter = new FeatureMatcherSet();
1906 Object[] data = ((FeatureTableModel) table.getModel())
1907 .getData()[rowSelected];
1908 data[COLOUR_COLUMN] = currentColor;
1909 data[FILTER_COLUMN] = currentFilter;
1910 fireEditingStopped();
1911 me.table.validate();
1916 public Object getCellEditorValue()
1918 return currentFilter;
1922 public Component getTableCellEditorComponent(JTable theTable, Object value,
1923 boolean isSelected, int row, int column)
1925 currentFilter = (FeatureMatcherSetI) value;
1926 this.rowSelected = row;
1927 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1928 button.setOpaque(true);
1929 button.setBackground(me.getBackground());
1930 button.setText(currentFilter.toString());
1931 button.setIcon(null);
1937 class FeatureIcon implements Icon
1939 FeatureColourI gcol;
1943 boolean midspace = false;
1945 int width = 50, height = 20;
1947 int s1, e1; // start and end of midpoint band for thresholded symbol
1949 Color mpcolour = Color.white;
1951 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1971 public int getIconWidth()
1977 public int getIconHeight()
1983 public void paintIcon(Component c, Graphics g, int x, int y)
1986 if (gcol.isColourByLabel())
1989 g.fillRect(0, 0, width, height);
1990 // need an icon here.
1991 g.setColor(gcol.getMaxColour());
1993 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1995 // g.setFont(g.getFont().deriveFont(
1996 // AffineTransform.getScaleInstance(
1997 // width/g.getFontMetrics().stringWidth("Label"),
1998 // height/g.getFontMetrics().getHeight())));
2000 g.drawString(MessageManager.getString("label.label"), 0, 0);
2005 Color minCol = gcol.getMinColour();
2007 g.fillRect(0, 0, s1, height);
2010 g.setColor(Color.white);
2011 g.fillRect(s1, 0, e1 - s1, height);
2013 g.setColor(gcol.getMaxColour());
2014 // g.fillRect(0, e1, width - e1, height); // BH 2018
2015 g.fillRect(e1, 0, width - e1, height);