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.gui.Help.HelpId;
31 import jalview.io.JalviewFileChooser;
32 import jalview.io.JalviewFileView;
33 import jalview.schemabinding.version2.Filter;
34 import jalview.schemabinding.version2.JalviewUserColours;
35 import jalview.schemabinding.version2.MatcherSet;
36 import jalview.schemes.FeatureColour;
37 import jalview.util.MessageManager;
38 import jalview.util.Platform;
39 import jalview.viewmodel.AlignmentViewport;
40 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
41 import jalview.ws.DasSequenceFeatureFetcher;
42 import jalview.ws.dbsources.das.api.jalviewSourceI;
44 import java.awt.BorderLayout;
45 import java.awt.Color;
46 import java.awt.Component;
47 import java.awt.Dimension;
49 import java.awt.Graphics;
50 import java.awt.GridLayout;
51 import java.awt.Point;
52 import java.awt.Rectangle;
53 import java.awt.event.ActionEvent;
54 import java.awt.event.ActionListener;
55 import java.awt.event.ItemEvent;
56 import java.awt.event.ItemListener;
57 import java.awt.event.MouseAdapter;
58 import java.awt.event.MouseEvent;
59 import java.awt.event.MouseMotionAdapter;
60 import java.beans.PropertyChangeEvent;
61 import java.beans.PropertyChangeListener;
63 import java.io.FileInputStream;
64 import java.io.FileOutputStream;
65 import java.io.InputStreamReader;
66 import java.io.OutputStreamWriter;
67 import java.io.PrintWriter;
68 import java.util.Arrays;
69 import java.util.Comparator;
70 import java.util.HashMap;
71 import java.util.HashSet;
72 import java.util.Hashtable;
73 import java.util.Iterator;
74 import java.util.List;
77 import java.util.Vector;
79 import javax.help.HelpSetException;
80 import javax.swing.AbstractCellEditor;
81 import javax.swing.BorderFactory;
82 import javax.swing.Icon;
83 import javax.swing.JButton;
84 import javax.swing.JCheckBox;
85 import javax.swing.JCheckBoxMenuItem;
86 import javax.swing.JColorChooser;
87 import javax.swing.JDialog;
88 import javax.swing.JInternalFrame;
89 import javax.swing.JLabel;
90 import javax.swing.JLayeredPane;
91 import javax.swing.JMenuItem;
92 import javax.swing.JPanel;
93 import javax.swing.JPopupMenu;
94 import javax.swing.JScrollPane;
95 import javax.swing.JSlider;
96 import javax.swing.JTable;
97 import javax.swing.ListSelectionModel;
98 import javax.swing.SwingConstants;
99 import javax.swing.SwingUtilities;
100 import javax.swing.event.ChangeEvent;
101 import javax.swing.event.ChangeListener;
102 import javax.swing.table.AbstractTableModel;
103 import javax.swing.table.TableCellEditor;
104 import javax.swing.table.TableCellRenderer;
105 import javax.swing.table.TableColumn;
107 public class FeatureSettings extends JPanel
108 implements FeatureSettingsControllerI
111 * column indices of fields in Feature Settings table
113 static final int TYPE_COLUMN = 0;
115 static final int COLOUR_COLUMN = 1;
117 static final int FILTER_COLUMN = 2;
119 static final int SHOW_COLUMN = 3;
121 private static final int COLUMN_COUNT = 4;
123 private static final int MIN_WIDTH = 400;
125 private static final int MIN_HEIGHT = 400;
127 DasSourceBrowser dassourceBrowser;
129 DasSequenceFeatureFetcher dasFeatureFetcher;
131 JPanel dasSettingsPane = new JPanel();
133 final FeatureRenderer fr;
135 public final AlignFrame af;
138 * 'original' fields hold settings to restore on Cancel
140 Object[][] originalData;
142 private float originalTransparency;
144 private Map<String, FeatureMatcherSetI> originalFilters;
146 final JInternalFrame frame;
148 JScrollPane scrollPane = new JScrollPane();
154 JSlider transparency = new JSlider();
157 * when true, constructor is still executing - so ignore UI events
159 protected volatile boolean inConstruction = true;
161 int selectedRow = -1;
163 JButton fetchDAS = new JButton();
165 JButton saveDAS = new JButton();
167 JButton cancelDAS = new JButton();
169 boolean resettingTable = false;
172 * true when Feature Settings are updating from feature renderer
174 private boolean handlingUpdate = false;
177 * holds {featureCount, totalExtent} for each feature type
179 Map<String, float[]> typeWidth = null;
186 public FeatureSettings(AlignFrame alignFrame)
188 this.af = alignFrame;
189 fr = af.getFeatureRenderer();
191 // save transparency for restore on Cancel
192 originalTransparency = fr.getTransparency();
193 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
194 transparency.setMaximum(100 - originalTransparencyAsPercent);
196 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
201 } catch (Exception ex)
203 ex.printStackTrace();
209 public String getToolTipText(MouseEvent e)
212 int column = table.columnAtPoint(e.getPoint());
216 tip = JvSwingUtils.wrapTooltip(true, MessageManager
217 .getString("label.feature_settings_click_drag"));
220 int row = table.rowAtPoint(e.getPoint());
221 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
224 ? MessageManager.getString("label.filters_tooltip")
233 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
234 table.setFont(new Font("Verdana", Font.PLAIN, 12));
236 // table.setDefaultRenderer(Color.class, new ColorRenderer());
237 // table.setDefaultEditor(Color.class, new ColorEditor(this));
239 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
240 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
242 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
243 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
245 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
246 new ColorRenderer(), new ColorEditor(this));
247 table.addColumn(colourColumn);
249 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
250 new FilterRenderer(), new FilterEditor(this));
251 table.addColumn(filterColumn);
253 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
255 table.addMouseListener(new MouseAdapter()
258 public void mousePressed(MouseEvent evt)
260 selectedRow = table.rowAtPoint(evt.getPoint());
261 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
262 if (evt.isPopupTrigger())
264 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
265 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
268 else if (evt.getClickCount() == 2)
270 boolean invertSelection = evt.isAltDown();
271 boolean toggleSelection = Platform.isControlDown(evt);
272 boolean extendSelection = evt.isShiftDown();
273 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
274 invertSelection, extendSelection, toggleSelection, type);
278 // isPopupTrigger fires on mouseReleased on Windows
280 public void mouseReleased(MouseEvent evt)
282 selectedRow = table.rowAtPoint(evt.getPoint());
283 if (evt.isPopupTrigger())
285 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
286 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
287 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
293 table.addMouseMotionListener(new MouseMotionAdapter()
296 public void mouseDragged(MouseEvent evt)
298 int newRow = table.rowAtPoint(evt.getPoint());
299 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
302 * reposition 'selectedRow' to 'newRow' (the dragged to location)
303 * this could be more than one row away for a very fast drag action
304 * so just swap it with adjacent rows until we get it there
306 Object[][] data = ((FeatureTableModel) table.getModel())
308 int direction = newRow < selectedRow ? -1 : 1;
309 for (int i = selectedRow; i != newRow; i += direction)
311 Object[] temp = data[i];
312 data[i] = data[i + direction];
313 data[i + direction] = temp;
315 updateFeatureRenderer(data);
317 selectedRow = newRow;
321 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
322 // MessageManager.getString("label.feature_settings_click_drag")));
323 scrollPane.setViewportView(table);
325 dassourceBrowser = new DasSourceBrowser(this);
326 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
328 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
330 fr.findAllFeatures(true); // display everything!
333 discoverAllFeatureData();
334 final PropertyChangeListener change;
335 final FeatureSettings fs = this;
336 fr.addPropertyChangeListener(change = new PropertyChangeListener()
339 public void propertyChange(PropertyChangeEvent evt)
341 if (!fs.resettingTable && !fs.handlingUpdate)
343 fs.handlingUpdate = true;
345 // new groups may be added with new sequence feature types only
346 fs.handlingUpdate = false;
352 frame = new JInternalFrame();
353 frame.setContentPane(this);
354 if (Platform.isAMac())
356 Desktop.addInternalFrame(frame,
357 MessageManager.getString("label.sequence_feature_settings"),
362 Desktop.addInternalFrame(frame,
363 MessageManager.getString("label.sequence_feature_settings"),
366 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
368 frame.addInternalFrameListener(
369 new javax.swing.event.InternalFrameAdapter()
372 public void internalFrameClosed(
373 javax.swing.event.InternalFrameEvent evt)
375 fr.removePropertyChangeListener(change);
376 dassourceBrowser.fs = null;
379 frame.setLayer(JLayeredPane.PALETTE_LAYER);
380 inConstruction = false;
383 protected void popupSort(final int rowSelected, final String type,
384 final Object typeCol, final Map<String, float[][]> minmax, int x,
387 final FeatureColourI featureColour = (FeatureColourI) typeCol;
389 JPopupMenu men = new JPopupMenu(MessageManager
390 .formatMessage("label.settings_for_param", new String[]
392 JMenuItem scr = new JMenuItem(
393 MessageManager.getString("label.sort_by_score"));
395 final FeatureSettings me = this;
396 scr.addActionListener(new ActionListener()
400 public void actionPerformed(ActionEvent e)
403 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
408 JMenuItem dens = new JMenuItem(
409 MessageManager.getString("label.sort_by_density"));
410 dens.addActionListener(new ActionListener()
414 public void actionPerformed(ActionEvent e)
417 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
425 * variable colour options include colour by label, by score,
426 * by selected attribute text, or attribute value
428 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
429 MessageManager.getString("label.variable_colour"));
430 mxcol.setSelected(!featureColour.isSimpleColour());
432 mxcol.addActionListener(new ActionListener()
434 JColorChooser colorChooser;
437 public void actionPerformed(ActionEvent e)
439 if (e.getSource() == mxcol)
441 if (featureColour.isSimpleColour())
443 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
444 fc.addActionListener(this);
448 // bring up simple color chooser
449 colorChooser = new JColorChooser();
450 String title = MessageManager
451 .getString("label.select_colour");
452 JDialog dialog = JColorChooser.createDialog(me,
453 title, true, // modal
454 colorChooser, this, // OK button handler
455 null); // no CANCEL button handler
456 colorChooser.setColor(featureColour.getMaxColour());
457 dialog.setVisible(true);
462 if (e.getSource() instanceof FeatureTypeSettings)
465 * update after OK in feature colour dialog; the updated
466 * colour will have already been set in the FeatureRenderer
468 FeatureColourI fci = fr.getFeatureColours().get(type);
469 table.setValueAt(fci, rowSelected, 1);
474 // probably the color chooser!
475 table.setValueAt(new FeatureColour(colorChooser.getColor()),
478 me.updateFeatureRenderer(
479 ((FeatureTableModel) table.getModel()).getData(),
487 JMenuItem selCols = new JMenuItem(
488 MessageManager.getString("label.select_columns_containing"));
489 selCols.addActionListener(new ActionListener()
492 public void actionPerformed(ActionEvent arg0)
494 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
498 JMenuItem clearCols = new JMenuItem(MessageManager
499 .getString("label.select_columns_not_containing"));
500 clearCols.addActionListener(new ActionListener()
503 public void actionPerformed(ActionEvent arg0)
505 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
509 JMenuItem hideCols = new JMenuItem(
510 MessageManager.getString("label.hide_columns_containing"));
511 hideCols.addActionListener(new ActionListener()
514 public void actionPerformed(ActionEvent arg0)
516 fr.ap.alignFrame.hideFeatureColumns(type, true);
519 JMenuItem hideOtherCols = new JMenuItem(
520 MessageManager.getString("label.hide_columns_not_containing"));
521 hideOtherCols.addActionListener(new ActionListener()
524 public void actionPerformed(ActionEvent arg0)
526 fr.ap.alignFrame.hideFeatureColumns(type, false);
532 men.add(hideOtherCols);
533 men.show(table, x, y);
537 synchronized public void discoverAllFeatureData()
539 Set<String> allGroups = new HashSet<>();
540 AlignmentI alignment = af.getViewport().getAlignment();
542 for (int i = 0; i < alignment.getHeight(); i++)
544 SequenceI seq = alignment.getSequenceAt(i);
545 for (String group : seq.getFeatures().getFeatureGroups(true))
547 if (group != null && !allGroups.contains(group))
549 allGroups.add(group);
550 checkGroupState(group);
561 * Synchronise gui group list and check visibility of group
564 * @return true if group is visible
566 private boolean checkGroupState(String group)
568 boolean visible = fr.checkGroupVisibility(group, true);
570 for (int g = 0; g < groupPanel.getComponentCount(); g++)
572 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
574 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
579 final String grp = group;
580 final JCheckBox check = new JCheckBox(group, visible);
581 check.setFont(new Font("Serif", Font.BOLD, 12));
582 check.setToolTipText(group);
583 check.addItemListener(new ItemListener()
586 public void itemStateChanged(ItemEvent evt)
588 fr.setGroupVisibility(check.getText(), check.isSelected());
589 resetTable(new String[] { grp });
590 af.alignPanel.paintAlignment(true, true);
593 groupPanel.add(check);
597 synchronized void resetTable(String[] groupChanged)
603 resettingTable = true;
604 typeWidth = new Hashtable<>();
605 // TODO: change avWidth calculation to 'per-sequence' average and use long
608 Set<String> displayableTypes = new HashSet<>();
609 Set<String> foundGroups = new HashSet<>();
612 * determine which feature types may be visible depending on
613 * which groups are selected, and recompute average width data
615 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
618 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
621 * get the sequence's groups for positional features
622 * and keep track of which groups are visible
624 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
625 Set<String> visibleGroups = new HashSet<>();
626 for (String group : groups)
628 if (group == null || checkGroupState(group))
630 visibleGroups.add(group);
633 foundGroups.addAll(groups);
636 * get distinct feature types for visible groups
637 * record distinct visible types, and their count and total length
639 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
640 visibleGroups.toArray(new String[visibleGroups.size()]));
641 for (String type : types)
643 displayableTypes.add(type);
644 float[] avWidth = typeWidth.get(type);
647 avWidth = new float[2];
648 typeWidth.put(type, avWidth);
650 // todo this could include features with a non-visible group
651 // - do we greatly care?
652 // todo should we include non-displayable features here, and only
653 // update when features are added?
654 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
655 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
659 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
662 if (fr.hasRenderOrder())
666 fr.findAllFeatures(groupChanged != null); // prod to update
667 // colourschemes. but don't
669 // First add the checks in the previous render order,
670 // in case the window has been closed and reopened
672 List<String> frl = fr.getRenderOrder();
673 for (int ro = frl.size() - 1; ro > -1; ro--)
675 String type = frl.get(ro);
677 if (!displayableTypes.contains(type))
682 data[dataIndex][TYPE_COLUMN] = type;
683 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
684 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
685 data[dataIndex][FILTER_COLUMN] = featureFilter == null
686 ? new FeatureMatcherSet()
688 data[dataIndex][SHOW_COLUMN] = new Boolean(
689 af.getViewport().getFeaturesDisplayed().isVisible(type));
691 displayableTypes.remove(type);
696 * process any extra features belonging only to
697 * a group which was just selected
699 while (!displayableTypes.isEmpty())
701 String type = displayableTypes.iterator().next();
702 data[dataIndex][TYPE_COLUMN] = type;
704 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
705 if (data[dataIndex][COLOUR_COLUMN] == null)
707 // "Colour has been updated in another view!!"
708 fr.clearRenderOrder();
711 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
712 data[dataIndex][FILTER_COLUMN] = featureFilter == null
713 ? new FeatureMatcherSet()
715 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
717 displayableTypes.remove(type);
720 if (originalData == null)
722 originalData = new Object[data.length][COLUMN_COUNT];
723 for (int i = 0; i < data.length; i++)
725 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
730 updateOriginalData(data);
733 table.setModel(new FeatureTableModel(data));
734 table.getColumnModel().getColumn(0).setPreferredWidth(200);
736 groupPanel.setLayout(
737 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
738 pruneGroups(foundGroups);
739 groupPanel.validate();
741 updateFeatureRenderer(data, groupChanged != null);
742 resettingTable = false;
746 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
747 * have been made outwith this dialog
749 * <li>a new feature type added (and made visible)</li>
750 * <li>a feature colour changed (in the Amend Features dialog)</li>
755 protected void updateOriginalData(Object[][] foundData)
757 // todo LinkedHashMap instead of Object[][] would be nice
759 Object[][] currentData = ((FeatureTableModel) table.getModel())
761 for (Object[] row : foundData)
763 String type = (String) row[TYPE_COLUMN];
764 boolean found = false;
765 for (Object[] current : currentData)
767 if (type.equals(current[TYPE_COLUMN]))
771 * currently dependent on object equality here;
772 * really need an equals method on FeatureColour
774 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
777 * feature colour has changed externally - update originalData
779 for (Object[] original : originalData)
781 if (type.equals(original[TYPE_COLUMN]))
783 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
794 * new feature detected - add to original data (on top)
796 Object[][] newData = new Object[originalData.length
798 for (int i = 0; i < originalData.length; i++)
800 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
804 originalData = newData;
810 * Remove from the groups panel any checkboxes for groups that are not in the
811 * foundGroups set. This enables removing a group from the display when the last
812 * feature in that group is deleted.
816 protected void pruneGroups(Set<String> foundGroups)
818 for (int g = 0; g < groupPanel.getComponentCount(); g++)
820 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
821 if (!foundGroups.contains(checkbox.getText()))
823 groupPanel.remove(checkbox);
829 * reorder data based on the featureRenderers global priority list.
833 private void ensureOrder(Object[][] data)
835 boolean sort = false;
836 float[] order = new float[data.length];
837 for (int i = 0; i < order.length; i++)
839 order[i] = fr.getOrder(data[i][0].toString());
842 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
846 sort = sort || order[i - 1] > order[i];
851 jalview.util.QuickSort.sort(order, data);
856 * Offers a file chooser dialog, and then loads the feature colours and
857 * filters from file in XML format and unmarshals to Jalview feature settings
861 JalviewFileChooser chooser = new JalviewFileChooser("fc",
862 "Sequence Feature Colours");
863 chooser.setFileView(new JalviewFileView());
864 chooser.setDialogTitle(
865 MessageManager.getString("label.load_feature_colours"));
866 chooser.setToolTipText(MessageManager.getString("action.load"));
868 int value = chooser.showOpenDialog(this);
870 if (value == JalviewFileChooser.APPROVE_OPTION)
872 File file = chooser.getSelectedFile();
876 InputStreamReader in = new InputStreamReader(
877 new FileInputStream(file), "UTF-8");
879 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
882 * load feature colours
884 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
886 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
887 FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
888 fr.setColour(newcol.getName(), colour);
889 fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
893 * load feature filters; loaded filters will replace any that are
894 * currently defined, other defined filters are left unchanged
896 for (int i = 0; i < jucs.getFilterCount(); i++)
898 jalview.schemabinding.version2.Filter filterModel = jucs
900 String featureType = filterModel.getFeatureType();
901 FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
902 filterModel.getMatcherSet());
903 if (!filter.isEmpty())
905 fr.setFeatureFilter(featureType, filter);
910 * update feature settings table
915 Object[][] data = ((FeatureTableModel) table.getModel())
918 updateFeatureRenderer(data, false);
921 } catch (Exception ex)
923 System.out.println("Error loading User Colour File\n" + ex);
929 * Offers a file chooser dialog, and then saves the current feature colours
930 * and any filters to the selected file in XML format
934 JalviewFileChooser chooser = new JalviewFileChooser("fc",
935 "Sequence Feature Colours");
936 chooser.setFileView(new JalviewFileView());
937 chooser.setDialogTitle(
938 MessageManager.getString("label.save_feature_colours"));
939 chooser.setToolTipText(MessageManager.getString("action.save"));
941 int value = chooser.showSaveDialog(this);
943 if (value == JalviewFileChooser.APPROVE_OPTION)
945 String choice = chooser.getSelectedFile().getPath();
946 JalviewUserColours ucs = new JalviewUserColours();
947 ucs.setSchemeName("Sequence Features");
950 PrintWriter out = new PrintWriter(new OutputStreamWriter(
951 new FileOutputStream(choice), "UTF-8"));
954 * sort feature types by colour order, from 0 (highest)
957 Set<String> fr_colours = fr.getAllFeatureColours();
958 String[] sortedTypes = fr_colours
959 .toArray(new String[fr_colours.size()]);
960 Arrays.sort(sortedTypes, new Comparator<String>()
963 public int compare(String type1, String type2)
965 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
970 * save feature colours
972 for (String featureType : sortedTypes)
974 FeatureColourI fcol = fr.getFeatureStyle(featureType);
975 jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
981 * save any feature filters
983 for (String featureType : sortedTypes)
985 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
986 if (filter != null && !filter.isEmpty())
988 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
989 FeatureMatcherI firstMatcher = iterator.next();
990 MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
992 Filter filterModel = new Filter();
993 filterModel.setFeatureType(featureType);
994 filterModel.setMatcherSet(ms);
995 ucs.addFilter(filterModel);
1001 } catch (Exception ex)
1003 ex.printStackTrace();
1008 public void invertSelection()
1010 for (int i = 0; i < table.getRowCount(); i++)
1012 Boolean value = (Boolean) table.getValueAt(i, SHOW_COLUMN);
1014 table.setValueAt(new Boolean(!value.booleanValue()), i, SHOW_COLUMN);
1018 public void orderByAvWidth()
1020 if (table == null || table.getModel() == null)
1024 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1025 float[] width = new float[data.length];
1029 for (int i = 0; i < data.length; i++)
1031 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1034 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1035 // weight - but have to make per
1036 // sequence, too (awidth[2])
1037 // if (width[i]==1) // hack to distinguish single width sequences.
1048 boolean sort = false;
1049 for (int i = 0; i < width.length; i++)
1051 // awidth = (float[]) typeWidth.get(data[i][0]);
1054 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1057 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1063 width[i] /= max; // normalize
1064 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1068 sort = sort || width[i - 1] > width[i];
1073 jalview.util.QuickSort.sort(width, data);
1074 // update global priority order
1077 updateFeatureRenderer(data, false);
1085 frame.setClosed(true);
1086 } catch (Exception exe)
1092 public void updateFeatureRenderer(Object[][] data)
1094 updateFeatureRenderer(data, true);
1098 * Update the priority order of features; only repaint if this changed the order
1099 * of visible features
1104 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1106 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1108 if (fr.setFeaturePriority(rowData, visibleNew))
1110 af.alignPanel.paintAlignment(true, true);
1115 * Converts table data into an array of data beans
1117 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1119 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1120 for (int i = 0; i < data.length; i++)
1122 String type = (String) data[i][TYPE_COLUMN];
1123 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1124 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1125 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1126 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1132 private void jbInit() throws Exception
1134 this.setLayout(new BorderLayout());
1136 JPanel settingsPane = new JPanel();
1137 settingsPane.setLayout(new BorderLayout());
1139 dasSettingsPane.setLayout(new BorderLayout());
1141 JPanel bigPanel = new JPanel();
1142 bigPanel.setLayout(new BorderLayout());
1144 groupPanel = new JPanel();
1145 bigPanel.add(groupPanel, BorderLayout.NORTH);
1147 JButton invert = new JButton(
1148 MessageManager.getString("label.invert_selection"));
1149 invert.setFont(JvSwingUtils.getLabelFont());
1150 invert.addActionListener(new ActionListener()
1153 public void actionPerformed(ActionEvent e)
1159 JButton optimizeOrder = new JButton(
1160 MessageManager.getString("label.optimise_order"));
1161 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1162 optimizeOrder.addActionListener(new ActionListener()
1165 public void actionPerformed(ActionEvent e)
1171 JButton sortByScore = new JButton(
1172 MessageManager.getString("label.seq_sort_by_score"));
1173 sortByScore.setFont(JvSwingUtils.getLabelFont());
1174 sortByScore.addActionListener(new ActionListener()
1177 public void actionPerformed(ActionEvent e)
1179 af.avc.sortAlignmentByFeatureScore(null);
1182 JButton sortByDens = new JButton(
1183 MessageManager.getString("label.sequence_sort_by_density"));
1184 sortByDens.setFont(JvSwingUtils.getLabelFont());
1185 sortByDens.addActionListener(new ActionListener()
1188 public void actionPerformed(ActionEvent e)
1190 af.avc.sortAlignmentByFeatureDensity(null);
1194 JButton help = new JButton(MessageManager.getString("action.help"));
1195 help.setFont(JvSwingUtils.getLabelFont());
1196 help.addActionListener(new ActionListener()
1199 public void actionPerformed(ActionEvent e)
1203 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1204 } catch (HelpSetException e1)
1206 e1.printStackTrace();
1210 help.setFont(JvSwingUtils.getLabelFont());
1211 help.setText(MessageManager.getString("action.help"));
1212 help.addActionListener(new ActionListener()
1215 public void actionPerformed(ActionEvent e)
1219 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1220 } catch (HelpSetException e1)
1222 e1.printStackTrace();
1227 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1228 cancel.setFont(JvSwingUtils.getLabelFont());
1229 cancel.addActionListener(new ActionListener()
1232 public void actionPerformed(ActionEvent e)
1234 fr.setTransparency(originalTransparency);
1235 fr.setFeatureFilters(originalFilters);
1236 updateFeatureRenderer(originalData);
1241 JButton ok = new JButton(MessageManager.getString("action.ok"));
1242 ok.setFont(JvSwingUtils.getLabelFont());
1243 ok.addActionListener(new ActionListener()
1246 public void actionPerformed(ActionEvent e)
1252 JButton loadColours = new JButton(
1253 MessageManager.getString("label.load_colours"));
1254 loadColours.setFont(JvSwingUtils.getLabelFont());
1255 loadColours.addActionListener(new ActionListener()
1258 public void actionPerformed(ActionEvent e)
1264 JButton saveColours = new JButton(
1265 MessageManager.getString("label.save_colours"));
1266 saveColours.setFont(JvSwingUtils.getLabelFont());
1267 saveColours.addActionListener(new ActionListener()
1270 public void actionPerformed(ActionEvent e)
1275 transparency.addChangeListener(new ChangeListener()
1278 public void stateChanged(ChangeEvent evt)
1280 if (!inConstruction)
1282 fr.setTransparency((100 - transparency.getValue()) / 100f);
1283 af.alignPanel.paintAlignment(true, true);
1288 transparency.setMaximum(70);
1289 transparency.setToolTipText(
1290 MessageManager.getString("label.transparency_tip"));
1291 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1292 fetchDAS.addActionListener(new ActionListener()
1295 public void actionPerformed(ActionEvent e)
1297 fetchDAS_actionPerformed(e);
1300 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1301 saveDAS.addActionListener(new ActionListener()
1304 public void actionPerformed(ActionEvent e)
1306 saveDAS_actionPerformed(e);
1310 JPanel dasButtonPanel = new JPanel();
1311 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1312 dasSettingsPane.setBorder(null);
1313 cancelDAS.setEnabled(false);
1314 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1315 cancelDAS.addActionListener(new ActionListener()
1318 public void actionPerformed(ActionEvent e)
1320 cancelDAS_actionPerformed(e);
1324 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1325 bigPanel.add(transPanel, BorderLayout.SOUTH);
1327 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1328 transbuttons.add(optimizeOrder);
1329 transbuttons.add(invert);
1330 transbuttons.add(sortByScore);
1331 transbuttons.add(sortByDens);
1332 transbuttons.add(help);
1333 transPanel.add(transparency);
1334 transPanel.add(transbuttons);
1336 JPanel buttonPanel = new JPanel();
1337 buttonPanel.add(ok);
1338 buttonPanel.add(cancel);
1339 buttonPanel.add(loadColours);
1340 buttonPanel.add(saveColours);
1341 bigPanel.add(scrollPane, BorderLayout.CENTER);
1342 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1343 dasButtonPanel.add(fetchDAS);
1344 dasButtonPanel.add(cancelDAS);
1345 dasButtonPanel.add(saveDAS);
1346 settingsPane.add(bigPanel, BorderLayout.CENTER);
1347 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1348 this.add(settingsPane);
1351 public void fetchDAS_actionPerformed(ActionEvent e)
1353 fetchDAS.setEnabled(false);
1354 cancelDAS.setEnabled(true);
1355 dassourceBrowser.setGuiEnabled(false);
1356 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1357 .getSelectedSources();
1358 doDasFeatureFetch(selectedSources, true, true);
1362 * get the features from selectedSources for all or the current selection
1364 * @param selectedSources
1365 * @param checkDbRefs
1366 * @param promptFetchDbRefs
1368 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1369 boolean checkDbRefs, boolean promptFetchDbRefs)
1371 SequenceI[] dataset, seqs;
1373 AlignmentViewport vp = af.getViewport();
1374 if (vp.getSelectionGroup() != null
1375 && vp.getSelectionGroup().getSize() > 0)
1377 iSize = vp.getSelectionGroup().getSize();
1378 dataset = new SequenceI[iSize];
1379 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1383 iSize = vp.getAlignment().getHeight();
1384 seqs = vp.getAlignment().getSequencesArray();
1387 dataset = new SequenceI[iSize];
1388 for (int i = 0; i < iSize; i++)
1390 dataset[i] = seqs[i].getDatasetSequence();
1393 cancelDAS.setEnabled(true);
1394 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1395 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1396 af.getViewport().setShowSequenceFeatures(true);
1397 af.showSeqFeatures.setSelected(true);
1401 * blocking call to initialise the das source browser
1403 public void initDasSources()
1405 dassourceBrowser.initDasSources();
1409 * examine the current list of das sources and return any matching the given
1410 * nicknames in sources
1413 * Vector of Strings to resolve to DAS source nicknames.
1414 * @return sources that are present in source list.
1416 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1418 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1422 * get currently selected das sources. ensure you have called initDasSources
1423 * before calling this.
1425 * @return vector of selected das source nicknames
1427 public Vector<jalviewSourceI> getSelectedSources()
1429 return dassourceBrowser.getSelectedSources();
1433 * properly initialise DAS fetcher and then initiate a new thread to fetch
1434 * features from the named sources (rather than any turned on by default)
1438 * if true then runs in same thread, otherwise passes to the Swing
1441 public void fetchDasFeatures(Vector<String> sources, boolean block)
1444 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1445 .resolveSourceNicknames(sources);
1446 if (resolved.size() == 0)
1448 resolved = dassourceBrowser.getSelectedSources();
1450 if (resolved.size() > 0)
1452 final List<jalviewSourceI> dassources = resolved;
1453 fetchDAS.setEnabled(false);
1454 // cancelDAS.setEnabled(true); doDasFetch does this.
1455 Runnable fetcher = new Runnable()
1461 doDasFeatureFetch(dassources, true, false);
1471 SwingUtilities.invokeLater(fetcher);
1476 public void saveDAS_actionPerformed(ActionEvent e)
1479 .saveProperties(jalview.bin.Cache.applicationProperties);
1482 public void complete()
1484 fetchDAS.setEnabled(true);
1485 cancelDAS.setEnabled(false);
1486 dassourceBrowser.setGuiEnabled(true);
1490 public void cancelDAS_actionPerformed(ActionEvent e)
1492 if (dasFeatureFetcher != null)
1494 dasFeatureFetcher.cancel();
1499 public void noDasSourceActive()
1502 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1503 MessageManager.getString("label.no_das_sources_selected_warn"),
1504 MessageManager.getString("label.no_das_sources_selected_title"),
1505 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1508 // ///////////////////////////////////////////////////////////////////////
1509 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1510 // ///////////////////////////////////////////////////////////////////////
1511 class FeatureTableModel extends AbstractTableModel
1513 private String[] columnNames = {
1514 MessageManager.getString("label.feature_type"),
1515 MessageManager.getString("action.colour"),
1516 MessageManager.getString("label.filter"),
1517 MessageManager.getString("label.show") };
1519 private Object[][] data;
1521 FeatureTableModel(Object[][] data)
1526 public Object[][] getData()
1531 public void setData(Object[][] data)
1537 public int getColumnCount()
1539 return columnNames.length;
1542 public Object[] getRow(int row)
1548 public int getRowCount()
1554 public String getColumnName(int col)
1556 return columnNames[col];
1560 public Object getValueAt(int row, int col)
1562 return data[row][col];
1566 * Answers the class of the object in column c of the first row of the table
1569 public Class<?> getColumnClass(int c)
1571 Object v = getValueAt(0, c);
1572 return v == null ? null : v.getClass();
1576 public boolean isCellEditable(int row, int col)
1578 return col == 0 ? false : true;
1582 public void setValueAt(Object value, int row, int col)
1584 data[row][col] = value;
1585 fireTableCellUpdated(row, col);
1586 updateFeatureRenderer(data);
1591 class ColorRenderer extends JLabel implements TableCellRenderer
1593 javax.swing.border.Border unselectedBorder = null;
1595 javax.swing.border.Border selectedBorder = null;
1597 final String baseTT = "Click to edit, right/apple click for menu.";
1599 public ColorRenderer()
1601 setOpaque(true); // MUST do this for background to show up.
1602 setHorizontalTextPosition(SwingConstants.CENTER);
1603 setVerticalTextPosition(SwingConstants.CENTER);
1607 public Component getTableCellRendererComponent(JTable tbl, Object color,
1608 boolean isSelected, boolean hasFocus, int row, int column)
1610 FeatureColourI cellColour = (FeatureColourI) color;
1612 setToolTipText(baseTT);
1613 setBackground(tbl.getBackground());
1614 if (!cellColour.isSimpleColour())
1616 Rectangle cr = tbl.getCellRect(row, column, false);
1617 FeatureSettings.renderGraduatedColor(this, cellColour,
1618 (int) cr.getWidth(), (int) cr.getHeight());
1624 setBackground(cellColour.getColour());
1628 if (selectedBorder == null)
1630 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1631 tbl.getSelectionBackground());
1633 setBorder(selectedBorder);
1637 if (unselectedBorder == null)
1639 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1640 tbl.getBackground());
1642 setBorder(unselectedBorder);
1649 class FilterRenderer extends JLabel implements TableCellRenderer
1651 javax.swing.border.Border unselectedBorder = null;
1653 javax.swing.border.Border selectedBorder = null;
1655 public FilterRenderer()
1657 setOpaque(true); // MUST do this for background to show up.
1658 setHorizontalTextPosition(SwingConstants.CENTER);
1659 setVerticalTextPosition(SwingConstants.CENTER);
1663 public Component getTableCellRendererComponent(JTable tbl,
1664 Object filter, boolean isSelected, boolean hasFocus, int row,
1667 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1669 String asText = theFilter.toString();
1670 setBackground(tbl.getBackground());
1671 this.setText(asText);
1676 if (selectedBorder == null)
1678 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1679 tbl.getSelectionBackground());
1681 setBorder(selectedBorder);
1685 if (unselectedBorder == null)
1687 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1688 tbl.getBackground());
1690 setBorder(unselectedBorder);
1698 * update comp using rendering settings from gcol
1703 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1705 int w = comp.getWidth(), h = comp.getHeight();
1708 w = (int) comp.getPreferredSize().getWidth();
1709 h = (int) comp.getPreferredSize().getHeight();
1716 renderGraduatedColor(comp, gcol, w, h);
1719 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1722 boolean thr = false;
1723 StringBuilder tt = new StringBuilder();
1724 StringBuilder tx = new StringBuilder();
1726 if (gcol.isColourByAttribute())
1728 tx.append(String.join(":", gcol.getAttributeName()));
1730 else if (!gcol.isColourByLabel())
1732 tx.append(MessageManager.getString("label.score"));
1735 if (gcol.isAboveThreshold())
1739 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1742 if (gcol.isBelowThreshold())
1746 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1749 if (gcol.isColourByLabel())
1751 tt.append("Coloured by label text. ").append(tt);
1756 if (!gcol.isColourByAttribute())
1764 Color newColor = gcol.getMaxColour();
1765 comp.setBackground(newColor);
1766 // System.err.println("Width is " + w / 2);
1767 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1768 comp.setIcon(ficon);
1769 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1770 // + newColor.getGreen() + ", " + newColor.getBlue()
1771 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1772 // + ", " + minCol.getBlue() + ")");
1774 comp.setHorizontalAlignment(SwingConstants.CENTER);
1775 comp.setText(tx.toString());
1776 if (tt.length() > 0)
1778 if (comp.getToolTipText() == null)
1780 comp.setToolTipText(tt.toString());
1784 comp.setToolTipText(
1785 tt.append(" ").append(comp.getToolTipText()).toString());
1790 class ColorEditor extends AbstractCellEditor
1791 implements TableCellEditor, ActionListener
1795 FeatureColourI currentColor;
1797 FeatureTypeSettings chooser;
1803 JColorChooser colorChooser;
1807 protected static final String EDIT = "edit";
1809 int rowSelected = 0;
1811 public ColorEditor(FeatureSettings me)
1814 // Set up the editor (from the table's point of view),
1815 // which is a button.
1816 // This button brings up the color chooser dialog,
1817 // which is the editor from the user's point of view.
1818 button = new JButton();
1819 button.setActionCommand(EDIT);
1820 button.addActionListener(this);
1821 button.setBorderPainted(false);
1822 // Set up the dialog that the button brings up.
1823 colorChooser = new JColorChooser();
1824 dialog = JColorChooser.createDialog(button,
1825 MessageManager.getString("label.select_colour"), true, // modal
1826 colorChooser, this, // OK button handler
1827 null); // no CANCEL button handler
1831 * Handles events from the editor button and from the dialog's OK button.
1834 public void actionPerformed(ActionEvent e)
1836 // todo test e.getSource() instead here
1837 if (EDIT.equals(e.getActionCommand()))
1839 // The user has clicked the cell, so
1840 // bring up the dialog.
1841 if (currentColor.isSimpleColour())
1843 // bring up simple color chooser
1844 button.setBackground(currentColor.getColour());
1845 colorChooser.setColor(currentColor.getColour());
1846 dialog.setVisible(true);
1850 // bring up graduated chooser.
1851 chooser = new FeatureTypeSettings(me.fr, type);
1852 chooser.setRequestFocusEnabled(true);
1853 chooser.requestFocus();
1854 chooser.addActionListener(this);
1855 chooser.showTab(true);
1857 // Make the renderer reappear.
1858 fireEditingStopped();
1863 if (currentColor.isSimpleColour())
1866 * read off colour picked in colour chooser after OK pressed
1868 currentColor = new FeatureColour(colorChooser.getColor());
1869 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1874 * after OK in variable colour dialog, any changes to colour
1875 * (or filters!) are already set in FeatureRenderer, so just
1876 * update table data without triggering updateFeatureRenderer
1878 currentColor = fr.getFeatureColours().get(type);
1879 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1880 if (currentFilter == null)
1882 currentFilter = new FeatureMatcherSet();
1884 Object[] data = ((FeatureTableModel) table.getModel())
1885 .getData()[rowSelected];
1886 data[COLOUR_COLUMN] = currentColor;
1887 data[FILTER_COLUMN] = currentFilter;
1889 fireEditingStopped();
1890 me.table.validate();
1894 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1896 public Object getCellEditorValue()
1898 return currentColor;
1901 // Implement the one method defined by TableCellEditor.
1903 public Component getTableCellEditorComponent(JTable theTable, Object value,
1904 boolean isSelected, int row, int column)
1906 currentColor = (FeatureColourI) value;
1907 this.rowSelected = row;
1908 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1909 button.setOpaque(true);
1910 button.setBackground(me.getBackground());
1911 if (!currentColor.isSimpleColour())
1913 JLabel btn = new JLabel();
1914 btn.setSize(button.getSize());
1915 FeatureSettings.renderGraduatedColor(btn, currentColor);
1916 button.setBackground(btn.getBackground());
1917 button.setIcon(btn.getIcon());
1918 button.setText(btn.getText());
1923 button.setIcon(null);
1924 button.setBackground(currentColor.getColour());
1931 * The cell editor for the Filter column. It displays the text of any filters
1932 * for the feature type in that row (in full as a tooltip, possible abbreviated
1933 * as display text). On click in the cell, opens the Feature Display Settings
1934 * dialog at the Filters tab.
1936 class FilterEditor extends AbstractCellEditor
1937 implements TableCellEditor, ActionListener
1941 FeatureMatcherSetI currentFilter;
1949 protected static final String EDIT = "edit";
1951 int rowSelected = 0;
1953 public FilterEditor(FeatureSettings me)
1956 button = new JButton();
1957 button.setActionCommand(EDIT);
1958 button.addActionListener(this);
1959 button.setBorderPainted(false);
1963 * Handles events from the editor button
1966 public void actionPerformed(ActionEvent e)
1968 if (button == e.getSource())
1970 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1971 chooser.addActionListener(this);
1972 chooser.setRequestFocusEnabled(true);
1973 chooser.requestFocus();
1974 if (lastLocation != null)
1976 // todo open at its last position on screen
1977 chooser.setBounds(lastLocation.x, lastLocation.y,
1978 chooser.getWidth(), chooser.getHeight());
1981 chooser.showTab(false);
1982 fireEditingStopped();
1984 else if (e.getSource() instanceof Component)
1988 * after OK in variable colour dialog, any changes to filter
1989 * (or colours!) are already set in FeatureRenderer, so just
1990 * update table data without triggering updateFeatureRenderer
1992 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1993 currentFilter = me.fr.getFeatureFilter(type);
1994 if (currentFilter == null)
1996 currentFilter = new FeatureMatcherSet();
1998 Object[] data = ((FeatureTableModel) table.getModel())
1999 .getData()[rowSelected];
2000 data[COLOUR_COLUMN] = currentColor;
2001 data[FILTER_COLUMN] = currentFilter;
2002 fireEditingStopped();
2003 me.table.validate();
2008 public Object getCellEditorValue()
2010 return currentFilter;
2014 public Component getTableCellEditorComponent(JTable theTable, Object value,
2015 boolean isSelected, int row, int column)
2017 currentFilter = (FeatureMatcherSetI) value;
2018 this.rowSelected = row;
2019 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2020 button.setOpaque(true);
2021 button.setBackground(me.getBackground());
2022 button.setText(currentFilter.toString());
2023 button.setToolTipText(currentFilter.toString());
2024 button.setIcon(null);
2030 class FeatureIcon implements Icon
2032 FeatureColourI gcol;
2036 boolean midspace = false;
2038 int width = 50, height = 20;
2040 int s1, e1; // start and end of midpoint band for thresholded symbol
2042 Color mpcolour = Color.white;
2044 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2064 public int getIconWidth()
2070 public int getIconHeight()
2076 public void paintIcon(Component c, Graphics g, int x, int y)
2079 if (gcol.isColourByLabel())
2082 g.fillRect(0, 0, width, height);
2083 // need an icon here.
2084 g.setColor(gcol.getMaxColour());
2086 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2088 // g.setFont(g.getFont().deriveFont(
2089 // AffineTransform.getScaleInstance(
2090 // width/g.getFontMetrics().stringWidth("Label"),
2091 // height/g.getFontMetrics().getHeight())));
2093 g.drawString(MessageManager.getString("label.label"), 0, 0);
2098 Color minCol = gcol.getMinColour();
2100 g.fillRect(0, 0, s1, height);
2103 g.setColor(Color.white);
2104 g.fillRect(s1, 0, e1 - s1, height);
2106 g.setColor(gcol.getMaxColour());
2107 g.fillRect(0, e1, width - e1, height);