2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewControllerGuiI;
24 import jalview.api.AlignViewportI;
25 import jalview.api.FeatureColourI;
26 import jalview.api.FeatureSettingsControllerI;
27 import jalview.api.SplitContainerI;
28 import jalview.api.ViewStyleI;
29 import jalview.controller.FeatureSettingsControllerGuiI;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.SequenceI;
32 import jalview.datamodel.features.FeatureMatcher;
33 import jalview.datamodel.features.FeatureMatcherI;
34 import jalview.datamodel.features.FeatureMatcherSet;
35 import jalview.datamodel.features.FeatureMatcherSetI;
36 import jalview.gui.Help.HelpId;
37 import jalview.io.JalviewFileChooser;
38 import jalview.io.JalviewFileView;
39 import jalview.schemes.FeatureColour;
40 import jalview.util.MessageManager;
41 import jalview.util.Platform;
42 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
43 import jalview.viewmodel.styles.ViewStyle;
44 import jalview.xml.binding.jalview.JalviewUserColours;
45 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
46 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
47 import jalview.xml.binding.jalview.ObjectFactory;
49 import java.awt.BorderLayout;
50 import java.awt.Color;
51 import java.awt.Component;
52 import java.awt.Dimension;
53 import java.awt.FlowLayout;
55 import java.awt.Graphics;
56 import java.awt.GridLayout;
57 import java.awt.Point;
58 import java.awt.Rectangle;
59 import java.awt.event.ActionEvent;
60 import java.awt.event.ActionListener;
61 import java.awt.event.ItemEvent;
62 import java.awt.event.ItemListener;
63 import java.awt.event.MouseAdapter;
64 import java.awt.event.MouseEvent;
65 import java.awt.event.MouseMotionAdapter;
66 import java.beans.PropertyChangeEvent;
67 import java.beans.PropertyChangeListener;
69 import java.io.FileInputStream;
70 import java.io.FileOutputStream;
71 import java.io.InputStreamReader;
72 import java.io.OutputStreamWriter;
73 import java.io.PrintWriter;
74 import java.util.Arrays;
75 import java.util.Comparator;
76 import java.util.HashMap;
77 import java.util.HashSet;
78 import java.util.Hashtable;
79 import java.util.Iterator;
80 import java.util.List;
84 import javax.help.HelpSetException;
85 import javax.swing.AbstractCellEditor;
86 import javax.swing.BorderFactory;
87 import javax.swing.Icon;
88 import javax.swing.JButton;
89 import javax.swing.JCheckBox;
90 import javax.swing.JColorChooser;
91 import javax.swing.JDialog;
92 import javax.swing.JInternalFrame;
93 import javax.swing.JLabel;
94 import javax.swing.JLayeredPane;
95 import javax.swing.JMenuItem;
96 import javax.swing.JPanel;
97 import javax.swing.JPopupMenu;
98 import javax.swing.JScrollPane;
99 import javax.swing.JSlider;
100 import javax.swing.JTable;
101 import javax.swing.ListSelectionModel;
102 import javax.swing.SwingConstants;
103 import javax.swing.border.Border;
104 import javax.swing.event.ChangeEvent;
105 import javax.swing.event.ChangeListener;
106 import javax.swing.table.AbstractTableModel;
107 import javax.swing.table.JTableHeader;
108 import javax.swing.table.TableCellEditor;
109 import javax.swing.table.TableCellRenderer;
110 import javax.swing.table.TableColumn;
111 import javax.xml.bind.JAXBContext;
112 import javax.xml.bind.JAXBElement;
113 import javax.xml.bind.Marshaller;
114 import javax.xml.stream.XMLInputFactory;
115 import javax.xml.stream.XMLStreamReader;
117 public class FeatureSettings extends JPanel
118 implements FeatureSettingsControllerI, FeatureSettingsControllerGuiI
120 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
121 .getString("label.sequence_feature_colours");
124 * column indices of fields in Feature Settings table
126 static final int TYPE_COLUMN = 0;
128 static final int COLOUR_COLUMN = 1;
130 static final int FILTER_COLUMN = 2;
132 static final int SHOW_COLUMN = 3;
134 private static final int COLUMN_COUNT = 4;
136 private static final int MIN_WIDTH = 400;
138 private static final int MIN_HEIGHT = 400;
140 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
142 final FeatureRenderer fr;
144 public final AlignFrame af;
147 * 'original' fields hold settings to restore on Cancel
149 Object[][] originalData;
151 private float originalTransparency;
153 private ViewStyleI originalViewStyle;
155 private Map<String, FeatureMatcherSetI> originalFilters;
157 final JInternalFrame frame;
159 JScrollPane scrollPane = new JScrollPane();
165 JSlider transparency= new JSlider();
167 private JCheckBox showComplementOnTop;
169 private JCheckBox showComplement;
172 * when true, constructor is still executing - so ignore UI events
174 protected volatile boolean inConstruction = true;
176 int selectedRow = -1;
178 boolean resettingTable = false;
181 * true when Feature Settings are updating from feature renderer
183 private boolean handlingUpdate = false;
186 * a change listener to ensure the dialog is updated if
187 * FeatureRenderer discovers new features
189 private PropertyChangeListener change;
192 * holds {featureCount, totalExtent} for each feature type
194 Map<String, float[]> typeWidth = null;
196 private void storeOriginalSettings()
198 // save transparency for restore on Cancel
199 originalTransparency = fr.getTransparency();
201 updateTransparencySliderFromFR();
203 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
204 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
207 private void updateTransparencySliderFromFR()
209 boolean incon = inConstruction;
210 inConstruction = true;
212 int transparencyAsPercent = (int) (fr.getTransparency() * 100);
213 transparency.setValue(100 - transparencyAsPercent);
214 inConstruction = incon;
221 public FeatureSettings(AlignFrame alignFrame)
223 this.af = alignFrame;
224 fr = af.getFeatureRenderer();
226 storeOriginalSettings();
231 } catch (Exception ex)
233 ex.printStackTrace();
239 public String getToolTipText(MouseEvent e)
242 int column = table.columnAtPoint(e.getPoint());
243 int row = table.rowAtPoint(e.getPoint());
248 tip = JvSwingUtils.wrapTooltip(true, MessageManager
249 .getString("label.feature_settings_click_drag"));
252 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
254 tip = getColorTooltip(colour, true);
257 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
261 .getString("label.configure_feature_tooltip")
272 * Position the tooltip near the bottom edge of, and half way across, the
276 public Point getToolTipLocation(MouseEvent e)
278 Point point = e.getPoint();
279 int column = table.columnAtPoint(point);
280 int row = table.rowAtPoint(point);
281 Rectangle r = getCellRect(row, column, false);
282 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
286 JTableHeader tableHeader = table.getTableHeader();
287 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
288 tableHeader.setReorderingAllowed(false);
289 table.setFont(new Font("Verdana", Font.PLAIN, 12));
291 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
292 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
294 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
295 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
297 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
298 new ColorRenderer(), new ColorEditor(this));
299 table.addColumn(colourColumn);
301 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
302 new FilterRenderer(), new FilterEditor(this));
303 table.addColumn(filterColumn);
305 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
307 table.addMouseListener(new MouseAdapter()
310 public void mousePressed(MouseEvent evt)
312 Point pt = evt.getPoint();
313 selectedRow = table.rowAtPoint(pt);
314 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
315 if (evt.isPopupTrigger())
317 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
318 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
321 else if (evt.getClickCount() == 2
322 && table.columnAtPoint(pt) == TYPE_COLUMN)
324 boolean invertSelection = evt.isAltDown();
325 boolean toggleSelection = Platform.isControlDown(evt);
326 boolean extendSelection = evt.isShiftDown();
327 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
328 invertSelection, extendSelection, toggleSelection, type);
329 fr.ap.av.sendSelection();
333 // isPopupTrigger fires on mouseReleased on Windows
335 public void mouseReleased(MouseEvent evt)
337 selectedRow = table.rowAtPoint(evt.getPoint());
338 if (evt.isPopupTrigger())
340 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
341 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
342 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
348 table.addMouseMotionListener(new MouseMotionAdapter()
351 public void mouseDragged(MouseEvent evt)
353 int newRow = table.rowAtPoint(evt.getPoint());
354 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
357 * reposition 'selectedRow' to 'newRow' (the dragged to location)
358 * this could be more than one row away for a very fast drag action
359 * so just swap it with adjacent rows until we get it there
361 Object[][] data = ((FeatureTableModel) table.getModel())
363 int direction = newRow < selectedRow ? -1 : 1;
364 for (int i = selectedRow; i != newRow; i += direction)
366 Object[] temp = data[i];
367 data[i] = data[i + direction];
368 data[i + direction] = temp;
370 updateFeatureRenderer(data);
372 selectedRow = newRow;
376 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
377 // MessageManager.getString("label.feature_settings_click_drag")));
378 scrollPane.setViewportView(table);
380 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
382 fr.findAllFeatures(true); // display everything!
385 discoverAllFeatureData();
386 final FeatureSettings fs = this;
387 fr.addPropertyChangeListener(change = new PropertyChangeListener()
390 public void propertyChange(PropertyChangeEvent evt)
392 if (!fs.resettingTable && !fs.handlingUpdate)
394 fs.handlingUpdate = true;
396 // new groups may be added with new sequence feature types only
397 fs.handlingUpdate = false;
402 SplitContainerI splitframe = af.getSplitViewContainer();
403 if (splitframe != null)
405 frame = null; // keeps eclipse happy
406 splitframe.addFeatureSettingsUI(this);
410 frame = new JInternalFrame();
411 frame.setContentPane(this);
412 Rectangle bounds = af.getFeatureSettingsGeometry();
414 if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels(
415 af.alignPanel.av.getSequenceSetId()).length > 1)
417 title = MessageManager.formatMessage(
418 "label.sequence_feature_settings_for_view",
419 af.alignPanel.getViewName());
423 title = MessageManager.getString("label.sequence_feature_settings");
427 if (Platform.isAMac())
429 Desktop.addInternalFrame(frame, title, 600, 480);
433 Desktop.addInternalFrame(frame, title, 600, 450);
438 Desktop.addInternalFrame(frame, title,
439 false, bounds.width, bounds.height);
440 frame.setBounds(bounds);
441 frame.setVisible(true);
443 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
445 frame.addInternalFrameListener(
446 new javax.swing.event.InternalFrameAdapter()
449 public void internalFrameClosed(
450 javax.swing.event.InternalFrameEvent evt)
452 featureSettings_isClosed();
455 frame.setLayer(JLayeredPane.PALETTE_LAYER);
457 inConstruction = false;
461 * Sets the state of buttons to show complement features from viewport
464 private void updateComplementButtons()
466 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
468 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
472 public AlignViewControllerGuiI getAlignframe()
478 public void featureSettings_isClosed()
480 fr.removePropertyChangeListener(change);
484 protected void popupSort(final int rowSelected, final String type,
485 final Object typeCol, final Map<String, float[][]> minmax, int x,
488 JPopupMenu men = new JPopupMenu(MessageManager
489 .formatMessage("label.settings_for_param", new String[]
491 JMenuItem scr = new JMenuItem(
492 MessageManager.getString("label.sort_by_score"));
494 scr.addActionListener(new ActionListener()
497 public void actionPerformed(ActionEvent e)
499 sortByScore(Arrays.asList(new String[] { type }));
502 JMenuItem dens = new JMenuItem(
503 MessageManager.getString("label.sort_by_density"));
504 dens.addActionListener(new ActionListener()
507 public void actionPerformed(ActionEvent e)
509 sortByDensity(Arrays.asList(new String[] { type }));
514 JMenuItem selCols = new JMenuItem(
515 MessageManager.getString("label.select_columns_containing"));
516 selCols.addActionListener(new ActionListener()
519 public void actionPerformed(ActionEvent arg0)
521 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
523 fr.ap.av.sendSelection();
526 JMenuItem clearCols = new JMenuItem(MessageManager
527 .getString("label.select_columns_not_containing"));
528 clearCols.addActionListener(new ActionListener()
531 public void actionPerformed(ActionEvent arg0)
533 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
535 fr.ap.av.sendSelection();
538 JMenuItem hideCols = new JMenuItem(
539 MessageManager.getString("label.hide_columns_containing"));
540 hideCols.addActionListener(new ActionListener()
543 public void actionPerformed(ActionEvent arg0)
545 fr.ap.alignFrame.hideFeatureColumns(type, true);
546 fr.ap.av.sendSelection();
549 JMenuItem hideOtherCols = new JMenuItem(
550 MessageManager.getString("label.hide_columns_not_containing"));
551 hideOtherCols.addActionListener(new ActionListener()
554 public void actionPerformed(ActionEvent arg0)
556 fr.ap.alignFrame.hideFeatureColumns(type, false);
557 fr.ap.av.sendSelection();
563 men.add(hideOtherCols);
564 men.show(table, x, y);
568 * Sort the sequences in the alignment by the number of features for the given
569 * feature types (or all features if null)
571 * @param featureTypes
573 protected void sortByDensity(List<String> featureTypes)
575 af.avc.sortAlignmentByFeatureDensity(featureTypes);
579 * Sort the sequences in the alignment by average score for the given feature
580 * types (or all features if null)
582 * @param featureTypes
584 protected void sortByScore(List<String> featureTypes)
586 af.avc.sortAlignmentByFeatureScore(featureTypes);
590 * Returns true if at least one feature type is visible. Else shows a warning
591 * dialog and returns false.
596 private boolean canSortBy(String title)
598 if (fr.getDisplayedFeatureTypes().isEmpty())
600 JvOptionPane.showMessageDialog(this,
601 MessageManager.getString("label.no_features_to_sort_by"),
602 title, JvOptionPane.OK_OPTION);
609 synchronized public void discoverAllFeatureData()
611 Set<String> allGroups = new HashSet<>();
612 AlignmentI alignment = af.getViewport().getAlignment();
614 for (int i = 0; i < alignment.getHeight(); i++)
616 SequenceI seq = alignment.getSequenceAt(i);
617 for (String group : seq.getFeatures().getFeatureGroups(true))
619 if (group != null && !allGroups.contains(group))
621 allGroups.add(group);
622 checkGroupState(group);
633 * Synchronise gui group list and check visibility of group
636 * @return true if group is visible
638 private boolean checkGroupState(String group)
640 boolean visible = fr.checkGroupVisibility(group, true);
642 for (int g = 0; g < groupPanel.getComponentCount(); g++)
644 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
646 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
651 final String grp = group;
652 final JCheckBox check = new JCheckBox(group, visible);
653 check.setFont(new Font("Serif", Font.BOLD, 12));
654 check.setToolTipText(group);
655 check.addItemListener(new ItemListener()
658 public void itemStateChanged(ItemEvent evt)
660 fr.setGroupVisibility(check.getText(), check.isSelected());
661 resetTable(new String[] { grp });
665 groupPanel.add(check);
669 synchronized void resetTable(String[] groupChanged)
675 resettingTable = true;
676 typeWidth = new Hashtable<>();
677 // TODO: change avWidth calculation to 'per-sequence' average and use long
680 Set<String> displayableTypes = new HashSet<>();
681 Set<String> foundGroups = new HashSet<>();
684 * determine which feature types may be visible depending on
685 * which groups are selected, and recompute average width data
687 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
690 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
693 * get the sequence's groups for positional features
694 * and keep track of which groups are visible
696 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
697 Set<String> visibleGroups = new HashSet<>();
698 for (String group : groups)
700 if (group == null || checkGroupState(group))
702 visibleGroups.add(group);
705 foundGroups.addAll(groups);
708 * get distinct feature types for visible groups
709 * record distinct visible types, and their count and total length
711 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
712 visibleGroups.toArray(new String[visibleGroups.size()]));
713 for (String type : types)
715 displayableTypes.add(type);
716 float[] avWidth = typeWidth.get(type);
719 avWidth = new float[2];
720 typeWidth.put(type, avWidth);
722 // todo this could include features with a non-visible group
723 // - do we greatly care?
724 // todo should we include non-displayable features here, and only
725 // update when features are added?
726 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
727 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
731 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
734 if (fr.hasRenderOrder())
738 fr.findAllFeatures(groupChanged != null); // prod to update
739 // colourschemes. but don't
741 // First add the checks in the previous render order,
742 // in case the window has been closed and reopened
744 List<String> frl = fr.getRenderOrder();
745 for (int ro = frl.size() - 1; ro > -1; ro--)
747 String type = frl.get(ro);
749 if (!displayableTypes.contains(type))
754 data[dataIndex][TYPE_COLUMN] = type;
755 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
756 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
757 data[dataIndex][FILTER_COLUMN] = featureFilter == null
758 ? new FeatureMatcherSet()
760 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
761 af.getViewport().getFeaturesDisplayed().isVisible(type));
763 displayableTypes.remove(type);
768 * process any extra features belonging only to
769 * a group which was just selected
771 while (!displayableTypes.isEmpty())
773 String type = displayableTypes.iterator().next();
774 data[dataIndex][TYPE_COLUMN] = type;
776 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
777 if (data[dataIndex][COLOUR_COLUMN] == null)
779 // "Colour has been updated in another view!!"
780 fr.clearRenderOrder();
783 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
784 data[dataIndex][FILTER_COLUMN] = featureFilter == null
785 ? new FeatureMatcherSet()
787 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
789 displayableTypes.remove(type);
792 if (originalData == null)
794 originalData = new Object[data.length][COLUMN_COUNT];
795 for (int i = 0; i < data.length; i++)
797 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
802 updateOriginalData(data);
805 table.setModel(new FeatureTableModel(data));
806 table.getColumnModel().getColumn(0).setPreferredWidth(200);
808 groupPanel.setLayout(
809 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
810 pruneGroups(foundGroups);
811 groupPanel.validate();
813 updateFeatureRenderer(data, groupChanged != null);
814 resettingTable = false;
818 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
819 * have been made outwith this dialog
821 * <li>a new feature type added (and made visible)</li>
822 * <li>a feature colour changed (in the Amend Features dialog)</li>
827 protected void updateOriginalData(Object[][] foundData)
829 // todo LinkedHashMap instead of Object[][] would be nice
831 Object[][] currentData = ((FeatureTableModel) table.getModel())
833 for (Object[] row : foundData)
835 String type = (String) row[TYPE_COLUMN];
836 boolean found = false;
837 for (Object[] current : currentData)
839 if (type.equals(current[TYPE_COLUMN]))
843 * currently dependent on object equality here;
844 * really need an equals method on FeatureColour
846 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
849 * feature colour has changed externally - update originalData
851 for (Object[] original : originalData)
853 if (type.equals(original[TYPE_COLUMN]))
855 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
866 * new feature detected - add to original data (on top)
868 Object[][] newData = new Object[originalData.length
870 for (int i = 0; i < originalData.length; i++)
872 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
876 originalData = newData;
882 * Remove from the groups panel any checkboxes for groups that are not in the
883 * foundGroups set. This enables removing a group from the display when the last
884 * feature in that group is deleted.
888 protected void pruneGroups(Set<String> foundGroups)
890 for (int g = 0; g < groupPanel.getComponentCount(); g++)
892 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
893 if (!foundGroups.contains(checkbox.getText()))
895 groupPanel.remove(checkbox);
901 * reorder data based on the featureRenderers global priority list.
905 private void ensureOrder(Object[][] data)
907 boolean sort = false;
908 float[] order = new float[data.length];
909 for (int i = 0; i < order.length; i++)
911 order[i] = fr.getOrder(data[i][0].toString());
914 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
918 sort = sort || order[i - 1] > order[i];
923 jalview.util.QuickSort.sort(order, data);
928 * Offers a file chooser dialog, and then loads the feature colours and
929 * filters from file in XML format and unmarshals to Jalview feature settings
933 JalviewFileChooser chooser = new JalviewFileChooser("fc",
934 SEQUENCE_FEATURE_COLOURS);
935 chooser.setFileView(new JalviewFileView());
936 chooser.setDialogTitle(
937 MessageManager.getString("label.load_feature_colours"));
938 chooser.setToolTipText(MessageManager.getString("action.load"));
940 int value = chooser.showOpenDialog(this);
942 if (value == JalviewFileChooser.APPROVE_OPTION)
944 File file = chooser.getSelectedFile();
950 * Loads feature colours and filters from XML stored in the given file
958 InputStreamReader in = new InputStreamReader(
959 new FileInputStream(file), "UTF-8");
961 JAXBContext jc = JAXBContext
962 .newInstance("jalview.xml.binding.jalview");
963 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
964 XMLStreamReader streamReader = XMLInputFactory.newInstance()
965 .createXMLStreamReader(in);
966 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
967 JalviewUserColours.class);
968 JalviewUserColours jucs = jbe.getValue();
970 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
973 * load feature colours
975 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
977 Colour newcol = jucs.getColour().get(i);
978 FeatureColourI colour = jalview.project.Jalview2XML
979 .parseColour(newcol);
980 fr.setColour(newcol.getName(), colour);
981 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
985 * load feature filters; loaded filters will replace any that are
986 * currently defined, other defined filters are left unchanged
988 for (int i = 0; i < jucs.getFilter().size(); i++)
990 Filter filterModel = jucs.getFilter().get(i);
991 String featureType = filterModel.getFeatureType();
992 FeatureMatcherSetI filter = jalview.project.Jalview2XML
993 .parseFilter(featureType, filterModel.getMatcherSet());
994 if (!filter.isEmpty())
996 fr.setFeatureFilter(featureType, filter);
1001 * update feature settings table
1006 Object[][] data = ((FeatureTableModel) table.getModel())
1009 updateFeatureRenderer(data, false);
1012 } catch (Exception ex)
1014 System.out.println("Error loading User Colour File\n" + ex);
1019 * Offers a file chooser dialog, and then saves the current feature colours
1020 * and any filters to the selected file in XML format
1024 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1025 SEQUENCE_FEATURE_COLOURS);
1026 chooser.setFileView(new JalviewFileView());
1027 chooser.setDialogTitle(
1028 MessageManager.getString("label.save_feature_colours"));
1029 chooser.setToolTipText(MessageManager.getString("action.save"));
1031 int value = chooser.showSaveDialog(this);
1033 if (value == JalviewFileChooser.APPROVE_OPTION)
1035 save(chooser.getSelectedFile());
1040 * Saves feature colours and filters to the given file
1044 void save(File file)
1046 JalviewUserColours ucs = new JalviewUserColours();
1047 ucs.setSchemeName("Sequence Features");
1050 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1051 new FileOutputStream(file), "UTF-8"));
1054 * sort feature types by colour order, from 0 (highest)
1057 Set<String> fr_colours = fr.getAllFeatureColours();
1058 String[] sortedTypes = fr_colours
1059 .toArray(new String[fr_colours.size()]);
1060 Arrays.sort(sortedTypes, new Comparator<String>()
1063 public int compare(String type1, String type2)
1065 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1070 * save feature colours
1072 for (String featureType : sortedTypes)
1074 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1075 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1077 ucs.getColour().add(col);
1081 * save any feature filters
1083 for (String featureType : sortedTypes)
1085 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1086 if (filter != null && !filter.isEmpty())
1088 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1089 FeatureMatcherI firstMatcher = iterator.next();
1090 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1091 .marshalFilter(firstMatcher, iterator,
1093 Filter filterModel = new Filter();
1094 filterModel.setFeatureType(featureType);
1095 filterModel.setMatcherSet(ms);
1096 ucs.getFilter().add(filterModel);
1099 JAXBContext jaxbContext = JAXBContext
1100 .newInstance(JalviewUserColours.class);
1101 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1102 jaxbMarshaller.marshal(
1103 new ObjectFactory().createJalviewUserColours(ucs), out);
1105 // jaxbMarshaller.marshal(object, pout);
1106 // marshaller.marshal(object);
1109 // ucs.marshal(out);
1111 } catch (Exception ex)
1113 ex.printStackTrace();
1117 public void invertSelection()
1119 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1120 for (int i = 0; i < data.length; i++)
1122 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1124 updateFeatureRenderer(data, true);
1128 public void orderByAvWidth()
1130 if (table == null || table.getModel() == null)
1134 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1135 float[] width = new float[data.length];
1139 for (int i = 0; i < data.length; i++)
1141 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1144 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1145 // weight - but have to make per
1146 // sequence, too (awidth[2])
1147 // if (width[i]==1) // hack to distinguish single width sequences.
1158 boolean sort = false;
1159 for (int i = 0; i < width.length; i++)
1161 // awidth = (float[]) typeWidth.get(data[i][0]);
1164 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1167 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1173 width[i] /= max; // normalize
1174 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1178 sort = sort || width[i - 1] > width[i];
1183 jalview.util.QuickSort.sort(width, data);
1184 // update global priority order
1187 updateFeatureRenderer(data, false);
1192 * close ourselves but leave any existing UI handlers (e.g a CDS/Protein tabbed
1193 * feature settings dialog) intact
1195 public void closeOldSettings()
1201 * close the feature settings dialog (and any containing frame)
1208 private void closeDialog(boolean closeContainingFrame)
1214 af.setFeatureSettingsGeometry(frame.getBounds());
1215 frame.setClosed(true);
1219 SplitContainerI sc = af.getSplitViewContainer();
1220 sc.closeFeatureSettings(this, closeContainingFrame);
1221 af.featureSettings = null;
1223 } catch (Exception exe)
1229 public void updateFeatureRenderer(Object[][] data)
1231 updateFeatureRenderer(data, true);
1235 * Update the priority order of features; only repaint if this changed the order
1236 * of visible features
1241 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1243 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1245 if (fr.setFeaturePriority(rowData, visibleNew))
1252 * Converts table data into an array of data beans
1254 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1256 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1257 for (int i = 0; i < data.length; i++)
1259 String type = (String) data[i][TYPE_COLUMN];
1260 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1261 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1262 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1263 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1269 private void jbInit() throws Exception
1271 this.setLayout(new BorderLayout());
1273 final boolean hasComplement = af.getViewport()
1274 .getCodingComplement() != null;
1276 JPanel settingsPane = new JPanel();
1277 settingsPane.setLayout(new BorderLayout());
1279 JPanel bigPanel = new JPanel();
1280 bigPanel.setLayout(new BorderLayout());
1282 groupPanel = new JPanel();
1283 bigPanel.add(groupPanel, BorderLayout.NORTH);
1285 JButton invert = new JButton(
1286 MessageManager.getString("label.invert_selection"));
1287 invert.setFont(JvSwingUtils.getLabelFont());
1288 invert.addActionListener(new ActionListener()
1291 public void actionPerformed(ActionEvent e)
1297 JButton optimizeOrder = new JButton(
1298 MessageManager.getString("label.optimise_order"));
1299 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1300 optimizeOrder.addActionListener(new ActionListener()
1303 public void actionPerformed(ActionEvent e)
1309 final String byScoreLabel = MessageManager.getString("label.seq_sort_by_score");
1310 JButton sortByScore = new JButton(byScoreLabel);
1311 sortByScore.setFont(JvSwingUtils.getLabelFont());
1312 sortByScore.addActionListener(new ActionListener()
1315 public void actionPerformed(ActionEvent e)
1317 if (canSortBy(byScoreLabel))
1323 final String byDensityLabel = MessageManager.getString("label.sequence_sort_by_density");
1324 JButton sortByDens = new JButton(byDensityLabel);
1325 sortByDens.setFont(JvSwingUtils.getLabelFont());
1326 sortByDens.addActionListener(new ActionListener()
1329 public void actionPerformed(ActionEvent e)
1331 if (canSortBy(byDensityLabel))
1333 sortByDensity(null);
1338 JButton help = new JButton(MessageManager.getString("action.help"));
1339 help.setFont(JvSwingUtils.getLabelFont());
1340 help.addActionListener(new ActionListener()
1343 public void actionPerformed(ActionEvent e)
1347 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1348 } catch (HelpSetException e1)
1350 e1.printStackTrace();
1354 // Cancel for a SplitFrame should just revert changes to the currently displayed
1355 // settings. May want to do this for either or both - so need a splitview
1356 // feature settings cancel/OK.
1357 JButton cancel = new JButton(MessageManager
1358 .getString(hasComplement ? "action.revert" : "action.cancel"));
1359 cancel.setToolTipText(MessageManager.getString(hasComplement
1360 ? "action.undo_changes_to_feature_settings"
1361 : "action.undo_changes_to_feature_settings_and_close_the_dialog"));
1362 cancel.setFont(JvSwingUtils.getLabelFont());
1363 // TODO: disable cancel (and apply!) until current settings are different
1364 cancel.addActionListener(new ActionListener()
1367 public void actionPerformed(ActionEvent e)
1377 // Cancel for the whole dialog should cancel both CDS and Protein.
1378 // OK for an individual feature settings just applies changes, but dialog
1380 JButton ok = new JButton(MessageManager
1381 .getString(hasComplement ? "action.apply" : "action.ok"));
1382 ok.setFont(JvSwingUtils.getLabelFont());
1383 ok.addActionListener(new ActionListener()
1386 public void actionPerformed(ActionEvent e)
1394 storeOriginalSettings();
1399 JButton loadColours = new JButton(
1400 MessageManager.getString("label.load_colours"));
1401 loadColours.setFont(JvSwingUtils.getLabelFont());
1402 loadColours.setToolTipText(
1403 MessageManager.getString("label.load_colours_tooltip"));
1404 loadColours.addActionListener(new ActionListener()
1407 public void actionPerformed(ActionEvent e)
1413 JButton saveColours = new JButton(
1414 MessageManager.getString("label.save_colours"));
1415 saveColours.setFont(JvSwingUtils.getLabelFont());
1416 saveColours.setToolTipText(
1417 MessageManager.getString("label.save_colours_tooltip"));
1418 saveColours.addActionListener(new ActionListener()
1421 public void actionPerformed(ActionEvent e)
1426 transparency.addChangeListener(new ChangeListener()
1429 public void stateChanged(ChangeEvent evt)
1431 if (!inConstruction)
1433 fr.setTransparency((100 - transparency.getValue()) / 100f);
1439 transparency.setMaximum(70);
1440 transparency.setToolTipText(
1441 MessageManager.getString("label.transparency_tip"));
1443 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1444 String text = MessageManager.formatMessage("label.show_linked_features",
1446 ? MessageManager.getString("label.protein")
1449 showComplement = new JCheckBox(text);
1450 showComplement.addActionListener(new ActionListener()
1453 public void actionPerformed(ActionEvent e)
1456 .setShowComplementFeatures(showComplement.isSelected());
1461 showComplementOnTop = new JCheckBox(
1462 MessageManager.getString("label.on_top"));
1463 showComplementOnTop.addActionListener(new ActionListener()
1466 public void actionPerformed(ActionEvent e)
1468 af.getViewport().setShowComplementFeaturesOnTop(
1469 showComplementOnTop.isSelected());
1474 updateComplementButtons();
1476 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1477 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1479 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1480 transbuttons.add(optimizeOrder);
1481 transbuttons.add(invert);
1482 transbuttons.add(sortByScore);
1483 transbuttons.add(sortByDens);
1484 transbuttons.add(help);
1486 JPanel transPanelLeft = new JPanel(
1487 new GridLayout(hasComplement ? 4 : 2, 1));
1488 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1489 transPanelLeft.add(transparency);
1492 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1493 cp.add(showComplement);
1494 cp.add(showComplementOnTop);
1495 transPanelLeft.add(cp);
1497 lowerPanel.add(transPanelLeft);
1498 lowerPanel.add(transbuttons);
1500 JPanel buttonPanel = new JPanel();
1501 buttonPanel.add(ok);
1502 buttonPanel.add(cancel);
1503 buttonPanel.add(loadColours);
1504 buttonPanel.add(saveColours);
1505 bigPanel.add(scrollPane, BorderLayout.CENTER);
1506 settingsPane.add(bigPanel, BorderLayout.CENTER);
1507 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1508 this.add(settingsPane);
1512 * Repaints alignment, structure and overview (if shown). If there is a
1513 * complementary view which is showing this view's features, then also
1516 void refreshDisplay()
1518 af.alignPanel.paintAlignment(true, true);
1519 AlignViewportI complement = af.getViewport().getCodingComplement();
1520 if (complement != null && complement.isShowComplementFeatures())
1522 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1523 af2.alignPanel.paintAlignment(true, true);
1528 * Answers a suitable tooltip to show on the colour cell of the table
1532 * if true include 'click to edit' and similar text
1535 public static String getColorTooltip(FeatureColourI fcol,
1542 if (fcol.isSimpleColour())
1544 return withHint ? BASE_TOOLTIP : null;
1546 String description = fcol.getDescription();
1547 description = description.replaceAll("<", "<");
1548 description = description.replaceAll(">", ">");
1549 StringBuilder tt = new StringBuilder(description);
1552 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1554 return JvSwingUtils.wrapTooltip(true, tt.toString());
1557 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1560 boolean thr = false;
1561 StringBuilder tx = new StringBuilder();
1563 if (gcol.isColourByAttribute())
1565 tx.append(FeatureMatcher
1566 .toAttributeDisplayName(gcol.getAttributeName()));
1568 else if (!gcol.isColourByLabel())
1570 tx.append(MessageManager.getString("label.score"));
1573 if (gcol.isAboveThreshold())
1578 if (gcol.isBelowThreshold())
1583 if (gcol.isColourByLabel())
1589 if (!gcol.isColourByAttribute())
1597 Color newColor = gcol.getMaxColour();
1598 comp.setBackground(newColor);
1599 // System.err.println("Width is " + w / 2);
1600 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1601 comp.setIcon(ficon);
1602 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1603 // + newColor.getGreen() + ", " + newColor.getBlue()
1604 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1605 // + ", " + minCol.getBlue() + ")");
1607 comp.setHorizontalAlignment(SwingConstants.CENTER);
1608 comp.setText(tx.toString());
1611 // ///////////////////////////////////////////////////////////////////////
1612 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1613 // ///////////////////////////////////////////////////////////////////////
1614 class FeatureTableModel extends AbstractTableModel
1616 private String[] columnNames = {
1617 MessageManager.getString("label.feature_type"),
1618 MessageManager.getString("action.colour"),
1619 MessageManager.getString("label.configuration"),
1620 MessageManager.getString("label.show") };
1622 private Object[][] data;
1624 FeatureTableModel(Object[][] data)
1629 public Object[][] getData()
1634 public void setData(Object[][] data)
1640 public int getColumnCount()
1642 return columnNames.length;
1645 public Object[] getRow(int row)
1651 public int getRowCount()
1657 public String getColumnName(int col)
1659 return columnNames[col];
1663 public Object getValueAt(int row, int col)
1665 return data[row][col];
1669 * Answers the class of column c of the table
1672 public Class<?> getColumnClass(int c)
1677 return String.class;
1679 return FeatureColour.class;
1681 return FeatureMatcherSet.class;
1683 return Boolean.class;
1688 public boolean isCellEditable(int row, int col)
1690 return col == 0 ? false : true;
1694 public void setValueAt(Object value, int row, int col)
1696 data[row][col] = value;
1697 fireTableCellUpdated(row, col);
1698 updateFeatureRenderer(data);
1703 class ColorRenderer extends JLabel implements TableCellRenderer
1705 Border unselectedBorder = null;
1707 Border selectedBorder = null;
1709 public ColorRenderer()
1711 setOpaque(true); // MUST do this for background to show up.
1712 setHorizontalTextPosition(SwingConstants.CENTER);
1713 setVerticalTextPosition(SwingConstants.CENTER);
1717 public Component getTableCellRendererComponent(JTable tbl, Object color,
1718 boolean isSelected, boolean hasFocus, int row, int column)
1720 FeatureColourI cellColour = (FeatureColourI) color;
1722 setBackground(tbl.getBackground());
1723 if (!cellColour.isSimpleColour())
1725 Rectangle cr = tbl.getCellRect(row, column, false);
1726 FeatureSettings.renderGraduatedColor(this, cellColour,
1727 (int) cr.getWidth(), (int) cr.getHeight());
1733 setBackground(cellColour.getColour());
1737 if (selectedBorder == null)
1739 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1740 tbl.getSelectionBackground());
1742 setBorder(selectedBorder);
1746 if (unselectedBorder == null)
1748 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1749 tbl.getBackground());
1751 setBorder(unselectedBorder);
1758 class FilterRenderer extends JLabel implements TableCellRenderer
1760 javax.swing.border.Border unselectedBorder = null;
1762 javax.swing.border.Border selectedBorder = null;
1764 public FilterRenderer()
1766 setOpaque(true); // MUST do this for background to show up.
1767 setHorizontalTextPosition(SwingConstants.CENTER);
1768 setVerticalTextPosition(SwingConstants.CENTER);
1772 public Component getTableCellRendererComponent(JTable tbl,
1773 Object filter, boolean isSelected, boolean hasFocus, int row,
1776 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1778 String asText = theFilter.toString();
1779 setBackground(tbl.getBackground());
1780 this.setText(asText);
1785 if (selectedBorder == null)
1787 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1788 tbl.getSelectionBackground());
1790 setBorder(selectedBorder);
1794 if (unselectedBorder == null)
1796 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1797 tbl.getBackground());
1799 setBorder(unselectedBorder);
1807 * update comp using rendering settings from gcol
1812 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1814 int w = comp.getWidth(), h = comp.getHeight();
1817 w = (int) comp.getPreferredSize().getWidth();
1818 h = (int) comp.getPreferredSize().getHeight();
1825 renderGraduatedColor(comp, gcol, w, h);
1828 class ColorEditor extends AbstractCellEditor
1829 implements TableCellEditor, ActionListener
1833 FeatureColourI currentColor;
1835 FeatureTypeSettings chooser;
1841 JColorChooser colorChooser;
1845 protected static final String EDIT = "edit";
1847 int rowSelected = 0;
1849 public ColorEditor(FeatureSettings me)
1852 // Set up the editor (from the table's point of view),
1853 // which is a button.
1854 // This button brings up the color chooser dialog,
1855 // which is the editor from the user's point of view.
1856 button = new JButton();
1857 button.setActionCommand(EDIT);
1858 button.addActionListener(this);
1859 button.setBorderPainted(false);
1860 // Set up the dialog that the button brings up.
1861 colorChooser = new JColorChooser();
1862 dialog = JColorChooser.createDialog(button,
1863 MessageManager.getString("label.select_colour"), true, // modal
1864 colorChooser, this, // OK button handler
1865 null); // no CANCEL button handler
1869 * Handles events from the editor button and from the dialog's OK button.
1872 public void actionPerformed(ActionEvent e)
1874 // todo test e.getSource() instead here
1875 if (EDIT.equals(e.getActionCommand()))
1877 // The user has clicked the cell, so
1878 // bring up the dialog.
1879 if (currentColor.isSimpleColour())
1881 // bring up simple color chooser
1882 button.setBackground(currentColor.getColour());
1883 colorChooser.setColor(currentColor.getColour());
1884 dialog.setVisible(true);
1888 // bring up graduated chooser.
1889 chooser = new FeatureTypeSettings(me.fr, type);
1894 chooser.setRequestFocusEnabled(true);
1895 chooser.requestFocus();
1897 chooser.addActionListener(this);
1898 // Make the renderer reappear.
1899 fireEditingStopped();
1904 if (currentColor.isSimpleColour())
1907 * read off colour picked in colour chooser after OK pressed
1909 currentColor = new FeatureColour(colorChooser.getColor());
1910 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1915 * after OK in variable colour dialog, any changes to colour
1916 * (or filters!) are already set in FeatureRenderer, so just
1917 * update table data without triggering updateFeatureRenderer
1919 currentColor = fr.getFeatureColours().get(type);
1920 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1921 if (currentFilter == null)
1923 currentFilter = new FeatureMatcherSet();
1925 Object[] data = ((FeatureTableModel) table.getModel())
1926 .getData()[rowSelected];
1927 data[COLOUR_COLUMN] = currentColor;
1928 data[FILTER_COLUMN] = currentFilter;
1930 fireEditingStopped();
1931 me.table.validate();
1935 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1937 public Object getCellEditorValue()
1939 return currentColor;
1942 // Implement the one method defined by TableCellEditor.
1944 public Component getTableCellEditorComponent(JTable theTable, Object value,
1945 boolean isSelected, int row, int column)
1947 currentColor = (FeatureColourI) value;
1948 this.rowSelected = row;
1949 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1950 button.setOpaque(true);
1951 button.setBackground(me.getBackground());
1952 if (!currentColor.isSimpleColour())
1954 JLabel btn = new JLabel();
1955 btn.setSize(button.getSize());
1956 FeatureSettings.renderGraduatedColor(btn, currentColor);
1957 button.setBackground(btn.getBackground());
1958 button.setIcon(btn.getIcon());
1959 button.setText(btn.getText());
1964 button.setIcon(null);
1965 button.setBackground(currentColor.getColour());
1972 * The cell editor for the Filter column. It displays the text of any filters
1973 * for the feature type in that row (in full as a tooltip, possible abbreviated
1974 * as display text). On click in the cell, opens the Feature Display Settings
1975 * dialog at the Filters tab.
1977 class FilterEditor extends AbstractCellEditor
1978 implements TableCellEditor, ActionListener
1982 FeatureMatcherSetI currentFilter;
1990 protected static final String EDIT = "edit";
1992 int rowSelected = 0;
1994 public FilterEditor(FeatureSettings me)
1997 button = new JButton();
1998 button.setActionCommand(EDIT);
1999 button.addActionListener(this);
2000 button.setBorderPainted(false);
2004 * Handles events from the editor button
2007 public void actionPerformed(ActionEvent e)
2009 if (button == e.getSource())
2011 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2012 chooser.addActionListener(this);
2013 chooser.setRequestFocusEnabled(true);
2014 chooser.requestFocus();
2015 if (lastLocation != null)
2017 // todo open at its last position on screen
2018 chooser.setBounds(lastLocation.x, lastLocation.y,
2019 chooser.getWidth(), chooser.getHeight());
2022 fireEditingStopped();
2024 else if (e.getSource() instanceof Component)
2028 * after OK in variable colour dialog, any changes to filter
2029 * (or colours!) are already set in FeatureRenderer, so just
2030 * update table data without triggering updateFeatureRenderer
2032 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2033 currentFilter = me.fr.getFeatureFilter(type);
2034 if (currentFilter == null)
2036 currentFilter = new FeatureMatcherSet();
2038 Object[] data = ((FeatureTableModel) table.getModel())
2039 .getData()[rowSelected];
2040 data[COLOUR_COLUMN] = currentColor;
2041 data[FILTER_COLUMN] = currentFilter;
2042 fireEditingStopped();
2043 me.table.validate();
2048 public Object getCellEditorValue()
2050 return currentFilter;
2054 public Component getTableCellEditorComponent(JTable theTable, Object value,
2055 boolean isSelected, int row, int column)
2057 currentFilter = (FeatureMatcherSetI) value;
2058 this.rowSelected = row;
2059 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2060 button.setOpaque(true);
2061 button.setBackground(me.getBackground());
2062 button.setText(currentFilter.toString());
2063 button.setIcon(null);
2068 public boolean isOpen()
2070 if (af.getSplitViewContainer() != null)
2072 return af.getSplitViewContainer().isFeatureSettingsOpen();
2074 return frame != null && !frame.isClosed();
2078 public void revert()
2080 fr.setTransparency(originalTransparency);
2081 fr.setFeatureFilters(originalFilters);
2082 updateFeatureRenderer(originalData);
2083 af.getViewport().setViewStyle(originalViewStyle);
2084 updateTransparencySliderFromFR();
2085 updateComplementButtons();
2090 class FeatureIcon implements Icon
2092 FeatureColourI gcol;
2096 boolean midspace = false;
2098 int width = 50, height = 20;
2100 int s1, e1; // start and end of midpoint band for thresholded symbol
2102 Color mpcolour = Color.white;
2104 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2124 public int getIconWidth()
2130 public int getIconHeight()
2136 public void paintIcon(Component c, Graphics g, int x, int y)
2139 if (gcol.isColourByLabel())
2142 g.fillRect(0, 0, width, height);
2143 // need an icon here.
2144 g.setColor(gcol.getMaxColour());
2146 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2148 // g.setFont(g.getFont().deriveFont(
2149 // AffineTransform.getScaleInstance(
2150 // width/g.getFontMetrics().stringWidth("Label"),
2151 // height/g.getFontMetrics().getHeight())));
2153 g.drawString(MessageManager.getString("label.label"), 0, 0);
2158 Color minCol = gcol.getMinColour();
2160 g.fillRect(0, 0, s1, height);
2163 g.setColor(Color.white);
2164 g.fillRect(s1, 0, e1 - s1, height);
2166 g.setColor(gcol.getMaxColour());
2167 g.fillRect(0, e1, width - e1, height);