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.AbstractButton;
86 import javax.swing.AbstractCellEditor;
87 import javax.swing.BorderFactory;
88 import javax.swing.Icon;
89 import javax.swing.JButton;
90 import javax.swing.JCheckBox;
91 import javax.swing.JColorChooser;
92 import javax.swing.JDialog;
93 import javax.swing.JInternalFrame;
94 import javax.swing.JLabel;
95 import javax.swing.JLayeredPane;
96 import javax.swing.JMenuItem;
97 import javax.swing.JPanel;
98 import javax.swing.JPopupMenu;
99 import javax.swing.JScrollPane;
100 import javax.swing.JSlider;
101 import javax.swing.JTable;
102 import javax.swing.ListSelectionModel;
103 import javax.swing.SwingConstants;
104 import javax.swing.border.Border;
105 import javax.swing.event.ChangeEvent;
106 import javax.swing.event.ChangeListener;
107 import javax.swing.table.AbstractTableModel;
108 import javax.swing.table.JTableHeader;
109 import javax.swing.table.TableCellEditor;
110 import javax.swing.table.TableCellRenderer;
111 import javax.swing.table.TableColumn;
112 import javax.xml.bind.JAXBContext;
113 import javax.xml.bind.JAXBElement;
114 import javax.xml.bind.Marshaller;
115 import javax.xml.stream.XMLInputFactory;
116 import javax.xml.stream.XMLStreamReader;
118 public class FeatureSettings extends JPanel
119 implements FeatureSettingsControllerI, FeatureSettingsControllerGuiI
121 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
122 .getString("label.sequence_feature_colours");
125 * column indices of fields in Feature Settings table
127 static final int TYPE_COLUMN = 0;
129 static final int COLOUR_COLUMN = 1;
131 static final int FILTER_COLUMN = 2;
133 static final int SHOW_COLUMN = 3;
135 private static final int COLUMN_COUNT = 4;
137 private static final int MIN_WIDTH = 400;
139 private static final int MIN_HEIGHT = 400;
141 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
143 final FeatureRenderer fr;
145 public final AlignFrame af;
148 * 'original' fields hold settings to restore on Cancel
150 Object[][] originalData;
152 private float originalTransparency;
154 private ViewStyleI originalViewStyle;
156 private Map<String, FeatureMatcherSetI> originalFilters;
158 final JInternalFrame frame;
160 JScrollPane scrollPane = new JScrollPane();
166 JSlider transparency = new JSlider();
169 * when true, constructor is still executing - so ignore UI events
171 protected volatile boolean inConstruction = true;
173 int selectedRow = -1;
175 boolean resettingTable = false;
178 * true when Feature Settings are updating from feature renderer
180 private boolean handlingUpdate = false;
183 * holds {featureCount, totalExtent} for each feature type
185 Map<String, float[]> typeWidth = null;
187 private void storeOriginalSettings()
189 // save transparency for restore on Cancel
190 originalTransparency = fr.getTransparency();
191 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
192 transparency.setMaximum(100 - originalTransparencyAsPercent);
194 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
195 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
202 public FeatureSettings(AlignFrame alignFrame)
204 this.af = alignFrame;
205 fr = af.getFeatureRenderer();
206 storeOriginalSettings();
210 } catch (Exception ex)
212 ex.printStackTrace();
218 public String getToolTipText(MouseEvent e)
221 int column = table.columnAtPoint(e.getPoint());
222 int row = table.rowAtPoint(e.getPoint());
227 tip = JvSwingUtils.wrapTooltip(true, MessageManager
228 .getString("label.feature_settings_click_drag"));
231 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
233 tip = getColorTooltip(colour, true);
236 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
240 .getString("label.configure_feature_tooltip")
251 * Position the tooltip near the bottom edge of, and half way across, the
255 public Point getToolTipLocation(MouseEvent e)
257 Point point = e.getPoint();
258 int column = table.columnAtPoint(point);
259 int row = table.rowAtPoint(point);
260 Rectangle r = getCellRect(row, column, false);
261 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
265 JTableHeader tableHeader = table.getTableHeader();
266 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
267 tableHeader.setReorderingAllowed(false);
268 table.setFont(new Font("Verdana", Font.PLAIN, 12));
270 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
271 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
273 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
274 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
276 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
277 new ColorRenderer(), new ColorEditor(this));
278 table.addColumn(colourColumn);
280 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
281 new FilterRenderer(), new FilterEditor(this));
282 table.addColumn(filterColumn);
284 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
286 table.addMouseListener(new MouseAdapter()
289 public void mousePressed(MouseEvent evt)
291 Point pt = evt.getPoint();
292 selectedRow = table.rowAtPoint(pt);
293 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
294 if (evt.isPopupTrigger())
296 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
297 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
300 else if (evt.getClickCount() == 2
301 && table.columnAtPoint(pt) == TYPE_COLUMN)
303 boolean invertSelection = evt.isAltDown();
304 boolean toggleSelection = Platform.isControlDown(evt);
305 boolean extendSelection = evt.isShiftDown();
306 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
307 invertSelection, extendSelection, toggleSelection, type);
308 fr.ap.av.sendSelection();
312 // isPopupTrigger fires on mouseReleased on Windows
314 public void mouseReleased(MouseEvent evt)
316 selectedRow = table.rowAtPoint(evt.getPoint());
317 if (evt.isPopupTrigger())
319 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
320 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
321 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
327 table.addMouseMotionListener(new MouseMotionAdapter()
330 public void mouseDragged(MouseEvent evt)
332 int newRow = table.rowAtPoint(evt.getPoint());
333 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
336 * reposition 'selectedRow' to 'newRow' (the dragged to location)
337 * this could be more than one row away for a very fast drag action
338 * so just swap it with adjacent rows until we get it there
340 Object[][] data = ((FeatureTableModel) table.getModel())
342 int direction = newRow < selectedRow ? -1 : 1;
343 for (int i = selectedRow; i != newRow; i += direction)
345 Object[] temp = data[i];
346 data[i] = data[i + direction];
347 data[i + direction] = temp;
349 updateFeatureRenderer(data);
351 selectedRow = newRow;
355 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
356 // MessageManager.getString("label.feature_settings_click_drag")));
357 scrollPane.setViewportView(table);
359 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
361 fr.findAllFeatures(true); // display everything!
364 discoverAllFeatureData();
365 final FeatureSettings fs = this;
366 fr.addPropertyChangeListener(change = new PropertyChangeListener()
369 public void propertyChange(PropertyChangeEvent evt)
371 if (!fs.resettingTable && !fs.handlingUpdate)
373 fs.handlingUpdate = true;
375 // new groups may be added with new sequence feature types only
376 fs.handlingUpdate = false;
381 SplitContainerI splitframe = af.getSplitViewContainer();
382 if (splitframe != null)
384 frame = null; // keeps eclipse happy
385 splitframe.addFeatureSettingsUI(this);
389 frame = new JInternalFrame();
390 frame.setContentPane(this);
391 Rectangle bounds = af.getFeatureSettingsGeometry();
393 if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels(
394 af.alignPanel.av.getSequenceSetId()).length > 1)
396 title = MessageManager.formatMessage(
397 "label.sequence_feature_settings_for_view",
398 af.alignPanel.getViewName());
402 title = MessageManager.getString("label.sequence_feature_settings");
406 if (Platform.isAMac())
408 Desktop.addInternalFrame(frame, title, 600, 480);
412 Desktop.addInternalFrame(frame, title, 600, 450);
417 Desktop.addInternalFrame(frame, title,
418 false, bounds.width, bounds.height);
419 frame.setBounds(bounds);
420 frame.setVisible(true);
422 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
424 frame.addInternalFrameListener(
425 new javax.swing.event.InternalFrameAdapter()
428 public void internalFrameClosed(
429 javax.swing.event.InternalFrameEvent evt)
431 featureSettings_isClosed();
434 frame.setLayer(JLayeredPane.PALETTE_LAYER);
436 inConstruction = false;
439 PropertyChangeListener change;
441 private JCheckBox showComplementOnTop;
443 private AbstractButton showComplement;
445 private void updateComplementButtons()
447 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
449 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
453 public AlignViewControllerGuiI getAlignframe()
459 public void featureSettings_isClosed()
461 fr.removePropertyChangeListener(change);
465 protected void popupSort(final int rowSelected, final String type,
466 final Object typeCol, final Map<String, float[][]> minmax, int x,
469 JPopupMenu men = new JPopupMenu(MessageManager
470 .formatMessage("label.settings_for_param", new String[]
472 JMenuItem scr = new JMenuItem(
473 MessageManager.getString("label.sort_by_score"));
475 final FeatureSettings me = this;
476 scr.addActionListener(new ActionListener()
480 public void actionPerformed(ActionEvent e)
483 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
488 JMenuItem dens = new JMenuItem(
489 MessageManager.getString("label.sort_by_density"));
490 dens.addActionListener(new ActionListener()
494 public void actionPerformed(ActionEvent e)
497 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
504 JMenuItem selCols = new JMenuItem(
505 MessageManager.getString("label.select_columns_containing"));
506 selCols.addActionListener(new ActionListener()
509 public void actionPerformed(ActionEvent arg0)
511 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
513 fr.ap.av.sendSelection();
516 JMenuItem clearCols = new JMenuItem(MessageManager
517 .getString("label.select_columns_not_containing"));
518 clearCols.addActionListener(new ActionListener()
521 public void actionPerformed(ActionEvent arg0)
523 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
525 fr.ap.av.sendSelection();
528 JMenuItem hideCols = new JMenuItem(
529 MessageManager.getString("label.hide_columns_containing"));
530 hideCols.addActionListener(new ActionListener()
533 public void actionPerformed(ActionEvent arg0)
535 fr.ap.alignFrame.hideFeatureColumns(type, true);
536 fr.ap.av.sendSelection();
539 JMenuItem hideOtherCols = new JMenuItem(
540 MessageManager.getString("label.hide_columns_not_containing"));
541 hideOtherCols.addActionListener(new ActionListener()
544 public void actionPerformed(ActionEvent arg0)
546 fr.ap.alignFrame.hideFeatureColumns(type, false);
547 fr.ap.av.sendSelection();
553 men.add(hideOtherCols);
554 men.show(table, x, y);
558 synchronized public void discoverAllFeatureData()
560 Set<String> allGroups = new HashSet<>();
561 AlignmentI alignment = af.getViewport().getAlignment();
563 for (int i = 0; i < alignment.getHeight(); i++)
565 SequenceI seq = alignment.getSequenceAt(i);
566 for (String group : seq.getFeatures().getFeatureGroups(true))
568 if (group != null && !allGroups.contains(group))
570 allGroups.add(group);
571 checkGroupState(group);
582 * Synchronise gui group list and check visibility of group
585 * @return true if group is visible
587 private boolean checkGroupState(String group)
589 boolean visible = fr.checkGroupVisibility(group, true);
591 for (int g = 0; g < groupPanel.getComponentCount(); g++)
593 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
595 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
600 final String grp = group;
601 final JCheckBox check = new JCheckBox(group, visible);
602 check.setFont(new Font("Serif", Font.BOLD, 12));
603 check.setToolTipText(group);
604 check.addItemListener(new ItemListener()
607 public void itemStateChanged(ItemEvent evt)
609 fr.setGroupVisibility(check.getText(), check.isSelected());
610 resetTable(new String[] { grp });
614 groupPanel.add(check);
618 synchronized void resetTable(String[] groupChanged)
624 resettingTable = true;
625 typeWidth = new Hashtable<>();
626 // TODO: change avWidth calculation to 'per-sequence' average and use long
629 Set<String> displayableTypes = new HashSet<>();
630 Set<String> foundGroups = new HashSet<>();
633 * determine which feature types may be visible depending on
634 * which groups are selected, and recompute average width data
636 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
639 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
642 * get the sequence's groups for positional features
643 * and keep track of which groups are visible
645 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
646 Set<String> visibleGroups = new HashSet<>();
647 for (String group : groups)
649 if (group == null || checkGroupState(group))
651 visibleGroups.add(group);
654 foundGroups.addAll(groups);
657 * get distinct feature types for visible groups
658 * record distinct visible types, and their count and total length
660 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
661 visibleGroups.toArray(new String[visibleGroups.size()]));
662 for (String type : types)
664 displayableTypes.add(type);
665 float[] avWidth = typeWidth.get(type);
668 avWidth = new float[2];
669 typeWidth.put(type, avWidth);
671 // todo this could include features with a non-visible group
672 // - do we greatly care?
673 // todo should we include non-displayable features here, and only
674 // update when features are added?
675 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
676 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
680 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
683 if (fr.hasRenderOrder())
687 fr.findAllFeatures(groupChanged != null); // prod to update
688 // colourschemes. but don't
690 // First add the checks in the previous render order,
691 // in case the window has been closed and reopened
693 List<String> frl = fr.getRenderOrder();
694 for (int ro = frl.size() - 1; ro > -1; ro--)
696 String type = frl.get(ro);
698 if (!displayableTypes.contains(type))
703 data[dataIndex][TYPE_COLUMN] = type;
704 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
705 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
706 data[dataIndex][FILTER_COLUMN] = featureFilter == null
707 ? new FeatureMatcherSet()
709 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
710 af.getViewport().getFeaturesDisplayed().isVisible(type));
712 displayableTypes.remove(type);
717 * process any extra features belonging only to
718 * a group which was just selected
720 while (!displayableTypes.isEmpty())
722 String type = displayableTypes.iterator().next();
723 data[dataIndex][TYPE_COLUMN] = type;
725 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
726 if (data[dataIndex][COLOUR_COLUMN] == null)
728 // "Colour has been updated in another view!!"
729 fr.clearRenderOrder();
732 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
733 data[dataIndex][FILTER_COLUMN] = featureFilter == null
734 ? new FeatureMatcherSet()
736 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
738 displayableTypes.remove(type);
741 if (originalData == null)
743 originalData = new Object[data.length][COLUMN_COUNT];
744 for (int i = 0; i < data.length; i++)
746 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
751 updateOriginalData(data);
754 table.setModel(new FeatureTableModel(data));
755 table.getColumnModel().getColumn(0).setPreferredWidth(200);
757 groupPanel.setLayout(
758 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
759 pruneGroups(foundGroups);
760 groupPanel.validate();
762 updateFeatureRenderer(data, groupChanged != null);
763 resettingTable = false;
767 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
768 * have been made outwith this dialog
770 * <li>a new feature type added (and made visible)</li>
771 * <li>a feature colour changed (in the Amend Features dialog)</li>
776 protected void updateOriginalData(Object[][] foundData)
778 // todo LinkedHashMap instead of Object[][] would be nice
780 Object[][] currentData = ((FeatureTableModel) table.getModel())
782 for (Object[] row : foundData)
784 String type = (String) row[TYPE_COLUMN];
785 boolean found = false;
786 for (Object[] current : currentData)
788 if (type.equals(current[TYPE_COLUMN]))
792 * currently dependent on object equality here;
793 * really need an equals method on FeatureColour
795 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
798 * feature colour has changed externally - update originalData
800 for (Object[] original : originalData)
802 if (type.equals(original[TYPE_COLUMN]))
804 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
815 * new feature detected - add to original data (on top)
817 Object[][] newData = new Object[originalData.length
819 for (int i = 0; i < originalData.length; i++)
821 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
825 originalData = newData;
831 * Remove from the groups panel any checkboxes for groups that are not in the
832 * foundGroups set. This enables removing a group from the display when the last
833 * feature in that group is deleted.
837 protected void pruneGroups(Set<String> foundGroups)
839 for (int g = 0; g < groupPanel.getComponentCount(); g++)
841 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
842 if (!foundGroups.contains(checkbox.getText()))
844 groupPanel.remove(checkbox);
850 * reorder data based on the featureRenderers global priority list.
854 private void ensureOrder(Object[][] data)
856 boolean sort = false;
857 float[] order = new float[data.length];
858 for (int i = 0; i < order.length; i++)
860 order[i] = fr.getOrder(data[i][0].toString());
863 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
867 sort = sort || order[i - 1] > order[i];
872 jalview.util.QuickSort.sort(order, data);
877 * Offers a file chooser dialog, and then loads the feature colours and
878 * filters from file in XML format and unmarshals to Jalview feature settings
882 JalviewFileChooser chooser = new JalviewFileChooser("fc",
883 SEQUENCE_FEATURE_COLOURS);
884 chooser.setFileView(new JalviewFileView());
885 chooser.setDialogTitle(
886 MessageManager.getString("label.load_feature_colours"));
887 chooser.setToolTipText(MessageManager.getString("action.load"));
889 int value = chooser.showOpenDialog(this);
891 if (value == JalviewFileChooser.APPROVE_OPTION)
893 File file = chooser.getSelectedFile();
899 * Loads feature colours and filters from XML stored in the given file
907 InputStreamReader in = new InputStreamReader(
908 new FileInputStream(file), "UTF-8");
910 JAXBContext jc = JAXBContext
911 .newInstance("jalview.xml.binding.jalview");
912 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
913 XMLStreamReader streamReader = XMLInputFactory.newInstance()
914 .createXMLStreamReader(in);
915 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
916 JalviewUserColours.class);
917 JalviewUserColours jucs = jbe.getValue();
919 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
922 * load feature colours
924 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
926 Colour newcol = jucs.getColour().get(i);
927 FeatureColourI colour = jalview.project.Jalview2XML
928 .parseColour(newcol);
929 fr.setColour(newcol.getName(), colour);
930 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
934 * load feature filters; loaded filters will replace any that are
935 * currently defined, other defined filters are left unchanged
937 for (int i = 0; i < jucs.getFilter().size(); i++)
939 Filter filterModel = jucs.getFilter().get(i);
940 String featureType = filterModel.getFeatureType();
941 FeatureMatcherSetI filter = jalview.project.Jalview2XML
942 .parseFilter(featureType, filterModel.getMatcherSet());
943 if (!filter.isEmpty())
945 fr.setFeatureFilter(featureType, filter);
950 * update feature settings table
955 Object[][] data = ((FeatureTableModel) table.getModel())
958 updateFeatureRenderer(data, false);
961 } catch (Exception ex)
963 System.out.println("Error loading User Colour File\n" + ex);
968 * Offers a file chooser dialog, and then saves the current feature colours
969 * and any filters to the selected file in XML format
973 JalviewFileChooser chooser = new JalviewFileChooser("fc",
974 SEQUENCE_FEATURE_COLOURS);
975 chooser.setFileView(new JalviewFileView());
976 chooser.setDialogTitle(
977 MessageManager.getString("label.save_feature_colours"));
978 chooser.setToolTipText(MessageManager.getString("action.save"));
980 int value = chooser.showSaveDialog(this);
982 if (value == JalviewFileChooser.APPROVE_OPTION)
984 save(chooser.getSelectedFile());
989 * Saves feature colours and filters to the given file
995 JalviewUserColours ucs = new JalviewUserColours();
996 ucs.setSchemeName("Sequence Features");
999 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1000 new FileOutputStream(file), "UTF-8"));
1003 * sort feature types by colour order, from 0 (highest)
1006 Set<String> fr_colours = fr.getAllFeatureColours();
1007 String[] sortedTypes = fr_colours
1008 .toArray(new String[fr_colours.size()]);
1009 Arrays.sort(sortedTypes, new Comparator<String>()
1012 public int compare(String type1, String type2)
1014 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1019 * save feature colours
1021 for (String featureType : sortedTypes)
1023 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1024 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1026 ucs.getColour().add(col);
1030 * save any feature filters
1032 for (String featureType : sortedTypes)
1034 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1035 if (filter != null && !filter.isEmpty())
1037 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1038 FeatureMatcherI firstMatcher = iterator.next();
1039 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1040 .marshalFilter(firstMatcher, iterator,
1042 Filter filterModel = new Filter();
1043 filterModel.setFeatureType(featureType);
1044 filterModel.setMatcherSet(ms);
1045 ucs.getFilter().add(filterModel);
1048 JAXBContext jaxbContext = JAXBContext
1049 .newInstance(JalviewUserColours.class);
1050 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1051 jaxbMarshaller.marshal(
1052 new ObjectFactory().createJalviewUserColours(ucs), out);
1054 // jaxbMarshaller.marshal(object, pout);
1055 // marshaller.marshal(object);
1058 // ucs.marshal(out);
1060 } catch (Exception ex)
1062 ex.printStackTrace();
1066 public void invertSelection()
1068 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1069 for (int i = 0; i < data.length; i++)
1071 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1073 updateFeatureRenderer(data, true);
1077 public void orderByAvWidth()
1079 if (table == null || table.getModel() == null)
1083 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1084 float[] width = new float[data.length];
1088 for (int i = 0; i < data.length; i++)
1090 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1093 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1094 // weight - but have to make per
1095 // sequence, too (awidth[2])
1096 // if (width[i]==1) // hack to distinguish single width sequences.
1107 boolean sort = false;
1108 for (int i = 0; i < width.length; i++)
1110 // awidth = (float[]) typeWidth.get(data[i][0]);
1113 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1116 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1122 width[i] /= max; // normalize
1123 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1127 sort = sort || width[i - 1] > width[i];
1132 jalview.util.QuickSort.sort(width, data);
1133 // update global priority order
1136 updateFeatureRenderer(data, false);
1141 * close ourselves but leave any existing UI handlers (e.g a CDS/Protein tabbed
1142 * feature settings dialog) intact
1144 public void closeOldSettings()
1150 * close the feature settings dialog (and any containing frame)
1157 private void closeDialog(boolean closeContainingFrame)
1163 af.setFeatureSettingsGeometry(frame.getBounds());
1164 frame.setClosed(true);
1168 SplitContainerI sc = af.getSplitViewContainer();
1169 sc.closeFeatureSettings(this, closeContainingFrame);
1170 af.featureSettings = null;
1172 } catch (Exception exe)
1178 public void updateFeatureRenderer(Object[][] data)
1180 updateFeatureRenderer(data, true);
1184 * Update the priority order of features; only repaint if this changed the order
1185 * of visible features
1190 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1192 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1194 if (fr.setFeaturePriority(rowData, visibleNew))
1201 * Converts table data into an array of data beans
1203 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1205 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1206 for (int i = 0; i < data.length; i++)
1208 String type = (String) data[i][TYPE_COLUMN];
1209 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1210 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1211 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1212 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1218 private void jbInit() throws Exception
1220 this.setLayout(new BorderLayout());
1222 final boolean hasComplement = af.getViewport()
1223 .getCodingComplement() != null;
1225 JPanel settingsPane = new JPanel();
1226 settingsPane.setLayout(new BorderLayout());
1228 JPanel bigPanel = new JPanel();
1229 bigPanel.setLayout(new BorderLayout());
1231 groupPanel = new JPanel();
1232 bigPanel.add(groupPanel, BorderLayout.NORTH);
1234 JButton invert = new JButton(
1235 MessageManager.getString("label.invert_selection"));
1236 invert.setFont(JvSwingUtils.getLabelFont());
1237 invert.addActionListener(new ActionListener()
1240 public void actionPerformed(ActionEvent e)
1246 JButton optimizeOrder = new JButton(
1247 MessageManager.getString("label.optimise_order"));
1248 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1249 optimizeOrder.addActionListener(new ActionListener()
1252 public void actionPerformed(ActionEvent e)
1258 JButton sortByScore = new JButton(
1259 MessageManager.getString("label.seq_sort_by_score"));
1260 sortByScore.setFont(JvSwingUtils.getLabelFont());
1261 sortByScore.addActionListener(new ActionListener()
1264 public void actionPerformed(ActionEvent e)
1266 af.avc.sortAlignmentByFeatureScore(null);
1269 JButton sortByDens = new JButton(
1270 MessageManager.getString("label.sequence_sort_by_density"));
1271 sortByDens.setFont(JvSwingUtils.getLabelFont());
1272 sortByDens.addActionListener(new ActionListener()
1275 public void actionPerformed(ActionEvent e)
1277 af.avc.sortAlignmentByFeatureDensity(null);
1281 JButton help = new JButton(MessageManager.getString("action.help"));
1282 help.setFont(JvSwingUtils.getLabelFont());
1283 help.addActionListener(new ActionListener()
1286 public void actionPerformed(ActionEvent e)
1290 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1291 } catch (HelpSetException e1)
1293 e1.printStackTrace();
1297 // Cancel for a SplitFrame should just revert changes to the currently displayed
1298 // settings. May want to do this for either or both - so need a splitview
1299 // feature settings cancel/OK.
1300 JButton cancel = new JButton(MessageManager
1301 .getString(hasComplement ? "action.revert" : "action.cancel"));
1302 cancel.setToolTipText(MessageManager.getString(hasComplement
1303 ? "action.undo_changes_to_feature_settings"
1304 : "action.undo_changes_to_feature_settings_and_close_the_dialog"));
1305 cancel.setFont(JvSwingUtils.getLabelFont());
1306 // TODO: disable cancel until current settings are different
1307 cancel.addActionListener(new ActionListener()
1310 public void actionPerformed(ActionEvent e)
1319 // Cancel for the whole dialog should cancel both CDS and Protein.
1320 // OK for an individual feature settings just applies changes, but dialog
1322 JButton ok = new JButton(MessageManager
1323 .getString(hasComplement ? "action.apply" : "action.ok"));
1324 ok.setFont(JvSwingUtils.getLabelFont());
1325 ok.addActionListener(new ActionListener()
1328 public void actionPerformed(ActionEvent e)
1336 storeOriginalSettings();
1341 JButton loadColours = new JButton(
1342 MessageManager.getString("label.load_colours"));
1343 loadColours.setFont(JvSwingUtils.getLabelFont());
1344 loadColours.setToolTipText(
1345 MessageManager.getString("label.load_colours_tooltip"));
1346 loadColours.addActionListener(new ActionListener()
1349 public void actionPerformed(ActionEvent e)
1355 JButton saveColours = new JButton(
1356 MessageManager.getString("label.save_colours"));
1357 saveColours.setFont(JvSwingUtils.getLabelFont());
1358 saveColours.setToolTipText(
1359 MessageManager.getString("label.save_colours_tooltip"));
1360 saveColours.addActionListener(new ActionListener()
1363 public void actionPerformed(ActionEvent e)
1368 transparency.addChangeListener(new ChangeListener()
1371 public void stateChanged(ChangeEvent evt)
1373 if (!inConstruction)
1375 fr.setTransparency((100 - transparency.getValue()) / 100f);
1381 transparency.setMaximum(70);
1382 transparency.setToolTipText(
1383 MessageManager.getString("label.transparency_tip"));
1385 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1386 String text = MessageManager.formatMessage("label.show_linked_features",
1388 ? MessageManager.getString("label.protein")
1391 showComplement = new JCheckBox(text);
1392 showComplement.addActionListener(new ActionListener()
1395 public void actionPerformed(ActionEvent e)
1398 .setShowComplementFeatures(showComplement.isSelected());
1403 showComplementOnTop = new JCheckBox(
1404 MessageManager.getString("label.on_top"));
1405 showComplementOnTop.addActionListener(new ActionListener()
1408 public void actionPerformed(ActionEvent e)
1410 af.getViewport().setShowComplementFeaturesOnTop(
1411 showComplementOnTop.isSelected());
1415 // JButton viewComplementSettings = new JButton(MessageManager
1416 // .formatMessage("label.show_linked_feature_settings",
1418 // ? MessageManager.getString("label.protein")
1421 // viewComplementSettings.addActionListener(new ActionListener()
1425 // public void actionPerformed(ActionEvent e)
1427 // AlignViewControllerGuiI complAf = af.getSplitViewContainer()
1428 // .getComplementAlignFrame(af);
1429 // FeatureSettings complFeatureSettings = (FeatureSettings) complAf
1430 // .getFeatureSettingsUI();
1431 // if (complFeatureSettings != null)
1433 // complFeatureSettings.frame.setVisible(true);
1436 // complFeatureSettings.frame.setSelected(true);
1438 // } catch (Exception q)
1443 // complAf.showFeatureSettingsUI();
1447 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1448 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1450 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1451 transbuttons.add(optimizeOrder);
1452 transbuttons.add(invert);
1453 transbuttons.add(sortByScore);
1454 transbuttons.add(sortByDens);
1455 transbuttons.add(help);
1457 JPanel transPanelLeft = new JPanel(
1458 new GridLayout(hasComplement ? 4 : 2, 1));
1459 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1460 transPanelLeft.add(transparency);
1463 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1464 cp.add(showComplement);
1465 cp.add(showComplementOnTop);
1466 transPanelLeft.add(cp);
1468 lowerPanel.add(transPanelLeft);
1469 lowerPanel.add(transbuttons);
1471 JPanel buttonPanel = new JPanel();
1472 buttonPanel.add(ok);
1473 buttonPanel.add(cancel);
1474 buttonPanel.add(loadColours);
1475 buttonPanel.add(saveColours);
1476 bigPanel.add(scrollPane, BorderLayout.CENTER);
1477 settingsPane.add(bigPanel, BorderLayout.CENTER);
1478 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1479 this.add(settingsPane);
1483 * Repaints alignment, structure and overview (if shown). If there is a
1484 * complementary view which is showing this view's features, then also
1487 void refreshDisplay()
1489 af.alignPanel.paintAlignment(true, true);
1490 AlignViewportI complement = af.getViewport().getCodingComplement();
1491 if (complement != null && complement.isShowComplementFeatures())
1493 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1494 af2.alignPanel.paintAlignment(true, true);
1499 * Answers a suitable tooltip to show on the colour cell of the table
1503 * if true include 'click to edit' and similar text
1506 public static String getColorTooltip(FeatureColourI fcol,
1513 if (fcol.isSimpleColour())
1515 return withHint ? BASE_TOOLTIP : null;
1517 String description = fcol.getDescription();
1518 description = description.replaceAll("<", "<");
1519 description = description.replaceAll(">", ">");
1520 StringBuilder tt = new StringBuilder(description);
1523 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1525 return JvSwingUtils.wrapTooltip(true, tt.toString());
1528 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1531 boolean thr = false;
1532 StringBuilder tx = new StringBuilder();
1534 if (gcol.isColourByAttribute())
1536 tx.append(FeatureMatcher
1537 .toAttributeDisplayName(gcol.getAttributeName()));
1539 else if (!gcol.isColourByLabel())
1541 tx.append(MessageManager.getString("label.score"));
1544 if (gcol.isAboveThreshold())
1549 if (gcol.isBelowThreshold())
1554 if (gcol.isColourByLabel())
1560 if (!gcol.isColourByAttribute())
1568 Color newColor = gcol.getMaxColour();
1569 comp.setBackground(newColor);
1570 // System.err.println("Width is " + w / 2);
1571 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1572 comp.setIcon(ficon);
1573 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1574 // + newColor.getGreen() + ", " + newColor.getBlue()
1575 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1576 // + ", " + minCol.getBlue() + ")");
1578 comp.setHorizontalAlignment(SwingConstants.CENTER);
1579 comp.setText(tx.toString());
1582 // ///////////////////////////////////////////////////////////////////////
1583 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1584 // ///////////////////////////////////////////////////////////////////////
1585 class FeatureTableModel extends AbstractTableModel
1587 private String[] columnNames = {
1588 MessageManager.getString("label.feature_type"),
1589 MessageManager.getString("action.colour"),
1590 MessageManager.getString("label.configuration"),
1591 MessageManager.getString("label.show") };
1593 private Object[][] data;
1595 FeatureTableModel(Object[][] data)
1600 public Object[][] getData()
1605 public void setData(Object[][] data)
1611 public int getColumnCount()
1613 return columnNames.length;
1616 public Object[] getRow(int row)
1622 public int getRowCount()
1628 public String getColumnName(int col)
1630 return columnNames[col];
1634 public Object getValueAt(int row, int col)
1636 return data[row][col];
1640 * Answers the class of column c of the table
1643 public Class<?> getColumnClass(int c)
1648 return String.class;
1650 return FeatureColour.class;
1652 return FeatureMatcherSet.class;
1654 return Boolean.class;
1659 public boolean isCellEditable(int row, int col)
1661 return col == 0 ? false : true;
1665 public void setValueAt(Object value, int row, int col)
1667 data[row][col] = value;
1668 fireTableCellUpdated(row, col);
1669 updateFeatureRenderer(data);
1674 class ColorRenderer extends JLabel implements TableCellRenderer
1676 Border unselectedBorder = null;
1678 Border selectedBorder = null;
1680 public ColorRenderer()
1682 setOpaque(true); // MUST do this for background to show up.
1683 setHorizontalTextPosition(SwingConstants.CENTER);
1684 setVerticalTextPosition(SwingConstants.CENTER);
1688 public Component getTableCellRendererComponent(JTable tbl, Object color,
1689 boolean isSelected, boolean hasFocus, int row, int column)
1691 FeatureColourI cellColour = (FeatureColourI) color;
1693 setBackground(tbl.getBackground());
1694 if (!cellColour.isSimpleColour())
1696 Rectangle cr = tbl.getCellRect(row, column, false);
1697 FeatureSettings.renderGraduatedColor(this, cellColour,
1698 (int) cr.getWidth(), (int) cr.getHeight());
1704 setBackground(cellColour.getColour());
1708 if (selectedBorder == null)
1710 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1711 tbl.getSelectionBackground());
1713 setBorder(selectedBorder);
1717 if (unselectedBorder == null)
1719 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1720 tbl.getBackground());
1722 setBorder(unselectedBorder);
1729 class FilterRenderer extends JLabel implements TableCellRenderer
1731 javax.swing.border.Border unselectedBorder = null;
1733 javax.swing.border.Border selectedBorder = null;
1735 public FilterRenderer()
1737 setOpaque(true); // MUST do this for background to show up.
1738 setHorizontalTextPosition(SwingConstants.CENTER);
1739 setVerticalTextPosition(SwingConstants.CENTER);
1743 public Component getTableCellRendererComponent(JTable tbl,
1744 Object filter, boolean isSelected, boolean hasFocus, int row,
1747 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1749 String asText = theFilter.toString();
1750 setBackground(tbl.getBackground());
1751 this.setText(asText);
1756 if (selectedBorder == null)
1758 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1759 tbl.getSelectionBackground());
1761 setBorder(selectedBorder);
1765 if (unselectedBorder == null)
1767 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1768 tbl.getBackground());
1770 setBorder(unselectedBorder);
1778 * update comp using rendering settings from gcol
1783 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1785 int w = comp.getWidth(), h = comp.getHeight();
1788 w = (int) comp.getPreferredSize().getWidth();
1789 h = (int) comp.getPreferredSize().getHeight();
1796 renderGraduatedColor(comp, gcol, w, h);
1799 class ColorEditor extends AbstractCellEditor
1800 implements TableCellEditor, ActionListener
1804 FeatureColourI currentColor;
1806 FeatureTypeSettings chooser;
1812 JColorChooser colorChooser;
1816 protected static final String EDIT = "edit";
1818 int rowSelected = 0;
1820 public ColorEditor(FeatureSettings me)
1823 // Set up the editor (from the table's point of view),
1824 // which is a button.
1825 // This button brings up the color chooser dialog,
1826 // which is the editor from the user's point of view.
1827 button = new JButton();
1828 button.setActionCommand(EDIT);
1829 button.addActionListener(this);
1830 button.setBorderPainted(false);
1831 // Set up the dialog that the button brings up.
1832 colorChooser = new JColorChooser();
1833 dialog = JColorChooser.createDialog(button,
1834 MessageManager.getString("label.select_colour"), true, // modal
1835 colorChooser, this, // OK button handler
1836 null); // no CANCEL button handler
1840 * Handles events from the editor button and from the dialog's OK button.
1843 public void actionPerformed(ActionEvent e)
1845 // todo test e.getSource() instead here
1846 if (EDIT.equals(e.getActionCommand()))
1848 // The user has clicked the cell, so
1849 // bring up the dialog.
1850 if (currentColor.isSimpleColour())
1852 // bring up simple color chooser
1853 button.setBackground(currentColor.getColour());
1854 colorChooser.setColor(currentColor.getColour());
1855 dialog.setVisible(true);
1859 // bring up graduated chooser.
1860 chooser = new FeatureTypeSettings(me.fr, type);
1865 chooser.setRequestFocusEnabled(true);
1866 chooser.requestFocus();
1868 chooser.addActionListener(this);
1869 // Make the renderer reappear.
1870 fireEditingStopped();
1875 if (currentColor.isSimpleColour())
1878 * read off colour picked in colour chooser after OK pressed
1880 currentColor = new FeatureColour(colorChooser.getColor());
1881 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1886 * after OK in variable colour dialog, any changes to colour
1887 * (or filters!) are already set in FeatureRenderer, so just
1888 * update table data without triggering updateFeatureRenderer
1890 currentColor = fr.getFeatureColours().get(type);
1891 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1892 if (currentFilter == null)
1894 currentFilter = new FeatureMatcherSet();
1896 Object[] data = ((FeatureTableModel) table.getModel())
1897 .getData()[rowSelected];
1898 data[COLOUR_COLUMN] = currentColor;
1899 data[FILTER_COLUMN] = currentFilter;
1901 fireEditingStopped();
1902 me.table.validate();
1906 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1908 public Object getCellEditorValue()
1910 return currentColor;
1913 // Implement the one method defined by TableCellEditor.
1915 public Component getTableCellEditorComponent(JTable theTable, Object value,
1916 boolean isSelected, int row, int column)
1918 currentColor = (FeatureColourI) value;
1919 this.rowSelected = row;
1920 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1921 button.setOpaque(true);
1922 button.setBackground(me.getBackground());
1923 if (!currentColor.isSimpleColour())
1925 JLabel btn = new JLabel();
1926 btn.setSize(button.getSize());
1927 FeatureSettings.renderGraduatedColor(btn, currentColor);
1928 button.setBackground(btn.getBackground());
1929 button.setIcon(btn.getIcon());
1930 button.setText(btn.getText());
1935 button.setIcon(null);
1936 button.setBackground(currentColor.getColour());
1943 * The cell editor for the Filter column. It displays the text of any filters
1944 * for the feature type in that row (in full as a tooltip, possible abbreviated
1945 * as display text). On click in the cell, opens the Feature Display Settings
1946 * dialog at the Filters tab.
1948 class FilterEditor extends AbstractCellEditor
1949 implements TableCellEditor, ActionListener
1953 FeatureMatcherSetI currentFilter;
1961 protected static final String EDIT = "edit";
1963 int rowSelected = 0;
1965 public FilterEditor(FeatureSettings me)
1968 button = new JButton();
1969 button.setActionCommand(EDIT);
1970 button.addActionListener(this);
1971 button.setBorderPainted(false);
1975 * Handles events from the editor button
1978 public void actionPerformed(ActionEvent e)
1980 if (button == e.getSource())
1982 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1983 chooser.addActionListener(this);
1984 chooser.setRequestFocusEnabled(true);
1985 chooser.requestFocus();
1986 if (lastLocation != null)
1988 // todo open at its last position on screen
1989 chooser.setBounds(lastLocation.x, lastLocation.y,
1990 chooser.getWidth(), chooser.getHeight());
1993 fireEditingStopped();
1995 else if (e.getSource() instanceof Component)
1999 * after OK in variable colour dialog, any changes to filter
2000 * (or colours!) are already set in FeatureRenderer, so just
2001 * update table data without triggering updateFeatureRenderer
2003 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2004 currentFilter = me.fr.getFeatureFilter(type);
2005 if (currentFilter == null)
2007 currentFilter = new FeatureMatcherSet();
2009 Object[] data = ((FeatureTableModel) table.getModel())
2010 .getData()[rowSelected];
2011 data[COLOUR_COLUMN] = currentColor;
2012 data[FILTER_COLUMN] = currentFilter;
2013 fireEditingStopped();
2014 me.table.validate();
2019 public Object getCellEditorValue()
2021 return currentFilter;
2025 public Component getTableCellEditorComponent(JTable theTable, Object value,
2026 boolean isSelected, int row, int column)
2028 currentFilter = (FeatureMatcherSetI) value;
2029 this.rowSelected = row;
2030 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2031 button.setOpaque(true);
2032 button.setBackground(me.getBackground());
2033 button.setText(currentFilter.toString());
2034 button.setIcon(null);
2039 public boolean isOpen()
2041 if (af.getSplitViewContainer() != null)
2043 return af.getSplitViewContainer().isFeatureSettingsOpen();
2045 return frame != null && !frame.isClosed();
2049 public void revert()
2051 fr.setTransparency(originalTransparency);
2052 fr.setFeatureFilters(originalFilters);
2053 updateFeatureRenderer(originalData);
2054 af.getViewport().setViewStyle(originalViewStyle);
2055 updateComplementButtons();
2060 class FeatureIcon implements Icon
2062 FeatureColourI gcol;
2066 boolean midspace = false;
2068 int width = 50, height = 20;
2070 int s1, e1; // start and end of midpoint band for thresholded symbol
2072 Color mpcolour = Color.white;
2074 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2094 public int getIconWidth()
2100 public int getIconHeight()
2106 public void paintIcon(Component c, Graphics g, int x, int y)
2109 if (gcol.isColourByLabel())
2112 g.fillRect(0, 0, width, height);
2113 // need an icon here.
2114 g.setColor(gcol.getMaxColour());
2116 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2118 // g.setFont(g.getFont().deriveFont(
2119 // AffineTransform.getScaleInstance(
2120 // width/g.getFontMetrics().stringWidth("Label"),
2121 // height/g.getFontMetrics().getHeight())));
2123 g.drawString(MessageManager.getString("label.label"), 0, 0);
2128 Color minCol = gcol.getMinColour();
2130 g.fillRect(0, 0, s1, height);
2133 g.setColor(Color.white);
2134 g.fillRect(s1, 0, e1 - s1, height);
2136 g.setColor(gcol.getMaxColour());
2137 g.fillRect(0, e1, width - e1, height);