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(
1365 MessageManager.getString("label.summary_view"));
1368 MessageManager.getString("label.summary_view_tip"));
1369 summaryView.addActionListener(new ActionListener()
1372 public void actionPerformed(ActionEvent e)
1378 transparency.setMaximum(70);
1379 transparency.setToolTipText(
1380 MessageManager.getString("label.transparency_tip"));
1381 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1382 fetchDAS.addActionListener(new ActionListener()
1385 public void actionPerformed(ActionEvent e)
1387 fetchDAS_actionPerformed(e);
1390 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1391 saveDAS.addActionListener(new ActionListener()
1394 public void actionPerformed(ActionEvent e)
1396 saveDAS_actionPerformed(e);
1400 JPanel dasButtonPanel = new JPanel();
1401 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1402 dasSettingsPane.setBorder(null);
1403 cancelDAS.setEnabled(false);
1404 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1405 cancelDAS.addActionListener(new ActionListener()
1408 public void actionPerformed(ActionEvent e)
1410 cancelDAS_actionPerformed(e);
1414 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1415 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1417 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1418 transbuttons.add(optimizeOrder);
1419 transbuttons.add(invert);
1420 transbuttons.add(sortByScore);
1421 transbuttons.add(sortByDens);
1422 transbuttons.add(help);
1423 JPanel transPanel = new JPanel(new GridLayout(3, 1));
1424 transPanel.add(summaryView);
1425 transPanel.add(new JLabel(" Colour transparency" + ":"));
1426 transPanel.add(transparency);
1427 lowerPanel.add(transPanel);
1428 lowerPanel.add(transbuttons);
1430 JPanel buttonPanel = new JPanel();
1431 buttonPanel.add(ok);
1432 buttonPanel.add(cancel);
1433 buttonPanel.add(loadColours);
1434 buttonPanel.add(saveColours);
1435 bigPanel.add(scrollPane, BorderLayout.CENTER);
1436 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1437 dasButtonPanel.add(fetchDAS);
1438 dasButtonPanel.add(cancelDAS);
1439 dasButtonPanel.add(saveDAS);
1440 settingsPane.add(bigPanel, BorderLayout.CENTER);
1441 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1442 this.add(settingsPane);
1445 public void fetchDAS_actionPerformed(ActionEvent e)
1447 fetchDAS.setEnabled(false);
1448 cancelDAS.setEnabled(true);
1449 dassourceBrowser.setGuiEnabled(false);
1450 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1451 .getSelectedSources();
1452 doDasFeatureFetch(selectedSources, true, true);
1456 * get the features from selectedSources for all or the current selection
1458 * @param selectedSources
1459 * @param checkDbRefs
1460 * @param promptFetchDbRefs
1462 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1463 boolean checkDbRefs, boolean promptFetchDbRefs)
1465 SequenceI[] dataset, seqs;
1467 AlignmentViewport vp = af.getViewport();
1468 if (vp.getSelectionGroup() != null
1469 && vp.getSelectionGroup().getSize() > 0)
1471 iSize = vp.getSelectionGroup().getSize();
1472 dataset = new SequenceI[iSize];
1473 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1477 iSize = vp.getAlignment().getHeight();
1478 seqs = vp.getAlignment().getSequencesArray();
1481 dataset = new SequenceI[iSize];
1482 for (int i = 0; i < iSize; i++)
1484 dataset[i] = seqs[i].getDatasetSequence();
1487 cancelDAS.setEnabled(true);
1488 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1489 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1490 af.getViewport().setShowSequenceFeatures(true);
1491 af.showSeqFeatures.setSelected(true);
1495 * blocking call to initialise the das source browser
1497 public void initDasSources()
1499 dassourceBrowser.initDasSources();
1503 * examine the current list of das sources and return any matching the given
1504 * nicknames in sources
1507 * Vector of Strings to resolve to DAS source nicknames.
1508 * @return sources that are present in source list.
1510 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1512 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1516 * get currently selected das sources. ensure you have called initDasSources
1517 * before calling this.
1519 * @return vector of selected das source nicknames
1521 public Vector<jalviewSourceI> getSelectedSources()
1523 return dassourceBrowser.getSelectedSources();
1527 * properly initialise DAS fetcher and then initiate a new thread to fetch
1528 * features from the named sources (rather than any turned on by default)
1532 * if true then runs in same thread, otherwise passes to the Swing
1535 public void fetchDasFeatures(Vector<String> sources, boolean block)
1538 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1539 .resolveSourceNicknames(sources);
1540 if (resolved.size() == 0)
1542 resolved = dassourceBrowser.getSelectedSources();
1544 if (resolved.size() > 0)
1546 final List<jalviewSourceI> dassources = resolved;
1547 fetchDAS.setEnabled(false);
1548 // cancelDAS.setEnabled(true); doDasFetch does this.
1549 Runnable fetcher = new Runnable()
1555 doDasFeatureFetch(dassources, true, false);
1565 SwingUtilities.invokeLater(fetcher);
1570 public void saveDAS_actionPerformed(ActionEvent e)
1573 .saveProperties(jalview.bin.Cache.applicationProperties);
1576 public void complete()
1578 fetchDAS.setEnabled(true);
1579 cancelDAS.setEnabled(false);
1580 dassourceBrowser.setGuiEnabled(true);
1584 public void cancelDAS_actionPerformed(ActionEvent e)
1586 if (dasFeatureFetcher != null)
1588 dasFeatureFetcher.cancel();
1593 public void noDasSourceActive()
1596 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1597 MessageManager.getString("label.no_das_sources_selected_warn"),
1598 MessageManager.getString("label.no_das_sources_selected_title"),
1599 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1603 * Reorders features by 'dragging' selectedRow to 'newRow'
1607 protected void dragRow(int newRow)
1609 if (summaryView.isSelected())
1611 // no drag while in summary view
1615 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1618 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1619 * this could be more than one row away for a very fast drag action
1620 * so just swap it with adjacent rows until we get it there
1622 Object[][] data = ((FeatureTableModel) table.getModel())
1624 int direction = newRow < selectedRow ? -1 : 1;
1625 for (int i = selectedRow; i != newRow; i += direction)
1627 Object[] temp = data[i];
1628 data[i] = data[i + direction];
1629 data[i + direction] = temp;
1631 updateFeatureRenderer(data);
1633 selectedRow = newRow;
1637 protected void refreshTable()
1639 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1640 for (Object[] row : data)
1642 String type = (String) row[TYPE_COLUMN];
1643 FeatureColourI colour = fr.getFeatureColours().get(type);
1644 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1647 filter = new FeatureMatcherSet();
1649 row[COLOUR_COLUMN] = colour;
1650 row[FILTER_COLUMN] = filter;
1655 // ///////////////////////////////////////////////////////////////////////
1656 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1657 // ///////////////////////////////////////////////////////////////////////
1658 class FeatureTableModel extends AbstractTableModel
1660 private String[] columnNames = {
1661 MessageManager.getString("label.feature_type"),
1662 MessageManager.getString("action.colour"),
1663 MessageManager.getString("label.filter"),
1664 MessageManager.getString("label.show") };
1666 private Object[][] data;
1668 FeatureTableModel(Object[][] data)
1673 public Object[][] getData()
1678 public void setData(Object[][] data)
1684 public int getColumnCount()
1686 return columnNames.length;
1689 public Object[] getRow(int row)
1695 public int getRowCount()
1701 public String getColumnName(int col)
1703 return columnNames[col];
1707 public Object getValueAt(int row, int col)
1709 return data[row][col];
1713 * Answers the class of the object in column c of the first row of the table
1716 public Class<?> getColumnClass(int c)
1718 Object v = getValueAt(0, c);
1719 return v == null ? null : v.getClass();
1723 * Answers true for all columns except Feature Type
1726 public boolean isCellEditable(int row, int col)
1728 return col != TYPE_COLUMN;
1732 * Sets the value in the model for a given row and column. If Visibility
1733 * (Show/Hide) is being set, and the table is in Summary View, then it is
1734 * set also on any sub-types of the row's feature type.
1737 public void setValueAt(Object value, int row, int col)
1739 data[row][col] = value;
1740 fireTableCellUpdated(row, col);
1741 if (summaryView.isSelected() && col == SHOW_COLUMN)
1743 setSubtypesVisibility(row, (Boolean) value);
1745 updateFeatureRenderer(data);
1749 * Sets the visibility of any feature types which are sub-types of the type
1750 * in the given row of the table
1755 protected void setSubtypesVisibility(int row, Boolean value)
1757 String type = (String) data[row][TYPE_COLUMN];
1758 OntologyI so = SequenceOntologyFactory.getInstance();
1760 for (int r = 0; r < data.length; r++)
1764 String type2 = (String) data[r][TYPE_COLUMN];
1765 if (so.isA(type2, type))
1767 data[r][SHOW_COLUMN] = value;
1768 fireTableCellUpdated(r, SHOW_COLUMN);
1776 class ColorRenderer extends JLabel implements TableCellRenderer
1778 javax.swing.border.Border unselectedBorder = null;
1780 javax.swing.border.Border selectedBorder = null;
1782 final String baseTT = "Click to edit, right/apple click for menu.";
1784 public ColorRenderer()
1786 setOpaque(true); // MUST do this for background to show up.
1787 setHorizontalTextPosition(SwingConstants.CENTER);
1788 setVerticalTextPosition(SwingConstants.CENTER);
1792 public Component getTableCellRendererComponent(JTable tbl, Object color,
1793 boolean isSelected, boolean hasFocus, int row, int column)
1795 FeatureColourI cellColour = (FeatureColourI) color;
1797 setToolTipText(baseTT);
1798 setBackground(tbl.getBackground());
1799 if (!cellColour.isSimpleColour())
1801 Rectangle cr = tbl.getCellRect(row, column, false);
1802 FeatureSettings.renderGraduatedColor(this, cellColour,
1803 (int) cr.getWidth(), (int) cr.getHeight());
1809 setBackground(cellColour.getColour());
1813 if (selectedBorder == null)
1815 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1816 tbl.getSelectionBackground());
1818 setBorder(selectedBorder);
1822 if (unselectedBorder == null)
1824 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1825 tbl.getBackground());
1827 setBorder(unselectedBorder);
1834 class FilterRenderer extends JLabel implements TableCellRenderer
1836 javax.swing.border.Border unselectedBorder = null;
1838 javax.swing.border.Border selectedBorder = null;
1840 public FilterRenderer()
1842 setOpaque(true); // MUST do this for background to show up.
1843 setHorizontalTextPosition(SwingConstants.CENTER);
1844 setVerticalTextPosition(SwingConstants.CENTER);
1848 public Component getTableCellRendererComponent(JTable tbl,
1849 Object filter, boolean isSelected, boolean hasFocus, int row,
1852 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1854 String asText = theFilter.toString();
1855 setBackground(tbl.getBackground());
1856 this.setText(asText);
1861 if (selectedBorder == null)
1863 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1864 tbl.getSelectionBackground());
1866 setBorder(selectedBorder);
1870 if (unselectedBorder == null)
1872 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1873 tbl.getBackground());
1875 setBorder(unselectedBorder);
1883 * update comp using rendering settings from gcol
1888 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1890 int w = comp.getWidth(), h = comp.getHeight();
1893 w = (int) comp.getPreferredSize().getWidth();
1894 h = (int) comp.getPreferredSize().getHeight();
1901 renderGraduatedColor(comp, gcol, w, h);
1904 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1907 boolean thr = false;
1908 StringBuilder tt = new StringBuilder();
1909 StringBuilder tx = new StringBuilder();
1911 if (gcol.isColourByAttribute())
1913 tx.append(String.join(":", gcol.getAttributeName()));
1915 else if (!gcol.isColourByLabel())
1917 tx.append(MessageManager.getString("label.score"));
1920 if (gcol.isAboveThreshold())
1924 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1927 if (gcol.isBelowThreshold())
1931 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1934 if (gcol.isColourByLabel())
1936 tt.append("Coloured by label text. ").append(tt);
1941 if (!gcol.isColourByAttribute())
1949 Color newColor = gcol.getMaxColour();
1950 comp.setBackground(newColor);
1951 // System.err.println("Width is " + w / 2);
1952 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1953 comp.setIcon(ficon);
1954 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1955 // + newColor.getGreen() + ", " + newColor.getBlue()
1956 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1957 // + ", " + minCol.getBlue() + ")");
1959 comp.setHorizontalAlignment(SwingConstants.CENTER);
1960 comp.setText(tx.toString());
1961 if (tt.length() > 0)
1963 if (comp.getToolTipText() == null)
1965 comp.setToolTipText(tt.toString());
1969 comp.setToolTipText(
1970 tt.append(" ").append(comp.getToolTipText()).toString());
1975 class ColorEditor extends AbstractCellEditor
1976 implements TableCellEditor, ActionListener
1980 FeatureColourI currentColor;
1982 FeatureTypeSettings chooser;
1986 JButton colourButton;
1988 JColorChooser colorChooser;
1992 protected static final String EDIT = "edit";
1994 int rowSelected = 0;
1996 public ColorEditor(FeatureSettings me)
1999 // Set up the editor (from the table's point of view),
2000 // which is a button.
2001 // This button brings up the color chooser dialog,
2002 // which is the editor from the user's point of view.
2003 colourButton = new JButton();
2004 colourButton.setActionCommand(EDIT);
2005 colourButton.addActionListener(this);
2006 colourButton.setBorderPainted(false);
2007 // Set up the dialog that the button brings up.
2008 colorChooser = new JColorChooser();
2009 dialog = JColorChooser.createDialog(colourButton,
2010 MessageManager.getString("label.select_colour"), true, // modal
2011 colorChooser, this, // OK button handler
2012 null); // no CANCEL button handler
2016 * Handles events from the editor button and from the dialog's OK button.
2019 public void actionPerformed(ActionEvent e)
2021 // todo test e.getSource() instead here
2022 if (EDIT.equals(e.getActionCommand()))
2024 // The user has clicked the cell, so
2025 // bring up the dialog.
2026 if (currentColor.isSimpleColour())
2028 // bring up simple color chooser
2029 colourButton.setBackground(currentColor.getColour());
2030 colorChooser.setColor(currentColor.getColour());
2031 dialog.setVisible(true);
2035 // bring up graduated chooser.
2036 chooser = new FeatureTypeSettings(me.fr, type);
2037 chooser.setRequestFocusEnabled(true);
2038 chooser.requestFocus();
2039 chooser.addActionListener(this);
2040 chooser.showTab(true);
2042 // Make the renderer reappear.
2043 fireEditingStopped();
2047 if (currentColor.isSimpleColour())
2050 * read off colour picked in colour chooser after OK pressed
2052 currentColor = new FeatureColour(colorChooser.getColor());
2053 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
2058 * after OK in variable colour dialog, any changes to colour
2059 * (or filters!) are already set in FeatureRenderer, so just
2060 * update table data without triggering updateFeatureRenderer
2064 fireEditingStopped();
2065 me.table.validate();
2069 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2071 public Object getCellEditorValue()
2073 return currentColor;
2076 // Implement the one method defined by TableCellEditor.
2078 public Component getTableCellEditorComponent(JTable theTable, Object value,
2079 boolean isSelected, int row, int column)
2081 currentColor = (FeatureColourI) value;
2082 this.rowSelected = row;
2083 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2084 colourButton.setOpaque(true);
2085 colourButton.setBackground(me.getBackground());
2086 if (!currentColor.isSimpleColour())
2088 JLabel btn = new JLabel();
2089 btn.setSize(colourButton.getSize());
2090 FeatureSettings.renderGraduatedColor(btn, currentColor);
2091 colourButton.setBackground(btn.getBackground());
2092 colourButton.setIcon(btn.getIcon());
2093 colourButton.setText(btn.getText());
2097 colourButton.setText("");
2098 colourButton.setIcon(null);
2099 colourButton.setBackground(currentColor.getColour());
2101 return colourButton;
2106 * The cell editor for the Filter column. It displays the text of any filters
2107 * for the feature type in that row (in full as a tooltip, possible abbreviated
2108 * as display text). On click in the cell, opens the Feature Display Settings
2109 * dialog at the Filters tab.
2111 class FilterEditor extends AbstractCellEditor
2112 implements TableCellEditor, ActionListener
2116 FeatureMatcherSetI currentFilter;
2122 JButton filterButton;
2124 protected static final String EDIT = "edit";
2126 int rowSelected = 0;
2128 public FilterEditor(FeatureSettings me)
2131 filterButton = new JButton();
2132 filterButton.setActionCommand(EDIT);
2133 filterButton.addActionListener(this);
2134 filterButton.setBorderPainted(false);
2138 * Handles events from the editor button
2141 public void actionPerformed(ActionEvent e)
2143 if (filterButton == e.getSource())
2145 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2146 chooser.addActionListener(this);
2147 chooser.setRequestFocusEnabled(true);
2148 chooser.requestFocus();
2149 if (lastLocation != null)
2151 // todo open at its last position on screen
2152 chooser.setBounds(lastLocation.x, lastLocation.y,
2153 chooser.getWidth(), chooser.getHeight());
2156 chooser.showTab(false);
2157 fireEditingStopped();
2159 else if (e.getSource() instanceof Component)
2162 * after OK in variable colour dialog, any changes to filter
2163 * (or colours!) are already set in FeatureRenderer, so just
2164 * update table data without triggering updateFeatureRenderer
2167 fireEditingStopped();
2168 me.table.validate();
2173 public Object getCellEditorValue()
2175 return currentFilter;
2179 public Component getTableCellEditorComponent(JTable theTable, Object value,
2180 boolean isSelected, int row, int column)
2182 currentFilter = (FeatureMatcherSetI) value;
2183 this.rowSelected = row;
2184 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2185 filterButton.setOpaque(true);
2186 filterButton.setBackground(me.getBackground());
2187 filterButton.setText(currentFilter.toString());
2188 filterButton.setToolTipText(currentFilter.toString());
2189 filterButton.setIcon(null);
2190 return filterButton;
2195 class FeatureIcon implements Icon
2197 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2199 FeatureColourI gcol;
2203 boolean midspace = false;
2205 int width = 50, height = 20;
2207 int s1, e1; // start and end of midpoint band for thresholded symbol
2209 Color mpcolour = Color.white;
2211 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2231 public int getIconWidth()
2237 public int getIconHeight()
2243 public void paintIcon(Component c, Graphics g, int x, int y)
2246 if (gcol.isColourByLabel())
2249 g.fillRect(0, 0, width, height);
2250 // need an icon here.
2251 g.setColor(gcol.getMaxColour());
2253 g.setFont(VERDANA_9);
2255 // g.setFont(g.getFont().deriveFont(
2256 // AffineTransform.getScaleInstance(
2257 // width/g.getFontMetrics().stringWidth("Label"),
2258 // height/g.getFontMetrics().getHeight())));
2260 g.drawString(MessageManager.getString("label.label"), 0, 0);
2265 Color minCol = gcol.getMinColour();
2267 g.fillRect(0, 0, s1, height);
2270 g.setColor(Color.white);
2271 g.fillRect(s1, 0, e1 - s1, height);
2273 g.setColor(gcol.getMaxColour());
2274 g.fillRect(0, e1, width - e1, height);