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.AlignViewportI;
24 import jalview.api.FeatureColourI;
25 import jalview.api.FeatureSettingsControllerI;
26 import jalview.api.ViewStyleI;
27 import jalview.datamodel.AlignmentI;
28 import jalview.datamodel.SequenceI;
29 import jalview.datamodel.features.FeatureMatcher;
30 import jalview.datamodel.features.FeatureMatcherI;
31 import jalview.datamodel.features.FeatureMatcherSet;
32 import jalview.datamodel.features.FeatureMatcherSetI;
33 import jalview.gui.Help.HelpId;
34 import jalview.io.JalviewFileChooser;
35 import jalview.io.JalviewFileView;
36 import jalview.schemes.FeatureColour;
37 import jalview.util.MessageManager;
38 import jalview.util.Platform;
39 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
40 import jalview.viewmodel.styles.ViewStyle;
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;
50 import java.awt.FlowLayout;
52 import java.awt.Graphics;
53 import java.awt.GridLayout;
54 import java.awt.Point;
55 import java.awt.Rectangle;
56 import java.awt.event.ActionEvent;
57 import java.awt.event.ActionListener;
58 import java.awt.event.ItemEvent;
59 import java.awt.event.ItemListener;
60 import java.awt.event.MouseAdapter;
61 import java.awt.event.MouseEvent;
62 import java.awt.event.MouseMotionAdapter;
63 import java.beans.PropertyChangeEvent;
64 import java.beans.PropertyChangeListener;
66 import java.io.FileInputStream;
67 import java.io.FileOutputStream;
68 import java.io.InputStreamReader;
69 import java.io.OutputStreamWriter;
70 import java.io.PrintWriter;
71 import java.util.Arrays;
72 import java.util.Comparator;
73 import java.util.HashMap;
74 import java.util.HashSet;
75 import java.util.Hashtable;
76 import java.util.Iterator;
77 import java.util.List;
81 import javax.help.HelpSetException;
82 import javax.swing.AbstractCellEditor;
83 import javax.swing.BorderFactory;
84 import javax.swing.Icon;
85 import javax.swing.JButton;
86 import javax.swing.JCheckBox;
87 import javax.swing.JColorChooser;
88 import javax.swing.JDialog;
89 import javax.swing.JInternalFrame;
90 import javax.swing.JLabel;
91 import javax.swing.JLayeredPane;
92 import javax.swing.JMenuItem;
93 import javax.swing.JPanel;
94 import javax.swing.JPopupMenu;
95 import javax.swing.JScrollPane;
96 import javax.swing.JSlider;
97 import javax.swing.JTable;
98 import javax.swing.ListSelectionModel;
99 import javax.swing.SwingConstants;
100 import javax.swing.border.Border;
101 import javax.swing.event.ChangeEvent;
102 import javax.swing.event.ChangeListener;
103 import javax.swing.table.AbstractTableModel;
104 import javax.swing.table.JTableHeader;
105 import javax.swing.table.TableCellEditor;
106 import javax.swing.table.TableCellRenderer;
107 import javax.swing.table.TableColumn;
108 import javax.xml.bind.JAXBContext;
109 import javax.xml.bind.JAXBElement;
110 import javax.xml.bind.Marshaller;
111 import javax.xml.stream.XMLInputFactory;
112 import javax.xml.stream.XMLStreamReader;
114 public class FeatureSettings extends JPanel
115 implements FeatureSettingsControllerI
117 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
118 .getString("label.sequence_feature_colours");
121 * column indices of fields in Feature Settings table
123 static final int TYPE_COLUMN = 0;
125 static final int COLOUR_COLUMN = 1;
127 static final int FILTER_COLUMN = 2;
129 static final int SHOW_COLUMN = 3;
131 private static final int COLUMN_COUNT = 4;
133 private static final int MIN_WIDTH = 400;
135 private static final int MIN_HEIGHT = 400;
137 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
139 final FeatureRenderer fr;
141 public final AlignFrame af;
144 * 'original' fields hold settings to restore on Cancel
146 Object[][] originalData;
148 private float originalTransparency;
150 private ViewStyleI originalViewStyle;
152 private Map<String, FeatureMatcherSetI> originalFilters;
154 final JInternalFrame frame;
156 JScrollPane scrollPane = new JScrollPane();
162 JSlider transparency = new JSlider();
165 * when true, constructor is still executing - so ignore UI events
167 protected volatile boolean inConstruction = true;
169 int selectedRow = -1;
171 boolean resettingTable = false;
174 * true when Feature Settings are updating from feature renderer
176 private boolean handlingUpdate = false;
179 * holds {featureCount, totalExtent} for each feature type
181 Map<String, float[]> typeWidth = null;
188 public FeatureSettings(AlignFrame alignFrame)
190 this.af = alignFrame;
191 fr = af.getFeatureRenderer();
193 // save transparency for restore on Cancel
194 originalTransparency = fr.getTransparency();
195 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
196 transparency.setMaximum(100 - originalTransparencyAsPercent);
198 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
199 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
204 } catch (Exception ex)
206 ex.printStackTrace();
212 public String getToolTipText(MouseEvent e)
215 int column = table.columnAtPoint(e.getPoint());
216 int row = table.rowAtPoint(e.getPoint());
221 tip = JvSwingUtils.wrapTooltip(true, MessageManager
222 .getString("label.feature_settings_click_drag"));
225 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
227 tip = getColorTooltip(colour, true);
230 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
234 .getString("label.configure_feature_tooltip")
245 * Position the tooltip near the bottom edge of, and half way across, the
249 public Point getToolTipLocation(MouseEvent e)
251 Point point = e.getPoint();
252 int column = table.columnAtPoint(point);
253 int row = table.rowAtPoint(point);
254 Rectangle r = getCellRect(row, column, false);
255 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
259 JTableHeader tableHeader = table.getTableHeader();
260 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
261 tableHeader.setReorderingAllowed(false);
262 table.setFont(new Font("Verdana", Font.PLAIN, 12));
264 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
265 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
267 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
268 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
270 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
271 new ColorRenderer(), new ColorEditor(this));
272 table.addColumn(colourColumn);
274 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
275 new FilterRenderer(), new FilterEditor(this));
276 table.addColumn(filterColumn);
278 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
280 table.addMouseListener(new MouseAdapter()
283 public void mousePressed(MouseEvent evt)
285 selectedRow = table.rowAtPoint(evt.getPoint());
286 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
287 if (evt.isPopupTrigger())
289 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
290 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
293 else if (evt.getClickCount() == 2)
295 boolean invertSelection = evt.isAltDown();
296 boolean toggleSelection = Platform.isControlDown(evt);
297 boolean extendSelection = evt.isShiftDown();
298 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
299 invertSelection, extendSelection, toggleSelection, type);
300 fr.ap.av.sendSelection();
304 // isPopupTrigger fires on mouseReleased on Windows
306 public void mouseReleased(MouseEvent evt)
308 selectedRow = table.rowAtPoint(evt.getPoint());
309 if (evt.isPopupTrigger())
311 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
312 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
313 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
319 table.addMouseMotionListener(new MouseMotionAdapter()
322 public void mouseDragged(MouseEvent evt)
324 int newRow = table.rowAtPoint(evt.getPoint());
325 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
328 * reposition 'selectedRow' to 'newRow' (the dragged to location)
329 * this could be more than one row away for a very fast drag action
330 * so just swap it with adjacent rows until we get it there
332 Object[][] data = ((FeatureTableModel) table.getModel())
334 int direction = newRow < selectedRow ? -1 : 1;
335 for (int i = selectedRow; i != newRow; i += direction)
337 Object[] temp = data[i];
338 data[i] = data[i + direction];
339 data[i + direction] = temp;
341 updateFeatureRenderer(data);
343 selectedRow = newRow;
347 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
348 // MessageManager.getString("label.feature_settings_click_drag")));
349 scrollPane.setViewportView(table);
351 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
353 fr.findAllFeatures(true); // display everything!
356 discoverAllFeatureData();
357 final PropertyChangeListener change;
358 final FeatureSettings fs = this;
359 fr.addPropertyChangeListener(change = new PropertyChangeListener()
362 public void propertyChange(PropertyChangeEvent evt)
364 if (!fs.resettingTable && !fs.handlingUpdate)
366 fs.handlingUpdate = true;
368 // new groups may be added with new sequence feature types only
369 fs.handlingUpdate = false;
375 frame = new JInternalFrame();
376 frame.setContentPane(this);
377 if (Platform.isAMac())
379 Desktop.addInternalFrame(frame,
380 MessageManager.getString("label.sequence_feature_settings"),
385 Desktop.addInternalFrame(frame,
386 MessageManager.getString("label.sequence_feature_settings"),
389 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
391 frame.addInternalFrameListener(
392 new javax.swing.event.InternalFrameAdapter()
395 public void internalFrameClosed(
396 javax.swing.event.InternalFrameEvent evt)
398 fr.removePropertyChangeListener(change);
401 frame.setLayer(JLayeredPane.PALETTE_LAYER);
402 inConstruction = false;
405 protected void popupSort(final int rowSelected, final String type,
406 final Object typeCol, final Map<String, float[][]> minmax, int x,
409 JPopupMenu men = new JPopupMenu(MessageManager
410 .formatMessage("label.settings_for_param", new String[]
412 JMenuItem scr = new JMenuItem(
413 MessageManager.getString("label.sort_by_score"));
415 final FeatureSettings me = this;
416 scr.addActionListener(new ActionListener()
420 public void actionPerformed(ActionEvent e)
423 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
428 JMenuItem dens = new JMenuItem(
429 MessageManager.getString("label.sort_by_density"));
430 dens.addActionListener(new ActionListener()
434 public void actionPerformed(ActionEvent e)
437 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
444 JMenuItem selCols = new JMenuItem(
445 MessageManager.getString("label.select_columns_containing"));
446 selCols.addActionListener(new ActionListener()
449 public void actionPerformed(ActionEvent arg0)
451 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
453 fr.ap.av.sendSelection();
456 JMenuItem clearCols = new JMenuItem(MessageManager
457 .getString("label.select_columns_not_containing"));
458 clearCols.addActionListener(new ActionListener()
461 public void actionPerformed(ActionEvent arg0)
463 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
465 fr.ap.av.sendSelection();
468 JMenuItem hideCols = new JMenuItem(
469 MessageManager.getString("label.hide_columns_containing"));
470 hideCols.addActionListener(new ActionListener()
473 public void actionPerformed(ActionEvent arg0)
475 fr.ap.alignFrame.hideFeatureColumns(type, true);
476 fr.ap.av.sendSelection();
479 JMenuItem hideOtherCols = new JMenuItem(
480 MessageManager.getString("label.hide_columns_not_containing"));
481 hideOtherCols.addActionListener(new ActionListener()
484 public void actionPerformed(ActionEvent arg0)
486 fr.ap.alignFrame.hideFeatureColumns(type, false);
487 fr.ap.av.sendSelection();
493 men.add(hideOtherCols);
494 men.show(table, x, y);
498 synchronized public void discoverAllFeatureData()
500 Set<String> allGroups = new HashSet<>();
501 AlignmentI alignment = af.getViewport().getAlignment();
503 for (int i = 0; i < alignment.getHeight(); i++)
505 SequenceI seq = alignment.getSequenceAt(i);
506 for (String group : seq.getFeatures().getFeatureGroups(true))
508 if (group != null && !allGroups.contains(group))
510 allGroups.add(group);
511 checkGroupState(group);
522 * Synchronise gui group list and check visibility of group
525 * @return true if group is visible
527 private boolean checkGroupState(String group)
529 boolean visible = fr.checkGroupVisibility(group, true);
531 for (int g = 0; g < groupPanel.getComponentCount(); g++)
533 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
535 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
540 final String grp = group;
541 final JCheckBox check = new JCheckBox(group, visible);
542 check.setFont(new Font("Serif", Font.BOLD, 12));
543 check.setToolTipText(group);
544 check.addItemListener(new ItemListener()
547 public void itemStateChanged(ItemEvent evt)
549 fr.setGroupVisibility(check.getText(), check.isSelected());
550 resetTable(new String[] { grp });
554 groupPanel.add(check);
558 synchronized void resetTable(String[] groupChanged)
564 resettingTable = true;
565 typeWidth = new Hashtable<>();
566 // TODO: change avWidth calculation to 'per-sequence' average and use long
569 Set<String> displayableTypes = new HashSet<>();
570 Set<String> foundGroups = new HashSet<>();
573 * determine which feature types may be visible depending on
574 * which groups are selected, and recompute average width data
576 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
579 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
582 * get the sequence's groups for positional features
583 * and keep track of which groups are visible
585 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
586 Set<String> visibleGroups = new HashSet<>();
587 for (String group : groups)
589 if (group == null || checkGroupState(group))
591 visibleGroups.add(group);
594 foundGroups.addAll(groups);
597 * get distinct feature types for visible groups
598 * record distinct visible types, and their count and total length
600 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
601 visibleGroups.toArray(new String[visibleGroups.size()]));
602 for (String type : types)
604 displayableTypes.add(type);
605 float[] avWidth = typeWidth.get(type);
608 avWidth = new float[2];
609 typeWidth.put(type, avWidth);
611 // todo this could include features with a non-visible group
612 // - do we greatly care?
613 // todo should we include non-displayable features here, and only
614 // update when features are added?
615 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
616 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
620 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
623 if (fr.hasRenderOrder())
627 fr.findAllFeatures(groupChanged != null); // prod to update
628 // colourschemes. but don't
630 // First add the checks in the previous render order,
631 // in case the window has been closed and reopened
633 List<String> frl = fr.getRenderOrder();
634 for (int ro = frl.size() - 1; ro > -1; ro--)
636 String type = frl.get(ro);
638 if (!displayableTypes.contains(type))
643 data[dataIndex][TYPE_COLUMN] = type;
644 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
645 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
646 data[dataIndex][FILTER_COLUMN] = featureFilter == null
647 ? new FeatureMatcherSet()
649 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
650 af.getViewport().getFeaturesDisplayed().isVisible(type));
652 displayableTypes.remove(type);
657 * process any extra features belonging only to
658 * a group which was just selected
660 while (!displayableTypes.isEmpty())
662 String type = displayableTypes.iterator().next();
663 data[dataIndex][TYPE_COLUMN] = type;
665 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
666 if (data[dataIndex][COLOUR_COLUMN] == null)
668 // "Colour has been updated in another view!!"
669 fr.clearRenderOrder();
672 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
673 data[dataIndex][FILTER_COLUMN] = featureFilter == null
674 ? new FeatureMatcherSet()
676 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
678 displayableTypes.remove(type);
681 if (originalData == null)
683 originalData = new Object[data.length][COLUMN_COUNT];
684 for (int i = 0; i < data.length; i++)
686 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
691 updateOriginalData(data);
694 table.setModel(new FeatureTableModel(data));
695 table.getColumnModel().getColumn(0).setPreferredWidth(200);
697 groupPanel.setLayout(
698 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
699 pruneGroups(foundGroups);
700 groupPanel.validate();
702 updateFeatureRenderer(data, groupChanged != null);
703 resettingTable = false;
707 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
708 * have been made outwith this dialog
710 * <li>a new feature type added (and made visible)</li>
711 * <li>a feature colour changed (in the Amend Features dialog)</li>
716 protected void updateOriginalData(Object[][] foundData)
718 // todo LinkedHashMap instead of Object[][] would be nice
720 Object[][] currentData = ((FeatureTableModel) table.getModel())
722 for (Object[] row : foundData)
724 String type = (String) row[TYPE_COLUMN];
725 boolean found = false;
726 for (Object[] current : currentData)
728 if (type.equals(current[TYPE_COLUMN]))
732 * currently dependent on object equality here;
733 * really need an equals method on FeatureColour
735 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
738 * feature colour has changed externally - update originalData
740 for (Object[] original : originalData)
742 if (type.equals(original[TYPE_COLUMN]))
744 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
755 * new feature detected - add to original data (on top)
757 Object[][] newData = new Object[originalData.length
759 for (int i = 0; i < originalData.length; i++)
761 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
765 originalData = newData;
771 * Remove from the groups panel any checkboxes for groups that are not in the
772 * foundGroups set. This enables removing a group from the display when the last
773 * feature in that group is deleted.
777 protected void pruneGroups(Set<String> foundGroups)
779 for (int g = 0; g < groupPanel.getComponentCount(); g++)
781 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
782 if (!foundGroups.contains(checkbox.getText()))
784 groupPanel.remove(checkbox);
790 * reorder data based on the featureRenderers global priority list.
794 private void ensureOrder(Object[][] data)
796 boolean sort = false;
797 float[] order = new float[data.length];
798 for (int i = 0; i < order.length; i++)
800 order[i] = fr.getOrder(data[i][0].toString());
803 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
807 sort = sort || order[i - 1] > order[i];
812 jalview.util.QuickSort.sort(order, data);
817 * Offers a file chooser dialog, and then loads the feature colours and
818 * filters from file in XML format and unmarshals to Jalview feature settings
822 JalviewFileChooser chooser = new JalviewFileChooser("fc",
823 SEQUENCE_FEATURE_COLOURS);
824 chooser.setFileView(new JalviewFileView());
825 chooser.setDialogTitle(
826 MessageManager.getString("label.load_feature_colours"));
827 chooser.setToolTipText(MessageManager.getString("action.load"));
829 int value = chooser.showOpenDialog(this);
831 if (value == JalviewFileChooser.APPROVE_OPTION)
833 File file = chooser.getSelectedFile();
839 * Loads feature colours and filters from XML stored in the given file
847 InputStreamReader in = new InputStreamReader(
848 new FileInputStream(file), "UTF-8");
850 JAXBContext jc = JAXBContext
851 .newInstance("jalview.xml.binding.jalview");
852 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
853 XMLStreamReader streamReader = XMLInputFactory.newInstance()
854 .createXMLStreamReader(in);
855 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
856 JalviewUserColours.class);
857 JalviewUserColours jucs = jbe.getValue();
859 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
862 * load feature colours
864 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
866 Colour newcol = jucs.getColour().get(i);
867 FeatureColourI colour = jalview.project.Jalview2XML
868 .parseColour(newcol);
869 fr.setColour(newcol.getName(), colour);
870 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
874 * load feature filters; loaded filters will replace any that are
875 * currently defined, other defined filters are left unchanged
877 for (int i = 0; i < jucs.getFilter().size(); i++)
879 Filter filterModel = jucs.getFilter().get(i);
880 String featureType = filterModel.getFeatureType();
881 FeatureMatcherSetI filter = jalview.project.Jalview2XML
882 .parseFilter(featureType, filterModel.getMatcherSet());
883 if (!filter.isEmpty())
885 fr.setFeatureFilter(featureType, filter);
890 * update feature settings table
895 Object[][] data = ((FeatureTableModel) table.getModel())
898 updateFeatureRenderer(data, false);
901 } catch (Exception ex)
903 System.out.println("Error loading User Colour File\n" + ex);
908 * Offers a file chooser dialog, and then saves the current feature colours
909 * and any filters to the selected file in XML format
913 JalviewFileChooser chooser = new JalviewFileChooser("fc",
914 SEQUENCE_FEATURE_COLOURS);
915 chooser.setFileView(new JalviewFileView());
916 chooser.setDialogTitle(
917 MessageManager.getString("label.save_feature_colours"));
918 chooser.setToolTipText(MessageManager.getString("action.save"));
920 int value = chooser.showSaveDialog(this);
922 if (value == JalviewFileChooser.APPROVE_OPTION)
924 save(chooser.getSelectedFile());
929 * Saves feature colours and filters to the given file
935 JalviewUserColours ucs = new JalviewUserColours();
936 ucs.setSchemeName("Sequence Features");
939 PrintWriter out = new PrintWriter(new OutputStreamWriter(
940 new FileOutputStream(file), "UTF-8"));
943 * sort feature types by colour order, from 0 (highest)
946 Set<String> fr_colours = fr.getAllFeatureColours();
947 String[] sortedTypes = fr_colours
948 .toArray(new String[fr_colours.size()]);
949 Arrays.sort(sortedTypes, new Comparator<String>()
952 public int compare(String type1, String type2)
954 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
959 * save feature colours
961 for (String featureType : sortedTypes)
963 FeatureColourI fcol = fr.getFeatureStyle(featureType);
964 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
966 ucs.getColour().add(col);
970 * save any feature filters
972 for (String featureType : sortedTypes)
974 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
975 if (filter != null && !filter.isEmpty())
977 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
978 FeatureMatcherI firstMatcher = iterator.next();
979 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
980 .marshalFilter(firstMatcher, iterator,
982 Filter filterModel = new Filter();
983 filterModel.setFeatureType(featureType);
984 filterModel.setMatcherSet(ms);
985 ucs.getFilter().add(filterModel);
988 JAXBContext jaxbContext = JAXBContext
989 .newInstance(JalviewUserColours.class);
990 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
991 jaxbMarshaller.marshal(
992 new ObjectFactory().createJalviewUserColours(ucs), out);
994 // jaxbMarshaller.marshal(object, pout);
995 // marshaller.marshal(object);
1000 } catch (Exception ex)
1002 ex.printStackTrace();
1006 public void invertSelection()
1008 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1009 for (int i = 0; i < data.length; i++)
1011 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1013 updateFeatureRenderer(data, true);
1017 public void orderByAvWidth()
1019 if (table == null || table.getModel() == null)
1023 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1024 float[] width = new float[data.length];
1028 for (int i = 0; i < data.length; i++)
1030 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1033 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1034 // weight - but have to make per
1035 // sequence, too (awidth[2])
1036 // if (width[i]==1) // hack to distinguish single width sequences.
1047 boolean sort = false;
1048 for (int i = 0; i < width.length; i++)
1050 // awidth = (float[]) typeWidth.get(data[i][0]);
1053 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1056 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1062 width[i] /= max; // normalize
1063 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1067 sort = sort || width[i - 1] > width[i];
1072 jalview.util.QuickSort.sort(width, data);
1073 // update global priority order
1076 updateFeatureRenderer(data, false);
1084 frame.setClosed(true);
1085 } catch (Exception exe)
1091 public void updateFeatureRenderer(Object[][] data)
1093 updateFeatureRenderer(data, true);
1097 * Update the priority order of features; only repaint if this changed the order
1098 * of visible features
1103 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1105 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1107 if (fr.setFeaturePriority(rowData, visibleNew))
1114 * Converts table data into an array of data beans
1116 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1118 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1119 for (int i = 0; i < data.length; i++)
1121 String type = (String) data[i][TYPE_COLUMN];
1122 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1123 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1124 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1125 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1131 private void jbInit() throws Exception
1133 this.setLayout(new BorderLayout());
1135 JPanel settingsPane = new JPanel();
1136 settingsPane.setLayout(new BorderLayout());
1138 JPanel bigPanel = new JPanel();
1139 bigPanel.setLayout(new BorderLayout());
1141 groupPanel = new JPanel();
1142 bigPanel.add(groupPanel, BorderLayout.NORTH);
1144 JButton invert = new JButton(
1145 MessageManager.getString("label.invert_selection"));
1146 invert.setFont(JvSwingUtils.getLabelFont());
1147 invert.addActionListener(new ActionListener()
1150 public void actionPerformed(ActionEvent e)
1156 JButton optimizeOrder = new JButton(
1157 MessageManager.getString("label.optimise_order"));
1158 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1159 optimizeOrder.addActionListener(new ActionListener()
1162 public void actionPerformed(ActionEvent e)
1168 JButton sortByScore = new JButton(
1169 MessageManager.getString("label.seq_sort_by_score"));
1170 sortByScore.setFont(JvSwingUtils.getLabelFont());
1171 sortByScore.addActionListener(new ActionListener()
1174 public void actionPerformed(ActionEvent e)
1176 af.avc.sortAlignmentByFeatureScore(null);
1179 JButton sortByDens = new JButton(
1180 MessageManager.getString("label.sequence_sort_by_density"));
1181 sortByDens.setFont(JvSwingUtils.getLabelFont());
1182 sortByDens.addActionListener(new ActionListener()
1185 public void actionPerformed(ActionEvent e)
1187 af.avc.sortAlignmentByFeatureDensity(null);
1191 JButton help = new JButton(MessageManager.getString("action.help"));
1192 help.setFont(JvSwingUtils.getLabelFont());
1193 help.addActionListener(new ActionListener()
1196 public void actionPerformed(ActionEvent e)
1200 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1201 } catch (HelpSetException e1)
1203 e1.printStackTrace();
1208 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1209 cancel.setFont(JvSwingUtils.getLabelFont());
1210 cancel.addActionListener(new ActionListener()
1213 public void actionPerformed(ActionEvent e)
1215 fr.setTransparency(originalTransparency);
1216 fr.setFeatureFilters(originalFilters);
1217 updateFeatureRenderer(originalData);
1218 af.getViewport().setViewStyle(originalViewStyle);
1223 JButton ok = new JButton(MessageManager.getString("action.ok"));
1224 ok.setFont(JvSwingUtils.getLabelFont());
1225 ok.addActionListener(new ActionListener()
1228 public void actionPerformed(ActionEvent e)
1234 JButton loadColours = new JButton(
1235 MessageManager.getString("label.load_colours"));
1236 loadColours.setFont(JvSwingUtils.getLabelFont());
1237 loadColours.setToolTipText(
1238 MessageManager.getString("label.load_colours_tooltip"));
1239 loadColours.addActionListener(new ActionListener()
1242 public void actionPerformed(ActionEvent e)
1248 JButton saveColours = new JButton(
1249 MessageManager.getString("label.save_colours"));
1250 saveColours.setFont(JvSwingUtils.getLabelFont());
1251 saveColours.setToolTipText(
1252 MessageManager.getString("label.save_colours_tooltip"));
1253 saveColours.addActionListener(new ActionListener()
1256 public void actionPerformed(ActionEvent e)
1261 transparency.addChangeListener(new ChangeListener()
1264 public void stateChanged(ChangeEvent evt)
1266 if (!inConstruction)
1268 fr.setTransparency((100 - transparency.getValue()) / 100f);
1274 transparency.setMaximum(70);
1275 transparency.setToolTipText(
1276 MessageManager.getString("label.transparency_tip"));
1278 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1279 JCheckBox showComplement = new JCheckBox(
1280 "Show " + (nucleotide ? "protein" : "CDS") + " features");
1281 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
1282 showComplement.addActionListener(new ActionListener()
1285 public void actionPerformed(ActionEvent e)
1288 .setShowComplementFeatures(showComplement.isSelected());
1293 JCheckBox showComplementOnTop = new JCheckBox("on top");
1295 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
1296 showComplementOnTop.addActionListener(new ActionListener()
1299 public void actionPerformed(ActionEvent e)
1301 af.getViewport().setShowComplementFeaturesOnTop(
1302 showComplementOnTop.isSelected());
1307 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1308 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1310 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1311 transbuttons.add(optimizeOrder);
1312 transbuttons.add(invert);
1313 transbuttons.add(sortByScore);
1314 transbuttons.add(sortByDens);
1315 transbuttons.add(help);
1317 boolean hasComplement = af.getViewport().getCodingComplement() != null;
1318 JPanel transPanelLeft = new JPanel(
1319 new GridLayout(hasComplement ? 3 : 2, 1));
1320 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1321 transPanelLeft.add(transparency);
1324 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1325 cp.add(showComplement);
1326 cp.add(showComplementOnTop);
1327 transPanelLeft.add(cp);
1329 lowerPanel.add(transPanelLeft);
1330 lowerPanel.add(transbuttons);
1332 JPanel buttonPanel = new JPanel();
1333 buttonPanel.add(ok);
1334 buttonPanel.add(cancel);
1335 buttonPanel.add(loadColours);
1336 buttonPanel.add(saveColours);
1337 bigPanel.add(scrollPane, BorderLayout.CENTER);
1338 settingsPane.add(bigPanel, BorderLayout.CENTER);
1339 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1340 this.add(settingsPane);
1344 * Repaints alignment, structure and overview (if shown). If there is a
1345 * complementary view which is showing this view's features, then also
1348 void refreshDisplay()
1350 af.alignPanel.paintAlignment(true, true);
1351 AlignViewportI complement = af.getViewport().getCodingComplement();
1352 if (complement != null && complement.isShowComplementFeatures())
1354 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1355 af2.alignPanel.paintAlignment(true, true);
1360 * Answers a suitable tooltip to show on the colour cell of the table
1364 * if true include 'click to edit' and similar text
1367 public static String getColorTooltip(FeatureColourI fcol,
1374 if (fcol.isSimpleColour())
1376 return withHint ? BASE_TOOLTIP : null;
1378 String description = fcol.getDescription();
1379 description = description.replaceAll("<", "<");
1380 description = description.replaceAll(">", ">");
1381 StringBuilder tt = new StringBuilder(description);
1384 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1386 return JvSwingUtils.wrapTooltip(true, tt.toString());
1389 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1392 boolean thr = false;
1393 StringBuilder tx = new StringBuilder();
1395 if (gcol.isColourByAttribute())
1397 tx.append(FeatureMatcher
1398 .toAttributeDisplayName(gcol.getAttributeName()));
1400 else if (!gcol.isColourByLabel())
1402 tx.append(MessageManager.getString("label.score"));
1405 if (gcol.isAboveThreshold())
1410 if (gcol.isBelowThreshold())
1415 if (gcol.isColourByLabel())
1421 if (!gcol.isColourByAttribute())
1429 Color newColor = gcol.getMaxColour();
1430 comp.setBackground(newColor);
1431 // System.err.println("Width is " + w / 2);
1432 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1433 comp.setIcon(ficon);
1434 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1435 // + newColor.getGreen() + ", " + newColor.getBlue()
1436 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1437 // + ", " + minCol.getBlue() + ")");
1439 comp.setHorizontalAlignment(SwingConstants.CENTER);
1440 comp.setText(tx.toString());
1443 // ///////////////////////////////////////////////////////////////////////
1444 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1445 // ///////////////////////////////////////////////////////////////////////
1446 class FeatureTableModel extends AbstractTableModel
1448 private String[] columnNames = {
1449 MessageManager.getString("label.feature_type"),
1450 MessageManager.getString("action.colour"),
1451 MessageManager.getString("label.configuration"),
1452 MessageManager.getString("label.show") };
1454 private Object[][] data;
1456 FeatureTableModel(Object[][] data)
1461 public Object[][] getData()
1466 public void setData(Object[][] data)
1472 public int getColumnCount()
1474 return columnNames.length;
1477 public Object[] getRow(int row)
1483 public int getRowCount()
1489 public String getColumnName(int col)
1491 return columnNames[col];
1495 public Object getValueAt(int row, int col)
1497 return data[row][col];
1501 * Answers the class of column c of the table
1504 public Class<?> getColumnClass(int c)
1509 return String.class;
1511 return FeatureColour.class;
1513 return FeatureMatcherSet.class;
1515 return Boolean.class;
1520 public boolean isCellEditable(int row, int col)
1522 return col == 0 ? false : true;
1526 public void setValueAt(Object value, int row, int col)
1528 data[row][col] = value;
1529 fireTableCellUpdated(row, col);
1530 updateFeatureRenderer(data);
1535 class ColorRenderer extends JLabel implements TableCellRenderer
1537 Border unselectedBorder = null;
1539 Border selectedBorder = null;
1541 public ColorRenderer()
1543 setOpaque(true); // MUST do this for background to show up.
1544 setHorizontalTextPosition(SwingConstants.CENTER);
1545 setVerticalTextPosition(SwingConstants.CENTER);
1549 public Component getTableCellRendererComponent(JTable tbl, Object color,
1550 boolean isSelected, boolean hasFocus, int row, int column)
1552 FeatureColourI cellColour = (FeatureColourI) color;
1554 setBackground(tbl.getBackground());
1555 if (!cellColour.isSimpleColour())
1557 Rectangle cr = tbl.getCellRect(row, column, false);
1558 FeatureSettings.renderGraduatedColor(this, cellColour,
1559 (int) cr.getWidth(), (int) cr.getHeight());
1565 setBackground(cellColour.getColour());
1569 if (selectedBorder == null)
1571 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1572 tbl.getSelectionBackground());
1574 setBorder(selectedBorder);
1578 if (unselectedBorder == null)
1580 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1581 tbl.getBackground());
1583 setBorder(unselectedBorder);
1590 class FilterRenderer extends JLabel implements TableCellRenderer
1592 javax.swing.border.Border unselectedBorder = null;
1594 javax.swing.border.Border selectedBorder = null;
1596 public FilterRenderer()
1598 setOpaque(true); // MUST do this for background to show up.
1599 setHorizontalTextPosition(SwingConstants.CENTER);
1600 setVerticalTextPosition(SwingConstants.CENTER);
1604 public Component getTableCellRendererComponent(JTable tbl,
1605 Object filter, boolean isSelected, boolean hasFocus, int row,
1608 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1610 String asText = theFilter.toString();
1611 setBackground(tbl.getBackground());
1612 this.setText(asText);
1617 if (selectedBorder == null)
1619 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1620 tbl.getSelectionBackground());
1622 setBorder(selectedBorder);
1626 if (unselectedBorder == null)
1628 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1629 tbl.getBackground());
1631 setBorder(unselectedBorder);
1639 * update comp using rendering settings from gcol
1644 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1646 int w = comp.getWidth(), h = comp.getHeight();
1649 w = (int) comp.getPreferredSize().getWidth();
1650 h = (int) comp.getPreferredSize().getHeight();
1657 renderGraduatedColor(comp, gcol, w, h);
1660 class ColorEditor extends AbstractCellEditor
1661 implements TableCellEditor, ActionListener
1665 FeatureColourI currentColor;
1667 FeatureTypeSettings chooser;
1673 JColorChooser colorChooser;
1677 protected static final String EDIT = "edit";
1679 int rowSelected = 0;
1681 public ColorEditor(FeatureSettings me)
1684 // Set up the editor (from the table's point of view),
1685 // which is a button.
1686 // This button brings up the color chooser dialog,
1687 // which is the editor from the user's point of view.
1688 button = new JButton();
1689 button.setActionCommand(EDIT);
1690 button.addActionListener(this);
1691 button.setBorderPainted(false);
1692 // Set up the dialog that the button brings up.
1693 colorChooser = new JColorChooser();
1694 dialog = JColorChooser.createDialog(button,
1695 MessageManager.getString("label.select_colour"), true, // modal
1696 colorChooser, this, // OK button handler
1697 null); // no CANCEL button handler
1701 * Handles events from the editor button and from the dialog's OK button.
1704 public void actionPerformed(ActionEvent e)
1706 // todo test e.getSource() instead here
1707 if (EDIT.equals(e.getActionCommand()))
1709 // The user has clicked the cell, so
1710 // bring up the dialog.
1711 if (currentColor.isSimpleColour())
1713 // bring up simple color chooser
1714 button.setBackground(currentColor.getColour());
1715 colorChooser.setColor(currentColor.getColour());
1716 dialog.setVisible(true);
1720 // bring up graduated chooser.
1721 chooser = new FeatureTypeSettings(me.fr, type);
1726 chooser.setRequestFocusEnabled(true);
1727 chooser.requestFocus();
1729 chooser.addActionListener(this);
1730 // Make the renderer reappear.
1731 fireEditingStopped();
1736 if (currentColor.isSimpleColour())
1739 * read off colour picked in colour chooser after OK pressed
1741 currentColor = new FeatureColour(colorChooser.getColor());
1742 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1747 * after OK in variable colour dialog, any changes to colour
1748 * (or filters!) are already set in FeatureRenderer, so just
1749 * update table data without triggering updateFeatureRenderer
1751 currentColor = fr.getFeatureColours().get(type);
1752 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1753 if (currentFilter == null)
1755 currentFilter = new FeatureMatcherSet();
1757 Object[] data = ((FeatureTableModel) table.getModel())
1758 .getData()[rowSelected];
1759 data[COLOUR_COLUMN] = currentColor;
1760 data[FILTER_COLUMN] = currentFilter;
1762 fireEditingStopped();
1763 me.table.validate();
1767 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1769 public Object getCellEditorValue()
1771 return currentColor;
1774 // Implement the one method defined by TableCellEditor.
1776 public Component getTableCellEditorComponent(JTable theTable, Object value,
1777 boolean isSelected, int row, int column)
1779 currentColor = (FeatureColourI) value;
1780 this.rowSelected = row;
1781 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1782 button.setOpaque(true);
1783 button.setBackground(me.getBackground());
1784 if (!currentColor.isSimpleColour())
1786 JLabel btn = new JLabel();
1787 btn.setSize(button.getSize());
1788 FeatureSettings.renderGraduatedColor(btn, currentColor);
1789 button.setBackground(btn.getBackground());
1790 button.setIcon(btn.getIcon());
1791 button.setText(btn.getText());
1796 button.setIcon(null);
1797 button.setBackground(currentColor.getColour());
1804 * The cell editor for the Filter column. It displays the text of any filters
1805 * for the feature type in that row (in full as a tooltip, possible abbreviated
1806 * as display text). On click in the cell, opens the Feature Display Settings
1807 * dialog at the Filters tab.
1809 class FilterEditor extends AbstractCellEditor
1810 implements TableCellEditor, ActionListener
1814 FeatureMatcherSetI currentFilter;
1822 protected static final String EDIT = "edit";
1824 int rowSelected = 0;
1826 public FilterEditor(FeatureSettings me)
1829 button = new JButton();
1830 button.setActionCommand(EDIT);
1831 button.addActionListener(this);
1832 button.setBorderPainted(false);
1836 * Handles events from the editor button
1839 public void actionPerformed(ActionEvent e)
1841 if (button == e.getSource())
1843 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1844 chooser.addActionListener(this);
1845 chooser.setRequestFocusEnabled(true);
1846 chooser.requestFocus();
1847 if (lastLocation != null)
1849 // todo open at its last position on screen
1850 chooser.setBounds(lastLocation.x, lastLocation.y,
1851 chooser.getWidth(), chooser.getHeight());
1854 fireEditingStopped();
1856 else if (e.getSource() instanceof Component)
1860 * after OK in variable colour dialog, any changes to filter
1861 * (or colours!) are already set in FeatureRenderer, so just
1862 * update table data without triggering updateFeatureRenderer
1864 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1865 currentFilter = me.fr.getFeatureFilter(type);
1866 if (currentFilter == null)
1868 currentFilter = new FeatureMatcherSet();
1870 Object[] data = ((FeatureTableModel) table.getModel())
1871 .getData()[rowSelected];
1872 data[COLOUR_COLUMN] = currentColor;
1873 data[FILTER_COLUMN] = currentFilter;
1874 fireEditingStopped();
1875 me.table.validate();
1880 public Object getCellEditorValue()
1882 return currentFilter;
1886 public Component getTableCellEditorComponent(JTable theTable, Object value,
1887 boolean isSelected, int row, int column)
1889 currentFilter = (FeatureMatcherSetI) value;
1890 this.rowSelected = row;
1891 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1892 button.setOpaque(true);
1893 button.setBackground(me.getBackground());
1894 button.setText(currentFilter.toString());
1895 button.setIcon(null);
1901 class FeatureIcon implements Icon
1903 FeatureColourI gcol;
1907 boolean midspace = false;
1909 int width = 50, height = 20;
1911 int s1, e1; // start and end of midpoint band for thresholded symbol
1913 Color mpcolour = Color.white;
1915 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1935 public int getIconWidth()
1941 public int getIconHeight()
1947 public void paintIcon(Component c, Graphics g, int x, int y)
1950 if (gcol.isColourByLabel())
1953 g.fillRect(0, 0, width, height);
1954 // need an icon here.
1955 g.setColor(gcol.getMaxColour());
1957 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1959 // g.setFont(g.getFont().deriveFont(
1960 // AffineTransform.getScaleInstance(
1961 // width/g.getFontMetrics().stringWidth("Label"),
1962 // height/g.getFontMetrics().getHeight())));
1964 g.drawString(MessageManager.getString("label.label"), 0, 0);
1969 Color minCol = gcol.getMinColour();
1971 g.fillRect(0, 0, s1, height);
1974 g.setColor(Color.white);
1975 g.fillRect(s1, 0, e1 - s1, height);
1977 g.setColor(gcol.getMaxColour());
1978 g.fillRect(0, e1, width - e1, height);