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.datamodel.AlignmentI;
27 import jalview.datamodel.SequenceI;
28 import jalview.datamodel.features.FeatureMatcherI;
29 import jalview.datamodel.features.FeatureMatcherSet;
30 import jalview.datamodel.features.FeatureMatcherSetI;
31 import jalview.datamodel.ontology.OntologyI;
32 import jalview.gui.Help.HelpId;
33 import jalview.io.JalviewFileChooser;
34 import jalview.io.JalviewFileView;
35 import jalview.io.gff.SequenceOntologyFactory;
36 import jalview.schemes.FeatureColour;
37 import jalview.util.MessageManager;
38 import jalview.util.Platform;
39 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
40 import jalview.xml.binding.jalview.JalviewUserColours;
41 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
42 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
43 import jalview.xml.binding.jalview.ObjectFactory;
45 import java.awt.BorderLayout;
46 import java.awt.Color;
47 import java.awt.Component;
48 import java.awt.Dimension;
49 import java.awt.FlowLayout;
51 import java.awt.Graphics;
52 import java.awt.GridLayout;
53 import java.awt.Point;
54 import java.awt.Rectangle;
55 import java.awt.event.ActionEvent;
56 import java.awt.event.ActionListener;
57 import java.awt.event.ItemEvent;
58 import java.awt.event.ItemListener;
59 import java.awt.event.MouseAdapter;
60 import java.awt.event.MouseEvent;
61 import java.awt.event.MouseMotionAdapter;
62 import java.beans.PropertyChangeEvent;
63 import java.beans.PropertyChangeListener;
65 import java.io.FileInputStream;
66 import java.io.FileOutputStream;
67 import java.io.InputStreamReader;
68 import java.io.OutputStreamWriter;
69 import java.io.PrintWriter;
70 import java.util.ArrayList;
71 import java.util.Arrays;
72 import java.util.Comparator;
73 import java.util.HashMap;
74 import java.util.HashSet;
75 import java.util.Hashtable;
76 import java.util.Iterator;
77 import java.util.List;
81 import javax.help.HelpSetException;
82 import javax.swing.AbstractCellEditor;
83 import javax.swing.BorderFactory;
84 import javax.swing.Icon;
85 import javax.swing.JButton;
86 import javax.swing.JCheckBox;
87 import javax.swing.JCheckBoxMenuItem;
88 import javax.swing.JColorChooser;
89 import javax.swing.JDialog;
90 import javax.swing.JInternalFrame;
91 import javax.swing.JLabel;
92 import javax.swing.JLayeredPane;
93 import javax.swing.JMenuItem;
94 import javax.swing.JPanel;
95 import javax.swing.JPopupMenu;
96 import javax.swing.JScrollPane;
97 import javax.swing.JSlider;
98 import javax.swing.JTable;
99 import javax.swing.ListSelectionModel;
100 import javax.swing.RowFilter;
101 import javax.swing.SwingConstants;
102 import javax.swing.event.ChangeEvent;
103 import javax.swing.event.ChangeListener;
104 import javax.swing.table.AbstractTableModel;
105 import javax.swing.table.TableCellEditor;
106 import javax.swing.table.TableCellRenderer;
107 import javax.swing.table.TableColumn;
108 import javax.swing.table.TableRowSorter;
109 import javax.xml.bind.JAXBContext;
110 import javax.xml.bind.JAXBElement;
111 import javax.xml.bind.Marshaller;
112 import javax.xml.stream.XMLInputFactory;
113 import javax.xml.stream.XMLStreamReader;
115 public class FeatureSettings extends JPanel
116 implements FeatureSettingsControllerI
118 private static final Font VERDANA_12 = new Font("Verdana", Font.PLAIN, 12);
120 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
121 .getString("label.sequence_feature_colours");
124 * column indices of fields in Feature Settings table
126 static final int TYPE_COLUMN = 0;
128 static final int COLOUR_COLUMN = 1;
130 static final int FILTER_COLUMN = 2;
132 static final int SHOW_COLUMN = 3;
134 private static final int COLUMN_COUNT = 4;
136 private static final int MIN_WIDTH = 400;
138 private static final int MIN_HEIGHT = 400;
140 final FeatureRenderer fr;
142 public final AlignFrame af;
145 * 'original' fields hold settings to restore on Cancel
147 Object[][] originalData;
149 private float originalTransparency;
151 private Map<String, FeatureMatcherSetI> originalFilters;
153 final JInternalFrame frame;
155 JScrollPane scrollPane = new JScrollPane();
161 JSlider transparency = new JSlider();
163 JCheckBox showComplement;
165 JCheckBox showComplementOnTop;
168 * when true, constructor is still executing - so ignore UI events
170 protected volatile boolean inConstruction = true;
172 int selectedRow = -1;
174 boolean resettingTable = false;
177 * true when Feature Settings are updating from feature renderer
179 private boolean handlingUpdate = false;
182 * holds {featureCount, totalExtent} for each feature type
184 Map<String, float[]> typeWidth = null;
187 * if true, 'child' feature types are not displayed
189 JCheckBox summaryView;
192 * those feature types that do not have a parent feature type present
193 * (as determined by an Ontology relationship)
195 List<String> topLevelTypes;
202 public FeatureSettings(AlignFrame alignFrame)
204 this.af = alignFrame;
205 fr = af.getFeatureRenderer();
207 // save transparency for restore on Cancel
208 originalTransparency = fr.getTransparency();
209 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
210 transparency.setMaximum(100 - originalTransparencyAsPercent);
212 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
214 topLevelTypes = new ArrayList<>();
219 } catch (Exception ex)
221 ex.printStackTrace();
226 scrollPane.setViewportView(table);
228 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
230 fr.findAllFeatures(true); // display everything!
233 discoverAllFeatureData();
234 final PropertyChangeListener change;
235 final FeatureSettings fs = this;
236 fr.addPropertyChangeListener(change = new PropertyChangeListener()
239 public void propertyChange(PropertyChangeEvent evt)
241 if (!fs.resettingTable && !fs.handlingUpdate)
243 fs.handlingUpdate = true;
245 // new groups may be added with new sequence feature types only
246 fs.handlingUpdate = false;
251 frame = new JInternalFrame();
252 frame.setContentPane(this);
253 if (Platform.isAMac())
255 Desktop.addInternalFrame(frame,
256 MessageManager.getString("label.sequence_feature_settings"),
261 Desktop.addInternalFrame(frame,
262 MessageManager.getString("label.sequence_feature_settings"),
265 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
267 frame.addInternalFrameListener(
268 new javax.swing.event.InternalFrameAdapter()
271 public void internalFrameClosed(
272 javax.swing.event.InternalFrameEvent evt)
274 fr.removePropertyChangeListener(change);
277 frame.setLayer(JLayeredPane.PALETTE_LAYER);
278 inConstruction = false;
282 * Constructs and configures the JTable which displays columns of data for
285 protected void initTable()
290 public String getToolTipText(MouseEvent e)
293 int column = table.columnAtPoint(e.getPoint());
298 * drag to reorder not enabled in Summary View
300 tip = summaryView.isSelected()
301 ? MessageManager.getString(
302 "label.feature_settings_select_columns")
303 : JvSwingUtils.wrapTooltip(true, MessageManager
304 .getString("label.feature_settings_click_drag"));
307 int row = table.rowAtPoint(e.getPoint());
308 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
312 .getString("label.configure_feature_tooltip")
321 table.getTableHeader().setFont(VERDANA_12);
322 table.setFont(VERDANA_12);
324 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
325 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
327 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
328 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
330 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
331 new ColorRenderer(), new ColorEditor(this));
332 table.addColumn(colourColumn);
334 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
335 new FilterRenderer(), new FilterEditor(this));
336 table.addColumn(filterColumn);
338 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
340 table.addMouseListener(new MouseAdapter()
343 public void mousePressed(MouseEvent evt)
345 selectedRow = table.rowAtPoint(evt.getPoint());
346 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
347 if (evt.isPopupTrigger())
349 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
350 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
352 else if (evt.getClickCount() == 2)
354 boolean invertSelection = evt.isAltDown();
355 boolean toggleSelection = Platform.isControlDown(evt);
356 boolean extendSelection = evt.isShiftDown();
357 String[] terms = getTermsInScope(type);
358 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
359 invertSelection, extendSelection, toggleSelection, terms);
363 // isPopupTrigger fires on mouseReleased on Windows
365 public void mouseReleased(MouseEvent evt)
367 selectedRow = table.rowAtPoint(evt.getPoint());
368 if (evt.isPopupTrigger())
370 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
371 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
372 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
377 table.addMouseMotionListener(new MouseMotionAdapter()
380 public void mouseDragged(MouseEvent evt)
382 int newRow = table.rowAtPoint(evt.getPoint());
389 * Answers an array consisting of the given type, and also (if 'Summary View'
390 * is selected), any child terms in the sequence ontology
395 protected String[] getTermsInScope(String type)
397 if (!summaryView.isSelected())
399 return new String[] { type };
402 List<String> terms = new ArrayList<>();
405 OntologyI so = SequenceOntologyFactory.getInstance();
407 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
408 for (Object[] row : data)
410 String type2 = (String) row[TYPE_COLUMN];
411 if (!type2.equals(type) && so.isA(type2, type))
416 return terms.toArray(new String[terms.size()]);
419 protected void popupMenu(final int rowSelected, final String type,
420 final Object typeCol, int x, int y)
422 final FeatureColourI featureColour = (FeatureColourI) typeCol;
424 JPopupMenu men = new JPopupMenu(MessageManager
425 .formatMessage("label.settings_for_param", new String[]
427 JMenuItem scr = new JMenuItem(
428 MessageManager.getString("label.sort_by_score"));
430 final FeatureSettings me = this;
431 scr.addActionListener(new ActionListener()
434 public void actionPerformed(ActionEvent e)
436 String[] types = getTermsInScope(type);
437 me.af.avc.sortAlignmentByFeatureScore(Arrays.asList(types));
440 JMenuItem dens = new JMenuItem(
441 MessageManager.getString("label.sort_by_density"));
442 dens.addActionListener(new ActionListener()
445 public void actionPerformed(ActionEvent e)
447 String[] types = getTermsInScope(type);
448 me.af.avc.sortAlignmentByFeatureDensity(Arrays.asList(types));
453 // fixme is Variable Colour in popup menu or not
455 * variable colour options include colour by label, by score,
456 * by selected attribute text, or attribute value
458 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
459 MessageManager.getString("label.variable_colour"));
460 mxcol.setSelected(!featureColour.isSimpleColour());
462 mxcol.addActionListener(new ActionListener()
464 JColorChooser colorChooser;
467 public void actionPerformed(ActionEvent e)
469 if (e.getSource() == mxcol)
471 if (featureColour.isSimpleColour())
473 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
474 fc.addActionListener(this);
478 // bring up simple color chooser
479 colorChooser = new JColorChooser();
480 String title = MessageManager
481 .getString("label.select_colour");
482 JDialog dialog = JColorChooser.createDialog(me,
483 title, true, // modal
484 colorChooser, this, // OK button handler
485 null); // no CANCEL button handler
486 colorChooser.setColor(featureColour.getMaxColour());
487 dialog.setVisible(true);
492 if (e.getSource() instanceof FeatureTypeSettings)
495 * update after OK in feature colour dialog; the updated
496 * colour will have already been set in the FeatureRenderer
498 FeatureColourI fci = fr.getFeatureColours().get(type);
499 table.setValueAt(fci, rowSelected, 1);
504 // probably the color chooser!
505 table.setValueAt(new FeatureColour(colorChooser.getColor()),
508 me.updateFeatureRenderer(
509 ((FeatureTableModel) table.getModel()).getData(),
516 JMenuItem selCols = new JMenuItem(
517 MessageManager.getString("label.select_columns_containing"));
518 selCols.addActionListener(new ActionListener()
521 public void actionPerformed(ActionEvent arg0)
523 String[] types = getTermsInScope(type);
524 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
528 JMenuItem clearCols = new JMenuItem(MessageManager
529 .getString("label.select_columns_not_containing"));
530 clearCols.addActionListener(new ActionListener()
533 public void actionPerformed(ActionEvent arg0)
535 String[] types = getTermsInScope(type);
536 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
540 JMenuItem hideCols = new JMenuItem(
541 MessageManager.getString("label.hide_columns_containing"));
542 hideCols.addActionListener(new ActionListener()
545 public void actionPerformed(ActionEvent arg0)
547 String[] types = getTermsInScope(type);
548 fr.ap.alignFrame.hideFeatureColumns(true, types);
551 JMenuItem hideOtherCols = new JMenuItem(
552 MessageManager.getString("label.hide_columns_not_containing"));
553 hideOtherCols.addActionListener(new ActionListener()
556 public void actionPerformed(ActionEvent arg0)
558 String[] types = getTermsInScope(type);
559 fr.ap.alignFrame.hideFeatureColumns(false, types);
565 men.add(hideOtherCols);
566 men.show(table, x, y);
570 synchronized public void discoverAllFeatureData()
572 Set<String> allGroups = new HashSet<>();
573 AlignmentI alignment = af.getViewport().getAlignment();
575 for (int i = 0; i < alignment.getHeight(); i++)
577 SequenceI seq = alignment.getSequenceAt(i);
578 for (String group : seq.getFeatures().getFeatureGroups(true))
580 if (group != null && !allGroups.contains(group))
582 allGroups.add(group);
583 checkGroupState(group);
594 * Synchronise gui group list and check visibility of group
597 * @return true if group is visible
599 private boolean checkGroupState(String group)
601 boolean visible = fr.checkGroupVisibility(group, true);
603 for (int g = 0; g < groupPanel.getComponentCount(); g++)
605 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
607 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
612 final String grp = group;
613 final JCheckBox check = new JCheckBox(group, visible);
614 check.setFont(new Font("Serif", Font.BOLD, 12));
615 check.setToolTipText(group);
616 check.addItemListener(new ItemListener()
619 public void itemStateChanged(ItemEvent evt)
621 fr.setGroupVisibility(check.getText(), check.isSelected());
622 resetTable(new String[] { grp });
626 groupPanel.add(check);
630 synchronized void resetTable(String[] groupChanged)
636 resettingTable = true;
637 typeWidth = new Hashtable<>();
638 // TODO: change avWidth calculation to 'per-sequence' average and use long
641 Set<String> displayableTypes = new HashSet<>();
642 Set<String> foundGroups = new HashSet<>();
645 * determine which feature types may be visible depending on
646 * which groups are selected, and recompute average width data
648 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
651 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
654 * get the sequence's groups for positional features
655 * and keep track of which groups are visible
657 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
658 Set<String> visibleGroups = new HashSet<>();
659 for (String group : groups)
661 if (group == null || checkGroupState(group))
663 visibleGroups.add(group);
666 foundGroups.addAll(groups);
669 * get distinct feature types for visible groups
670 * record distinct visible types, and their count and total length
672 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
673 visibleGroups.toArray(new String[visibleGroups.size()]));
675 for (String type : types)
677 displayableTypes.add(type);
678 float[] avWidth = typeWidth.get(type);
681 avWidth = new float[2];
682 typeWidth.put(type, avWidth);
684 // todo this could include features with a non-visible group
685 // - do we greatly care?
686 // todo should we include non-displayable features here, and only
687 // update when features are added?
688 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
689 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
694 * enable 'Summary View' if some types are sub-types of others
696 Set<String> parents = SequenceOntologyFactory.getInstance()
697 .getParentTerms(displayableTypes);
698 summaryView.setEnabled(parents.size() < displayableTypes.size());
700 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
703 if (fr.hasRenderOrder())
707 fr.findAllFeatures(groupChanged != null); // prod to update
708 // colourschemes. but don't
710 // First add the checks in the previous render order,
711 // in case the window has been closed and reopened
713 List<String> frl = fr.getRenderOrder();
714 for (int ro = frl.size() - 1; ro > -1; ro--)
716 String type = frl.get(ro);
718 if (!displayableTypes.contains(type))
722 data[dataIndex][TYPE_COLUMN] = type;
723 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
724 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
725 data[dataIndex][FILTER_COLUMN] = featureFilter == null
726 ? new FeatureMatcherSet()
728 data[dataIndex][SHOW_COLUMN] = new Boolean(
729 af.getViewport().getFeaturesDisplayed().isVisible(type));
731 displayableTypes.remove(type);
736 * process any extra features belonging only to
737 * a group which was just selected
739 while (!displayableTypes.isEmpty())
741 String type = displayableTypes.iterator().next();
742 data[dataIndex][TYPE_COLUMN] = type;
744 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
745 if (data[dataIndex][COLOUR_COLUMN] == null)
747 // "Colour has been updated in another view!!"
748 fr.clearRenderOrder();
751 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
752 data[dataIndex][FILTER_COLUMN] = featureFilter == null
753 ? new FeatureMatcherSet()
755 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
757 displayableTypes.remove(type);
760 if (originalData == null)
762 originalData = new Object[data.length][COLUMN_COUNT];
763 for (int i = 0; i < data.length; i++)
765 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
770 updateOriginalData(data);
774 * recreate the table model
776 FeatureTableModel dataModel = new FeatureTableModel(data);
777 table.setModel(dataModel);
780 * we want to be able to filter out rows for sub-types, but not to sort
781 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
783 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
785 for (int i = 0; i < table.getColumnCount(); i++)
787 sorter.setSortable(i, false);
791 * filter rows to only top-level Ontology types if requested
793 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
796 public boolean include(
797 Entry<? extends FeatureTableModel, ? extends Integer> entry)
799 if (!summaryView.isSelected())
803 int row = entry.getIdentifier(); // this is model, not view, row number
804 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
805 return parents.contains(featureType);
808 table.setRowSorter(sorter);
810 table.getColumnModel().getColumn(0).setPreferredWidth(200);
812 groupPanel.setLayout(
813 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
814 pruneGroups(foundGroups);
815 groupPanel.validate();
817 updateFeatureRenderer(data, groupChanged != null);
818 resettingTable = false;
822 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
823 * have been made outwith this dialog
825 * <li>a new feature type added (and made visible)</li>
826 * <li>a feature colour changed (in the Amend Features dialog)</li>
831 protected void updateOriginalData(Object[][] foundData)
833 // todo LinkedHashMap instead of Object[][] would be nice
835 Object[][] currentData = ((FeatureTableModel) table.getModel())
837 for (Object[] row : foundData)
839 String type = (String) row[TYPE_COLUMN];
840 boolean found = false;
841 for (Object[] current : currentData)
843 if (type.equals(current[TYPE_COLUMN]))
847 * currently dependent on object equality here;
848 * really need an equals method on FeatureColour
850 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
853 * feature colour has changed externally - update originalData
855 for (Object[] original : originalData)
857 if (type.equals(original[TYPE_COLUMN]))
859 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
870 * new feature detected - add to original data (on top)
872 Object[][] newData = new Object[originalData.length
874 for (int i = 0; i < originalData.length; i++)
876 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
880 originalData = newData;
886 * Remove from the groups panel any checkboxes for groups that are not in the
887 * foundGroups set. This enables removing a group from the display when the last
888 * feature in that group is deleted.
892 protected void pruneGroups(Set<String> foundGroups)
894 for (int g = 0; g < groupPanel.getComponentCount(); g++)
896 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
897 if (!foundGroups.contains(checkbox.getText()))
899 groupPanel.remove(checkbox);
905 * reorder data based on the featureRenderers global priority list.
909 private void ensureOrder(Object[][] data)
911 boolean sort = false;
912 float[] order = new float[data.length];
913 for (int i = 0; i < order.length; i++)
915 order[i] = fr.getOrder(data[i][0].toString());
918 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
922 sort = sort || order[i - 1] > order[i];
927 jalview.util.QuickSort.sort(order, data);
932 * Offers a file chooser dialog, and then loads the feature colours and
933 * filters from file in XML format and unmarshals to Jalview feature settings
937 JalviewFileChooser chooser = new JalviewFileChooser("fc",
938 SEQUENCE_FEATURE_COLOURS);
939 chooser.setFileView(new JalviewFileView());
940 chooser.setDialogTitle(
941 MessageManager.getString("label.load_feature_colours"));
942 chooser.setToolTipText(MessageManager.getString("action.load"));
944 int value = chooser.showOpenDialog(this);
946 if (value == JalviewFileChooser.APPROVE_OPTION)
948 File file = chooser.getSelectedFile();
954 * Loads feature colours and filters from XML stored in the given file
962 InputStreamReader in = new InputStreamReader(
963 new FileInputStream(file), "UTF-8");
965 JAXBContext jc = JAXBContext
966 .newInstance("jalview.xml.binding.jalview");
967 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
968 XMLStreamReader streamReader = XMLInputFactory.newInstance()
969 .createXMLStreamReader(in);
970 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
971 JalviewUserColours.class);
972 JalviewUserColours jucs = jbe.getValue();
974 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
977 * load feature colours
979 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
981 Colour newcol = jucs.getColour().get(i);
982 FeatureColourI colour = jalview.project.Jalview2XML
983 .parseColour(newcol);
984 fr.setColour(newcol.getName(), colour);
985 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
989 * load feature filters; loaded filters will replace any that are
990 * currently defined, other defined filters are left unchanged
992 for (int i = 0; i < jucs.getFilter().size(); i++)
994 Filter filterModel = jucs.getFilter().get(i);
995 String featureType = filterModel.getFeatureType();
996 FeatureMatcherSetI filter = jalview.project.Jalview2XML
997 .parseFilter(featureType, filterModel.getMatcherSet());
998 if (!filter.isEmpty())
1000 fr.setFeatureFilter(featureType, filter);
1005 * update feature settings table
1010 Object[][] data = ((FeatureTableModel) table.getModel())
1013 updateFeatureRenderer(data, false);
1016 } catch (Exception ex)
1018 System.out.println("Error loading User Colour File\n" + ex);
1023 * Offers a file chooser dialog, and then saves the current feature colours
1024 * and any filters to the selected file in XML format
1028 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1029 SEQUENCE_FEATURE_COLOURS);
1030 chooser.setFileView(new JalviewFileView());
1031 chooser.setDialogTitle(
1032 MessageManager.getString("label.save_feature_colours"));
1033 chooser.setToolTipText(MessageManager.getString("action.save"));
1035 int value = chooser.showSaveDialog(this);
1037 if (value == JalviewFileChooser.APPROVE_OPTION)
1039 save(chooser.getSelectedFile());
1044 * Saves feature colours and filters to the given file
1048 void save(File file)
1050 JalviewUserColours ucs = new JalviewUserColours();
1051 ucs.setSchemeName("Sequence Features");
1054 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1055 new FileOutputStream(file), "UTF-8"));
1058 * sort feature types by colour order, from 0 (highest)
1061 Set<String> fr_colours = fr.getAllFeatureColours();
1062 String[] sortedTypes = fr_colours
1063 .toArray(new String[fr_colours.size()]);
1064 Arrays.sort(sortedTypes, new Comparator<String>()
1067 public int compare(String type1, String type2)
1069 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1074 * save feature colours
1076 for (String featureType : sortedTypes)
1078 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1079 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1081 ucs.getColour().add(col);
1085 * save any feature filters
1087 for (String featureType : sortedTypes)
1089 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1090 if (filter != null && !filter.isEmpty())
1092 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1093 FeatureMatcherI firstMatcher = iterator.next();
1094 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1095 .marshalFilter(firstMatcher, iterator,
1097 Filter filterModel = new Filter();
1098 filterModel.setFeatureType(featureType);
1099 filterModel.setMatcherSet(ms);
1100 ucs.getFilter().add(filterModel);
1103 JAXBContext jaxbContext = JAXBContext
1104 .newInstance(JalviewUserColours.class);
1105 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1106 jaxbMarshaller.marshal(
1107 new ObjectFactory().createJalviewUserColours(ucs), out);
1109 // jaxbMarshaller.marshal(object, pout);
1110 // marshaller.marshal(object);
1113 // ucs.marshal(out);
1115 } catch (Exception ex)
1117 ex.printStackTrace();
1121 public void invertSelection()
1123 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1124 for (int i = 0; i < data.length; i++)
1126 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1128 updateFeatureRenderer(data, true);
1132 public void orderByAvWidth()
1134 if (table == null || table.getModel() == null)
1138 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1139 float[] width = new float[data.length];
1143 for (int i = 0; i < data.length; i++)
1145 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1148 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1149 // weight - but have to make per
1150 // sequence, too (awidth[2])
1151 // if (width[i]==1) // hack to distinguish single width sequences.
1162 boolean sort = false;
1163 for (int i = 0; i < width.length; i++)
1165 // awidth = (float[]) typeWidth.get(data[i][0]);
1168 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1171 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1177 width[i] /= max; // normalize
1178 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1182 sort = sort || width[i - 1] > width[i];
1187 jalview.util.QuickSort.sort(width, data);
1188 // update global priority order
1191 updateFeatureRenderer(data, false);
1199 frame.setClosed(true);
1200 } catch (Exception exe)
1206 public void updateFeatureRenderer(Object[][] data)
1208 updateFeatureRenderer(data, true);
1212 * Update the priority order of features; only repaint if this changed the order
1213 * of visible features
1218 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1220 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1222 if (fr.setFeaturePriority(rowData, visibleNew))
1229 * Converts table data into an array of data beans
1231 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1233 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1234 for (int i = 0; i < data.length; i++)
1236 String type = (String) data[i][TYPE_COLUMN];
1237 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1238 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1239 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1240 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1246 private void jbInit() throws Exception
1248 this.setLayout(new BorderLayout());
1250 JPanel settingsPane = new JPanel();
1251 settingsPane.setLayout(new BorderLayout());
1253 JPanel bigPanel = new JPanel();
1254 bigPanel.setLayout(new BorderLayout());
1256 groupPanel = new JPanel();
1257 bigPanel.add(groupPanel, BorderLayout.NORTH);
1259 JButton invert = new JButton(
1260 MessageManager.getString("label.invert_selection"));
1261 invert.setFont(JvSwingUtils.getLabelFont());
1262 invert.addActionListener(new ActionListener()
1265 public void actionPerformed(ActionEvent e)
1271 JButton optimizeOrder = new JButton(
1272 MessageManager.getString("label.optimise_order"));
1273 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1274 optimizeOrder.addActionListener(new ActionListener()
1277 public void actionPerformed(ActionEvent e)
1283 JButton sortByScore = new JButton(
1284 MessageManager.getString("label.seq_sort_by_score"));
1285 sortByScore.setFont(JvSwingUtils.getLabelFont());
1286 sortByScore.addActionListener(new ActionListener()
1289 public void actionPerformed(ActionEvent e)
1291 af.avc.sortAlignmentByFeatureScore(null);
1294 JButton sortByDens = new JButton(
1295 MessageManager.getString("label.sequence_sort_by_density"));
1296 sortByDens.setFont(JvSwingUtils.getLabelFont());
1297 sortByDens.addActionListener(new ActionListener()
1300 public void actionPerformed(ActionEvent e)
1302 af.avc.sortAlignmentByFeatureDensity(null);
1306 JButton help = new JButton(MessageManager.getString("action.help"));
1307 help.setFont(JvSwingUtils.getLabelFont());
1308 help.addActionListener(new ActionListener()
1311 public void actionPerformed(ActionEvent e)
1315 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1316 } catch (HelpSetException e1)
1318 e1.printStackTrace();
1322 help.setFont(JvSwingUtils.getLabelFont());
1323 help.setText(MessageManager.getString("action.help"));
1324 help.addActionListener(new ActionListener()
1327 public void actionPerformed(ActionEvent e)
1331 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1332 } catch (HelpSetException e1)
1334 e1.printStackTrace();
1339 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1340 cancel.setFont(JvSwingUtils.getLabelFont());
1341 cancel.addActionListener(new ActionListener()
1344 public void actionPerformed(ActionEvent e)
1346 fr.setTransparency(originalTransparency);
1347 fr.setFeatureFilters(originalFilters);
1348 updateFeatureRenderer(originalData);
1353 JButton ok = new JButton(MessageManager.getString("action.ok"));
1354 ok.setFont(JvSwingUtils.getLabelFont());
1355 ok.addActionListener(new ActionListener()
1358 public void actionPerformed(ActionEvent e)
1364 JButton loadColours = new JButton(
1365 MessageManager.getString("label.load_colours"));
1366 loadColours.setFont(JvSwingUtils.getLabelFont());
1367 loadColours.setToolTipText(
1368 MessageManager.getString("label.load_colours_tooltip"));
1369 loadColours.addActionListener(new ActionListener()
1372 public void actionPerformed(ActionEvent e)
1378 JButton saveColours = new JButton(
1379 MessageManager.getString("label.save_colours"));
1380 saveColours.setFont(JvSwingUtils.getLabelFont());
1381 saveColours.setToolTipText(
1382 MessageManager.getString("label.save_colours_tooltip"));
1383 saveColours.addActionListener(new ActionListener()
1386 public void actionPerformed(ActionEvent e)
1391 transparency.addChangeListener(new ChangeListener()
1394 public void stateChanged(ChangeEvent evt)
1396 if (!inConstruction)
1398 fr.setTransparency((100 - transparency.getValue()) / 100f);
1404 summaryView = new JCheckBox(
1405 MessageManager.getString("label.summary_view"));
1408 MessageManager.getString("label.summary_view_tip"));
1409 summaryView.addActionListener(new ActionListener()
1412 public void actionPerformed(ActionEvent e)
1418 transparency.setMaximum(70);
1419 transparency.setToolTipText(
1420 MessageManager.getString("label.transparency_tip"));
1422 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1423 showComplement = new JCheckBox(
1424 "Show " + (nucleotide ? "protein" : "CDS") + " features");
1425 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
1426 showComplement.addActionListener(new ActionListener()
1429 public void actionPerformed(ActionEvent e)
1432 .setShowComplementFeatures(showComplement.isSelected());
1437 showComplementOnTop = new JCheckBox("on top");
1439 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
1440 showComplementOnTop.addActionListener(new ActionListener()
1443 public void actionPerformed(ActionEvent e)
1445 af.getViewport().setShowComplementFeaturesOnTop(
1446 showComplementOnTop.isSelected());
1451 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1452 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1454 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1455 transbuttons.add(optimizeOrder);
1456 transbuttons.add(invert);
1457 transbuttons.add(sortByScore);
1458 transbuttons.add(sortByDens);
1459 transbuttons.add(help);
1461 boolean hasComplement = af.getViewport().getCodingComplement() != null;
1462 JPanel transPanelLeft = new JPanel(
1463 new GridLayout(hasComplement ? 4 : 3, 1));
1464 transPanelLeft.add(summaryView);
1465 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1466 transPanelLeft.add(transparency);
1469 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1470 cp.add(showComplement);
1471 cp.add(showComplementOnTop);
1472 transPanelLeft.add(cp);
1474 lowerPanel.add(transPanelLeft);
1475 lowerPanel.add(transbuttons);
1477 JPanel buttonPanel = new JPanel();
1478 buttonPanel.add(ok);
1479 buttonPanel.add(cancel);
1480 buttonPanel.add(loadColours);
1481 buttonPanel.add(saveColours);
1482 bigPanel.add(scrollPane, BorderLayout.CENTER);
1483 settingsPane.add(bigPanel, BorderLayout.CENTER);
1484 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1485 this.add(settingsPane);
1489 * Repaints alignment, structure and overview (if shown). If there is a
1490 * complementary view which is showing this view's features, then also
1493 void refreshDisplay()
1495 af.alignPanel.paintAlignment(true, true);
1496 AlignViewportI complement = af.getViewport().getCodingComplement();
1497 if (complement != null && complement.isShowComplementFeatures())
1499 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1500 af2.alignPanel.paintAlignment(true, true);
1505 * Reorders features by 'dragging' selectedRow to 'newRow'
1509 protected void dragRow(int newRow)
1511 if (summaryView.isSelected())
1513 // no drag while in summary view
1517 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1520 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1521 * this could be more than one row away for a very fast drag action
1522 * so just swap it with adjacent rows until we get it there
1524 Object[][] data = ((FeatureTableModel) table.getModel())
1526 int direction = newRow < selectedRow ? -1 : 1;
1527 for (int i = selectedRow; i != newRow; i += direction)
1529 Object[] temp = data[i];
1530 data[i] = data[i + direction];
1531 data[i + direction] = temp;
1533 updateFeatureRenderer(data);
1535 selectedRow = newRow;
1539 protected void refreshTable()
1541 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1542 for (Object[] row : data)
1544 String type = (String) row[TYPE_COLUMN];
1545 FeatureColourI colour = fr.getFeatureColours().get(type);
1546 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1549 filter = new FeatureMatcherSet();
1551 row[COLOUR_COLUMN] = colour;
1552 row[FILTER_COLUMN] = filter;
1557 // ///////////////////////////////////////////////////////////////////////
1558 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1559 // ///////////////////////////////////////////////////////////////////////
1560 class FeatureTableModel extends AbstractTableModel
1562 private String[] columnNames = {
1563 MessageManager.getString("label.feature_type"),
1564 MessageManager.getString("action.colour"),
1565 MessageManager.getString("label.configuration"),
1566 MessageManager.getString("label.show") };
1568 private Object[][] data;
1570 FeatureTableModel(Object[][] data)
1575 public Object[][] getData()
1580 public void setData(Object[][] data)
1586 public int getColumnCount()
1588 return columnNames.length;
1591 public Object[] getRow(int row)
1597 public int getRowCount()
1603 public String getColumnName(int col)
1605 return columnNames[col];
1609 public Object getValueAt(int row, int col)
1611 return data[row][col];
1615 * Answers the class of the object in column c of the first row of the table
1618 public Class<?> getColumnClass(int c)
1620 Object v = getValueAt(0, c);
1621 return v == null ? null : v.getClass();
1625 * Answers true for all columns except Feature Type
1628 public boolean isCellEditable(int row, int col)
1630 return col != TYPE_COLUMN;
1634 * Sets the value in the model for a given row and column. If Visibility
1635 * (Show/Hide) is being set, and the table is in Summary View, then it is
1636 * set also on any sub-types of the row's feature type.
1639 public void setValueAt(Object value, int row, int col)
1641 data[row][col] = value;
1642 fireTableCellUpdated(row, col);
1643 if (summaryView.isSelected() && col == SHOW_COLUMN)
1645 setSubtypesVisibility(row, (Boolean) value);
1647 updateFeatureRenderer(data);
1651 * Sets the visibility of any feature types which are sub-types of the type
1652 * in the given row of the table
1657 protected void setSubtypesVisibility(int row, Boolean value)
1659 String type = (String) data[row][TYPE_COLUMN];
1660 OntologyI so = SequenceOntologyFactory.getInstance();
1662 for (int r = 0; r < data.length; r++)
1666 String type2 = (String) data[r][TYPE_COLUMN];
1667 if (so.isA(type2, type))
1669 data[r][SHOW_COLUMN] = value;
1670 fireTableCellUpdated(r, SHOW_COLUMN);
1677 class ColorRenderer extends JLabel implements TableCellRenderer
1679 javax.swing.border.Border unselectedBorder = null;
1681 javax.swing.border.Border selectedBorder = null;
1683 final String baseTT = "Click to edit, right/apple click for menu.";
1685 public ColorRenderer()
1687 setOpaque(true); // MUST do this for background to show up.
1688 setHorizontalTextPosition(SwingConstants.CENTER);
1689 setVerticalTextPosition(SwingConstants.CENTER);
1693 public Component getTableCellRendererComponent(JTable tbl, Object color,
1694 boolean isSelected, boolean hasFocus, int row, int column)
1696 FeatureColourI cellColour = (FeatureColourI) color;
1698 setToolTipText(baseTT);
1699 setBackground(tbl.getBackground());
1700 if (!cellColour.isSimpleColour())
1702 Rectangle cr = tbl.getCellRect(row, column, false);
1703 FeatureSettings.renderGraduatedColor(this, cellColour,
1704 (int) cr.getWidth(), (int) cr.getHeight());
1710 setBackground(cellColour.getColour());
1714 if (selectedBorder == null)
1716 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1717 tbl.getSelectionBackground());
1719 setBorder(selectedBorder);
1723 if (unselectedBorder == null)
1725 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1726 tbl.getBackground());
1728 setBorder(unselectedBorder);
1735 class FilterRenderer extends JLabel implements TableCellRenderer
1737 javax.swing.border.Border unselectedBorder = null;
1739 javax.swing.border.Border selectedBorder = null;
1741 public FilterRenderer()
1743 setOpaque(true); // MUST do this for background to show up.
1744 setHorizontalTextPosition(SwingConstants.CENTER);
1745 setVerticalTextPosition(SwingConstants.CENTER);
1749 public Component getTableCellRendererComponent(JTable tbl,
1750 Object filter, boolean isSelected, boolean hasFocus, int row,
1753 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1755 String asText = theFilter.toString();
1756 setBackground(tbl.getBackground());
1757 this.setText(asText);
1762 if (selectedBorder == null)
1764 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1765 tbl.getSelectionBackground());
1767 setBorder(selectedBorder);
1771 if (unselectedBorder == null)
1773 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1774 tbl.getBackground());
1776 setBorder(unselectedBorder);
1784 * update comp using rendering settings from gcol
1789 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1791 int w = comp.getWidth(), h = comp.getHeight();
1794 w = (int) comp.getPreferredSize().getWidth();
1795 h = (int) comp.getPreferredSize().getHeight();
1802 renderGraduatedColor(comp, gcol, w, h);
1805 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1808 boolean thr = false;
1809 StringBuilder tt = new StringBuilder();
1810 StringBuilder tx = new StringBuilder();
1812 if (gcol.isColourByAttribute())
1814 tx.append(String.join(":", gcol.getAttributeName()));
1816 else if (!gcol.isColourByLabel())
1818 tx.append(MessageManager.getString("label.score"));
1821 if (gcol.isAboveThreshold())
1825 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1828 if (gcol.isBelowThreshold())
1832 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1835 if (gcol.isColourByLabel())
1837 tt.append("Coloured by label text. ").append(tt);
1842 if (!gcol.isColourByAttribute())
1850 Color newColor = gcol.getMaxColour();
1851 comp.setBackground(newColor);
1852 // System.err.println("Width is " + w / 2);
1853 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1854 comp.setIcon(ficon);
1855 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1856 // + newColor.getGreen() + ", " + newColor.getBlue()
1857 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1858 // + ", " + minCol.getBlue() + ")");
1860 comp.setHorizontalAlignment(SwingConstants.CENTER);
1861 comp.setText(tx.toString());
1862 if (tt.length() > 0)
1864 if (comp.getToolTipText() == null)
1866 comp.setToolTipText(tt.toString());
1870 comp.setToolTipText(
1871 tt.append(" ").append(comp.getToolTipText()).toString());
1876 class ColorEditor extends AbstractCellEditor
1877 implements TableCellEditor, ActionListener
1881 FeatureColourI currentColor;
1883 FeatureTypeSettings chooser;
1887 JButton colourButton;
1889 JColorChooser colorChooser;
1893 protected static final String EDIT = "edit";
1895 int rowSelected = 0;
1897 public ColorEditor(FeatureSettings me)
1900 // Set up the editor (from the table's point of view),
1901 // which is a button.
1902 // This button brings up the color chooser dialog,
1903 // which is the editor from the user's point of view.
1904 colourButton = new JButton();
1905 colourButton.setActionCommand(EDIT);
1906 colourButton.addActionListener(this);
1907 colourButton.setBorderPainted(false);
1908 // Set up the dialog that the button brings up.
1909 colorChooser = new JColorChooser();
1910 dialog = JColorChooser.createDialog(colourButton,
1911 MessageManager.getString("label.select_colour"), true, // modal
1912 colorChooser, this, // OK button handler
1913 null); // no CANCEL button handler
1917 * Handles events from the editor button and from the dialog's OK button.
1920 public void actionPerformed(ActionEvent e)
1922 // todo test e.getSource() instead here
1923 if (EDIT.equals(e.getActionCommand()))
1925 // The user has clicked the cell, so
1926 // bring up the dialog.
1927 if (currentColor.isSimpleColour())
1929 // bring up simple color chooser
1930 colourButton.setBackground(currentColor.getColour());
1931 colorChooser.setColor(currentColor.getColour());
1932 dialog.setVisible(true);
1936 // bring up graduated chooser.
1937 chooser = new FeatureTypeSettings(me.fr, type);
1938 chooser.setRequestFocusEnabled(true);
1939 chooser.requestFocus();
1940 chooser.addActionListener(this);
1941 // Make the renderer reappear.
1942 fireEditingStopped();
1947 if (currentColor.isSimpleColour())
1950 * read off colour picked in colour chooser after OK pressed
1952 currentColor = new FeatureColour(colorChooser.getColor());
1953 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1958 * after OK in variable colour dialog, any changes to colour
1959 * (or filters!) are already set in FeatureRenderer, so just
1960 * update table data without triggering updateFeatureRenderer
1964 fireEditingStopped();
1965 me.table.validate();
1969 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1971 public Object getCellEditorValue()
1973 return currentColor;
1976 // Implement the one method defined by TableCellEditor.
1978 public Component getTableCellEditorComponent(JTable theTable, Object value,
1979 boolean isSelected, int row, int column)
1981 currentColor = (FeatureColourI) value;
1982 this.rowSelected = row;
1983 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1984 colourButton.setOpaque(true);
1985 colourButton.setBackground(me.getBackground());
1986 if (!currentColor.isSimpleColour())
1988 JLabel btn = new JLabel();
1989 btn.setSize(colourButton.getSize());
1990 FeatureSettings.renderGraduatedColor(btn, currentColor);
1991 colourButton.setBackground(btn.getBackground());
1992 colourButton.setIcon(btn.getIcon());
1993 colourButton.setText(btn.getText());
1997 colourButton.setText("");
1998 colourButton.setIcon(null);
1999 colourButton.setBackground(currentColor.getColour());
2001 return colourButton;
2006 * The cell editor for the Filter column. It displays the text of any filters
2007 * for the feature type in that row (in full as a tooltip, possible abbreviated
2008 * as display text). On click in the cell, opens the Feature Display Settings
2009 * dialog at the Filters tab.
2011 class FilterEditor extends AbstractCellEditor
2012 implements TableCellEditor, ActionListener
2016 FeatureMatcherSetI currentFilter;
2022 JButton filterButton;
2024 protected static final String EDIT = "edit";
2026 int rowSelected = 0;
2028 public FilterEditor(FeatureSettings me)
2031 filterButton = new JButton();
2032 filterButton.setActionCommand(EDIT);
2033 filterButton.addActionListener(this);
2034 filterButton.setBorderPainted(false);
2038 * Handles events from the editor button
2041 public void actionPerformed(ActionEvent e)
2043 if (filterButton == e.getSource())
2045 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2046 chooser.addActionListener(this);
2047 chooser.setRequestFocusEnabled(true);
2048 chooser.requestFocus();
2049 if (lastLocation != null)
2051 // todo open at its last position on screen
2052 chooser.setBounds(lastLocation.x, lastLocation.y,
2053 chooser.getWidth(), chooser.getHeight());
2056 fireEditingStopped();
2058 else if (e.getSource() instanceof Component)
2061 * after OK in variable colour dialog, any changes to filter
2062 * (or colours!) are already set in FeatureRenderer, so just
2063 * update table data without triggering updateFeatureRenderer
2066 fireEditingStopped();
2067 me.table.validate();
2072 public Object getCellEditorValue()
2074 return currentFilter;
2078 public Component getTableCellEditorComponent(JTable theTable, Object value,
2079 boolean isSelected, int row, int column)
2081 currentFilter = (FeatureMatcherSetI) value;
2082 this.rowSelected = row;
2083 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2084 filterButton.setOpaque(true);
2085 filterButton.setBackground(me.getBackground());
2086 filterButton.setText(currentFilter.toString());
2087 filterButton.setToolTipText(currentFilter.toString());
2088 filterButton.setIcon(null);
2089 return filterButton;
2094 class FeatureIcon implements Icon
2096 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2098 FeatureColourI gcol;
2102 boolean midspace = false;
2104 int width = 50, height = 20;
2106 int s1, e1; // start and end of midpoint band for thresholded symbol
2108 Color mpcolour = Color.white;
2110 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2130 public int getIconWidth()
2136 public int getIconHeight()
2142 public void paintIcon(Component c, Graphics g, int x, int y)
2145 if (gcol.isColourByLabel())
2148 g.fillRect(0, 0, width, height);
2149 // need an icon here.
2150 g.setColor(gcol.getMaxColour());
2152 g.setFont(VERDANA_9);
2154 // g.setFont(g.getFont().deriveFont(
2155 // AffineTransform.getScaleInstance(
2156 // width/g.getFontMetrics().stringWidth("Label"),
2157 // height/g.getFontMetrics().getHeight())));
2159 g.drawString(MessageManager.getString("label.label"), 0, 0);
2164 Color minCol = gcol.getMinColour();
2166 g.fillRect(0, 0, s1, height);
2169 g.setColor(Color.white);
2170 g.fillRect(s1, 0, e1 - s1, height);
2172 g.setColor(gcol.getMaxColour());
2173 g.fillRect(0, e1, width - e1, height);