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 java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Component;
26 import java.awt.Dimension;
27 import java.awt.FlowLayout;
29 import java.awt.Graphics;
30 import java.awt.GridLayout;
31 import java.awt.Point;
32 import java.awt.Rectangle;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.ItemEvent;
36 import java.awt.event.ItemListener;
37 import java.awt.event.MouseAdapter;
38 import java.awt.event.MouseEvent;
39 import java.awt.event.MouseMotionAdapter;
40 import java.beans.PropertyChangeEvent;
41 import java.beans.PropertyChangeListener;
43 import java.io.FileInputStream;
44 import java.io.FileOutputStream;
45 import java.io.InputStreamReader;
46 import java.io.OutputStreamWriter;
47 import java.io.PrintWriter;
48 import java.util.Arrays;
49 import java.util.Comparator;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.Hashtable;
53 import java.util.Iterator;
54 import java.util.List;
55 import java.util.Locale;
59 import javax.help.HelpSetException;
60 import javax.swing.AbstractCellEditor;
61 import javax.swing.BorderFactory;
62 import javax.swing.Icon;
63 import javax.swing.JButton;
64 import javax.swing.JCheckBox;
65 import javax.swing.JInternalFrame;
66 import javax.swing.JLabel;
67 import javax.swing.JLayeredPane;
68 import javax.swing.JMenuItem;
69 import javax.swing.JPanel;
70 import javax.swing.JPopupMenu;
71 import javax.swing.JScrollPane;
72 import javax.swing.JSlider;
73 import javax.swing.JTable;
74 import javax.swing.ListSelectionModel;
75 import javax.swing.SwingConstants;
76 import javax.swing.ToolTipManager;
77 import javax.swing.border.Border;
78 import javax.swing.event.ChangeEvent;
79 import javax.swing.event.ChangeListener;
80 import javax.swing.table.AbstractTableModel;
81 import javax.swing.table.JTableHeader;
82 import javax.swing.table.TableCellEditor;
83 import javax.swing.table.TableCellRenderer;
84 import javax.swing.table.TableColumn;
85 import javax.xml.bind.JAXBContext;
86 import javax.xml.bind.JAXBElement;
87 import javax.xml.bind.Marshaller;
88 import javax.xml.stream.XMLInputFactory;
89 import javax.xml.stream.XMLStreamReader;
91 import jalview.api.AlignViewControllerGuiI;
92 import jalview.api.AlignViewportI;
93 import jalview.api.FeatureColourI;
94 import jalview.api.FeatureSettingsControllerI;
95 import jalview.api.SplitContainerI;
96 import jalview.api.ViewStyleI;
97 import jalview.controller.FeatureSettingsControllerGuiI;
98 import jalview.datamodel.AlignmentI;
99 import jalview.datamodel.SequenceI;
100 import jalview.datamodel.features.FeatureMatcher;
101 import jalview.datamodel.features.FeatureMatcherI;
102 import jalview.datamodel.features.FeatureMatcherSet;
103 import jalview.datamodel.features.FeatureMatcherSetI;
104 import jalview.gui.Help.HelpId;
105 import jalview.gui.JalviewColourChooser.ColourChooserListener;
106 import jalview.io.JalviewFileChooser;
107 import jalview.io.JalviewFileView;
108 import jalview.schemes.FeatureColour;
109 import jalview.util.MessageManager;
110 import jalview.util.Platform;
111 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
112 import jalview.viewmodel.styles.ViewStyle;
113 import jalview.xml.binding.jalview.JalviewUserColours;
114 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
115 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
116 import jalview.xml.binding.jalview.ObjectFactory;
118 public class FeatureSettings extends JPanel
119 implements FeatureSettingsControllerI, FeatureSettingsControllerGuiI
121 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
122 .getString("label.sequence_feature_colours");
125 * column indices of fields in Feature Settings table
127 static final int TYPE_COLUMN = 0;
129 static final int COLOUR_COLUMN = 1;
131 static final int FILTER_COLUMN = 2;
133 static final int SHOW_COLUMN = 3;
135 private static final int COLUMN_COUNT = 4;
137 private static final int MIN_WIDTH = 400;
139 private static final int MIN_HEIGHT = 400;
141 private final static String BASE_TOOLTIP = MessageManager
142 .getString("label.click_to_edit");
144 final FeatureRenderer fr;
146 public final AlignFrame af;
149 * 'original' fields hold settings to restore on Cancel
151 Object[][] originalData;
153 private float originalTransparency;
155 private ViewStyleI originalViewStyle;
157 private Map<String, FeatureMatcherSetI> originalFilters;
159 final JInternalFrame frame;
161 JScrollPane scrollPane = new JScrollPane();
167 JSlider transparency = new JSlider();
169 private JCheckBox showComplementOnTop;
171 private JCheckBox showComplement;
174 * when true, constructor is still executing - so ignore UI events
176 protected volatile boolean inConstruction = true;
178 int selectedRow = -1;
180 boolean resettingTable = false;
183 * true when Feature Settings are updating from feature renderer
185 private boolean handlingUpdate = false;
188 * a change listener to ensure the dialog is updated if
189 * FeatureRenderer discovers new features
191 private PropertyChangeListener change;
194 * holds {featureCount, totalExtent} for each feature type
196 Map<String, float[]> typeWidth = null;
198 private void storeOriginalSettings()
200 // save transparency for restore on Cancel
201 originalTransparency = fr.getTransparency();
203 updateTransparencySliderFromFR();
205 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
206 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
209 private void updateTransparencySliderFromFR()
211 boolean incon = inConstruction;
212 inConstruction = true;
214 int transparencyAsPercent = (int) (fr.getTransparency() * 100);
215 transparency.setValue(100 - transparencyAsPercent);
216 inConstruction = incon;
224 public FeatureSettings(AlignFrame alignFrame)
226 this.af = alignFrame;
227 fr = af.getFeatureRenderer();
229 storeOriginalSettings();
234 } catch (Exception ex)
236 ex.printStackTrace();
242 public String getToolTipText(MouseEvent e)
245 int column = table.columnAtPoint(e.getPoint());
246 int row = table.rowAtPoint(e.getPoint());
251 tip = JvSwingUtils.wrapTooltip(true, MessageManager
252 .getString("label.feature_settings_click_drag"));
255 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
257 tip = getColorTooltip(colour, true);
260 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
264 .getString("label.configure_feature_tooltip")
275 * Position the tooltip near the bottom edge of, and half way across, the
279 public Point getToolTipLocation(MouseEvent e)
281 Point point = e.getPoint();
282 int column = table.columnAtPoint(point);
283 int row = table.rowAtPoint(point);
284 Rectangle r = getCellRect(row, column, false);
285 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
289 JTableHeader tableHeader = table.getTableHeader();
290 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
291 tableHeader.setReorderingAllowed(false);
292 table.setFont(new Font("Verdana", Font.PLAIN, 12));
293 ToolTipManager.sharedInstance().registerComponent(table);
294 table.setDefaultEditor(FeatureColour.class, new ColorEditor());
295 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
297 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor());
298 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
300 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
301 new ColorRenderer(), new ColorEditor());
302 table.addColumn(colourColumn);
304 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
305 new FilterRenderer(), new FilterEditor());
306 table.addColumn(filterColumn);
308 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
310 table.addMouseListener(new MouseAdapter()
313 public void mousePressed(MouseEvent evt)
315 Point pt = evt.getPoint();
316 selectedRow = table.rowAtPoint(pt);
317 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
318 if (evt.isPopupTrigger())
320 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
321 showPopupMenu(selectedRow, type, colour, evt.getPoint());
323 else if (evt.getClickCount() == 2
324 && table.columnAtPoint(pt) == TYPE_COLUMN)
326 boolean invertSelection = evt.isAltDown();
327 boolean toggleSelection = Platform.isControlDown(evt);
328 boolean extendSelection = evt.isShiftDown();
329 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
330 invertSelection, extendSelection, toggleSelection, type);
331 fr.ap.av.sendSelection();
335 // isPopupTrigger fires on mouseReleased on Windows
337 public void mouseReleased(MouseEvent evt)
339 selectedRow = table.rowAtPoint(evt.getPoint());
340 if (evt.isPopupTrigger())
342 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
343 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
344 showPopupMenu(selectedRow, type, colour, evt.getPoint());
349 table.addMouseMotionListener(new MouseMotionAdapter()
352 public void mouseDragged(MouseEvent evt)
354 int newRow = table.rowAtPoint(evt.getPoint());
355 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
358 * reposition 'selectedRow' to 'newRow' (the dragged to location)
359 * this could be more than one row away for a very fast drag action
360 * so just swap it with adjacent rows until we get it there
362 Object[][] data = ((FeatureTableModel) table.getModel())
364 int direction = newRow < selectedRow ? -1 : 1;
365 for (int i = selectedRow; i != newRow; i += direction)
367 Object[] temp = data[i];
368 data[i] = data[i + direction];
369 data[i + direction] = temp;
371 updateFeatureRenderer(data);
373 selectedRow = newRow;
377 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
378 // MessageManager.getString("label.feature_settings_click_drag")));
379 scrollPane.setViewportView(table);
381 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
383 fr.findAllFeatures(true); // display everything!
386 discoverAllFeatureData();
387 final FeatureSettings fs = this;
388 fr.addPropertyChangeListener(change = new PropertyChangeListener()
391 public void propertyChange(PropertyChangeEvent evt)
393 if (!fs.resettingTable && !fs.handlingUpdate)
395 fs.handlingUpdate = true;
397 // new groups may be added with new sequence feature types only
398 fs.handlingUpdate = false;
404 SplitContainerI splitframe = af.getSplitViewContainer();
405 if (splitframe != null)
407 frame = null; // keeps eclipse happy
408 splitframe.addFeatureSettingsUI(this);
412 frame = new JInternalFrame();
413 frame.setContentPane(this);
414 Rectangle bounds = af.getFeatureSettingsGeometry();
416 if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels(
417 af.alignPanel.av.getSequenceSetId()).length > 1)
419 title = MessageManager.formatMessage(
420 "label.sequence_feature_settings_for_view",
421 af.alignPanel.getViewName());
425 title = MessageManager.getString("label.sequence_feature_settings");
429 if (Platform.isAMacAndNotJS())
431 Desktop.addInternalFrame(frame, title, 600, 480);
435 Desktop.addInternalFrame(frame, title, 600, 450);
440 Desktop.addInternalFrame(frame, title, false, bounds.width,
442 frame.setBounds(bounds);
443 frame.setVisible(true);
445 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
447 frame.addInternalFrameListener(
448 new javax.swing.event.InternalFrameAdapter()
451 public void internalFrameClosed(
452 javax.swing.event.InternalFrameEvent evt)
454 featureSettings_isClosed();
457 frame.setLayer(JLayeredPane.PALETTE_LAYER);
459 inConstruction = false;
463 * Sets the state of buttons to show complement features from viewport
466 private void updateComplementButtons()
468 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
470 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
474 public AlignViewControllerGuiI getAlignframe()
480 public void featureSettings_isClosed()
482 fr.removePropertyChangeListener(change);
487 * Constructs and shows a popup menu of possible actions on the selected row
495 protected void showPopupMenu(final int rowSelected, final String type,
496 final Object typeCol, final Point pt)
498 JPopupMenu men = new JPopupMenu(MessageManager
499 .formatMessage("label.settings_for_param", new String[]
502 JMenuItem scr = new JMenuItem(
503 MessageManager.getString("label.sort_by_score"));
505 scr.addActionListener(new ActionListener()
508 public void actionPerformed(ActionEvent e)
510 sortByScore(Arrays.asList(new String[] { type }));
513 JMenuItem dens = new JMenuItem(
514 MessageManager.getString("label.sort_by_density"));
515 dens.addActionListener(new ActionListener()
518 public void actionPerformed(ActionEvent e)
520 sortByDensity(Arrays.asList(new String[] { type }));
525 JMenuItem selCols = new JMenuItem(
526 MessageManager.getString("label.select_columns_containing"));
527 selCols.addActionListener(new ActionListener()
530 public void actionPerformed(ActionEvent arg0)
532 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
534 fr.ap.av.sendSelection();
537 JMenuItem clearCols = new JMenuItem(MessageManager
538 .getString("label.select_columns_not_containing"));
539 clearCols.addActionListener(new ActionListener()
542 public void actionPerformed(ActionEvent arg0)
544 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
546 fr.ap.av.sendSelection();
549 JMenuItem hideCols = new JMenuItem(
550 MessageManager.getString("label.hide_columns_containing"));
551 hideCols.addActionListener(new ActionListener()
554 public void actionPerformed(ActionEvent arg0)
556 fr.ap.alignFrame.hideFeatureColumns(type, true);
557 fr.ap.av.sendSelection();
560 JMenuItem hideOtherCols = new JMenuItem(
561 MessageManager.getString("label.hide_columns_not_containing"));
562 hideOtherCols.addActionListener(new ActionListener()
565 public void actionPerformed(ActionEvent arg0)
567 fr.ap.alignFrame.hideFeatureColumns(type, false);
568 fr.ap.av.sendSelection();
574 men.add(hideOtherCols);
575 men.show(table, pt.x, pt.y);
579 * Sort the sequences in the alignment by the number of features for the given
580 * feature types (or all features if null)
582 * @param featureTypes
584 protected void sortByDensity(List<String> featureTypes)
586 af.avc.sortAlignmentByFeatureDensity(featureTypes);
590 * Sort the sequences in the alignment by average score for the given feature
591 * types (or all features if null)
593 * @param featureTypes
595 protected void sortByScore(List<String> featureTypes)
597 af.avc.sortAlignmentByFeatureScore(featureTypes);
601 * Returns true if at least one feature type is visible. Else shows a warning
602 * dialog and returns false.
607 private boolean canSortBy(String title)
609 if (fr.getDisplayedFeatureTypes().isEmpty())
611 JvOptionPane.showMessageDialog(this,
612 MessageManager.getString("label.no_features_to_sort_by"),
613 title, JvOptionPane.OK_OPTION);
620 synchronized public void discoverAllFeatureData()
622 Set<String> allGroups = new HashSet<>();
623 AlignmentI alignment = af.getViewport().getAlignment();
625 for (int i = 0; i < alignment.getHeight(); i++)
627 SequenceI seq = alignment.getSequenceAt(i);
628 for (String group : seq.getFeatures().getFeatureGroups(true))
630 if (group != null && !allGroups.contains(group))
632 allGroups.add(group);
633 checkGroupState(group);
644 * Synchronise gui group list and check visibility of group
647 * @return true if group is visible
649 private boolean checkGroupState(String group)
651 boolean visible = fr.checkGroupVisibility(group, true);
653 for (int g = 0; g < groupPanel.getComponentCount(); g++)
655 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
657 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
662 final String grp = group;
663 final JCheckBox check = new JCheckBox(group, visible);
664 check.setFont(new Font("Serif", Font.BOLD, 12));
665 check.setToolTipText(group);
666 check.addItemListener(new ItemListener()
669 public void itemStateChanged(ItemEvent evt)
671 fr.setGroupVisibility(check.getText(), check.isSelected());
672 resetTable(new String[] { grp });
676 groupPanel.add(check);
680 synchronized void resetTable(String[] groupChanged)
686 resettingTable = true;
687 typeWidth = new Hashtable<>();
688 // TODO: change avWidth calculation to 'per-sequence' average and use long
691 Set<String> displayableTypes = new HashSet<>();
692 Set<String> foundGroups = new HashSet<>();
695 * determine which feature types may be visible depending on
696 * which groups are selected, and recompute average width data
698 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
701 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
704 * get the sequence's groups for positional features
705 * and keep track of which groups are visible
707 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
708 Set<String> visibleGroups = new HashSet<>();
709 for (String group : groups)
711 if (group == null || checkGroupState(group))
713 visibleGroups.add(group);
716 foundGroups.addAll(groups);
719 * get distinct feature types for visible groups
720 * record distinct visible types, and their count and total length
722 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
723 visibleGroups.toArray(new String[visibleGroups.size()]));
724 for (String type : types)
726 displayableTypes.add(type);
727 float[] avWidth = typeWidth.get(type);
730 avWidth = new float[2];
731 typeWidth.put(type, avWidth);
733 // todo this could include features with a non-visible group
734 // - do we greatly care?
735 // todo should we include non-displayable features here, and only
736 // update when features are added?
737 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
738 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
742 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
745 if (fr.hasRenderOrder())
749 fr.findAllFeatures(groupChanged != null); // prod to update
750 // colourschemes. but don't
752 // First add the checks in the previous render order,
753 // in case the window has been closed and reopened
755 List<String> frl = fr.getRenderOrder();
756 for (int ro = frl.size() - 1; ro > -1; ro--)
758 String type = frl.get(ro);
760 if (!displayableTypes.contains(type))
765 data[dataIndex][TYPE_COLUMN] = type;
766 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
767 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
768 data[dataIndex][FILTER_COLUMN] = featureFilter == null
769 ? new FeatureMatcherSet()
771 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
772 af.getViewport().getFeaturesDisplayed().isVisible(type));
774 displayableTypes.remove(type);
779 * process any extra features belonging only to
780 * a group which was just selected
782 while (!displayableTypes.isEmpty())
784 String type = displayableTypes.iterator().next();
785 data[dataIndex][TYPE_COLUMN] = type;
787 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
788 if (data[dataIndex][COLOUR_COLUMN] == null)
790 // "Colour has been updated in another view!!"
791 fr.clearRenderOrder();
794 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
795 data[dataIndex][FILTER_COLUMN] = featureFilter == null
796 ? new FeatureMatcherSet()
798 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
800 displayableTypes.remove(type);
803 if (originalData == null)
805 originalData = new Object[data.length][COLUMN_COUNT];
806 for (int i = 0; i < data.length; i++)
808 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
813 updateOriginalData(data);
816 table.setModel(new FeatureTableModel(data));
817 table.getColumnModel().getColumn(0).setPreferredWidth(200);
819 groupPanel.setLayout(
820 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
821 pruneGroups(foundGroups);
822 groupPanel.validate();
824 updateFeatureRenderer(data, groupChanged != null);
825 resettingTable = false;
829 * Updates 'originalData' (used for restore on Cancel) if we detect that
830 * changes have been made outwith this dialog
832 * <li>a new feature type added (and made visible)</li>
833 * <li>a feature colour changed (in the Amend Features dialog)</li>
838 protected void updateOriginalData(Object[][] foundData)
840 // todo LinkedHashMap instead of Object[][] would be nice
842 Object[][] currentData = ((FeatureTableModel) table.getModel())
844 for (Object[] row : foundData)
846 String type = (String) row[TYPE_COLUMN];
847 boolean found = false;
848 for (Object[] current : currentData)
850 if (type.equals(current[TYPE_COLUMN]))
854 * currently dependent on object equality here;
855 * really need an equals method on FeatureColour
857 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
860 * feature colour has changed externally - update originalData
862 for (Object[] original : originalData)
864 if (type.equals(original[TYPE_COLUMN]))
866 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
877 * new feature detected - add to original data (on top)
879 Object[][] newData = new Object[originalData.length
881 for (int i = 0; i < originalData.length; i++)
883 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
887 originalData = newData;
893 * Remove from the groups panel any checkboxes for groups that are not in the
894 * foundGroups set. This enables removing a group from the display when the
895 * last feature in that group is deleted.
899 protected void pruneGroups(Set<String> foundGroups)
901 for (int g = 0; g < groupPanel.getComponentCount(); g++)
903 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
904 if (!foundGroups.contains(checkbox.getText()))
906 groupPanel.remove(checkbox);
912 * reorder data based on the featureRenderers global priority list.
916 private void ensureOrder(Object[][] data)
918 boolean sort = false;
919 float[] order = new float[data.length];
920 for (int i = 0; i < order.length; i++)
922 order[i] = fr.getOrder(data[i][0].toString());
925 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
929 sort = sort || order[i - 1] > order[i];
934 jalview.util.QuickSort.sort(order, data);
939 * Offers a file chooser dialog, and then loads the feature colours and
940 * filters from file in XML format and unmarshals to Jalview feature settings
944 JalviewFileChooser chooser = new JalviewFileChooser("fc",
945 SEQUENCE_FEATURE_COLOURS);
946 chooser.setFileView(new JalviewFileView());
947 chooser.setDialogTitle(
948 MessageManager.getString("label.load_feature_colours"));
949 chooser.setToolTipText(MessageManager.getString("action.load"));
950 chooser.setResponseHandler(0, () -> {
951 File file = chooser.getSelectedFile();
955 chooser.showOpenDialog(this);
959 * Loads feature colours and filters from XML stored in the given file
967 InputStreamReader in = new InputStreamReader(
968 new FileInputStream(file), "UTF-8");
970 JAXBContext jc = JAXBContext
971 .newInstance("jalview.xml.binding.jalview");
972 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
973 XMLStreamReader streamReader = XMLInputFactory.newInstance()
974 .createXMLStreamReader(in);
975 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
976 JalviewUserColours.class);
977 JalviewUserColours jucs = jbe.getValue();
979 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
982 * load feature colours
984 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
986 Colour newcol = jucs.getColour().get(i);
987 FeatureColourI colour = jalview.project.Jalview2XML
988 .parseColour(newcol);
989 fr.setColour(newcol.getName(), colour);
990 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
994 * load feature filters; loaded filters will replace any that are
995 * currently defined, other defined filters are left unchanged
997 for (int i = 0; i < jucs.getFilter().size(); i++)
999 Filter filterModel = jucs.getFilter().get(i);
1000 String featureType = filterModel.getFeatureType();
1001 FeatureMatcherSetI filter = jalview.project.Jalview2XML
1002 .parseFilter(featureType, filterModel.getMatcherSet());
1003 if (!filter.isEmpty())
1005 fr.setFeatureFilter(featureType, filter);
1010 * update feature settings table
1015 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1017 updateFeatureRenderer(data, false);
1020 } catch (Exception ex)
1022 System.out.println("Error loading User Colour File\n" + ex);
1027 * Offers a file chooser dialog, and then saves the current feature colours
1028 * and any filters to the selected file in XML format
1032 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1033 SEQUENCE_FEATURE_COLOURS);
1034 chooser.setFileView(new JalviewFileView());
1035 chooser.setDialogTitle(
1036 MessageManager.getString("label.save_feature_colours"));
1037 chooser.setToolTipText(MessageManager.getString("action.save"));
1038 int option = chooser.showSaveDialog(this);
1039 if (option == JalviewFileChooser.APPROVE_OPTION)
1041 File file = chooser.getSelectedFile();
1047 * Saves feature colours and filters to the given file
1051 void save(File file)
1053 JalviewUserColours ucs = new JalviewUserColours();
1054 ucs.setSchemeName("Sequence Features");
1057 PrintWriter out = new PrintWriter(
1058 new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
1061 * sort feature types by colour order, from 0 (highest)
1064 Set<String> fr_colours = fr.getAllFeatureColours();
1065 String[] sortedTypes = fr_colours
1066 .toArray(new String[fr_colours.size()]);
1067 Arrays.sort(sortedTypes, new Comparator<String>()
1070 public int compare(String type1, String type2)
1072 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1077 * save feature colours
1079 for (String featureType : sortedTypes)
1081 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1082 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1084 ucs.getColour().add(col);
1088 * save any feature filters
1090 for (String featureType : sortedTypes)
1092 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1093 if (filter != null && !filter.isEmpty())
1095 Iterator<FeatureMatcherI> iterator = filter.getMatchers()
1097 FeatureMatcherI firstMatcher = iterator.next();
1098 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1099 .marshalFilter(firstMatcher, iterator, filter.isAnded());
1100 Filter filterModel = new Filter();
1101 filterModel.setFeatureType(featureType);
1102 filterModel.setMatcherSet(ms);
1103 ucs.getFilter().add(filterModel);
1106 JAXBContext jaxbContext = JAXBContext
1107 .newInstance(JalviewUserColours.class);
1108 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1109 jaxbMarshaller.marshal(
1110 new ObjectFactory().createJalviewUserColours(ucs), out);
1112 // jaxbMarshaller.marshal(object, pout);
1113 // marshaller.marshal(object);
1116 // ucs.marshal(out);
1118 } catch (Exception ex)
1120 ex.printStackTrace();
1124 public void invertSelection()
1126 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1127 for (int i = 0; i < data.length; i++)
1129 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1131 updateFeatureRenderer(data, true);
1135 public void orderByAvWidth()
1137 if (table == null || table.getModel() == null)
1141 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1142 float[] width = new float[data.length];
1146 for (int i = 0; i < data.length; i++)
1148 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1151 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1152 // weight - but have to make per
1153 // sequence, too (awidth[2])
1154 // if (width[i]==1) // hack to distinguish single width sequences.
1165 boolean sort = false;
1166 for (int i = 0; i < width.length; i++)
1168 // awidth = (float[]) typeWidth.get(data[i][0]);
1171 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1174 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1180 width[i] /= max; // normalize
1181 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for
1186 sort = sort || width[i - 1] > width[i];
1191 jalview.util.QuickSort.sort(width, data);
1192 // update global priority order
1195 updateFeatureRenderer(data, false);
1200 * close ourselves but leave any existing UI handlers (e.g a CDS/Protein
1201 * tabbed feature settings dialog) intact
1203 public void closeOldSettings()
1209 * close the feature settings dialog (and any containing frame)
1216 private void closeDialog(boolean closeContainingFrame)
1222 af.setFeatureSettingsGeometry(frame.getBounds());
1223 frame.setClosed(true);
1227 SplitContainerI sc = af.getSplitViewContainer();
1228 sc.closeFeatureSettings(this, closeContainingFrame);
1229 af.featureSettings = null;
1231 } catch (Exception exe)
1237 public void updateFeatureRenderer(Object[][] data)
1239 updateFeatureRenderer(data, true);
1243 * Update the priority order of features; only repaint if this changed the
1244 * order of visible features
1249 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1251 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1253 if (fr.setFeaturePriority(rowData, visibleNew))
1260 * Converts table data into an array of data beans
1262 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1264 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1265 for (int i = 0; i < data.length; i++)
1267 String type = (String) data[i][TYPE_COLUMN];
1268 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1269 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1270 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1271 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1277 private void jbInit() throws Exception
1279 this.setLayout(new BorderLayout());
1281 final boolean hasComplement = af.getViewport()
1282 .getCodingComplement() != null;
1284 JPanel settingsPane = new JPanel();
1285 settingsPane.setLayout(new BorderLayout());
1287 JPanel bigPanel = new JPanel();
1288 bigPanel.setLayout(new BorderLayout());
1290 groupPanel = new JPanel();
1291 bigPanel.add(groupPanel, BorderLayout.NORTH);
1293 JButton invert = new JButton(
1294 MessageManager.getString("label.invert_selection"));
1295 invert.setFont(JvSwingUtils.getLabelFont());
1296 invert.addActionListener(new ActionListener()
1299 public void actionPerformed(ActionEvent e)
1305 JButton optimizeOrder = new JButton(
1306 MessageManager.getString("label.optimise_order"));
1307 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1308 optimizeOrder.addActionListener(new ActionListener()
1311 public void actionPerformed(ActionEvent e)
1317 final String byScoreLabel = MessageManager
1318 .getString("label.seq_sort_by_score");
1319 JButton sortByScore = new JButton(byScoreLabel);
1320 sortByScore.setFont(JvSwingUtils.getLabelFont());
1321 sortByScore.addActionListener(new ActionListener()
1324 public void actionPerformed(ActionEvent e)
1326 if (canSortBy(byScoreLabel))
1332 final String byDensityLabel = MessageManager
1333 .getString("label.sequence_sort_by_density");
1334 JButton sortByDens = new JButton(byDensityLabel);
1335 sortByDens.setFont(JvSwingUtils.getLabelFont());
1336 sortByDens.addActionListener(new ActionListener()
1339 public void actionPerformed(ActionEvent e)
1341 if (canSortBy(byDensityLabel))
1343 sortByDensity(null);
1348 JButton help = new JButton(MessageManager.getString("action.help"));
1349 help.setFont(JvSwingUtils.getLabelFont());
1350 help.addActionListener(new ActionListener()
1353 public void actionPerformed(ActionEvent e)
1357 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1358 } catch (HelpSetException e1)
1360 e1.printStackTrace();
1364 // Cancel for a SplitFrame should just revert changes to the currently
1366 // settings. May want to do this for either or both - so need a splitview
1367 // feature settings cancel/OK.
1368 JButton cancel = new JButton(MessageManager
1369 .getString(hasComplement ? "action.revert" : "action.cancel"));
1370 cancel.setToolTipText(MessageManager.getString(hasComplement
1371 ? "action.undo_changes_to_feature_settings"
1372 : "action.undo_changes_to_feature_settings_and_close_the_dialog"));
1373 cancel.setFont(JvSwingUtils.getLabelFont());
1374 // TODO: disable cancel (and apply!) until current settings are different
1375 cancel.addActionListener(new ActionListener()
1378 public void actionPerformed(ActionEvent e)
1388 // Cancel for the whole dialog should cancel both CDS and Protein.
1389 // OK for an individual feature settings just applies changes, but dialog
1391 JButton ok = new JButton(MessageManager
1392 .getString(hasComplement ? "action.apply" : "action.ok"));
1393 ok.setFont(JvSwingUtils.getLabelFont());
1394 ok.addActionListener(new ActionListener()
1397 public void actionPerformed(ActionEvent e)
1405 storeOriginalSettings();
1410 JButton loadColours = new JButton(
1411 MessageManager.getString("label.load_colours"));
1412 loadColours.setFont(JvSwingUtils.getLabelFont());
1413 loadColours.setToolTipText(
1414 MessageManager.getString("label.load_colours_tooltip"));
1415 loadColours.addActionListener(new ActionListener()
1418 public void actionPerformed(ActionEvent e)
1424 JButton saveColours = new JButton(
1425 MessageManager.getString("label.save_colours"));
1426 saveColours.setFont(JvSwingUtils.getLabelFont());
1427 saveColours.setToolTipText(
1428 MessageManager.getString("label.save_colours_tooltip"));
1429 saveColours.addActionListener(new ActionListener()
1432 public void actionPerformed(ActionEvent e)
1437 transparency.addChangeListener(new ChangeListener()
1440 public void stateChanged(ChangeEvent evt)
1442 if (!inConstruction)
1444 fr.setTransparency((100 - transparency.getValue()) / 100f);
1450 transparency.setMaximum(70);
1451 transparency.setToolTipText(
1452 MessageManager.getString("label.transparency_tip"));
1454 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1455 String text = MessageManager
1456 .formatMessage("label.show_linked_features",
1458 ? MessageManager.getString("label.protein")
1459 .toLowerCase(Locale.ROOT)
1461 showComplement = new JCheckBox(text);
1462 showComplement.addActionListener(new ActionListener()
1465 public void actionPerformed(ActionEvent e)
1468 .setShowComplementFeatures(showComplement.isSelected());
1473 showComplementOnTop = new JCheckBox(
1474 MessageManager.getString("label.on_top"));
1475 showComplementOnTop.addActionListener(new ActionListener()
1478 public void actionPerformed(ActionEvent e)
1480 af.getViewport().setShowComplementFeaturesOnTop(
1481 showComplementOnTop.isSelected());
1486 updateComplementButtons();
1488 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1489 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1491 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1492 transbuttons.add(optimizeOrder);
1493 transbuttons.add(invert);
1494 transbuttons.add(sortByScore);
1495 transbuttons.add(sortByDens);
1496 transbuttons.add(help);
1498 JPanel transPanelLeft = new JPanel(
1499 new GridLayout(hasComplement ? 4 : 2, 1));
1500 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1501 transPanelLeft.add(transparency);
1504 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1505 cp.add(showComplement);
1506 cp.add(showComplementOnTop);
1507 transPanelLeft.add(cp);
1509 lowerPanel.add(transPanelLeft);
1510 lowerPanel.add(transbuttons);
1512 JPanel buttonPanel = new JPanel();
1513 buttonPanel.add(ok);
1514 buttonPanel.add(cancel);
1515 buttonPanel.add(loadColours);
1516 buttonPanel.add(saveColours);
1517 bigPanel.add(scrollPane, BorderLayout.CENTER);
1518 settingsPane.add(bigPanel, BorderLayout.CENTER);
1519 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1520 this.add(settingsPane);
1524 * Repaints alignment, structure and overview (if shown). If there is a
1525 * complementary view which is showing this view's features, then also
1528 void refreshDisplay()
1530 af.alignPanel.paintAlignment(true, true);
1531 AlignViewportI complement = af.getViewport().getCodingComplement();
1532 if (complement != null && complement.isShowComplementFeatures())
1534 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1535 af2.alignPanel.paintAlignment(true, true);
1540 * Answers a suitable tooltip to show on the colour cell of the table
1544 * if true include 'click to edit' and similar text
1547 public static String getColorTooltip(FeatureColourI fcol,
1554 if (fcol.isSimpleColour())
1556 return withHint ? BASE_TOOLTIP : null;
1558 String description = fcol.getDescription();
1559 description = description.replaceAll("<", "<");
1560 description = description.replaceAll(">", ">");
1561 StringBuilder tt = new StringBuilder(description);
1564 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1566 return JvSwingUtils.wrapTooltip(true, tt.toString());
1569 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1572 boolean thr = false;
1573 StringBuilder tx = new StringBuilder();
1575 if (gcol.isColourByAttribute())
1577 tx.append(FeatureMatcher
1578 .toAttributeDisplayName(gcol.getAttributeName()));
1580 else if (!gcol.isColourByLabel())
1582 tx.append(MessageManager.getString("label.score"));
1585 if (gcol.isAboveThreshold())
1590 if (gcol.isBelowThreshold())
1595 if (gcol.isColourByLabel())
1601 if (!gcol.isColourByAttribute())
1609 Color newColor = gcol.getMaxColour();
1610 comp.setBackground(newColor);
1611 // System.err.println("Width is " + w / 2);
1612 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1613 comp.setIcon(ficon);
1614 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1615 // + newColor.getGreen() + ", " + newColor.getBlue()
1616 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1617 // + ", " + minCol.getBlue() + ")");
1619 comp.setHorizontalAlignment(SwingConstants.CENTER);
1620 comp.setText(tx.toString());
1623 // ///////////////////////////////////////////////////////////////////////
1624 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1625 // ///////////////////////////////////////////////////////////////////////
1626 class FeatureTableModel extends AbstractTableModel
1628 private String[] columnNames = {
1629 MessageManager.getString("label.feature_type"),
1630 MessageManager.getString("action.colour"),
1631 MessageManager.getString("label.configuration"),
1632 MessageManager.getString("label.show") };
1634 private Object[][] data;
1636 FeatureTableModel(Object[][] data)
1641 public Object[][] getData()
1646 public void setData(Object[][] data)
1652 public int getColumnCount()
1654 return columnNames.length;
1657 public Object[] getRow(int row)
1663 public int getRowCount()
1669 public String getColumnName(int col)
1671 return columnNames[col];
1675 public Object getValueAt(int row, int col)
1677 return data[row][col];
1681 * Answers the class of column c of the table
1684 public Class<?> getColumnClass(int c)
1689 return String.class;
1691 return FeatureColour.class;
1693 return FeatureMatcherSet.class;
1695 return Boolean.class;
1700 public boolean isCellEditable(int row, int col)
1702 return col == 0 ? false : true;
1706 public void setValueAt(Object value, int row, int col)
1708 data[row][col] = value;
1709 fireTableCellUpdated(row, col);
1710 updateFeatureRenderer(data);
1715 class ColorRenderer extends JLabel implements TableCellRenderer
1717 Border unselectedBorder = null;
1719 Border selectedBorder = null;
1721 public ColorRenderer()
1723 setOpaque(true); // MUST do this for background to show up.
1724 setHorizontalTextPosition(SwingConstants.CENTER);
1725 setVerticalTextPosition(SwingConstants.CENTER);
1729 public Component getTableCellRendererComponent(JTable tbl, Object color,
1730 boolean isSelected, boolean hasFocus, int row, int column)
1732 FeatureColourI cellColour = (FeatureColourI) color;
1734 setBackground(tbl.getBackground());
1735 if (!cellColour.isSimpleColour())
1737 Rectangle cr = tbl.getCellRect(row, column, false);
1738 FeatureSettings.renderGraduatedColor(this, cellColour,
1739 (int) cr.getWidth(), (int) cr.getHeight());
1745 setBackground(cellColour.getColour());
1749 if (selectedBorder == null)
1751 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1752 tbl.getSelectionBackground());
1754 setBorder(selectedBorder);
1758 if (unselectedBorder == null)
1760 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1761 tbl.getBackground());
1763 setBorder(unselectedBorder);
1770 class FilterRenderer extends JLabel implements TableCellRenderer
1772 javax.swing.border.Border unselectedBorder = null;
1774 javax.swing.border.Border selectedBorder = null;
1776 public FilterRenderer()
1778 setOpaque(true); // MUST do this for background to show up.
1779 setHorizontalTextPosition(SwingConstants.CENTER);
1780 setVerticalTextPosition(SwingConstants.CENTER);
1784 public Component getTableCellRendererComponent(JTable tbl,
1785 Object filter, boolean isSelected, boolean hasFocus, int row,
1788 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1790 String asText = theFilter.toString();
1791 setBackground(tbl.getBackground());
1792 this.setText(asText);
1797 if (selectedBorder == null)
1799 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1800 tbl.getSelectionBackground());
1802 setBorder(selectedBorder);
1806 if (unselectedBorder == null)
1808 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1809 tbl.getBackground());
1811 setBorder(unselectedBorder);
1819 * update comp using rendering settings from gcol
1824 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1826 int w = comp.getWidth(), h = comp.getHeight();
1829 w = (int) comp.getPreferredSize().getWidth();
1830 h = (int) comp.getPreferredSize().getHeight();
1837 renderGraduatedColor(comp, gcol, w, h);
1840 @SuppressWarnings("serial")
1841 class ColorEditor extends AbstractCellEditor
1842 implements TableCellEditor, ActionListener
1844 FeatureColourI currentColor;
1846 FeatureTypeSettings chooser;
1852 protected static final String EDIT = "edit";
1854 int rowSelected = 0;
1856 public ColorEditor()
1858 // Set up the editor (from the table's point of view),
1859 // which is a button.
1860 // This button brings up the color chooser dialog,
1861 // which is the editor from the user's point of view.
1862 button = new JButton();
1863 button.setActionCommand(EDIT);
1864 button.addActionListener(this);
1865 button.setBorderPainted(false);
1869 * Handles events from the editor button, and from the colour/filters
1870 * dialog's OK button
1873 public void actionPerformed(ActionEvent e)
1875 if (button == e.getSource())
1877 if (currentColor.isSimpleColour())
1880 * simple colour chooser
1882 String ttl = MessageManager
1883 .formatMessage("label.select_colour_for", type);
1884 ColourChooserListener listener = new ColourChooserListener()
1887 public void colourSelected(Color c)
1889 currentColor = new FeatureColour(c);
1890 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1891 fireEditingStopped();
1895 public void cancel()
1897 fireEditingStopped();
1900 JalviewColourChooser.showColourChooser(button, ttl,
1901 currentColor.getColour(), listener);
1906 * variable colour and filters dialog
1908 chooser = new FeatureTypeSettings(fr, type);
1909 if (!Platform.isJS())
1916 chooser.setRequestFocusEnabled(true);
1917 chooser.requestFocus();
1919 chooser.addActionListener(this);
1920 fireEditingStopped();
1926 * after OK in variable colour dialog, any changes to colour
1927 * (or filters!) are already set in FeatureRenderer, so just
1928 * update table data without triggering updateFeatureRenderer
1930 currentColor = fr.getFeatureColours().get(type);
1931 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1932 if (currentFilter == null)
1934 currentFilter = new FeatureMatcherSet();
1936 Object[] data = ((FeatureTableModel) table.getModel())
1937 .getData()[rowSelected];
1938 data[COLOUR_COLUMN] = currentColor;
1939 data[FILTER_COLUMN] = currentFilter;
1940 fireEditingStopped();
1941 // SwingJS needs an explicit repaint() here,
1942 // rather than relying upon no validation having
1943 // occurred since the stopEditing call was made.
1944 // Its laying out has not been stopped by the modal frame
1951 * Override allows access to this method from anonymous inner classes
1954 protected void fireEditingStopped()
1956 super.fireEditingStopped();
1959 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1961 public Object getCellEditorValue()
1963 return currentColor;
1966 // Implement the one method defined by TableCellEditor.
1968 public Component getTableCellEditorComponent(JTable theTable,
1969 Object value, boolean isSelected, int row, int column)
1971 currentColor = (FeatureColourI) value;
1972 this.rowSelected = row;
1973 type = table.getValueAt(row, TYPE_COLUMN).toString();
1974 button.setOpaque(true);
1975 button.setBackground(FeatureSettings.this.getBackground());
1976 if (!currentColor.isSimpleColour())
1978 JLabel btn = new JLabel();
1979 btn.setSize(button.getSize());
1980 FeatureSettings.renderGraduatedColor(btn, currentColor);
1981 button.setBackground(btn.getBackground());
1982 button.setIcon(btn.getIcon());
1983 button.setText(btn.getText());
1988 button.setIcon(null);
1989 button.setBackground(currentColor.getColour());
1996 * The cell editor for the Filter column. It displays the text of any filters
1997 * for the feature type in that row (in full as a tooltip, possible
1998 * abbreviated as display text). On click in the cell, opens the Feature
1999 * Display Settings dialog at the Filters tab.
2001 @SuppressWarnings("serial")
2002 class FilterEditor extends AbstractCellEditor
2003 implements TableCellEditor, ActionListener
2006 FeatureMatcherSetI currentFilter;
2014 protected static final String EDIT = "edit";
2016 int rowSelected = 0;
2018 public FilterEditor()
2020 button = new JButton();
2021 button.setActionCommand(EDIT);
2022 button.addActionListener(this);
2023 button.setBorderPainted(false);
2027 * Handles events from the editor button
2030 public void actionPerformed(ActionEvent e)
2032 if (button == e.getSource())
2034 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
2035 chooser.addActionListener(this);
2036 chooser.setRequestFocusEnabled(true);
2037 chooser.requestFocus();
2038 if (lastLocation != null)
2040 // todo open at its last position on screen
2041 chooser.setBounds(lastLocation.x, lastLocation.y,
2042 chooser.getWidth(), chooser.getHeight());
2045 fireEditingStopped();
2047 else if (e.getSource() instanceof Component)
2051 * after OK in variable colour dialog, any changes to filter
2052 * (or colours!) are already set in FeatureRenderer, so just
2053 * update table data without triggering updateFeatureRenderer
2055 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2056 currentFilter = fr.getFeatureFilter(type);
2057 if (currentFilter == null)
2059 currentFilter = new FeatureMatcherSet();
2062 Object[] data = ((FeatureTableModel) table.getModel())
2063 .getData()[rowSelected];
2064 data[COLOUR_COLUMN] = currentColor;
2065 data[FILTER_COLUMN] = currentFilter;
2066 fireEditingStopped();
2067 // SwingJS needs an explicit repaint() here,
2068 // rather than relying upon no validation having
2069 // occurred since the stopEditing call was made.
2070 // Its laying out has not been stopped by the modal frame
2077 public Object getCellEditorValue()
2079 return currentFilter;
2083 public Component getTableCellEditorComponent(JTable theTable,
2084 Object value, boolean isSelected, int row, int column)
2086 currentFilter = (FeatureMatcherSetI) value;
2087 this.rowSelected = row;
2088 type = table.getValueAt(row, TYPE_COLUMN).toString();
2089 button.setOpaque(true);
2090 button.setBackground(FeatureSettings.this.getBackground());
2091 button.setText(currentFilter.toString());
2092 button.setIcon(null);
2097 public boolean isOpen()
2099 if (af.getSplitViewContainer() != null)
2101 return af.getSplitViewContainer().isFeatureSettingsOpen();
2103 return frame != null && !frame.isClosed();
2107 public void revert()
2109 fr.setTransparency(originalTransparency);
2110 fr.setFeatureFilters(originalFilters);
2111 updateFeatureRenderer(originalData);
2112 af.getViewport().setViewStyle(originalViewStyle);
2113 updateTransparencySliderFromFR();
2114 updateComplementButtons();
2119 class FeatureIcon implements Icon
2121 FeatureColourI gcol;
2125 boolean midspace = false;
2127 int width = 50, height = 20;
2129 int s1, e1; // start and end of midpoint band for thresholded symbol
2131 Color mpcolour = Color.white;
2133 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2153 public int getIconWidth()
2159 public int getIconHeight()
2165 public void paintIcon(Component c, Graphics g, int x, int y)
2168 if (gcol.isColourByLabel())
2171 g.fillRect(0, 0, width, height);
2172 // need an icon here.
2173 g.setColor(gcol.getMaxColour());
2175 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2177 // g.setFont(g.getFont().deriveFont(
2178 // AffineTransform.getScaleInstance(
2179 // width/g.getFontMetrics().stringWidth("Label"),
2180 // height/g.getFontMetrics().getHeight())));
2182 g.drawString(MessageManager.getString("label.label"), 0, 0);
2187 Color minCol = gcol.getMinColour();
2189 g.fillRect(0, 0, s1, height);
2192 g.setColor(Color.white);
2193 g.fillRect(s1, 0, e1 - s1, height);
2195 g.setColor(gcol.getMaxColour());
2196 // g.fillRect(0, e1, width - e1, height); // BH 2018
2197 g.fillRect(e1, 0, width - e1, height);