2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.bin.Jalview;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.SequenceI;
28 import jalview.datamodel.features.FeatureMatcher;
29 import jalview.datamodel.features.FeatureMatcherI;
30 import jalview.datamodel.features.FeatureMatcherSet;
31 import jalview.datamodel.features.FeatureMatcherSetI;
32 import jalview.gui.Help.HelpId;
33 import jalview.gui.JalviewColourChooser.ColourChooserListener;
34 import jalview.io.JalviewFileChooser;
35 import jalview.io.JalviewFileView;
36 import jalview.schemes.FeatureColour;
37 import jalview.util.MessageManager;
38 import jalview.util.Platform;
39 import jalview.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;
49 import java.awt.FlowLayout;
51 import java.awt.Graphics;
52 import java.awt.GridLayout;
53 import java.awt.Point;
54 import java.awt.Rectangle;
55 import java.awt.event.ActionEvent;
56 import java.awt.event.ActionListener;
57 import java.awt.event.ItemEvent;
58 import java.awt.event.ItemListener;
59 import java.awt.event.MouseAdapter;
60 import java.awt.event.MouseEvent;
61 import java.awt.event.MouseMotionAdapter;
62 import java.beans.PropertyChangeEvent;
63 import java.beans.PropertyChangeListener;
65 import java.io.FileInputStream;
66 import java.io.FileOutputStream;
67 import java.io.InputStreamReader;
68 import java.io.OutputStreamWriter;
69 import java.io.PrintWriter;
70 import java.util.Arrays;
71 import java.util.Comparator;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.Hashtable;
75 import java.util.Iterator;
76 import java.util.List;
80 import javax.help.HelpSetException;
81 import javax.swing.AbstractCellEditor;
82 import javax.swing.BorderFactory;
83 import javax.swing.Icon;
84 import javax.swing.JButton;
85 import javax.swing.JCheckBox;
86 import javax.swing.JCheckBoxMenuItem;
87 import javax.swing.JInternalFrame;
88 import javax.swing.JLabel;
89 import javax.swing.JLayeredPane;
90 import javax.swing.JMenuItem;
91 import javax.swing.JPanel;
92 import javax.swing.JPopupMenu;
93 import javax.swing.JScrollPane;
94 import javax.swing.JSlider;
95 import javax.swing.JTable;
96 import javax.swing.ListSelectionModel;
97 import javax.swing.SwingConstants;
98 import javax.swing.ToolTipManager;
99 import javax.swing.border.Border;
100 import javax.swing.event.ChangeEvent;
101 import javax.swing.event.ChangeListener;
102 import javax.swing.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")
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 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
260 ToolTipManager.sharedInstance().registerComponent(table);
262 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
263 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
265 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
266 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
268 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
269 new ColorRenderer(), new ColorEditor(this));
270 table.addColumn(colourColumn);
272 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
273 new FilterRenderer(), new FilterEditor(this));
274 table.addColumn(filterColumn);
276 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
278 table.addMouseListener(new MouseAdapter()
281 public void mousePressed(MouseEvent evt)
283 selectedRow = table.rowAtPoint(evt.getPoint());
284 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
285 if (evt.isPopupTrigger())
287 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
288 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
291 else if (evt.getClickCount() == 2)
293 boolean invertSelection = evt.isAltDown();
294 boolean toggleSelection = Platform.isControlDown(evt);
295 boolean extendSelection = evt.isShiftDown();
296 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
297 invertSelection, extendSelection, toggleSelection, type);
301 // isPopupTrigger fires on mouseReleased on Windows
303 public void mouseReleased(MouseEvent evt)
305 selectedRow = table.rowAtPoint(evt.getPoint());
306 if (evt.isPopupTrigger())
308 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
309 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
310 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
316 table.addMouseMotionListener(new MouseMotionAdapter()
319 public void mouseDragged(MouseEvent evt)
321 int newRow = table.rowAtPoint(evt.getPoint());
322 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
325 * reposition 'selectedRow' to 'newRow' (the dragged to location)
326 * this could be more than one row away for a very fast drag action
327 * so just swap it with adjacent rows until we get it there
329 Object[][] data = ((FeatureTableModel) table.getModel())
331 int direction = newRow < selectedRow ? -1 : 1;
332 for (int i = selectedRow; i != newRow; i += direction)
334 Object[] temp = data[i];
335 data[i] = data[i + direction];
336 data[i + direction] = temp;
338 updateFeatureRenderer(data);
340 selectedRow = newRow;
344 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
345 // MessageManager.getString("label.feature_settings_click_drag")));
346 scrollPane.setViewportView(table);
348 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
350 fr.findAllFeatures(true); // display everything!
353 discoverAllFeatureData();
354 final PropertyChangeListener change;
355 final FeatureSettings fs = this;
356 fr.addPropertyChangeListener(change = new PropertyChangeListener()
359 public void propertyChange(PropertyChangeEvent evt)
361 if (!fs.resettingTable && !fs.handlingUpdate)
363 fs.handlingUpdate = true;
365 // new groups may be added with new sequence feature types only
366 fs.handlingUpdate = false;
372 frame = new JInternalFrame();
373 frame.setContentPane(this);
374 if (Platform.isAMac())
376 Desktop.addInternalFrame(frame,
377 MessageManager.getString("label.sequence_feature_settings"),
382 Desktop.addInternalFrame(frame,
383 MessageManager.getString("label.sequence_feature_settings"),
386 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
388 frame.addInternalFrameListener(
389 new javax.swing.event.InternalFrameAdapter()
392 public void internalFrameClosed(
393 javax.swing.event.InternalFrameEvent evt)
395 fr.removePropertyChangeListener(change);
398 frame.setLayer(JLayeredPane.PALETTE_LAYER);
399 inConstruction = false;
402 protected void popupSort(final int rowSelected, final String type,
403 final Object typeCol, final Map<String, float[][]> minmax, int x,
406 final FeatureColourI featureColour = (FeatureColourI) typeCol;
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[]
444 * variable colour options include colour by label, by score,
445 * by selected attribute text, or attribute value
447 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
448 MessageManager.getString("label.variable_colour"));
449 variableColourCB.setSelected(!featureColour.isSimpleColour());
450 men.add(variableColourCB);
453 * checkbox action listener doubles up as listener to OK
454 * from the variable colour / filters dialog
456 variableColourCB.addActionListener(new ActionListener()
459 public void actionPerformed(ActionEvent e)
461 if (e.getSource() == variableColourCB)
463 if (featureColour.isSimpleColour())
466 * toggle simple colour to variable colour - show dialog
468 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
469 fc.addActionListener(this);
474 * toggle variable to simple colour - show colour chooser
476 String title = MessageManager.getString("label.select_colour");
477 ColourChooserListener listener = new ColourChooserListener()
480 public void colourSelected(Color c)
482 table.setValueAt(new FeatureColour(c), rowSelected,
485 me.updateFeatureRenderer(
486 ((FeatureTableModel) table.getModel()).getData(),
490 JalviewColourChooser.showColourChooser(me, title, featureColour.getMaxColour(), listener);
494 if (e.getSource() instanceof FeatureTypeSettings)
497 * update after OK in feature colour dialog; the updated
498 * colour will have already been set in the FeatureRenderer
500 FeatureColourI fci = fr.getFeatureColours().get(type);
501 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
509 JMenuItem selCols = new JMenuItem(
510 MessageManager.getString("label.select_columns_containing"));
511 selCols.addActionListener(new ActionListener()
514 public void actionPerformed(ActionEvent arg0)
516 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
520 JMenuItem clearCols = new JMenuItem(MessageManager
521 .getString("label.select_columns_not_containing"));
522 clearCols.addActionListener(new ActionListener()
525 public void actionPerformed(ActionEvent arg0)
527 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
531 JMenuItem hideCols = new JMenuItem(
532 MessageManager.getString("label.hide_columns_containing"));
533 hideCols.addActionListener(new ActionListener()
536 public void actionPerformed(ActionEvent arg0)
538 fr.ap.alignFrame.hideFeatureColumns(type, true);
541 JMenuItem hideOtherCols = new JMenuItem(
542 MessageManager.getString("label.hide_columns_not_containing"));
543 hideOtherCols.addActionListener(new ActionListener()
546 public void actionPerformed(ActionEvent arg0)
548 fr.ap.alignFrame.hideFeatureColumns(type, false);
554 men.add(hideOtherCols);
555 men.show(table, x, y);
559 synchronized public void discoverAllFeatureData()
561 Set<String> allGroups = new HashSet<>();
562 AlignmentI alignment = af.getViewport().getAlignment();
564 for (int i = 0; i < alignment.getHeight(); i++)
566 SequenceI seq = alignment.getSequenceAt(i);
567 for (String group : seq.getFeatures().getFeatureGroups(true))
569 if (group != null && !allGroups.contains(group))
571 allGroups.add(group);
572 checkGroupState(group);
583 * Synchronise gui group list and check visibility of group
586 * @return true if group is visible
588 private boolean checkGroupState(String group)
590 boolean visible = fr.checkGroupVisibility(group, true);
592 for (int g = 0; g < groupPanel.getComponentCount(); g++)
594 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
596 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
601 final String grp = group;
602 final JCheckBox check = new JCheckBox(group, visible);
603 check.setFont(new Font("Serif", Font.BOLD, 12));
604 check.setToolTipText(group);
605 check.addItemListener(new ItemListener()
608 public void itemStateChanged(ItemEvent evt)
610 fr.setGroupVisibility(check.getText(), check.isSelected());
611 resetTable(new String[] { grp });
612 af.alignPanel.paintAlignment(true, true);
615 groupPanel.add(check);
619 synchronized void resetTable(String[] groupChanged)
625 resettingTable = true;
626 typeWidth = new Hashtable<>();
627 // TODO: change avWidth calculation to 'per-sequence' average and use long
630 Set<String> displayableTypes = new HashSet<>();
631 Set<String> foundGroups = new HashSet<>();
634 * determine which feature types may be visible depending on
635 * which groups are selected, and recompute average width data
637 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
640 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
643 * get the sequence's groups for positional features
644 * and keep track of which groups are visible
646 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
647 Set<String> visibleGroups = new HashSet<>();
648 for (String group : groups)
650 if (group == null || checkGroupState(group))
652 visibleGroups.add(group);
655 foundGroups.addAll(groups);
658 * get distinct feature types for visible groups
659 * record distinct visible types, and their count and total length
661 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
662 visibleGroups.toArray(new String[visibleGroups.size()]));
663 for (String type : types)
665 displayableTypes.add(type);
666 float[] avWidth = typeWidth.get(type);
669 avWidth = new float[2];
670 typeWidth.put(type, avWidth);
672 // todo this could include features with a non-visible group
673 // - do we greatly care?
674 // todo should we include non-displayable features here, and only
675 // update when features are added?
676 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
677 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
681 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
684 if (fr.hasRenderOrder())
688 fr.findAllFeatures(groupChanged != null); // prod to update
689 // colourschemes. but don't
691 // First add the checks in the previous render order,
692 // in case the window has been closed and reopened
694 List<String> frl = fr.getRenderOrder();
695 for (int ro = frl.size() - 1; ro > -1; ro--)
697 String type = frl.get(ro);
699 if (!displayableTypes.contains(type))
704 data[dataIndex][TYPE_COLUMN] = type;
705 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
706 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
707 data[dataIndex][FILTER_COLUMN] = featureFilter == null
708 ? new FeatureMatcherSet()
710 data[dataIndex][SHOW_COLUMN] = new Boolean(
711 af.getViewport().getFeaturesDisplayed().isVisible(type));
713 displayableTypes.remove(type);
718 * process any extra features belonging only to
719 * a group which was just selected
721 while (!displayableTypes.isEmpty())
723 String type = displayableTypes.iterator().next();
724 data[dataIndex][TYPE_COLUMN] = type;
726 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
727 if (data[dataIndex][COLOUR_COLUMN] == null)
729 // "Colour has been updated in another view!!"
730 fr.clearRenderOrder();
733 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
734 data[dataIndex][FILTER_COLUMN] = featureFilter == null
735 ? new FeatureMatcherSet()
737 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
739 displayableTypes.remove(type);
742 if (originalData == null)
744 originalData = new Object[data.length][COLUMN_COUNT];
745 for (int i = 0; i < data.length; i++)
747 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
752 updateOriginalData(data);
755 table.setModel(new FeatureTableModel(data));
756 table.getColumnModel().getColumn(0).setPreferredWidth(200);
758 groupPanel.setLayout(
759 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
760 pruneGroups(foundGroups);
761 groupPanel.validate();
763 updateFeatureRenderer(data, groupChanged != null);
764 resettingTable = false;
768 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
769 * have been made outwith this dialog
771 * <li>a new feature type added (and made visible)</li>
772 * <li>a feature colour changed (in the Amend Features dialog)</li>
777 protected void updateOriginalData(Object[][] foundData)
779 // todo LinkedHashMap instead of Object[][] would be nice
781 Object[][] currentData = ((FeatureTableModel) table.getModel())
783 for (Object[] row : foundData)
785 String type = (String) row[TYPE_COLUMN];
786 boolean found = false;
787 for (Object[] current : currentData)
789 if (type.equals(current[TYPE_COLUMN]))
793 * currently dependent on object equality here;
794 * really need an equals method on FeatureColour
796 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
799 * feature colour has changed externally - update originalData
801 for (Object[] original : originalData)
803 if (type.equals(original[TYPE_COLUMN]))
805 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
816 * new feature detected - add to original data (on top)
818 Object[][] newData = new Object[originalData.length
820 for (int i = 0; i < originalData.length; i++)
822 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
826 originalData = newData;
832 * Remove from the groups panel any checkboxes for groups that are not in the
833 * foundGroups set. This enables removing a group from the display when the last
834 * feature in that group is deleted.
838 protected void pruneGroups(Set<String> foundGroups)
840 for (int g = 0; g < groupPanel.getComponentCount(); g++)
842 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
843 if (!foundGroups.contains(checkbox.getText()))
845 groupPanel.remove(checkbox);
851 * reorder data based on the featureRenderers global priority list.
855 private void ensureOrder(Object[][] data)
857 boolean sort = false;
858 float[] order = new float[data.length];
859 for (int i = 0; i < order.length; i++)
861 order[i] = fr.getOrder(data[i][0].toString());
864 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
868 sort = sort || order[i - 1] > order[i];
873 jalview.util.QuickSort.sort(order, data);
878 * Offers a file chooser dialog, and then loads the feature colours and
879 * filters from file in XML format and unmarshals to Jalview feature settings
883 // TODO: JAL-3048 relies on Castor XML parsing: not needed for JS-jalview core
886 JalviewFileChooser chooser = new JalviewFileChooser("fc",
887 SEQUENCE_FEATURE_COLOURS);
888 chooser.setFileView(new JalviewFileView());
889 chooser.setDialogTitle(
890 MessageManager.getString("label.load_feature_colours"));
891 chooser.setToolTipText(MessageManager.getString("action.load"));
893 int value = chooser.showOpenDialog(this);
895 if (value == JalviewFileChooser.APPROVE_OPTION)
897 File file = chooser.getSelectedFile();
903 * Loads feature colours and filters from XML stored in the given file
911 InputStreamReader in = new InputStreamReader(
912 new FileInputStream(file), "UTF-8");
914 JAXBContext jc = JAXBContext
915 .newInstance("jalview.xml.binding.jalview");
916 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
917 XMLStreamReader streamReader = XMLInputFactory.newInstance()
918 .createXMLStreamReader(in);
919 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
920 JalviewUserColours.class);
921 JalviewUserColours jucs = jbe.getValue();
923 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
926 * load feature colours
928 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
930 Colour newcol = jucs.getColour().get(i);
931 FeatureColourI colour = jalview.project.Jalview2XML
932 .parseColour(newcol);
933 fr.setColour(newcol.getName(), colour);
934 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
938 * load feature filters; loaded filters will replace any that are
939 * currently defined, other defined filters are left unchanged
941 for (int i = 0; i < jucs.getFilter().size(); i++)
943 Filter filterModel = jucs.getFilter().get(i);
944 String featureType = filterModel.getFeatureType();
945 FeatureMatcherSetI filter = jalview.project.Jalview2XML
946 .parseFilter(featureType, filterModel.getMatcherSet());
947 if (!filter.isEmpty())
949 fr.setFeatureFilter(featureType, filter);
954 * update feature settings table
959 Object[][] data = ((FeatureTableModel) table.getModel())
962 updateFeatureRenderer(data, false);
965 } catch (Exception ex)
967 System.out.println("Error loading User Colour File\n" + ex);
972 * Offers a file chooser dialog, and then saves the current feature colours
973 * and any filters to the selected file in XML format
977 // TODO: JAL-3048 not needed for Jalview-JS - save colours
978 JalviewFileChooser chooser = new JalviewFileChooser("fc",
979 SEQUENCE_FEATURE_COLOURS);
980 chooser.setFileView(new JalviewFileView());
981 chooser.setDialogTitle(
982 MessageManager.getString("label.save_feature_colours"));
983 chooser.setToolTipText(MessageManager.getString("action.save"));
985 int value = chooser.showSaveDialog(this);
987 if (value == JalviewFileChooser.APPROVE_OPTION)
989 save(chooser.getSelectedFile());
994 * Saves feature colours and filters to the given file
1000 JalviewUserColours ucs = new JalviewUserColours();
1001 ucs.setSchemeName("Sequence Features");
1004 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1005 new FileOutputStream(file), "UTF-8"));
1008 * sort feature types by colour order, from 0 (highest)
1011 Set<String> fr_colours = fr.getAllFeatureColours();
1012 String[] sortedTypes = fr_colours
1013 .toArray(new String[fr_colours.size()]);
1014 Arrays.sort(sortedTypes, new Comparator<String>()
1017 public int compare(String type1, String type2)
1019 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1024 * save feature colours
1026 for (String featureType : sortedTypes)
1028 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1029 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1031 ucs.getColour().add(col);
1035 * save any feature filters
1037 for (String featureType : sortedTypes)
1039 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1040 if (filter != null && !filter.isEmpty())
1042 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1043 FeatureMatcherI firstMatcher = iterator.next();
1044 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1045 .marshalFilter(firstMatcher, iterator,
1047 Filter filterModel = new Filter();
1048 filterModel.setFeatureType(featureType);
1049 filterModel.setMatcherSet(ms);
1050 ucs.getFilter().add(filterModel);
1053 JAXBContext jaxbContext = JAXBContext
1054 .newInstance(JalviewUserColours.class);
1055 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1056 jaxbMarshaller.marshal(
1057 new ObjectFactory().createJalviewUserColours(ucs), out);
1059 // jaxbMarshaller.marshal(object, pout);
1060 // marshaller.marshal(object);
1063 // ucs.marshal(out);
1065 } catch (Exception ex)
1067 ex.printStackTrace();
1071 public void invertSelection()
1073 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1074 for (int i = 0; i < data.length; i++)
1076 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1078 updateFeatureRenderer(data, true);
1082 public void orderByAvWidth()
1084 if (table == null || table.getModel() == null)
1088 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1089 float[] width = new float[data.length];
1093 for (int i = 0; i < data.length; i++)
1095 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1098 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1099 // weight - but have to make per
1100 // sequence, too (awidth[2])
1101 // if (width[i]==1) // hack to distinguish single width sequences.
1112 boolean sort = false;
1113 for (int i = 0; i < width.length; i++)
1115 // awidth = (float[]) typeWidth.get(data[i][0]);
1118 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1121 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1127 width[i] /= max; // normalize
1128 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1132 sort = sort || width[i - 1] > width[i];
1137 jalview.util.QuickSort.sort(width, data);
1138 // update global priority order
1141 updateFeatureRenderer(data, false);
1149 frame.setClosed(true);
1150 } catch (Exception exe)
1156 public void updateFeatureRenderer(Object[][] data)
1158 updateFeatureRenderer(data, true);
1162 * Update the priority order of features; only repaint if this changed the order
1163 * of visible features
1168 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1170 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1172 if (fr.setFeaturePriority(rowData, visibleNew))
1174 af.alignPanel.paintAlignment(true, true);
1179 * Converts table data into an array of data beans
1181 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1183 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1184 for (int i = 0; i < data.length; i++)
1186 String type = (String) data[i][TYPE_COLUMN];
1187 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1188 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1189 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1190 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1196 private void jbInit() throws Exception
1198 this.setLayout(new BorderLayout());
1200 JPanel settingsPane = new JPanel();
1201 settingsPane.setLayout(new BorderLayout());
1203 JPanel bigPanel = new JPanel();
1204 bigPanel.setLayout(new BorderLayout());
1206 groupPanel = new JPanel();
1207 bigPanel.add(groupPanel, BorderLayout.NORTH);
1209 JButton invert = new JButton(
1210 MessageManager.getString("label.invert_selection"));
1211 invert.setFont(JvSwingUtils.getLabelFont());
1212 invert.addActionListener(new ActionListener()
1215 public void actionPerformed(ActionEvent e)
1221 JButton optimizeOrder = new JButton(
1222 MessageManager.getString("label.optimise_order"));
1223 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1224 optimizeOrder.addActionListener(new ActionListener()
1227 public void actionPerformed(ActionEvent e)
1233 JButton sortByScore = new JButton(
1234 MessageManager.getString("label.seq_sort_by_score"));
1235 sortByScore.setFont(JvSwingUtils.getLabelFont());
1236 sortByScore.addActionListener(new ActionListener()
1239 public void actionPerformed(ActionEvent e)
1241 af.avc.sortAlignmentByFeatureScore(null);
1244 JButton sortByDens = new JButton(
1245 MessageManager.getString("label.sequence_sort_by_density"));
1246 sortByDens.setFont(JvSwingUtils.getLabelFont());
1247 sortByDens.addActionListener(new ActionListener()
1250 public void actionPerformed(ActionEvent e)
1252 af.avc.sortAlignmentByFeatureDensity(null);
1256 JButton help = new JButton(MessageManager.getString("action.help"));
1257 help.setFont(JvSwingUtils.getLabelFont());
1258 help.addActionListener(new ActionListener()
1261 public void actionPerformed(ActionEvent e)
1265 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1266 } catch (HelpSetException e1)
1268 e1.printStackTrace();
1272 help.setFont(JvSwingUtils.getLabelFont());
1273 help.setText(MessageManager.getString("action.help"));
1274 help.addActionListener(new ActionListener()
1277 public void actionPerformed(ActionEvent e)
1281 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1282 } catch (HelpSetException e1)
1284 e1.printStackTrace();
1289 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1290 cancel.setFont(JvSwingUtils.getLabelFont());
1291 cancel.addActionListener(new ActionListener()
1294 public void actionPerformed(ActionEvent e)
1296 fr.setTransparency(originalTransparency);
1297 fr.setFeatureFilters(originalFilters);
1298 updateFeatureRenderer(originalData);
1303 JButton ok = new JButton(MessageManager.getString("action.ok"));
1304 ok.setFont(JvSwingUtils.getLabelFont());
1305 ok.addActionListener(new ActionListener()
1308 public void actionPerformed(ActionEvent e)
1314 JButton loadColours = new JButton(
1315 MessageManager.getString("label.load_colours"));
1316 loadColours.setFont(JvSwingUtils.getLabelFont());
1317 loadColours.setToolTipText(
1318 MessageManager.getString("label.load_colours_tooltip"));
1319 loadColours.addActionListener(new ActionListener()
1322 public void actionPerformed(ActionEvent e)
1328 JButton saveColours = new JButton(
1329 MessageManager.getString("label.save_colours"));
1330 saveColours.setFont(JvSwingUtils.getLabelFont());
1331 saveColours.setToolTipText(
1332 MessageManager.getString("label.save_colours_tooltip"));
1333 saveColours.addActionListener(new ActionListener()
1336 public void actionPerformed(ActionEvent e)
1341 transparency.addChangeListener(new ChangeListener()
1344 public void stateChanged(ChangeEvent evt)
1346 if (!inConstruction)
1348 fr.setTransparency((100 - transparency.getValue()) / 100f);
1349 af.alignPanel.paintAlignment(true, true);
1354 transparency.setMaximum(70);
1355 transparency.setToolTipText(
1356 MessageManager.getString("label.transparency_tip"));
1358 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1359 bigPanel.add(transPanel, BorderLayout.SOUTH);
1361 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1362 transbuttons.add(optimizeOrder);
1363 transbuttons.add(invert);
1364 transbuttons.add(sortByScore);
1365 transbuttons.add(sortByDens);
1366 transbuttons.add(help);
1367 transPanel.add(transparency);
1368 transPanel.add(transbuttons);
1370 JPanel buttonPanel = new JPanel();
1371 buttonPanel.add(ok);
1372 buttonPanel.add(cancel);
1373 if (!Jalview.isJS())
1376 * no save/load XML in JalviewJS for now
1378 buttonPanel.add(loadColours);
1379 buttonPanel.add(saveColours);
1381 bigPanel.add(scrollPane, BorderLayout.CENTER);
1382 settingsPane.add(bigPanel, BorderLayout.CENTER);
1383 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1384 this.add(settingsPane);
1388 * Answers a suitable tooltip to show on the colour cell of the table
1392 * if true include 'click to edit' and similar text
1395 public static String getColorTooltip(FeatureColourI fcol,
1402 if (fcol.isSimpleColour())
1404 return withHint ? BASE_TOOLTIP : null;
1406 String description = fcol.getDescription();
1407 description = description.replaceAll("<", "<");
1408 description = description.replaceAll(">", ">");
1409 StringBuilder tt = new StringBuilder(description);
1412 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1414 return JvSwingUtils.wrapTooltip(true, tt.toString());
1417 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1420 boolean thr = false;
1421 StringBuilder tx = new StringBuilder();
1423 if (gcol.isColourByAttribute())
1425 tx.append(FeatureMatcher
1426 .toAttributeDisplayName(gcol.getAttributeName()));
1428 else if (!gcol.isColourByLabel())
1430 tx.append(MessageManager.getString("label.score"));
1433 if (gcol.isAboveThreshold())
1438 if (gcol.isBelowThreshold())
1443 if (gcol.isColourByLabel())
1449 if (!gcol.isColourByAttribute())
1457 Color newColor = gcol.getMaxColour();
1458 comp.setBackground(newColor);
1459 // System.err.println("Width is " + w / 2);
1460 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1461 comp.setIcon(ficon);
1462 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1463 // + newColor.getGreen() + ", " + newColor.getBlue()
1464 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1465 // + ", " + minCol.getBlue() + ")");
1467 comp.setHorizontalAlignment(SwingConstants.CENTER);
1468 comp.setText(tx.toString());
1471 // ///////////////////////////////////////////////////////////////////////
1472 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1473 // ///////////////////////////////////////////////////////////////////////
1474 class FeatureTableModel extends AbstractTableModel
1476 private String[] columnNames = {
1477 MessageManager.getString("label.feature_type"),
1478 MessageManager.getString("action.colour"),
1479 MessageManager.getString("label.filter"),
1480 MessageManager.getString("label.show") };
1482 private Object[][] data;
1484 FeatureTableModel(Object[][] data)
1489 public Object[][] getData()
1494 public void setData(Object[][] data)
1500 public int getColumnCount()
1502 return columnNames.length;
1505 public Object[] getRow(int row)
1511 public int getRowCount()
1517 public String getColumnName(int col)
1519 return columnNames[col];
1523 public Object getValueAt(int row, int col)
1525 return data[row][col];
1529 * Answers the class of the object in column c of the first row of the table
1532 public Class<?> getColumnClass(int c)
1534 Object v = getValueAt(0, c);
1535 return v == null ? null : v.getClass();
1539 public boolean isCellEditable(int row, int col)
1541 return col == 0 ? false : true;
1545 public void setValueAt(Object value, int row, int col)
1547 data[row][col] = value;
1548 fireTableCellUpdated(row, col);
1549 updateFeatureRenderer(data);
1554 class ColorRenderer extends JLabel implements TableCellRenderer
1556 Border unselectedBorder = null;
1558 Border selectedBorder = null;
1560 public ColorRenderer()
1562 setOpaque(true); // MUST do this for background to show up.
1563 setHorizontalTextPosition(SwingConstants.CENTER);
1564 setVerticalTextPosition(SwingConstants.CENTER);
1568 public Component getTableCellRendererComponent(JTable tbl, Object color,
1569 boolean isSelected, boolean hasFocus, int row, int column)
1571 FeatureColourI cellColour = (FeatureColourI) color;
1573 setBackground(tbl.getBackground());
1574 if (!cellColour.isSimpleColour())
1576 Rectangle cr = tbl.getCellRect(row, column, false);
1577 FeatureSettings.renderGraduatedColor(this, cellColour,
1578 (int) cr.getWidth(), (int) cr.getHeight());
1584 setBackground(cellColour.getColour());
1588 if (selectedBorder == null)
1590 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1591 tbl.getSelectionBackground());
1593 setBorder(selectedBorder);
1597 if (unselectedBorder == null)
1599 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1600 tbl.getBackground());
1602 setBorder(unselectedBorder);
1609 class FilterRenderer extends JLabel implements TableCellRenderer
1611 javax.swing.border.Border unselectedBorder = null;
1613 javax.swing.border.Border selectedBorder = null;
1615 public FilterRenderer()
1617 setOpaque(true); // MUST do this for background to show up.
1618 setHorizontalTextPosition(SwingConstants.CENTER);
1619 setVerticalTextPosition(SwingConstants.CENTER);
1623 public Component getTableCellRendererComponent(JTable tbl,
1624 Object filter, boolean isSelected, boolean hasFocus, int row,
1627 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1629 String asText = theFilter.toString();
1630 setBackground(tbl.getBackground());
1631 this.setText(asText);
1636 if (selectedBorder == null)
1638 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1639 tbl.getSelectionBackground());
1641 setBorder(selectedBorder);
1645 if (unselectedBorder == null)
1647 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1648 tbl.getBackground());
1650 setBorder(unselectedBorder);
1658 * update comp using rendering settings from gcol
1663 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1665 int w = comp.getWidth(), h = comp.getHeight();
1668 w = (int) comp.getPreferredSize().getWidth();
1669 h = (int) comp.getPreferredSize().getHeight();
1676 renderGraduatedColor(comp, gcol, w, h);
1679 class ColorEditor extends AbstractCellEditor
1680 implements TableCellEditor, ActionListener
1684 FeatureColourI currentColor;
1686 FeatureTypeSettings chooser;
1692 protected static final String EDIT = "edit";
1694 int rowSelected = 0;
1696 public ColorEditor(FeatureSettings fs)
1699 // Set up the editor (from the table's point of view),
1700 // which is a button.
1701 // This button brings up the color chooser dialog,
1702 // which is the editor from the user's point of view.
1703 button = new JButton();
1704 button.setActionCommand(EDIT);
1705 button.addActionListener(this);
1706 button.setBorderPainted(false);
1710 * Handles events from the editor button, and from the colour/filters
1711 * dialog's OK button
1714 public void actionPerformed(ActionEvent e)
1716 if (button == e.getSource())
1718 if (currentColor.isSimpleColour())
1721 * simple colour chooser
1723 String ttl = MessageManager.getString("label.select_colour");
1724 ColourChooserListener listener = new ColourChooserListener() {
1726 public void colourSelected(Color c)
1728 currentColor = new FeatureColour(c);
1729 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1732 JalviewColourChooser.showColourChooser(button, ttl, currentColor.getColour(), listener);
1737 * variable colour and filters dialog
1739 chooser = new FeatureTypeSettings(me.fr, type);
1744 chooser.setRequestFocusEnabled(true);
1745 chooser.requestFocus();
1747 chooser.addActionListener(this);
1748 // Make the renderer reappear.
1749 fireEditingStopped();
1755 * after OK in variable colour dialog, any changes to colour
1756 * (or filters!) are already set in FeatureRenderer, so just
1757 * update table data without triggering updateFeatureRenderer
1759 currentColor = fr.getFeatureColours().get(type);
1760 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1761 if (currentFilter == null)
1763 currentFilter = new FeatureMatcherSet();
1765 Object[] data = ((FeatureTableModel) table.getModel())
1766 .getData()[rowSelected];
1767 data[COLOUR_COLUMN] = currentColor;
1768 data[FILTER_COLUMN] = currentFilter;
1770 fireEditingStopped();
1771 me.table.validate();
1775 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1777 public Object getCellEditorValue()
1779 return currentColor;
1782 // Implement the one method defined by TableCellEditor.
1784 public Component getTableCellEditorComponent(JTable theTable, Object value,
1785 boolean isSelected, int row, int column)
1787 currentColor = (FeatureColourI) value;
1788 this.rowSelected = row;
1789 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1790 button.setOpaque(true);
1791 button.setBackground(me.getBackground());
1792 if (!currentColor.isSimpleColour())
1794 JLabel btn = new JLabel();
1795 btn.setSize(button.getSize());
1796 FeatureSettings.renderGraduatedColor(btn, currentColor);
1797 button.setBackground(btn.getBackground());
1798 button.setIcon(btn.getIcon());
1799 button.setText(btn.getText());
1804 button.setIcon(null);
1805 button.setBackground(currentColor.getColour());
1812 * The cell editor for the Filter column. It displays the text of any filters
1813 * for the feature type in that row (in full as a tooltip, possible abbreviated
1814 * as display text). On click in the cell, opens the Feature Display Settings
1815 * dialog at the Filters tab.
1817 class FilterEditor extends AbstractCellEditor
1818 implements TableCellEditor, ActionListener
1822 FeatureMatcherSetI currentFilter;
1830 protected static final String EDIT = "edit";
1832 int rowSelected = 0;
1834 public FilterEditor(FeatureSettings me)
1837 button = new JButton();
1838 button.setActionCommand(EDIT);
1839 button.addActionListener(this);
1840 button.setBorderPainted(false);
1844 * Handles events from the editor button
1847 public void actionPerformed(ActionEvent e)
1849 if (button == e.getSource())
1851 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1852 chooser.addActionListener(this);
1853 chooser.setRequestFocusEnabled(true);
1854 chooser.requestFocus();
1855 if (lastLocation != null)
1857 // todo open at its last position on screen
1858 chooser.setBounds(lastLocation.x, lastLocation.y,
1859 chooser.getWidth(), chooser.getHeight());
1862 fireEditingStopped();
1864 else if (e.getSource() instanceof Component)
1868 * after OK in variable colour dialog, any changes to filter
1869 * (or colours!) are already set in FeatureRenderer, so just
1870 * update table data without triggering updateFeatureRenderer
1872 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1873 currentFilter = me.fr.getFeatureFilter(type);
1874 if (currentFilter == null)
1876 currentFilter = new FeatureMatcherSet();
1878 Object[] data = ((FeatureTableModel) table.getModel())
1879 .getData()[rowSelected];
1880 data[COLOUR_COLUMN] = currentColor;
1881 data[FILTER_COLUMN] = currentFilter;
1882 fireEditingStopped();
1883 me.table.validate();
1888 public Object getCellEditorValue()
1890 return currentFilter;
1894 public Component getTableCellEditorComponent(JTable theTable, Object value,
1895 boolean isSelected, int row, int column)
1897 currentFilter = (FeatureMatcherSetI) value;
1898 this.rowSelected = row;
1899 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1900 button.setOpaque(true);
1901 button.setBackground(me.getBackground());
1902 button.setText(currentFilter.toString());
1903 button.setIcon(null);
1909 class FeatureIcon implements Icon
1911 FeatureColourI gcol;
1915 boolean midspace = false;
1917 int width = 50, height = 20;
1919 int s1, e1; // start and end of midpoint band for thresholded symbol
1921 Color mpcolour = Color.white;
1923 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1943 public int getIconWidth()
1949 public int getIconHeight()
1955 public void paintIcon(Component c, Graphics g, int x, int y)
1958 if (gcol.isColourByLabel())
1961 g.fillRect(0, 0, width, height);
1962 // need an icon here.
1963 g.setColor(gcol.getMaxColour());
1965 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1967 // g.setFont(g.getFont().deriveFont(
1968 // AffineTransform.getScaleInstance(
1969 // width/g.getFontMetrics().stringWidth("Label"),
1970 // height/g.getFontMetrics().getHeight())));
1972 g.drawString(MessageManager.getString("label.label"), 0, 0);
1977 Color minCol = gcol.getMinColour();
1979 g.fillRect(0, 0, s1, height);
1982 g.setColor(Color.white);
1983 g.fillRect(s1, 0, e1 - s1, height);
1985 g.setColor(gcol.getMaxColour());
1986 g.fillRect(0, e1, width - e1, height);