2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.SequenceI;
27 import jalview.datamodel.features.FeatureMatcher;
28 import jalview.datamodel.features.FeatureMatcherI;
29 import jalview.datamodel.features.FeatureMatcherSet;
30 import jalview.datamodel.features.FeatureMatcherSetI;
31 import jalview.gui.Help.HelpId;
32 import jalview.gui.JalviewColourChooser.ColourChooserListener;
33 import jalview.io.JalviewFileChooser;
34 import jalview.io.JalviewFileView;
35 import jalview.schemes.FeatureColour;
36 import jalview.util.MessageManager;
37 import jalview.util.Platform;
38 import jalview.util.dialogrunner.RunResponse;
39 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
40 import jalview.xml.binding.jalview.JalviewUserColours;
41 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
42 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
43 import jalview.xml.binding.jalview.ObjectFactory;
45 import java.awt.BorderLayout;
46 import java.awt.Color;
47 import java.awt.Component;
48 import java.awt.Dimension;
50 import java.awt.Graphics;
51 import java.awt.GridLayout;
52 import java.awt.Point;
53 import java.awt.Rectangle;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.ActionListener;
56 import java.awt.event.ItemEvent;
57 import java.awt.event.ItemListener;
58 import java.awt.event.MouseAdapter;
59 import java.awt.event.MouseEvent;
60 import java.awt.event.MouseMotionAdapter;
61 import java.beans.PropertyChangeEvent;
62 import java.beans.PropertyChangeListener;
64 import java.io.FileInputStream;
65 import java.io.FileOutputStream;
66 import java.io.InputStreamReader;
67 import java.io.OutputStreamWriter;
68 import java.io.PrintWriter;
69 import java.util.Arrays;
70 import java.util.Comparator;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.Hashtable;
74 import java.util.Iterator;
75 import java.util.List;
79 import javax.help.HelpSetException;
80 import javax.swing.AbstractCellEditor;
81 import javax.swing.BorderFactory;
82 import javax.swing.Icon;
83 import javax.swing.JButton;
84 import javax.swing.JCheckBox;
85 import javax.swing.JCheckBoxMenuItem;
86 import javax.swing.JInternalFrame;
87 import javax.swing.JLabel;
88 import javax.swing.JLayeredPane;
89 import javax.swing.JMenuItem;
90 import javax.swing.JPanel;
91 import javax.swing.JPopupMenu;
92 import javax.swing.JScrollPane;
93 import javax.swing.JSlider;
94 import javax.swing.JTable;
95 import javax.swing.ListSelectionModel;
96 import javax.swing.SwingConstants;
97 import javax.swing.ToolTipManager;
98 import javax.swing.border.Border;
99 import javax.swing.event.ChangeEvent;
100 import javax.swing.event.ChangeListener;
101 import javax.swing.plaf.TableUI;
102 import javax.swing.table.AbstractTableModel;
103 import javax.swing.table.TableCellEditor;
104 import javax.swing.table.TableCellRenderer;
105 import javax.swing.table.TableColumn;
106 import javax.xml.bind.JAXBContext;
107 import javax.xml.bind.JAXBElement;
108 import javax.xml.bind.Marshaller;
109 import javax.xml.stream.XMLInputFactory;
110 import javax.xml.stream.XMLStreamReader;
112 public class FeatureSettings extends JPanel
113 implements FeatureSettingsControllerI
115 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
116 .getString("label.sequence_feature_colours");
119 * column indices of fields in Feature Settings table
121 static final int TYPE_COLUMN = 0;
123 static final int COLOUR_COLUMN = 1;
125 static final int FILTER_COLUMN = 2;
127 static final int SHOW_COLUMN = 3;
129 private static final int COLUMN_COUNT = 4;
131 private static final int MIN_WIDTH = 400;
133 private static final int MIN_HEIGHT = 400;
135 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
137 final FeatureRenderer fr;
139 public final AlignFrame af;
142 * 'original' fields hold settings to restore on Cancel
144 Object[][] originalData;
146 float originalTransparency;
148 Map<String, FeatureMatcherSetI> originalFilters;
150 final JInternalFrame frame;
152 JScrollPane scrollPane = new JScrollPane();
158 JSlider transparency = new JSlider();
161 * when true, constructor is still executing - so ignore UI events
163 protected volatile boolean inConstruction = true;
165 int selectedRow = -1;
167 JButton fetchDAS = new JButton();
169 JButton saveDAS = new JButton();
171 JButton cancelDAS = new JButton();
173 boolean resettingTable = false;
176 * true when Feature Settings are updating from feature renderer
178 boolean handlingUpdate = false;
181 * holds {featureCount, totalExtent} for each feature type
183 Map<String, float[]> typeWidth = null;
190 public FeatureSettings(AlignFrame alignFrame)
192 this.af = alignFrame;
193 fr = af.getFeatureRenderer();
195 // save transparency for restore on Cancel
196 originalTransparency = fr.getTransparency();
197 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
198 transparency.setMaximum(100 - originalTransparencyAsPercent);
200 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
205 } catch (Exception ex)
207 ex.printStackTrace();
213 public String getToolTipText(MouseEvent e)
216 int column = table.columnAtPoint(e.getPoint());
217 int row = table.rowAtPoint(e.getPoint());
222 tip = JvSwingUtils.wrapTooltip(true, MessageManager
223 .getString("label.feature_settings_click_drag"));
226 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
228 tip = getColorTooltip(colour, true);
231 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
234 ? MessageManager.getString("label.filters_tooltip")
246 * Position the tooltip near the bottom edge of, and half way across, the
250 public Point getToolTipLocation(MouseEvent e)
252 Point point = e.getPoint();
253 int column = table.columnAtPoint(point);
254 int row = table.rowAtPoint(point);
255 Rectangle r = getCellRect(row, column, false);
256 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
261 // next line is needed to avoid (quiet) exceptions thrown
262 // when column ordering changes so that the above constants
264 table.getTableHeader().setReorderingAllowed(false); // BH 2018
266 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
267 ToolTipManager.sharedInstance().registerComponent(table);
269 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
270 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
272 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
273 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
275 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
276 new ColorRenderer(), new ColorEditor(this));
277 table.addColumn(colourColumn);
279 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
280 new FilterRenderer(), new FilterEditor(this));
281 table.addColumn(filterColumn);
283 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
285 table.addMouseListener(new MouseAdapter()
288 public void mousePressed(MouseEvent evt)
290 selectedRow = table.rowAtPoint(evt.getPoint());
291 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
292 if (evt.isPopupTrigger())
294 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
295 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
298 else if (evt.getClickCount() == 2)
300 boolean invertSelection = evt.isAltDown();
301 boolean toggleSelection = Platform.isControlDown(evt);
302 boolean extendSelection = evt.isShiftDown();
303 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
304 invertSelection, extendSelection, toggleSelection, type);
308 // isPopupTrigger fires on mouseReleased on Windows
310 public void mouseReleased(MouseEvent evt)
312 selectedRow = table.rowAtPoint(evt.getPoint());
313 if (evt.isPopupTrigger())
315 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
316 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
317 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
323 table.addMouseMotionListener(new MouseMotionAdapter()
326 public void mouseDragged(MouseEvent evt)
328 int newRow = table.rowAtPoint(evt.getPoint());
329 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
332 * reposition 'selectedRow' to 'newRow' (the dragged to location)
333 * this could be more than one row away for a very fast drag action
334 * so just swap it with adjacent rows until we get it there
336 Object[][] data = ((FeatureTableModel) table.getModel())
338 int direction = newRow < selectedRow ? -1 : 1;
339 for (int i = selectedRow; i != newRow; i += direction)
341 Object[] temp = data[i];
342 data[i] = data[i + direction];
343 data[i + direction] = temp;
345 updateFeatureRenderer(data);
347 selectedRow = newRow;
351 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
352 // MessageManager.getString("label.feature_settings_click_drag")));
353 scrollPane.setViewportView(table);
355 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
357 fr.findAllFeatures(true); // display everything!
360 discoverAllFeatureData();
361 final PropertyChangeListener change;
362 final FeatureSettings fs = this;
363 fr.addPropertyChangeListener(change = new PropertyChangeListener()
366 public void propertyChange(PropertyChangeEvent evt)
368 if (!fs.resettingTable && !fs.handlingUpdate)
370 fs.handlingUpdate = true;
372 // new groups may be added with new sequence feature types only
373 fs.handlingUpdate = false;
379 frame = new JInternalFrame();
380 frame.setContentPane(this);
381 if (Platform.isAMac())
383 Desktop.addInternalFrame(frame,
384 MessageManager.getString("label.sequence_feature_settings"),
389 Desktop.addInternalFrame(frame,
390 MessageManager.getString("label.sequence_feature_settings"),
393 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
395 frame.addInternalFrameListener(
396 new javax.swing.event.InternalFrameAdapter()
399 public void internalFrameClosed(
400 javax.swing.event.InternalFrameEvent evt)
402 fr.removePropertyChangeListener(change);
405 frame.setLayer(JLayeredPane.PALETTE_LAYER);
406 inConstruction = false;
409 protected void popupSort(final int rowSelected, final String type,
410 final Object typeCol, final Map<String, float[][]> minmax, int x,
413 final FeatureColourI featureColour = (FeatureColourI) typeCol;
415 JPopupMenu men = new JPopupMenu(MessageManager
416 .formatMessage("label.settings_for_param", new String[]
418 JMenuItem scr = new JMenuItem(
419 MessageManager.getString("label.sort_by_score"));
421 final FeatureSettings me = this;
422 scr.addActionListener(new ActionListener()
426 public void actionPerformed(ActionEvent e)
429 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
434 JMenuItem dens = new JMenuItem(
435 MessageManager.getString("label.sort_by_density"));
436 dens.addActionListener(new ActionListener()
440 public void actionPerformed(ActionEvent e)
443 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
451 * variable colour options include colour by label, by score,
452 * by selected attribute text, or attribute value
454 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
455 MessageManager.getString("label.variable_colour"));
456 variableColourCB.setSelected(!featureColour.isSimpleColour());
457 men.add(variableColourCB);
460 * checkbox action listener doubles up as listener to OK
461 * from the variable colour / filters dialog
463 variableColourCB.addActionListener(new ActionListener()
466 public void actionPerformed(ActionEvent e)
468 if (e.getSource() == variableColourCB)
470 if (featureColour.isSimpleColour())
473 * toggle simple colour to variable colour - show dialog
475 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
476 fc.addActionListener(this);
481 * toggle variable to simple colour - show colour chooser
483 String title = MessageManager.getString("label.select_colour");
484 ColourChooserListener listener = new ColourChooserListener()
487 public void colourSelected(Color c)
489 table.setValueAt(new FeatureColour(c), rowSelected,
492 me.updateFeatureRenderer(
493 ((FeatureTableModel) table.getModel()).getData(),
497 JalviewColourChooser.showColourChooser(me, title, featureColour.getMaxColour(), listener);
501 if (e.getSource() instanceof FeatureTypeSettings)
504 * update after OK in feature colour dialog; the updated
505 * colour will have already been set in the FeatureRenderer
507 FeatureColourI fci = fr.getFeatureColours().get(type);
508 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
516 JMenuItem selCols = new JMenuItem(
517 MessageManager.getString("label.select_columns_containing"));
518 selCols.addActionListener(new ActionListener()
521 public void actionPerformed(ActionEvent arg0)
523 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
527 JMenuItem clearCols = new JMenuItem(MessageManager
528 .getString("label.select_columns_not_containing"));
529 clearCols.addActionListener(new ActionListener()
532 public void actionPerformed(ActionEvent arg0)
534 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
538 JMenuItem hideCols = new JMenuItem(
539 MessageManager.getString("label.hide_columns_containing"));
540 hideCols.addActionListener(new ActionListener()
543 public void actionPerformed(ActionEvent arg0)
545 fr.ap.alignFrame.hideFeatureColumns(type, true);
548 JMenuItem hideOtherCols = new JMenuItem(
549 MessageManager.getString("label.hide_columns_not_containing"));
550 hideOtherCols.addActionListener(new ActionListener()
553 public void actionPerformed(ActionEvent arg0)
555 fr.ap.alignFrame.hideFeatureColumns(type, false);
561 men.add(hideOtherCols);
562 men.show(table, x, y);
566 synchronized public void discoverAllFeatureData()
568 Set<String> allGroups = new HashSet<>();
569 AlignmentI alignment = af.getViewport().getAlignment();
571 for (int i = 0; i < alignment.getHeight(); i++)
573 SequenceI seq = alignment.getSequenceAt(i);
574 for (String group : seq.getFeatures().getFeatureGroups(true))
576 if (group != null && !allGroups.contains(group))
578 allGroups.add(group);
579 checkGroupState(group);
590 * Synchronise gui group list and check visibility of group
593 * @return true if group is visible
595 private boolean checkGroupState(String group)
597 boolean visible = fr.checkGroupVisibility(group, true);
599 for (int g = 0; g < groupPanel.getComponentCount(); g++)
601 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
603 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
608 final String grp = group;
609 final JCheckBox check = new JCheckBox(group, visible);
610 check.setFont(new Font("Serif", Font.BOLD, 12));
611 check.setToolTipText(group);
612 check.addItemListener(new ItemListener()
615 public void itemStateChanged(ItemEvent evt)
617 fr.setGroupVisibility(check.getText(), check.isSelected());
618 resetTable(new String[] { grp });
619 af.alignPanel.paintAlignment(true, true);
622 groupPanel.add(check);
626 synchronized void resetTable(String[] groupChanged)
632 resettingTable = true;
633 typeWidth = new Hashtable<>();
634 // TODO: change avWidth calculation to 'per-sequence' average and use long
637 Set<String> displayableTypes = new HashSet<>();
638 Set<String> foundGroups = new HashSet<>();
641 * determine which feature types may be visible depending on
642 * which groups are selected, and recompute average width data
644 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
647 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
650 * get the sequence's groups for positional features
651 * and keep track of which groups are visible
653 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
654 Set<String> visibleGroups = new HashSet<>();
655 for (String group : groups)
657 if (group == null || checkGroupState(group))
659 visibleGroups.add(group);
662 foundGroups.addAll(groups);
665 * get distinct feature types for visible groups
666 * record distinct visible types, and their count and total length
668 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
669 visibleGroups.toArray(new String[visibleGroups.size()]));
670 for (String type : types)
672 displayableTypes.add(type);
673 float[] avWidth = typeWidth.get(type);
676 avWidth = new float[2];
677 typeWidth.put(type, avWidth);
679 // todo this could include features with a non-visible group
680 // - do we greatly care?
681 // todo should we include non-displayable features here, and only
682 // update when features are added?
683 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
684 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
688 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
691 if (fr.hasRenderOrder())
695 fr.findAllFeatures(groupChanged != null); // prod to update
696 // colourschemes. but don't
698 // First add the checks in the previous render order,
699 // in case the window has been closed and reopened
701 List<String> frl = fr.getRenderOrder();
702 for (int ro = frl.size() - 1; ro > -1; ro--)
704 String type = frl.get(ro);
706 if (!displayableTypes.contains(type))
711 data[dataIndex][TYPE_COLUMN] = type;
712 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
713 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
714 data[dataIndex][FILTER_COLUMN] = featureFilter == null
715 ? new FeatureMatcherSet()
717 data[dataIndex][SHOW_COLUMN] = new Boolean(
718 af.getViewport().getFeaturesDisplayed().isVisible(type));
720 displayableTypes.remove(type);
725 * process any extra features belonging only to
726 * a group which was just selected
728 while (!displayableTypes.isEmpty())
730 String type = displayableTypes.iterator().next();
731 data[dataIndex][TYPE_COLUMN] = type;
733 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
734 if (data[dataIndex][COLOUR_COLUMN] == null)
736 // "Colour has been updated in another view!!"
737 fr.clearRenderOrder();
740 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
741 data[dataIndex][FILTER_COLUMN] = featureFilter == null
742 ? new FeatureMatcherSet()
744 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
746 displayableTypes.remove(type);
749 if (originalData == null)
751 originalData = new Object[data.length][COLUMN_COUNT];
752 for (int i = 0; i < data.length; i++)
754 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
759 updateOriginalData(data);
762 table.setModel(new FeatureTableModel(data));
763 table.getColumnModel().getColumn(0).setPreferredWidth(200);
765 groupPanel.setLayout(
766 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
767 pruneGroups(foundGroups);
768 groupPanel.validate();
770 updateFeatureRenderer(data, groupChanged != null);
771 resettingTable = false;
775 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
776 * have been made outwith this dialog
778 * <li>a new feature type added (and made visible)</li>
779 * <li>a feature colour changed (in the Amend Features dialog)</li>
784 protected void updateOriginalData(Object[][] foundData)
786 // todo LinkedHashMap instead of Object[][] would be nice
788 Object[][] currentData = ((FeatureTableModel) table.getModel())
790 for (Object[] row : foundData)
792 String type = (String) row[TYPE_COLUMN];
793 boolean found = false;
794 for (Object[] current : currentData)
796 if (type.equals(current[TYPE_COLUMN]))
800 * currently dependent on object equality here;
801 * really need an equals method on FeatureColour
803 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
806 * feature colour has changed externally - update originalData
808 for (Object[] original : originalData)
810 if (type.equals(original[TYPE_COLUMN]))
812 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
823 * new feature detected - add to original data (on top)
825 Object[][] newData = new Object[originalData.length
827 for (int i = 0; i < originalData.length; i++)
829 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
833 originalData = newData;
839 * Remove from the groups panel any checkboxes for groups that are not in the
840 * foundGroups set. This enables removing a group from the display when the last
841 * feature in that group is deleted.
845 protected void pruneGroups(Set<String> foundGroups)
847 for (int g = 0; g < groupPanel.getComponentCount(); g++)
849 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
850 if (!foundGroups.contains(checkbox.getText()))
852 groupPanel.remove(checkbox);
858 * reorder data based on the featureRenderers global priority list.
862 private void ensureOrder(Object[][] data)
864 boolean sort = false;
865 float[] order = new float[data.length];
866 for (int i = 0; i < order.length; i++)
868 order[i] = fr.getOrder(data[i][0].toString());
871 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
875 sort = sort || order[i - 1] > order[i];
880 jalview.util.QuickSort.sort(order, data);
885 * Offers a file chooser dialog, and then loads the feature colours and
886 * filters from file in XML format and unmarshals to Jalview feature settings
890 JalviewFileChooser chooser = new JalviewFileChooser("fc",
891 SEQUENCE_FEATURE_COLOURS);
892 chooser.setFileView(new JalviewFileView());
893 chooser.setDialogTitle(
894 MessageManager.getString("label.load_feature_colours"));
895 chooser.setToolTipText(MessageManager.getString("action.load"));
896 chooser.response(new RunResponse(JalviewFileChooser.APPROVE_OPTION){
900 File file = chooser.getSelectedFile();
903 chooser.showOpenDialog(this);
907 * Loads feature colours and filters from XML stored in the given file
915 InputStreamReader in = new InputStreamReader(
916 new FileInputStream(file), "UTF-8");
918 JAXBContext jc = JAXBContext
919 .newInstance("jalview.xml.binding.jalview");
920 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
921 XMLStreamReader streamReader = XMLInputFactory.newInstance()
922 .createXMLStreamReader(in);
923 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
924 JalviewUserColours.class);
925 JalviewUserColours jucs = jbe.getValue();
927 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
930 * load feature colours
932 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
934 Colour newcol = jucs.getColour().get(i);
935 FeatureColourI colour = jalview.project.Jalview2XML
936 .parseColour(newcol);
937 fr.setColour(newcol.getName(), colour);
938 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
942 * load feature filters; loaded filters will replace any that are
943 * currently defined, other defined filters are left unchanged
945 for (int i = 0; i < jucs.getFilter().size(); i++)
947 Filter filterModel = jucs.getFilter().get(i);
948 String featureType = filterModel.getFeatureType();
949 FeatureMatcherSetI filter = jalview.project.Jalview2XML
950 .parseFilter(featureType, filterModel.getMatcherSet());
951 if (!filter.isEmpty())
953 fr.setFeatureFilter(featureType, filter);
958 * update feature settings table
963 Object[][] data = ((FeatureTableModel) table.getModel())
966 updateFeatureRenderer(data, false);
969 } catch (Exception ex)
971 System.out.println("Error loading User Colour File\n" + ex);
976 * Offers a file chooser dialog, and then saves the current feature colours
977 * and any filters to the selected file in XML format
981 JalviewFileChooser chooser = new JalviewFileChooser("fc",
982 SEQUENCE_FEATURE_COLOURS);
983 chooser.setFileView(new JalviewFileView());
984 chooser.setDialogTitle(
985 MessageManager.getString("label.save_feature_colours"));
986 chooser.setToolTipText(MessageManager.getString("action.save"));
987 int option = chooser.showSaveDialog(this);
988 if (option == JalviewFileChooser.APPROVE_OPTION)
990 File file = chooser.getSelectedFile();
996 * Saves feature colours and filters to the given file
1000 void save(File file)
1002 JalviewUserColours ucs = new JalviewUserColours();
1003 ucs.setSchemeName("Sequence Features");
1006 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1007 new FileOutputStream(file), "UTF-8"));
1010 * sort feature types by colour order, from 0 (highest)
1013 Set<String> fr_colours = fr.getAllFeatureColours();
1014 String[] sortedTypes = fr_colours
1015 .toArray(new String[fr_colours.size()]);
1016 Arrays.sort(sortedTypes, new Comparator<String>()
1019 public int compare(String type1, String type2)
1021 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1026 * save feature colours
1028 for (String featureType : sortedTypes)
1030 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1031 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1033 ucs.getColour().add(col);
1037 * save any feature filters
1039 for (String featureType : sortedTypes)
1041 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1042 if (filter != null && !filter.isEmpty())
1044 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1045 FeatureMatcherI firstMatcher = iterator.next();
1046 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1047 .marshalFilter(firstMatcher, iterator,
1049 Filter filterModel = new Filter();
1050 filterModel.setFeatureType(featureType);
1051 filterModel.setMatcherSet(ms);
1052 ucs.getFilter().add(filterModel);
1055 JAXBContext jaxbContext = JAXBContext
1056 .newInstance(JalviewUserColours.class);
1057 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1058 jaxbMarshaller.marshal(
1059 new ObjectFactory().createJalviewUserColours(ucs), out);
1061 // jaxbMarshaller.marshal(object, pout);
1062 // marshaller.marshal(object);
1065 // ucs.marshal(out);
1067 } catch (Exception ex)
1069 ex.printStackTrace();
1073 public void invertSelection()
1075 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1076 for (int i = 0; i < data.length; i++)
1078 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1080 updateFeatureRenderer(data, true);
1084 public void orderByAvWidth()
1086 if (table == null || table.getModel() == null)
1090 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1091 float[] width = new float[data.length];
1095 for (int i = 0; i < data.length; i++)
1097 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1100 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1101 // weight - but have to make per
1102 // sequence, too (awidth[2])
1103 // if (width[i]==1) // hack to distinguish single width sequences.
1114 boolean sort = false;
1115 for (int i = 0; i < width.length; i++)
1117 // awidth = (float[]) typeWidth.get(data[i][0]);
1120 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1123 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1129 width[i] /= max; // normalize
1130 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1134 sort = sort || width[i - 1] > width[i];
1139 jalview.util.QuickSort.sort(width, data);
1140 // update global priority order
1143 updateFeatureRenderer(data, false);
1151 frame.setClosed(true);
1152 } catch (Exception exe)
1158 public void updateFeatureRenderer(Object[][] data)
1160 updateFeatureRenderer(data, true);
1164 * Update the priority order of features; only repaint if this changed the order
1165 * of visible features
1170 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1172 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1174 if (fr.setFeaturePriority(rowData, visibleNew))
1176 af.alignPanel.paintAlignment(true, true);
1181 * Converts table data into an array of data beans
1183 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1185 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1186 for (int i = 0; i < data.length; i++)
1188 String type = (String) data[i][TYPE_COLUMN];
1189 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1190 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1191 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1192 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1198 private void jbInit() throws Exception
1200 this.setLayout(new BorderLayout());
1202 JPanel settingsPane = new JPanel();
1203 settingsPane.setLayout(new BorderLayout());
1205 JPanel bigPanel = new JPanel();
1206 bigPanel.setLayout(new BorderLayout());
1208 groupPanel = new JPanel();
1209 bigPanel.add(groupPanel, BorderLayout.NORTH);
1211 JButton invert = new JButton(
1212 MessageManager.getString("label.invert_selection"));
1213 invert.setFont(JvSwingUtils.getLabelFont());
1214 invert.addActionListener(new ActionListener()
1217 public void actionPerformed(ActionEvent e)
1223 JButton optimizeOrder = new JButton(
1224 MessageManager.getString("label.optimise_order"));
1225 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1226 optimizeOrder.addActionListener(new ActionListener()
1229 public void actionPerformed(ActionEvent e)
1235 JButton sortByScore = new JButton(
1236 MessageManager.getString("label.seq_sort_by_score"));
1237 sortByScore.setFont(JvSwingUtils.getLabelFont());
1238 sortByScore.addActionListener(new ActionListener()
1241 public void actionPerformed(ActionEvent e)
1243 af.avc.sortAlignmentByFeatureScore(null);
1246 JButton sortByDens = new JButton(
1247 MessageManager.getString("label.sequence_sort_by_density"));
1248 sortByDens.setFont(JvSwingUtils.getLabelFont());
1249 sortByDens.addActionListener(new ActionListener()
1252 public void actionPerformed(ActionEvent e)
1254 af.avc.sortAlignmentByFeatureDensity(null);
1258 JButton help = new JButton(MessageManager.getString("action.help"));
1259 help.setFont(JvSwingUtils.getLabelFont());
1260 help.addActionListener(new ActionListener()
1263 public void actionPerformed(ActionEvent e)
1267 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1268 } catch (HelpSetException e1)
1270 e1.printStackTrace();
1274 help.setFont(JvSwingUtils.getLabelFont());
1275 help.setText(MessageManager.getString("action.help"));
1276 help.addActionListener(new ActionListener()
1279 public void actionPerformed(ActionEvent e)
1283 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1284 } catch (HelpSetException e1)
1286 e1.printStackTrace();
1291 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1292 cancel.setFont(JvSwingUtils.getLabelFont());
1293 cancel.addActionListener(new ActionListener()
1296 public void actionPerformed(ActionEvent e)
1298 fr.setTransparency(originalTransparency);
1299 fr.setFeatureFilters(originalFilters);
1300 updateFeatureRenderer(originalData);
1305 JButton ok = new JButton(MessageManager.getString("action.ok"));
1306 ok.setFont(JvSwingUtils.getLabelFont());
1307 ok.addActionListener(new ActionListener()
1310 public void actionPerformed(ActionEvent e)
1316 JButton loadColours = new JButton(
1317 MessageManager.getString("label.load_colours"));
1318 loadColours.setFont(JvSwingUtils.getLabelFont());
1319 loadColours.setToolTipText(
1320 MessageManager.getString("label.load_colours_tooltip"));
1321 loadColours.addActionListener(new ActionListener()
1324 public void actionPerformed(ActionEvent e)
1330 JButton saveColours = new JButton(
1331 MessageManager.getString("label.save_colours"));
1332 saveColours.setFont(JvSwingUtils.getLabelFont());
1333 saveColours.setToolTipText(
1334 MessageManager.getString("label.save_colours_tooltip"));
1335 saveColours.addActionListener(new ActionListener()
1338 public void actionPerformed(ActionEvent e)
1343 transparency.addChangeListener(new ChangeListener()
1346 public void stateChanged(ChangeEvent evt)
1348 if (!inConstruction)
1350 fr.setTransparency((100 - transparency.getValue()) / 100f);
1351 af.alignPanel.paintAlignment(true, true);
1356 transparency.setMaximum(70);
1357 transparency.setToolTipText(
1358 MessageManager.getString("label.transparency_tip"));
1360 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1361 bigPanel.add(transPanel, BorderLayout.SOUTH);
1363 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1364 transbuttons.add(optimizeOrder);
1365 transbuttons.add(invert);
1366 transbuttons.add(sortByScore);
1367 transbuttons.add(sortByDens);
1368 transbuttons.add(help);
1369 transPanel.add(transparency);
1370 transPanel.add(transbuttons);
1372 JPanel buttonPanel = new JPanel();
1373 buttonPanel.add(ok);
1374 buttonPanel.add(cancel);
1375 buttonPanel.add(loadColours);
1376 buttonPanel.add(saveColours);
1377 bigPanel.add(scrollPane, BorderLayout.CENTER);
1378 settingsPane.add(bigPanel, BorderLayout.CENTER);
1379 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1380 this.add(settingsPane);
1384 * Answers a suitable tooltip to show on the colour cell of the table
1388 * if true include 'click to edit' and similar text
1391 public static String getColorTooltip(FeatureColourI fcol,
1398 if (fcol.isSimpleColour())
1400 return withHint ? BASE_TOOLTIP : null;
1402 String description = fcol.getDescription();
1403 description = description.replaceAll("<", "<");
1404 description = description.replaceAll(">", ">");
1405 StringBuilder tt = new StringBuilder(description);
1408 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1410 return JvSwingUtils.wrapTooltip(true, tt.toString());
1413 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1416 boolean thr = false;
1417 StringBuilder tx = new StringBuilder();
1419 if (gcol.isColourByAttribute())
1421 tx.append(FeatureMatcher
1422 .toAttributeDisplayName(gcol.getAttributeName()));
1424 else if (!gcol.isColourByLabel())
1426 tx.append(MessageManager.getString("label.score"));
1429 if (gcol.isAboveThreshold())
1434 if (gcol.isBelowThreshold())
1439 if (gcol.isColourByLabel())
1445 if (!gcol.isColourByAttribute())
1453 Color newColor = gcol.getMaxColour();
1454 comp.setBackground(newColor);
1455 // System.err.println("Width is " + w / 2);
1456 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1457 comp.setIcon(ficon);
1458 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1459 // + newColor.getGreen() + ", " + newColor.getBlue()
1460 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1461 // + ", " + minCol.getBlue() + ")");
1463 comp.setHorizontalAlignment(SwingConstants.CENTER);
1464 comp.setText(tx.toString());
1467 // ///////////////////////////////////////////////////////////////////////
1468 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1469 // ///////////////////////////////////////////////////////////////////////
1470 class FeatureTableModel extends AbstractTableModel
1472 private String[] columnNames = {
1473 MessageManager.getString("label.feature_type"),
1474 MessageManager.getString("action.colour"),
1475 MessageManager.getString("label.filter"),
1476 MessageManager.getString("label.show") };
1478 private Object[][] data;
1480 FeatureTableModel(Object[][] data)
1485 public Object[][] getData()
1490 public void setData(Object[][] data)
1496 public int getColumnCount()
1498 return columnNames.length;
1501 public Object[] getRow(int row)
1507 public int getRowCount()
1513 public String getColumnName(int col)
1515 return columnNames[col];
1519 public Object getValueAt(int row, int col)
1521 return data[row][col];
1525 * Answers the class of the object in column c of the first row of the table
1528 public Class<?> getColumnClass(int c)
1530 Object v = getValueAt(0, c);
1531 return v == null ? null : v.getClass();
1535 public boolean isCellEditable(int row, int col)
1537 return col == 0 ? false : true;
1541 public void setValueAt(Object value, int row, int col)
1543 data[row][col] = value;
1544 fireTableCellUpdated(row, col);
1545 updateFeatureRenderer(data);
1550 class ColorRenderer extends JLabel implements TableCellRenderer
1552 Border unselectedBorder = null;
1554 Border selectedBorder = null;
1556 public ColorRenderer()
1558 setOpaque(true); // MUST do this for background to show up.
1559 setHorizontalTextPosition(SwingConstants.CENTER);
1560 setVerticalTextPosition(SwingConstants.CENTER);
1564 public Component getTableCellRendererComponent(JTable tbl, Object color,
1565 boolean isSelected, boolean hasFocus, int row, int column)
1567 FeatureColourI cellColour = (FeatureColourI) color;
1569 setBackground(tbl.getBackground());
1570 if (!cellColour.isSimpleColour())
1572 Rectangle cr = tbl.getCellRect(row, column, false);
1573 FeatureSettings.renderGraduatedColor(this, cellColour,
1574 (int) cr.getWidth(), (int) cr.getHeight());
1580 setBackground(cellColour.getColour());
1584 if (selectedBorder == null)
1586 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1587 tbl.getSelectionBackground());
1589 setBorder(selectedBorder);
1593 if (unselectedBorder == null)
1595 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1596 tbl.getBackground());
1598 setBorder(unselectedBorder);
1605 class FilterRenderer extends JLabel implements TableCellRenderer
1607 javax.swing.border.Border unselectedBorder = null;
1609 javax.swing.border.Border selectedBorder = null;
1611 public FilterRenderer()
1613 setOpaque(true); // MUST do this for background to show up.
1614 setHorizontalTextPosition(SwingConstants.CENTER);
1615 setVerticalTextPosition(SwingConstants.CENTER);
1619 public Component getTableCellRendererComponent(JTable tbl,
1620 Object filter, boolean isSelected, boolean hasFocus, int row,
1623 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1625 String asText = theFilter.toString();
1626 setBackground(tbl.getBackground());
1627 this.setText(asText);
1632 if (selectedBorder == null)
1634 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1635 tbl.getSelectionBackground());
1637 setBorder(selectedBorder);
1641 if (unselectedBorder == null)
1643 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1644 tbl.getBackground());
1646 setBorder(unselectedBorder);
1654 * update comp using rendering settings from gcol
1659 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1661 int w = comp.getWidth(), h = comp.getHeight();
1664 w = (int) comp.getPreferredSize().getWidth();
1665 h = (int) comp.getPreferredSize().getHeight();
1672 renderGraduatedColor(comp, gcol, w, h);
1675 class ColorEditor extends AbstractCellEditor
1676 implements TableCellEditor, ActionListener
1680 FeatureColourI currentColor;
1682 FeatureTypeSettings chooser;
1688 protected static final String EDIT = "edit";
1690 int rowSelected = 0;
1692 public ColorEditor(FeatureSettings fs)
1695 // Set up the editor (from the table's point of view),
1696 // which is a button.
1697 // This button brings up the color chooser dialog,
1698 // which is the editor from the user's point of view.
1699 button = new JButton();
1700 button.setActionCommand(EDIT);
1701 button.addActionListener(this);
1702 button.setBorderPainted(false);
1706 * Handles events from the editor button, and from the colour/filters
1707 * dialog's OK button
1710 public void actionPerformed(ActionEvent e)
1712 if (button == e.getSource())
1714 if (currentColor.isSimpleColour())
1717 * simple colour chooser
1719 String ttl = MessageManager.getString("label.select_colour");
1720 ColourChooserListener listener = new ColourChooserListener() {
1722 public void colourSelected(Color c)
1724 currentColor = new FeatureColour(c);
1725 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1728 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1733 * variable colour and filters dialog
1735 chooser = new FeatureTypeSettings(me.fr, type);
1740 chooser.setRequestFocusEnabled(true);
1741 chooser.requestFocus();
1743 chooser.addActionListener(this);
1744 // Make the renderer reappear.
1745 fireEditingStopped();
1751 * after OK in variable colour dialog, any changes to colour
1752 * (or filters!) are already set in FeatureRenderer, so just
1753 * update table data without triggering updateFeatureRenderer
1755 currentColor = fr.getFeatureColours().get(type);
1756 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1757 if (currentFilter == null)
1759 currentFilter = new FeatureMatcherSet();
1761 Object[] data = ((FeatureTableModel) table.getModel())
1762 .getData()[rowSelected];
1763 data[COLOUR_COLUMN] = currentColor;
1764 data[FILTER_COLUMN] = currentFilter;
1766 fireEditingStopped();
1767 me.table.validate();
1771 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1773 public Object getCellEditorValue()
1775 return currentColor;
1778 // Implement the one method defined by TableCellEditor.
1780 public Component getTableCellEditorComponent(JTable theTable, Object value,
1781 boolean isSelected, int row, int column)
1783 currentColor = (FeatureColourI) value;
1784 this.rowSelected = row;
1785 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1786 button.setOpaque(true);
1787 button.setBackground(me.getBackground());
1788 if (!currentColor.isSimpleColour())
1790 JLabel btn = new JLabel();
1791 btn.setSize(button.getSize());
1792 FeatureSettings.renderGraduatedColor(btn, currentColor);
1793 button.setBackground(btn.getBackground());
1794 button.setIcon(btn.getIcon());
1795 button.setText(btn.getText());
1800 button.setIcon(null);
1801 button.setBackground(currentColor.getColour());
1808 * The cell editor for the Filter column. It displays the text of any filters
1809 * for the feature type in that row (in full as a tooltip, possible abbreviated
1810 * as display text). On click in the cell, opens the Feature Display Settings
1811 * dialog at the Filters tab.
1813 class FilterEditor extends AbstractCellEditor
1814 implements TableCellEditor, ActionListener
1818 FeatureMatcherSetI currentFilter;
1826 protected static final String EDIT = "edit";
1828 int rowSelected = 0;
1830 public FilterEditor(FeatureSettings me)
1833 button = new JButton();
1834 button.setActionCommand(EDIT);
1835 button.addActionListener(this);
1836 button.setBorderPainted(false);
1840 * Handles events from the editor button
1843 public void actionPerformed(ActionEvent e)
1845 if (button == e.getSource())
1847 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1848 chooser.addActionListener(this);
1849 chooser.setRequestFocusEnabled(true);
1850 chooser.requestFocus();
1851 if (lastLocation != null)
1853 // todo open at its last position on screen
1854 chooser.setBounds(lastLocation.x, lastLocation.y,
1855 chooser.getWidth(), chooser.getHeight());
1858 fireEditingStopped();
1860 else if (e.getSource() instanceof Component)
1864 * after OK in variable colour dialog, any changes to filter
1865 * (or colours!) are already set in FeatureRenderer, so just
1866 * update table data without triggering updateFeatureRenderer
1868 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1869 currentFilter = me.fr.getFeatureFilter(type);
1870 if (currentFilter == null)
1872 currentFilter = new FeatureMatcherSet();
1874 Object[] data = ((FeatureTableModel) table.getModel())
1875 .getData()[rowSelected];
1876 data[COLOUR_COLUMN] = currentColor;
1877 data[FILTER_COLUMN] = currentFilter;
1878 fireEditingStopped();
1879 me.table.validate();
1884 public Object getCellEditorValue()
1886 return currentFilter;
1890 public Component getTableCellEditorComponent(JTable theTable, Object value,
1891 boolean isSelected, int row, int column)
1893 currentFilter = (FeatureMatcherSetI) value;
1894 this.rowSelected = row;
1895 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1896 button.setOpaque(true);
1897 button.setBackground(me.getBackground());
1898 button.setText(currentFilter.toString());
1899 button.setIcon(null);
1905 class FeatureIcon implements Icon
1907 FeatureColourI gcol;
1911 boolean midspace = false;
1913 int width = 50, height = 20;
1915 int s1, e1; // start and end of midpoint band for thresholded symbol
1917 Color mpcolour = Color.white;
1919 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1939 public int getIconWidth()
1945 public int getIconHeight()
1951 public void paintIcon(Component c, Graphics g, int x, int y)
1954 if (gcol.isColourByLabel())
1957 g.fillRect(0, 0, width, height);
1958 // need an icon here.
1959 g.setColor(gcol.getMaxColour());
1961 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1963 // g.setFont(g.getFont().deriveFont(
1964 // AffineTransform.getScaleInstance(
1965 // width/g.getFontMetrics().stringWidth("Label"),
1966 // height/g.getFontMetrics().getHeight())));
1968 g.drawString(MessageManager.getString("label.label"), 0, 0);
1973 Color minCol = gcol.getMinColour();
1975 g.fillRect(0, 0, s1, height);
1978 g.setColor(Color.white);
1979 g.fillRect(s1, 0, e1 - s1, height);
1981 g.setColor(gcol.getMaxColour());
1982 g.fillRect(0, e1, width - e1, height);