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.io.Reader;
49 import java.util.Arrays;
50 import java.util.Comparator;
51 import java.util.HashMap;
52 import java.util.HashSet;
53 import java.util.Hashtable;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Locale;
60 import javax.help.HelpSetException;
61 import javax.swing.AbstractCellEditor;
62 import javax.swing.BorderFactory;
63 import javax.swing.Icon;
64 import javax.swing.JButton;
65 import javax.swing.JCheckBox;
66 import javax.swing.JInternalFrame;
67 import javax.swing.JLabel;
68 import javax.swing.JLayeredPane;
69 import javax.swing.JMenuItem;
70 import javax.swing.JPanel;
71 import javax.swing.JPopupMenu;
72 import javax.swing.JScrollPane;
73 import javax.swing.JSlider;
74 import javax.swing.JTable;
75 import javax.swing.ListSelectionModel;
76 import javax.swing.SwingConstants;
77 import javax.swing.ToolTipManager;
78 import javax.swing.border.Border;
79 import javax.swing.event.ChangeEvent;
80 import javax.swing.event.ChangeListener;
81 import javax.swing.table.AbstractTableModel;
82 import javax.swing.table.JTableHeader;
83 import javax.swing.table.TableCellEditor;
84 import javax.swing.table.TableCellRenderer;
85 import javax.swing.table.TableColumn;
86 import javax.xml.bind.JAXBContext;
87 import javax.xml.bind.JAXBElement;
88 import javax.xml.bind.Marshaller;
89 import javax.xml.stream.XMLInputFactory;
90 import javax.xml.stream.XMLStreamReader;
92 import jalview.api.AlignViewControllerGuiI;
93 import jalview.api.AlignViewportI;
94 import jalview.api.FeatureColourI;
95 import jalview.api.FeatureSettingsControllerI;
96 import jalview.api.SplitContainerI;
97 import jalview.api.ViewStyleI;
98 import jalview.controller.FeatureSettingsControllerGuiI;
99 import jalview.datamodel.AlignmentI;
100 import jalview.datamodel.SequenceI;
101 import jalview.datamodel.features.FeatureMatcher;
102 import jalview.datamodel.features.FeatureMatcherI;
103 import jalview.datamodel.features.FeatureMatcherSet;
104 import jalview.datamodel.features.FeatureMatcherSetI;
105 import jalview.gui.Help.HelpId;
106 import jalview.gui.JalviewColourChooser.ColourChooserListener;
107 import jalview.io.DataSourceType;
108 import jalview.io.FileParse;
109 import jalview.io.JalviewFileChooser;
110 import jalview.io.JalviewFileView;
111 import jalview.schemes.FeatureColour;
112 import jalview.util.MessageManager;
113 import jalview.util.Platform;
114 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
115 import jalview.viewmodel.styles.ViewStyle;
116 import jalview.xml.binding.jalview.JalviewUserColours;
117 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
118 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
119 import jalview.xml.binding.jalview.ObjectFactory;
121 public class FeatureSettings extends JPanel
122 implements FeatureSettingsControllerI, FeatureSettingsControllerGuiI
124 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
125 .getString("label.sequence_feature_colours");
128 * column indices of fields in Feature Settings table
130 static final int TYPE_COLUMN = 0;
132 static final int COLOUR_COLUMN = 1;
134 static final int FILTER_COLUMN = 2;
136 static final int SHOW_COLUMN = 3;
138 private static final int COLUMN_COUNT = 4;
140 private static final int MIN_WIDTH = 400;
142 private static final int MIN_HEIGHT = 400;
144 private final static String BASE_TOOLTIP = MessageManager
145 .getString("label.click_to_edit");
147 final FeatureRenderer fr;
149 public final AlignFrame af;
152 * 'original' fields hold settings to restore on Cancel
154 Object[][] originalData;
156 private float originalTransparency;
158 private ViewStyleI originalViewStyle;
160 private Map<String, FeatureMatcherSetI> originalFilters;
162 final JInternalFrame frame;
164 JScrollPane scrollPane = new JScrollPane();
170 JSlider transparency = new JSlider();
172 private JCheckBox showComplementOnTop;
174 private JCheckBox showComplement;
177 * when true, constructor is still executing - so ignore UI events
179 protected volatile boolean inConstruction = true;
181 int selectedRow = -1;
183 boolean resettingTable = false;
186 * true when Feature Settings are updating from feature renderer
188 private boolean handlingUpdate = false;
191 * a change listener to ensure the dialog is updated if
192 * FeatureRenderer discovers new features
194 private PropertyChangeListener change;
197 * holds {featureCount, totalExtent} for each feature type
199 Map<String, float[]> typeWidth = null;
201 private void storeOriginalSettings()
203 // save transparency for restore on Cancel
204 originalTransparency = fr.getTransparency();
206 updateTransparencySliderFromFR();
208 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
209 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
212 private void updateTransparencySliderFromFR()
214 boolean incon = inConstruction;
215 inConstruction = true;
217 int transparencyAsPercent = (int) (fr.getTransparency() * 100);
218 transparency.setValue(100 - transparencyAsPercent);
219 inConstruction = incon;
227 public FeatureSettings(AlignFrame alignFrame)
229 this.af = alignFrame;
230 fr = af.getFeatureRenderer();
232 storeOriginalSettings();
237 } catch (Exception ex)
239 ex.printStackTrace();
245 public String getToolTipText(MouseEvent e)
248 int column = table.columnAtPoint(e.getPoint());
249 int row = table.rowAtPoint(e.getPoint());
254 tip = JvSwingUtils.wrapTooltip(true, MessageManager
255 .getString("label.feature_settings_click_drag"));
258 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
260 tip = getColorTooltip(colour, true);
263 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
267 .getString("label.configure_feature_tooltip")
278 * Position the tooltip near the bottom edge of, and half way across, the
282 public Point getToolTipLocation(MouseEvent e)
284 Point point = e.getPoint();
285 int column = table.columnAtPoint(point);
286 int row = table.rowAtPoint(point);
287 Rectangle r = getCellRect(row, column, false);
288 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
292 JTableHeader tableHeader = table.getTableHeader();
293 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
294 tableHeader.setReorderingAllowed(false);
295 table.setFont(new Font("Verdana", Font.PLAIN, 12));
296 ToolTipManager.sharedInstance().registerComponent(table);
297 table.setDefaultEditor(FeatureColour.class, new ColorEditor());
298 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
300 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor());
301 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
303 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
304 new ColorRenderer(), new ColorEditor());
305 table.addColumn(colourColumn);
307 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
308 new FilterRenderer(), new FilterEditor());
309 table.addColumn(filterColumn);
311 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
313 table.addMouseListener(new MouseAdapter()
316 public void mousePressed(MouseEvent evt)
318 Point pt = evt.getPoint();
319 selectedRow = table.rowAtPoint(pt);
320 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
321 if (evt.isPopupTrigger())
323 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
324 showPopupMenu(selectedRow, type, colour, evt.getPoint());
326 else if (evt.getClickCount() == 2
327 && table.columnAtPoint(pt) == TYPE_COLUMN)
329 boolean invertSelection = evt.isAltDown();
330 boolean toggleSelection = Platform.isControlDown(evt);
331 boolean extendSelection = evt.isShiftDown();
332 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
333 invertSelection, extendSelection, toggleSelection, type);
334 fr.ap.av.sendSelection();
338 // isPopupTrigger fires on mouseReleased on Windows
340 public void mouseReleased(MouseEvent evt)
342 selectedRow = table.rowAtPoint(evt.getPoint());
343 if (evt.isPopupTrigger())
345 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
346 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
347 showPopupMenu(selectedRow, type, colour, evt.getPoint());
352 table.addMouseMotionListener(new MouseMotionAdapter()
355 public void mouseDragged(MouseEvent evt)
357 int newRow = table.rowAtPoint(evt.getPoint());
358 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
361 * reposition 'selectedRow' to 'newRow' (the dragged to location)
362 * this could be more than one row away for a very fast drag action
363 * so just swap it with adjacent rows until we get it there
365 Object[][] data = ((FeatureTableModel) table.getModel())
367 int direction = newRow < selectedRow ? -1 : 1;
368 for (int i = selectedRow; i != newRow; i += direction)
370 Object[] temp = data[i];
371 data[i] = data[i + direction];
372 data[i + direction] = temp;
374 updateFeatureRenderer(data);
376 selectedRow = newRow;
380 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
381 // MessageManager.getString("label.feature_settings_click_drag")));
382 scrollPane.setViewportView(table);
384 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
386 fr.findAllFeatures(true); // display everything!
389 discoverAllFeatureData();
390 final FeatureSettings fs = this;
391 fr.addPropertyChangeListener(change = new PropertyChangeListener()
394 public void propertyChange(PropertyChangeEvent evt)
396 if (!fs.resettingTable && !fs.handlingUpdate)
398 fs.handlingUpdate = true;
400 // new groups may be added with new sequence feature types only
401 fs.handlingUpdate = false;
407 SplitContainerI splitframe = af.getSplitViewContainer();
408 if (splitframe != null)
410 frame = null; // keeps eclipse happy
411 splitframe.addFeatureSettingsUI(this);
415 frame = new JInternalFrame();
416 frame.setContentPane(this);
417 frame.setFrameIcon(null);
418 Rectangle bounds = af.getFeatureSettingsGeometry();
420 if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels(
421 af.alignPanel.av.getSequenceSetId()).length > 1)
423 title = MessageManager.formatMessage(
424 "label.sequence_feature_settings_for_view",
425 af.alignPanel.getViewName());
429 title = MessageManager.getString("label.sequence_feature_settings");
433 if (Platform.isAMacAndNotJS())
435 Desktop.addInternalFrame(frame, title, 600, 480);
439 Desktop.addInternalFrame(frame, title, 600, 450);
444 Desktop.addInternalFrame(frame, title, false, bounds.width,
446 frame.setBounds(bounds);
447 frame.setVisible(true);
449 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
451 frame.addInternalFrameListener(
452 new javax.swing.event.InternalFrameAdapter()
455 public void internalFrameClosed(
456 javax.swing.event.InternalFrameEvent evt)
458 featureSettings_isClosed();
461 frame.setLayer(JLayeredPane.PALETTE_LAYER);
463 inConstruction = false;
467 * Sets the state of buttons to show complement features from viewport
470 private void updateComplementButtons()
472 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
474 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
478 public AlignViewControllerGuiI getAlignframe()
484 public void featureSettings_isClosed()
486 fr.removePropertyChangeListener(change);
491 * Constructs and shows a popup menu of possible actions on the selected row
499 protected void showPopupMenu(final int rowSelected, final String type,
500 final Object typeCol, final Point pt)
502 JPopupMenu men = new JPopupMenu(MessageManager
503 .formatMessage("label.settings_for_param", new String[]
506 JMenuItem scr = new JMenuItem(
507 MessageManager.getString("label.sort_by_score"));
509 scr.addActionListener(new ActionListener()
512 public void actionPerformed(ActionEvent e)
514 sortByScore(Arrays.asList(new String[] { type }));
517 JMenuItem dens = new JMenuItem(
518 MessageManager.getString("label.sort_by_density"));
519 dens.addActionListener(new ActionListener()
522 public void actionPerformed(ActionEvent e)
524 sortByDensity(Arrays.asList(new String[] { type }));
529 JMenuItem selCols = new JMenuItem(
530 MessageManager.getString("label.select_columns_containing"));
531 selCols.addActionListener(new ActionListener()
534 public void actionPerformed(ActionEvent arg0)
536 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
538 fr.ap.av.sendSelection();
541 JMenuItem clearCols = new JMenuItem(MessageManager
542 .getString("label.select_columns_not_containing"));
543 clearCols.addActionListener(new ActionListener()
546 public void actionPerformed(ActionEvent arg0)
548 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
550 fr.ap.av.sendSelection();
553 JMenuItem hideCols = new JMenuItem(
554 MessageManager.getString("label.hide_columns_containing"));
555 hideCols.addActionListener(new ActionListener()
558 public void actionPerformed(ActionEvent arg0)
560 fr.ap.alignFrame.hideFeatureColumns(type, true);
561 fr.ap.av.sendSelection();
564 JMenuItem hideOtherCols = new JMenuItem(
565 MessageManager.getString("label.hide_columns_not_containing"));
566 hideOtherCols.addActionListener(new ActionListener()
569 public void actionPerformed(ActionEvent arg0)
571 fr.ap.alignFrame.hideFeatureColumns(type, false);
572 fr.ap.av.sendSelection();
578 men.add(hideOtherCols);
579 men.show(table, pt.x, pt.y);
583 * Sort the sequences in the alignment by the number of features for the given
584 * feature types (or all features if null)
586 * @param featureTypes
588 protected void sortByDensity(List<String> featureTypes)
590 af.avc.sortAlignmentByFeatureDensity(featureTypes);
594 * Sort the sequences in the alignment by average score for the given feature
595 * types (or all features if null)
597 * @param featureTypes
599 protected void sortByScore(List<String> featureTypes)
601 af.avc.sortAlignmentByFeatureScore(featureTypes);
605 * Returns true if at least one feature type is visible. Else shows a warning
606 * dialog and returns false.
611 private boolean canSortBy(String title)
613 if (fr.getDisplayedFeatureTypes().isEmpty())
615 JvOptionPane.showMessageDialog(this,
616 MessageManager.getString("label.no_features_to_sort_by"),
617 title, JvOptionPane.OK_OPTION);
624 synchronized public void discoverAllFeatureData()
626 Set<String> allGroups = new HashSet<>();
627 AlignmentI alignment = af.getViewport().getAlignment();
629 for (int i = 0; i < alignment.getHeight(); i++)
631 SequenceI seq = alignment.getSequenceAt(i);
632 for (String group : seq.getFeatures().getFeatureGroups(true))
634 if (group != null && !allGroups.contains(group))
636 allGroups.add(group);
637 checkGroupState(group);
648 * Synchronise gui group list and check visibility of group
651 * @return true if group is visible
653 private boolean checkGroupState(String group)
655 boolean visible = fr.checkGroupVisibility(group, true);
657 for (int g = 0; g < groupPanel.getComponentCount(); g++)
659 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
661 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
666 final String grp = group;
667 final JCheckBox check = new JCheckBox(group, visible);
668 check.setFont(new Font("Serif", Font.BOLD, 12));
669 check.setToolTipText(group);
670 check.addItemListener(new ItemListener()
673 public void itemStateChanged(ItemEvent evt)
675 fr.setGroupVisibility(check.getText(), check.isSelected());
676 resetTable(new String[] { grp });
680 groupPanel.add(check);
684 synchronized void resetTable(String[] groupChanged)
690 resettingTable = true;
691 typeWidth = new Hashtable<>();
692 // TODO: change avWidth calculation to 'per-sequence' average and use long
695 Set<String> displayableTypes = new HashSet<>();
696 Set<String> foundGroups = new HashSet<>();
699 * determine which feature types may be visible depending on
700 * which groups are selected, and recompute average width data
702 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
705 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
708 * get the sequence's groups for positional features
709 * and keep track of which groups are visible
711 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
712 Set<String> visibleGroups = new HashSet<>();
713 for (String group : groups)
715 if (group == null || checkGroupState(group))
717 visibleGroups.add(group);
720 foundGroups.addAll(groups);
723 * get distinct feature types for visible groups
724 * record distinct visible types, and their count and total length
726 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
727 visibleGroups.toArray(new String[visibleGroups.size()]));
728 for (String type : types)
730 displayableTypes.add(type);
731 float[] avWidth = typeWidth.get(type);
734 avWidth = new float[2];
735 typeWidth.put(type, avWidth);
737 // todo this could include features with a non-visible group
738 // - do we greatly care?
739 // todo should we include non-displayable features here, and only
740 // update when features are added?
741 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
742 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
746 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
749 if (fr.hasRenderOrder())
753 fr.findAllFeatures(groupChanged != null); // prod to update
754 // colourschemes. but don't
756 // First add the checks in the previous render order,
757 // in case the window has been closed and reopened
759 List<String> frl = fr.getRenderOrder();
760 for (int ro = frl.size() - 1; ro > -1; ro--)
762 String type = frl.get(ro);
764 if (!displayableTypes.contains(type))
769 data[dataIndex][TYPE_COLUMN] = type;
770 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
771 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
772 data[dataIndex][FILTER_COLUMN] = featureFilter == null
773 ? new FeatureMatcherSet()
775 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
776 af.getViewport().getFeaturesDisplayed().isVisible(type));
778 displayableTypes.remove(type);
783 * process any extra features belonging only to
784 * a group which was just selected
786 while (!displayableTypes.isEmpty())
788 String type = displayableTypes.iterator().next();
789 data[dataIndex][TYPE_COLUMN] = type;
791 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
792 if (data[dataIndex][COLOUR_COLUMN] == null)
794 // "Colour has been updated in another view!!"
795 fr.clearRenderOrder();
798 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
799 data[dataIndex][FILTER_COLUMN] = featureFilter == null
800 ? new FeatureMatcherSet()
802 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
804 displayableTypes.remove(type);
807 if (originalData == null)
809 originalData = new Object[data.length][COLUMN_COUNT];
810 for (int i = 0; i < data.length; i++)
812 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
817 updateOriginalData(data);
820 table.setModel(new FeatureTableModel(data));
821 table.getColumnModel().getColumn(0).setPreferredWidth(200);
823 groupPanel.setLayout(
824 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
825 pruneGroups(foundGroups);
826 groupPanel.validate();
828 updateFeatureRenderer(data, groupChanged != null);
829 resettingTable = false;
833 * Updates 'originalData' (used for restore on Cancel) if we detect that
834 * changes have been made outwith this dialog
836 * <li>a new feature type added (and made visible)</li>
837 * <li>a feature colour changed (in the Amend Features dialog)</li>
842 protected void updateOriginalData(Object[][] foundData)
844 // todo LinkedHashMap instead of Object[][] would be nice
846 Object[][] currentData = ((FeatureTableModel) table.getModel())
848 for (Object[] row : foundData)
850 String type = (String) row[TYPE_COLUMN];
851 boolean found = false;
852 for (Object[] current : currentData)
854 if (type.equals(current[TYPE_COLUMN]))
858 * currently dependent on object equality here;
859 * really need an equals method on FeatureColour
861 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
864 * feature colour has changed externally - update originalData
866 for (Object[] original : originalData)
868 if (type.equals(original[TYPE_COLUMN]))
870 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
881 * new feature detected - add to original data (on top)
883 Object[][] newData = new Object[originalData.length
885 for (int i = 0; i < originalData.length; i++)
887 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
891 originalData = newData;
897 * Remove from the groups panel any checkboxes for groups that are not in the
898 * foundGroups set. This enables removing a group from the display when the
899 * last feature in that group is deleted.
903 protected void pruneGroups(Set<String> foundGroups)
905 for (int g = 0; g < groupPanel.getComponentCount(); g++)
907 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
908 if (!foundGroups.contains(checkbox.getText()))
910 groupPanel.remove(checkbox);
916 * reorder data based on the featureRenderers global priority list.
920 private void ensureOrder(Object[][] data)
922 boolean sort = false;
923 float[] order = new float[data.length];
924 for (int i = 0; i < order.length; i++)
926 order[i] = fr.getOrder(data[i][0].toString());
929 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
933 sort = sort || order[i - 1] > order[i];
938 jalview.util.QuickSort.sort(order, data);
943 * Offers a file chooser dialog, and then loads the feature colours and
944 * filters from file in XML format and unmarshals to Jalview feature settings
948 JalviewFileChooser chooser = new JalviewFileChooser("fc",
949 SEQUENCE_FEATURE_COLOURS);
950 chooser.setFileView(new JalviewFileView());
951 chooser.setDialogTitle(
952 MessageManager.getString("label.load_feature_colours"));
953 chooser.setToolTipText(MessageManager.getString("action.load"));
954 chooser.setResponseHandler(0, () -> {
955 File file = chooser.getSelectedFile();
958 chooser.showOpenDialog(this);
961 public static boolean loadFeatureSettingsFile(FeatureRenderer fr,
962 File file) throws Exception
964 InputStreamReader in = new InputStreamReader(new FileInputStream(file),
966 return loadFeatureSettingsFile(fr, in);
969 public static void loadFeatureSettingsFile(
970 FeatureRenderer featureRenderer, Object fileObject,
971 DataSourceType sourceType) throws Exception
973 FileParse fp = new FileParse(fileObject, sourceType);
974 loadFeatureSettingsFile(featureRenderer, fp.getReader());
977 private static boolean loadFeatureSettingsFile(FeatureRenderer fr,
978 Reader in) throws Exception
980 JAXBContext jc = JAXBContext.newInstance("jalview.xml.binding.jalview");
981 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
982 XMLStreamReader streamReader = XMLInputFactory.newInstance()
983 .createXMLStreamReader(in);
984 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
985 JalviewUserColours.class);
986 JalviewUserColours jucs = jbe.getValue();
988 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
991 * load feature colours
993 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
995 Colour newcol = jucs.getColour().get(i);
996 FeatureColourI colour = jalview.project.Jalview2XML
997 .parseColour(newcol);
998 fr.setColour(newcol.getName(), colour);
999 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
1003 * load feature filters; loaded filters will replace any that are
1004 * currently defined, other defined filters are left unchanged
1006 for (int i = 0; i < jucs.getFilter().size(); i++)
1008 Filter filterModel = jucs.getFilter().get(i);
1009 String featureType = filterModel.getFeatureType();
1010 FeatureMatcherSetI filter = jalview.project.Jalview2XML
1011 .parseFilter(featureType, filterModel.getMatcherSet());
1012 if (!filter.isEmpty())
1014 fr.setFeatureFilter(featureType, filter);
1021 * Loads feature colours and filters from XML stored in the given file
1025 void load(File file)
1027 load(file, DataSourceType.FILE);
1031 * Loads feature colours and filters from XML at a specified source
1034 * - string or file or other object that allows FileParse to be
1037 void load(Object file, DataSourceType sourceType)
1041 loadFeatureSettingsFile(fr, file, sourceType);
1043 * update feature settings table
1048 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1050 updateFeatureRenderer(data, false);
1053 } catch (Exception ex)
1056 .outPrintln("Error loading User Colour File\n" + ex);
1061 * Offers a file chooser dialog, and then saves the current feature colours
1062 * and any filters to the selected file in XML format
1066 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1067 SEQUENCE_FEATURE_COLOURS);
1068 chooser.setFileView(new JalviewFileView());
1069 chooser.setDialogTitle(
1070 MessageManager.getString("label.save_feature_colours"));
1071 chooser.setToolTipText(MessageManager.getString("action.save"));
1072 int option = chooser.showSaveDialog(this);
1073 if (option == JalviewFileChooser.APPROVE_OPTION)
1075 File file = chooser.getSelectedFile();
1081 * Saves feature colours and filters to the given file
1085 void save(File file)
1087 JalviewUserColours ucs = new JalviewUserColours();
1088 ucs.setSchemeName("Sequence Features");
1091 PrintWriter out = new PrintWriter(
1092 new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
1095 * sort feature types by colour order, from 0 (highest)
1098 Set<String> fr_colours = fr.getAllFeatureColours();
1099 String[] sortedTypes = fr_colours
1100 .toArray(new String[fr_colours.size()]);
1101 Arrays.sort(sortedTypes, new Comparator<String>()
1104 public int compare(String type1, String type2)
1106 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1111 * save feature colours
1113 for (String featureType : sortedTypes)
1115 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1116 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1118 ucs.getColour().add(col);
1122 * save any feature filters
1124 for (String featureType : sortedTypes)
1126 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1127 if (filter != null && !filter.isEmpty())
1129 Iterator<FeatureMatcherI> iterator = filter.getMatchers()
1131 FeatureMatcherI firstMatcher = iterator.next();
1132 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1133 .marshalFilter(firstMatcher, iterator, filter.isAnded());
1134 Filter filterModel = new Filter();
1135 filterModel.setFeatureType(featureType);
1136 filterModel.setMatcherSet(ms);
1137 ucs.getFilter().add(filterModel);
1140 JAXBContext jaxbContext = JAXBContext
1141 .newInstance(JalviewUserColours.class);
1142 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1143 jaxbMarshaller.marshal(
1144 new ObjectFactory().createJalviewUserColours(ucs), out);
1146 // jaxbMarshaller.marshal(object, pout);
1147 // marshaller.marshal(object);
1150 // ucs.marshal(out);
1152 } catch (Exception ex)
1154 ex.printStackTrace();
1158 public void invertSelection()
1160 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1161 for (int i = 0; i < data.length; i++)
1163 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1165 updateFeatureRenderer(data, true);
1169 public void orderByAvWidth()
1171 if (table == null || table.getModel() == null)
1175 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1176 float[] width = new float[data.length];
1180 for (int i = 0; i < data.length; i++)
1182 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1185 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1186 // weight - but have to make per
1187 // sequence, too (awidth[2])
1188 // if (width[i]==1) // hack to distinguish single width sequences.
1199 boolean sort = false;
1200 for (int i = 0; i < width.length; i++)
1202 // awidth = (float[]) typeWidth.get(data[i][0]);
1205 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1208 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1214 width[i] /= max; // normalize
1215 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for
1220 sort = sort || width[i - 1] > width[i];
1225 jalview.util.QuickSort.sort(width, data);
1226 // update global priority order
1229 updateFeatureRenderer(data, false);
1234 * close ourselves but leave any existing UI handlers (e.g a CDS/Protein
1235 * tabbed feature settings dialog) intact
1237 public void closeOldSettings()
1243 * close the feature settings dialog (and any containing frame)
1250 private void closeDialog(boolean closeContainingFrame)
1256 af.setFeatureSettingsGeometry(frame.getBounds());
1257 frame.setClosed(true);
1261 SplitContainerI sc = af.getSplitViewContainer();
1262 sc.closeFeatureSettings(this, closeContainingFrame);
1263 af.featureSettings = null;
1265 } catch (Exception exe)
1271 public void updateFeatureRenderer(Object[][] data)
1273 updateFeatureRenderer(data, true);
1277 * Update the priority order of features; only repaint if this changed the
1278 * order of visible features
1283 void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1285 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1287 if (fr.setFeaturePriority(rowData, visibleNew))
1294 * Converts table data into an array of data beans
1296 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1298 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1299 for (int i = 0; i < data.length; i++)
1301 String type = (String) data[i][TYPE_COLUMN];
1302 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1303 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1304 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1305 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1311 private void jbInit() throws Exception
1313 this.setLayout(new BorderLayout());
1315 final boolean hasComplement = af.getViewport()
1316 .getCodingComplement() != null;
1318 JPanel settingsPane = new JPanel();
1319 settingsPane.setLayout(new BorderLayout());
1321 JPanel bigPanel = new JPanel();
1322 bigPanel.setLayout(new BorderLayout());
1324 groupPanel = new JPanel();
1325 bigPanel.add(groupPanel, BorderLayout.NORTH);
1327 JButton invert = new JButton(
1328 MessageManager.getString("label.invert_selection"));
1329 invert.setFont(JvSwingUtils.getLabelFont());
1330 invert.addActionListener(new ActionListener()
1333 public void actionPerformed(ActionEvent e)
1339 JButton optimizeOrder = new JButton(
1340 MessageManager.getString("label.optimise_order"));
1341 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1342 optimizeOrder.addActionListener(new ActionListener()
1345 public void actionPerformed(ActionEvent e)
1351 final String byScoreLabel = MessageManager
1352 .getString("label.seq_sort_by_score");
1353 JButton sortByScore = new JButton(byScoreLabel);
1354 sortByScore.setFont(JvSwingUtils.getLabelFont());
1355 sortByScore.addActionListener(new ActionListener()
1358 public void actionPerformed(ActionEvent e)
1360 if (canSortBy(byScoreLabel))
1366 final String byDensityLabel = MessageManager
1367 .getString("label.sequence_sort_by_density");
1368 JButton sortByDens = new JButton(byDensityLabel);
1369 sortByDens.setFont(JvSwingUtils.getLabelFont());
1370 sortByDens.addActionListener(new ActionListener()
1373 public void actionPerformed(ActionEvent e)
1375 if (canSortBy(byDensityLabel))
1377 sortByDensity(null);
1382 JButton help = new JButton(MessageManager.getString("action.help"));
1383 help.setFont(JvSwingUtils.getLabelFont());
1384 help.addActionListener(new ActionListener()
1387 public void actionPerformed(ActionEvent e)
1391 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1392 } catch (HelpSetException e1)
1394 e1.printStackTrace();
1398 // Cancel for a SplitFrame should just revert changes to the currently
1400 // settings. May want to do this for either or both - so need a splitview
1401 // feature settings cancel/OK.
1402 JButton cancel = new JButton(MessageManager
1403 .getString(hasComplement ? "action.revert" : "action.cancel"));
1404 cancel.setToolTipText(MessageManager.getString(hasComplement
1405 ? "action.undo_changes_to_feature_settings"
1406 : "action.undo_changes_to_feature_settings_and_close_the_dialog"));
1407 cancel.setFont(JvSwingUtils.getLabelFont());
1408 // TODO: disable cancel (and apply!) until current settings are different
1409 cancel.addActionListener(new ActionListener()
1412 public void actionPerformed(ActionEvent e)
1422 // Cancel for the whole dialog should cancel both CDS and Protein.
1423 // OK for an individual feature settings just applies changes, but dialog
1425 JButton ok = new JButton(MessageManager
1426 .getString(hasComplement ? "action.apply" : "action.ok"));
1427 ok.setFont(JvSwingUtils.getLabelFont());
1428 ok.addActionListener(new ActionListener()
1431 public void actionPerformed(ActionEvent e)
1439 storeOriginalSettings();
1444 JButton loadColours = new JButton(
1445 MessageManager.getString("label.load_colours"));
1446 loadColours.setFont(JvSwingUtils.getLabelFont());
1447 loadColours.setToolTipText(
1448 MessageManager.getString("label.load_colours_tooltip"));
1449 loadColours.addActionListener(new ActionListener()
1452 public void actionPerformed(ActionEvent e)
1458 JButton saveColours = new JButton(
1459 MessageManager.getString("label.save_colours"));
1460 saveColours.setFont(JvSwingUtils.getLabelFont());
1461 saveColours.setToolTipText(
1462 MessageManager.getString("label.save_colours_tooltip"));
1463 saveColours.addActionListener(new ActionListener()
1466 public void actionPerformed(ActionEvent e)
1471 transparency.addChangeListener(new ChangeListener()
1474 public void stateChanged(ChangeEvent evt)
1476 if (!inConstruction)
1478 fr.setTransparency((100 - transparency.getValue()) / 100f);
1484 transparency.setMaximum(70);
1485 transparency.setToolTipText(
1486 MessageManager.getString("label.transparency_tip"));
1488 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1489 String text = MessageManager
1490 .formatMessage("label.show_linked_features",
1492 ? MessageManager.getString("label.protein")
1493 .toLowerCase(Locale.ROOT)
1495 showComplement = new JCheckBox(text);
1496 showComplement.addActionListener(new ActionListener()
1499 public void actionPerformed(ActionEvent e)
1502 .setShowComplementFeatures(showComplement.isSelected());
1507 showComplementOnTop = new JCheckBox(
1508 MessageManager.getString("label.on_top"));
1509 showComplementOnTop.addActionListener(new ActionListener()
1512 public void actionPerformed(ActionEvent e)
1514 af.getViewport().setShowComplementFeaturesOnTop(
1515 showComplementOnTop.isSelected());
1520 updateComplementButtons();
1522 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1523 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1525 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1526 transbuttons.add(optimizeOrder);
1527 transbuttons.add(invert);
1528 transbuttons.add(sortByScore);
1529 transbuttons.add(sortByDens);
1530 transbuttons.add(help);
1532 JPanel transPanelLeft = new JPanel(
1533 new GridLayout(hasComplement ? 4 : 2, 1));
1534 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1535 transPanelLeft.add(transparency);
1538 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1539 cp.add(showComplement);
1540 cp.add(showComplementOnTop);
1541 transPanelLeft.add(cp);
1543 lowerPanel.add(transPanelLeft);
1544 lowerPanel.add(transbuttons);
1546 JPanel buttonPanel = new JPanel();
1547 buttonPanel.add(ok);
1548 buttonPanel.add(cancel);
1549 buttonPanel.add(loadColours);
1550 buttonPanel.add(saveColours);
1551 bigPanel.add(scrollPane, BorderLayout.CENTER);
1552 settingsPane.add(bigPanel, BorderLayout.CENTER);
1553 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1554 this.add(settingsPane);
1558 * Repaints alignment, structure and overview (if shown). If there is a
1559 * complementary view which is showing this view's features, then also
1562 void refreshDisplay()
1564 af.alignPanel.paintAlignment(true, true);
1565 AlignViewportI complement = af.getViewport().getCodingComplement();
1566 if (complement != null && complement.isShowComplementFeatures())
1568 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1569 af2.alignPanel.paintAlignment(true, true);
1574 * Answers a suitable tooltip to show on the colour cell of the table
1578 * if true include 'click to edit' and similar text
1581 public static String getColorTooltip(FeatureColourI fcol,
1588 if (fcol.isSimpleColour())
1590 return withHint ? BASE_TOOLTIP : null;
1592 String description = fcol.getDescription();
1593 description = description.replaceAll("<", "<");
1594 description = description.replaceAll(">", ">");
1595 StringBuilder tt = new StringBuilder(description);
1598 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1600 return JvSwingUtils.wrapTooltip(true, tt.toString());
1603 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1606 boolean thr = false;
1607 StringBuilder tx = new StringBuilder();
1609 if (gcol.isColourByAttribute())
1611 tx.append(FeatureMatcher
1612 .toAttributeDisplayName(gcol.getAttributeName()));
1614 else if (!gcol.isColourByLabel())
1616 tx.append(MessageManager.getString("label.score"));
1619 if (gcol.isAboveThreshold())
1624 if (gcol.isBelowThreshold())
1629 if (gcol.isColourByLabel())
1635 if (!gcol.isColourByAttribute())
1643 Color newColor = gcol.getMaxColour();
1644 comp.setBackground(newColor);
1645 // jalview.bin.Console.errPrintln("Width is " + w / 2);
1646 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1647 comp.setIcon(ficon);
1648 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1649 // + newColor.getGreen() + ", " + newColor.getBlue()
1650 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1651 // + ", " + minCol.getBlue() + ")");
1653 comp.setHorizontalAlignment(SwingConstants.CENTER);
1654 comp.setText(tx.toString());
1657 // ///////////////////////////////////////////////////////////////////////
1658 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1659 // ///////////////////////////////////////////////////////////////////////
1660 class FeatureTableModel extends AbstractTableModel
1662 private String[] columnNames = {
1663 MessageManager.getString("label.feature_type"),
1664 MessageManager.getString("action.colour"),
1665 MessageManager.getString("label.configuration"),
1666 MessageManager.getString("label.show") };
1668 private Object[][] data;
1670 FeatureTableModel(Object[][] data)
1675 public Object[][] getData()
1680 public void setData(Object[][] data)
1686 public int getColumnCount()
1688 return columnNames.length;
1691 public Object[] getRow(int row)
1697 public int getRowCount()
1703 public String getColumnName(int col)
1705 return columnNames[col];
1709 public Object getValueAt(int row, int col)
1711 return data[row][col];
1715 * Answers the class of column c of the table
1718 public Class<?> getColumnClass(int c)
1723 return String.class;
1725 return FeatureColour.class;
1727 return FeatureMatcherSet.class;
1729 return Boolean.class;
1734 public boolean isCellEditable(int row, int col)
1736 return col == 0 ? false : true;
1740 public void setValueAt(Object value, int row, int col)
1742 data[row][col] = value;
1743 fireTableCellUpdated(row, col);
1744 updateFeatureRenderer(data);
1749 class ColorRenderer extends JLabel implements TableCellRenderer
1751 Border unselectedBorder = null;
1753 Border selectedBorder = null;
1755 public ColorRenderer()
1757 setOpaque(true); // MUST do this for background to show up.
1758 setHorizontalTextPosition(SwingConstants.CENTER);
1759 setVerticalTextPosition(SwingConstants.CENTER);
1763 public Component getTableCellRendererComponent(JTable tbl, Object color,
1764 boolean isSelected, boolean hasFocus, int row, int column)
1766 FeatureColourI cellColour = (FeatureColourI) color;
1768 setBackground(tbl.getBackground());
1769 if (!cellColour.isSimpleColour())
1771 Rectangle cr = tbl.getCellRect(row, column, false);
1772 FeatureSettings.renderGraduatedColor(this, cellColour,
1773 (int) cr.getWidth(), (int) cr.getHeight());
1779 setBackground(cellColour.getColour());
1783 if (selectedBorder == null)
1785 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1786 tbl.getSelectionBackground());
1788 setBorder(selectedBorder);
1792 if (unselectedBorder == null)
1794 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1795 tbl.getBackground());
1797 setBorder(unselectedBorder);
1804 class FilterRenderer extends JLabel implements TableCellRenderer
1806 javax.swing.border.Border unselectedBorder = null;
1808 javax.swing.border.Border selectedBorder = null;
1810 public FilterRenderer()
1812 setOpaque(true); // MUST do this for background to show up.
1813 setHorizontalTextPosition(SwingConstants.CENTER);
1814 setVerticalTextPosition(SwingConstants.CENTER);
1818 public Component getTableCellRendererComponent(JTable tbl,
1819 Object filter, boolean isSelected, boolean hasFocus, int row,
1822 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1824 setBackground(tbl.getBackground());
1827 if (theFilter != null)
1829 String asText = theFilter.toString();
1830 this.setText(asText);
1835 if (selectedBorder == null)
1837 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1838 tbl.getSelectionBackground());
1840 setBorder(selectedBorder);
1844 if (unselectedBorder == null)
1846 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1847 tbl.getBackground());
1849 setBorder(unselectedBorder);
1857 * update comp using rendering settings from gcol
1862 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1864 int w = comp.getWidth(), h = comp.getHeight();
1867 w = (int) comp.getPreferredSize().getWidth();
1868 h = (int) comp.getPreferredSize().getHeight();
1875 renderGraduatedColor(comp, gcol, w, h);
1878 @SuppressWarnings("serial")
1879 class ColorEditor extends AbstractCellEditor
1880 implements TableCellEditor, ActionListener
1882 FeatureColourI currentColor;
1884 FeatureTypeSettings chooser;
1890 protected static final String EDIT = "edit";
1892 int rowSelected = 0;
1894 public ColorEditor()
1896 // Set up the editor (from the table's point of view),
1897 // which is a button.
1898 // This button brings up the color chooser dialog,
1899 // which is the editor from the user's point of view.
1900 button = new JButton();
1901 button.setActionCommand(EDIT);
1902 button.addActionListener(this);
1903 button.setBorderPainted(false);
1907 * Handles events from the editor button, and from the colour/filters
1908 * dialog's OK button
1911 public void actionPerformed(ActionEvent e)
1913 if (button == e.getSource())
1915 if (currentColor.isSimpleColour())
1918 * simple colour chooser
1920 String ttl = MessageManager
1921 .formatMessage("label.select_colour_for", type);
1922 Object last = (Boolean) table.getValueAt(selectedRow,
1924 table.setValueAt(Boolean.TRUE, selectedRow, SHOW_COLUMN);
1925 ColourChooserListener listener = new ColourChooserListener()
1928 public void colourSelected(Color c)
1930 currentColor = new FeatureColour(c);
1931 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1932 table.setValueAt(Boolean.TRUE, selectedRow, SHOW_COLUMN);
1933 fireEditingStopped();
1937 public void cancel()
1939 table.setValueAt(last, selectedRow, SHOW_COLUMN);
1940 fireEditingStopped();
1943 JalviewColourChooser.showColourChooser(button, ttl,
1944 currentColor.getColour(), listener);
1949 * variable colour and filters dialog
1951 boolean last = (Boolean) table.getValueAt(selectedRow,
1953 table.setValueAt(Boolean.TRUE, selectedRow, SHOW_COLUMN);
1954 chooser = new FeatureTypeSettings(fr, type, last);
1955 if (!Platform.isJS())
1962 chooser.setRequestFocusEnabled(true);
1963 chooser.requestFocus();
1965 chooser.addActionListener(this);
1966 fireEditingStopped();
1972 * after OK in variable colour dialog, any changes to colour
1973 * (or filters!) are already set in FeatureRenderer, so just
1974 * update table data without triggering updateFeatureRenderer
1976 currentColor = fr.getFeatureColours().get(type);
1977 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
1978 if (currentFilter == null)
1980 currentFilter = new FeatureMatcherSet();
1982 Object[] data = ((FeatureTableModel) table.getModel())
1983 .getData()[rowSelected];
1984 data[COLOUR_COLUMN] = currentColor;
1985 data[FILTER_COLUMN] = currentFilter;
1986 fireEditingStopped();
1987 // SwingJS needs an explicit repaint() here,
1988 // rather than relying upon no validation having
1989 // occurred since the stopEditing call was made.
1990 // Its laying out has not been stopped by the modal frame
1997 * Override allows access to this method from anonymous inner classes
2000 protected void fireEditingStopped()
2002 super.fireEditingStopped();
2005 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2007 public Object getCellEditorValue()
2009 return currentColor;
2012 // Implement the one method defined by TableCellEditor.
2014 public Component getTableCellEditorComponent(JTable theTable,
2015 Object value, boolean isSelected, int row, int column)
2017 currentColor = (FeatureColourI) value;
2018 this.rowSelected = row;
2019 type = table.getValueAt(row, TYPE_COLUMN).toString();
2020 button.setOpaque(true);
2021 button.setBackground(FeatureSettings.this.getBackground());
2022 if (!currentColor.isSimpleColour())
2024 JLabel btn = new JLabel();
2025 btn.setSize(button.getSize());
2026 FeatureSettings.renderGraduatedColor(btn, currentColor);
2027 button.setBackground(btn.getBackground());
2028 button.setIcon(btn.getIcon());
2029 button.setText(btn.getText());
2034 button.setIcon(null);
2035 button.setBackground(currentColor.getColour());
2042 * The cell editor for the Filter column. It displays the text of any filters
2043 * for the feature type in that row (in full as a tooltip, possible
2044 * abbreviated as display text). On click in the cell, opens the Feature
2045 * Display Settings dialog at the Filters tab.
2047 @SuppressWarnings("serial")
2048 class FilterEditor extends AbstractCellEditor
2049 implements TableCellEditor, ActionListener
2052 FeatureMatcherSetI currentFilter;
2060 protected static final String EDIT = "edit";
2062 int rowSelected = 0;
2064 public FilterEditor()
2066 button = new JButton();
2067 button.setActionCommand(EDIT);
2068 button.addActionListener(this);
2069 button.setBorderPainted(false);
2073 * Handles events from the editor button
2076 public void actionPerformed(ActionEvent e)
2078 if (button == e.getSource())
2080 boolean last = fr.getFeaturesDisplayed().isVisible(type);
2081 ((FeatureTableModel) table.getModel()).setValueAt(Boolean.TRUE,
2082 rowSelected, SHOW_COLUMN);
2083 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type,
2085 chooser.addActionListener(this);
2086 chooser.setRequestFocusEnabled(true);
2087 chooser.requestFocus();
2088 if (lastLocation != null)
2090 // todo open at its last position on screen
2091 chooser.setBounds(lastLocation.x, lastLocation.y,
2092 chooser.getWidth(), chooser.getHeight());
2095 fireEditingStopped();
2097 else if (e.getSource() instanceof Component)
2101 * after OK in variable colour dialog, any changes to filter
2102 * (or colours!) are already set in FeatureRenderer, so just
2103 * update table data without triggering updateFeatureRenderer
2105 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2106 currentFilter = fr.getFeatureFilter(type);
2107 if (currentFilter == null)
2109 currentFilter = new FeatureMatcherSet();
2112 Object[] data = ((FeatureTableModel) table.getModel())
2113 .getData()[rowSelected];
2114 data[COLOUR_COLUMN] = currentColor;
2115 data[FILTER_COLUMN] = currentFilter;
2116 data[SHOW_COLUMN] = fr.getFeaturesDisplayed().isVisible(type);
2118 fireEditingStopped();
2119 // SwingJS needs an explicit repaint() here,
2120 // rather than relying upon no validation having
2121 // occurred since the stopEditing call was made.
2122 // Its laying out has not been stopped by the modal frame
2129 public Object getCellEditorValue()
2131 return currentFilter;
2135 public Component getTableCellEditorComponent(JTable theTable,
2136 Object value, boolean isSelected, int row, int column)
2138 currentFilter = (FeatureMatcherSetI) value;
2139 this.rowSelected = row;
2140 type = table.getValueAt(row, TYPE_COLUMN).toString();
2141 button.setOpaque(true);
2142 button.setBackground(FeatureSettings.this.getBackground());
2143 button.setText(currentFilter.toString());
2144 button.setIcon(null);
2149 public boolean isOpen()
2151 if (af.getSplitViewContainer() != null)
2153 return af.getSplitViewContainer().isFeatureSettingsOpen();
2155 return frame != null && !frame.isClosed();
2159 public void revert()
2161 fr.setTransparency(originalTransparency);
2162 fr.setFeatureFilters(originalFilters);
2163 updateFeatureRenderer(originalData);
2164 af.getViewport().setViewStyle(originalViewStyle);
2165 updateTransparencySliderFromFR();
2166 updateComplementButtons();
2171 class FeatureIcon implements Icon
2173 FeatureColourI gcol;
2177 boolean midspace = false;
2179 int width = 50, height = 20;
2181 int s1, e1; // start and end of midpoint band for thresholded symbol
2183 Color mpcolour = Color.white;
2185 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2205 public int getIconWidth()
2211 public int getIconHeight()
2217 public void paintIcon(Component c, Graphics g, int x, int y)
2220 if (gcol.isColourByLabel())
2223 g.fillRect(0, 0, width, height);
2224 // need an icon here.
2225 g.setColor(gcol.getMaxColour());
2227 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2229 // g.setFont(g.getFont().deriveFont(
2230 // AffineTransform.getScaleInstance(
2231 // width/g.getFontMetrics().stringWidth("Label"),
2232 // height/g.getFontMetrics().getHeight())));
2234 g.drawString(MessageManager.getString("label.label"), 0, 0);
2239 Color minCol = gcol.getMinColour();
2241 g.fillRect(0, 0, s1, height);
2244 g.setColor(Color.white);
2245 g.fillRect(s1, 0, e1 - s1, height);
2247 g.setColor(gcol.getMaxColour());
2248 // g.fillRect(0, e1, width - e1, height); // BH 2018
2249 g.fillRect(e1, 0, width - e1, height);