2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewportI;
24 import jalview.api.FeatureColourI;
25 import jalview.api.FeatureSettingsControllerI;
26 import jalview.api.ViewStyleI;
27 import jalview.datamodel.AlignmentI;
28 import jalview.datamodel.SequenceI;
29 import jalview.datamodel.features.FeatureMatcherI;
30 import jalview.datamodel.features.FeatureMatcherSet;
31 import jalview.datamodel.features.FeatureMatcherSetI;
32 import jalview.datamodel.ontology.OntologyI;
33 import jalview.gui.Help.HelpId;
34 import jalview.io.JalviewFileChooser;
35 import jalview.io.JalviewFileView;
36 import jalview.io.gff.SequenceOntologyFactory;
37 import jalview.schemes.FeatureColour;
38 import jalview.util.MessageManager;
39 import jalview.util.Platform;
40 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
41 import jalview.viewmodel.styles.ViewStyle;
42 import jalview.xml.binding.jalview.JalviewUserColours;
43 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
44 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
45 import jalview.xml.binding.jalview.ObjectFactory;
47 import java.awt.BorderLayout;
48 import java.awt.Color;
49 import java.awt.Component;
50 import java.awt.Dimension;
51 import java.awt.FlowLayout;
53 import java.awt.Graphics;
54 import java.awt.GridLayout;
55 import java.awt.Point;
56 import java.awt.Rectangle;
57 import java.awt.event.ActionEvent;
58 import java.awt.event.ActionListener;
59 import java.awt.event.ItemEvent;
60 import java.awt.event.ItemListener;
61 import java.awt.event.MouseAdapter;
62 import java.awt.event.MouseEvent;
63 import java.awt.event.MouseMotionAdapter;
64 import java.beans.PropertyChangeEvent;
65 import java.beans.PropertyChangeListener;
67 import java.io.FileInputStream;
68 import java.io.FileOutputStream;
69 import java.io.InputStreamReader;
70 import java.io.OutputStreamWriter;
71 import java.io.PrintWriter;
72 import java.util.ArrayList;
73 import java.util.Arrays;
74 import java.util.Comparator;
75 import java.util.HashMap;
76 import java.util.HashSet;
77 import java.util.Hashtable;
78 import java.util.Iterator;
79 import java.util.List;
83 import javax.help.HelpSetException;
84 import javax.swing.AbstractCellEditor;
85 import javax.swing.BorderFactory;
86 import javax.swing.Icon;
87 import javax.swing.JButton;
88 import javax.swing.JCheckBox;
89 import javax.swing.JCheckBoxMenuItem;
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.RowFilter;
103 import javax.swing.SwingConstants;
104 import javax.swing.event.ChangeEvent;
105 import javax.swing.event.ChangeListener;
106 import javax.swing.table.AbstractTableModel;
107 import javax.swing.table.TableCellEditor;
108 import javax.swing.table.TableCellRenderer;
109 import javax.swing.table.TableColumn;
110 import javax.swing.table.TableRowSorter;
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
120 private static final Font VERDANA_12 = new Font("Verdana", Font.PLAIN, 12);
122 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
123 .getString("label.sequence_feature_colours");
126 * column indices of fields in Feature Settings table
128 static final int TYPE_COLUMN = 0;
130 static final int COLOUR_COLUMN = 1;
132 static final int FILTER_COLUMN = 2;
134 static final int SHOW_COLUMN = 3;
136 private static final int COLUMN_COUNT = 4;
138 private static final int MIN_WIDTH = 400;
140 private static final int MIN_HEIGHT = 400;
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 JCheckBox showComplement;
169 JCheckBox showComplementOnTop;
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 * holds {featureCount, totalExtent} for each feature type
188 Map<String, float[]> typeWidth = null;
191 * if true, 'child' feature types are not displayed
193 JCheckBox summaryView;
196 * those feature types that do not have a parent feature type present
197 * (as determined by an Ontology relationship)
199 List<String> topLevelTypes;
206 public FeatureSettings(AlignFrame alignFrame)
208 this.af = alignFrame;
209 fr = af.getFeatureRenderer();
211 // save transparency for restore on Cancel
212 originalTransparency = fr.getTransparency();
213 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
214 transparency.setMaximum(100 - originalTransparencyAsPercent);
216 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
217 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
219 topLevelTypes = new ArrayList<>();
224 } catch (Exception ex)
226 ex.printStackTrace();
231 scrollPane.setViewportView(table);
233 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
235 fr.findAllFeatures(true); // display everything!
238 discoverAllFeatureData();
239 final PropertyChangeListener change;
240 final FeatureSettings fs = this;
241 fr.addPropertyChangeListener(change = new PropertyChangeListener()
244 public void propertyChange(PropertyChangeEvent evt)
246 if (!fs.resettingTable && !fs.handlingUpdate)
248 fs.handlingUpdate = true;
250 // new groups may be added with new sequence feature types only
251 fs.handlingUpdate = false;
256 frame = new JInternalFrame();
257 frame.setContentPane(this);
258 if (Platform.isAMac())
260 Desktop.addInternalFrame(frame,
261 MessageManager.getString("label.sequence_feature_settings"),
266 Desktop.addInternalFrame(frame,
267 MessageManager.getString("label.sequence_feature_settings"),
270 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
272 frame.addInternalFrameListener(
273 new javax.swing.event.InternalFrameAdapter()
276 public void internalFrameClosed(
277 javax.swing.event.InternalFrameEvent evt)
279 fr.removePropertyChangeListener(change);
282 frame.setLayer(JLayeredPane.PALETTE_LAYER);
283 inConstruction = false;
287 * Constructs and configures the JTable which displays columns of data for
290 protected void initTable()
295 public String getToolTipText(MouseEvent e)
298 int column = table.columnAtPoint(e.getPoint());
303 * drag to reorder not enabled in Summary View
305 tip = summaryView.isSelected()
306 ? MessageManager.getString(
307 "label.feature_settings_select_columns")
308 : JvSwingUtils.wrapTooltip(true, MessageManager
309 .getString("label.feature_settings_click_drag"));
312 int row = table.rowAtPoint(e.getPoint());
313 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
317 .getString("label.configure_feature_tooltip")
326 table.getTableHeader().setFont(VERDANA_12);
327 table.setFont(VERDANA_12);
329 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
330 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
332 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
333 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
335 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
336 new ColorRenderer(), new ColorEditor(this));
337 table.addColumn(colourColumn);
339 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
340 new FilterRenderer(), new FilterEditor(this));
341 table.addColumn(filterColumn);
343 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
345 table.addMouseListener(new MouseAdapter()
348 public void mousePressed(MouseEvent evt)
350 selectedRow = table.rowAtPoint(evt.getPoint());
351 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
352 if (evt.isPopupTrigger())
354 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
355 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
357 else if (evt.getClickCount() == 2)
359 boolean invertSelection = evt.isAltDown();
360 boolean toggleSelection = Platform.isControlDown(evt);
361 boolean extendSelection = evt.isShiftDown();
362 String[] terms = getTermsInScope(type);
363 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
364 invertSelection, extendSelection, toggleSelection, terms);
368 // isPopupTrigger fires on mouseReleased on Windows
370 public void mouseReleased(MouseEvent evt)
372 selectedRow = table.rowAtPoint(evt.getPoint());
373 if (evt.isPopupTrigger())
375 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
376 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
377 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
382 table.addMouseMotionListener(new MouseMotionAdapter()
385 public void mouseDragged(MouseEvent evt)
387 int newRow = table.rowAtPoint(evt.getPoint());
394 * Answers an array consisting of the given type, and also (if 'Summary View'
395 * is selected), any child terms in the sequence ontology
400 protected String[] getTermsInScope(String type)
402 if (!summaryView.isSelected())
404 return new String[] { type };
407 List<String> terms = new ArrayList<>();
410 OntologyI so = SequenceOntologyFactory.getInstance();
412 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
413 for (Object[] row : data)
415 String type2 = (String) row[TYPE_COLUMN];
416 if (!type2.equals(type) && so.isA(type2, type))
421 return terms.toArray(new String[terms.size()]);
424 protected void popupMenu(final int rowSelected, final String type,
425 final Object typeCol, int x, int y)
427 final FeatureColourI featureColour = (FeatureColourI) typeCol;
429 JPopupMenu men = new JPopupMenu(MessageManager
430 .formatMessage("label.settings_for_param", new String[]
432 JMenuItem scr = new JMenuItem(
433 MessageManager.getString("label.sort_by_score"));
435 final FeatureSettings me = this;
436 scr.addActionListener(new ActionListener()
439 public void actionPerformed(ActionEvent e)
441 String[] types = getTermsInScope(type);
442 me.af.avc.sortAlignmentByFeatureScore(Arrays.asList(types));
445 JMenuItem dens = new JMenuItem(
446 MessageManager.getString("label.sort_by_density"));
447 dens.addActionListener(new ActionListener()
450 public void actionPerformed(ActionEvent e)
452 String[] types = getTermsInScope(type);
453 me.af.avc.sortAlignmentByFeatureDensity(Arrays.asList(types));
458 // fixme is Variable Colour in popup menu or not
460 * variable colour options include colour by label, by score,
461 * by selected attribute text, or attribute value
463 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
464 MessageManager.getString("label.variable_colour"));
465 mxcol.setSelected(!featureColour.isSimpleColour());
467 mxcol.addActionListener(new ActionListener()
469 JColorChooser colorChooser;
472 public void actionPerformed(ActionEvent e)
474 if (e.getSource() == mxcol)
476 if (featureColour.isSimpleColour())
478 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
479 fc.addActionListener(this);
483 // bring up simple color chooser
484 colorChooser = new JColorChooser();
485 String title = MessageManager
486 .getString("label.select_colour");
487 JDialog dialog = JColorChooser.createDialog(me,
488 title, true, // modal
489 colorChooser, this, // OK button handler
490 null); // no CANCEL button handler
491 colorChooser.setColor(featureColour.getMaxColour());
492 dialog.setVisible(true);
497 if (e.getSource() instanceof FeatureTypeSettings)
500 * update after OK in feature colour dialog; the updated
501 * colour will have already been set in the FeatureRenderer
503 FeatureColourI fci = fr.getFeatureColours().get(type);
504 table.setValueAt(fci, rowSelected, 1);
509 // probably the color chooser!
510 table.setValueAt(new FeatureColour(colorChooser.getColor()),
513 me.updateFeatureRenderer(
514 ((FeatureTableModel) table.getModel()).getData(),
521 JMenuItem selCols = new JMenuItem(
522 MessageManager.getString("label.select_columns_containing"));
523 selCols.addActionListener(new ActionListener()
526 public void actionPerformed(ActionEvent arg0)
528 String[] types = getTermsInScope(type);
529 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
533 JMenuItem clearCols = new JMenuItem(MessageManager
534 .getString("label.select_columns_not_containing"));
535 clearCols.addActionListener(new ActionListener()
538 public void actionPerformed(ActionEvent arg0)
540 String[] types = getTermsInScope(type);
541 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
545 JMenuItem hideCols = new JMenuItem(
546 MessageManager.getString("label.hide_columns_containing"));
547 hideCols.addActionListener(new ActionListener()
550 public void actionPerformed(ActionEvent arg0)
552 String[] types = getTermsInScope(type);
553 fr.ap.alignFrame.hideFeatureColumns(true, types);
556 JMenuItem hideOtherCols = new JMenuItem(
557 MessageManager.getString("label.hide_columns_not_containing"));
558 hideOtherCols.addActionListener(new ActionListener()
561 public void actionPerformed(ActionEvent arg0)
563 String[] types = getTermsInScope(type);
564 fr.ap.alignFrame.hideFeatureColumns(false, types);
570 men.add(hideOtherCols);
571 men.show(table, x, y);
575 synchronized public void discoverAllFeatureData()
577 Set<String> allGroups = new HashSet<>();
578 AlignmentI alignment = af.getViewport().getAlignment();
580 for (int i = 0; i < alignment.getHeight(); i++)
582 SequenceI seq = alignment.getSequenceAt(i);
583 for (String group : seq.getFeatures().getFeatureGroups(true))
585 if (group != null && !allGroups.contains(group))
587 allGroups.add(group);
588 checkGroupState(group);
599 * Synchronise gui group list and check visibility of group
602 * @return true if group is visible
604 private boolean checkGroupState(String group)
606 boolean visible = fr.checkGroupVisibility(group, true);
608 for (int g = 0; g < groupPanel.getComponentCount(); g++)
610 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
612 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
617 final String grp = group;
618 final JCheckBox check = new JCheckBox(group, visible);
619 check.setFont(new Font("Serif", Font.BOLD, 12));
620 check.setToolTipText(group);
621 check.addItemListener(new ItemListener()
624 public void itemStateChanged(ItemEvent evt)
626 fr.setGroupVisibility(check.getText(), check.isSelected());
627 resetTable(new String[] { grp });
631 groupPanel.add(check);
635 synchronized void resetTable(String[] groupChanged)
641 resettingTable = true;
642 typeWidth = new Hashtable<>();
643 // TODO: change avWidth calculation to 'per-sequence' average and use long
646 Set<String> displayableTypes = new HashSet<>();
647 Set<String> foundGroups = new HashSet<>();
650 * determine which feature types may be visible depending on
651 * which groups are selected, and recompute average width data
653 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
656 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
659 * get the sequence's groups for positional features
660 * and keep track of which groups are visible
662 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
663 Set<String> visibleGroups = new HashSet<>();
664 for (String group : groups)
666 if (group == null || checkGroupState(group))
668 visibleGroups.add(group);
671 foundGroups.addAll(groups);
674 * get distinct feature types for visible groups
675 * record distinct visible types, and their count and total length
677 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
678 visibleGroups.toArray(new String[visibleGroups.size()]));
680 for (String type : types)
682 displayableTypes.add(type);
683 float[] avWidth = typeWidth.get(type);
686 avWidth = new float[2];
687 typeWidth.put(type, avWidth);
689 // todo this could include features with a non-visible group
690 // - do we greatly care?
691 // todo should we include non-displayable features here, and only
692 // update when features are added?
693 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
694 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
699 * enable 'Summary View' if some types are sub-types of others
701 Set<String> parents = SequenceOntologyFactory.getInstance()
702 .getParentTerms(displayableTypes);
703 summaryView.setEnabled(parents.size() < displayableTypes.size());
705 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
708 if (fr.hasRenderOrder())
712 fr.findAllFeatures(groupChanged != null); // prod to update
713 // colourschemes. but don't
715 // First add the checks in the previous render order,
716 // in case the window has been closed and reopened
718 List<String> frl = fr.getRenderOrder();
719 for (int ro = frl.size() - 1; ro > -1; ro--)
721 String type = frl.get(ro);
723 if (!displayableTypes.contains(type))
727 data[dataIndex][TYPE_COLUMN] = type;
728 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
729 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
730 data[dataIndex][FILTER_COLUMN] = featureFilter == null
731 ? new FeatureMatcherSet()
733 data[dataIndex][SHOW_COLUMN] = new Boolean(
734 af.getViewport().getFeaturesDisplayed().isVisible(type));
736 displayableTypes.remove(type);
741 * process any extra features belonging only to
742 * a group which was just selected
744 while (!displayableTypes.isEmpty())
746 String type = displayableTypes.iterator().next();
747 data[dataIndex][TYPE_COLUMN] = type;
749 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
750 if (data[dataIndex][COLOUR_COLUMN] == null)
752 // "Colour has been updated in another view!!"
753 fr.clearRenderOrder();
756 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
757 data[dataIndex][FILTER_COLUMN] = featureFilter == null
758 ? new FeatureMatcherSet()
760 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
762 displayableTypes.remove(type);
765 if (originalData == null)
767 originalData = new Object[data.length][COLUMN_COUNT];
768 for (int i = 0; i < data.length; i++)
770 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
775 updateOriginalData(data);
779 * recreate the table model
781 FeatureTableModel dataModel = new FeatureTableModel(data);
782 table.setModel(dataModel);
785 * we want to be able to filter out rows for sub-types, but not to sort
786 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
788 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
790 for (int i = 0; i < table.getColumnCount(); i++)
792 sorter.setSortable(i, false);
796 * filter rows to only top-level Ontology types if requested
798 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
801 public boolean include(
802 Entry<? extends FeatureTableModel, ? extends Integer> entry)
804 if (!summaryView.isSelected())
808 int row = entry.getIdentifier(); // this is model, not view, row number
809 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
810 return parents.contains(featureType);
813 table.setRowSorter(sorter);
815 table.getColumnModel().getColumn(0).setPreferredWidth(200);
817 groupPanel.setLayout(
818 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
819 pruneGroups(foundGroups);
820 groupPanel.validate();
822 updateFeatureRenderer(data, groupChanged != null);
823 resettingTable = false;
827 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
828 * have been made outwith this dialog
830 * <li>a new feature type added (and made visible)</li>
831 * <li>a feature colour changed (in the Amend Features dialog)</li>
836 protected void updateOriginalData(Object[][] foundData)
838 // todo LinkedHashMap instead of Object[][] would be nice
840 Object[][] currentData = ((FeatureTableModel) table.getModel())
842 for (Object[] row : foundData)
844 String type = (String) row[TYPE_COLUMN];
845 boolean found = false;
846 for (Object[] current : currentData)
848 if (type.equals(current[TYPE_COLUMN]))
852 * currently dependent on object equality here;
853 * really need an equals method on FeatureColour
855 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
858 * feature colour has changed externally - update originalData
860 for (Object[] original : originalData)
862 if (type.equals(original[TYPE_COLUMN]))
864 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
875 * new feature detected - add to original data (on top)
877 Object[][] newData = new Object[originalData.length
879 for (int i = 0; i < originalData.length; i++)
881 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
885 originalData = newData;
891 * Remove from the groups panel any checkboxes for groups that are not in the
892 * foundGroups set. This enables removing a group from the display when the last
893 * feature in that group is deleted.
897 protected void pruneGroups(Set<String> foundGroups)
899 for (int g = 0; g < groupPanel.getComponentCount(); g++)
901 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
902 if (!foundGroups.contains(checkbox.getText()))
904 groupPanel.remove(checkbox);
910 * reorder data based on the featureRenderers global priority list.
914 private void ensureOrder(Object[][] data)
916 boolean sort = false;
917 float[] order = new float[data.length];
918 for (int i = 0; i < order.length; i++)
920 order[i] = fr.getOrder(data[i][0].toString());
923 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
927 sort = sort || order[i - 1] > order[i];
932 jalview.util.QuickSort.sort(order, data);
937 * Offers a file chooser dialog, and then loads the feature colours and
938 * filters from file in XML format and unmarshals to Jalview feature settings
942 JalviewFileChooser chooser = new JalviewFileChooser("fc",
943 SEQUENCE_FEATURE_COLOURS);
944 chooser.setFileView(new JalviewFileView());
945 chooser.setDialogTitle(
946 MessageManager.getString("label.load_feature_colours"));
947 chooser.setToolTipText(MessageManager.getString("action.load"));
949 int value = chooser.showOpenDialog(this);
951 if (value == JalviewFileChooser.APPROVE_OPTION)
953 File file = chooser.getSelectedFile();
959 * Loads feature colours and filters from XML stored in the given file
967 InputStreamReader in = new InputStreamReader(
968 new FileInputStream(file), "UTF-8");
970 JAXBContext jc = JAXBContext
971 .newInstance("jalview.xml.binding.jalview");
972 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
973 XMLStreamReader streamReader = XMLInputFactory.newInstance()
974 .createXMLStreamReader(in);
975 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
976 JalviewUserColours.class);
977 JalviewUserColours jucs = jbe.getValue();
979 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
982 * load feature colours
984 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
986 Colour newcol = jucs.getColour().get(i);
987 FeatureColourI colour = jalview.project.Jalview2XML
988 .parseColour(newcol);
989 fr.setColour(newcol.getName(), colour);
990 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
994 * load feature filters; loaded filters will replace any that are
995 * currently defined, other defined filters are left unchanged
997 for (int i = 0; i < jucs.getFilter().size(); i++)
999 Filter filterModel = jucs.getFilter().get(i);
1000 String featureType = filterModel.getFeatureType();
1001 FeatureMatcherSetI filter = jalview.project.Jalview2XML
1002 .parseFilter(featureType, filterModel.getMatcherSet());
1003 if (!filter.isEmpty())
1005 fr.setFeatureFilter(featureType, filter);
1010 * update feature settings table
1015 Object[][] data = ((FeatureTableModel) table.getModel())
1018 updateFeatureRenderer(data, false);
1021 } catch (Exception ex)
1023 System.out.println("Error loading User Colour File\n" + ex);
1028 * Offers a file chooser dialog, and then saves the current feature colours
1029 * and any filters to the selected file in XML format
1033 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1034 SEQUENCE_FEATURE_COLOURS);
1035 chooser.setFileView(new JalviewFileView());
1036 chooser.setDialogTitle(
1037 MessageManager.getString("label.save_feature_colours"));
1038 chooser.setToolTipText(MessageManager.getString("action.save"));
1040 int value = chooser.showSaveDialog(this);
1042 if (value == JalviewFileChooser.APPROVE_OPTION)
1044 save(chooser.getSelectedFile());
1049 * Saves feature colours and filters to the given file
1053 void save(File file)
1055 JalviewUserColours ucs = new JalviewUserColours();
1056 ucs.setSchemeName("Sequence Features");
1059 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1060 new FileOutputStream(file), "UTF-8"));
1063 * sort feature types by colour order, from 0 (highest)
1066 Set<String> fr_colours = fr.getAllFeatureColours();
1067 String[] sortedTypes = fr_colours
1068 .toArray(new String[fr_colours.size()]);
1069 Arrays.sort(sortedTypes, new Comparator<String>()
1072 public int compare(String type1, String type2)
1074 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1079 * save feature colours
1081 for (String featureType : sortedTypes)
1083 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1084 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1086 ucs.getColour().add(col);
1090 * save any feature filters
1092 for (String featureType : sortedTypes)
1094 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1095 if (filter != null && !filter.isEmpty())
1097 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1098 FeatureMatcherI firstMatcher = iterator.next();
1099 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1100 .marshalFilter(firstMatcher, iterator,
1102 Filter filterModel = new Filter();
1103 filterModel.setFeatureType(featureType);
1104 filterModel.setMatcherSet(ms);
1105 ucs.getFilter().add(filterModel);
1108 JAXBContext jaxbContext = JAXBContext
1109 .newInstance(JalviewUserColours.class);
1110 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1111 jaxbMarshaller.marshal(
1112 new ObjectFactory().createJalviewUserColours(ucs), out);
1114 // jaxbMarshaller.marshal(object, pout);
1115 // marshaller.marshal(object);
1118 // ucs.marshal(out);
1120 } catch (Exception ex)
1122 ex.printStackTrace();
1126 public void invertSelection()
1128 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1129 for (int i = 0; i < data.length; i++)
1131 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1133 updateFeatureRenderer(data, true);
1137 public void orderByAvWidth()
1139 if (table == null || table.getModel() == null)
1143 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1144 float[] width = new float[data.length];
1148 for (int i = 0; i < data.length; i++)
1150 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1153 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1154 // weight - but have to make per
1155 // sequence, too (awidth[2])
1156 // if (width[i]==1) // hack to distinguish single width sequences.
1167 boolean sort = false;
1168 for (int i = 0; i < width.length; i++)
1170 // awidth = (float[]) typeWidth.get(data[i][0]);
1173 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1176 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1182 width[i] /= max; // normalize
1183 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1187 sort = sort || width[i - 1] > width[i];
1192 jalview.util.QuickSort.sort(width, data);
1193 // update global priority order
1196 updateFeatureRenderer(data, false);
1204 frame.setClosed(true);
1205 } catch (Exception exe)
1211 public void updateFeatureRenderer(Object[][] data)
1213 updateFeatureRenderer(data, true);
1217 * Update the priority order of features; only repaint if this changed the order
1218 * of visible features
1223 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1225 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1227 if (fr.setFeaturePriority(rowData, visibleNew))
1234 * Converts table data into an array of data beans
1236 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1238 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1239 for (int i = 0; i < data.length; i++)
1241 String type = (String) data[i][TYPE_COLUMN];
1242 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1243 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1244 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1245 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1251 private void jbInit() throws Exception
1253 this.setLayout(new BorderLayout());
1255 JPanel settingsPane = new JPanel();
1256 settingsPane.setLayout(new BorderLayout());
1258 JPanel bigPanel = new JPanel();
1259 bigPanel.setLayout(new BorderLayout());
1261 groupPanel = new JPanel();
1262 bigPanel.add(groupPanel, BorderLayout.NORTH);
1264 JButton invert = new JButton(
1265 MessageManager.getString("label.invert_selection"));
1266 invert.setFont(JvSwingUtils.getLabelFont());
1267 invert.addActionListener(new ActionListener()
1270 public void actionPerformed(ActionEvent e)
1276 JButton optimizeOrder = new JButton(
1277 MessageManager.getString("label.optimise_order"));
1278 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1279 optimizeOrder.addActionListener(new ActionListener()
1282 public void actionPerformed(ActionEvent e)
1288 JButton sortByScore = new JButton(
1289 MessageManager.getString("label.seq_sort_by_score"));
1290 sortByScore.setFont(JvSwingUtils.getLabelFont());
1291 sortByScore.addActionListener(new ActionListener()
1294 public void actionPerformed(ActionEvent e)
1296 af.avc.sortAlignmentByFeatureScore(null);
1299 JButton sortByDens = new JButton(
1300 MessageManager.getString("label.sequence_sort_by_density"));
1301 sortByDens.setFont(JvSwingUtils.getLabelFont());
1302 sortByDens.addActionListener(new ActionListener()
1305 public void actionPerformed(ActionEvent e)
1307 af.avc.sortAlignmentByFeatureDensity(null);
1311 JButton help = new JButton(MessageManager.getString("action.help"));
1312 help.setFont(JvSwingUtils.getLabelFont());
1313 help.addActionListener(new ActionListener()
1316 public void actionPerformed(ActionEvent e)
1320 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1321 } catch (HelpSetException e1)
1323 e1.printStackTrace();
1327 help.setFont(JvSwingUtils.getLabelFont());
1328 help.setText(MessageManager.getString("action.help"));
1329 help.addActionListener(new ActionListener()
1332 public void actionPerformed(ActionEvent e)
1336 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1337 } catch (HelpSetException e1)
1339 e1.printStackTrace();
1344 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1345 cancel.setFont(JvSwingUtils.getLabelFont());
1346 cancel.addActionListener(new ActionListener()
1349 public void actionPerformed(ActionEvent e)
1351 fr.setTransparency(originalTransparency);
1352 fr.setFeatureFilters(originalFilters);
1353 updateFeatureRenderer(originalData);
1354 af.getViewport().setViewStyle(originalViewStyle);
1359 JButton ok = new JButton(MessageManager.getString("action.ok"));
1360 ok.setFont(JvSwingUtils.getLabelFont());
1361 ok.addActionListener(new ActionListener()
1364 public void actionPerformed(ActionEvent e)
1370 JButton loadColours = new JButton(
1371 MessageManager.getString("label.load_colours"));
1372 loadColours.setFont(JvSwingUtils.getLabelFont());
1373 loadColours.setToolTipText(
1374 MessageManager.getString("label.load_colours_tooltip"));
1375 loadColours.addActionListener(new ActionListener()
1378 public void actionPerformed(ActionEvent e)
1384 JButton saveColours = new JButton(
1385 MessageManager.getString("label.save_colours"));
1386 saveColours.setFont(JvSwingUtils.getLabelFont());
1387 saveColours.setToolTipText(
1388 MessageManager.getString("label.save_colours_tooltip"));
1389 saveColours.addActionListener(new ActionListener()
1392 public void actionPerformed(ActionEvent e)
1397 transparency.addChangeListener(new ChangeListener()
1400 public void stateChanged(ChangeEvent evt)
1402 if (!inConstruction)
1404 fr.setTransparency((100 - transparency.getValue()) / 100f);
1410 summaryView = new JCheckBox(
1411 MessageManager.getString("label.summary_view"));
1414 MessageManager.getString("label.summary_view_tip"));
1415 summaryView.addActionListener(new ActionListener()
1418 public void actionPerformed(ActionEvent e)
1424 transparency.setMaximum(70);
1425 transparency.setToolTipText(
1426 MessageManager.getString("label.transparency_tip"));
1428 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1429 showComplement = new JCheckBox(
1430 "Show " + (nucleotide ? "protein" : "CDS") + " features");
1431 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
1432 showComplement.addActionListener(new ActionListener()
1435 public void actionPerformed(ActionEvent e)
1438 .setShowComplementFeatures(showComplement.isSelected());
1443 showComplementOnTop = new JCheckBox("on top");
1445 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
1446 showComplementOnTop.addActionListener(new ActionListener()
1449 public void actionPerformed(ActionEvent e)
1451 af.getViewport().setShowComplementFeaturesOnTop(
1452 showComplementOnTop.isSelected());
1457 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1458 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1460 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1461 transbuttons.add(optimizeOrder);
1462 transbuttons.add(invert);
1463 transbuttons.add(sortByScore);
1464 transbuttons.add(sortByDens);
1465 transbuttons.add(help);
1467 boolean hasComplement = af.getViewport().getCodingComplement() != null;
1468 JPanel transPanelLeft = new JPanel(
1469 new GridLayout(hasComplement ? 4 : 3, 1));
1470 transPanelLeft.add(summaryView);
1471 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1472 transPanelLeft.add(transparency);
1475 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1476 cp.add(showComplement);
1477 cp.add(showComplementOnTop);
1478 transPanelLeft.add(cp);
1480 lowerPanel.add(transPanelLeft);
1481 lowerPanel.add(transbuttons);
1483 JPanel buttonPanel = new JPanel();
1484 buttonPanel.add(ok);
1485 buttonPanel.add(cancel);
1486 buttonPanel.add(loadColours);
1487 buttonPanel.add(saveColours);
1488 bigPanel.add(scrollPane, BorderLayout.CENTER);
1489 settingsPane.add(bigPanel, BorderLayout.CENTER);
1490 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1491 this.add(settingsPane);
1495 * Repaints alignment, structure and overview (if shown). If there is a
1496 * complementary view which is showing this view's features, then also
1499 void refreshDisplay()
1501 af.alignPanel.paintAlignment(true, true);
1502 AlignViewportI complement = af.getViewport().getCodingComplement();
1503 if (complement != null && complement.isShowComplementFeatures())
1505 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1506 af2.alignPanel.paintAlignment(true, true);
1511 * Reorders features by 'dragging' selectedRow to 'newRow'
1515 protected void dragRow(int newRow)
1517 if (summaryView.isSelected())
1519 // no drag while in summary view
1523 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1526 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1527 * this could be more than one row away for a very fast drag action
1528 * so just swap it with adjacent rows until we get it there
1530 Object[][] data = ((FeatureTableModel) table.getModel())
1532 int direction = newRow < selectedRow ? -1 : 1;
1533 for (int i = selectedRow; i != newRow; i += direction)
1535 Object[] temp = data[i];
1536 data[i] = data[i + direction];
1537 data[i + direction] = temp;
1539 updateFeatureRenderer(data);
1541 selectedRow = newRow;
1545 protected void refreshTable()
1547 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1548 for (Object[] row : data)
1550 String type = (String) row[TYPE_COLUMN];
1551 FeatureColourI colour = fr.getFeatureColours().get(type);
1552 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1555 filter = new FeatureMatcherSet();
1557 row[COLOUR_COLUMN] = colour;
1558 row[FILTER_COLUMN] = filter;
1563 // ///////////////////////////////////////////////////////////////////////
1564 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1565 // ///////////////////////////////////////////////////////////////////////
1566 class FeatureTableModel extends AbstractTableModel
1568 private String[] columnNames = {
1569 MessageManager.getString("label.feature_type"),
1570 MessageManager.getString("action.colour"),
1571 MessageManager.getString("label.configuration"),
1572 MessageManager.getString("label.show") };
1574 private Object[][] data;
1576 FeatureTableModel(Object[][] data)
1581 public Object[][] getData()
1586 public void setData(Object[][] data)
1592 public int getColumnCount()
1594 return columnNames.length;
1597 public Object[] getRow(int row)
1603 public int getRowCount()
1609 public String getColumnName(int col)
1611 return columnNames[col];
1615 public Object getValueAt(int row, int col)
1617 return data[row][col];
1621 * Answers the class of column c of the table
1624 public Class<?> getColumnClass(int c)
1629 return String.class;
1631 return FeatureColour.class;
1633 return FeatureMatcherSet.class;
1635 return Boolean.class;
1640 * Answers true for all columns except Feature Type
1643 public boolean isCellEditable(int row, int col)
1645 return col != TYPE_COLUMN;
1649 * Sets the value in the model for a given row and column. If Visibility
1650 * (Show/Hide) is being set, and the table is in Summary View, then it is
1651 * set also on any sub-types of the row's feature type.
1654 public void setValueAt(Object value, int row, int col)
1656 data[row][col] = value;
1657 fireTableCellUpdated(row, col);
1658 if (summaryView.isSelected() && col == SHOW_COLUMN)
1660 setSubtypesVisibility(row, (Boolean) value);
1662 updateFeatureRenderer(data);
1666 * Sets the visibility of any feature types which are sub-types of the type
1667 * in the given row of the table
1672 protected void setSubtypesVisibility(int row, Boolean value)
1674 String type = (String) data[row][TYPE_COLUMN];
1675 OntologyI so = SequenceOntologyFactory.getInstance();
1677 for (int r = 0; r < data.length; r++)
1681 String type2 = (String) data[r][TYPE_COLUMN];
1682 if (so.isA(type2, type))
1684 data[r][SHOW_COLUMN] = value;
1685 fireTableCellUpdated(r, SHOW_COLUMN);
1692 class ColorRenderer extends JLabel implements TableCellRenderer
1694 javax.swing.border.Border unselectedBorder = null;
1696 javax.swing.border.Border selectedBorder = null;
1698 final String baseTT = "Click to edit, right/apple click for menu.";
1700 public ColorRenderer()
1702 setOpaque(true); // MUST do this for background to show up.
1703 setHorizontalTextPosition(SwingConstants.CENTER);
1704 setVerticalTextPosition(SwingConstants.CENTER);
1708 public Component getTableCellRendererComponent(JTable tbl, Object color,
1709 boolean isSelected, boolean hasFocus, int row, int column)
1711 FeatureColourI cellColour = (FeatureColourI) color;
1713 setToolTipText(baseTT);
1714 setBackground(tbl.getBackground());
1715 if (!cellColour.isSimpleColour())
1717 Rectangle cr = tbl.getCellRect(row, column, false);
1718 FeatureSettings.renderGraduatedColor(this, cellColour,
1719 (int) cr.getWidth(), (int) cr.getHeight());
1725 setBackground(cellColour.getColour());
1729 if (selectedBorder == null)
1731 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1732 tbl.getSelectionBackground());
1734 setBorder(selectedBorder);
1738 if (unselectedBorder == null)
1740 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1741 tbl.getBackground());
1743 setBorder(unselectedBorder);
1750 class FilterRenderer extends JLabel implements TableCellRenderer
1752 javax.swing.border.Border unselectedBorder = null;
1754 javax.swing.border.Border selectedBorder = null;
1756 public FilterRenderer()
1758 setOpaque(true); // MUST do this for background to show up.
1759 setHorizontalTextPosition(SwingConstants.CENTER);
1760 setVerticalTextPosition(SwingConstants.CENTER);
1764 public Component getTableCellRendererComponent(JTable tbl,
1765 Object filter, boolean isSelected, boolean hasFocus, int row,
1768 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1770 String asText = theFilter.toString();
1771 setBackground(tbl.getBackground());
1772 this.setText(asText);
1777 if (selectedBorder == null)
1779 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1780 tbl.getSelectionBackground());
1782 setBorder(selectedBorder);
1786 if (unselectedBorder == null)
1788 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1789 tbl.getBackground());
1791 setBorder(unselectedBorder);
1799 * update comp using rendering settings from gcol
1804 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1806 int w = comp.getWidth(), h = comp.getHeight();
1809 w = (int) comp.getPreferredSize().getWidth();
1810 h = (int) comp.getPreferredSize().getHeight();
1817 renderGraduatedColor(comp, gcol, w, h);
1820 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1823 boolean thr = false;
1824 StringBuilder tt = new StringBuilder();
1825 StringBuilder tx = new StringBuilder();
1827 if (gcol.isColourByAttribute())
1829 tx.append(String.join(":", gcol.getAttributeName()));
1831 else if (!gcol.isColourByLabel())
1833 tx.append(MessageManager.getString("label.score"));
1836 if (gcol.isAboveThreshold())
1840 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1843 if (gcol.isBelowThreshold())
1847 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1850 if (gcol.isColourByLabel())
1852 tt.append("Coloured by label text. ").append(tt);
1857 if (!gcol.isColourByAttribute())
1865 Color newColor = gcol.getMaxColour();
1866 comp.setBackground(newColor);
1867 // System.err.println("Width is " + w / 2);
1868 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1869 comp.setIcon(ficon);
1870 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1871 // + newColor.getGreen() + ", " + newColor.getBlue()
1872 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1873 // + ", " + minCol.getBlue() + ")");
1875 comp.setHorizontalAlignment(SwingConstants.CENTER);
1876 comp.setText(tx.toString());
1877 if (tt.length() > 0)
1879 if (comp.getToolTipText() == null)
1881 comp.setToolTipText(tt.toString());
1885 comp.setToolTipText(
1886 tt.append(" ").append(comp.getToolTipText()).toString());
1891 class ColorEditor extends AbstractCellEditor
1892 implements TableCellEditor, ActionListener
1896 FeatureColourI currentColor;
1898 FeatureTypeSettings chooser;
1902 JButton colourButton;
1904 JColorChooser colorChooser;
1908 protected static final String EDIT = "edit";
1910 int rowSelected = 0;
1912 public ColorEditor(FeatureSettings me)
1915 // Set up the editor (from the table's point of view),
1916 // which is a button.
1917 // This button brings up the color chooser dialog,
1918 // which is the editor from the user's point of view.
1919 colourButton = new JButton();
1920 colourButton.setActionCommand(EDIT);
1921 colourButton.addActionListener(this);
1922 colourButton.setBorderPainted(false);
1923 // Set up the dialog that the button brings up.
1924 colorChooser = new JColorChooser();
1925 dialog = JColorChooser.createDialog(colourButton,
1926 MessageManager.getString("label.select_colour"), true, // modal
1927 colorChooser, this, // OK button handler
1928 null); // no CANCEL button handler
1932 * Handles events from the editor button and from the dialog's OK button.
1935 public void actionPerformed(ActionEvent e)
1937 // todo test e.getSource() instead here
1938 if (EDIT.equals(e.getActionCommand()))
1940 // The user has clicked the cell, so
1941 // bring up the dialog.
1942 if (currentColor.isSimpleColour())
1944 // bring up simple color chooser
1945 colourButton.setBackground(currentColor.getColour());
1946 colorChooser.setColor(currentColor.getColour());
1947 dialog.setVisible(true);
1951 // bring up graduated chooser.
1952 chooser = new FeatureTypeSettings(me.fr, type);
1953 chooser.setRequestFocusEnabled(true);
1954 chooser.requestFocus();
1955 chooser.addActionListener(this);
1956 // Make the renderer reappear.
1957 fireEditingStopped();
1962 if (currentColor.isSimpleColour())
1965 * read off colour picked in colour chooser after OK pressed
1967 currentColor = new FeatureColour(colorChooser.getColor());
1968 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1973 * after OK in variable colour dialog, any changes to colour
1974 * (or filters!) are already set in FeatureRenderer, so just
1975 * update table data without triggering updateFeatureRenderer
1979 fireEditingStopped();
1980 me.table.validate();
1984 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1986 public Object getCellEditorValue()
1988 return currentColor;
1991 // Implement the one method defined by TableCellEditor.
1993 public Component getTableCellEditorComponent(JTable theTable, Object value,
1994 boolean isSelected, int row, int column)
1996 currentColor = (FeatureColourI) value;
1997 this.rowSelected = row;
1998 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1999 colourButton.setOpaque(true);
2000 colourButton.setBackground(me.getBackground());
2001 if (!currentColor.isSimpleColour())
2003 JLabel btn = new JLabel();
2004 btn.setSize(colourButton.getSize());
2005 FeatureSettings.renderGraduatedColor(btn, currentColor);
2006 colourButton.setBackground(btn.getBackground());
2007 colourButton.setIcon(btn.getIcon());
2008 colourButton.setText(btn.getText());
2012 colourButton.setText("");
2013 colourButton.setIcon(null);
2014 colourButton.setBackground(currentColor.getColour());
2016 return colourButton;
2021 * The cell editor for the Filter column. It displays the text of any filters
2022 * for the feature type in that row (in full as a tooltip, possible abbreviated
2023 * as display text). On click in the cell, opens the Feature Display Settings
2024 * dialog at the Filters tab.
2026 class FilterEditor extends AbstractCellEditor
2027 implements TableCellEditor, ActionListener
2031 FeatureMatcherSetI currentFilter;
2037 JButton filterButton;
2039 protected static final String EDIT = "edit";
2041 int rowSelected = 0;
2043 public FilterEditor(FeatureSettings me)
2046 filterButton = new JButton();
2047 filterButton.setActionCommand(EDIT);
2048 filterButton.addActionListener(this);
2049 filterButton.setBorderPainted(false);
2053 * Handles events from the editor button
2056 public void actionPerformed(ActionEvent e)
2058 if (filterButton == e.getSource())
2060 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2061 chooser.addActionListener(this);
2062 chooser.setRequestFocusEnabled(true);
2063 chooser.requestFocus();
2064 if (lastLocation != null)
2066 // todo open at its last position on screen
2067 chooser.setBounds(lastLocation.x, lastLocation.y,
2068 chooser.getWidth(), chooser.getHeight());
2071 fireEditingStopped();
2073 else if (e.getSource() instanceof Component)
2076 * after OK in variable colour dialog, any changes to filter
2077 * (or colours!) are already set in FeatureRenderer, so just
2078 * update table data without triggering updateFeatureRenderer
2081 fireEditingStopped();
2082 me.table.validate();
2087 public Object getCellEditorValue()
2089 return currentFilter;
2093 public Component getTableCellEditorComponent(JTable theTable, Object value,
2094 boolean isSelected, int row, int column)
2096 currentFilter = (FeatureMatcherSetI) value;
2097 this.rowSelected = row;
2098 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2099 filterButton.setOpaque(true);
2100 filterButton.setBackground(me.getBackground());
2101 filterButton.setText(currentFilter.toString());
2102 filterButton.setToolTipText(currentFilter.toString());
2103 filterButton.setIcon(null);
2104 return filterButton;
2109 class FeatureIcon implements Icon
2111 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2113 FeatureColourI gcol;
2117 boolean midspace = false;
2119 int width = 50, height = 20;
2121 int s1, e1; // start and end of midpoint band for thresholded symbol
2123 Color mpcolour = Color.white;
2125 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2145 public int getIconWidth()
2151 public int getIconHeight()
2157 public void paintIcon(Component c, Graphics g, int x, int y)
2160 if (gcol.isColourByLabel())
2163 g.fillRect(0, 0, width, height);
2164 // need an icon here.
2165 g.setColor(gcol.getMaxColour());
2167 g.setFont(VERDANA_9);
2169 // g.setFont(g.getFont().deriveFont(
2170 // AffineTransform.getScaleInstance(
2171 // width/g.getFontMetrics().stringWidth("Label"),
2172 // height/g.getFontMetrics().getHeight())));
2174 g.drawString(MessageManager.getString("label.label"), 0, 0);
2179 Color minCol = gcol.getMinColour();
2181 g.fillRect(0, 0, s1, height);
2184 g.setColor(Color.white);
2185 g.fillRect(s1, 0, e1 - s1, height);
2187 g.setColor(gcol.getMaxColour());
2188 g.fillRect(0, e1, width - e1, height);