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.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.SequenceI;
27 import jalview.datamodel.features.FeatureMatcherI;
28 import jalview.datamodel.features.FeatureMatcherSet;
29 import jalview.datamodel.features.FeatureMatcherSetI;
30 import jalview.datamodel.ontology.OntologyI;
31 import jalview.gui.Help.HelpId;
32 import jalview.io.JalviewFileChooser;
33 import jalview.io.JalviewFileView;
34 import jalview.io.gff.SequenceOntologyFactory;
35 import jalview.schemabinding.version2.Filter;
36 import jalview.schemabinding.version2.JalviewUserColours;
37 import jalview.schemabinding.version2.MatcherSet;
38 import jalview.schemes.FeatureColour;
39 import jalview.util.MessageManager;
40 import jalview.util.Platform;
41 import jalview.viewmodel.AlignmentViewport;
42 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
43 import jalview.ws.DasSequenceFeatureFetcher;
44 import jalview.ws.dbsources.das.api.jalviewSourceI;
46 import java.awt.BorderLayout;
47 import java.awt.Color;
48 import java.awt.Component;
49 import java.awt.Dimension;
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;
80 import java.util.Vector;
82 import javax.help.HelpSetException;
83 import javax.swing.AbstractCellEditor;
84 import javax.swing.BorderFactory;
85 import javax.swing.Icon;
86 import javax.swing.JButton;
87 import javax.swing.JCheckBox;
88 import javax.swing.JCheckBoxMenuItem;
89 import javax.swing.JColorChooser;
90 import javax.swing.JDialog;
91 import javax.swing.JInternalFrame;
92 import javax.swing.JLabel;
93 import javax.swing.JLayeredPane;
94 import javax.swing.JMenuItem;
95 import javax.swing.JPanel;
96 import javax.swing.JPopupMenu;
97 import javax.swing.JScrollPane;
98 import javax.swing.JSlider;
99 import javax.swing.JTable;
100 import javax.swing.ListSelectionModel;
101 import javax.swing.RowFilter;
102 import javax.swing.SwingConstants;
103 import javax.swing.SwingUtilities;
104 import javax.swing.event.ChangeEvent;
105 import javax.swing.event.ChangeListener;
106 import javax.swing.table.AbstractTableModel;
107 import javax.swing.table.TableCellEditor;
108 import javax.swing.table.TableCellRenderer;
109 import javax.swing.table.TableColumn;
110 import javax.swing.table.TableRowSorter;
112 public class FeatureSettings extends JPanel
113 implements FeatureSettingsControllerI
115 private static final Font VERDANA_12 = new Font("Verdana", Font.PLAIN, 12);
117 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
118 .getString("label.sequence_feature_colours");
121 * column indices of fields in Feature Settings table
123 static final int TYPE_COLUMN = 0;
125 static final int COLOUR_COLUMN = 1;
127 static final int FILTER_COLUMN = 2;
129 static final int SHOW_COLUMN = 3;
131 private static final int COLUMN_COUNT = 4;
133 private static final int MIN_WIDTH = 400;
135 private static final int MIN_HEIGHT = 400;
137 DasSourceBrowser dassourceBrowser;
139 DasSequenceFeatureFetcher dasFeatureFetcher;
141 JPanel dasSettingsPane = new JPanel();
143 final FeatureRenderer fr;
145 public final AlignFrame af;
148 * 'original' fields hold settings to restore on Cancel
150 Object[][] originalData;
152 private float originalTransparency;
154 private Map<String, FeatureMatcherSetI> originalFilters;
156 final JInternalFrame frame;
158 JScrollPane scrollPane = new JScrollPane();
164 JSlider transparency = new JSlider();
167 * when true, constructor is still executing - so ignore UI events
169 protected volatile boolean inConstruction = true;
171 int selectedRow = -1;
173 JButton fetchDAS = new JButton();
175 JButton saveDAS = new JButton();
177 JButton cancelDAS = new JButton();
179 boolean resettingTable = false;
182 * true when Feature Settings are updating from feature renderer
184 private boolean handlingUpdate = false;
187 * holds {featureCount, totalExtent} for each feature type
189 Map<String, float[]> typeWidth = null;
192 * if true, 'child' feature types are not displayed
194 JCheckBox summaryView;
197 * those feature types that do not have a parent feature type present
198 * (as determined by an Ontology relationship)
200 List<String> topLevelTypes;
207 public FeatureSettings(AlignFrame alignFrame)
209 this.af = alignFrame;
210 fr = af.getFeatureRenderer();
212 // save transparency for restore on Cancel
213 originalTransparency = fr.getTransparency();
214 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
215 transparency.setMaximum(100 - originalTransparencyAsPercent);
217 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
219 topLevelTypes = new ArrayList<>();
224 } catch (Exception ex)
226 ex.printStackTrace();
231 scrollPane.setViewportView(table);
233 dassourceBrowser = new DasSourceBrowser(this);
234 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
236 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
238 fr.findAllFeatures(true); // display everything!
241 discoverAllFeatureData();
242 final PropertyChangeListener change;
243 final FeatureSettings fs = this;
244 fr.addPropertyChangeListener(change = new PropertyChangeListener()
247 public void propertyChange(PropertyChangeEvent evt)
249 if (!fs.resettingTable && !fs.handlingUpdate)
251 fs.handlingUpdate = true;
253 // new groups may be added with new sequence feature types only
254 fs.handlingUpdate = false;
259 frame = new JInternalFrame();
260 frame.setContentPane(this);
261 if (Platform.isAMac())
263 Desktop.addInternalFrame(frame,
264 MessageManager.getString("label.sequence_feature_settings"),
269 Desktop.addInternalFrame(frame,
270 MessageManager.getString("label.sequence_feature_settings"),
273 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
275 frame.addInternalFrameListener(
276 new javax.swing.event.InternalFrameAdapter()
279 public void internalFrameClosed(
280 javax.swing.event.InternalFrameEvent evt)
282 fr.removePropertyChangeListener(change);
283 dassourceBrowser.fs = null;
286 frame.setLayer(JLayeredPane.PALETTE_LAYER);
287 inConstruction = false;
291 * Constructs and configures the JTable which displays columns of data for
294 protected void initTable()
299 public String getToolTipText(MouseEvent e)
302 int column = table.columnAtPoint(e.getPoint());
307 * drag to reorder not enabled in Summary View
309 tip = summaryView.isSelected()
310 ? MessageManager.getString(
311 "label.feature_settings_select_columns")
312 : JvSwingUtils.wrapTooltip(true, MessageManager
313 .getString("label.feature_settings_click_drag"));
316 int row = table.rowAtPoint(e.getPoint());
317 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
320 ? MessageManager.getString("label.filters_tooltip")
329 table.getTableHeader().setFont(VERDANA_12);
330 table.setFont(VERDANA_12);
332 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
333 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
335 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
336 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
338 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
339 new ColorRenderer(), new ColorEditor(this));
340 table.addColumn(colourColumn);
342 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
343 new FilterRenderer(), new FilterEditor(this));
344 table.addColumn(filterColumn);
346 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
348 table.addMouseListener(new MouseAdapter()
351 public void mousePressed(MouseEvent evt)
353 selectedRow = table.rowAtPoint(evt.getPoint());
354 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
355 if (evt.isPopupTrigger())
357 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
358 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
360 else if (evt.getClickCount() == 2)
362 boolean invertSelection = evt.isAltDown();
363 boolean toggleSelection = Platform.isControlDown(evt);
364 boolean extendSelection = evt.isShiftDown();
365 String[] terms = getTermsInScope(type);
366 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
367 invertSelection, extendSelection, toggleSelection, terms);
371 // isPopupTrigger fires on mouseReleased on Windows
373 public void mouseReleased(MouseEvent evt)
375 selectedRow = table.rowAtPoint(evt.getPoint());
376 if (evt.isPopupTrigger())
378 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
379 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
380 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
385 table.addMouseMotionListener(new MouseMotionAdapter()
388 public void mouseDragged(MouseEvent evt)
390 int newRow = table.rowAtPoint(evt.getPoint());
397 * Answers an array consisting of the given type, and also (if 'Summary View'
398 * is selected), any child terms in the sequence ontology
403 protected String[] getTermsInScope(String type)
405 if (!summaryView.isSelected())
407 return new String[] { type };
410 List<String> terms = new ArrayList<>();
413 OntologyI so = SequenceOntologyFactory.getInstance();
415 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
416 for (Object[] row : data)
418 String type2 = (String) row[TYPE_COLUMN];
419 if (!type2.equals(type) && so.isA(type2, type))
424 return terms.toArray(new String[terms.size()]);
427 protected void popupMenu(final int rowSelected, final String type,
428 final Object typeCol, int x, int y)
430 final FeatureColourI featureColour = (FeatureColourI) typeCol;
432 JPopupMenu men = new JPopupMenu(MessageManager
433 .formatMessage("label.settings_for_param", new String[]
435 JMenuItem scr = new JMenuItem(
436 MessageManager.getString("label.sort_by_score"));
438 final FeatureSettings me = this;
439 scr.addActionListener(new ActionListener()
442 public void actionPerformed(ActionEvent e)
444 String[] types = getTermsInScope(type);
445 me.af.avc.sortAlignmentByFeatureScore(Arrays.asList(types));
448 JMenuItem dens = new JMenuItem(
449 MessageManager.getString("label.sort_by_density"));
450 dens.addActionListener(new ActionListener()
453 public void actionPerformed(ActionEvent e)
455 String[] types = getTermsInScope(type);
456 me.af.avc.sortAlignmentByFeatureDensity(Arrays.asList(types));
462 * variable colour options include colour by label, by score,
463 * by selected attribute text, or attribute value
465 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
466 MessageManager.getString("label.variable_colour"));
467 mxcol.setSelected(!featureColour.isSimpleColour());
469 mxcol.addActionListener(new ActionListener()
471 JColorChooser colorChooser;
474 public void actionPerformed(ActionEvent e)
476 if (e.getSource() == mxcol)
478 if (featureColour.isSimpleColour())
480 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
481 fc.addActionListener(this);
485 // bring up simple color chooser
486 colorChooser = new JColorChooser();
487 String title = MessageManager
488 .getString("label.select_colour");
489 JDialog dialog = JColorChooser.createDialog(me,
490 title, true, // modal
491 colorChooser, this, // OK button handler
492 null); // no CANCEL button handler
493 colorChooser.setColor(featureColour.getMaxColour());
494 dialog.setVisible(true);
499 if (e.getSource() instanceof FeatureTypeSettings)
502 * update after OK in feature colour dialog; the updated
503 * colour will have already been set in the FeatureRenderer
505 FeatureColourI fci = fr.getFeatureColours().get(type);
506 table.setValueAt(fci, rowSelected, 1);
511 // probably the color chooser!
512 table.setValueAt(new FeatureColour(colorChooser.getColor()),
515 me.updateFeatureRenderer(
516 ((FeatureTableModel) table.getModel()).getData(),
523 JMenuItem selCols = new JMenuItem(
524 MessageManager.getString("label.select_columns_containing"));
525 selCols.addActionListener(new ActionListener()
528 public void actionPerformed(ActionEvent arg0)
530 String[] types = getTermsInScope(type);
531 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
535 JMenuItem clearCols = new JMenuItem(MessageManager
536 .getString("label.select_columns_not_containing"));
537 clearCols.addActionListener(new ActionListener()
540 public void actionPerformed(ActionEvent arg0)
542 String[] types = getTermsInScope(type);
543 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
547 JMenuItem hideCols = new JMenuItem(
548 MessageManager.getString("label.hide_columns_containing"));
549 hideCols.addActionListener(new ActionListener()
552 public void actionPerformed(ActionEvent arg0)
554 String[] types = getTermsInScope(type);
555 fr.ap.alignFrame.hideFeatureColumns(true, types);
558 JMenuItem hideOtherCols = new JMenuItem(
559 MessageManager.getString("label.hide_columns_not_containing"));
560 hideOtherCols.addActionListener(new ActionListener()
563 public void actionPerformed(ActionEvent arg0)
565 String[] types = getTermsInScope(type);
566 fr.ap.alignFrame.hideFeatureColumns(false, types);
572 men.add(hideOtherCols);
573 men.show(table, x, y);
577 synchronized public void discoverAllFeatureData()
579 Set<String> allGroups = new HashSet<>();
580 AlignmentI alignment = af.getViewport().getAlignment();
582 for (int i = 0; i < alignment.getHeight(); i++)
584 SequenceI seq = alignment.getSequenceAt(i);
585 for (String group : seq.getFeatures().getFeatureGroups(true))
587 if (group != null && !allGroups.contains(group))
589 allGroups.add(group);
590 checkGroupState(group);
601 * Synchronise gui group list and check visibility of group
604 * @return true if group is visible
606 private boolean checkGroupState(String group)
608 boolean visible = fr.checkGroupVisibility(group, true);
610 for (int g = 0; g < groupPanel.getComponentCount(); g++)
612 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
614 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
619 final String grp = group;
620 final JCheckBox check = new JCheckBox(group, visible);
621 check.setFont(new Font("Serif", Font.BOLD, 12));
622 check.setToolTipText(group);
623 check.addItemListener(new ItemListener()
626 public void itemStateChanged(ItemEvent evt)
628 fr.setGroupVisibility(check.getText(), check.isSelected());
629 resetTable(new String[] { grp });
630 af.alignPanel.paintAlignment(true, true);
633 groupPanel.add(check);
637 synchronized void resetTable(String[] groupChanged)
643 resettingTable = true;
644 typeWidth = new Hashtable<>();
645 // TODO: change avWidth calculation to 'per-sequence' average and use long
648 Set<String> displayableTypes = new HashSet<>();
649 Set<String> foundGroups = new HashSet<>();
652 * determine which feature types may be visible depending on
653 * which groups are selected, and recompute average width data
655 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
658 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
661 * get the sequence's groups for positional features
662 * and keep track of which groups are visible
664 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
665 Set<String> visibleGroups = new HashSet<>();
666 for (String group : groups)
668 if (group == null || checkGroupState(group))
670 visibleGroups.add(group);
673 foundGroups.addAll(groups);
676 * get distinct feature types for visible groups
677 * record distinct visible types, and their count and total length
679 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
680 visibleGroups.toArray(new String[visibleGroups.size()]));
682 for (String type : types)
684 displayableTypes.add(type);
685 float[] avWidth = typeWidth.get(type);
688 avWidth = new float[2];
689 typeWidth.put(type, avWidth);
691 // todo this could include features with a non-visible group
692 // - do we greatly care?
693 // todo should we include non-displayable features here, and only
694 // update when features are added?
695 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
696 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
701 * enable 'Summary View' if some types are sub-types of others
703 Set<String> parents = SequenceOntologyFactory.getInstance()
704 .getParentTerms(displayableTypes);
705 summaryView.setEnabled(parents.size() < displayableTypes.size());
707 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
710 if (fr.hasRenderOrder())
714 fr.findAllFeatures(groupChanged != null); // prod to update
715 // colourschemes. but don't
717 // First add the checks in the previous render order,
718 // in case the window has been closed and reopened
720 List<String> frl = fr.getRenderOrder();
721 for (int ro = frl.size() - 1; ro > -1; ro--)
723 String type = frl.get(ro);
725 if (!displayableTypes.contains(type))
729 data[dataIndex][TYPE_COLUMN] = type;
730 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
731 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
732 data[dataIndex][FILTER_COLUMN] = featureFilter == null
733 ? new FeatureMatcherSet()
735 data[dataIndex][SHOW_COLUMN] = new Boolean(
736 af.getViewport().getFeaturesDisplayed().isVisible(type));
738 displayableTypes.remove(type);
743 * process any extra features belonging only to
744 * a group which was just selected
746 while (!displayableTypes.isEmpty())
748 String type = displayableTypes.iterator().next();
749 data[dataIndex][TYPE_COLUMN] = type;
751 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
752 if (data[dataIndex][COLOUR_COLUMN] == null)
754 // "Colour has been updated in another view!!"
755 fr.clearRenderOrder();
758 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
759 data[dataIndex][FILTER_COLUMN] = featureFilter == null
760 ? new FeatureMatcherSet()
762 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
764 displayableTypes.remove(type);
767 if (originalData == null)
769 originalData = new Object[data.length][COLUMN_COUNT];
770 for (int i = 0; i < data.length; i++)
772 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
777 updateOriginalData(data);
781 * recreate the table model
783 FeatureTableModel dataModel = new FeatureTableModel(data);
784 table.setModel(dataModel);
787 * we want to be able to filter out rows for sub-types, but not to sort
788 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
790 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
792 for (int i = 0; i < table.getColumnCount(); i++)
794 sorter.setSortable(i, false);
798 * filter rows to only top-level Ontology types if requested
800 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
803 public boolean include(
804 Entry<? extends FeatureTableModel, ? extends Integer> entry)
806 if (!summaryView.isSelected())
810 int row = entry.getIdentifier(); // this is model, not view, row number
811 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
812 return parents.contains(featureType);
815 table.setRowSorter(sorter);
817 table.getColumnModel().getColumn(0).setPreferredWidth(200);
819 groupPanel.setLayout(
820 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
821 pruneGroups(foundGroups);
822 groupPanel.validate();
824 updateFeatureRenderer(data, groupChanged != null);
825 resettingTable = false;
829 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
830 * have been made outwith this dialog
832 * <li>a new feature type added (and made visible)</li>
833 * <li>a feature colour changed (in the Amend Features dialog)</li>
838 protected void updateOriginalData(Object[][] foundData)
840 // todo LinkedHashMap instead of Object[][] would be nice
842 Object[][] currentData = ((FeatureTableModel) table.getModel())
844 for (Object[] row : foundData)
846 String type = (String) row[TYPE_COLUMN];
847 boolean found = false;
848 for (Object[] current : currentData)
850 if (type.equals(current[TYPE_COLUMN]))
854 * currently dependent on object equality here;
855 * really need an equals method on FeatureColour
857 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
860 * feature colour has changed externally - update originalData
862 for (Object[] original : originalData)
864 if (type.equals(original[TYPE_COLUMN]))
866 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
877 * new feature detected - add to original data (on top)
879 Object[][] newData = new Object[originalData.length
881 for (int i = 0; i < originalData.length; i++)
883 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
887 originalData = newData;
893 * Remove from the groups panel any checkboxes for groups that are not in the
894 * foundGroups set. This enables removing a group from the display when the last
895 * feature in that group is deleted.
899 protected void pruneGroups(Set<String> foundGroups)
901 for (int g = 0; g < groupPanel.getComponentCount(); g++)
903 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
904 if (!foundGroups.contains(checkbox.getText()))
906 groupPanel.remove(checkbox);
912 * reorder data based on the featureRenderers global priority list.
916 private void ensureOrder(Object[][] data)
918 boolean sort = false;
919 float[] order = new float[data.length];
920 for (int i = 0; i < order.length; i++)
922 order[i] = fr.getOrder(data[i][0].toString());
925 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
929 sort = sort || order[i - 1] > order[i];
934 jalview.util.QuickSort.sort(order, data);
939 * Offers a file chooser dialog, and then loads the feature colours and
940 * filters from file in XML format and unmarshals to Jalview feature settings
944 JalviewFileChooser chooser = new JalviewFileChooser("fc",
945 SEQUENCE_FEATURE_COLOURS);
946 chooser.setFileView(new JalviewFileView());
947 chooser.setDialogTitle(
948 MessageManager.getString("label.load_feature_colours"));
949 chooser.setToolTipText(MessageManager.getString("action.load"));
951 int value = chooser.showOpenDialog(this);
953 if (value == JalviewFileChooser.APPROVE_OPTION)
955 File file = chooser.getSelectedFile();
961 * Loads feature colours and filters from XML stored in the given file
969 InputStreamReader in = new InputStreamReader(
970 new FileInputStream(file), "UTF-8");
972 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
975 * load feature colours
977 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
979 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
980 FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
981 fr.setColour(newcol.getName(), colour);
982 fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
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.getFilterCount(); i++)
991 jalview.schemabinding.version2.Filter filterModel = jucs
993 String featureType = filterModel.getFeatureType();
994 FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
995 filterModel.getMatcherSet());
996 if (!filter.isEmpty())
998 fr.setFeatureFilter(featureType, filter);
1003 * update feature settings table
1008 Object[][] data = ((FeatureTableModel) table.getModel())
1011 updateFeatureRenderer(data, false);
1014 } catch (Exception ex)
1016 System.out.println("Error loading User Colour File\n" + ex);
1021 * Offers a file chooser dialog, and then saves the current feature colours
1022 * and any filters to the selected file in XML format
1026 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1027 SEQUENCE_FEATURE_COLOURS);
1028 chooser.setFileView(new JalviewFileView());
1029 chooser.setDialogTitle(
1030 MessageManager.getString("label.save_feature_colours"));
1031 chooser.setToolTipText(MessageManager.getString("action.save"));
1033 int value = chooser.showSaveDialog(this);
1035 if (value == JalviewFileChooser.APPROVE_OPTION)
1037 save(chooser.getSelectedFile());
1042 * Saves feature colours and filters to the given file
1046 void save(File file)
1048 JalviewUserColours ucs = new JalviewUserColours();
1049 ucs.setSchemeName("Sequence Features");
1052 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1053 new FileOutputStream(file), "UTF-8"));
1056 * sort feature types by colour order, from 0 (highest)
1059 Set<String> fr_colours = fr.getAllFeatureColours();
1060 String[] sortedTypes = fr_colours
1061 .toArray(new String[fr_colours.size()]);
1062 Arrays.sort(sortedTypes, new Comparator<String>()
1065 public int compare(String type1, String type2)
1067 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1072 * save feature colours
1074 for (String featureType : sortedTypes)
1076 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1077 jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
1083 * save any feature filters
1085 for (String featureType : sortedTypes)
1087 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1088 if (filter != null && !filter.isEmpty())
1090 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1091 FeatureMatcherI firstMatcher = iterator.next();
1092 MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
1094 Filter filterModel = new Filter();
1095 filterModel.setFeatureType(featureType);
1096 filterModel.setMatcherSet(ms);
1097 ucs.addFilter(filterModel);
1103 } catch (Exception ex)
1105 ex.printStackTrace();
1109 public void invertSelection()
1111 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1112 for (int i = 0; i < data.length; i++)
1114 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1116 updateFeatureRenderer(data, true);
1120 public void orderByAvWidth()
1122 if (table == null || table.getModel() == null)
1126 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1127 float[] width = new float[data.length];
1131 for (int i = 0; i < data.length; i++)
1133 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1136 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1137 // weight - but have to make per
1138 // sequence, too (awidth[2])
1139 // if (width[i]==1) // hack to distinguish single width sequences.
1150 boolean sort = false;
1151 for (int i = 0; i < width.length; i++)
1153 // awidth = (float[]) typeWidth.get(data[i][0]);
1156 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1159 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1165 width[i] /= max; // normalize
1166 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1170 sort = sort || width[i - 1] > width[i];
1175 jalview.util.QuickSort.sort(width, data);
1176 // update global priority order
1179 updateFeatureRenderer(data, false);
1187 frame.setClosed(true);
1188 } catch (Exception exe)
1194 public void updateFeatureRenderer(Object[][] data)
1196 updateFeatureRenderer(data, true);
1200 * Update the priority order of features; only repaint if this changed the order
1201 * of visible features
1206 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1208 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1210 if (fr.setFeaturePriority(rowData, visibleNew))
1212 af.alignPanel.paintAlignment(true, true);
1217 * Converts table data into an array of data beans
1219 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1221 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1222 for (int i = 0; i < data.length; i++)
1224 String type = (String) data[i][TYPE_COLUMN];
1225 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1226 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1227 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1228 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1234 private void jbInit() throws Exception
1236 this.setLayout(new BorderLayout());
1238 JPanel settingsPane = new JPanel();
1239 settingsPane.setLayout(new BorderLayout());
1241 dasSettingsPane.setLayout(new BorderLayout());
1243 JPanel bigPanel = new JPanel();
1244 bigPanel.setLayout(new BorderLayout());
1246 groupPanel = new JPanel();
1247 bigPanel.add(groupPanel, BorderLayout.NORTH);
1249 JButton invert = new JButton(
1250 MessageManager.getString("label.invert_selection"));
1251 invert.setFont(JvSwingUtils.getLabelFont());
1252 invert.addActionListener(new ActionListener()
1255 public void actionPerformed(ActionEvent e)
1261 JButton optimizeOrder = new JButton(
1262 MessageManager.getString("label.optimise_order"));
1263 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1264 optimizeOrder.addActionListener(new ActionListener()
1267 public void actionPerformed(ActionEvent e)
1273 JButton sortByScore = new JButton(
1274 MessageManager.getString("label.seq_sort_by_score"));
1275 sortByScore.setFont(JvSwingUtils.getLabelFont());
1276 sortByScore.addActionListener(new ActionListener()
1279 public void actionPerformed(ActionEvent e)
1281 af.avc.sortAlignmentByFeatureScore(null);
1284 JButton sortByDens = new JButton(
1285 MessageManager.getString("label.sequence_sort_by_density"));
1286 sortByDens.setFont(JvSwingUtils.getLabelFont());
1287 sortByDens.addActionListener(new ActionListener()
1290 public void actionPerformed(ActionEvent e)
1292 af.avc.sortAlignmentByFeatureDensity(null);
1296 JButton help = new JButton(MessageManager.getString("action.help"));
1297 help.setFont(JvSwingUtils.getLabelFont());
1298 help.addActionListener(new ActionListener()
1301 public void actionPerformed(ActionEvent e)
1305 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1306 } catch (HelpSetException e1)
1308 e1.printStackTrace();
1312 help.setFont(JvSwingUtils.getLabelFont());
1313 help.setText(MessageManager.getString("action.help"));
1314 help.addActionListener(new ActionListener()
1317 public void actionPerformed(ActionEvent e)
1321 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1322 } catch (HelpSetException e1)
1324 e1.printStackTrace();
1329 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1330 cancel.setFont(JvSwingUtils.getLabelFont());
1331 cancel.addActionListener(new ActionListener()
1334 public void actionPerformed(ActionEvent e)
1336 fr.setTransparency(originalTransparency);
1337 fr.setFeatureFilters(originalFilters);
1338 updateFeatureRenderer(originalData);
1343 JButton ok = new JButton(MessageManager.getString("action.ok"));
1344 ok.setFont(JvSwingUtils.getLabelFont());
1345 ok.addActionListener(new ActionListener()
1348 public void actionPerformed(ActionEvent e)
1354 JButton loadColours = new JButton(
1355 MessageManager.getString("label.load_colours"));
1356 loadColours.setFont(JvSwingUtils.getLabelFont());
1357 loadColours.setToolTipText(
1358 MessageManager.getString("label.load_colours_tooltip"));
1359 loadColours.addActionListener(new ActionListener()
1362 public void actionPerformed(ActionEvent e)
1368 JButton saveColours = new JButton(
1369 MessageManager.getString("label.save_colours"));
1370 saveColours.setFont(JvSwingUtils.getLabelFont());
1371 saveColours.setToolTipText(
1372 MessageManager.getString("label.save_colours_tooltip"));
1373 saveColours.addActionListener(new ActionListener()
1376 public void actionPerformed(ActionEvent e)
1381 transparency.addChangeListener(new ChangeListener()
1384 public void stateChanged(ChangeEvent evt)
1386 if (!inConstruction)
1388 fr.setTransparency((100 - transparency.getValue()) / 100f);
1389 af.alignPanel.paintAlignment(true, true);
1394 summaryView = new JCheckBox(
1395 MessageManager.getString("label.summary_view"));
1398 MessageManager.getString("label.summary_view_tip"));
1399 summaryView.addActionListener(new ActionListener()
1402 public void actionPerformed(ActionEvent e)
1408 transparency.setMaximum(70);
1409 transparency.setToolTipText(
1410 MessageManager.getString("label.transparency_tip"));
1411 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1412 fetchDAS.addActionListener(new ActionListener()
1415 public void actionPerformed(ActionEvent e)
1417 fetchDAS_actionPerformed(e);
1420 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1421 saveDAS.addActionListener(new ActionListener()
1424 public void actionPerformed(ActionEvent e)
1426 saveDAS_actionPerformed(e);
1430 JPanel dasButtonPanel = new JPanel();
1431 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1432 dasSettingsPane.setBorder(null);
1433 cancelDAS.setEnabled(false);
1434 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1435 cancelDAS.addActionListener(new ActionListener()
1438 public void actionPerformed(ActionEvent e)
1440 cancelDAS_actionPerformed(e);
1444 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1445 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1447 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1448 transbuttons.add(optimizeOrder);
1449 transbuttons.add(invert);
1450 transbuttons.add(sortByScore);
1451 transbuttons.add(sortByDens);
1452 transbuttons.add(help);
1453 JPanel transPanel = new JPanel(new GridLayout(3, 1));
1454 transPanel.add(summaryView);
1455 transPanel.add(new JLabel(" Colour transparency" + ":"));
1456 transPanel.add(transparency);
1457 lowerPanel.add(transPanel);
1458 lowerPanel.add(transbuttons);
1460 JPanel buttonPanel = new JPanel();
1461 buttonPanel.add(ok);
1462 buttonPanel.add(cancel);
1463 buttonPanel.add(loadColours);
1464 buttonPanel.add(saveColours);
1465 bigPanel.add(scrollPane, BorderLayout.CENTER);
1466 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1467 dasButtonPanel.add(fetchDAS);
1468 dasButtonPanel.add(cancelDAS);
1469 dasButtonPanel.add(saveDAS);
1470 settingsPane.add(bigPanel, BorderLayout.CENTER);
1471 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1472 this.add(settingsPane);
1475 public void fetchDAS_actionPerformed(ActionEvent e)
1477 fetchDAS.setEnabled(false);
1478 cancelDAS.setEnabled(true);
1479 dassourceBrowser.setGuiEnabled(false);
1480 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1481 .getSelectedSources();
1482 doDasFeatureFetch(selectedSources, true, true);
1486 * get the features from selectedSources for all or the current selection
1488 * @param selectedSources
1489 * @param checkDbRefs
1490 * @param promptFetchDbRefs
1492 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1493 boolean checkDbRefs, boolean promptFetchDbRefs)
1495 SequenceI[] dataset, seqs;
1497 AlignmentViewport vp = af.getViewport();
1498 if (vp.getSelectionGroup() != null
1499 && vp.getSelectionGroup().getSize() > 0)
1501 iSize = vp.getSelectionGroup().getSize();
1502 dataset = new SequenceI[iSize];
1503 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1507 iSize = vp.getAlignment().getHeight();
1508 seqs = vp.getAlignment().getSequencesArray();
1511 dataset = new SequenceI[iSize];
1512 for (int i = 0; i < iSize; i++)
1514 dataset[i] = seqs[i].getDatasetSequence();
1517 cancelDAS.setEnabled(true);
1518 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1519 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1520 af.getViewport().setShowSequenceFeatures(true);
1521 af.showSeqFeatures.setSelected(true);
1525 * blocking call to initialise the das source browser
1527 public void initDasSources()
1529 dassourceBrowser.initDasSources();
1533 * examine the current list of das sources and return any matching the given
1534 * nicknames in sources
1537 * Vector of Strings to resolve to DAS source nicknames.
1538 * @return sources that are present in source list.
1540 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1542 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1546 * get currently selected das sources. ensure you have called initDasSources
1547 * before calling this.
1549 * @return vector of selected das source nicknames
1551 public Vector<jalviewSourceI> getSelectedSources()
1553 return dassourceBrowser.getSelectedSources();
1557 * properly initialise DAS fetcher and then initiate a new thread to fetch
1558 * features from the named sources (rather than any turned on by default)
1562 * if true then runs in same thread, otherwise passes to the Swing
1565 public void fetchDasFeatures(Vector<String> sources, boolean block)
1568 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1569 .resolveSourceNicknames(sources);
1570 if (resolved.size() == 0)
1572 resolved = dassourceBrowser.getSelectedSources();
1574 if (resolved.size() > 0)
1576 final List<jalviewSourceI> dassources = resolved;
1577 fetchDAS.setEnabled(false);
1578 // cancelDAS.setEnabled(true); doDasFetch does this.
1579 Runnable fetcher = new Runnable()
1585 doDasFeatureFetch(dassources, true, false);
1595 SwingUtilities.invokeLater(fetcher);
1600 public void saveDAS_actionPerformed(ActionEvent e)
1603 .saveProperties(jalview.bin.Cache.applicationProperties);
1606 public void complete()
1608 fetchDAS.setEnabled(true);
1609 cancelDAS.setEnabled(false);
1610 dassourceBrowser.setGuiEnabled(true);
1614 public void cancelDAS_actionPerformed(ActionEvent e)
1616 if (dasFeatureFetcher != null)
1618 dasFeatureFetcher.cancel();
1623 public void noDasSourceActive()
1626 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1627 MessageManager.getString("label.no_das_sources_selected_warn"),
1628 MessageManager.getString("label.no_das_sources_selected_title"),
1629 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1633 * Reorders features by 'dragging' selectedRow to 'newRow'
1637 protected void dragRow(int newRow)
1639 if (summaryView.isSelected())
1641 // no drag while in summary view
1645 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1648 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1649 * this could be more than one row away for a very fast drag action
1650 * so just swap it with adjacent rows until we get it there
1652 Object[][] data = ((FeatureTableModel) table.getModel())
1654 int direction = newRow < selectedRow ? -1 : 1;
1655 for (int i = selectedRow; i != newRow; i += direction)
1657 Object[] temp = data[i];
1658 data[i] = data[i + direction];
1659 data[i + direction] = temp;
1661 updateFeatureRenderer(data);
1663 selectedRow = newRow;
1667 protected void refreshTable()
1669 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1670 for (Object[] row : data)
1672 String type = (String) row[TYPE_COLUMN];
1673 FeatureColourI colour = fr.getFeatureColours().get(type);
1674 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1677 filter = new FeatureMatcherSet();
1679 row[COLOUR_COLUMN] = colour;
1680 row[FILTER_COLUMN] = filter;
1685 // ///////////////////////////////////////////////////////////////////////
1686 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1687 // ///////////////////////////////////////////////////////////////////////
1688 class FeatureTableModel extends AbstractTableModel
1690 private String[] columnNames = {
1691 MessageManager.getString("label.feature_type"),
1692 MessageManager.getString("action.colour"),
1693 MessageManager.getString("label.filter"),
1694 MessageManager.getString("label.show") };
1696 private Object[][] data;
1698 FeatureTableModel(Object[][] data)
1703 public Object[][] getData()
1708 public void setData(Object[][] data)
1714 public int getColumnCount()
1716 return columnNames.length;
1719 public Object[] getRow(int row)
1725 public int getRowCount()
1731 public String getColumnName(int col)
1733 return columnNames[col];
1737 public Object getValueAt(int row, int col)
1739 return data[row][col];
1743 * Answers the class of the object in column c of the first row of the table
1746 public Class<?> getColumnClass(int c)
1748 Object v = getValueAt(0, c);
1749 return v == null ? null : v.getClass();
1753 * Answers true for all columns except Feature Type
1756 public boolean isCellEditable(int row, int col)
1758 return col != TYPE_COLUMN;
1762 * Sets the value in the model for a given row and column. If Visibility
1763 * (Show/Hide) is being set, and the table is in Summary View, then it is
1764 * set also on any sub-types of the row's feature type.
1767 public void setValueAt(Object value, int row, int col)
1769 data[row][col] = value;
1770 fireTableCellUpdated(row, col);
1771 if (summaryView.isSelected() && col == SHOW_COLUMN)
1773 setSubtypesVisibility(row, (Boolean) value);
1775 updateFeatureRenderer(data);
1779 * Sets the visibility of any feature types which are sub-types of the type
1780 * in the given row of the table
1785 protected void setSubtypesVisibility(int row, Boolean value)
1787 String type = (String) data[row][TYPE_COLUMN];
1788 OntologyI so = SequenceOntologyFactory.getInstance();
1790 for (int r = 0; r < data.length; r++)
1794 String type2 = (String) data[r][TYPE_COLUMN];
1795 if (so.isA(type2, type))
1797 data[r][SHOW_COLUMN] = value;
1798 fireTableCellUpdated(r, SHOW_COLUMN);
1806 class ColorRenderer extends JLabel implements TableCellRenderer
1808 javax.swing.border.Border unselectedBorder = null;
1810 javax.swing.border.Border selectedBorder = null;
1812 final String baseTT = "Click to edit, right/apple click for menu.";
1814 public ColorRenderer()
1816 setOpaque(true); // MUST do this for background to show up.
1817 setHorizontalTextPosition(SwingConstants.CENTER);
1818 setVerticalTextPosition(SwingConstants.CENTER);
1822 public Component getTableCellRendererComponent(JTable tbl, Object color,
1823 boolean isSelected, boolean hasFocus, int row, int column)
1825 FeatureColourI cellColour = (FeatureColourI) color;
1827 setToolTipText(baseTT);
1828 setBackground(tbl.getBackground());
1829 if (!cellColour.isSimpleColour())
1831 Rectangle cr = tbl.getCellRect(row, column, false);
1832 FeatureSettings.renderGraduatedColor(this, cellColour,
1833 (int) cr.getWidth(), (int) cr.getHeight());
1839 setBackground(cellColour.getColour());
1843 if (selectedBorder == null)
1845 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1846 tbl.getSelectionBackground());
1848 setBorder(selectedBorder);
1852 if (unselectedBorder == null)
1854 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1855 tbl.getBackground());
1857 setBorder(unselectedBorder);
1864 class FilterRenderer extends JLabel implements TableCellRenderer
1866 javax.swing.border.Border unselectedBorder = null;
1868 javax.swing.border.Border selectedBorder = null;
1870 public FilterRenderer()
1872 setOpaque(true); // MUST do this for background to show up.
1873 setHorizontalTextPosition(SwingConstants.CENTER);
1874 setVerticalTextPosition(SwingConstants.CENTER);
1878 public Component getTableCellRendererComponent(JTable tbl,
1879 Object filter, boolean isSelected, boolean hasFocus, int row,
1882 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1884 String asText = theFilter.toString();
1885 setBackground(tbl.getBackground());
1886 this.setText(asText);
1891 if (selectedBorder == null)
1893 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1894 tbl.getSelectionBackground());
1896 setBorder(selectedBorder);
1900 if (unselectedBorder == null)
1902 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1903 tbl.getBackground());
1905 setBorder(unselectedBorder);
1913 * update comp using rendering settings from gcol
1918 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1920 int w = comp.getWidth(), h = comp.getHeight();
1923 w = (int) comp.getPreferredSize().getWidth();
1924 h = (int) comp.getPreferredSize().getHeight();
1931 renderGraduatedColor(comp, gcol, w, h);
1934 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1937 boolean thr = false;
1938 StringBuilder tt = new StringBuilder();
1939 StringBuilder tx = new StringBuilder();
1941 if (gcol.isColourByAttribute())
1943 tx.append(String.join(":", gcol.getAttributeName()));
1945 else if (!gcol.isColourByLabel())
1947 tx.append(MessageManager.getString("label.score"));
1950 if (gcol.isAboveThreshold())
1954 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1957 if (gcol.isBelowThreshold())
1961 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1964 if (gcol.isColourByLabel())
1966 tt.append("Coloured by label text. ").append(tt);
1971 if (!gcol.isColourByAttribute())
1979 Color newColor = gcol.getMaxColour();
1980 comp.setBackground(newColor);
1981 // System.err.println("Width is " + w / 2);
1982 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1983 comp.setIcon(ficon);
1984 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1985 // + newColor.getGreen() + ", " + newColor.getBlue()
1986 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1987 // + ", " + minCol.getBlue() + ")");
1989 comp.setHorizontalAlignment(SwingConstants.CENTER);
1990 comp.setText(tx.toString());
1991 if (tt.length() > 0)
1993 if (comp.getToolTipText() == null)
1995 comp.setToolTipText(tt.toString());
1999 comp.setToolTipText(
2000 tt.append(" ").append(comp.getToolTipText()).toString());
2005 class ColorEditor extends AbstractCellEditor
2006 implements TableCellEditor, ActionListener
2010 FeatureColourI currentColor;
2012 FeatureTypeSettings chooser;
2016 JButton colourButton;
2018 JColorChooser colorChooser;
2022 protected static final String EDIT = "edit";
2024 int rowSelected = 0;
2026 public ColorEditor(FeatureSettings me)
2029 // Set up the editor (from the table's point of view),
2030 // which is a button.
2031 // This button brings up the color chooser dialog,
2032 // which is the editor from the user's point of view.
2033 colourButton = new JButton();
2034 colourButton.setActionCommand(EDIT);
2035 colourButton.addActionListener(this);
2036 colourButton.setBorderPainted(false);
2037 // Set up the dialog that the button brings up.
2038 colorChooser = new JColorChooser();
2039 dialog = JColorChooser.createDialog(colourButton,
2040 MessageManager.getString("label.select_colour"), true, // modal
2041 colorChooser, this, // OK button handler
2042 null); // no CANCEL button handler
2046 * Handles events from the editor button and from the dialog's OK button.
2049 public void actionPerformed(ActionEvent e)
2051 // todo test e.getSource() instead here
2052 if (EDIT.equals(e.getActionCommand()))
2054 // The user has clicked the cell, so
2055 // bring up the dialog.
2056 if (currentColor.isSimpleColour())
2058 // bring up simple color chooser
2059 colourButton.setBackground(currentColor.getColour());
2060 colorChooser.setColor(currentColor.getColour());
2061 dialog.setVisible(true);
2065 // bring up graduated chooser.
2066 chooser = new FeatureTypeSettings(me.fr, type);
2067 chooser.setRequestFocusEnabled(true);
2068 chooser.requestFocus();
2069 chooser.addActionListener(this);
2070 chooser.showTab(true);
2072 // Make the renderer reappear.
2073 fireEditingStopped();
2077 if (currentColor.isSimpleColour())
2080 * read off colour picked in colour chooser after OK pressed
2082 currentColor = new FeatureColour(colorChooser.getColor());
2083 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
2088 * after OK in variable colour dialog, any changes to colour
2089 * (or filters!) are already set in FeatureRenderer, so just
2090 * update table data without triggering updateFeatureRenderer
2094 fireEditingStopped();
2095 me.table.validate();
2099 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2101 public Object getCellEditorValue()
2103 return currentColor;
2106 // Implement the one method defined by TableCellEditor.
2108 public Component getTableCellEditorComponent(JTable theTable, Object value,
2109 boolean isSelected, int row, int column)
2111 currentColor = (FeatureColourI) value;
2112 this.rowSelected = row;
2113 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2114 colourButton.setOpaque(true);
2115 colourButton.setBackground(me.getBackground());
2116 if (!currentColor.isSimpleColour())
2118 JLabel btn = new JLabel();
2119 btn.setSize(colourButton.getSize());
2120 FeatureSettings.renderGraduatedColor(btn, currentColor);
2121 colourButton.setBackground(btn.getBackground());
2122 colourButton.setIcon(btn.getIcon());
2123 colourButton.setText(btn.getText());
2127 colourButton.setText("");
2128 colourButton.setIcon(null);
2129 colourButton.setBackground(currentColor.getColour());
2131 return colourButton;
2136 * The cell editor for the Filter column. It displays the text of any filters
2137 * for the feature type in that row (in full as a tooltip, possible abbreviated
2138 * as display text). On click in the cell, opens the Feature Display Settings
2139 * dialog at the Filters tab.
2141 class FilterEditor extends AbstractCellEditor
2142 implements TableCellEditor, ActionListener
2146 FeatureMatcherSetI currentFilter;
2152 JButton filterButton;
2154 protected static final String EDIT = "edit";
2156 int rowSelected = 0;
2158 public FilterEditor(FeatureSettings me)
2161 filterButton = new JButton();
2162 filterButton.setActionCommand(EDIT);
2163 filterButton.addActionListener(this);
2164 filterButton.setBorderPainted(false);
2168 * Handles events from the editor button
2171 public void actionPerformed(ActionEvent e)
2173 if (filterButton == e.getSource())
2175 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2176 chooser.addActionListener(this);
2177 chooser.setRequestFocusEnabled(true);
2178 chooser.requestFocus();
2179 if (lastLocation != null)
2181 // todo open at its last position on screen
2182 chooser.setBounds(lastLocation.x, lastLocation.y,
2183 chooser.getWidth(), chooser.getHeight());
2186 chooser.showTab(false);
2187 fireEditingStopped();
2189 else if (e.getSource() instanceof Component)
2192 * after OK in variable colour dialog, any changes to filter
2193 * (or colours!) are already set in FeatureRenderer, so just
2194 * update table data without triggering updateFeatureRenderer
2197 fireEditingStopped();
2198 me.table.validate();
2203 public Object getCellEditorValue()
2205 return currentFilter;
2209 public Component getTableCellEditorComponent(JTable theTable, Object value,
2210 boolean isSelected, int row, int column)
2212 currentFilter = (FeatureMatcherSetI) value;
2213 this.rowSelected = row;
2214 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2215 filterButton.setOpaque(true);
2216 filterButton.setBackground(me.getBackground());
2217 filterButton.setText(currentFilter.toString());
2218 filterButton.setToolTipText(currentFilter.toString());
2219 filterButton.setIcon(null);
2220 return filterButton;
2225 class FeatureIcon implements Icon
2227 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2229 FeatureColourI gcol;
2233 boolean midspace = false;
2235 int width = 50, height = 20;
2237 int s1, e1; // start and end of midpoint band for thresholded symbol
2239 Color mpcolour = Color.white;
2241 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2261 public int getIconWidth()
2267 public int getIconHeight()
2273 public void paintIcon(Component c, Graphics g, int x, int y)
2276 if (gcol.isColourByLabel())
2279 g.fillRect(0, 0, width, height);
2280 // need an icon here.
2281 g.setColor(gcol.getMaxColour());
2283 g.setFont(VERDANA_9);
2285 // g.setFont(g.getFont().deriveFont(
2286 // AffineTransform.getScaleInstance(
2287 // width/g.getFontMetrics().stringWidth("Label"),
2288 // height/g.getFontMetrics().getHeight())));
2290 g.drawString(MessageManager.getString("label.label"), 0, 0);
2295 Color minCol = gcol.getMinColour();
2297 g.fillRect(0, 0, s1, height);
2300 g.setColor(Color.white);
2301 g.fillRect(s1, 0, e1 - s1, height);
2303 g.setColor(gcol.getMaxColour());
2304 g.fillRect(0, e1, width - e1, height);