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.gui.JalviewColourChooser.ColourChooserListener;
35 import jalview.io.JalviewFileChooser;
36 import jalview.io.JalviewFileView;
37 import jalview.schemes.FeatureColour;
38 import jalview.util.MessageManager;
39 import jalview.util.Platform;
40 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
41 import jalview.viewmodel.styles.ViewStyle;
42 import jalview.xml.binding.jalview.JalviewUserColours;
43 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
44 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
45 import jalview.xml.binding.jalview.ObjectFactory;
47 import java.awt.BorderLayout;
48 import java.awt.Color;
49 import java.awt.Component;
50 import java.awt.Dimension;
51 import java.awt.FlowLayout;
53 import java.awt.Graphics;
54 import java.awt.GridLayout;
55 import java.awt.Point;
56 import java.awt.Rectangle;
57 import java.awt.event.ActionEvent;
58 import java.awt.event.ActionListener;
59 import java.awt.event.ItemEvent;
60 import java.awt.event.ItemListener;
61 import java.awt.event.MouseAdapter;
62 import java.awt.event.MouseEvent;
63 import java.awt.event.MouseMotionAdapter;
64 import java.beans.PropertyChangeEvent;
65 import java.beans.PropertyChangeListener;
67 import java.io.FileInputStream;
68 import java.io.FileOutputStream;
69 import java.io.InputStreamReader;
70 import java.io.OutputStreamWriter;
71 import java.io.PrintWriter;
72 import java.util.Arrays;
73 import java.util.Comparator;
74 import java.util.HashMap;
75 import java.util.HashSet;
76 import java.util.Hashtable;
77 import java.util.Iterator;
78 import java.util.List;
82 import javax.help.HelpSetException;
83 import javax.swing.AbstractCellEditor;
84 import javax.swing.BorderFactory;
85 import javax.swing.Icon;
86 import javax.swing.JButton;
87 import javax.swing.JCheckBox;
88 import javax.swing.JCheckBoxMenuItem;
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.ToolTipManager;
101 import javax.swing.border.Border;
102 import javax.swing.event.ChangeEvent;
103 import javax.swing.event.ChangeListener;
104 import javax.swing.table.AbstractTableModel;
105 import javax.swing.table.JTableHeader;
106 import javax.swing.table.TableCellEditor;
107 import javax.swing.table.TableCellRenderer;
108 import javax.swing.table.TableColumn;
109 import javax.xml.bind.JAXBContext;
110 import javax.xml.bind.JAXBElement;
111 import javax.xml.bind.Marshaller;
112 import javax.xml.stream.XMLInputFactory;
113 import javax.xml.stream.XMLStreamReader;
115 public class FeatureSettings extends JPanel
116 implements FeatureSettingsControllerI
118 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
119 .getString("label.sequence_feature_colours");
122 * column indices of fields in Feature Settings table
124 static final int TYPE_COLUMN = 0;
126 static final int COLOUR_COLUMN = 1;
128 static final int FILTER_COLUMN = 2;
130 static final int SHOW_COLUMN = 3;
132 private static final int COLUMN_COUNT = 4;
134 private static final int MIN_WIDTH = 400;
136 private static final int MIN_HEIGHT = 400;
138 private final static String BASE_TOOLTIP = MessageManager
139 .getString("label.click_to_edit");
141 final FeatureRenderer fr;
143 public final AlignFrame af;
146 * 'original' fields hold settings to restore on Cancel
148 Object[][] originalData;
150 float originalTransparency;
152 private ViewStyleI originalViewStyle;
154 private Map<String, FeatureMatcherSetI> originalFilters;
156 final JInternalFrame frame;
158 JScrollPane scrollPane = new JScrollPane();
164 JSlider transparency = new JSlider();
167 * when true, constructor is still executing - so ignore UI events
169 protected volatile boolean inConstruction = true;
171 int selectedRow = -1;
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
201 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
206 } catch (Exception ex)
208 ex.printStackTrace();
214 public String getToolTipText(MouseEvent e)
217 int column = table.columnAtPoint(e.getPoint());
218 int row = table.rowAtPoint(e.getPoint());
223 tip = JvSwingUtils.wrapTooltip(true, MessageManager
224 .getString("label.feature_settings_click_drag"));
227 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
229 tip = getColorTooltip(colour, true);
232 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
236 .getString("label.configure_feature_tooltip")
247 * Position the tooltip near the bottom edge of, and half way across, the
251 public Point getToolTipLocation(MouseEvent e)
253 Point point = e.getPoint();
254 int column = table.columnAtPoint(point);
255 int row = table.rowAtPoint(point);
256 Rectangle r = getCellRect(row, column, false);
257 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
261 JTableHeader tableHeader = table.getTableHeader();
262 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
263 tableHeader.setReorderingAllowed(false);
264 table.setFont(new Font("Verdana", Font.PLAIN, 12));
265 ToolTipManager.sharedInstance().registerComponent(table);
266 table.setDefaultEditor(FeatureColour.class, new ColorEditor());
267 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
269 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor());
270 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
272 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
273 new ColorRenderer(), new ColorEditor());
274 table.addColumn(colourColumn);
276 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
277 new FilterRenderer(), new FilterEditor());
278 table.addColumn(filterColumn);
280 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
282 table.addMouseListener(new MouseAdapter()
285 public void mousePressed(MouseEvent evt)
287 Point pt = evt.getPoint();
288 selectedRow = table.rowAtPoint(pt);
289 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
290 if (evt.isPopupTrigger())
292 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
293 showPopupMenu(selectedRow, type, colour, evt.getPoint());
295 else if (evt.getClickCount() == 2
296 && table.columnAtPoint(pt) == TYPE_COLUMN)
298 boolean invertSelection = evt.isAltDown();
299 boolean toggleSelection = Platform.isControlDown(evt);
300 boolean extendSelection = evt.isShiftDown();
301 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
302 invertSelection, extendSelection, toggleSelection, type);
303 fr.ap.av.sendSelection();
307 // isPopupTrigger fires on mouseReleased on Windows
309 public void mouseReleased(MouseEvent evt)
311 selectedRow = table.rowAtPoint(evt.getPoint());
312 if (evt.isPopupTrigger())
314 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
315 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
316 showPopupMenu(selectedRow, type, colour, evt.getPoint());
321 table.addMouseMotionListener(new MouseMotionAdapter()
324 public void mouseDragged(MouseEvent evt)
326 int newRow = table.rowAtPoint(evt.getPoint());
327 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
330 * reposition 'selectedRow' to 'newRow' (the dragged to location)
331 * this could be more than one row away for a very fast drag action
332 * so just swap it with adjacent rows until we get it there
334 Object[][] data = ((FeatureTableModel) table.getModel())
336 int direction = newRow < selectedRow ? -1 : 1;
337 for (int i = selectedRow; i != newRow; i += direction)
339 Object[] temp = data[i];
340 data[i] = data[i + direction];
341 data[i + direction] = temp;
343 updateFeatureRenderer(data);
345 selectedRow = newRow;
349 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
350 // MessageManager.getString("label.feature_settings_click_drag")));
351 scrollPane.setViewportView(table);
353 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
355 fr.findAllFeatures(true); // display everything!
358 discoverAllFeatureData();
359 final PropertyChangeListener change;
360 final FeatureSettings fs = this;
361 fr.addPropertyChangeListener(change = new PropertyChangeListener()
364 public void propertyChange(PropertyChangeEvent evt)
366 if (!fs.resettingTable && !fs.handlingUpdate)
368 fs.handlingUpdate = true;
370 // new groups may be added with new sequence feature types only
371 fs.handlingUpdate = false;
377 frame = new JInternalFrame();
378 frame.setContentPane(this);
379 Desktop.addInternalFrame(frame,
380 MessageManager.getString("label.sequence_feature_settings"),
381 600, Platform.isAMacAndNotJS() ? 480 : 450);
382 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
384 frame.addInternalFrameListener(
385 new javax.swing.event.InternalFrameAdapter()
388 public void internalFrameClosed(
389 javax.swing.event.InternalFrameEvent evt)
391 fr.removePropertyChangeListener(change);
394 frame.setLayer(JLayeredPane.PALETTE_LAYER);
395 inConstruction = false;
399 * Constructs and shows a popup menu of possible actions on the selected row
407 protected void showPopupMenu(final int rowSelected, final String type,
408 final Object typeCol, final Point pt)
410 JPopupMenu men = new JPopupMenu(MessageManager
411 .formatMessage("label.settings_for_param", new String[]
413 final FeatureColourI featureColour = (FeatureColourI) typeCol;
416 * menu option to select (or deselect) variable colour
418 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
419 MessageManager.getString("label.variable_colour"));
420 variableColourCB.setSelected(!featureColour.isSimpleColour());
421 men.add(variableColourCB);
424 * checkbox action listener doubles up as listener to OK
425 * from the variable colour / filters dialog
427 variableColourCB.addActionListener(new ActionListener()
430 public void actionPerformed(ActionEvent e)
432 if (e.getSource() == variableColourCB)
434 men.setVisible(true); // BH 2018 for JavaScript because this is a
436 men.setVisible(false); // BH 2018 for JavaScript because this is a
438 if (featureColour.isSimpleColour())
441 * toggle simple colour to variable colour - show dialog
443 FeatureTypeSettings fc = new FeatureTypeSettings(fr, type);
444 fc.addActionListener(this);
449 * toggle variable to simple colour - show colour chooser
451 String title = MessageManager
452 .formatMessage("label.select_colour_for", type);
453 ColourChooserListener listener = new ColourChooserListener()
456 public void colourSelected(Color c)
458 table.setValueAt(new FeatureColour(c), rowSelected,
461 updateFeatureRenderer(
462 ((FeatureTableModel) table.getModel()).getData(),
466 JalviewColourChooser.showColourChooser(FeatureSettings.this,
467 title, featureColour.getMaxColour(), listener);
472 if (e.getSource() instanceof FeatureTypeSettings)
475 * update after OK in feature colour dialog; the updated
476 * colour will have already been set in the FeatureRenderer
478 FeatureColourI fci = fr.getFeatureColours().get(type);
479 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
480 // BH 2018 setting a table value does not invalidate it.
481 // System.out.println("FeatureSettings is valied" +
490 JMenuItem scr = new JMenuItem(
491 MessageManager.getString("label.sort_by_score"));
493 scr.addActionListener(new ActionListener()
497 public void actionPerformed(ActionEvent e)
499 af.avc.sortAlignmentByFeatureScore(Arrays.asList(new String[]
503 JMenuItem dens = new JMenuItem(
504 MessageManager.getString("label.sort_by_density"));
505 dens.addActionListener(new ActionListener()
509 public void actionPerformed(ActionEvent e)
511 af.avc.sortAlignmentByFeatureDensity(Arrays.asList(new String[]
517 JMenuItem selCols = new JMenuItem(
518 MessageManager.getString("label.select_columns_containing"));
519 selCols.addActionListener(new ActionListener()
522 public void actionPerformed(ActionEvent arg0)
524 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
526 fr.ap.av.sendSelection();
529 JMenuItem clearCols = new JMenuItem(MessageManager
530 .getString("label.select_columns_not_containing"));
531 clearCols.addActionListener(new ActionListener()
534 public void actionPerformed(ActionEvent arg0)
536 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
538 fr.ap.av.sendSelection();
541 JMenuItem hideCols = new JMenuItem(
542 MessageManager.getString("label.hide_columns_containing"));
543 hideCols.addActionListener(new ActionListener()
546 public void actionPerformed(ActionEvent arg0)
548 fr.ap.alignFrame.hideFeatureColumns(type, true);
549 fr.ap.av.sendSelection();
552 JMenuItem hideOtherCols = new JMenuItem(
553 MessageManager.getString("label.hide_columns_not_containing"));
554 hideOtherCols.addActionListener(new ActionListener()
557 public void actionPerformed(ActionEvent arg0)
559 fr.ap.alignFrame.hideFeatureColumns(type, false);
560 fr.ap.av.sendSelection();
566 men.add(hideOtherCols);
567 men.show(table, pt.x, pt.y);
571 synchronized public void discoverAllFeatureData()
573 Set<String> allGroups = new HashSet<>();
574 AlignmentI alignment = af.getViewport().getAlignment();
576 for (int i = 0; i < alignment.getHeight(); i++)
578 SequenceI seq = alignment.getSequenceAt(i);
579 for (String group : seq.getFeatures().getFeatureGroups(true))
581 if (group != null && !allGroups.contains(group))
583 allGroups.add(group);
584 checkGroupState(group);
595 * Synchronise gui group list and check visibility of group
598 * @return true if group is visible
600 private boolean checkGroupState(String group)
602 boolean visible = fr.checkGroupVisibility(group, true);
604 for (int g = 0; g < groupPanel.getComponentCount(); g++)
606 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
608 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
613 final String grp = group;
614 final JCheckBox check = new JCheckBox(group, visible);
615 check.setFont(new Font("Serif", Font.BOLD, 12));
616 check.setToolTipText(group);
617 check.addItemListener(new ItemListener()
620 public void itemStateChanged(ItemEvent evt)
622 fr.setGroupVisibility(check.getText(), check.isSelected());
623 resetTable(new String[] { grp });
627 groupPanel.add(check);
631 synchronized void resetTable(String[] groupChanged)
637 resettingTable = true;
638 typeWidth = new Hashtable<>();
639 // TODO: change avWidth calculation to 'per-sequence' average and use long
642 Set<String> displayableTypes = new HashSet<>();
643 Set<String> foundGroups = new HashSet<>();
646 * determine which feature types may be visible depending on
647 * which groups are selected, and recompute average width data
649 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
652 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
655 * get the sequence's groups for positional features
656 * and keep track of which groups are visible
658 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
659 Set<String> visibleGroups = new HashSet<>();
660 for (String group : groups)
662 if (group == null || checkGroupState(group))
664 visibleGroups.add(group);
667 foundGroups.addAll(groups);
670 * get distinct feature types for visible groups
671 * record distinct visible types, and their count and total length
673 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
674 visibleGroups.toArray(new String[visibleGroups.size()]));
675 for (String type : types)
677 displayableTypes.add(type);
678 float[] avWidth = typeWidth.get(type);
681 avWidth = new float[2];
682 typeWidth.put(type, avWidth);
684 // todo this could include features with a non-visible group
685 // - do we greatly care?
686 // todo should we include non-displayable features here, and only
687 // update when features are added?
688 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
689 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
693 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
696 if (fr.hasRenderOrder())
700 fr.findAllFeatures(groupChanged != null); // prod to update
701 // colourschemes. but don't
703 // First add the checks in the previous render order,
704 // in case the window has been closed and reopened
706 List<String> frl = fr.getRenderOrder();
707 for (int ro = frl.size() - 1; ro > -1; ro--)
709 String type = frl.get(ro);
711 if (!displayableTypes.contains(type))
716 data[dataIndex][TYPE_COLUMN] = type;
717 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
718 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
719 data[dataIndex][FILTER_COLUMN] = featureFilter == null
720 ? new FeatureMatcherSet()
722 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
723 af.getViewport().getFeaturesDisplayed().isVisible(type));
725 displayableTypes.remove(type);
730 * process any extra features belonging only to
731 * a group which was just selected
733 while (!displayableTypes.isEmpty())
735 String type = displayableTypes.iterator().next();
736 data[dataIndex][TYPE_COLUMN] = type;
738 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
739 if (data[dataIndex][COLOUR_COLUMN] == null)
741 // "Colour has been updated in another view!!"
742 fr.clearRenderOrder();
745 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
746 data[dataIndex][FILTER_COLUMN] = featureFilter == null
747 ? new FeatureMatcherSet()
749 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
751 displayableTypes.remove(type);
754 if (originalData == null)
756 originalData = new Object[data.length][COLUMN_COUNT];
757 for (int i = 0; i < data.length; i++)
759 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
764 updateOriginalData(data);
767 table.setModel(new FeatureTableModel(data));
768 table.getColumnModel().getColumn(0).setPreferredWidth(200);
770 groupPanel.setLayout(
771 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
772 pruneGroups(foundGroups);
773 groupPanel.validate();
775 updateFeatureRenderer(data, groupChanged != null);
776 resettingTable = false;
780 * Updates 'originalData' (used for restore on Cancel) if we detect that
781 * changes have been made outwith this dialog
783 * <li>a new feature type added (and made visible)</li>
784 * <li>a feature colour changed (in the Amend Features dialog)</li>
789 protected void updateOriginalData(Object[][] foundData)
791 // todo LinkedHashMap instead of Object[][] would be nice
793 Object[][] currentData = ((FeatureTableModel) table.getModel())
795 for (Object[] row : foundData)
797 String type = (String) row[TYPE_COLUMN];
798 boolean found = false;
799 for (Object[] current : currentData)
801 if (type.equals(current[TYPE_COLUMN]))
805 * currently dependent on object equality here;
806 * really need an equals method on FeatureColour
808 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
811 * feature colour has changed externally - update originalData
813 for (Object[] original : originalData)
815 if (type.equals(original[TYPE_COLUMN]))
817 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
828 * new feature detected - add to original data (on top)
830 Object[][] newData = new Object[originalData.length
832 for (int i = 0; i < originalData.length; i++)
834 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
838 originalData = newData;
844 * Remove from the groups panel any checkboxes for groups that are not in the
845 * foundGroups set. This enables removing a group from the display when the
846 * last feature in that group is deleted.
850 protected void pruneGroups(Set<String> foundGroups)
852 for (int g = 0; g < groupPanel.getComponentCount(); g++)
854 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
855 if (!foundGroups.contains(checkbox.getText()))
857 groupPanel.remove(checkbox);
863 * reorder data based on the featureRenderers global priority list.
867 private void ensureOrder(Object[][] data)
869 boolean sort = false;
870 float[] order = new float[data.length];
871 for (int i = 0; i < order.length; i++)
873 order[i] = fr.getOrder(data[i][0].toString());
876 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
880 sort = sort || order[i - 1] > order[i];
885 jalview.util.QuickSort.sort(order, data);
890 * Offers a file chooser dialog, and then loads the feature colours and
891 * filters from file in XML format and unmarshals to Jalview feature settings
895 JalviewFileChooser chooser = new JalviewFileChooser("fc",
896 SEQUENCE_FEATURE_COLOURS);
897 chooser.setFileView(new JalviewFileView());
898 chooser.setDialogTitle(
899 MessageManager.getString("label.load_feature_colours"));
900 chooser.setToolTipText(MessageManager.getString("action.load"));
901 chooser.setResponseHandler(0, new Runnable()
906 File file = chooser.getSelectedFile();
910 chooser.showOpenDialog(this);
914 * Loads feature colours and filters from XML stored in the given file
922 InputStreamReader in = new InputStreamReader(
923 new FileInputStream(file), "UTF-8");
925 JAXBContext jc = JAXBContext
926 .newInstance("jalview.xml.binding.jalview");
927 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
928 XMLStreamReader streamReader = XMLInputFactory.newInstance()
929 .createXMLStreamReader(in);
930 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
931 JalviewUserColours.class);
932 JalviewUserColours jucs = jbe.getValue();
934 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
937 * load feature colours
939 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
941 Colour newcol = jucs.getColour().get(i);
942 FeatureColourI colour = jalview.project.Jalview2XML
943 .parseColour(newcol);
944 fr.setColour(newcol.getName(), colour);
945 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
949 * load feature filters; loaded filters will replace any that are
950 * currently defined, other defined filters are left unchanged
952 for (int i = 0; i < jucs.getFilter().size(); i++)
954 Filter filterModel = jucs.getFilter().get(i);
955 String featureType = filterModel.getFeatureType();
956 FeatureMatcherSetI filter = jalview.project.Jalview2XML
957 .parseFilter(featureType, filterModel.getMatcherSet());
958 if (!filter.isEmpty())
960 fr.setFeatureFilter(featureType, filter);
965 * update feature settings table
970 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
972 updateFeatureRenderer(data, false);
975 } catch (Exception ex)
977 System.out.println("Error loading User Colour File\n" + ex);
982 * Offers a file chooser dialog, and then saves the current feature colours
983 * and any filters to the selected file in XML format
987 JalviewFileChooser chooser = new JalviewFileChooser("fc",
988 SEQUENCE_FEATURE_COLOURS);
989 chooser.setFileView(new JalviewFileView());
990 chooser.setDialogTitle(
991 MessageManager.getString("label.save_feature_colours"));
992 chooser.setToolTipText(MessageManager.getString("action.save"));
993 int option = chooser.showSaveDialog(this);
994 if (option == JalviewFileChooser.APPROVE_OPTION)
996 File file = chooser.getSelectedFile();
1002 * Saves feature colours and filters to the given file
1006 void save(File file)
1008 JalviewUserColours ucs = new JalviewUserColours();
1009 ucs.setSchemeName("Sequence Features");
1012 PrintWriter out = new PrintWriter(
1013 new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
1016 * sort feature types by colour order, from 0 (highest)
1019 Set<String> fr_colours = fr.getAllFeatureColours();
1020 String[] sortedTypes = fr_colours
1021 .toArray(new String[fr_colours.size()]);
1022 Arrays.sort(sortedTypes, new Comparator<String>()
1025 public int compare(String type1, String type2)
1027 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1032 * save feature colours
1034 for (String featureType : sortedTypes)
1036 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1037 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1039 ucs.getColour().add(col);
1043 * save any feature filters
1045 for (String featureType : sortedTypes)
1047 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1048 if (filter != null && !filter.isEmpty())
1050 Iterator<FeatureMatcherI> iterator = filter.getMatchers()
1052 FeatureMatcherI firstMatcher = iterator.next();
1053 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1054 .marshalFilter(firstMatcher, iterator, filter.isAnded());
1055 Filter filterModel = new Filter();
1056 filterModel.setFeatureType(featureType);
1057 filterModel.setMatcherSet(ms);
1058 ucs.getFilter().add(filterModel);
1061 JAXBContext jaxbContext = JAXBContext
1062 .newInstance(JalviewUserColours.class);
1063 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1064 jaxbMarshaller.marshal(
1065 new ObjectFactory().createJalviewUserColours(ucs), out);
1067 // jaxbMarshaller.marshal(object, pout);
1068 // marshaller.marshal(object);
1071 // ucs.marshal(out);
1073 } catch (Exception ex)
1075 ex.printStackTrace();
1079 public void invertSelection()
1081 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1082 for (int i = 0; i < data.length; i++)
1084 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1086 updateFeatureRenderer(data, true);
1090 public void orderByAvWidth()
1092 if (table == null || table.getModel() == null)
1096 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1097 float[] width = new float[data.length];
1101 for (int i = 0; i < data.length; i++)
1103 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1106 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1107 // weight - but have to make per
1108 // sequence, too (awidth[2])
1109 // if (width[i]==1) // hack to distinguish single width sequences.
1120 boolean sort = false;
1121 for (int i = 0; i < width.length; i++)
1123 // awidth = (float[]) typeWidth.get(data[i][0]);
1126 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1129 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1135 width[i] /= max; // normalize
1136 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for
1141 sort = sort || width[i - 1] > width[i];
1146 jalview.util.QuickSort.sort(width, data);
1147 // update global priority order
1150 updateFeatureRenderer(data, false);
1158 frame.setClosed(true);
1159 } catch (Exception exe)
1165 public void updateFeatureRenderer(Object[][] data)
1167 updateFeatureRenderer(data, true);
1171 * Update the priority order of features; only repaint if this changed the
1172 * order of visible features
1177 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1179 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1181 if (fr.setFeaturePriority(rowData, visibleNew))
1188 * Converts table data into an array of data beans
1190 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1192 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1193 for (int i = 0; i < data.length; i++)
1195 String type = (String) data[i][TYPE_COLUMN];
1196 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1197 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1198 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1199 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1205 private void jbInit() throws Exception
1207 this.setLayout(new BorderLayout());
1209 JPanel settingsPane = new JPanel();
1210 settingsPane.setLayout(new BorderLayout());
1212 JPanel bigPanel = new JPanel();
1213 bigPanel.setLayout(new BorderLayout());
1215 groupPanel = new JPanel();
1216 bigPanel.add(groupPanel, BorderLayout.NORTH);
1218 JButton invert = new JButton(
1219 MessageManager.getString("label.invert_selection"));
1220 invert.setFont(JvSwingUtils.getLabelFont());
1221 invert.addActionListener(new ActionListener()
1224 public void actionPerformed(ActionEvent e)
1230 JButton optimizeOrder = new JButton(
1231 MessageManager.getString("label.optimise_order"));
1232 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1233 optimizeOrder.addActionListener(new ActionListener()
1236 public void actionPerformed(ActionEvent e)
1242 JButton sortByScore = new JButton(
1243 MessageManager.getString("label.seq_sort_by_score"));
1244 sortByScore.setFont(JvSwingUtils.getLabelFont());
1245 sortByScore.addActionListener(new ActionListener()
1248 public void actionPerformed(ActionEvent e)
1250 af.avc.sortAlignmentByFeatureScore(null);
1253 JButton sortByDens = new JButton(
1254 MessageManager.getString("label.sequence_sort_by_density"));
1255 sortByDens.setFont(JvSwingUtils.getLabelFont());
1256 sortByDens.addActionListener(new ActionListener()
1259 public void actionPerformed(ActionEvent e)
1261 af.avc.sortAlignmentByFeatureDensity(null);
1265 JButton help = new JButton(MessageManager.getString("action.help"));
1266 help.setFont(JvSwingUtils.getLabelFont());
1267 help.addActionListener(new ActionListener()
1270 public void actionPerformed(ActionEvent e)
1274 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1275 } catch (HelpSetException e1)
1277 e1.printStackTrace();
1282 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1283 cancel.setFont(JvSwingUtils.getLabelFont());
1284 cancel.addActionListener(new ActionListener()
1287 public void actionPerformed(ActionEvent e)
1289 fr.setTransparency(originalTransparency);
1290 fr.setFeatureFilters(originalFilters);
1291 updateFeatureRenderer(originalData);
1292 af.getViewport().setViewStyle(originalViewStyle);
1297 JButton ok = new JButton(MessageManager.getString("action.ok"));
1298 ok.setFont(JvSwingUtils.getLabelFont());
1299 ok.addActionListener(new ActionListener()
1302 public void actionPerformed(ActionEvent e)
1308 JButton loadColours = new JButton(
1309 MessageManager.getString("label.load_colours"));
1310 loadColours.setFont(JvSwingUtils.getLabelFont());
1311 loadColours.setToolTipText(
1312 MessageManager.getString("label.load_colours_tooltip"));
1313 loadColours.addActionListener(new ActionListener()
1316 public void actionPerformed(ActionEvent e)
1322 JButton saveColours = new JButton(
1323 MessageManager.getString("label.save_colours"));
1324 saveColours.setFont(JvSwingUtils.getLabelFont());
1325 saveColours.setToolTipText(
1326 MessageManager.getString("label.save_colours_tooltip"));
1327 saveColours.addActionListener(new ActionListener()
1330 public void actionPerformed(ActionEvent e)
1335 transparency.addChangeListener(new ChangeListener()
1338 public void stateChanged(ChangeEvent evt)
1340 if (!inConstruction)
1342 fr.setTransparency((100 - transparency.getValue()) / 100f);
1348 transparency.setMaximum(70);
1349 transparency.setToolTipText(
1350 MessageManager.getString("label.transparency_tip"));
1352 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1353 String text = MessageManager.formatMessage("label.show_linked_features",
1355 ? MessageManager.getString("label.protein")
1358 JCheckBox showComplement = new JCheckBox(text);
1359 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
1360 showComplement.addActionListener(new ActionListener()
1363 public void actionPerformed(ActionEvent e)
1366 .setShowComplementFeatures(showComplement.isSelected());
1371 JCheckBox showComplementOnTop = new JCheckBox(
1372 MessageManager.getString("label.on_top"));
1374 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
1375 showComplementOnTop.addActionListener(new ActionListener()
1378 public void actionPerformed(ActionEvent e)
1380 af.getViewport().setShowComplementFeaturesOnTop(
1381 showComplementOnTop.isSelected());
1386 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1387 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1389 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1390 transbuttons.add(optimizeOrder);
1391 transbuttons.add(invert);
1392 transbuttons.add(sortByScore);
1393 transbuttons.add(sortByDens);
1394 transbuttons.add(help);
1396 boolean hasComplement = af.getViewport().getCodingComplement() != null;
1397 JPanel transPanelLeft = new JPanel(
1398 new GridLayout(hasComplement ? 3 : 2, 1));
1399 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1400 transPanelLeft.add(transparency);
1403 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1404 cp.add(showComplement);
1405 cp.add(showComplementOnTop);
1406 transPanelLeft.add(cp);
1408 lowerPanel.add(transPanelLeft);
1409 lowerPanel.add(transbuttons);
1411 JPanel buttonPanel = new JPanel();
1412 buttonPanel.add(ok);
1413 buttonPanel.add(cancel);
1414 buttonPanel.add(loadColours);
1415 buttonPanel.add(saveColours);
1416 bigPanel.add(scrollPane, BorderLayout.CENTER);
1417 settingsPane.add(bigPanel, BorderLayout.CENTER);
1418 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1419 this.add(settingsPane);
1423 * Repaints alignment, structure and overview (if shown). If there is a
1424 * complementary view which is showing this view's features, then also
1427 void refreshDisplay()
1429 af.alignPanel.paintAlignment(true, true);
1430 AlignViewportI complement = af.getViewport().getCodingComplement();
1431 if (complement != null && complement.isShowComplementFeatures())
1433 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1434 af2.alignPanel.paintAlignment(true, true);
1439 * Answers a suitable tooltip to show on the colour cell of the table
1443 * if true include 'click to edit' and similar text
1446 public static String getColorTooltip(FeatureColourI fcol,
1453 if (fcol.isSimpleColour())
1455 return withHint ? BASE_TOOLTIP : null;
1457 String description = fcol.getDescription();
1458 description = description.replaceAll("<", "<");
1459 description = description.replaceAll(">", ">");
1460 StringBuilder tt = new StringBuilder(description);
1463 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1465 return JvSwingUtils.wrapTooltip(true, tt.toString());
1468 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1471 boolean thr = false;
1472 StringBuilder tx = new StringBuilder();
1474 if (gcol.isColourByAttribute())
1476 tx.append(FeatureMatcher
1477 .toAttributeDisplayName(gcol.getAttributeName()));
1479 else if (!gcol.isColourByLabel())
1481 tx.append(MessageManager.getString("label.score"));
1484 if (gcol.isAboveThreshold())
1489 if (gcol.isBelowThreshold())
1494 if (gcol.isColourByLabel())
1500 if (!gcol.isColourByAttribute())
1508 Color newColor = gcol.getMaxColour();
1509 comp.setBackground(newColor);
1510 // System.err.println("Width is " + w / 2);
1511 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1512 comp.setIcon(ficon);
1513 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1514 // + newColor.getGreen() + ", " + newColor.getBlue()
1515 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1516 // + ", " + minCol.getBlue() + ")");
1518 comp.setHorizontalAlignment(SwingConstants.CENTER);
1519 comp.setText(tx.toString());
1522 // ///////////////////////////////////////////////////////////////////////
1523 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1524 // ///////////////////////////////////////////////////////////////////////
1525 class FeatureTableModel extends AbstractTableModel
1527 private String[] columnNames = {
1528 MessageManager.getString("label.feature_type"),
1529 MessageManager.getString("action.colour"),
1530 MessageManager.getString("label.configuration"),
1531 MessageManager.getString("label.show") };
1533 private Object[][] data;
1535 FeatureTableModel(Object[][] data)
1540 public Object[][] getData()
1545 public void setData(Object[][] data)
1551 public int getColumnCount()
1553 return columnNames.length;
1556 public Object[] getRow(int row)
1562 public int getRowCount()
1568 public String getColumnName(int col)
1570 return columnNames[col];
1574 public Object getValueAt(int row, int col)
1576 return data[row][col];
1580 * Answers the class of column c of the table
1583 public Class<?> getColumnClass(int c)
1588 return String.class;
1590 return FeatureColour.class;
1592 return FeatureMatcherSet.class;
1594 return Boolean.class;
1599 public boolean isCellEditable(int row, int col)
1601 return col == 0 ? false : true;
1605 public void setValueAt(Object value, int row, int col)
1607 data[row][col] = value;
1608 fireTableCellUpdated(row, col);
1609 updateFeatureRenderer(data);
1614 class ColorRenderer extends JLabel implements TableCellRenderer
1616 Border unselectedBorder = null;
1618 Border selectedBorder = null;
1620 public ColorRenderer()
1622 setOpaque(true); // MUST do this for background to show up.
1623 setHorizontalTextPosition(SwingConstants.CENTER);
1624 setVerticalTextPosition(SwingConstants.CENTER);
1628 public Component getTableCellRendererComponent(JTable tbl, Object color,
1629 boolean isSelected, boolean hasFocus, int row, int column)
1631 FeatureColourI cellColour = (FeatureColourI) color;
1633 setBackground(tbl.getBackground());
1634 if (!cellColour.isSimpleColour())
1636 Rectangle cr = tbl.getCellRect(row, column, false);
1637 FeatureSettings.renderGraduatedColor(this, cellColour,
1638 (int) cr.getWidth(), (int) cr.getHeight());
1644 setBackground(cellColour.getColour());
1648 if (selectedBorder == null)
1650 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1651 tbl.getSelectionBackground());
1653 setBorder(selectedBorder);
1657 if (unselectedBorder == null)
1659 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1660 tbl.getBackground());
1662 setBorder(unselectedBorder);
1669 class FilterRenderer extends JLabel implements TableCellRenderer
1671 javax.swing.border.Border unselectedBorder = null;
1673 javax.swing.border.Border selectedBorder = null;
1675 public FilterRenderer()
1677 setOpaque(true); // MUST do this for background to show up.
1678 setHorizontalTextPosition(SwingConstants.CENTER);
1679 setVerticalTextPosition(SwingConstants.CENTER);
1683 public Component getTableCellRendererComponent(JTable tbl,
1684 Object filter, boolean isSelected, boolean hasFocus, int row,
1687 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1689 String asText = theFilter.toString();
1690 setBackground(tbl.getBackground());
1691 this.setText(asText);
1696 if (selectedBorder == null)
1698 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1699 tbl.getSelectionBackground());
1701 setBorder(selectedBorder);
1705 if (unselectedBorder == null)
1707 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1708 tbl.getBackground());
1710 setBorder(unselectedBorder);
1718 * update comp using rendering settings from gcol
1723 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1725 int w = comp.getWidth(), h = comp.getHeight();
1728 w = (int) comp.getPreferredSize().getWidth();
1729 h = (int) comp.getPreferredSize().getHeight();
1736 renderGraduatedColor(comp, gcol, w, h);
1739 @SuppressWarnings("serial")
1740 class ColorEditor extends AbstractCellEditor
1741 implements TableCellEditor, ActionListener
1743 FeatureColourI currentColor;
1745 FeatureTypeSettings chooser;
1751 protected static final String EDIT = "edit";
1753 int rowSelected = 0;
1755 public ColorEditor()
1757 // Set up the editor (from the table's point of view),
1758 // which is a button.
1759 // This button brings up the color chooser dialog,
1760 // which is the editor from the user's point of view.
1761 button = new JButton();
1762 button.setActionCommand(EDIT);
1763 button.addActionListener(this);
1764 button.setBorderPainted(false);
1768 * Handles events from the editor button, and from the colour/filters
1769 * dialog's OK button
1772 public void actionPerformed(ActionEvent e)
1774 if (button == e.getSource())
1776 if (currentColor.isSimpleColour())
1779 * simple colour chooser
1781 String ttl = MessageManager
1782 .formatMessage("label.select_colour_for", type);
1783 ColourChooserListener listener = new ColourChooserListener()
1786 public void colourSelected(Color c)
1788 currentColor = new FeatureColour(c);
1789 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1790 fireEditingStopped();
1794 public void cancel()
1796 fireEditingStopped();
1799 JalviewColourChooser.showColourChooser(button, ttl,
1800 currentColor.getColour(), listener);
1805 * variable colour and filters dialog
1807 chooser = new FeatureTypeSettings(fr, type);
1808 if (!Platform.isJS())
1815 chooser.setRequestFocusEnabled(true);
1816 chooser.requestFocus();
1818 chooser.addActionListener(this);
1819 fireEditingStopped();
1825 * after OK in variable colour dialog, any changes to colour
1826 * (or filters!) are already set in FeatureRenderer, so just
1827 * update table data without triggering updateFeatureRenderer
1829 currentColor = fr.getFeatureColours().get(type);
1830 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1831 if (currentFilter == null)
1833 currentFilter = new FeatureMatcherSet();
1835 Object[] data = ((FeatureTableModel) table.getModel())
1836 .getData()[rowSelected];
1837 data[COLOUR_COLUMN] = currentColor;
1838 data[FILTER_COLUMN] = currentFilter;
1839 fireEditingStopped();
1840 // SwingJS needs an explicit repaint() here,
1841 // rather than relying upon no validation having
1842 // occurred since the stopEditing call was made.
1843 // Its laying out has not been stopped by the modal frame
1850 * Override allows access to this method from anonymous inner classes
1853 protected void fireEditingStopped()
1855 super.fireEditingStopped();
1858 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1860 public Object getCellEditorValue()
1862 return currentColor;
1865 // Implement the one method defined by TableCellEditor.
1867 public Component getTableCellEditorComponent(JTable theTable,
1868 Object value, boolean isSelected, int row, int column)
1870 currentColor = (FeatureColourI) value;
1871 this.rowSelected = row;
1872 type = table.getValueAt(row, TYPE_COLUMN).toString();
1873 button.setOpaque(true);
1874 button.setBackground(FeatureSettings.this.getBackground());
1875 if (!currentColor.isSimpleColour())
1877 JLabel btn = new JLabel();
1878 btn.setSize(button.getSize());
1879 FeatureSettings.renderGraduatedColor(btn, currentColor);
1880 button.setBackground(btn.getBackground());
1881 button.setIcon(btn.getIcon());
1882 button.setText(btn.getText());
1887 button.setIcon(null);
1888 button.setBackground(currentColor.getColour());
1895 * The cell editor for the Filter column. It displays the text of any filters
1896 * for the feature type in that row (in full as a tooltip, possible
1897 * abbreviated as display text). On click in the cell, opens the Feature
1898 * Display Settings dialog at the Filters tab.
1900 @SuppressWarnings("serial")
1901 class FilterEditor extends AbstractCellEditor
1902 implements TableCellEditor, ActionListener
1905 FeatureMatcherSetI currentFilter;
1913 protected static final String EDIT = "edit";
1915 int rowSelected = 0;
1917 public FilterEditor()
1919 button = new JButton();
1920 button.setActionCommand(EDIT);
1921 button.addActionListener(this);
1922 button.setBorderPainted(false);
1926 * Handles events from the editor button
1929 public void actionPerformed(ActionEvent e)
1931 if (button == e.getSource())
1933 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
1934 chooser.addActionListener(this);
1935 chooser.setRequestFocusEnabled(true);
1936 chooser.requestFocus();
1937 if (lastLocation != null)
1939 // todo open at its last position on screen
1940 chooser.setBounds(lastLocation.x, lastLocation.y,
1941 chooser.getWidth(), chooser.getHeight());
1944 fireEditingStopped();
1946 else if (e.getSource() instanceof Component)
1950 * after OK in variable colour dialog, any changes to filter
1951 * (or colours!) are already set in FeatureRenderer, so just
1952 * update table data without triggering updateFeatureRenderer
1954 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1955 currentFilter = fr.getFeatureFilter(type);
1956 if (currentFilter == null)
1958 currentFilter = new FeatureMatcherSet();
1961 Object[] data = ((FeatureTableModel) table.getModel())
1962 .getData()[rowSelected];
1963 data[COLOUR_COLUMN] = currentColor;
1964 data[FILTER_COLUMN] = currentFilter;
1965 fireEditingStopped();
1966 // SwingJS needs an explicit repaint() here,
1967 // rather than relying upon no validation having
1968 // occurred since the stopEditing call was made.
1969 // Its laying out has not been stopped by the modal frame
1976 public Object getCellEditorValue()
1978 return currentFilter;
1982 public Component getTableCellEditorComponent(JTable theTable,
1983 Object value, boolean isSelected, int row, int column)
1985 currentFilter = (FeatureMatcherSetI) value;
1986 this.rowSelected = row;
1987 type = table.getValueAt(row, TYPE_COLUMN).toString();
1988 button.setOpaque(true);
1989 button.setBackground(FeatureSettings.this.getBackground());
1990 button.setText(currentFilter.toString());
1991 button.setIcon(null);
1997 class FeatureIcon implements Icon
1999 FeatureColourI gcol;
2003 boolean midspace = false;
2005 int width = 50, height = 20;
2007 int s1, e1; // start and end of midpoint band for thresholded symbol
2009 Color mpcolour = Color.white;
2011 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2031 public int getIconWidth()
2037 public int getIconHeight()
2043 public void paintIcon(Component c, Graphics g, int x, int y)
2046 if (gcol.isColourByLabel())
2049 g.fillRect(0, 0, width, height);
2050 // need an icon here.
2051 g.setColor(gcol.getMaxColour());
2053 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2055 // g.setFont(g.getFont().deriveFont(
2056 // AffineTransform.getScaleInstance(
2057 // width/g.getFontMetrics().stringWidth("Label"),
2058 // height/g.getFontMetrics().getHeight())));
2060 g.drawString(MessageManager.getString("label.label"), 0, 0);
2065 Color minCol = gcol.getMinColour();
2067 g.fillRect(0, 0, s1, height);
2070 g.setColor(Color.white);
2071 g.fillRect(s1, 0, e1 - s1, height);
2073 g.setColor(gcol.getMaxColour());
2074 // g.fillRect(0, e1, width - e1, height); // BH 2018
2075 g.fillRect(e1, 0, width - e1, height);