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 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
366 invertSelection, extendSelection, toggleSelection, type);
370 // isPopupTrigger fires on mouseReleased on Windows
372 public void mouseReleased(MouseEvent evt)
374 selectedRow = table.rowAtPoint(evt.getPoint());
375 if (evt.isPopupTrigger())
377 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
378 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
379 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
384 table.addMouseMotionListener(new MouseMotionAdapter()
387 public void mouseDragged(MouseEvent evt)
389 int newRow = table.rowAtPoint(evt.getPoint());
395 protected void popupMenu(final int rowSelected, final String type,
396 final Object typeCol, int x, int y)
398 final FeatureColourI featureColour = (FeatureColourI) typeCol;
400 JPopupMenu men = new JPopupMenu(MessageManager
401 .formatMessage("label.settings_for_param", new String[]
403 JMenuItem scr = new JMenuItem(
404 MessageManager.getString("label.sort_by_score"));
406 final FeatureSettings me = this;
407 scr.addActionListener(new ActionListener()
411 public void actionPerformed(ActionEvent e)
414 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
419 JMenuItem dens = new JMenuItem(
420 MessageManager.getString("label.sort_by_density"));
421 dens.addActionListener(new ActionListener()
425 public void actionPerformed(ActionEvent e)
428 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
436 * variable colour options include colour by label, by score,
437 * by selected attribute text, or attribute value
439 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
440 MessageManager.getString("label.variable_colour"));
441 mxcol.setSelected(!featureColour.isSimpleColour());
443 mxcol.addActionListener(new ActionListener()
445 JColorChooser colorChooser;
448 public void actionPerformed(ActionEvent e)
450 if (e.getSource() == mxcol)
452 if (featureColour.isSimpleColour())
454 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
455 fc.addActionListener(this);
459 // bring up simple color chooser
460 colorChooser = new JColorChooser();
461 String title = MessageManager
462 .getString("label.select_colour");
463 JDialog dialog = JColorChooser.createDialog(me,
464 title, true, // modal
465 colorChooser, this, // OK button handler
466 null); // no CANCEL button handler
467 colorChooser.setColor(featureColour.getMaxColour());
468 dialog.setVisible(true);
473 if (e.getSource() instanceof FeatureTypeSettings)
476 * update after OK in feature colour dialog; the updated
477 * colour will have already been set in the FeatureRenderer
479 FeatureColourI fci = fr.getFeatureColours().get(type);
480 table.setValueAt(fci, rowSelected, 1);
485 // probably the color chooser!
486 table.setValueAt(new FeatureColour(colorChooser.getColor()),
489 me.updateFeatureRenderer(
490 ((FeatureTableModel) table.getModel()).getData(),
497 JMenuItem selCols = new JMenuItem(
498 MessageManager.getString("label.select_columns_containing"));
499 selCols.addActionListener(new ActionListener()
502 public void actionPerformed(ActionEvent arg0)
504 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
508 JMenuItem clearCols = new JMenuItem(MessageManager
509 .getString("label.select_columns_not_containing"));
510 clearCols.addActionListener(new ActionListener()
513 public void actionPerformed(ActionEvent arg0)
515 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
519 JMenuItem hideCols = new JMenuItem(
520 MessageManager.getString("label.hide_columns_containing"));
521 hideCols.addActionListener(new ActionListener()
524 public void actionPerformed(ActionEvent arg0)
526 fr.ap.alignFrame.hideFeatureColumns(type, true);
529 JMenuItem hideOtherCols = new JMenuItem(
530 MessageManager.getString("label.hide_columns_not_containing"));
531 hideOtherCols.addActionListener(new ActionListener()
534 public void actionPerformed(ActionEvent arg0)
536 fr.ap.alignFrame.hideFeatureColumns(type, false);
542 men.add(hideOtherCols);
543 men.show(table, x, y);
547 synchronized public void discoverAllFeatureData()
549 Set<String> allGroups = new HashSet<>();
550 AlignmentI alignment = af.getViewport().getAlignment();
552 for (int i = 0; i < alignment.getHeight(); i++)
554 SequenceI seq = alignment.getSequenceAt(i);
555 for (String group : seq.getFeatures().getFeatureGroups(true))
557 if (group != null && !allGroups.contains(group))
559 allGroups.add(group);
560 checkGroupState(group);
571 * Synchronise gui group list and check visibility of group
574 * @return true if group is visible
576 private boolean checkGroupState(String group)
578 boolean visible = fr.checkGroupVisibility(group, true);
580 for (int g = 0; g < groupPanel.getComponentCount(); g++)
582 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
584 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
589 final String grp = group;
590 final JCheckBox check = new JCheckBox(group, visible);
591 check.setFont(new Font("Serif", Font.BOLD, 12));
592 check.setToolTipText(group);
593 check.addItemListener(new ItemListener()
596 public void itemStateChanged(ItemEvent evt)
598 fr.setGroupVisibility(check.getText(), check.isSelected());
599 resetTable(new String[] { grp });
600 af.alignPanel.paintAlignment(true, true);
603 groupPanel.add(check);
607 synchronized void resetTable(String[] groupChanged)
613 resettingTable = true;
614 typeWidth = new Hashtable<>();
615 // TODO: change avWidth calculation to 'per-sequence' average and use long
618 Set<String> displayableTypes = new HashSet<>();
619 Set<String> foundGroups = new HashSet<>();
622 * determine which feature types may be visible depending on
623 * which groups are selected, and recompute average width data
625 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
628 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
631 * get the sequence's groups for positional features
632 * and keep track of which groups are visible
634 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
635 Set<String> visibleGroups = new HashSet<>();
636 for (String group : groups)
638 if (group == null || checkGroupState(group))
640 visibleGroups.add(group);
643 foundGroups.addAll(groups);
646 * get distinct feature types for visible groups
647 * record distinct visible types, and their count and total length
649 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
650 visibleGroups.toArray(new String[visibleGroups.size()]));
652 for (String type : types)
654 displayableTypes.add(type);
655 float[] avWidth = typeWidth.get(type);
658 avWidth = new float[2];
659 typeWidth.put(type, avWidth);
661 // todo this could include features with a non-visible group
662 // - do we greatly care?
663 // todo should we include non-displayable features here, and only
664 // update when features are added?
665 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
666 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
671 * enable 'Summary View' if some types are sub-types of others
673 Set<String> parents = SequenceOntologyFactory.getInstance()
674 .getParentTerms(displayableTypes);
675 summaryView.setEnabled(parents.size() < displayableTypes.size());
677 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
680 if (fr.hasRenderOrder())
684 fr.findAllFeatures(groupChanged != null); // prod to update
685 // colourschemes. but don't
687 // First add the checks in the previous render order,
688 // in case the window has been closed and reopened
690 List<String> frl = fr.getRenderOrder();
691 for (int ro = frl.size() - 1; ro > -1; ro--)
693 String type = frl.get(ro);
695 if (!displayableTypes.contains(type))
699 data[dataIndex][TYPE_COLUMN] = type;
700 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
701 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
702 data[dataIndex][FILTER_COLUMN] = featureFilter == null
703 ? new FeatureMatcherSet()
705 data[dataIndex][SHOW_COLUMN] = new Boolean(
706 af.getViewport().getFeaturesDisplayed().isVisible(type));
708 displayableTypes.remove(type);
713 * process any extra features belonging only to
714 * a group which was just selected
716 while (!displayableTypes.isEmpty())
718 String type = displayableTypes.iterator().next();
719 data[dataIndex][TYPE_COLUMN] = type;
721 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
722 if (data[dataIndex][COLOUR_COLUMN] == null)
724 // "Colour has been updated in another view!!"
725 fr.clearRenderOrder();
728 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
729 data[dataIndex][FILTER_COLUMN] = featureFilter == null
730 ? new FeatureMatcherSet()
732 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
734 displayableTypes.remove(type);
737 if (originalData == null)
739 originalData = new Object[data.length][COLUMN_COUNT];
740 for (int i = 0; i < data.length; i++)
742 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
747 updateOriginalData(data);
751 * recreate the table model
753 FeatureTableModel dataModel = new FeatureTableModel(data);
754 table.setModel(dataModel);
757 * we want to be able to filter out rows for sub-types, but not to sort
758 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
760 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
762 for (int i = 0; i < table.getColumnCount(); i++)
764 sorter.setSortable(i, false);
768 * filter rows to only top-level Ontology types if requested
770 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
773 public boolean include(
774 Entry<? extends FeatureTableModel, ? extends Integer> entry)
776 if (!summaryView.isSelected())
780 int row = entry.getIdentifier(); // this is model, not view, row number
781 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
782 return parents.contains(featureType);
785 table.setRowSorter(sorter);
787 table.getColumnModel().getColumn(0).setPreferredWidth(200);
789 groupPanel.setLayout(
790 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
791 pruneGroups(foundGroups);
792 groupPanel.validate();
794 updateFeatureRenderer(data, groupChanged != null);
795 resettingTable = false;
799 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
800 * have been made outwith this dialog
802 * <li>a new feature type added (and made visible)</li>
803 * <li>a feature colour changed (in the Amend Features dialog)</li>
808 protected void updateOriginalData(Object[][] foundData)
810 // todo LinkedHashMap instead of Object[][] would be nice
812 Object[][] currentData = ((FeatureTableModel) table.getModel())
814 for (Object[] row : foundData)
816 String type = (String) row[TYPE_COLUMN];
817 boolean found = false;
818 for (Object[] current : currentData)
820 if (type.equals(current[TYPE_COLUMN]))
824 * currently dependent on object equality here;
825 * really need an equals method on FeatureColour
827 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
830 * feature colour has changed externally - update originalData
832 for (Object[] original : originalData)
834 if (type.equals(original[TYPE_COLUMN]))
836 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
847 * new feature detected - add to original data (on top)
849 Object[][] newData = new Object[originalData.length
851 for (int i = 0; i < originalData.length; i++)
853 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
857 originalData = newData;
863 * Remove from the groups panel any checkboxes for groups that are not in the
864 * foundGroups set. This enables removing a group from the display when the last
865 * feature in that group is deleted.
869 protected void pruneGroups(Set<String> foundGroups)
871 for (int g = 0; g < groupPanel.getComponentCount(); g++)
873 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
874 if (!foundGroups.contains(checkbox.getText()))
876 groupPanel.remove(checkbox);
882 * reorder data based on the featureRenderers global priority list.
886 private void ensureOrder(Object[][] data)
888 boolean sort = false;
889 float[] order = new float[data.length];
890 for (int i = 0; i < order.length; i++)
892 order[i] = fr.getOrder(data[i][0].toString());
895 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
899 sort = sort || order[i - 1] > order[i];
904 jalview.util.QuickSort.sort(order, data);
909 * Offers a file chooser dialog, and then loads the feature colours and
910 * filters from file in XML format and unmarshals to Jalview feature settings
914 JalviewFileChooser chooser = new JalviewFileChooser("fc",
915 SEQUENCE_FEATURE_COLOURS);
916 chooser.setFileView(new JalviewFileView());
917 chooser.setDialogTitle(
918 MessageManager.getString("label.load_feature_colours"));
919 chooser.setToolTipText(MessageManager.getString("action.load"));
921 int value = chooser.showOpenDialog(this);
923 if (value == JalviewFileChooser.APPROVE_OPTION)
925 File file = chooser.getSelectedFile();
931 * Loads feature colours and filters from XML stored in the given file
939 InputStreamReader in = new InputStreamReader(
940 new FileInputStream(file), "UTF-8");
942 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
945 * load feature colours
947 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
949 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
950 FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
951 fr.setColour(newcol.getName(), colour);
952 fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
956 * load feature filters; loaded filters will replace any that are
957 * currently defined, other defined filters are left unchanged
959 for (int i = 0; i < jucs.getFilterCount(); i++)
961 jalview.schemabinding.version2.Filter filterModel = jucs
963 String featureType = filterModel.getFeatureType();
964 FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
965 filterModel.getMatcherSet());
966 if (!filter.isEmpty())
968 fr.setFeatureFilter(featureType, filter);
973 * update feature settings table
978 Object[][] data = ((FeatureTableModel) table.getModel())
981 updateFeatureRenderer(data, false);
984 } catch (Exception ex)
986 System.out.println("Error loading User Colour File\n" + ex);
991 * Offers a file chooser dialog, and then saves the current feature colours
992 * and any filters to the selected file in XML format
996 JalviewFileChooser chooser = new JalviewFileChooser("fc",
997 SEQUENCE_FEATURE_COLOURS);
998 chooser.setFileView(new JalviewFileView());
999 chooser.setDialogTitle(
1000 MessageManager.getString("label.save_feature_colours"));
1001 chooser.setToolTipText(MessageManager.getString("action.save"));
1003 int value = chooser.showSaveDialog(this);
1005 if (value == JalviewFileChooser.APPROVE_OPTION)
1007 save(chooser.getSelectedFile());
1012 * Saves feature colours and filters to the given file
1016 void save(File file)
1018 JalviewUserColours ucs = new JalviewUserColours();
1019 ucs.setSchemeName("Sequence Features");
1022 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1023 new FileOutputStream(file), "UTF-8"));
1026 * sort feature types by colour order, from 0 (highest)
1029 Set<String> fr_colours = fr.getAllFeatureColours();
1030 String[] sortedTypes = fr_colours
1031 .toArray(new String[fr_colours.size()]);
1032 Arrays.sort(sortedTypes, new Comparator<String>()
1035 public int compare(String type1, String type2)
1037 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1042 * save feature colours
1044 for (String featureType : sortedTypes)
1046 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1047 jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
1053 * save any feature filters
1055 for (String featureType : sortedTypes)
1057 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1058 if (filter != null && !filter.isEmpty())
1060 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1061 FeatureMatcherI firstMatcher = iterator.next();
1062 MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
1064 Filter filterModel = new Filter();
1065 filterModel.setFeatureType(featureType);
1066 filterModel.setMatcherSet(ms);
1067 ucs.addFilter(filterModel);
1073 } catch (Exception ex)
1075 ex.printStackTrace();
1079 public void invertSelection()
1081 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1082 for (int i = 0; i < data.length; i++)
1084 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1086 updateFeatureRenderer(data, true);
1090 public void orderByAvWidth()
1092 if (table == null || table.getModel() == null)
1096 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1097 float[] width = new float[data.length];
1101 for (int i = 0; i < data.length; i++)
1103 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1106 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1107 // weight - but have to make per
1108 // sequence, too (awidth[2])
1109 // if (width[i]==1) // hack to distinguish single width sequences.
1120 boolean sort = false;
1121 for (int i = 0; i < width.length; i++)
1123 // awidth = (float[]) typeWidth.get(data[i][0]);
1126 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1129 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1135 width[i] /= max; // normalize
1136 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1140 sort = sort || width[i - 1] > width[i];
1145 jalview.util.QuickSort.sort(width, data);
1146 // update global priority order
1149 updateFeatureRenderer(data, false);
1157 frame.setClosed(true);
1158 } catch (Exception exe)
1164 public void updateFeatureRenderer(Object[][] data)
1166 updateFeatureRenderer(data, true);
1170 * Update the priority order of features; only repaint if this changed the order
1171 * of visible features
1176 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1178 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1180 if (fr.setFeaturePriority(rowData, visibleNew))
1182 af.alignPanel.paintAlignment(true, true);
1187 * Converts table data into an array of data beans
1189 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1191 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1192 for (int i = 0; i < data.length; i++)
1194 String type = (String) data[i][TYPE_COLUMN];
1195 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1196 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1197 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1198 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1204 private void jbInit() throws Exception
1206 this.setLayout(new BorderLayout());
1208 JPanel settingsPane = new JPanel();
1209 settingsPane.setLayout(new BorderLayout());
1211 dasSettingsPane.setLayout(new BorderLayout());
1213 JPanel bigPanel = new JPanel();
1214 bigPanel.setLayout(new BorderLayout());
1216 groupPanel = new JPanel();
1217 bigPanel.add(groupPanel, BorderLayout.NORTH);
1219 JButton invert = new JButton(
1220 MessageManager.getString("label.invert_selection"));
1221 invert.setFont(JvSwingUtils.getLabelFont());
1222 invert.addActionListener(new ActionListener()
1225 public void actionPerformed(ActionEvent e)
1231 JButton optimizeOrder = new JButton(
1232 MessageManager.getString("label.optimise_order"));
1233 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1234 optimizeOrder.addActionListener(new ActionListener()
1237 public void actionPerformed(ActionEvent e)
1243 JButton sortByScore = new JButton(
1244 MessageManager.getString("label.seq_sort_by_score"));
1245 sortByScore.setFont(JvSwingUtils.getLabelFont());
1246 sortByScore.addActionListener(new ActionListener()
1249 public void actionPerformed(ActionEvent e)
1251 af.avc.sortAlignmentByFeatureScore(null);
1254 JButton sortByDens = new JButton(
1255 MessageManager.getString("label.sequence_sort_by_density"));
1256 sortByDens.setFont(JvSwingUtils.getLabelFont());
1257 sortByDens.addActionListener(new ActionListener()
1260 public void actionPerformed(ActionEvent e)
1262 af.avc.sortAlignmentByFeatureDensity(null);
1266 JButton help = new JButton(MessageManager.getString("action.help"));
1267 help.setFont(JvSwingUtils.getLabelFont());
1268 help.addActionListener(new ActionListener()
1271 public void actionPerformed(ActionEvent e)
1275 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1276 } catch (HelpSetException e1)
1278 e1.printStackTrace();
1282 help.setFont(JvSwingUtils.getLabelFont());
1283 help.setText(MessageManager.getString("action.help"));
1284 help.addActionListener(new ActionListener()
1287 public void actionPerformed(ActionEvent e)
1291 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1292 } catch (HelpSetException e1)
1294 e1.printStackTrace();
1299 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1300 cancel.setFont(JvSwingUtils.getLabelFont());
1301 cancel.addActionListener(new ActionListener()
1304 public void actionPerformed(ActionEvent e)
1306 fr.setTransparency(originalTransparency);
1307 fr.setFeatureFilters(originalFilters);
1308 updateFeatureRenderer(originalData);
1313 JButton ok = new JButton(MessageManager.getString("action.ok"));
1314 ok.setFont(JvSwingUtils.getLabelFont());
1315 ok.addActionListener(new ActionListener()
1318 public void actionPerformed(ActionEvent e)
1324 JButton loadColours = new JButton(
1325 MessageManager.getString("label.load_colours"));
1326 loadColours.setFont(JvSwingUtils.getLabelFont());
1327 loadColours.setToolTipText(
1328 MessageManager.getString("label.load_colours_tooltip"));
1329 loadColours.addActionListener(new ActionListener()
1332 public void actionPerformed(ActionEvent e)
1338 JButton saveColours = new JButton(
1339 MessageManager.getString("label.save_colours"));
1340 saveColours.setFont(JvSwingUtils.getLabelFont());
1341 saveColours.setToolTipText(
1342 MessageManager.getString("label.save_colours_tooltip"));
1343 saveColours.addActionListener(new ActionListener()
1346 public void actionPerformed(ActionEvent e)
1351 transparency.addChangeListener(new ChangeListener()
1354 public void stateChanged(ChangeEvent evt)
1356 if (!inConstruction)
1358 fr.setTransparency((100 - transparency.getValue()) / 100f);
1359 af.alignPanel.paintAlignment(true, true);
1364 summaryView = new JCheckBox(MessageManager.getString("summary_view"));
1366 .setToolTipText(MessageManager.getString("summary_view_tip"));
1367 summaryView.addActionListener(new ActionListener()
1370 public void actionPerformed(ActionEvent e)
1376 transparency.setMaximum(70);
1377 transparency.setToolTipText(
1378 MessageManager.getString("label.transparency_tip"));
1379 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1380 fetchDAS.addActionListener(new ActionListener()
1383 public void actionPerformed(ActionEvent e)
1385 fetchDAS_actionPerformed(e);
1388 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1389 saveDAS.addActionListener(new ActionListener()
1392 public void actionPerformed(ActionEvent e)
1394 saveDAS_actionPerformed(e);
1398 JPanel dasButtonPanel = new JPanel();
1399 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1400 dasSettingsPane.setBorder(null);
1401 cancelDAS.setEnabled(false);
1402 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1403 cancelDAS.addActionListener(new ActionListener()
1406 public void actionPerformed(ActionEvent e)
1408 cancelDAS_actionPerformed(e);
1412 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1413 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1415 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1416 transbuttons.add(optimizeOrder);
1417 transbuttons.add(invert);
1418 transbuttons.add(sortByScore);
1419 transbuttons.add(sortByDens);
1420 transbuttons.add(help);
1421 JPanel transPanel = new JPanel(new GridLayout(3, 1));
1422 transPanel.add(summaryView);
1423 transPanel.add(new JLabel(" Colour transparency" + ":"));
1424 transPanel.add(transparency);
1425 lowerPanel.add(transPanel);
1426 lowerPanel.add(transbuttons);
1428 JPanel buttonPanel = new JPanel();
1429 buttonPanel.add(ok);
1430 buttonPanel.add(cancel);
1431 buttonPanel.add(loadColours);
1432 buttonPanel.add(saveColours);
1433 bigPanel.add(scrollPane, BorderLayout.CENTER);
1434 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1435 dasButtonPanel.add(fetchDAS);
1436 dasButtonPanel.add(cancelDAS);
1437 dasButtonPanel.add(saveDAS);
1438 settingsPane.add(bigPanel, BorderLayout.CENTER);
1439 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1440 this.add(settingsPane);
1443 public void fetchDAS_actionPerformed(ActionEvent e)
1445 fetchDAS.setEnabled(false);
1446 cancelDAS.setEnabled(true);
1447 dassourceBrowser.setGuiEnabled(false);
1448 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1449 .getSelectedSources();
1450 doDasFeatureFetch(selectedSources, true, true);
1454 * get the features from selectedSources for all or the current selection
1456 * @param selectedSources
1457 * @param checkDbRefs
1458 * @param promptFetchDbRefs
1460 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1461 boolean checkDbRefs, boolean promptFetchDbRefs)
1463 SequenceI[] dataset, seqs;
1465 AlignmentViewport vp = af.getViewport();
1466 if (vp.getSelectionGroup() != null
1467 && vp.getSelectionGroup().getSize() > 0)
1469 iSize = vp.getSelectionGroup().getSize();
1470 dataset = new SequenceI[iSize];
1471 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1475 iSize = vp.getAlignment().getHeight();
1476 seqs = vp.getAlignment().getSequencesArray();
1479 dataset = new SequenceI[iSize];
1480 for (int i = 0; i < iSize; i++)
1482 dataset[i] = seqs[i].getDatasetSequence();
1485 cancelDAS.setEnabled(true);
1486 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1487 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1488 af.getViewport().setShowSequenceFeatures(true);
1489 af.showSeqFeatures.setSelected(true);
1493 * blocking call to initialise the das source browser
1495 public void initDasSources()
1497 dassourceBrowser.initDasSources();
1501 * examine the current list of das sources and return any matching the given
1502 * nicknames in sources
1505 * Vector of Strings to resolve to DAS source nicknames.
1506 * @return sources that are present in source list.
1508 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1510 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1514 * get currently selected das sources. ensure you have called initDasSources
1515 * before calling this.
1517 * @return vector of selected das source nicknames
1519 public Vector<jalviewSourceI> getSelectedSources()
1521 return dassourceBrowser.getSelectedSources();
1525 * properly initialise DAS fetcher and then initiate a new thread to fetch
1526 * features from the named sources (rather than any turned on by default)
1530 * if true then runs in same thread, otherwise passes to the Swing
1533 public void fetchDasFeatures(Vector<String> sources, boolean block)
1536 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1537 .resolveSourceNicknames(sources);
1538 if (resolved.size() == 0)
1540 resolved = dassourceBrowser.getSelectedSources();
1542 if (resolved.size() > 0)
1544 final List<jalviewSourceI> dassources = resolved;
1545 fetchDAS.setEnabled(false);
1546 // cancelDAS.setEnabled(true); doDasFetch does this.
1547 Runnable fetcher = new Runnable()
1553 doDasFeatureFetch(dassources, true, false);
1563 SwingUtilities.invokeLater(fetcher);
1568 public void saveDAS_actionPerformed(ActionEvent e)
1571 .saveProperties(jalview.bin.Cache.applicationProperties);
1574 public void complete()
1576 fetchDAS.setEnabled(true);
1577 cancelDAS.setEnabled(false);
1578 dassourceBrowser.setGuiEnabled(true);
1582 public void cancelDAS_actionPerformed(ActionEvent e)
1584 if (dasFeatureFetcher != null)
1586 dasFeatureFetcher.cancel();
1591 public void noDasSourceActive()
1594 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1595 MessageManager.getString("label.no_das_sources_selected_warn"),
1596 MessageManager.getString("label.no_das_sources_selected_title"),
1597 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1601 * Reorders features by 'dragging' selectedRow to 'newRow'
1605 protected void dragRow(int newRow)
1607 if (summaryView.isSelected())
1609 // no drag while in summary view
1613 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1616 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1617 * this could be more than one row away for a very fast drag action
1618 * so just swap it with adjacent rows until we get it there
1620 Object[][] data = ((FeatureTableModel) table.getModel())
1622 int direction = newRow < selectedRow ? -1 : 1;
1623 for (int i = selectedRow; i != newRow; i += direction)
1625 Object[] temp = data[i];
1626 data[i] = data[i + direction];
1627 data[i + direction] = temp;
1629 updateFeatureRenderer(data);
1631 selectedRow = newRow;
1635 protected void refreshTable()
1637 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1638 for (Object[] row : data)
1640 String type = (String) row[TYPE_COLUMN];
1641 FeatureColourI colour = fr.getFeatureColours().get(type);
1642 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1645 filter = new FeatureMatcherSet();
1647 row[COLOUR_COLUMN] = colour;
1648 row[FILTER_COLUMN] = filter;
1653 // ///////////////////////////////////////////////////////////////////////
1654 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1655 // ///////////////////////////////////////////////////////////////////////
1656 class FeatureTableModel extends AbstractTableModel
1658 private String[] columnNames = {
1659 MessageManager.getString("label.feature_type"),
1660 MessageManager.getString("action.colour"),
1661 MessageManager.getString("label.filter"),
1662 MessageManager.getString("label.show") };
1664 private Object[][] data;
1666 FeatureTableModel(Object[][] data)
1671 public Object[][] getData()
1676 public void setData(Object[][] data)
1682 public int getColumnCount()
1684 return columnNames.length;
1687 public Object[] getRow(int row)
1693 public int getRowCount()
1699 public String getColumnName(int col)
1701 return columnNames[col];
1705 public Object getValueAt(int row, int col)
1707 return data[row][col];
1711 * Answers the class of the object in column c of the first row of the table
1714 public Class<?> getColumnClass(int c)
1716 Object v = getValueAt(0, c);
1717 return v == null ? null : v.getClass();
1721 * Answers true for all columns except Feature Type
1724 public boolean isCellEditable(int row, int col)
1726 return col != TYPE_COLUMN;
1730 * Sets the value in the model for a given row and column. If Visibility
1731 * (Show/Hide) is being set, and the table is in Summary View, then it is
1732 * set also on any sub-types of the row's feature type.
1735 public void setValueAt(Object value, int row, int col)
1737 data[row][col] = value;
1738 fireTableCellUpdated(row, col);
1739 if (summaryView.isSelected() && col == SHOW_COLUMN)
1741 String type = (String) data[row][TYPE_COLUMN];
1742 OntologyI so = SequenceOntologyFactory.getInstance();
1743 for (int r = 0; r < data.length; r++)
1747 String type2 = (String) data[r][TYPE_COLUMN];
1748 if (so.isA(type2, type))
1750 data[r][col] = value;
1751 fireTableCellUpdated(r, col);
1757 updateFeatureRenderer(data);
1762 class ColorRenderer extends JLabel implements TableCellRenderer
1764 javax.swing.border.Border unselectedBorder = null;
1766 javax.swing.border.Border selectedBorder = null;
1768 final String baseTT = "Click to edit, right/apple click for menu.";
1770 public ColorRenderer()
1772 setOpaque(true); // MUST do this for background to show up.
1773 setHorizontalTextPosition(SwingConstants.CENTER);
1774 setVerticalTextPosition(SwingConstants.CENTER);
1778 public Component getTableCellRendererComponent(JTable tbl, Object color,
1779 boolean isSelected, boolean hasFocus, int row, int column)
1781 FeatureColourI cellColour = (FeatureColourI) color;
1783 setToolTipText(baseTT);
1784 setBackground(tbl.getBackground());
1785 if (!cellColour.isSimpleColour())
1787 Rectangle cr = tbl.getCellRect(row, column, false);
1788 FeatureSettings.renderGraduatedColor(this, cellColour,
1789 (int) cr.getWidth(), (int) cr.getHeight());
1795 setBackground(cellColour.getColour());
1799 if (selectedBorder == null)
1801 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1802 tbl.getSelectionBackground());
1804 setBorder(selectedBorder);
1808 if (unselectedBorder == null)
1810 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1811 tbl.getBackground());
1813 setBorder(unselectedBorder);
1820 class FilterRenderer extends JLabel implements TableCellRenderer
1822 javax.swing.border.Border unselectedBorder = null;
1824 javax.swing.border.Border selectedBorder = null;
1826 public FilterRenderer()
1828 setOpaque(true); // MUST do this for background to show up.
1829 setHorizontalTextPosition(SwingConstants.CENTER);
1830 setVerticalTextPosition(SwingConstants.CENTER);
1834 public Component getTableCellRendererComponent(JTable tbl,
1835 Object filter, boolean isSelected, boolean hasFocus, int row,
1838 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1840 String asText = theFilter.toString();
1841 setBackground(tbl.getBackground());
1842 this.setText(asText);
1847 if (selectedBorder == null)
1849 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1850 tbl.getSelectionBackground());
1852 setBorder(selectedBorder);
1856 if (unselectedBorder == null)
1858 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1859 tbl.getBackground());
1861 setBorder(unselectedBorder);
1869 * update comp using rendering settings from gcol
1874 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1876 int w = comp.getWidth(), h = comp.getHeight();
1879 w = (int) comp.getPreferredSize().getWidth();
1880 h = (int) comp.getPreferredSize().getHeight();
1887 renderGraduatedColor(comp, gcol, w, h);
1890 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1893 boolean thr = false;
1894 StringBuilder tt = new StringBuilder();
1895 StringBuilder tx = new StringBuilder();
1897 if (gcol.isColourByAttribute())
1899 tx.append(String.join(":", gcol.getAttributeName()));
1901 else if (!gcol.isColourByLabel())
1903 tx.append(MessageManager.getString("label.score"));
1906 if (gcol.isAboveThreshold())
1910 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1913 if (gcol.isBelowThreshold())
1917 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1920 if (gcol.isColourByLabel())
1922 tt.append("Coloured by label text. ").append(tt);
1927 if (!gcol.isColourByAttribute())
1935 Color newColor = gcol.getMaxColour();
1936 comp.setBackground(newColor);
1937 // System.err.println("Width is " + w / 2);
1938 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1939 comp.setIcon(ficon);
1940 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1941 // + newColor.getGreen() + ", " + newColor.getBlue()
1942 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1943 // + ", " + minCol.getBlue() + ")");
1945 comp.setHorizontalAlignment(SwingConstants.CENTER);
1946 comp.setText(tx.toString());
1947 if (tt.length() > 0)
1949 if (comp.getToolTipText() == null)
1951 comp.setToolTipText(tt.toString());
1955 comp.setToolTipText(
1956 tt.append(" ").append(comp.getToolTipText()).toString());
1961 class ColorEditor extends AbstractCellEditor
1962 implements TableCellEditor, ActionListener
1966 FeatureColourI currentColor;
1968 FeatureTypeSettings chooser;
1972 JButton colourButton;
1974 JColorChooser colorChooser;
1978 protected static final String EDIT = "edit";
1980 int rowSelected = 0;
1982 public ColorEditor(FeatureSettings me)
1985 // Set up the editor (from the table's point of view),
1986 // which is a button.
1987 // This button brings up the color chooser dialog,
1988 // which is the editor from the user's point of view.
1989 colourButton = new JButton();
1990 colourButton.setActionCommand(EDIT);
1991 colourButton.addActionListener(this);
1992 colourButton.setBorderPainted(false);
1993 // Set up the dialog that the button brings up.
1994 colorChooser = new JColorChooser();
1995 dialog = JColorChooser.createDialog(colourButton,
1996 MessageManager.getString("label.select_colour"), true, // modal
1997 colorChooser, this, // OK button handler
1998 null); // no CANCEL button handler
2002 * Handles events from the editor button and from the dialog's OK button.
2005 public void actionPerformed(ActionEvent e)
2007 // todo test e.getSource() instead here
2008 if (EDIT.equals(e.getActionCommand()))
2010 // The user has clicked the cell, so
2011 // bring up the dialog.
2012 if (currentColor.isSimpleColour())
2014 // bring up simple color chooser
2015 colourButton.setBackground(currentColor.getColour());
2016 colorChooser.setColor(currentColor.getColour());
2017 dialog.setVisible(true);
2021 // bring up graduated chooser.
2022 chooser = new FeatureTypeSettings(me.fr, type);
2023 chooser.setRequestFocusEnabled(true);
2024 chooser.requestFocus();
2025 chooser.addActionListener(this);
2026 chooser.showTab(true);
2028 // Make the renderer reappear.
2029 fireEditingStopped();
2033 if (currentColor.isSimpleColour())
2036 * read off colour picked in colour chooser after OK pressed
2038 currentColor = new FeatureColour(colorChooser.getColor());
2039 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
2044 * after OK in variable colour dialog, any changes to colour
2045 * (or filters!) are already set in FeatureRenderer, so just
2046 * update table data without triggering updateFeatureRenderer
2050 fireEditingStopped();
2051 me.table.validate();
2055 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2057 public Object getCellEditorValue()
2059 return currentColor;
2062 // Implement the one method defined by TableCellEditor.
2064 public Component getTableCellEditorComponent(JTable theTable, Object value,
2065 boolean isSelected, int row, int column)
2067 currentColor = (FeatureColourI) value;
2068 this.rowSelected = row;
2069 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2070 colourButton.setOpaque(true);
2071 colourButton.setBackground(me.getBackground());
2072 if (!currentColor.isSimpleColour())
2074 JLabel btn = new JLabel();
2075 btn.setSize(colourButton.getSize());
2076 FeatureSettings.renderGraduatedColor(btn, currentColor);
2077 colourButton.setBackground(btn.getBackground());
2078 colourButton.setIcon(btn.getIcon());
2079 colourButton.setText(btn.getText());
2083 colourButton.setText("");
2084 colourButton.setIcon(null);
2085 colourButton.setBackground(currentColor.getColour());
2087 return colourButton;
2092 * The cell editor for the Filter column. It displays the text of any filters
2093 * for the feature type in that row (in full as a tooltip, possible abbreviated
2094 * as display text). On click in the cell, opens the Feature Display Settings
2095 * dialog at the Filters tab.
2097 class FilterEditor extends AbstractCellEditor
2098 implements TableCellEditor, ActionListener
2102 FeatureMatcherSetI currentFilter;
2108 JButton filterButton;
2110 protected static final String EDIT = "edit";
2112 int rowSelected = 0;
2114 public FilterEditor(FeatureSettings me)
2117 filterButton = new JButton();
2118 filterButton.setActionCommand(EDIT);
2119 filterButton.addActionListener(this);
2120 filterButton.setBorderPainted(false);
2124 * Handles events from the editor button
2127 public void actionPerformed(ActionEvent e)
2129 if (filterButton == e.getSource())
2131 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2132 chooser.addActionListener(this);
2133 chooser.setRequestFocusEnabled(true);
2134 chooser.requestFocus();
2135 if (lastLocation != null)
2137 // todo open at its last position on screen
2138 chooser.setBounds(lastLocation.x, lastLocation.y,
2139 chooser.getWidth(), chooser.getHeight());
2142 chooser.showTab(false);
2143 fireEditingStopped();
2145 else if (e.getSource() instanceof Component)
2148 * after OK in variable colour dialog, any changes to filter
2149 * (or colours!) are already set in FeatureRenderer, so just
2150 * update table data without triggering updateFeatureRenderer
2153 fireEditingStopped();
2154 me.table.validate();
2159 public Object getCellEditorValue()
2161 return currentFilter;
2165 public Component getTableCellEditorComponent(JTable theTable, Object value,
2166 boolean isSelected, int row, int column)
2168 currentFilter = (FeatureMatcherSetI) value;
2169 this.rowSelected = row;
2170 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2171 filterButton.setOpaque(true);
2172 filterButton.setBackground(me.getBackground());
2173 filterButton.setText(currentFilter.toString());
2174 filterButton.setToolTipText(currentFilter.toString());
2175 filterButton.setIcon(null);
2176 return filterButton;
2181 class FeatureIcon implements Icon
2183 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2185 FeatureColourI gcol;
2189 boolean midspace = false;
2191 int width = 50, height = 20;
2193 int s1, e1; // start and end of midpoint band for thresholded symbol
2195 Color mpcolour = Color.white;
2197 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2217 public int getIconWidth()
2223 public int getIconHeight()
2229 public void paintIcon(Component c, Graphics g, int x, int y)
2232 if (gcol.isColourByLabel())
2235 g.fillRect(0, 0, width, height);
2236 // need an icon here.
2237 g.setColor(gcol.getMaxColour());
2239 g.setFont(VERDANA_9);
2241 // g.setFont(g.getFont().deriveFont(
2242 // AffineTransform.getScaleInstance(
2243 // width/g.getFontMetrics().stringWidth("Label"),
2244 // height/g.getFontMetrics().getHeight())));
2246 g.drawString(MessageManager.getString("label.label"), 0, 0);
2251 Color minCol = gcol.getMinColour();
2253 g.fillRect(0, 0, s1, height);
2256 g.setColor(Color.white);
2257 g.fillRect(s1, 0, e1 - s1, height);
2259 g.setColor(gcol.getMaxColour());
2260 g.fillRect(0, e1, width - e1, height);