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;
50 import java.awt.Graphics;
51 import java.awt.GridLayout;
52 import java.awt.Point;
53 import java.awt.Rectangle;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.ActionListener;
56 import java.awt.event.ItemEvent;
57 import java.awt.event.ItemListener;
58 import java.awt.event.MouseAdapter;
59 import java.awt.event.MouseEvent;
60 import java.awt.event.MouseMotionAdapter;
61 import java.beans.PropertyChangeEvent;
62 import java.beans.PropertyChangeListener;
64 import java.io.FileInputStream;
65 import java.io.FileOutputStream;
66 import java.io.InputStreamReader;
67 import java.io.OutputStreamWriter;
68 import java.io.PrintWriter;
69 import java.util.ArrayList;
70 import java.util.Arrays;
71 import java.util.Comparator;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.Hashtable;
75 import java.util.Iterator;
76 import java.util.List;
80 import javax.help.HelpSetException;
81 import javax.swing.AbstractCellEditor;
82 import javax.swing.BorderFactory;
83 import javax.swing.Icon;
84 import javax.swing.JButton;
85 import javax.swing.JCheckBox;
86 import javax.swing.JCheckBoxMenuItem;
87 import javax.swing.JColorChooser;
88 import javax.swing.JDialog;
89 import javax.swing.JInternalFrame;
90 import javax.swing.JLabel;
91 import javax.swing.JLayeredPane;
92 import javax.swing.JMenuItem;
93 import javax.swing.JPanel;
94 import javax.swing.JPopupMenu;
95 import javax.swing.JScrollPane;
96 import javax.swing.JSlider;
97 import javax.swing.JTable;
98 import javax.swing.ListSelectionModel;
99 import javax.swing.RowFilter;
100 import javax.swing.SwingConstants;
101 import javax.swing.event.ChangeEvent;
102 import javax.swing.event.ChangeListener;
103 import javax.swing.table.AbstractTableModel;
104 import javax.swing.table.TableCellEditor;
105 import javax.swing.table.TableCellRenderer;
106 import javax.swing.table.TableColumn;
107 import javax.swing.table.TableRowSorter;
108 import javax.xml.bind.JAXBContext;
109 import javax.xml.bind.JAXBElement;
110 import javax.xml.bind.Marshaller;
111 import javax.xml.stream.XMLInputFactory;
112 import javax.xml.stream.XMLStreamReader;
114 public class FeatureSettings extends JPanel
115 implements FeatureSettingsControllerI
117 private static final Font VERDANA_12 = new Font("Verdana", Font.PLAIN, 12);
119 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
120 .getString("label.sequence_feature_colours");
123 * column indices of fields in Feature Settings table
125 static final int TYPE_COLUMN = 0;
127 static final int COLOUR_COLUMN = 1;
129 static final int FILTER_COLUMN = 2;
131 static final int SHOW_COLUMN = 3;
133 private static final int COLUMN_COUNT = 4;
135 private static final int MIN_WIDTH = 400;
137 private static final int MIN_HEIGHT = 400;
139 final FeatureRenderer fr;
141 public final AlignFrame af;
144 * 'original' fields hold settings to restore on Cancel
146 Object[][] originalData;
148 private float originalTransparency;
150 private Map<String, FeatureMatcherSetI> originalFilters;
152 final JInternalFrame frame;
154 JScrollPane scrollPane = new JScrollPane();
160 JSlider transparency = new JSlider();
162 JCheckBox showComplement;
165 * when true, constructor is still executing - so ignore UI events
167 protected volatile boolean inConstruction = true;
169 int selectedRow = -1;
171 boolean resettingTable = false;
174 * true when Feature Settings are updating from feature renderer
176 private boolean handlingUpdate = false;
179 * holds {featureCount, totalExtent} for each feature type
181 Map<String, float[]> typeWidth = null;
184 * if true, 'child' feature types are not displayed
186 JCheckBox summaryView;
189 * those feature types that do not have a parent feature type present
190 * (as determined by an Ontology relationship)
192 List<String> topLevelTypes;
199 public FeatureSettings(AlignFrame alignFrame)
201 this.af = alignFrame;
202 fr = af.getFeatureRenderer();
204 // save transparency for restore on Cancel
205 originalTransparency = fr.getTransparency();
206 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
207 transparency.setMaximum(100 - originalTransparencyAsPercent);
209 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
211 topLevelTypes = new ArrayList<>();
216 } catch (Exception ex)
218 ex.printStackTrace();
223 scrollPane.setViewportView(table);
225 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
227 fr.findAllFeatures(true); // display everything!
230 discoverAllFeatureData();
231 final PropertyChangeListener change;
232 final FeatureSettings fs = this;
233 fr.addPropertyChangeListener(change = new PropertyChangeListener()
236 public void propertyChange(PropertyChangeEvent evt)
238 if (!fs.resettingTable && !fs.handlingUpdate)
240 fs.handlingUpdate = true;
242 // new groups may be added with new sequence feature types only
243 fs.handlingUpdate = false;
248 frame = new JInternalFrame();
249 frame.setContentPane(this);
250 if (Platform.isAMac())
252 Desktop.addInternalFrame(frame,
253 MessageManager.getString("label.sequence_feature_settings"),
258 Desktop.addInternalFrame(frame,
259 MessageManager.getString("label.sequence_feature_settings"),
262 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
264 frame.addInternalFrameListener(
265 new javax.swing.event.InternalFrameAdapter()
268 public void internalFrameClosed(
269 javax.swing.event.InternalFrameEvent evt)
271 fr.removePropertyChangeListener(change);
274 frame.setLayer(JLayeredPane.PALETTE_LAYER);
275 inConstruction = false;
279 * Constructs and configures the JTable which displays columns of data for
282 protected void initTable()
287 public String getToolTipText(MouseEvent e)
290 int column = table.columnAtPoint(e.getPoint());
295 * drag to reorder not enabled in Summary View
297 tip = summaryView.isSelected()
298 ? MessageManager.getString(
299 "label.feature_settings_select_columns")
300 : JvSwingUtils.wrapTooltip(true, MessageManager
301 .getString("label.feature_settings_click_drag"));
304 int row = table.rowAtPoint(e.getPoint());
305 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
309 .getString("label.configure_feature_tooltip")
318 table.getTableHeader().setFont(VERDANA_12);
319 table.setFont(VERDANA_12);
321 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
322 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
324 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
325 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
327 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
328 new ColorRenderer(), new ColorEditor(this));
329 table.addColumn(colourColumn);
331 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
332 new FilterRenderer(), new FilterEditor(this));
333 table.addColumn(filterColumn);
335 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
337 table.addMouseListener(new MouseAdapter()
340 public void mousePressed(MouseEvent evt)
342 selectedRow = table.rowAtPoint(evt.getPoint());
343 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
344 if (evt.isPopupTrigger())
346 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
347 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
349 else if (evt.getClickCount() == 2)
351 boolean invertSelection = evt.isAltDown();
352 boolean toggleSelection = Platform.isControlDown(evt);
353 boolean extendSelection = evt.isShiftDown();
354 String[] terms = getTermsInScope(type);
355 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
356 invertSelection, extendSelection, toggleSelection, terms);
360 // isPopupTrigger fires on mouseReleased on Windows
362 public void mouseReleased(MouseEvent evt)
364 selectedRow = table.rowAtPoint(evt.getPoint());
365 if (evt.isPopupTrigger())
367 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
368 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
369 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
374 table.addMouseMotionListener(new MouseMotionAdapter()
377 public void mouseDragged(MouseEvent evt)
379 int newRow = table.rowAtPoint(evt.getPoint());
386 * Answers an array consisting of the given type, and also (if 'Summary View'
387 * is selected), any child terms in the sequence ontology
392 protected String[] getTermsInScope(String type)
394 if (!summaryView.isSelected())
396 return new String[] { type };
399 List<String> terms = new ArrayList<>();
402 OntologyI so = SequenceOntologyFactory.getInstance();
404 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
405 for (Object[] row : data)
407 String type2 = (String) row[TYPE_COLUMN];
408 if (!type2.equals(type) && so.isA(type2, type))
413 return terms.toArray(new String[terms.size()]);
416 protected void popupMenu(final int rowSelected, final String type,
417 final Object typeCol, int x, int y)
419 final FeatureColourI featureColour = (FeatureColourI) typeCol;
421 JPopupMenu men = new JPopupMenu(MessageManager
422 .formatMessage("label.settings_for_param", new String[]
424 JMenuItem scr = new JMenuItem(
425 MessageManager.getString("label.sort_by_score"));
427 final FeatureSettings me = this;
428 scr.addActionListener(new ActionListener()
431 public void actionPerformed(ActionEvent e)
433 String[] types = getTermsInScope(type);
434 me.af.avc.sortAlignmentByFeatureScore(Arrays.asList(types));
437 JMenuItem dens = new JMenuItem(
438 MessageManager.getString("label.sort_by_density"));
439 dens.addActionListener(new ActionListener()
442 public void actionPerformed(ActionEvent e)
444 String[] types = getTermsInScope(type);
445 me.af.avc.sortAlignmentByFeatureDensity(Arrays.asList(types));
450 // fixme is Variable Colour in popup menu or not
452 * variable colour options include colour by label, by score,
453 * by selected attribute text, or attribute value
455 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
456 MessageManager.getString("label.variable_colour"));
457 mxcol.setSelected(!featureColour.isSimpleColour());
459 mxcol.addActionListener(new ActionListener()
461 JColorChooser colorChooser;
464 public void actionPerformed(ActionEvent e)
466 if (e.getSource() == mxcol)
468 if (featureColour.isSimpleColour())
470 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
471 fc.addActionListener(this);
475 // bring up simple color chooser
476 colorChooser = new JColorChooser();
477 String title = MessageManager
478 .getString("label.select_colour");
479 JDialog dialog = JColorChooser.createDialog(me,
480 title, true, // modal
481 colorChooser, this, // OK button handler
482 null); // no CANCEL button handler
483 colorChooser.setColor(featureColour.getMaxColour());
484 dialog.setVisible(true);
489 if (e.getSource() instanceof FeatureTypeSettings)
492 * update after OK in feature colour dialog; the updated
493 * colour will have already been set in the FeatureRenderer
495 FeatureColourI fci = fr.getFeatureColours().get(type);
496 table.setValueAt(fci, rowSelected, 1);
501 // probably the color chooser!
502 table.setValueAt(new FeatureColour(colorChooser.getColor()),
505 me.updateFeatureRenderer(
506 ((FeatureTableModel) table.getModel()).getData(),
513 JMenuItem selCols = new JMenuItem(
514 MessageManager.getString("label.select_columns_containing"));
515 selCols.addActionListener(new ActionListener()
518 public void actionPerformed(ActionEvent arg0)
520 String[] types = getTermsInScope(type);
521 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
525 JMenuItem clearCols = new JMenuItem(MessageManager
526 .getString("label.select_columns_not_containing"));
527 clearCols.addActionListener(new ActionListener()
530 public void actionPerformed(ActionEvent arg0)
532 String[] types = getTermsInScope(type);
533 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
537 JMenuItem hideCols = new JMenuItem(
538 MessageManager.getString("label.hide_columns_containing"));
539 hideCols.addActionListener(new ActionListener()
542 public void actionPerformed(ActionEvent arg0)
544 String[] types = getTermsInScope(type);
545 fr.ap.alignFrame.hideFeatureColumns(true, types);
548 JMenuItem hideOtherCols = new JMenuItem(
549 MessageManager.getString("label.hide_columns_not_containing"));
550 hideOtherCols.addActionListener(new ActionListener()
553 public void actionPerformed(ActionEvent arg0)
555 String[] types = getTermsInScope(type);
556 fr.ap.alignFrame.hideFeatureColumns(false, types);
562 men.add(hideOtherCols);
563 men.show(table, x, y);
567 synchronized public void discoverAllFeatureData()
569 Set<String> allGroups = new HashSet<>();
570 AlignmentI alignment = af.getViewport().getAlignment();
572 for (int i = 0; i < alignment.getHeight(); i++)
574 SequenceI seq = alignment.getSequenceAt(i);
575 for (String group : seq.getFeatures().getFeatureGroups(true))
577 if (group != null && !allGroups.contains(group))
579 allGroups.add(group);
580 checkGroupState(group);
591 * Synchronise gui group list and check visibility of group
594 * @return true if group is visible
596 private boolean checkGroupState(String group)
598 boolean visible = fr.checkGroupVisibility(group, true);
600 for (int g = 0; g < groupPanel.getComponentCount(); g++)
602 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
604 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
609 final String grp = group;
610 final JCheckBox check = new JCheckBox(group, visible);
611 check.setFont(new Font("Serif", Font.BOLD, 12));
612 check.setToolTipText(group);
613 check.addItemListener(new ItemListener()
616 public void itemStateChanged(ItemEvent evt)
618 fr.setGroupVisibility(check.getText(), check.isSelected());
619 resetTable(new String[] { grp });
623 groupPanel.add(check);
627 synchronized void resetTable(String[] groupChanged)
633 resettingTable = true;
634 typeWidth = new Hashtable<>();
635 // TODO: change avWidth calculation to 'per-sequence' average and use long
638 Set<String> displayableTypes = new HashSet<>();
639 Set<String> foundGroups = new HashSet<>();
642 * determine which feature types may be visible depending on
643 * which groups are selected, and recompute average width data
645 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
648 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
651 * get the sequence's groups for positional features
652 * and keep track of which groups are visible
654 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
655 Set<String> visibleGroups = new HashSet<>();
656 for (String group : groups)
658 if (group == null || checkGroupState(group))
660 visibleGroups.add(group);
663 foundGroups.addAll(groups);
666 * get distinct feature types for visible groups
667 * record distinct visible types, and their count and total length
669 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
670 visibleGroups.toArray(new String[visibleGroups.size()]));
672 for (String type : types)
674 displayableTypes.add(type);
675 float[] avWidth = typeWidth.get(type);
678 avWidth = new float[2];
679 typeWidth.put(type, avWidth);
681 // todo this could include features with a non-visible group
682 // - do we greatly care?
683 // todo should we include non-displayable features here, and only
684 // update when features are added?
685 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
686 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
691 * enable 'Summary View' if some types are sub-types of others
693 Set<String> parents = SequenceOntologyFactory.getInstance()
694 .getParentTerms(displayableTypes);
695 summaryView.setEnabled(parents.size() < displayableTypes.size());
697 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
700 if (fr.hasRenderOrder())
704 fr.findAllFeatures(groupChanged != null); // prod to update
705 // colourschemes. but don't
707 // First add the checks in the previous render order,
708 // in case the window has been closed and reopened
710 List<String> frl = fr.getRenderOrder();
711 for (int ro = frl.size() - 1; ro > -1; ro--)
713 String type = frl.get(ro);
715 if (!displayableTypes.contains(type))
719 data[dataIndex][TYPE_COLUMN] = type;
720 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
721 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
722 data[dataIndex][FILTER_COLUMN] = featureFilter == null
723 ? new FeatureMatcherSet()
725 data[dataIndex][SHOW_COLUMN] = new Boolean(
726 af.getViewport().getFeaturesDisplayed().isVisible(type));
728 displayableTypes.remove(type);
733 * process any extra features belonging only to
734 * a group which was just selected
736 while (!displayableTypes.isEmpty())
738 String type = displayableTypes.iterator().next();
739 data[dataIndex][TYPE_COLUMN] = type;
741 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
742 if (data[dataIndex][COLOUR_COLUMN] == null)
744 // "Colour has been updated in another view!!"
745 fr.clearRenderOrder();
748 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
749 data[dataIndex][FILTER_COLUMN] = featureFilter == null
750 ? new FeatureMatcherSet()
752 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
754 displayableTypes.remove(type);
757 if (originalData == null)
759 originalData = new Object[data.length][COLUMN_COUNT];
760 for (int i = 0; i < data.length; i++)
762 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
767 updateOriginalData(data);
771 * recreate the table model
773 FeatureTableModel dataModel = new FeatureTableModel(data);
774 table.setModel(dataModel);
777 * we want to be able to filter out rows for sub-types, but not to sort
778 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
780 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
782 for (int i = 0; i < table.getColumnCount(); i++)
784 sorter.setSortable(i, false);
788 * filter rows to only top-level Ontology types if requested
790 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
793 public boolean include(
794 Entry<? extends FeatureTableModel, ? extends Integer> entry)
796 if (!summaryView.isSelected())
800 int row = entry.getIdentifier(); // this is model, not view, row number
801 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
802 return parents.contains(featureType);
805 table.setRowSorter(sorter);
807 table.getColumnModel().getColumn(0).setPreferredWidth(200);
809 groupPanel.setLayout(
810 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
811 pruneGroups(foundGroups);
812 groupPanel.validate();
814 updateFeatureRenderer(data, groupChanged != null);
815 resettingTable = false;
819 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
820 * have been made outwith this dialog
822 * <li>a new feature type added (and made visible)</li>
823 * <li>a feature colour changed (in the Amend Features dialog)</li>
828 protected void updateOriginalData(Object[][] foundData)
830 // todo LinkedHashMap instead of Object[][] would be nice
832 Object[][] currentData = ((FeatureTableModel) table.getModel())
834 for (Object[] row : foundData)
836 String type = (String) row[TYPE_COLUMN];
837 boolean found = false;
838 for (Object[] current : currentData)
840 if (type.equals(current[TYPE_COLUMN]))
844 * currently dependent on object equality here;
845 * really need an equals method on FeatureColour
847 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
850 * feature colour has changed externally - update originalData
852 for (Object[] original : originalData)
854 if (type.equals(original[TYPE_COLUMN]))
856 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
867 * new feature detected - add to original data (on top)
869 Object[][] newData = new Object[originalData.length
871 for (int i = 0; i < originalData.length; i++)
873 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
877 originalData = newData;
883 * Remove from the groups panel any checkboxes for groups that are not in the
884 * foundGroups set. This enables removing a group from the display when the last
885 * feature in that group is deleted.
889 protected void pruneGroups(Set<String> foundGroups)
891 for (int g = 0; g < groupPanel.getComponentCount(); g++)
893 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
894 if (!foundGroups.contains(checkbox.getText()))
896 groupPanel.remove(checkbox);
902 * reorder data based on the featureRenderers global priority list.
906 private void ensureOrder(Object[][] data)
908 boolean sort = false;
909 float[] order = new float[data.length];
910 for (int i = 0; i < order.length; i++)
912 order[i] = fr.getOrder(data[i][0].toString());
915 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
919 sort = sort || order[i - 1] > order[i];
924 jalview.util.QuickSort.sort(order, data);
929 * Offers a file chooser dialog, and then loads the feature colours and
930 * filters from file in XML format and unmarshals to Jalview feature settings
934 JalviewFileChooser chooser = new JalviewFileChooser("fc",
935 SEQUENCE_FEATURE_COLOURS);
936 chooser.setFileView(new JalviewFileView());
937 chooser.setDialogTitle(
938 MessageManager.getString("label.load_feature_colours"));
939 chooser.setToolTipText(MessageManager.getString("action.load"));
941 int value = chooser.showOpenDialog(this);
943 if (value == JalviewFileChooser.APPROVE_OPTION)
945 File file = chooser.getSelectedFile();
951 * Loads feature colours and filters from XML stored in the given file
959 InputStreamReader in = new InputStreamReader(
960 new FileInputStream(file), "UTF-8");
962 JAXBContext jc = JAXBContext
963 .newInstance("jalview.xml.binding.jalview");
964 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
965 XMLStreamReader streamReader = XMLInputFactory.newInstance()
966 .createXMLStreamReader(in);
967 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
968 JalviewUserColours.class);
969 JalviewUserColours jucs = jbe.getValue();
971 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
974 * load feature colours
976 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
978 Colour newcol = jucs.getColour().get(i);
979 FeatureColourI colour = jalview.project.Jalview2XML
980 .parseColour(newcol);
981 fr.setColour(newcol.getName(), colour);
982 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
986 * load feature filters; loaded filters will replace any that are
987 * currently defined, other defined filters are left unchanged
989 for (int i = 0; i < jucs.getFilter().size(); i++)
991 Filter filterModel = jucs.getFilter().get(i);
992 String featureType = filterModel.getFeatureType();
993 FeatureMatcherSetI filter = jalview.project.Jalview2XML
994 .parseFilter(featureType, filterModel.getMatcherSet());
995 if (!filter.isEmpty())
997 fr.setFeatureFilter(featureType, filter);
1002 * update feature settings table
1007 Object[][] data = ((FeatureTableModel) table.getModel())
1010 updateFeatureRenderer(data, false);
1013 } catch (Exception ex)
1015 System.out.println("Error loading User Colour File\n" + ex);
1020 * Offers a file chooser dialog, and then saves the current feature colours
1021 * and any filters to the selected file in XML format
1025 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1026 SEQUENCE_FEATURE_COLOURS);
1027 chooser.setFileView(new JalviewFileView());
1028 chooser.setDialogTitle(
1029 MessageManager.getString("label.save_feature_colours"));
1030 chooser.setToolTipText(MessageManager.getString("action.save"));
1032 int value = chooser.showSaveDialog(this);
1034 if (value == JalviewFileChooser.APPROVE_OPTION)
1036 save(chooser.getSelectedFile());
1041 * Saves feature colours and filters to the given file
1045 void save(File file)
1047 JalviewUserColours ucs = new JalviewUserColours();
1048 ucs.setSchemeName("Sequence Features");
1051 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1052 new FileOutputStream(file), "UTF-8"));
1055 * sort feature types by colour order, from 0 (highest)
1058 Set<String> fr_colours = fr.getAllFeatureColours();
1059 String[] sortedTypes = fr_colours
1060 .toArray(new String[fr_colours.size()]);
1061 Arrays.sort(sortedTypes, new Comparator<String>()
1064 public int compare(String type1, String type2)
1066 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1071 * save feature colours
1073 for (String featureType : sortedTypes)
1075 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1076 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1078 ucs.getColour().add(col);
1082 * save any feature filters
1084 for (String featureType : sortedTypes)
1086 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1087 if (filter != null && !filter.isEmpty())
1089 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1090 FeatureMatcherI firstMatcher = iterator.next();
1091 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1092 .marshalFilter(firstMatcher, iterator,
1094 Filter filterModel = new Filter();
1095 filterModel.setFeatureType(featureType);
1096 filterModel.setMatcherSet(ms);
1097 ucs.getFilter().add(filterModel);
1100 JAXBContext jaxbContext = JAXBContext
1101 .newInstance(JalviewUserColours.class);
1102 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1103 jaxbMarshaller.marshal(
1104 new ObjectFactory().createJalviewUserColours(ucs), out);
1106 // jaxbMarshaller.marshal(object, pout);
1107 // marshaller.marshal(object);
1110 // ucs.marshal(out);
1112 } catch (Exception ex)
1114 ex.printStackTrace();
1118 public void invertSelection()
1120 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1121 for (int i = 0; i < data.length; i++)
1123 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1125 updateFeatureRenderer(data, true);
1129 public void orderByAvWidth()
1131 if (table == null || table.getModel() == null)
1135 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1136 float[] width = new float[data.length];
1140 for (int i = 0; i < data.length; i++)
1142 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1145 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1146 // weight - but have to make per
1147 // sequence, too (awidth[2])
1148 // if (width[i]==1) // hack to distinguish single width sequences.
1159 boolean sort = false;
1160 for (int i = 0; i < width.length; i++)
1162 // awidth = (float[]) typeWidth.get(data[i][0]);
1165 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1168 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1174 width[i] /= max; // normalize
1175 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1179 sort = sort || width[i - 1] > width[i];
1184 jalview.util.QuickSort.sort(width, data);
1185 // update global priority order
1188 updateFeatureRenderer(data, false);
1196 frame.setClosed(true);
1197 } catch (Exception exe)
1203 public void updateFeatureRenderer(Object[][] data)
1205 updateFeatureRenderer(data, true);
1209 * Update the priority order of features; only repaint if this changed the order
1210 * of visible features
1215 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1217 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1219 if (fr.setFeaturePriority(rowData, visibleNew))
1226 * Converts table data into an array of data beans
1228 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1230 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1231 for (int i = 0; i < data.length; i++)
1233 String type = (String) data[i][TYPE_COLUMN];
1234 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1235 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1236 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1237 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1243 private void jbInit() throws Exception
1245 this.setLayout(new BorderLayout());
1247 JPanel settingsPane = new JPanel();
1248 settingsPane.setLayout(new BorderLayout());
1250 JPanel bigPanel = new JPanel();
1251 bigPanel.setLayout(new BorderLayout());
1253 groupPanel = new JPanel();
1254 bigPanel.add(groupPanel, BorderLayout.NORTH);
1256 JButton invert = new JButton(
1257 MessageManager.getString("label.invert_selection"));
1258 invert.setFont(JvSwingUtils.getLabelFont());
1259 invert.addActionListener(new ActionListener()
1262 public void actionPerformed(ActionEvent e)
1268 JButton optimizeOrder = new JButton(
1269 MessageManager.getString("label.optimise_order"));
1270 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1271 optimizeOrder.addActionListener(new ActionListener()
1274 public void actionPerformed(ActionEvent e)
1280 JButton sortByScore = new JButton(
1281 MessageManager.getString("label.seq_sort_by_score"));
1282 sortByScore.setFont(JvSwingUtils.getLabelFont());
1283 sortByScore.addActionListener(new ActionListener()
1286 public void actionPerformed(ActionEvent e)
1288 af.avc.sortAlignmentByFeatureScore(null);
1291 JButton sortByDens = new JButton(
1292 MessageManager.getString("label.sequence_sort_by_density"));
1293 sortByDens.setFont(JvSwingUtils.getLabelFont());
1294 sortByDens.addActionListener(new ActionListener()
1297 public void actionPerformed(ActionEvent e)
1299 af.avc.sortAlignmentByFeatureDensity(null);
1303 JButton help = new JButton(MessageManager.getString("action.help"));
1304 help.setFont(JvSwingUtils.getLabelFont());
1305 help.addActionListener(new ActionListener()
1308 public void actionPerformed(ActionEvent e)
1312 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1313 } catch (HelpSetException e1)
1315 e1.printStackTrace();
1319 help.setFont(JvSwingUtils.getLabelFont());
1320 help.setText(MessageManager.getString("action.help"));
1321 help.addActionListener(new ActionListener()
1324 public void actionPerformed(ActionEvent e)
1328 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1329 } catch (HelpSetException e1)
1331 e1.printStackTrace();
1336 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1337 cancel.setFont(JvSwingUtils.getLabelFont());
1338 cancel.addActionListener(new ActionListener()
1341 public void actionPerformed(ActionEvent e)
1343 fr.setTransparency(originalTransparency);
1344 fr.setFeatureFilters(originalFilters);
1345 updateFeatureRenderer(originalData);
1350 JButton ok = new JButton(MessageManager.getString("action.ok"));
1351 ok.setFont(JvSwingUtils.getLabelFont());
1352 ok.addActionListener(new ActionListener()
1355 public void actionPerformed(ActionEvent e)
1361 JButton loadColours = new JButton(
1362 MessageManager.getString("label.load_colours"));
1363 loadColours.setFont(JvSwingUtils.getLabelFont());
1364 loadColours.setToolTipText(
1365 MessageManager.getString("label.load_colours_tooltip"));
1366 loadColours.addActionListener(new ActionListener()
1369 public void actionPerformed(ActionEvent e)
1375 JButton saveColours = new JButton(
1376 MessageManager.getString("label.save_colours"));
1377 saveColours.setFont(JvSwingUtils.getLabelFont());
1378 saveColours.setToolTipText(
1379 MessageManager.getString("label.save_colours_tooltip"));
1380 saveColours.addActionListener(new ActionListener()
1383 public void actionPerformed(ActionEvent e)
1388 transparency.addChangeListener(new ChangeListener()
1391 public void stateChanged(ChangeEvent evt)
1393 if (!inConstruction)
1395 fr.setTransparency((100 - transparency.getValue()) / 100f);
1401 summaryView = new JCheckBox(
1402 MessageManager.getString("label.summary_view"));
1405 MessageManager.getString("label.summary_view_tip"));
1406 summaryView.addActionListener(new ActionListener()
1409 public void actionPerformed(ActionEvent e)
1415 transparency.setMaximum(70);
1416 transparency.setToolTipText(
1417 MessageManager.getString("label.transparency_tip"));
1419 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1420 showComplement = new JCheckBox(
1421 "Show " + (nucleotide ? "protein" : "CDS") + " features");
1422 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
1423 showComplement.addActionListener(new ActionListener()
1426 public void actionPerformed(ActionEvent e)
1429 .setShowComplementFeatures(showComplement.isSelected());
1434 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1435 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1437 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1438 transbuttons.add(optimizeOrder);
1439 transbuttons.add(invert);
1440 transbuttons.add(sortByScore);
1441 transbuttons.add(sortByDens);
1442 transbuttons.add(help);
1444 boolean hasComplement = af.getViewport().getCodingComplement() != null;
1445 JPanel transPanelLeft = new JPanel(
1446 new GridLayout(hasComplement ? 4 : 3, 1));
1447 transPanelLeft.add(summaryView);
1448 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1449 transPanelLeft.add(transparency);
1452 transPanelLeft.add(showComplement);
1454 lowerPanel.add(transPanelLeft);
1455 lowerPanel.add(transbuttons);
1457 JPanel buttonPanel = new JPanel();
1458 buttonPanel.add(ok);
1459 buttonPanel.add(cancel);
1460 buttonPanel.add(loadColours);
1461 buttonPanel.add(saveColours);
1462 bigPanel.add(scrollPane, BorderLayout.CENTER);
1463 settingsPane.add(bigPanel, BorderLayout.CENTER);
1464 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1465 this.add(settingsPane);
1469 * Repaints alignment, structure and overview (if shown). If there is a
1470 * complementary view which is showing this view's features, then also
1473 void refreshDisplay()
1475 af.alignPanel.paintAlignment(true, true);
1476 AlignViewportI complement = af.getViewport().getCodingComplement();
1477 if (complement != null && complement.isShowComplementFeatures())
1479 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1480 af2.alignPanel.paintAlignment(true, true);
1485 * Reorders features by 'dragging' selectedRow to 'newRow'
1489 protected void dragRow(int newRow)
1491 if (summaryView.isSelected())
1493 // no drag while in summary view
1497 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1500 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1501 * this could be more than one row away for a very fast drag action
1502 * so just swap it with adjacent rows until we get it there
1504 Object[][] data = ((FeatureTableModel) table.getModel())
1506 int direction = newRow < selectedRow ? -1 : 1;
1507 for (int i = selectedRow; i != newRow; i += direction)
1509 Object[] temp = data[i];
1510 data[i] = data[i + direction];
1511 data[i + direction] = temp;
1513 updateFeatureRenderer(data);
1515 selectedRow = newRow;
1519 protected void refreshTable()
1521 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1522 for (Object[] row : data)
1524 String type = (String) row[TYPE_COLUMN];
1525 FeatureColourI colour = fr.getFeatureColours().get(type);
1526 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1529 filter = new FeatureMatcherSet();
1531 row[COLOUR_COLUMN] = colour;
1532 row[FILTER_COLUMN] = filter;
1537 // ///////////////////////////////////////////////////////////////////////
1538 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1539 // ///////////////////////////////////////////////////////////////////////
1540 class FeatureTableModel extends AbstractTableModel
1542 private String[] columnNames = {
1543 MessageManager.getString("label.feature_type"),
1544 MessageManager.getString("action.colour"),
1545 MessageManager.getString("label.configuration"),
1546 MessageManager.getString("label.show") };
1548 private Object[][] data;
1550 FeatureTableModel(Object[][] data)
1555 public Object[][] getData()
1560 public void setData(Object[][] data)
1566 public int getColumnCount()
1568 return columnNames.length;
1571 public Object[] getRow(int row)
1577 public int getRowCount()
1583 public String getColumnName(int col)
1585 return columnNames[col];
1589 public Object getValueAt(int row, int col)
1591 return data[row][col];
1595 * Answers the class of the object in column c of the first row of the table
1598 public Class<?> getColumnClass(int c)
1600 Object v = getValueAt(0, c);
1601 return v == null ? null : v.getClass();
1605 * Answers true for all columns except Feature Type
1608 public boolean isCellEditable(int row, int col)
1610 return col != TYPE_COLUMN;
1614 * Sets the value in the model for a given row and column. If Visibility
1615 * (Show/Hide) is being set, and the table is in Summary View, then it is
1616 * set also on any sub-types of the row's feature type.
1619 public void setValueAt(Object value, int row, int col)
1621 data[row][col] = value;
1622 fireTableCellUpdated(row, col);
1623 if (summaryView.isSelected() && col == SHOW_COLUMN)
1625 setSubtypesVisibility(row, (Boolean) value);
1627 updateFeatureRenderer(data);
1631 * Sets the visibility of any feature types which are sub-types of the type
1632 * in the given row of the table
1637 protected void setSubtypesVisibility(int row, Boolean value)
1639 String type = (String) data[row][TYPE_COLUMN];
1640 OntologyI so = SequenceOntologyFactory.getInstance();
1642 for (int r = 0; r < data.length; r++)
1646 String type2 = (String) data[r][TYPE_COLUMN];
1647 if (so.isA(type2, type))
1649 data[r][SHOW_COLUMN] = value;
1650 fireTableCellUpdated(r, SHOW_COLUMN);
1657 class ColorRenderer extends JLabel implements TableCellRenderer
1659 javax.swing.border.Border unselectedBorder = null;
1661 javax.swing.border.Border selectedBorder = null;
1663 final String baseTT = "Click to edit, right/apple click for menu.";
1665 public ColorRenderer()
1667 setOpaque(true); // MUST do this for background to show up.
1668 setHorizontalTextPosition(SwingConstants.CENTER);
1669 setVerticalTextPosition(SwingConstants.CENTER);
1673 public Component getTableCellRendererComponent(JTable tbl, Object color,
1674 boolean isSelected, boolean hasFocus, int row, int column)
1676 FeatureColourI cellColour = (FeatureColourI) color;
1678 setToolTipText(baseTT);
1679 setBackground(tbl.getBackground());
1680 if (!cellColour.isSimpleColour())
1682 Rectangle cr = tbl.getCellRect(row, column, false);
1683 FeatureSettings.renderGraduatedColor(this, cellColour,
1684 (int) cr.getWidth(), (int) cr.getHeight());
1690 setBackground(cellColour.getColour());
1694 if (selectedBorder == null)
1696 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1697 tbl.getSelectionBackground());
1699 setBorder(selectedBorder);
1703 if (unselectedBorder == null)
1705 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1706 tbl.getBackground());
1708 setBorder(unselectedBorder);
1715 class FilterRenderer extends JLabel implements TableCellRenderer
1717 javax.swing.border.Border unselectedBorder = null;
1719 javax.swing.border.Border selectedBorder = null;
1721 public FilterRenderer()
1723 setOpaque(true); // MUST do this for background to show up.
1724 setHorizontalTextPosition(SwingConstants.CENTER);
1725 setVerticalTextPosition(SwingConstants.CENTER);
1729 public Component getTableCellRendererComponent(JTable tbl,
1730 Object filter, boolean isSelected, boolean hasFocus, int row,
1733 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1735 String asText = theFilter.toString();
1736 setBackground(tbl.getBackground());
1737 this.setText(asText);
1742 if (selectedBorder == null)
1744 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1745 tbl.getSelectionBackground());
1747 setBorder(selectedBorder);
1751 if (unselectedBorder == null)
1753 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1754 tbl.getBackground());
1756 setBorder(unselectedBorder);
1764 * update comp using rendering settings from gcol
1769 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1771 int w = comp.getWidth(), h = comp.getHeight();
1774 w = (int) comp.getPreferredSize().getWidth();
1775 h = (int) comp.getPreferredSize().getHeight();
1782 renderGraduatedColor(comp, gcol, w, h);
1785 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1788 boolean thr = false;
1789 StringBuilder tt = new StringBuilder();
1790 StringBuilder tx = new StringBuilder();
1792 if (gcol.isColourByAttribute())
1794 tx.append(String.join(":", gcol.getAttributeName()));
1796 else if (!gcol.isColourByLabel())
1798 tx.append(MessageManager.getString("label.score"));
1801 if (gcol.isAboveThreshold())
1805 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1808 if (gcol.isBelowThreshold())
1812 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1815 if (gcol.isColourByLabel())
1817 tt.append("Coloured by label text. ").append(tt);
1822 if (!gcol.isColourByAttribute())
1830 Color newColor = gcol.getMaxColour();
1831 comp.setBackground(newColor);
1832 // System.err.println("Width is " + w / 2);
1833 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1834 comp.setIcon(ficon);
1835 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1836 // + newColor.getGreen() + ", " + newColor.getBlue()
1837 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1838 // + ", " + minCol.getBlue() + ")");
1840 comp.setHorizontalAlignment(SwingConstants.CENTER);
1841 comp.setText(tx.toString());
1842 if (tt.length() > 0)
1844 if (comp.getToolTipText() == null)
1846 comp.setToolTipText(tt.toString());
1850 comp.setToolTipText(
1851 tt.append(" ").append(comp.getToolTipText()).toString());
1856 class ColorEditor extends AbstractCellEditor
1857 implements TableCellEditor, ActionListener
1861 FeatureColourI currentColor;
1863 FeatureTypeSettings chooser;
1867 JButton colourButton;
1869 JColorChooser colorChooser;
1873 protected static final String EDIT = "edit";
1875 int rowSelected = 0;
1877 public ColorEditor(FeatureSettings me)
1880 // Set up the editor (from the table's point of view),
1881 // which is a button.
1882 // This button brings up the color chooser dialog,
1883 // which is the editor from the user's point of view.
1884 colourButton = new JButton();
1885 colourButton.setActionCommand(EDIT);
1886 colourButton.addActionListener(this);
1887 colourButton.setBorderPainted(false);
1888 // Set up the dialog that the button brings up.
1889 colorChooser = new JColorChooser();
1890 dialog = JColorChooser.createDialog(colourButton,
1891 MessageManager.getString("label.select_colour"), true, // modal
1892 colorChooser, this, // OK button handler
1893 null); // no CANCEL button handler
1897 * Handles events from the editor button and from the dialog's OK button.
1900 public void actionPerformed(ActionEvent e)
1902 // todo test e.getSource() instead here
1903 if (EDIT.equals(e.getActionCommand()))
1905 // The user has clicked the cell, so
1906 // bring up the dialog.
1907 if (currentColor.isSimpleColour())
1909 // bring up simple color chooser
1910 colourButton.setBackground(currentColor.getColour());
1911 colorChooser.setColor(currentColor.getColour());
1912 dialog.setVisible(true);
1916 // bring up graduated chooser.
1917 chooser = new FeatureTypeSettings(me.fr, type);
1918 chooser.setRequestFocusEnabled(true);
1919 chooser.requestFocus();
1920 chooser.addActionListener(this);
1921 // Make the renderer reappear.
1922 fireEditingStopped();
1927 if (currentColor.isSimpleColour())
1930 * read off colour picked in colour chooser after OK pressed
1932 currentColor = new FeatureColour(colorChooser.getColor());
1933 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1938 * after OK in variable colour dialog, any changes to colour
1939 * (or filters!) are already set in FeatureRenderer, so just
1940 * update table data without triggering updateFeatureRenderer
1944 fireEditingStopped();
1945 me.table.validate();
1949 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1951 public Object getCellEditorValue()
1953 return currentColor;
1956 // Implement the one method defined by TableCellEditor.
1958 public Component getTableCellEditorComponent(JTable theTable, Object value,
1959 boolean isSelected, int row, int column)
1961 currentColor = (FeatureColourI) value;
1962 this.rowSelected = row;
1963 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1964 colourButton.setOpaque(true);
1965 colourButton.setBackground(me.getBackground());
1966 if (!currentColor.isSimpleColour())
1968 JLabel btn = new JLabel();
1969 btn.setSize(colourButton.getSize());
1970 FeatureSettings.renderGraduatedColor(btn, currentColor);
1971 colourButton.setBackground(btn.getBackground());
1972 colourButton.setIcon(btn.getIcon());
1973 colourButton.setText(btn.getText());
1977 colourButton.setText("");
1978 colourButton.setIcon(null);
1979 colourButton.setBackground(currentColor.getColour());
1981 return colourButton;
1986 * The cell editor for the Filter column. It displays the text of any filters
1987 * for the feature type in that row (in full as a tooltip, possible abbreviated
1988 * as display text). On click in the cell, opens the Feature Display Settings
1989 * dialog at the Filters tab.
1991 class FilterEditor extends AbstractCellEditor
1992 implements TableCellEditor, ActionListener
1996 FeatureMatcherSetI currentFilter;
2002 JButton filterButton;
2004 protected static final String EDIT = "edit";
2006 int rowSelected = 0;
2008 public FilterEditor(FeatureSettings me)
2011 filterButton = new JButton();
2012 filterButton.setActionCommand(EDIT);
2013 filterButton.addActionListener(this);
2014 filterButton.setBorderPainted(false);
2018 * Handles events from the editor button
2021 public void actionPerformed(ActionEvent e)
2023 if (filterButton == e.getSource())
2025 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2026 chooser.addActionListener(this);
2027 chooser.setRequestFocusEnabled(true);
2028 chooser.requestFocus();
2029 if (lastLocation != null)
2031 // todo open at its last position on screen
2032 chooser.setBounds(lastLocation.x, lastLocation.y,
2033 chooser.getWidth(), chooser.getHeight());
2036 fireEditingStopped();
2038 else if (e.getSource() instanceof Component)
2041 * after OK in variable colour dialog, any changes to filter
2042 * (or colours!) are already set in FeatureRenderer, so just
2043 * update table data without triggering updateFeatureRenderer
2046 fireEditingStopped();
2047 me.table.validate();
2052 public Object getCellEditorValue()
2054 return currentFilter;
2058 public Component getTableCellEditorComponent(JTable theTable, Object value,
2059 boolean isSelected, int row, int column)
2061 currentFilter = (FeatureMatcherSetI) value;
2062 this.rowSelected = row;
2063 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2064 filterButton.setOpaque(true);
2065 filterButton.setBackground(me.getBackground());
2066 filterButton.setText(currentFilter.toString());
2067 filterButton.setToolTipText(currentFilter.toString());
2068 filterButton.setIcon(null);
2069 return filterButton;
2074 class FeatureIcon implements Icon
2076 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2078 FeatureColourI gcol;
2082 boolean midspace = false;
2084 int width = 50, height = 20;
2086 int s1, e1; // start and end of midpoint band for thresholded symbol
2088 Color mpcolour = Color.white;
2090 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2110 public int getIconWidth()
2116 public int getIconHeight()
2122 public void paintIcon(Component c, Graphics g, int x, int y)
2125 if (gcol.isColourByLabel())
2128 g.fillRect(0, 0, width, height);
2129 // need an icon here.
2130 g.setColor(gcol.getMaxColour());
2132 g.setFont(VERDANA_9);
2134 // g.setFont(g.getFont().deriveFont(
2135 // AffineTransform.getScaleInstance(
2136 // width/g.getFontMetrics().stringWidth("Label"),
2137 // height/g.getFontMetrics().getHeight())));
2139 g.drawString(MessageManager.getString("label.label"), 0, 0);
2144 Color minCol = gcol.getMinColour();
2146 g.fillRect(0, 0, s1, height);
2149 g.setColor(Color.white);
2150 g.fillRect(s1, 0, e1 - s1, height);
2152 g.setColor(gcol.getMaxColour());
2153 g.fillRect(0, e1, width - e1, height);