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(0, new RunResponse(JalviewFileChooser.APPROVE_OPTION)
906 File file = chooser.getSelectedFile();
910 chooser.showOpenDialog(this);
914 * Loads feature colours and filters from XML stored in the given file
922 InputStreamReader in = new InputStreamReader(
923 new FileInputStream(file), "UTF-8");
925 JAXBContext jc = JAXBContext
926 .newInstance("jalview.xml.binding.jalview");
927 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
928 XMLStreamReader streamReader = XMLInputFactory.newInstance()
929 .createXMLStreamReader(in);
930 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
931 JalviewUserColours.class);
932 JalviewUserColours jucs = jbe.getValue();
934 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
937 * load feature colours
939 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
941 Colour newcol = jucs.getColour().get(i);
942 FeatureColourI colour = jalview.project.Jalview2XML
943 .parseColour(newcol);
944 fr.setColour(newcol.getName(), colour);
945 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
949 * load feature filters; loaded filters will replace any that are
950 * currently defined, other defined filters are left unchanged
952 for (int i = 0; i < jucs.getFilter().size(); i++)
954 Filter filterModel = jucs.getFilter().get(i);
955 String featureType = filterModel.getFeatureType();
956 FeatureMatcherSetI filter = jalview.project.Jalview2XML
957 .parseFilter(featureType, filterModel.getMatcherSet());
958 if (!filter.isEmpty())
960 fr.setFeatureFilter(featureType, filter);
965 * update feature settings table
970 Object[][] data = ((FeatureTableModel) table.getModel())
973 updateFeatureRenderer(data, false);
976 } catch (Exception ex)
978 System.out.println("Error loading User Colour File\n" + ex);
983 * Offers a file chooser dialog, and then saves the current feature colours
984 * and any filters to the selected file in XML format
988 JalviewFileChooser chooser = new JalviewFileChooser("fc",
989 SEQUENCE_FEATURE_COLOURS);
990 chooser.setFileView(new JalviewFileView());
991 chooser.setDialogTitle(
992 MessageManager.getString("label.save_feature_colours"));
993 chooser.setToolTipText(MessageManager.getString("action.save"));
994 int option = chooser.showSaveDialog(this);
995 if (option == JalviewFileChooser.APPROVE_OPTION)
997 File file = chooser.getSelectedFile();
1003 * Saves feature colours and filters to the given file
1007 void save(File file)
1009 JalviewUserColours ucs = new JalviewUserColours();
1010 ucs.setSchemeName("Sequence Features");
1013 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1014 new FileOutputStream(file), "UTF-8"));
1017 * sort feature types by colour order, from 0 (highest)
1020 Set<String> fr_colours = fr.getAllFeatureColours();
1021 String[] sortedTypes = fr_colours
1022 .toArray(new String[fr_colours.size()]);
1023 Arrays.sort(sortedTypes, new Comparator<String>()
1026 public int compare(String type1, String type2)
1028 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1033 * save feature colours
1035 for (String featureType : sortedTypes)
1037 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1038 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1040 ucs.getColour().add(col);
1044 * save any feature filters
1046 for (String featureType : sortedTypes)
1048 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1049 if (filter != null && !filter.isEmpty())
1051 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1052 FeatureMatcherI firstMatcher = iterator.next();
1053 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1054 .marshalFilter(firstMatcher, iterator,
1056 Filter filterModel = new Filter();
1057 filterModel.setFeatureType(featureType);
1058 filterModel.setMatcherSet(ms);
1059 ucs.getFilter().add(filterModel);
1062 JAXBContext jaxbContext = JAXBContext
1063 .newInstance(JalviewUserColours.class);
1064 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1065 jaxbMarshaller.marshal(
1066 new ObjectFactory().createJalviewUserColours(ucs), out);
1068 // jaxbMarshaller.marshal(object, pout);
1069 // marshaller.marshal(object);
1072 // ucs.marshal(out);
1074 } catch (Exception ex)
1076 ex.printStackTrace();
1080 public void invertSelection()
1082 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1083 for (int i = 0; i < data.length; i++)
1085 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1087 updateFeatureRenderer(data, true);
1091 public void orderByAvWidth()
1093 if (table == null || table.getModel() == null)
1097 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1098 float[] width = new float[data.length];
1102 for (int i = 0; i < data.length; i++)
1104 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1107 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1108 // weight - but have to make per
1109 // sequence, too (awidth[2])
1110 // if (width[i]==1) // hack to distinguish single width sequences.
1121 boolean sort = false;
1122 for (int i = 0; i < width.length; i++)
1124 // awidth = (float[]) typeWidth.get(data[i][0]);
1127 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1130 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1136 width[i] /= max; // normalize
1137 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1141 sort = sort || width[i - 1] > width[i];
1146 jalview.util.QuickSort.sort(width, data);
1147 // update global priority order
1150 updateFeatureRenderer(data, false);
1155 * Repaints the table using alternative code for Java and J2S
1157 private void repaintTable()
1162 // Here is a needed intervention
1163 // because generally we don't "repaint"
1164 // the table. We re-create the HTML divs
1165 // that is associated with it. A better
1166 // way to do this would be to fire a property change.
1167 @SuppressWarnings("unused")
1168 TableUI ui = table.getUI();
1170 * @j2sNative ui.repaintTable$();
1181 frame.setClosed(true);
1182 } catch (Exception exe)
1188 public void updateFeatureRenderer(Object[][] data)
1190 updateFeatureRenderer(data, true);
1194 * Update the priority order of features; only repaint if this changed the order
1195 * of visible features
1200 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1202 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1204 if (fr.setFeaturePriority(rowData, visibleNew))
1206 af.alignPanel.paintAlignment(true, true);
1211 * Converts table data into an array of data beans
1213 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1215 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1216 for (int i = 0; i < data.length; i++)
1218 String type = (String) data[i][TYPE_COLUMN];
1219 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1220 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1221 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1222 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1228 private void jbInit() throws Exception
1230 this.setLayout(new BorderLayout());
1232 JPanel settingsPane = new JPanel();
1233 settingsPane.setLayout(new BorderLayout());
1235 JPanel bigPanel = new JPanel();
1236 bigPanel.setLayout(new BorderLayout());
1238 groupPanel = new JPanel();
1239 bigPanel.add(groupPanel, BorderLayout.NORTH);
1241 JButton invert = new JButton(
1242 MessageManager.getString("label.invert_selection"));
1243 invert.setFont(JvSwingUtils.getLabelFont());
1244 invert.addActionListener(new ActionListener()
1247 public void actionPerformed(ActionEvent e)
1253 JButton optimizeOrder = new JButton(
1254 MessageManager.getString("label.optimise_order"));
1255 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1256 optimizeOrder.addActionListener(new ActionListener()
1259 public void actionPerformed(ActionEvent e)
1265 JButton sortByScore = new JButton(
1266 MessageManager.getString("label.seq_sort_by_score"));
1267 sortByScore.setFont(JvSwingUtils.getLabelFont());
1268 sortByScore.addActionListener(new ActionListener()
1271 public void actionPerformed(ActionEvent e)
1273 af.avc.sortAlignmentByFeatureScore(null);
1276 JButton sortByDens = new JButton(
1277 MessageManager.getString("label.sequence_sort_by_density"));
1278 sortByDens.setFont(JvSwingUtils.getLabelFont());
1279 sortByDens.addActionListener(new ActionListener()
1282 public void actionPerformed(ActionEvent e)
1284 af.avc.sortAlignmentByFeatureDensity(null);
1288 JButton help = new JButton(MessageManager.getString("action.help"));
1289 help.setFont(JvSwingUtils.getLabelFont());
1290 help.addActionListener(new ActionListener()
1293 public void actionPerformed(ActionEvent e)
1297 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1298 } catch (HelpSetException e1)
1300 e1.printStackTrace();
1304 help.setFont(JvSwingUtils.getLabelFont());
1305 help.setText(MessageManager.getString("action.help"));
1306 help.addActionListener(new ActionListener()
1309 public void actionPerformed(ActionEvent e)
1313 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1314 } catch (HelpSetException e1)
1316 e1.printStackTrace();
1321 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1322 cancel.setFont(JvSwingUtils.getLabelFont());
1323 cancel.addActionListener(new ActionListener()
1326 public void actionPerformed(ActionEvent e)
1328 fr.setTransparency(originalTransparency);
1329 fr.setFeatureFilters(originalFilters);
1330 updateFeatureRenderer(originalData);
1335 JButton ok = new JButton(MessageManager.getString("action.ok"));
1336 ok.setFont(JvSwingUtils.getLabelFont());
1337 ok.addActionListener(new ActionListener()
1340 public void actionPerformed(ActionEvent e)
1346 JButton loadColours = new JButton(
1347 MessageManager.getString("label.load_colours"));
1348 loadColours.setFont(JvSwingUtils.getLabelFont());
1349 loadColours.setToolTipText(
1350 MessageManager.getString("label.load_colours_tooltip"));
1351 loadColours.addActionListener(new ActionListener()
1354 public void actionPerformed(ActionEvent e)
1360 JButton saveColours = new JButton(
1361 MessageManager.getString("label.save_colours"));
1362 saveColours.setFont(JvSwingUtils.getLabelFont());
1363 saveColours.setToolTipText(
1364 MessageManager.getString("label.save_colours_tooltip"));
1365 saveColours.addActionListener(new ActionListener()
1368 public void actionPerformed(ActionEvent e)
1373 transparency.addChangeListener(new ChangeListener()
1376 public void stateChanged(ChangeEvent evt)
1378 if (!inConstruction)
1380 fr.setTransparency((100 - transparency.getValue()) / 100f);
1381 af.alignPanel.paintAlignment(true, true);
1386 transparency.setMaximum(70);
1387 transparency.setToolTipText(
1388 MessageManager.getString("label.transparency_tip"));
1390 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1391 bigPanel.add(transPanel, BorderLayout.SOUTH);
1393 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1394 transbuttons.add(optimizeOrder);
1395 transbuttons.add(invert);
1396 transbuttons.add(sortByScore);
1397 transbuttons.add(sortByDens);
1398 transbuttons.add(help);
1399 transPanel.add(transparency);
1400 transPanel.add(transbuttons);
1402 JPanel buttonPanel = new JPanel();
1403 buttonPanel.add(ok);
1404 buttonPanel.add(cancel);
1405 buttonPanel.add(loadColours);
1406 buttonPanel.add(saveColours);
1407 bigPanel.add(scrollPane, BorderLayout.CENTER);
1408 settingsPane.add(bigPanel, BorderLayout.CENTER);
1409 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1410 this.add(settingsPane);
1414 * Answers a suitable tooltip to show on the colour cell of the table
1418 * if true include 'click to edit' and similar text
1421 public static String getColorTooltip(FeatureColourI fcol,
1428 if (fcol.isSimpleColour())
1430 return withHint ? BASE_TOOLTIP : null;
1432 String description = fcol.getDescription();
1433 description = description.replaceAll("<", "<");
1434 description = description.replaceAll(">", ">");
1435 StringBuilder tt = new StringBuilder(description);
1438 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1440 return JvSwingUtils.wrapTooltip(true, tt.toString());
1443 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1446 boolean thr = false;
1447 StringBuilder tx = new StringBuilder();
1449 if (gcol.isColourByAttribute())
1451 tx.append(FeatureMatcher
1452 .toAttributeDisplayName(gcol.getAttributeName()));
1454 else if (!gcol.isColourByLabel())
1456 tx.append(MessageManager.getString("label.score"));
1459 if (gcol.isAboveThreshold())
1464 if (gcol.isBelowThreshold())
1469 if (gcol.isColourByLabel())
1475 if (!gcol.isColourByAttribute())
1483 Color newColor = gcol.getMaxColour();
1484 comp.setBackground(newColor);
1485 // System.err.println("Width is " + w / 2);
1486 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1487 comp.setIcon(ficon);
1488 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1489 // + newColor.getGreen() + ", " + newColor.getBlue()
1490 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1491 // + ", " + minCol.getBlue() + ")");
1493 comp.setHorizontalAlignment(SwingConstants.CENTER);
1494 comp.setText(tx.toString());
1497 // ///////////////////////////////////////////////////////////////////////
1498 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1499 // ///////////////////////////////////////////////////////////////////////
1500 class FeatureTableModel extends AbstractTableModel
1502 private String[] columnNames = {
1503 MessageManager.getString("label.feature_type"),
1504 MessageManager.getString("action.colour"),
1505 MessageManager.getString("label.filter"),
1506 MessageManager.getString("label.show") };
1508 private Object[][] data;
1510 FeatureTableModel(Object[][] data)
1515 public Object[][] getData()
1520 public void setData(Object[][] data)
1526 public int getColumnCount()
1528 return columnNames.length;
1531 public Object[] getRow(int row)
1537 public int getRowCount()
1543 public String getColumnName(int col)
1545 return columnNames[col];
1549 public Object getValueAt(int row, int col)
1551 return data[row][col];
1555 * Answers the class of the object in column c of the first row of the table
1558 public Class<?> getColumnClass(int c)
1560 Object v = getValueAt(0, c);
1561 return v == null ? null : v.getClass();
1565 public boolean isCellEditable(int row, int col)
1567 return col == 0 ? false : true;
1571 public void setValueAt(Object value, int row, int col)
1573 data[row][col] = value;
1574 fireTableCellUpdated(row, col);
1575 updateFeatureRenderer(data);
1580 class ColorRenderer extends JLabel implements TableCellRenderer
1582 Border unselectedBorder = null;
1584 Border selectedBorder = null;
1586 public ColorRenderer()
1588 setOpaque(true); // MUST do this for background to show up.
1589 setHorizontalTextPosition(SwingConstants.CENTER);
1590 setVerticalTextPosition(SwingConstants.CENTER);
1594 public Component getTableCellRendererComponent(JTable tbl, Object color,
1595 boolean isSelected, boolean hasFocus, int row, int column)
1597 FeatureColourI cellColour = (FeatureColourI) color;
1599 setBackground(tbl.getBackground());
1600 if (!cellColour.isSimpleColour())
1602 Rectangle cr = tbl.getCellRect(row, column, false);
1603 FeatureSettings.renderGraduatedColor(this, cellColour,
1604 (int) cr.getWidth(), (int) cr.getHeight());
1610 setBackground(cellColour.getColour());
1614 if (selectedBorder == null)
1616 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1617 tbl.getSelectionBackground());
1619 setBorder(selectedBorder);
1623 if (unselectedBorder == null)
1625 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1626 tbl.getBackground());
1628 setBorder(unselectedBorder);
1635 class FilterRenderer extends JLabel implements TableCellRenderer
1637 javax.swing.border.Border unselectedBorder = null;
1639 javax.swing.border.Border selectedBorder = null;
1641 public FilterRenderer()
1643 setOpaque(true); // MUST do this for background to show up.
1644 setHorizontalTextPosition(SwingConstants.CENTER);
1645 setVerticalTextPosition(SwingConstants.CENTER);
1649 public Component getTableCellRendererComponent(JTable tbl,
1650 Object filter, boolean isSelected, boolean hasFocus, int row,
1653 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1655 String asText = theFilter.toString();
1656 setBackground(tbl.getBackground());
1657 this.setText(asText);
1662 if (selectedBorder == null)
1664 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1665 tbl.getSelectionBackground());
1667 setBorder(selectedBorder);
1671 if (unselectedBorder == null)
1673 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1674 tbl.getBackground());
1676 setBorder(unselectedBorder);
1684 * update comp using rendering settings from gcol
1689 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1691 int w = comp.getWidth(), h = comp.getHeight();
1694 w = (int) comp.getPreferredSize().getWidth();
1695 h = (int) comp.getPreferredSize().getHeight();
1702 renderGraduatedColor(comp, gcol, w, h);
1705 class ColorEditor extends AbstractCellEditor
1706 implements TableCellEditor, ActionListener
1710 FeatureColourI currentColor;
1712 FeatureTypeSettings chooser;
1718 protected static final String EDIT = "edit";
1720 int rowSelected = 0;
1722 public ColorEditor(FeatureSettings fs)
1725 // Set up the editor (from the table's point of view),
1726 // which is a button.
1727 // This button brings up the color chooser dialog,
1728 // which is the editor from the user's point of view.
1729 button = new JButton();
1730 button.setActionCommand(EDIT);
1731 button.addActionListener(this);
1732 button.setBorderPainted(false);
1736 * Handles events from the editor button, and from the colour/filters
1737 * dialog's OK button
1740 public void actionPerformed(ActionEvent e)
1742 if (button == e.getSource())
1744 if (currentColor.isSimpleColour())
1747 * simple colour chooser
1749 String ttl = MessageManager.formatMessage("label.select_colour_for", type);
1750 ColourChooserListener listener = new ColourChooserListener()
1753 public void colourSelected(Color c)
1755 currentColor = new FeatureColour(c);
1756 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1757 fireEditingStopped();
1760 public void cancel()
1762 fireEditingStopped();
1765 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1770 * variable colour and filters dialog
1772 chooser = new FeatureTypeSettings(me.fr, type);
1773 if (!Jalview.isJS())
1775 chooser.setRequestFocusEnabled(true);
1776 chooser.requestFocus();
1778 chooser.addActionListener(this);
1779 fireEditingStopped();
1785 * after OK in variable colour dialog, any changes to colour
1786 * (or filters!) are already set in FeatureRenderer, so just
1787 * update table data without triggering updateFeatureRenderer
1789 currentColor = fr.getFeatureColours().get(type);
1790 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1791 if (currentFilter == null)
1793 currentFilter = new FeatureMatcherSet();
1795 Object[] data = ((FeatureTableModel) table.getModel())
1796 .getData()[rowSelected];
1797 data[COLOUR_COLUMN] = currentColor;
1798 data[FILTER_COLUMN] = currentFilter;
1800 fireEditingStopped();
1801 me.table.validate();
1805 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1807 public Object getCellEditorValue()
1809 return currentColor;
1812 // Implement the one method defined by TableCellEditor.
1814 public Component getTableCellEditorComponent(JTable theTable, Object value,
1815 boolean isSelected, int row, int column)
1817 currentColor = (FeatureColourI) value;
1818 this.rowSelected = row;
1819 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1820 button.setOpaque(true);
1821 button.setBackground(me.getBackground());
1822 if (!currentColor.isSimpleColour())
1824 JLabel btn = new JLabel();
1825 btn.setSize(button.getSize());
1826 FeatureSettings.renderGraduatedColor(btn, currentColor);
1827 button.setBackground(btn.getBackground());
1828 button.setIcon(btn.getIcon());
1829 button.setText(btn.getText());
1834 button.setIcon(null);
1835 button.setBackground(currentColor.getColour());
1842 * The cell editor for the Filter column. It displays the text of any filters
1843 * for the feature type in that row (in full as a tooltip, possible abbreviated
1844 * as display text). On click in the cell, opens the Feature Display Settings
1845 * dialog at the Filters tab.
1847 class FilterEditor extends AbstractCellEditor
1848 implements TableCellEditor, ActionListener
1852 FeatureMatcherSetI currentFilter;
1860 protected static final String EDIT = "edit";
1862 int rowSelected = 0;
1864 public FilterEditor(FeatureSettings me)
1867 button = new JButton();
1868 button.setActionCommand(EDIT);
1869 button.addActionListener(this);
1870 button.setBorderPainted(false);
1874 * Handles events from the editor button
1877 public void actionPerformed(ActionEvent e)
1879 if (button == e.getSource())
1881 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1882 chooser.addActionListener(this);
1883 chooser.setRequestFocusEnabled(true);
1884 chooser.requestFocus();
1885 if (lastLocation != null)
1887 // todo open at its last position on screen
1888 chooser.setBounds(lastLocation.x, lastLocation.y,
1889 chooser.getWidth(), chooser.getHeight());
1892 fireEditingStopped();
1894 else if (e.getSource() instanceof Component)
1898 * after OK in variable colour dialog, any changes to filter
1899 * (or colours!) are already set in FeatureRenderer, so just
1900 * update table data without triggering updateFeatureRenderer
1902 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1903 currentFilter = me.fr.getFeatureFilter(type);
1904 if (currentFilter == null)
1906 currentFilter = new FeatureMatcherSet();
1908 Object[] data = ((FeatureTableModel) table.getModel())
1909 .getData()[rowSelected];
1910 data[COLOUR_COLUMN] = currentColor;
1911 data[FILTER_COLUMN] = currentFilter;
1912 fireEditingStopped();
1913 me.table.validate();
1918 public Object getCellEditorValue()
1920 return currentFilter;
1924 public Component getTableCellEditorComponent(JTable theTable, Object value,
1925 boolean isSelected, int row, int column)
1927 currentFilter = (FeatureMatcherSetI) value;
1928 this.rowSelected = row;
1929 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1930 button.setOpaque(true);
1931 button.setBackground(me.getBackground());
1932 button.setText(currentFilter.toString());
1933 button.setIcon(null);
1939 class FeatureIcon implements Icon
1941 FeatureColourI gcol;
1945 boolean midspace = false;
1947 int width = 50, height = 20;
1949 int s1, e1; // start and end of midpoint band for thresholded symbol
1951 Color mpcolour = Color.white;
1953 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1973 public int getIconWidth()
1979 public int getIconHeight()
1985 public void paintIcon(Component c, Graphics g, int x, int y)
1988 if (gcol.isColourByLabel())
1991 g.fillRect(0, 0, width, height);
1992 // need an icon here.
1993 g.setColor(gcol.getMaxColour());
1995 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1997 // g.setFont(g.getFont().deriveFont(
1998 // AffineTransform.getScaleInstance(
1999 // width/g.getFontMetrics().stringWidth("Label"),
2000 // height/g.getFontMetrics().getHeight())));
2002 g.drawString(MessageManager.getString("label.label"), 0, 0);
2007 Color minCol = gcol.getMinColour();
2009 g.fillRect(0, 0, s1, height);
2012 g.setColor(Color.white);
2013 g.fillRect(s1, 0, e1 - s1, height);
2015 g.setColor(gcol.getMaxColour());
2016 // g.fillRect(0, e1, width - e1, height); // BH 2018
2017 g.fillRect(e1, 0, width - e1, height);