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);
303 // isPopupTrigger fires on mouseReleased on Windows
305 public void mouseReleased(MouseEvent evt)
307 selectedRow = table.rowAtPoint(evt.getPoint());
308 if (evt.isPopupTrigger())
310 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
311 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
312 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
318 table.addMouseMotionListener(new MouseMotionAdapter()
321 public void mouseDragged(MouseEvent evt)
323 int newRow = table.rowAtPoint(evt.getPoint());
324 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
327 * reposition 'selectedRow' to 'newRow' (the dragged to location)
328 * this could be more than one row away for a very fast drag action
329 * so just swap it with adjacent rows until we get it there
331 Object[][] data = ((FeatureTableModel) table.getModel())
333 int direction = newRow < selectedRow ? -1 : 1;
334 for (int i = selectedRow; i != newRow; i += direction)
336 Object[] temp = data[i];
337 data[i] = data[i + direction];
338 data[i + direction] = temp;
340 updateFeatureRenderer(data);
342 selectedRow = newRow;
346 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
347 // MessageManager.getString("label.feature_settings_click_drag")));
348 scrollPane.setViewportView(table);
350 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
352 fr.findAllFeatures(true); // display everything!
355 discoverAllFeatureData();
356 final PropertyChangeListener change;
357 final FeatureSettings fs = this;
358 fr.addPropertyChangeListener(change = new PropertyChangeListener()
361 public void propertyChange(PropertyChangeEvent evt)
363 if (!fs.resettingTable && !fs.handlingUpdate)
365 fs.handlingUpdate = true;
367 // new groups may be added with new sequence feature types only
368 fs.handlingUpdate = false;
374 frame = new JInternalFrame();
375 frame.setContentPane(this);
376 if (Platform.isAMac())
378 Desktop.addInternalFrame(frame,
379 MessageManager.getString("label.sequence_feature_settings"),
384 Desktop.addInternalFrame(frame,
385 MessageManager.getString("label.sequence_feature_settings"),
388 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
390 frame.addInternalFrameListener(
391 new javax.swing.event.InternalFrameAdapter()
394 public void internalFrameClosed(
395 javax.swing.event.InternalFrameEvent evt)
397 fr.removePropertyChangeListener(change);
400 frame.setLayer(JLayeredPane.PALETTE_LAYER);
401 inConstruction = false;
404 protected void popupSort(final int rowSelected, final String type,
405 final Object typeCol, final Map<String, float[][]> minmax, int x,
408 JPopupMenu men = new JPopupMenu(MessageManager
409 .formatMessage("label.settings_for_param", new String[]
411 JMenuItem scr = new JMenuItem(
412 MessageManager.getString("label.sort_by_score"));
414 final FeatureSettings me = this;
415 scr.addActionListener(new ActionListener()
419 public void actionPerformed(ActionEvent e)
422 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
427 JMenuItem dens = new JMenuItem(
428 MessageManager.getString("label.sort_by_density"));
429 dens.addActionListener(new ActionListener()
433 public void actionPerformed(ActionEvent e)
436 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
443 JMenuItem selCols = new JMenuItem(
444 MessageManager.getString("label.select_columns_containing"));
445 selCols.addActionListener(new ActionListener()
448 public void actionPerformed(ActionEvent arg0)
450 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
454 JMenuItem clearCols = new JMenuItem(MessageManager
455 .getString("label.select_columns_not_containing"));
456 clearCols.addActionListener(new ActionListener()
459 public void actionPerformed(ActionEvent arg0)
461 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
465 JMenuItem hideCols = new JMenuItem(
466 MessageManager.getString("label.hide_columns_containing"));
467 hideCols.addActionListener(new ActionListener()
470 public void actionPerformed(ActionEvent arg0)
472 fr.ap.alignFrame.hideFeatureColumns(type, true);
475 JMenuItem hideOtherCols = new JMenuItem(
476 MessageManager.getString("label.hide_columns_not_containing"));
477 hideOtherCols.addActionListener(new ActionListener()
480 public void actionPerformed(ActionEvent arg0)
482 fr.ap.alignFrame.hideFeatureColumns(type, false);
488 men.add(hideOtherCols);
489 men.show(table, x, y);
493 synchronized public void discoverAllFeatureData()
495 Set<String> allGroups = new HashSet<>();
496 AlignmentI alignment = af.getViewport().getAlignment();
498 for (int i = 0; i < alignment.getHeight(); i++)
500 SequenceI seq = alignment.getSequenceAt(i);
501 for (String group : seq.getFeatures().getFeatureGroups(true))
503 if (group != null && !allGroups.contains(group))
505 allGroups.add(group);
506 checkGroupState(group);
517 * Synchronise gui group list and check visibility of group
520 * @return true if group is visible
522 private boolean checkGroupState(String group)
524 boolean visible = fr.checkGroupVisibility(group, true);
526 for (int g = 0; g < groupPanel.getComponentCount(); g++)
528 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
530 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
535 final String grp = group;
536 final JCheckBox check = new JCheckBox(group, visible);
537 check.setFont(new Font("Serif", Font.BOLD, 12));
538 check.setToolTipText(group);
539 check.addItemListener(new ItemListener()
542 public void itemStateChanged(ItemEvent evt)
544 fr.setGroupVisibility(check.getText(), check.isSelected());
545 resetTable(new String[] { grp });
549 groupPanel.add(check);
553 synchronized void resetTable(String[] groupChanged)
559 resettingTable = true;
560 typeWidth = new Hashtable<>();
561 // TODO: change avWidth calculation to 'per-sequence' average and use long
564 Set<String> displayableTypes = new HashSet<>();
565 Set<String> foundGroups = new HashSet<>();
568 * determine which feature types may be visible depending on
569 * which groups are selected, and recompute average width data
571 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
574 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
577 * get the sequence's groups for positional features
578 * and keep track of which groups are visible
580 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
581 Set<String> visibleGroups = new HashSet<>();
582 for (String group : groups)
584 if (group == null || checkGroupState(group))
586 visibleGroups.add(group);
589 foundGroups.addAll(groups);
592 * get distinct feature types for visible groups
593 * record distinct visible types, and their count and total length
595 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
596 visibleGroups.toArray(new String[visibleGroups.size()]));
597 for (String type : types)
599 displayableTypes.add(type);
600 float[] avWidth = typeWidth.get(type);
603 avWidth = new float[2];
604 typeWidth.put(type, avWidth);
606 // todo this could include features with a non-visible group
607 // - do we greatly care?
608 // todo should we include non-displayable features here, and only
609 // update when features are added?
610 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
611 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
615 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
618 if (fr.hasRenderOrder())
622 fr.findAllFeatures(groupChanged != null); // prod to update
623 // colourschemes. but don't
625 // First add the checks in the previous render order,
626 // in case the window has been closed and reopened
628 List<String> frl = fr.getRenderOrder();
629 for (int ro = frl.size() - 1; ro > -1; ro--)
631 String type = frl.get(ro);
633 if (!displayableTypes.contains(type))
638 data[dataIndex][TYPE_COLUMN] = type;
639 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
640 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
641 data[dataIndex][FILTER_COLUMN] = featureFilter == null
642 ? new FeatureMatcherSet()
644 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
645 af.getViewport().getFeaturesDisplayed().isVisible(type));
647 displayableTypes.remove(type);
652 * process any extra features belonging only to
653 * a group which was just selected
655 while (!displayableTypes.isEmpty())
657 String type = displayableTypes.iterator().next();
658 data[dataIndex][TYPE_COLUMN] = type;
660 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
661 if (data[dataIndex][COLOUR_COLUMN] == null)
663 // "Colour has been updated in another view!!"
664 fr.clearRenderOrder();
667 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
668 data[dataIndex][FILTER_COLUMN] = featureFilter == null
669 ? new FeatureMatcherSet()
671 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
673 displayableTypes.remove(type);
676 if (originalData == null)
678 originalData = new Object[data.length][COLUMN_COUNT];
679 for (int i = 0; i < data.length; i++)
681 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
686 updateOriginalData(data);
689 table.setModel(new FeatureTableModel(data));
690 table.getColumnModel().getColumn(0).setPreferredWidth(200);
692 groupPanel.setLayout(
693 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
694 pruneGroups(foundGroups);
695 groupPanel.validate();
697 updateFeatureRenderer(data, groupChanged != null);
698 resettingTable = false;
702 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
703 * have been made outwith this dialog
705 * <li>a new feature type added (and made visible)</li>
706 * <li>a feature colour changed (in the Amend Features dialog)</li>
711 protected void updateOriginalData(Object[][] foundData)
713 // todo LinkedHashMap instead of Object[][] would be nice
715 Object[][] currentData = ((FeatureTableModel) table.getModel())
717 for (Object[] row : foundData)
719 String type = (String) row[TYPE_COLUMN];
720 boolean found = false;
721 for (Object[] current : currentData)
723 if (type.equals(current[TYPE_COLUMN]))
727 * currently dependent on object equality here;
728 * really need an equals method on FeatureColour
730 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
733 * feature colour has changed externally - update originalData
735 for (Object[] original : originalData)
737 if (type.equals(original[TYPE_COLUMN]))
739 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
750 * new feature detected - add to original data (on top)
752 Object[][] newData = new Object[originalData.length
754 for (int i = 0; i < originalData.length; i++)
756 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
760 originalData = newData;
766 * Remove from the groups panel any checkboxes for groups that are not in the
767 * foundGroups set. This enables removing a group from the display when the last
768 * feature in that group is deleted.
772 protected void pruneGroups(Set<String> foundGroups)
774 for (int g = 0; g < groupPanel.getComponentCount(); g++)
776 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
777 if (!foundGroups.contains(checkbox.getText()))
779 groupPanel.remove(checkbox);
785 * reorder data based on the featureRenderers global priority list.
789 private void ensureOrder(Object[][] data)
791 boolean sort = false;
792 float[] order = new float[data.length];
793 for (int i = 0; i < order.length; i++)
795 order[i] = fr.getOrder(data[i][0].toString());
798 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
802 sort = sort || order[i - 1] > order[i];
807 jalview.util.QuickSort.sort(order, data);
812 * Offers a file chooser dialog, and then loads the feature colours and
813 * filters from file in XML format and unmarshals to Jalview feature settings
817 JalviewFileChooser chooser = new JalviewFileChooser("fc",
818 SEQUENCE_FEATURE_COLOURS);
819 chooser.setFileView(new JalviewFileView());
820 chooser.setDialogTitle(
821 MessageManager.getString("label.load_feature_colours"));
822 chooser.setToolTipText(MessageManager.getString("action.load"));
824 int value = chooser.showOpenDialog(this);
826 if (value == JalviewFileChooser.APPROVE_OPTION)
828 File file = chooser.getSelectedFile();
834 * Loads feature colours and filters from XML stored in the given file
842 InputStreamReader in = new InputStreamReader(
843 new FileInputStream(file), "UTF-8");
845 JAXBContext jc = JAXBContext
846 .newInstance("jalview.xml.binding.jalview");
847 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
848 XMLStreamReader streamReader = XMLInputFactory.newInstance()
849 .createXMLStreamReader(in);
850 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
851 JalviewUserColours.class);
852 JalviewUserColours jucs = jbe.getValue();
854 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
857 * load feature colours
859 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
861 Colour newcol = jucs.getColour().get(i);
862 FeatureColourI colour = jalview.project.Jalview2XML
863 .parseColour(newcol);
864 fr.setColour(newcol.getName(), colour);
865 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
869 * load feature filters; loaded filters will replace any that are
870 * currently defined, other defined filters are left unchanged
872 for (int i = 0; i < jucs.getFilter().size(); i++)
874 Filter filterModel = jucs.getFilter().get(i);
875 String featureType = filterModel.getFeatureType();
876 FeatureMatcherSetI filter = jalview.project.Jalview2XML
877 .parseFilter(featureType, filterModel.getMatcherSet());
878 if (!filter.isEmpty())
880 fr.setFeatureFilter(featureType, filter);
885 * update feature settings table
890 Object[][] data = ((FeatureTableModel) table.getModel())
893 updateFeatureRenderer(data, false);
896 } catch (Exception ex)
898 System.out.println("Error loading User Colour File\n" + ex);
903 * Offers a file chooser dialog, and then saves the current feature colours
904 * and any filters to the selected file in XML format
908 JalviewFileChooser chooser = new JalviewFileChooser("fc",
909 SEQUENCE_FEATURE_COLOURS);
910 chooser.setFileView(new JalviewFileView());
911 chooser.setDialogTitle(
912 MessageManager.getString("label.save_feature_colours"));
913 chooser.setToolTipText(MessageManager.getString("action.save"));
915 int value = chooser.showSaveDialog(this);
917 if (value == JalviewFileChooser.APPROVE_OPTION)
919 save(chooser.getSelectedFile());
924 * Saves feature colours and filters to the given file
930 JalviewUserColours ucs = new JalviewUserColours();
931 ucs.setSchemeName("Sequence Features");
934 PrintWriter out = new PrintWriter(new OutputStreamWriter(
935 new FileOutputStream(file), "UTF-8"));
938 * sort feature types by colour order, from 0 (highest)
941 Set<String> fr_colours = fr.getAllFeatureColours();
942 String[] sortedTypes = fr_colours
943 .toArray(new String[fr_colours.size()]);
944 Arrays.sort(sortedTypes, new Comparator<String>()
947 public int compare(String type1, String type2)
949 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
954 * save feature colours
956 for (String featureType : sortedTypes)
958 FeatureColourI fcol = fr.getFeatureStyle(featureType);
959 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
961 ucs.getColour().add(col);
965 * save any feature filters
967 for (String featureType : sortedTypes)
969 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
970 if (filter != null && !filter.isEmpty())
972 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
973 FeatureMatcherI firstMatcher = iterator.next();
974 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
975 .marshalFilter(firstMatcher, iterator,
977 Filter filterModel = new Filter();
978 filterModel.setFeatureType(featureType);
979 filterModel.setMatcherSet(ms);
980 ucs.getFilter().add(filterModel);
983 JAXBContext jaxbContext = JAXBContext
984 .newInstance(JalviewUserColours.class);
985 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
986 jaxbMarshaller.marshal(
987 new ObjectFactory().createJalviewUserColours(ucs), out);
989 // jaxbMarshaller.marshal(object, pout);
990 // marshaller.marshal(object);
995 } catch (Exception ex)
997 ex.printStackTrace();
1001 public void invertSelection()
1003 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1004 for (int i = 0; i < data.length; i++)
1006 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1008 updateFeatureRenderer(data, true);
1012 public void orderByAvWidth()
1014 if (table == null || table.getModel() == null)
1018 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1019 float[] width = new float[data.length];
1023 for (int i = 0; i < data.length; i++)
1025 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1028 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1029 // weight - but have to make per
1030 // sequence, too (awidth[2])
1031 // if (width[i]==1) // hack to distinguish single width sequences.
1042 boolean sort = false;
1043 for (int i = 0; i < width.length; i++)
1045 // awidth = (float[]) typeWidth.get(data[i][0]);
1048 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1051 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1057 width[i] /= max; // normalize
1058 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1062 sort = sort || width[i - 1] > width[i];
1067 jalview.util.QuickSort.sort(width, data);
1068 // update global priority order
1071 updateFeatureRenderer(data, false);
1079 frame.setClosed(true);
1080 } catch (Exception exe)
1086 public void updateFeatureRenderer(Object[][] data)
1088 updateFeatureRenderer(data, true);
1092 * Update the priority order of features; only repaint if this changed the order
1093 * of visible features
1098 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1100 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1102 if (fr.setFeaturePriority(rowData, visibleNew))
1109 * Converts table data into an array of data beans
1111 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1113 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1114 for (int i = 0; i < data.length; i++)
1116 String type = (String) data[i][TYPE_COLUMN];
1117 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1118 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1119 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1120 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1126 private void jbInit() throws Exception
1128 this.setLayout(new BorderLayout());
1130 JPanel settingsPane = new JPanel();
1131 settingsPane.setLayout(new BorderLayout());
1133 JPanel bigPanel = new JPanel();
1134 bigPanel.setLayout(new BorderLayout());
1136 groupPanel = new JPanel();
1137 bigPanel.add(groupPanel, BorderLayout.NORTH);
1139 JButton invert = new JButton(
1140 MessageManager.getString("label.invert_selection"));
1141 invert.setFont(JvSwingUtils.getLabelFont());
1142 invert.addActionListener(new ActionListener()
1145 public void actionPerformed(ActionEvent e)
1151 JButton optimizeOrder = new JButton(
1152 MessageManager.getString("label.optimise_order"));
1153 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1154 optimizeOrder.addActionListener(new ActionListener()
1157 public void actionPerformed(ActionEvent e)
1163 JButton sortByScore = new JButton(
1164 MessageManager.getString("label.seq_sort_by_score"));
1165 sortByScore.setFont(JvSwingUtils.getLabelFont());
1166 sortByScore.addActionListener(new ActionListener()
1169 public void actionPerformed(ActionEvent e)
1171 af.avc.sortAlignmentByFeatureScore(null);
1174 JButton sortByDens = new JButton(
1175 MessageManager.getString("label.sequence_sort_by_density"));
1176 sortByDens.setFont(JvSwingUtils.getLabelFont());
1177 sortByDens.addActionListener(new ActionListener()
1180 public void actionPerformed(ActionEvent e)
1182 af.avc.sortAlignmentByFeatureDensity(null);
1186 JButton help = new JButton(MessageManager.getString("action.help"));
1187 help.setFont(JvSwingUtils.getLabelFont());
1188 help.addActionListener(new ActionListener()
1191 public void actionPerformed(ActionEvent e)
1195 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1196 } catch (HelpSetException e1)
1198 e1.printStackTrace();
1203 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1204 cancel.setFont(JvSwingUtils.getLabelFont());
1205 cancel.addActionListener(new ActionListener()
1208 public void actionPerformed(ActionEvent e)
1210 fr.setTransparency(originalTransparency);
1211 fr.setFeatureFilters(originalFilters);
1212 updateFeatureRenderer(originalData);
1213 af.getViewport().setViewStyle(originalViewStyle);
1218 JButton ok = new JButton(MessageManager.getString("action.ok"));
1219 ok.setFont(JvSwingUtils.getLabelFont());
1220 ok.addActionListener(new ActionListener()
1223 public void actionPerformed(ActionEvent e)
1229 JButton loadColours = new JButton(
1230 MessageManager.getString("label.load_colours"));
1231 loadColours.setFont(JvSwingUtils.getLabelFont());
1232 loadColours.setToolTipText(
1233 MessageManager.getString("label.load_colours_tooltip"));
1234 loadColours.addActionListener(new ActionListener()
1237 public void actionPerformed(ActionEvent e)
1243 JButton saveColours = new JButton(
1244 MessageManager.getString("label.save_colours"));
1245 saveColours.setFont(JvSwingUtils.getLabelFont());
1246 saveColours.setToolTipText(
1247 MessageManager.getString("label.save_colours_tooltip"));
1248 saveColours.addActionListener(new ActionListener()
1251 public void actionPerformed(ActionEvent e)
1256 transparency.addChangeListener(new ChangeListener()
1259 public void stateChanged(ChangeEvent evt)
1261 if (!inConstruction)
1263 fr.setTransparency((100 - transparency.getValue()) / 100f);
1269 transparency.setMaximum(70);
1270 transparency.setToolTipText(
1271 MessageManager.getString("label.transparency_tip"));
1273 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1274 JCheckBox showComplement = new JCheckBox(
1275 "Show " + (nucleotide ? "protein" : "CDS") + " features");
1276 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
1277 showComplement.addActionListener(new ActionListener()
1280 public void actionPerformed(ActionEvent e)
1283 .setShowComplementFeatures(showComplement.isSelected());
1288 JCheckBox showComplementOnTop = new JCheckBox("on top");
1290 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
1291 showComplementOnTop.addActionListener(new ActionListener()
1294 public void actionPerformed(ActionEvent e)
1296 af.getViewport().setShowComplementFeaturesOnTop(
1297 showComplementOnTop.isSelected());
1302 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1303 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1305 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1306 transbuttons.add(optimizeOrder);
1307 transbuttons.add(invert);
1308 transbuttons.add(sortByScore);
1309 transbuttons.add(sortByDens);
1310 transbuttons.add(help);
1312 boolean hasComplement = af.getViewport().getCodingComplement() != null;
1313 JPanel transPanelLeft = new JPanel(
1314 new GridLayout(hasComplement ? 3 : 2, 1));
1315 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1316 transPanelLeft.add(transparency);
1319 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1320 cp.add(showComplement);
1321 cp.add(showComplementOnTop);
1322 transPanelLeft.add(cp);
1324 lowerPanel.add(transPanelLeft);
1325 lowerPanel.add(transbuttons);
1327 JPanel buttonPanel = new JPanel();
1328 buttonPanel.add(ok);
1329 buttonPanel.add(cancel);
1330 buttonPanel.add(loadColours);
1331 buttonPanel.add(saveColours);
1332 bigPanel.add(scrollPane, BorderLayout.CENTER);
1333 settingsPane.add(bigPanel, BorderLayout.CENTER);
1334 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1335 this.add(settingsPane);
1339 * Repaints alignment, structure and overview (if shown). If there is a
1340 * complementary view which is showing this view's features, then also
1343 void refreshDisplay()
1345 af.alignPanel.paintAlignment(true, true);
1346 AlignViewportI complement = af.getViewport().getCodingComplement();
1347 if (complement != null && complement.isShowComplementFeatures())
1349 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1350 af2.alignPanel.paintAlignment(true, true);
1355 * Answers a suitable tooltip to show on the colour cell of the table
1359 * if true include 'click to edit' and similar text
1362 public static String getColorTooltip(FeatureColourI fcol,
1369 if (fcol.isSimpleColour())
1371 return withHint ? BASE_TOOLTIP : null;
1373 String description = fcol.getDescription();
1374 description = description.replaceAll("<", "<");
1375 description = description.replaceAll(">", ">");
1376 StringBuilder tt = new StringBuilder(description);
1379 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1381 return JvSwingUtils.wrapTooltip(true, tt.toString());
1384 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1387 boolean thr = false;
1388 StringBuilder tx = new StringBuilder();
1390 if (gcol.isColourByAttribute())
1392 tx.append(FeatureMatcher
1393 .toAttributeDisplayName(gcol.getAttributeName()));
1395 else if (!gcol.isColourByLabel())
1397 tx.append(MessageManager.getString("label.score"));
1400 if (gcol.isAboveThreshold())
1405 if (gcol.isBelowThreshold())
1410 if (gcol.isColourByLabel())
1416 if (!gcol.isColourByAttribute())
1424 Color newColor = gcol.getMaxColour();
1425 comp.setBackground(newColor);
1426 // System.err.println("Width is " + w / 2);
1427 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1428 comp.setIcon(ficon);
1429 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1430 // + newColor.getGreen() + ", " + newColor.getBlue()
1431 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1432 // + ", " + minCol.getBlue() + ")");
1434 comp.setHorizontalAlignment(SwingConstants.CENTER);
1435 comp.setText(tx.toString());
1438 // ///////////////////////////////////////////////////////////////////////
1439 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1440 // ///////////////////////////////////////////////////////////////////////
1441 class FeatureTableModel extends AbstractTableModel
1443 private String[] columnNames = {
1444 MessageManager.getString("label.feature_type"),
1445 MessageManager.getString("action.colour"),
1446 MessageManager.getString("label.configuration"),
1447 MessageManager.getString("label.show") };
1449 private Object[][] data;
1451 FeatureTableModel(Object[][] data)
1456 public Object[][] getData()
1461 public void setData(Object[][] data)
1467 public int getColumnCount()
1469 return columnNames.length;
1472 public Object[] getRow(int row)
1478 public int getRowCount()
1484 public String getColumnName(int col)
1486 return columnNames[col];
1490 public Object getValueAt(int row, int col)
1492 return data[row][col];
1496 * Answers the class of column c of the table
1499 public Class<?> getColumnClass(int c)
1504 return String.class;
1506 return FeatureColour.class;
1508 return FeatureMatcherSet.class;
1510 return Boolean.class;
1515 public boolean isCellEditable(int row, int col)
1517 return col == 0 ? false : true;
1521 public void setValueAt(Object value, int row, int col)
1523 data[row][col] = value;
1524 fireTableCellUpdated(row, col);
1525 updateFeatureRenderer(data);
1530 class ColorRenderer extends JLabel implements TableCellRenderer
1532 Border unselectedBorder = null;
1534 Border selectedBorder = null;
1536 public ColorRenderer()
1538 setOpaque(true); // MUST do this for background to show up.
1539 setHorizontalTextPosition(SwingConstants.CENTER);
1540 setVerticalTextPosition(SwingConstants.CENTER);
1544 public Component getTableCellRendererComponent(JTable tbl, Object color,
1545 boolean isSelected, boolean hasFocus, int row, int column)
1547 FeatureColourI cellColour = (FeatureColourI) color;
1549 setBackground(tbl.getBackground());
1550 if (!cellColour.isSimpleColour())
1552 Rectangle cr = tbl.getCellRect(row, column, false);
1553 FeatureSettings.renderGraduatedColor(this, cellColour,
1554 (int) cr.getWidth(), (int) cr.getHeight());
1560 setBackground(cellColour.getColour());
1564 if (selectedBorder == null)
1566 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1567 tbl.getSelectionBackground());
1569 setBorder(selectedBorder);
1573 if (unselectedBorder == null)
1575 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1576 tbl.getBackground());
1578 setBorder(unselectedBorder);
1585 class FilterRenderer extends JLabel implements TableCellRenderer
1587 javax.swing.border.Border unselectedBorder = null;
1589 javax.swing.border.Border selectedBorder = null;
1591 public FilterRenderer()
1593 setOpaque(true); // MUST do this for background to show up.
1594 setHorizontalTextPosition(SwingConstants.CENTER);
1595 setVerticalTextPosition(SwingConstants.CENTER);
1599 public Component getTableCellRendererComponent(JTable tbl,
1600 Object filter, boolean isSelected, boolean hasFocus, int row,
1603 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1605 String asText = theFilter.toString();
1606 setBackground(tbl.getBackground());
1607 this.setText(asText);
1612 if (selectedBorder == null)
1614 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1615 tbl.getSelectionBackground());
1617 setBorder(selectedBorder);
1621 if (unselectedBorder == null)
1623 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1624 tbl.getBackground());
1626 setBorder(unselectedBorder);
1634 * update comp using rendering settings from gcol
1639 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1641 int w = comp.getWidth(), h = comp.getHeight();
1644 w = (int) comp.getPreferredSize().getWidth();
1645 h = (int) comp.getPreferredSize().getHeight();
1652 renderGraduatedColor(comp, gcol, w, h);
1655 class ColorEditor extends AbstractCellEditor
1656 implements TableCellEditor, ActionListener
1660 FeatureColourI currentColor;
1662 FeatureTypeSettings chooser;
1668 JColorChooser colorChooser;
1672 protected static final String EDIT = "edit";
1674 int rowSelected = 0;
1676 public ColorEditor(FeatureSettings me)
1679 // Set up the editor (from the table's point of view),
1680 // which is a button.
1681 // This button brings up the color chooser dialog,
1682 // which is the editor from the user's point of view.
1683 button = new JButton();
1684 button.setActionCommand(EDIT);
1685 button.addActionListener(this);
1686 button.setBorderPainted(false);
1687 // Set up the dialog that the button brings up.
1688 colorChooser = new JColorChooser();
1689 dialog = JColorChooser.createDialog(button,
1690 MessageManager.getString("label.select_colour"), true, // modal
1691 colorChooser, this, // OK button handler
1692 null); // no CANCEL button handler
1696 * Handles events from the editor button and from the dialog's OK button.
1699 public void actionPerformed(ActionEvent e)
1701 // todo test e.getSource() instead here
1702 if (EDIT.equals(e.getActionCommand()))
1704 // The user has clicked the cell, so
1705 // bring up the dialog.
1706 if (currentColor.isSimpleColour())
1708 // bring up simple color chooser
1709 button.setBackground(currentColor.getColour());
1710 colorChooser.setColor(currentColor.getColour());
1711 dialog.setVisible(true);
1715 // bring up graduated chooser.
1716 chooser = new FeatureTypeSettings(me.fr, type);
1721 chooser.setRequestFocusEnabled(true);
1722 chooser.requestFocus();
1724 chooser.addActionListener(this);
1725 // Make the renderer reappear.
1726 fireEditingStopped();
1731 if (currentColor.isSimpleColour())
1734 * read off colour picked in colour chooser after OK pressed
1736 currentColor = new FeatureColour(colorChooser.getColor());
1737 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1742 * after OK in variable colour dialog, any changes to colour
1743 * (or filters!) are already set in FeatureRenderer, so just
1744 * update table data without triggering updateFeatureRenderer
1746 currentColor = fr.getFeatureColours().get(type);
1747 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1748 if (currentFilter == null)
1750 currentFilter = new FeatureMatcherSet();
1752 Object[] data = ((FeatureTableModel) table.getModel())
1753 .getData()[rowSelected];
1754 data[COLOUR_COLUMN] = currentColor;
1755 data[FILTER_COLUMN] = currentFilter;
1757 fireEditingStopped();
1758 me.table.validate();
1762 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1764 public Object getCellEditorValue()
1766 return currentColor;
1769 // Implement the one method defined by TableCellEditor.
1771 public Component getTableCellEditorComponent(JTable theTable, Object value,
1772 boolean isSelected, int row, int column)
1774 currentColor = (FeatureColourI) value;
1775 this.rowSelected = row;
1776 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1777 button.setOpaque(true);
1778 button.setBackground(me.getBackground());
1779 if (!currentColor.isSimpleColour())
1781 JLabel btn = new JLabel();
1782 btn.setSize(button.getSize());
1783 FeatureSettings.renderGraduatedColor(btn, currentColor);
1784 button.setBackground(btn.getBackground());
1785 button.setIcon(btn.getIcon());
1786 button.setText(btn.getText());
1791 button.setIcon(null);
1792 button.setBackground(currentColor.getColour());
1799 * The cell editor for the Filter column. It displays the text of any filters
1800 * for the feature type in that row (in full as a tooltip, possible abbreviated
1801 * as display text). On click in the cell, opens the Feature Display Settings
1802 * dialog at the Filters tab.
1804 class FilterEditor extends AbstractCellEditor
1805 implements TableCellEditor, ActionListener
1809 FeatureMatcherSetI currentFilter;
1817 protected static final String EDIT = "edit";
1819 int rowSelected = 0;
1821 public FilterEditor(FeatureSettings me)
1824 button = new JButton();
1825 button.setActionCommand(EDIT);
1826 button.addActionListener(this);
1827 button.setBorderPainted(false);
1831 * Handles events from the editor button
1834 public void actionPerformed(ActionEvent e)
1836 if (button == e.getSource())
1838 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1839 chooser.addActionListener(this);
1840 chooser.setRequestFocusEnabled(true);
1841 chooser.requestFocus();
1842 if (lastLocation != null)
1844 // todo open at its last position on screen
1845 chooser.setBounds(lastLocation.x, lastLocation.y,
1846 chooser.getWidth(), chooser.getHeight());
1849 fireEditingStopped();
1851 else if (e.getSource() instanceof Component)
1855 * after OK in variable colour dialog, any changes to filter
1856 * (or colours!) are already set in FeatureRenderer, so just
1857 * update table data without triggering updateFeatureRenderer
1859 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1860 currentFilter = me.fr.getFeatureFilter(type);
1861 if (currentFilter == null)
1863 currentFilter = new FeatureMatcherSet();
1865 Object[] data = ((FeatureTableModel) table.getModel())
1866 .getData()[rowSelected];
1867 data[COLOUR_COLUMN] = currentColor;
1868 data[FILTER_COLUMN] = currentFilter;
1869 fireEditingStopped();
1870 me.table.validate();
1875 public Object getCellEditorValue()
1877 return currentFilter;
1881 public Component getTableCellEditorComponent(JTable theTable, Object value,
1882 boolean isSelected, int row, int column)
1884 currentFilter = (FeatureMatcherSetI) value;
1885 this.rowSelected = row;
1886 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1887 button.setOpaque(true);
1888 button.setBackground(me.getBackground());
1889 button.setText(currentFilter.toString());
1890 button.setIcon(null);
1896 class FeatureIcon implements Icon
1898 FeatureColourI gcol;
1902 boolean midspace = false;
1904 int width = 50, height = 20;
1906 int s1, e1; // start and end of midpoint band for thresholded symbol
1908 Color mpcolour = Color.white;
1910 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1930 public int getIconWidth()
1936 public int getIconHeight()
1942 public void paintIcon(Component c, Graphics g, int x, int y)
1945 if (gcol.isColourByLabel())
1948 g.fillRect(0, 0, width, height);
1949 // need an icon here.
1950 g.setColor(gcol.getMaxColour());
1952 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1954 // g.setFont(g.getFont().deriveFont(
1955 // AffineTransform.getScaleInstance(
1956 // width/g.getFontMetrics().stringWidth("Label"),
1957 // height/g.getFontMetrics().getHeight())));
1959 g.drawString(MessageManager.getString("label.label"), 0, 0);
1964 Color minCol = gcol.getMinColour();
1966 g.fillRect(0, 0, s1, height);
1969 g.setColor(Color.white);
1970 g.fillRect(s1, 0, e1 - s1, height);
1972 g.setColor(gcol.getMaxColour());
1973 g.fillRect(0, e1, width - e1, height);