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();
164 JCheckBox showComplement;
166 JCheckBox showComplementOnTop;
169 * when true, constructor is still executing - so ignore UI events
171 protected volatile boolean inConstruction = true;
173 int selectedRow = -1;
175 boolean resettingTable = false;
178 * true when Feature Settings are updating from feature renderer
180 private boolean handlingUpdate = false;
183 * holds {featureCount, totalExtent} for each feature type
185 Map<String, float[]> typeWidth = null;
192 public FeatureSettings(AlignFrame alignFrame)
194 this.af = alignFrame;
195 fr = af.getFeatureRenderer();
197 // save transparency for restore on Cancel
198 originalTransparency = fr.getTransparency();
199 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
200 transparency.setMaximum(100 - originalTransparencyAsPercent);
202 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
203 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
208 } catch (Exception ex)
210 ex.printStackTrace();
216 public String getToolTipText(MouseEvent e)
219 int column = table.columnAtPoint(e.getPoint());
220 int row = table.rowAtPoint(e.getPoint());
225 tip = JvSwingUtils.wrapTooltip(true, MessageManager
226 .getString("label.feature_settings_click_drag"));
229 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
231 tip = getColorTooltip(colour, true);
234 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
238 .getString("label.configure_feature_tooltip")
249 * Position the tooltip near the bottom edge of, and half way across, the
253 public Point getToolTipLocation(MouseEvent e)
255 Point point = e.getPoint();
256 int column = table.columnAtPoint(point);
257 int row = table.rowAtPoint(point);
258 Rectangle r = getCellRect(row, column, false);
259 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
263 JTableHeader tableHeader = table.getTableHeader();
264 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
265 tableHeader.setReorderingAllowed(false);
266 table.setFont(new Font("Verdana", Font.PLAIN, 12));
268 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
269 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
271 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
272 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
274 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
275 new ColorRenderer(), new ColorEditor(this));
276 table.addColumn(colourColumn);
278 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
279 new FilterRenderer(), new FilterEditor(this));
280 table.addColumn(filterColumn);
282 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
284 table.addMouseListener(new MouseAdapter()
287 public void mousePressed(MouseEvent evt)
289 selectedRow = table.rowAtPoint(evt.getPoint());
290 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
291 if (evt.isPopupTrigger())
293 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
294 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
297 else if (evt.getClickCount() == 2)
299 boolean invertSelection = evt.isAltDown();
300 boolean toggleSelection = Platform.isControlDown(evt);
301 boolean extendSelection = evt.isShiftDown();
302 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
303 invertSelection, extendSelection, toggleSelection, type);
307 // isPopupTrigger fires on mouseReleased on Windows
309 public void mouseReleased(MouseEvent evt)
311 selectedRow = table.rowAtPoint(evt.getPoint());
312 if (evt.isPopupTrigger())
314 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
315 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
316 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
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;
408 protected void popupSort(final int rowSelected, final String type,
409 final Object typeCol, final Map<String, float[][]> minmax, int x,
412 JPopupMenu men = new JPopupMenu(MessageManager
413 .formatMessage("label.settings_for_param", new String[]
415 JMenuItem scr = new JMenuItem(
416 MessageManager.getString("label.sort_by_score"));
418 final FeatureSettings me = this;
419 scr.addActionListener(new ActionListener()
423 public void actionPerformed(ActionEvent e)
426 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
431 JMenuItem dens = new JMenuItem(
432 MessageManager.getString("label.sort_by_density"));
433 dens.addActionListener(new ActionListener()
437 public void actionPerformed(ActionEvent e)
440 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
447 JMenuItem selCols = new JMenuItem(
448 MessageManager.getString("label.select_columns_containing"));
449 selCols.addActionListener(new ActionListener()
452 public void actionPerformed(ActionEvent arg0)
454 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
458 JMenuItem clearCols = new JMenuItem(MessageManager
459 .getString("label.select_columns_not_containing"));
460 clearCols.addActionListener(new ActionListener()
463 public void actionPerformed(ActionEvent arg0)
465 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
469 JMenuItem hideCols = new JMenuItem(
470 MessageManager.getString("label.hide_columns_containing"));
471 hideCols.addActionListener(new ActionListener()
474 public void actionPerformed(ActionEvent arg0)
476 fr.ap.alignFrame.hideFeatureColumns(type, true);
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);
492 men.add(hideOtherCols);
493 men.show(table, x, y);
497 synchronized public void discoverAllFeatureData()
499 Set<String> allGroups = new HashSet<>();
500 AlignmentI alignment = af.getViewport().getAlignment();
502 for (int i = 0; i < alignment.getHeight(); i++)
504 SequenceI seq = alignment.getSequenceAt(i);
505 for (String group : seq.getFeatures().getFeatureGroups(true))
507 if (group != null && !allGroups.contains(group))
509 allGroups.add(group);
510 checkGroupState(group);
521 * Synchronise gui group list and check visibility of group
524 * @return true if group is visible
526 private boolean checkGroupState(String group)
528 boolean visible = fr.checkGroupVisibility(group, true);
530 for (int g = 0; g < groupPanel.getComponentCount(); g++)
532 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
534 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
539 final String grp = group;
540 final JCheckBox check = new JCheckBox(group, visible);
541 check.setFont(new Font("Serif", Font.BOLD, 12));
542 check.setToolTipText(group);
543 check.addItemListener(new ItemListener()
546 public void itemStateChanged(ItemEvent evt)
548 fr.setGroupVisibility(check.getText(), check.isSelected());
549 resetTable(new String[] { grp });
553 groupPanel.add(check);
557 synchronized void resetTable(String[] groupChanged)
563 resettingTable = true;
564 typeWidth = new Hashtable<>();
565 // TODO: change avWidth calculation to 'per-sequence' average and use long
568 Set<String> displayableTypes = new HashSet<>();
569 Set<String> foundGroups = new HashSet<>();
572 * determine which feature types may be visible depending on
573 * which groups are selected, and recompute average width data
575 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
578 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
581 * get the sequence's groups for positional features
582 * and keep track of which groups are visible
584 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
585 Set<String> visibleGroups = new HashSet<>();
586 for (String group : groups)
588 if (group == null || checkGroupState(group))
590 visibleGroups.add(group);
593 foundGroups.addAll(groups);
596 * get distinct feature types for visible groups
597 * record distinct visible types, and their count and total length
599 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
600 visibleGroups.toArray(new String[visibleGroups.size()]));
601 for (String type : types)
603 displayableTypes.add(type);
604 float[] avWidth = typeWidth.get(type);
607 avWidth = new float[2];
608 typeWidth.put(type, avWidth);
610 // todo this could include features with a non-visible group
611 // - do we greatly care?
612 // todo should we include non-displayable features here, and only
613 // update when features are added?
614 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
615 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
619 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
622 if (fr.hasRenderOrder())
626 fr.findAllFeatures(groupChanged != null); // prod to update
627 // colourschemes. but don't
629 // First add the checks in the previous render order,
630 // in case the window has been closed and reopened
632 List<String> frl = fr.getRenderOrder();
633 for (int ro = frl.size() - 1; ro > -1; ro--)
635 String type = frl.get(ro);
637 if (!displayableTypes.contains(type))
642 data[dataIndex][TYPE_COLUMN] = type;
643 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
644 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
645 data[dataIndex][FILTER_COLUMN] = featureFilter == null
646 ? new FeatureMatcherSet()
648 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
649 af.getViewport().getFeaturesDisplayed().isVisible(type));
651 displayableTypes.remove(type);
656 * process any extra features belonging only to
657 * a group which was just selected
659 while (!displayableTypes.isEmpty())
661 String type = displayableTypes.iterator().next();
662 data[dataIndex][TYPE_COLUMN] = type;
664 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
665 if (data[dataIndex][COLOUR_COLUMN] == null)
667 // "Colour has been updated in another view!!"
668 fr.clearRenderOrder();
671 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
672 data[dataIndex][FILTER_COLUMN] = featureFilter == null
673 ? new FeatureMatcherSet()
675 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
677 displayableTypes.remove(type);
680 if (originalData == null)
682 originalData = new Object[data.length][COLUMN_COUNT];
683 for (int i = 0; i < data.length; i++)
685 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
690 updateOriginalData(data);
693 table.setModel(new FeatureTableModel(data));
694 table.getColumnModel().getColumn(0).setPreferredWidth(200);
696 groupPanel.setLayout(
697 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
698 pruneGroups(foundGroups);
699 groupPanel.validate();
701 updateFeatureRenderer(data, groupChanged != null);
702 resettingTable = false;
706 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
707 * have been made outwith this dialog
709 * <li>a new feature type added (and made visible)</li>
710 * <li>a feature colour changed (in the Amend Features dialog)</li>
715 protected void updateOriginalData(Object[][] foundData)
717 // todo LinkedHashMap instead of Object[][] would be nice
719 Object[][] currentData = ((FeatureTableModel) table.getModel())
721 for (Object[] row : foundData)
723 String type = (String) row[TYPE_COLUMN];
724 boolean found = false;
725 for (Object[] current : currentData)
727 if (type.equals(current[TYPE_COLUMN]))
731 * currently dependent on object equality here;
732 * really need an equals method on FeatureColour
734 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
737 * feature colour has changed externally - update originalData
739 for (Object[] original : originalData)
741 if (type.equals(original[TYPE_COLUMN]))
743 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
754 * new feature detected - add to original data (on top)
756 Object[][] newData = new Object[originalData.length
758 for (int i = 0; i < originalData.length; i++)
760 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
764 originalData = newData;
770 * Remove from the groups panel any checkboxes for groups that are not in the
771 * foundGroups set. This enables removing a group from the display when the last
772 * feature in that group is deleted.
776 protected void pruneGroups(Set<String> foundGroups)
778 for (int g = 0; g < groupPanel.getComponentCount(); g++)
780 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
781 if (!foundGroups.contains(checkbox.getText()))
783 groupPanel.remove(checkbox);
789 * reorder data based on the featureRenderers global priority list.
793 private void ensureOrder(Object[][] data)
795 boolean sort = false;
796 float[] order = new float[data.length];
797 for (int i = 0; i < order.length; i++)
799 order[i] = fr.getOrder(data[i][0].toString());
802 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
806 sort = sort || order[i - 1] > order[i];
811 jalview.util.QuickSort.sort(order, data);
816 * Offers a file chooser dialog, and then loads the feature colours and
817 * filters from file in XML format and unmarshals to Jalview feature settings
821 JalviewFileChooser chooser = new JalviewFileChooser("fc",
822 SEQUENCE_FEATURE_COLOURS);
823 chooser.setFileView(new JalviewFileView());
824 chooser.setDialogTitle(
825 MessageManager.getString("label.load_feature_colours"));
826 chooser.setToolTipText(MessageManager.getString("action.load"));
828 int value = chooser.showOpenDialog(this);
830 if (value == JalviewFileChooser.APPROVE_OPTION)
832 File file = chooser.getSelectedFile();
838 * Loads feature colours and filters from XML stored in the given file
846 InputStreamReader in = new InputStreamReader(
847 new FileInputStream(file), "UTF-8");
849 JAXBContext jc = JAXBContext
850 .newInstance("jalview.xml.binding.jalview");
851 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
852 XMLStreamReader streamReader = XMLInputFactory.newInstance()
853 .createXMLStreamReader(in);
854 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
855 JalviewUserColours.class);
856 JalviewUserColours jucs = jbe.getValue();
858 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
861 * load feature colours
863 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
865 Colour newcol = jucs.getColour().get(i);
866 FeatureColourI colour = jalview.project.Jalview2XML
867 .parseColour(newcol);
868 fr.setColour(newcol.getName(), colour);
869 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
873 * load feature filters; loaded filters will replace any that are
874 * currently defined, other defined filters are left unchanged
876 for (int i = 0; i < jucs.getFilter().size(); i++)
878 Filter filterModel = jucs.getFilter().get(i);
879 String featureType = filterModel.getFeatureType();
880 FeatureMatcherSetI filter = jalview.project.Jalview2XML
881 .parseFilter(featureType, filterModel.getMatcherSet());
882 if (!filter.isEmpty())
884 fr.setFeatureFilter(featureType, filter);
889 * update feature settings table
894 Object[][] data = ((FeatureTableModel) table.getModel())
897 updateFeatureRenderer(data, false);
900 } catch (Exception ex)
902 System.out.println("Error loading User Colour File\n" + ex);
907 * Offers a file chooser dialog, and then saves the current feature colours
908 * and any filters to the selected file in XML format
912 JalviewFileChooser chooser = new JalviewFileChooser("fc",
913 SEQUENCE_FEATURE_COLOURS);
914 chooser.setFileView(new JalviewFileView());
915 chooser.setDialogTitle(
916 MessageManager.getString("label.save_feature_colours"));
917 chooser.setToolTipText(MessageManager.getString("action.save"));
919 int value = chooser.showSaveDialog(this);
921 if (value == JalviewFileChooser.APPROVE_OPTION)
923 save(chooser.getSelectedFile());
928 * Saves feature colours and filters to the given file
934 JalviewUserColours ucs = new JalviewUserColours();
935 ucs.setSchemeName("Sequence Features");
938 PrintWriter out = new PrintWriter(new OutputStreamWriter(
939 new FileOutputStream(file), "UTF-8"));
942 * sort feature types by colour order, from 0 (highest)
945 Set<String> fr_colours = fr.getAllFeatureColours();
946 String[] sortedTypes = fr_colours
947 .toArray(new String[fr_colours.size()]);
948 Arrays.sort(sortedTypes, new Comparator<String>()
951 public int compare(String type1, String type2)
953 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
958 * save feature colours
960 for (String featureType : sortedTypes)
962 FeatureColourI fcol = fr.getFeatureStyle(featureType);
963 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
965 ucs.getColour().add(col);
969 * save any feature filters
971 for (String featureType : sortedTypes)
973 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
974 if (filter != null && !filter.isEmpty())
976 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
977 FeatureMatcherI firstMatcher = iterator.next();
978 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
979 .marshalFilter(firstMatcher, iterator,
981 Filter filterModel = new Filter();
982 filterModel.setFeatureType(featureType);
983 filterModel.setMatcherSet(ms);
984 ucs.getFilter().add(filterModel);
987 JAXBContext jaxbContext = JAXBContext
988 .newInstance(JalviewUserColours.class);
989 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
990 jaxbMarshaller.marshal(
991 new ObjectFactory().createJalviewUserColours(ucs), out);
993 // jaxbMarshaller.marshal(object, pout);
994 // marshaller.marshal(object);
999 } catch (Exception ex)
1001 ex.printStackTrace();
1005 public void invertSelection()
1007 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1008 for (int i = 0; i < data.length; i++)
1010 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1012 updateFeatureRenderer(data, true);
1016 public void orderByAvWidth()
1018 if (table == null || table.getModel() == null)
1022 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1023 float[] width = new float[data.length];
1027 for (int i = 0; i < data.length; i++)
1029 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1032 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1033 // weight - but have to make per
1034 // sequence, too (awidth[2])
1035 // if (width[i]==1) // hack to distinguish single width sequences.
1046 boolean sort = false;
1047 for (int i = 0; i < width.length; i++)
1049 // awidth = (float[]) typeWidth.get(data[i][0]);
1052 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1055 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1061 width[i] /= max; // normalize
1062 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1066 sort = sort || width[i - 1] > width[i];
1071 jalview.util.QuickSort.sort(width, data);
1072 // update global priority order
1075 updateFeatureRenderer(data, false);
1083 frame.setClosed(true);
1084 } catch (Exception exe)
1090 public void updateFeatureRenderer(Object[][] data)
1092 updateFeatureRenderer(data, true);
1096 * Update the priority order of features; only repaint if this changed the order
1097 * of visible features
1102 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1104 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1106 if (fr.setFeaturePriority(rowData, visibleNew))
1113 * Converts table data into an array of data beans
1115 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1117 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1118 for (int i = 0; i < data.length; i++)
1120 String type = (String) data[i][TYPE_COLUMN];
1121 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1122 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1123 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1124 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1130 private void jbInit() throws Exception
1132 this.setLayout(new BorderLayout());
1134 JPanel settingsPane = new JPanel();
1135 settingsPane.setLayout(new BorderLayout());
1137 JPanel bigPanel = new JPanel();
1138 bigPanel.setLayout(new BorderLayout());
1140 groupPanel = new JPanel();
1141 bigPanel.add(groupPanel, BorderLayout.NORTH);
1143 JButton invert = new JButton(
1144 MessageManager.getString("label.invert_selection"));
1145 invert.setFont(JvSwingUtils.getLabelFont());
1146 invert.addActionListener(new ActionListener()
1149 public void actionPerformed(ActionEvent e)
1155 JButton optimizeOrder = new JButton(
1156 MessageManager.getString("label.optimise_order"));
1157 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1158 optimizeOrder.addActionListener(new ActionListener()
1161 public void actionPerformed(ActionEvent e)
1167 JButton sortByScore = new JButton(
1168 MessageManager.getString("label.seq_sort_by_score"));
1169 sortByScore.setFont(JvSwingUtils.getLabelFont());
1170 sortByScore.addActionListener(new ActionListener()
1173 public void actionPerformed(ActionEvent e)
1175 af.avc.sortAlignmentByFeatureScore(null);
1178 JButton sortByDens = new JButton(
1179 MessageManager.getString("label.sequence_sort_by_density"));
1180 sortByDens.setFont(JvSwingUtils.getLabelFont());
1181 sortByDens.addActionListener(new ActionListener()
1184 public void actionPerformed(ActionEvent e)
1186 af.avc.sortAlignmentByFeatureDensity(null);
1190 JButton help = new JButton(MessageManager.getString("action.help"));
1191 help.setFont(JvSwingUtils.getLabelFont());
1192 help.addActionListener(new ActionListener()
1195 public void actionPerformed(ActionEvent e)
1199 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1200 } catch (HelpSetException e1)
1202 e1.printStackTrace();
1207 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1208 cancel.setFont(JvSwingUtils.getLabelFont());
1209 cancel.addActionListener(new ActionListener()
1212 public void actionPerformed(ActionEvent e)
1214 fr.setTransparency(originalTransparency);
1215 fr.setFeatureFilters(originalFilters);
1216 updateFeatureRenderer(originalData);
1217 af.getViewport().setViewStyle(originalViewStyle);
1222 JButton ok = new JButton(MessageManager.getString("action.ok"));
1223 ok.setFont(JvSwingUtils.getLabelFont());
1224 ok.addActionListener(new ActionListener()
1227 public void actionPerformed(ActionEvent e)
1233 JButton loadColours = new JButton(
1234 MessageManager.getString("label.load_colours"));
1235 loadColours.setFont(JvSwingUtils.getLabelFont());
1236 loadColours.setToolTipText(
1237 MessageManager.getString("label.load_colours_tooltip"));
1238 loadColours.addActionListener(new ActionListener()
1241 public void actionPerformed(ActionEvent e)
1247 JButton saveColours = new JButton(
1248 MessageManager.getString("label.save_colours"));
1249 saveColours.setFont(JvSwingUtils.getLabelFont());
1250 saveColours.setToolTipText(
1251 MessageManager.getString("label.save_colours_tooltip"));
1252 saveColours.addActionListener(new ActionListener()
1255 public void actionPerformed(ActionEvent e)
1260 transparency.addChangeListener(new ChangeListener()
1263 public void stateChanged(ChangeEvent evt)
1265 if (!inConstruction)
1267 fr.setTransparency((100 - transparency.getValue()) / 100f);
1273 transparency.setMaximum(70);
1274 transparency.setToolTipText(
1275 MessageManager.getString("label.transparency_tip"));
1277 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1278 showComplement = new JCheckBox(
1279 "Show " + (nucleotide ? "protein" : "CDS") + " features");
1280 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
1281 showComplement.addActionListener(new ActionListener()
1284 public void actionPerformed(ActionEvent e)
1287 .setShowComplementFeatures(showComplement.isSelected());
1292 showComplementOnTop = new JCheckBox("on top");
1294 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
1295 showComplementOnTop.addActionListener(new ActionListener()
1298 public void actionPerformed(ActionEvent e)
1300 af.getViewport().setShowComplementFeaturesOnTop(
1301 showComplementOnTop.isSelected());
1306 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1307 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1309 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1310 transbuttons.add(optimizeOrder);
1311 transbuttons.add(invert);
1312 transbuttons.add(sortByScore);
1313 transbuttons.add(sortByDens);
1314 transbuttons.add(help);
1316 boolean hasComplement = af.getViewport().getCodingComplement() != null;
1317 JPanel transPanelLeft = new JPanel(
1318 new GridLayout(hasComplement ? 3 : 2, 1));
1319 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1320 transPanelLeft.add(transparency);
1323 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1324 cp.add(showComplement);
1325 cp.add(showComplementOnTop);
1326 transPanelLeft.add(cp);
1328 lowerPanel.add(transPanelLeft);
1329 lowerPanel.add(transbuttons);
1331 JPanel buttonPanel = new JPanel();
1332 buttonPanel.add(ok);
1333 buttonPanel.add(cancel);
1334 buttonPanel.add(loadColours);
1335 buttonPanel.add(saveColours);
1336 bigPanel.add(scrollPane, BorderLayout.CENTER);
1337 settingsPane.add(bigPanel, BorderLayout.CENTER);
1338 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1339 this.add(settingsPane);
1343 * Repaints alignment, structure and overview (if shown). If there is a
1344 * complementary view which is showing this view's features, then also
1347 void refreshDisplay()
1349 af.alignPanel.paintAlignment(true, true);
1350 AlignViewportI complement = af.getViewport().getCodingComplement();
1351 if (complement != null && complement.isShowComplementFeatures())
1353 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1354 af2.alignPanel.paintAlignment(true, true);
1359 * Answers a suitable tooltip to show on the colour cell of the table
1363 * if true include 'click to edit' and similar text
1366 public static String getColorTooltip(FeatureColourI fcol,
1373 if (fcol.isSimpleColour())
1375 return withHint ? BASE_TOOLTIP : null;
1377 String description = fcol.getDescription();
1378 description = description.replaceAll("<", "<");
1379 description = description.replaceAll(">", ">");
1380 StringBuilder tt = new StringBuilder(description);
1383 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1385 return JvSwingUtils.wrapTooltip(true, tt.toString());
1388 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1391 boolean thr = false;
1392 StringBuilder tx = new StringBuilder();
1394 if (gcol.isColourByAttribute())
1396 tx.append(FeatureMatcher
1397 .toAttributeDisplayName(gcol.getAttributeName()));
1399 else if (!gcol.isColourByLabel())
1401 tx.append(MessageManager.getString("label.score"));
1404 if (gcol.isAboveThreshold())
1409 if (gcol.isBelowThreshold())
1414 if (gcol.isColourByLabel())
1420 if (!gcol.isColourByAttribute())
1428 Color newColor = gcol.getMaxColour();
1429 comp.setBackground(newColor);
1430 // System.err.println("Width is " + w / 2);
1431 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1432 comp.setIcon(ficon);
1433 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1434 // + newColor.getGreen() + ", " + newColor.getBlue()
1435 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1436 // + ", " + minCol.getBlue() + ")");
1438 comp.setHorizontalAlignment(SwingConstants.CENTER);
1439 comp.setText(tx.toString());
1442 // ///////////////////////////////////////////////////////////////////////
1443 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1444 // ///////////////////////////////////////////////////////////////////////
1445 class FeatureTableModel extends AbstractTableModel
1447 private String[] columnNames = {
1448 MessageManager.getString("label.feature_type"),
1449 MessageManager.getString("action.colour"),
1450 MessageManager.getString("label.configuration"),
1451 MessageManager.getString("label.show") };
1453 private Object[][] data;
1455 FeatureTableModel(Object[][] data)
1460 public Object[][] getData()
1465 public void setData(Object[][] data)
1471 public int getColumnCount()
1473 return columnNames.length;
1476 public Object[] getRow(int row)
1482 public int getRowCount()
1488 public String getColumnName(int col)
1490 return columnNames[col];
1494 public Object getValueAt(int row, int col)
1496 return data[row][col];
1500 * Answers the class of column c of the table
1503 public Class<?> getColumnClass(int c)
1508 return String.class;
1510 return FeatureColour.class;
1512 return FeatureMatcherSet.class;
1514 return Boolean.class;
1519 public boolean isCellEditable(int row, int col)
1521 return col == 0 ? false : true;
1525 public void setValueAt(Object value, int row, int col)
1527 data[row][col] = value;
1528 fireTableCellUpdated(row, col);
1529 updateFeatureRenderer(data);
1534 class ColorRenderer extends JLabel implements TableCellRenderer
1536 Border unselectedBorder = null;
1538 Border selectedBorder = null;
1540 public ColorRenderer()
1542 setOpaque(true); // MUST do this for background to show up.
1543 setHorizontalTextPosition(SwingConstants.CENTER);
1544 setVerticalTextPosition(SwingConstants.CENTER);
1548 public Component getTableCellRendererComponent(JTable tbl, Object color,
1549 boolean isSelected, boolean hasFocus, int row, int column)
1551 FeatureColourI cellColour = (FeatureColourI) color;
1553 setBackground(tbl.getBackground());
1554 if (!cellColour.isSimpleColour())
1556 Rectangle cr = tbl.getCellRect(row, column, false);
1557 FeatureSettings.renderGraduatedColor(this, cellColour,
1558 (int) cr.getWidth(), (int) cr.getHeight());
1564 setBackground(cellColour.getColour());
1568 if (selectedBorder == null)
1570 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1571 tbl.getSelectionBackground());
1573 setBorder(selectedBorder);
1577 if (unselectedBorder == null)
1579 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1580 tbl.getBackground());
1582 setBorder(unselectedBorder);
1589 class FilterRenderer extends JLabel implements TableCellRenderer
1591 javax.swing.border.Border unselectedBorder = null;
1593 javax.swing.border.Border selectedBorder = null;
1595 public FilterRenderer()
1597 setOpaque(true); // MUST do this for background to show up.
1598 setHorizontalTextPosition(SwingConstants.CENTER);
1599 setVerticalTextPosition(SwingConstants.CENTER);
1603 public Component getTableCellRendererComponent(JTable tbl,
1604 Object filter, boolean isSelected, boolean hasFocus, int row,
1607 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1609 String asText = theFilter.toString();
1610 setBackground(tbl.getBackground());
1611 this.setText(asText);
1616 if (selectedBorder == null)
1618 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1619 tbl.getSelectionBackground());
1621 setBorder(selectedBorder);
1625 if (unselectedBorder == null)
1627 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1628 tbl.getBackground());
1630 setBorder(unselectedBorder);
1638 * update comp using rendering settings from gcol
1643 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1645 int w = comp.getWidth(), h = comp.getHeight();
1648 w = (int) comp.getPreferredSize().getWidth();
1649 h = (int) comp.getPreferredSize().getHeight();
1656 renderGraduatedColor(comp, gcol, w, h);
1659 class ColorEditor extends AbstractCellEditor
1660 implements TableCellEditor, ActionListener
1664 FeatureColourI currentColor;
1666 FeatureTypeSettings chooser;
1672 JColorChooser colorChooser;
1676 protected static final String EDIT = "edit";
1678 int rowSelected = 0;
1680 public ColorEditor(FeatureSettings me)
1683 // Set up the editor (from the table's point of view),
1684 // which is a button.
1685 // This button brings up the color chooser dialog,
1686 // which is the editor from the user's point of view.
1687 button = new JButton();
1688 button.setActionCommand(EDIT);
1689 button.addActionListener(this);
1690 button.setBorderPainted(false);
1691 // Set up the dialog that the button brings up.
1692 colorChooser = new JColorChooser();
1693 dialog = JColorChooser.createDialog(button,
1694 MessageManager.getString("label.select_colour"), true, // modal
1695 colorChooser, this, // OK button handler
1696 null); // no CANCEL button handler
1700 * Handles events from the editor button and from the dialog's OK button.
1703 public void actionPerformed(ActionEvent e)
1705 // todo test e.getSource() instead here
1706 if (EDIT.equals(e.getActionCommand()))
1708 // The user has clicked the cell, so
1709 // bring up the dialog.
1710 if (currentColor.isSimpleColour())
1712 // bring up simple color chooser
1713 button.setBackground(currentColor.getColour());
1714 colorChooser.setColor(currentColor.getColour());
1715 dialog.setVisible(true);
1719 // bring up graduated chooser.
1720 chooser = new FeatureTypeSettings(me.fr, type);
1725 chooser.setRequestFocusEnabled(true);
1726 chooser.requestFocus();
1728 chooser.addActionListener(this);
1729 // Make the renderer reappear.
1730 fireEditingStopped();
1735 if (currentColor.isSimpleColour())
1738 * read off colour picked in colour chooser after OK pressed
1740 currentColor = new FeatureColour(colorChooser.getColor());
1741 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1746 * after OK in variable colour dialog, any changes to colour
1747 * (or filters!) are already set in FeatureRenderer, so just
1748 * update table data without triggering updateFeatureRenderer
1750 currentColor = fr.getFeatureColours().get(type);
1751 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1752 if (currentFilter == null)
1754 currentFilter = new FeatureMatcherSet();
1756 Object[] data = ((FeatureTableModel) table.getModel())
1757 .getData()[rowSelected];
1758 data[COLOUR_COLUMN] = currentColor;
1759 data[FILTER_COLUMN] = currentFilter;
1761 fireEditingStopped();
1762 me.table.validate();
1766 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1768 public Object getCellEditorValue()
1770 return currentColor;
1773 // Implement the one method defined by TableCellEditor.
1775 public Component getTableCellEditorComponent(JTable theTable, Object value,
1776 boolean isSelected, int row, int column)
1778 currentColor = (FeatureColourI) value;
1779 this.rowSelected = row;
1780 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1781 button.setOpaque(true);
1782 button.setBackground(me.getBackground());
1783 if (!currentColor.isSimpleColour())
1785 JLabel btn = new JLabel();
1786 btn.setSize(button.getSize());
1787 FeatureSettings.renderGraduatedColor(btn, currentColor);
1788 button.setBackground(btn.getBackground());
1789 button.setIcon(btn.getIcon());
1790 button.setText(btn.getText());
1795 button.setIcon(null);
1796 button.setBackground(currentColor.getColour());
1803 * The cell editor for the Filter column. It displays the text of any filters
1804 * for the feature type in that row (in full as a tooltip, possible abbreviated
1805 * as display text). On click in the cell, opens the Feature Display Settings
1806 * dialog at the Filters tab.
1808 class FilterEditor extends AbstractCellEditor
1809 implements TableCellEditor, ActionListener
1813 FeatureMatcherSetI currentFilter;
1821 protected static final String EDIT = "edit";
1823 int rowSelected = 0;
1825 public FilterEditor(FeatureSettings me)
1828 button = new JButton();
1829 button.setActionCommand(EDIT);
1830 button.addActionListener(this);
1831 button.setBorderPainted(false);
1835 * Handles events from the editor button
1838 public void actionPerformed(ActionEvent e)
1840 if (button == e.getSource())
1842 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1843 chooser.addActionListener(this);
1844 chooser.setRequestFocusEnabled(true);
1845 chooser.requestFocus();
1846 if (lastLocation != null)
1848 // todo open at its last position on screen
1849 chooser.setBounds(lastLocation.x, lastLocation.y,
1850 chooser.getWidth(), chooser.getHeight());
1853 fireEditingStopped();
1855 else if (e.getSource() instanceof Component)
1859 * after OK in variable colour dialog, any changes to filter
1860 * (or colours!) are already set in FeatureRenderer, so just
1861 * update table data without triggering updateFeatureRenderer
1863 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1864 currentFilter = me.fr.getFeatureFilter(type);
1865 if (currentFilter == null)
1867 currentFilter = new FeatureMatcherSet();
1869 Object[] data = ((FeatureTableModel) table.getModel())
1870 .getData()[rowSelected];
1871 data[COLOUR_COLUMN] = currentColor;
1872 data[FILTER_COLUMN] = currentFilter;
1873 fireEditingStopped();
1874 me.table.validate();
1879 public Object getCellEditorValue()
1881 return currentFilter;
1885 public Component getTableCellEditorComponent(JTable theTable, Object value,
1886 boolean isSelected, int row, int column)
1888 currentFilter = (FeatureMatcherSetI) value;
1889 this.rowSelected = row;
1890 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1891 button.setOpaque(true);
1892 button.setBackground(me.getBackground());
1893 button.setText(currentFilter.toString());
1894 button.setIcon(null);
1900 class FeatureIcon implements Icon
1902 FeatureColourI gcol;
1906 boolean midspace = false;
1908 int width = 50, height = 20;
1910 int s1, e1; // start and end of midpoint band for thresholded symbol
1912 Color mpcolour = Color.white;
1914 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1934 public int getIconWidth()
1940 public int getIconHeight()
1946 public void paintIcon(Component c, Graphics g, int x, int y)
1949 if (gcol.isColourByLabel())
1952 g.fillRect(0, 0, width, height);
1953 // need an icon here.
1954 g.setColor(gcol.getMaxColour());
1956 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1958 // g.setFont(g.getFont().deriveFont(
1959 // AffineTransform.getScaleInstance(
1960 // width/g.getFontMetrics().stringWidth("Label"),
1961 // height/g.getFontMetrics().getHeight())));
1963 g.drawString(MessageManager.getString("label.label"), 0, 0);
1968 Color minCol = gcol.getMinColour();
1970 g.fillRect(0, 0, s1, height);
1973 g.setColor(Color.white);
1974 g.fillRect(s1, 0, e1 - s1, height);
1976 g.setColor(gcol.getMaxColour());
1977 g.fillRect(0, e1, width - e1, height);