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.bin.Cache;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.SequenceI;
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.JalviewUserColours;
34 import jalview.schemes.FeatureColour;
35 import jalview.util.Format;
36 import jalview.util.MessageManager;
37 import jalview.util.Platform;
38 import jalview.util.QuickSort;
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.HashMap;
70 import java.util.HashSet;
71 import java.util.Hashtable;
72 import java.util.Iterator;
73 import java.util.List;
76 import java.util.Vector;
78 import javax.help.HelpSetException;
79 import javax.swing.AbstractCellEditor;
80 import javax.swing.BorderFactory;
81 import javax.swing.Icon;
82 import javax.swing.JButton;
83 import javax.swing.JCheckBox;
84 import javax.swing.JCheckBoxMenuItem;
85 import javax.swing.JColorChooser;
86 import javax.swing.JDialog;
87 import javax.swing.JInternalFrame;
88 import javax.swing.JLabel;
89 import javax.swing.JLayeredPane;
90 import javax.swing.JMenuItem;
91 import javax.swing.JPanel;
92 import javax.swing.JPopupMenu;
93 import javax.swing.JScrollPane;
94 import javax.swing.JSlider;
95 import javax.swing.JTable;
96 import javax.swing.ListSelectionModel;
97 import javax.swing.SwingConstants;
98 import javax.swing.SwingUtilities;
99 import javax.swing.event.ChangeEvent;
100 import javax.swing.event.ChangeListener;
101 import javax.swing.table.AbstractTableModel;
102 import javax.swing.table.TableCellEditor;
103 import javax.swing.table.TableCellRenderer;
104 import javax.swing.table.TableColumn;
106 public class FeatureSettings extends JPanel
107 implements FeatureSettingsControllerI
110 * column indices of fields in Feature Settings table
112 static final int TYPE_COLUMN = 0;
114 static final int COLOUR_COLUMN = 1;
116 static final int FILTER_COLUMN = 2;
118 static final int SHOW_COLUMN = 3;
120 private static final int COLUMN_COUNT = 4;
122 private static final int MIN_WIDTH = 400;
124 private static final int MIN_HEIGHT = 400;
126 DasSourceBrowser dassourceBrowser;
128 DasSequenceFeatureFetcher dasFeatureFetcher;
130 JPanel dasSettingsPane = new JPanel();
132 final FeatureRenderer fr;
134 public final AlignFrame af;
137 * 'original' fields hold settings to restore on Cancel
139 Object[][] originalData;
141 private float originalTransparency;
143 private Map<String, FeatureMatcherSetI> originalFilters;
145 final JInternalFrame frame;
147 JScrollPane scrollPane = new JScrollPane();
153 JSlider transparency = new JSlider();
156 * when true, constructor is still executing - so ignore UI events
158 protected volatile boolean inConstruction = true;
160 int selectedRow = -1;
162 JButton fetchDAS = new JButton();
164 JButton saveDAS = new JButton();
166 JButton cancelDAS = new JButton();
168 boolean resettingTable = false;
171 * true when Feature Settings are updating from feature renderer
173 private boolean handlingUpdate = false;
176 * holds {featureCount, totalExtent} for each feature type
178 Map<String, float[]> typeWidth = null;
185 public FeatureSettings(AlignFrame alignFrame)
187 this.af = alignFrame;
188 fr = af.getFeatureRenderer();
190 // save transparency for restore on Cancel
191 originalTransparency = fr.getTransparency();
192 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
193 transparency.setMaximum(100 - originalTransparencyAsPercent);
195 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
200 } catch (Exception ex)
202 ex.printStackTrace();
208 public String getToolTipText(MouseEvent e)
211 int column = table.columnAtPoint(e.getPoint());
215 tip = JvSwingUtils.wrapTooltip(true, MessageManager
216 .getString("label.feature_settings_click_drag"));
219 int row = table.rowAtPoint(e.getPoint());
220 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
223 ? MessageManager.getString("label.filters_tooltip")
232 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
233 table.setFont(new Font("Verdana", Font.PLAIN, 12));
235 // table.setDefaultRenderer(Color.class, new ColorRenderer());
236 // table.setDefaultEditor(Color.class, new ColorEditor(this));
238 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
239 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
241 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
242 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
244 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
245 new ColorRenderer(), new ColorEditor(this));
246 table.addColumn(colourColumn);
248 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
249 new FilterRenderer(), new FilterEditor(this));
250 table.addColumn(filterColumn);
252 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
254 table.addMouseListener(new MouseAdapter()
257 public void mousePressed(MouseEvent evt)
259 selectedRow = table.rowAtPoint(evt.getPoint());
260 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
261 if (evt.isPopupTrigger())
263 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
264 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
267 else if (evt.getClickCount() == 2)
269 boolean invertSelection = evt.isAltDown();
270 boolean toggleSelection = Platform.isControlDown(evt);
271 boolean extendSelection = evt.isShiftDown();
272 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
273 invertSelection, extendSelection, toggleSelection, type);
277 // isPopupTrigger fires on mouseReleased on Windows
279 public void mouseReleased(MouseEvent evt)
281 selectedRow = table.rowAtPoint(evt.getPoint());
282 if (evt.isPopupTrigger())
284 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
285 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
286 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
292 table.addMouseMotionListener(new MouseMotionAdapter()
295 public void mouseDragged(MouseEvent evt)
297 int newRow = table.rowAtPoint(evt.getPoint());
298 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
301 * reposition 'selectedRow' to 'newRow' (the dragged to location)
302 * this could be more than one row away for a very fast drag action
303 * so just swap it with adjacent rows until we get it there
305 Object[][] data = ((FeatureTableModel) table.getModel())
307 int direction = newRow < selectedRow ? -1 : 1;
308 for (int i = selectedRow; i != newRow; i += direction)
310 Object[] temp = data[i];
311 data[i] = data[i + direction];
312 data[i + direction] = temp;
314 updateFeatureRenderer(data);
316 selectedRow = newRow;
320 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
321 // MessageManager.getString("label.feature_settings_click_drag")));
322 scrollPane.setViewportView(table);
324 dassourceBrowser = new DasSourceBrowser(this);
325 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
327 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
329 fr.findAllFeatures(true); // display everything!
332 discoverAllFeatureData();
333 final PropertyChangeListener change;
334 final FeatureSettings fs = this;
335 fr.addPropertyChangeListener(change = new PropertyChangeListener()
338 public void propertyChange(PropertyChangeEvent evt)
340 if (!fs.resettingTable && !fs.handlingUpdate)
342 fs.handlingUpdate = true;
344 // new groups may be added with new sequence feature types only
345 fs.handlingUpdate = false;
351 frame = new JInternalFrame();
352 frame.setContentPane(this);
353 if (Platform.isAMac())
355 Desktop.addInternalFrame(frame,
356 MessageManager.getString("label.sequence_feature_settings"),
361 Desktop.addInternalFrame(frame,
362 MessageManager.getString("label.sequence_feature_settings"),
365 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
367 frame.addInternalFrameListener(
368 new javax.swing.event.InternalFrameAdapter()
371 public void internalFrameClosed(
372 javax.swing.event.InternalFrameEvent evt)
374 fr.removePropertyChangeListener(change);
375 dassourceBrowser.fs = null;
378 frame.setLayer(JLayeredPane.PALETTE_LAYER);
379 inConstruction = false;
382 protected void popupSort(final int rowSelected, final String type,
383 final Object typeCol, final Map<String, float[][]> minmax, int x,
386 final FeatureColourI featureColour = (FeatureColourI) typeCol;
388 JPopupMenu men = new JPopupMenu(MessageManager
389 .formatMessage("label.settings_for_param", new String[]
391 JMenuItem scr = new JMenuItem(
392 MessageManager.getString("label.sort_by_score"));
394 final FeatureSettings me = this;
395 scr.addActionListener(new ActionListener()
399 public void actionPerformed(ActionEvent e)
402 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
407 JMenuItem dens = new JMenuItem(
408 MessageManager.getString("label.sort_by_density"));
409 dens.addActionListener(new ActionListener()
413 public void actionPerformed(ActionEvent e)
416 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
424 * variable colour options include colour by label, by score,
425 * by selected attribute text, or attribute value
427 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
428 MessageManager.getString("label.variable_colour"));
429 mxcol.setSelected(!featureColour.isSimpleColour());
431 mxcol.addActionListener(new ActionListener()
433 JColorChooser colorChooser;
436 public void actionPerformed(ActionEvent e)
438 if (e.getSource() == mxcol)
440 if (featureColour.isSimpleColour())
442 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
443 fc.addActionListener(this);
447 // bring up simple color chooser
448 colorChooser = new JColorChooser();
449 String title = MessageManager
450 .getString("label.select_colour");
451 JDialog dialog = JColorChooser.createDialog(me,
452 title, true, // modal
453 colorChooser, this, // OK button handler
454 null); // no CANCEL button handler
455 colorChooser.setColor(featureColour.getMaxColour());
456 dialog.setVisible(true);
461 if (e.getSource() instanceof FeatureTypeSettings)
464 * update after OK in feature colour dialog; the updated
465 * colour will have already been set in the FeatureRenderer
467 FeatureColourI fci = fr.getFeatureColours().get(type);
468 table.setValueAt(fci, rowSelected, 1);
473 // probably the color chooser!
474 table.setValueAt(new FeatureColour(colorChooser.getColor()),
477 me.updateFeatureRenderer(
478 ((FeatureTableModel) table.getModel()).getData(),
486 JMenuItem selCols = new JMenuItem(
487 MessageManager.getString("label.select_columns_containing"));
488 selCols.addActionListener(new ActionListener()
491 public void actionPerformed(ActionEvent arg0)
493 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
497 JMenuItem clearCols = new JMenuItem(MessageManager
498 .getString("label.select_columns_not_containing"));
499 clearCols.addActionListener(new ActionListener()
502 public void actionPerformed(ActionEvent arg0)
504 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
508 JMenuItem hideCols = new JMenuItem(
509 MessageManager.getString("label.hide_columns_containing"));
510 hideCols.addActionListener(new ActionListener()
513 public void actionPerformed(ActionEvent arg0)
515 fr.ap.alignFrame.hideFeatureColumns(type, true);
518 JMenuItem hideOtherCols = new JMenuItem(
519 MessageManager.getString("label.hide_columns_not_containing"));
520 hideOtherCols.addActionListener(new ActionListener()
523 public void actionPerformed(ActionEvent arg0)
525 fr.ap.alignFrame.hideFeatureColumns(type, false);
531 men.add(hideOtherCols);
532 men.show(table, x, y);
536 synchronized public void discoverAllFeatureData()
538 Set<String> allGroups = new HashSet<>();
539 AlignmentI alignment = af.getViewport().getAlignment();
541 for (int i = 0; i < alignment.getHeight(); i++)
543 SequenceI seq = alignment.getSequenceAt(i);
544 for (String group : seq.getFeatures().getFeatureGroups(true))
546 if (group != null && !allGroups.contains(group))
548 allGroups.add(group);
549 checkGroupState(group);
560 * Synchronise gui group list and check visibility of group
563 * @return true if group is visible
565 private boolean checkGroupState(String group)
567 boolean visible = fr.checkGroupVisibility(group, true);
569 for (int g = 0; g < groupPanel.getComponentCount(); g++)
571 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
573 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
578 final String grp = group;
579 final JCheckBox check = new JCheckBox(group, visible);
580 check.setFont(new Font("Serif", Font.BOLD, 12));
581 check.setToolTipText(group);
582 check.addItemListener(new ItemListener()
585 public void itemStateChanged(ItemEvent evt)
587 fr.setGroupVisibility(check.getText(), check.isSelected());
588 resetTable(new String[] { grp });
589 af.alignPanel.paintAlignment(true, true);
592 groupPanel.add(check);
596 synchronized void resetTable(String[] groupChanged)
602 resettingTable = true;
603 typeWidth = new Hashtable<>();
604 // TODO: change avWidth calculation to 'per-sequence' average and use long
607 Set<String> displayableTypes = new HashSet<>();
608 Set<String> foundGroups = new HashSet<>();
611 * determine which feature types may be visible depending on
612 * which groups are selected, and recompute average width data
614 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
617 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
620 * get the sequence's groups for positional features
621 * and keep track of which groups are visible
623 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
624 Set<String> visibleGroups = new HashSet<>();
625 for (String group : groups)
627 if (group == null || checkGroupState(group))
629 visibleGroups.add(group);
632 foundGroups.addAll(groups);
635 * get distinct feature types for visible groups
636 * record distinct visible types, and their count and total length
638 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
639 visibleGroups.toArray(new String[visibleGroups.size()]));
640 for (String type : types)
642 displayableTypes.add(type);
643 float[] avWidth = typeWidth.get(type);
646 avWidth = new float[2];
647 typeWidth.put(type, avWidth);
649 // todo this could include features with a non-visible group
650 // - do we greatly care?
651 // todo should we include non-displayable features here, and only
652 // update when features are added?
653 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
654 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
658 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
661 if (fr.hasRenderOrder())
665 fr.findAllFeatures(groupChanged != null); // prod to update
666 // colourschemes. but don't
668 // First add the checks in the previous render order,
669 // in case the window has been closed and reopened
671 List<String> frl = fr.getRenderOrder();
672 for (int ro = frl.size() - 1; ro > -1; ro--)
674 String type = frl.get(ro);
676 if (!displayableTypes.contains(type))
681 data[dataIndex][TYPE_COLUMN] = type;
682 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
683 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
684 data[dataIndex][FILTER_COLUMN] = featureFilter == null
685 ? new FeatureMatcherSet()
687 data[dataIndex][SHOW_COLUMN] = new Boolean(
688 af.getViewport().getFeaturesDisplayed().isVisible(type));
690 displayableTypes.remove(type);
695 * process any extra features belonging only to
696 * a group which was just selected
698 while (!displayableTypes.isEmpty())
700 String type = displayableTypes.iterator().next();
701 data[dataIndex][TYPE_COLUMN] = type;
703 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
704 if (data[dataIndex][COLOUR_COLUMN] == null)
706 // "Colour has been updated in another view!!"
707 fr.clearRenderOrder();
710 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
711 data[dataIndex][FILTER_COLUMN] = featureFilter == null
712 ? new FeatureMatcherSet()
714 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
716 displayableTypes.remove(type);
719 if (originalData == null)
721 originalData = new Object[data.length][COLUMN_COUNT];
722 for (int i = 0; i < data.length; i++)
724 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
729 updateOriginalData(data);
732 table.setModel(new FeatureTableModel(data));
733 table.getColumnModel().getColumn(0).setPreferredWidth(200);
735 groupPanel.setLayout(
736 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
737 pruneGroups(foundGroups);
738 groupPanel.validate();
740 updateFeatureRenderer(data, groupChanged != null);
741 resettingTable = false;
745 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
746 * have been made outwith this dialog
748 * <li>a new feature type added (and made visible)</li>
749 * <li>a feature colour changed (in the Amend Features dialog)</li>
754 protected void updateOriginalData(Object[][] foundData)
756 // todo LinkedHashMap instead of Object[][] would be nice
758 Object[][] currentData = ((FeatureTableModel) table.getModel())
760 for (Object[] row : foundData)
762 String type = (String) row[TYPE_COLUMN];
763 boolean found = false;
764 for (Object[] current : currentData)
766 if (type.equals(current[TYPE_COLUMN]))
770 * currently dependent on object equality here;
771 * really need an equals method on FeatureColour
773 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
776 * feature colour has changed externally - update originalData
778 for (Object[] original : originalData)
780 if (type.equals(original[TYPE_COLUMN]))
782 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
793 * new feature detected - add to original data (on top)
795 Object[][] newData = new Object[originalData.length
797 for (int i = 0; i < originalData.length; i++)
799 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
803 originalData = newData;
809 * Remove from the groups panel any checkboxes for groups that are not in the
810 * foundGroups set. This enables removing a group from the display when the last
811 * feature in that group is deleted.
815 protected void pruneGroups(Set<String> foundGroups)
817 for (int g = 0; g < groupPanel.getComponentCount(); g++)
819 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
820 if (!foundGroups.contains(checkbox.getText()))
822 groupPanel.remove(checkbox);
828 * reorder data based on the featureRenderers global priority list.
832 private void ensureOrder(Object[][] data)
834 boolean sort = false;
835 float[] order = new float[data.length];
836 for (int i = 0; i < order.length; i++)
838 order[i] = fr.getOrder(data[i][0].toString());
841 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
845 sort = sort || order[i - 1] > order[i];
850 jalview.util.QuickSort.sort(order, data);
856 JalviewFileChooser chooser = new JalviewFileChooser("fc",
857 "Sequence Feature Colours");
858 chooser.setFileView(new JalviewFileView());
859 chooser.setDialogTitle(
860 MessageManager.getString("label.load_feature_colours"));
861 chooser.setToolTipText(MessageManager.getString("action.load"));
863 int value = chooser.showOpenDialog(this);
865 if (value == JalviewFileChooser.APPROVE_OPTION)
867 File file = chooser.getSelectedFile();
871 InputStreamReader in = new InputStreamReader(
872 new FileInputStream(file), "UTF-8");
874 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
876 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
879 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
882 Color mincol = null, maxcol = null;
885 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
886 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
888 } catch (Exception e)
890 Cache.log.warn("Couldn't parse out graduated feature color.",
893 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
894 newcol.getMin(), newcol.getMax());
895 if (newcol.hasAutoScale())
897 gcol.setAutoScaled(newcol.getAutoScale());
899 if (newcol.hasColourByLabel())
901 gcol.setColourByLabel(newcol.getColourByLabel());
903 if (newcol.hasThreshold())
905 gcol.setThreshold(newcol.getThreshold());
907 if (newcol.getThreshType().length() > 0)
909 String ttyp = newcol.getThreshType();
910 if (ttyp.equalsIgnoreCase("ABOVE"))
912 gcol.setAboveThreshold(true);
914 if (ttyp.equalsIgnoreCase("BELOW"))
916 gcol.setBelowThreshold(true);
919 fr.setColour(name = newcol.getName(), gcol);
923 Color color = new Color(
924 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
925 fr.setColour(name = jucs.getColour(i).getName(),
926 new FeatureColour(color));
928 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
933 Object[][] data = ((FeatureTableModel) table.getModel())
936 updateFeatureRenderer(data, false);
939 } catch (Exception ex)
941 System.out.println("Error loading User Colour File\n" + ex);
948 JalviewFileChooser chooser = new JalviewFileChooser("fc",
949 "Sequence Feature Colours");
950 chooser.setFileView(new JalviewFileView());
951 chooser.setDialogTitle(
952 MessageManager.getString("label.save_feature_colours"));
953 chooser.setToolTipText(MessageManager.getString("action.save"));
955 int value = chooser.showSaveDialog(this);
957 if (value == JalviewFileChooser.APPROVE_OPTION)
959 String choice = chooser.getSelectedFile().getPath();
960 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
961 ucs.setSchemeName("Sequence Features");
964 PrintWriter out = new PrintWriter(new OutputStreamWriter(
965 new FileOutputStream(choice), "UTF-8"));
967 Set<String> fr_colours = fr.getAllFeatureColours();
968 Iterator<String> e = fr_colours.iterator();
969 float[] sortOrder = new float[fr_colours.size()];
970 String[] sortTypes = new String[fr_colours.size()];
974 sortTypes[i] = e.next();
975 sortOrder[i] = fr.getOrder(sortTypes[i]);
978 QuickSort.sort(sortOrder, sortTypes);
980 for (i = 0; i < sortTypes.length; i++)
982 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
983 col.setName(sortTypes[i]);
984 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
985 if (fcol.isSimpleColour())
987 col.setRGB(Format.getHexString(fcol.getColour()));
991 col.setRGB(Format.getHexString(fcol.getMaxColour()));
992 col.setMin(fcol.getMin());
993 col.setMax(fcol.getMax());
995 jalview.util.Format.getHexString(fcol.getMinColour()));
996 col.setAutoScale(fcol.isAutoScaled());
997 col.setThreshold(fcol.getThreshold());
998 col.setColourByLabel(fcol.isColourByLabel());
999 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
1000 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
1006 } catch (Exception ex)
1008 ex.printStackTrace();
1013 public void invertSelection()
1015 for (int i = 0; i < table.getRowCount(); i++)
1017 Boolean value = (Boolean) table.getValueAt(i, SHOW_COLUMN);
1019 table.setValueAt(new Boolean(!value.booleanValue()), i, SHOW_COLUMN);
1023 public void orderByAvWidth()
1025 if (table == null || table.getModel() == null)
1029 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1030 float[] width = new float[data.length];
1034 for (int i = 0; i < data.length; i++)
1036 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1039 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1040 // weight - but have to make per
1041 // sequence, too (awidth[2])
1042 // if (width[i]==1) // hack to distinguish single width sequences.
1053 boolean sort = false;
1054 for (int i = 0; i < width.length; i++)
1056 // awidth = (float[]) typeWidth.get(data[i][0]);
1059 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1062 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1068 width[i] /= max; // normalize
1069 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1073 sort = sort || width[i - 1] > width[i];
1078 jalview.util.QuickSort.sort(width, data);
1079 // update global priority order
1082 updateFeatureRenderer(data, false);
1090 frame.setClosed(true);
1091 } catch (Exception exe)
1097 public void updateFeatureRenderer(Object[][] data)
1099 updateFeatureRenderer(data, true);
1103 * Update the priority order of features; only repaint if this changed the order
1104 * of visible features
1109 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1111 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1113 if (fr.setFeaturePriority(rowData, visibleNew))
1115 af.alignPanel.paintAlignment(true, true);
1120 * Converts table data into an array of data beans
1122 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1124 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1125 for (int i = 0; i < data.length; i++)
1127 String type = (String) data[i][TYPE_COLUMN];
1128 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1129 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1130 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1131 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1137 private void jbInit() throws Exception
1139 this.setLayout(new BorderLayout());
1141 JPanel settingsPane = new JPanel();
1142 settingsPane.setLayout(new BorderLayout());
1144 dasSettingsPane.setLayout(new BorderLayout());
1146 JPanel bigPanel = new JPanel();
1147 bigPanel.setLayout(new BorderLayout());
1149 groupPanel = new JPanel();
1150 bigPanel.add(groupPanel, BorderLayout.NORTH);
1152 JButton invert = new JButton(
1153 MessageManager.getString("label.invert_selection"));
1154 invert.setFont(JvSwingUtils.getLabelFont());
1155 invert.addActionListener(new ActionListener()
1158 public void actionPerformed(ActionEvent e)
1164 JButton optimizeOrder = new JButton(
1165 MessageManager.getString("label.optimise_order"));
1166 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1167 optimizeOrder.addActionListener(new ActionListener()
1170 public void actionPerformed(ActionEvent e)
1176 JButton sortByScore = new JButton(
1177 MessageManager.getString("label.seq_sort_by_score"));
1178 sortByScore.setFont(JvSwingUtils.getLabelFont());
1179 sortByScore.addActionListener(new ActionListener()
1182 public void actionPerformed(ActionEvent e)
1184 af.avc.sortAlignmentByFeatureScore(null);
1187 JButton sortByDens = new JButton(
1188 MessageManager.getString("label.sequence_sort_by_density"));
1189 sortByDens.setFont(JvSwingUtils.getLabelFont());
1190 sortByDens.addActionListener(new ActionListener()
1193 public void actionPerformed(ActionEvent e)
1195 af.avc.sortAlignmentByFeatureDensity(null);
1199 JButton help = new JButton(MessageManager.getString("action.help"));
1200 help.setFont(JvSwingUtils.getLabelFont());
1201 help.addActionListener(new ActionListener()
1204 public void actionPerformed(ActionEvent e)
1208 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1209 } catch (HelpSetException e1)
1211 e1.printStackTrace();
1215 help.setFont(JvSwingUtils.getLabelFont());
1216 help.setText(MessageManager.getString("action.help"));
1217 help.addActionListener(new ActionListener()
1220 public void actionPerformed(ActionEvent e)
1224 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1225 } catch (HelpSetException e1)
1227 e1.printStackTrace();
1232 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1233 cancel.setFont(JvSwingUtils.getLabelFont());
1234 cancel.addActionListener(new ActionListener()
1237 public void actionPerformed(ActionEvent e)
1239 fr.setTransparency(originalTransparency);
1240 fr.setFeatureFilters(originalFilters);
1241 updateFeatureRenderer(originalData);
1246 JButton ok = new JButton(MessageManager.getString("action.ok"));
1247 ok.setFont(JvSwingUtils.getLabelFont());
1248 ok.addActionListener(new ActionListener()
1251 public void actionPerformed(ActionEvent e)
1257 JButton loadColours = new JButton(
1258 MessageManager.getString("label.load_colours"));
1259 loadColours.setFont(JvSwingUtils.getLabelFont());
1260 loadColours.addActionListener(new ActionListener()
1263 public void actionPerformed(ActionEvent e)
1269 JButton saveColours = new JButton(
1270 MessageManager.getString("label.save_colours"));
1271 saveColours.setFont(JvSwingUtils.getLabelFont());
1272 saveColours.addActionListener(new ActionListener()
1275 public void actionPerformed(ActionEvent e)
1280 transparency.addChangeListener(new ChangeListener()
1283 public void stateChanged(ChangeEvent evt)
1285 if (!inConstruction)
1287 fr.setTransparency((100 - transparency.getValue()) / 100f);
1288 af.alignPanel.paintAlignment(true, true);
1293 transparency.setMaximum(70);
1294 transparency.setToolTipText(
1295 MessageManager.getString("label.transparency_tip"));
1296 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1297 fetchDAS.addActionListener(new ActionListener()
1300 public void actionPerformed(ActionEvent e)
1302 fetchDAS_actionPerformed(e);
1305 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1306 saveDAS.addActionListener(new ActionListener()
1309 public void actionPerformed(ActionEvent e)
1311 saveDAS_actionPerformed(e);
1315 JPanel dasButtonPanel = new JPanel();
1316 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1317 dasSettingsPane.setBorder(null);
1318 cancelDAS.setEnabled(false);
1319 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1320 cancelDAS.addActionListener(new ActionListener()
1323 public void actionPerformed(ActionEvent e)
1325 cancelDAS_actionPerformed(e);
1329 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1330 bigPanel.add(transPanel, BorderLayout.SOUTH);
1332 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1333 transbuttons.add(optimizeOrder);
1334 transbuttons.add(invert);
1335 transbuttons.add(sortByScore);
1336 transbuttons.add(sortByDens);
1337 transbuttons.add(help);
1338 transPanel.add(transparency);
1339 transPanel.add(transbuttons);
1341 JPanel buttonPanel = new JPanel();
1342 buttonPanel.add(ok);
1343 buttonPanel.add(cancel);
1344 buttonPanel.add(loadColours);
1345 buttonPanel.add(saveColours);
1346 bigPanel.add(scrollPane, BorderLayout.CENTER);
1347 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1348 dasButtonPanel.add(fetchDAS);
1349 dasButtonPanel.add(cancelDAS);
1350 dasButtonPanel.add(saveDAS);
1351 settingsPane.add(bigPanel, BorderLayout.CENTER);
1352 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1353 this.add(settingsPane);
1356 public void fetchDAS_actionPerformed(ActionEvent e)
1358 fetchDAS.setEnabled(false);
1359 cancelDAS.setEnabled(true);
1360 dassourceBrowser.setGuiEnabled(false);
1361 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1362 .getSelectedSources();
1363 doDasFeatureFetch(selectedSources, true, true);
1367 * get the features from selectedSources for all or the current selection
1369 * @param selectedSources
1370 * @param checkDbRefs
1371 * @param promptFetchDbRefs
1373 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1374 boolean checkDbRefs, boolean promptFetchDbRefs)
1376 SequenceI[] dataset, seqs;
1378 AlignmentViewport vp = af.getViewport();
1379 if (vp.getSelectionGroup() != null
1380 && vp.getSelectionGroup().getSize() > 0)
1382 iSize = vp.getSelectionGroup().getSize();
1383 dataset = new SequenceI[iSize];
1384 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1388 iSize = vp.getAlignment().getHeight();
1389 seqs = vp.getAlignment().getSequencesArray();
1392 dataset = new SequenceI[iSize];
1393 for (int i = 0; i < iSize; i++)
1395 dataset[i] = seqs[i].getDatasetSequence();
1398 cancelDAS.setEnabled(true);
1399 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1400 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1401 af.getViewport().setShowSequenceFeatures(true);
1402 af.showSeqFeatures.setSelected(true);
1406 * blocking call to initialise the das source browser
1408 public void initDasSources()
1410 dassourceBrowser.initDasSources();
1414 * examine the current list of das sources and return any matching the given
1415 * nicknames in sources
1418 * Vector of Strings to resolve to DAS source nicknames.
1419 * @return sources that are present in source list.
1421 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1423 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1427 * get currently selected das sources. ensure you have called initDasSources
1428 * before calling this.
1430 * @return vector of selected das source nicknames
1432 public Vector<jalviewSourceI> getSelectedSources()
1434 return dassourceBrowser.getSelectedSources();
1438 * properly initialise DAS fetcher and then initiate a new thread to fetch
1439 * features from the named sources (rather than any turned on by default)
1443 * if true then runs in same thread, otherwise passes to the Swing
1446 public void fetchDasFeatures(Vector<String> sources, boolean block)
1449 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1450 .resolveSourceNicknames(sources);
1451 if (resolved.size() == 0)
1453 resolved = dassourceBrowser.getSelectedSources();
1455 if (resolved.size() > 0)
1457 final List<jalviewSourceI> dassources = resolved;
1458 fetchDAS.setEnabled(false);
1459 // cancelDAS.setEnabled(true); doDasFetch does this.
1460 Runnable fetcher = new Runnable()
1466 doDasFeatureFetch(dassources, true, false);
1476 SwingUtilities.invokeLater(fetcher);
1481 public void saveDAS_actionPerformed(ActionEvent e)
1484 .saveProperties(jalview.bin.Cache.applicationProperties);
1487 public void complete()
1489 fetchDAS.setEnabled(true);
1490 cancelDAS.setEnabled(false);
1491 dassourceBrowser.setGuiEnabled(true);
1495 public void cancelDAS_actionPerformed(ActionEvent e)
1497 if (dasFeatureFetcher != null)
1499 dasFeatureFetcher.cancel();
1504 public void noDasSourceActive()
1507 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1508 MessageManager.getString("label.no_das_sources_selected_warn"),
1509 MessageManager.getString("label.no_das_sources_selected_title"),
1510 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1513 // ///////////////////////////////////////////////////////////////////////
1514 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1515 // ///////////////////////////////////////////////////////////////////////
1516 class FeatureTableModel extends AbstractTableModel
1518 private String[] columnNames = {
1519 MessageManager.getString("label.feature_type"),
1520 MessageManager.getString("action.colour"),
1521 MessageManager.getString("label.filter"),
1522 MessageManager.getString("label.show") };
1524 private Object[][] data;
1526 FeatureTableModel(Object[][] data)
1531 public Object[][] getData()
1536 public void setData(Object[][] data)
1542 public int getColumnCount()
1544 return columnNames.length;
1547 public Object[] getRow(int row)
1553 public int getRowCount()
1559 public String getColumnName(int col)
1561 return columnNames[col];
1565 public Object getValueAt(int row, int col)
1567 return data[row][col];
1571 * Answers the class of the object in column c of the first row of the table
1574 public Class<?> getColumnClass(int c)
1576 Object v = getValueAt(0, c);
1577 return v == null ? null : v.getClass();
1581 public boolean isCellEditable(int row, int col)
1583 return col == 0 ? false : true;
1587 public void setValueAt(Object value, int row, int col)
1589 data[row][col] = value;
1590 fireTableCellUpdated(row, col);
1591 updateFeatureRenderer(data);
1596 class ColorRenderer extends JLabel implements TableCellRenderer
1598 javax.swing.border.Border unselectedBorder = null;
1600 javax.swing.border.Border selectedBorder = null;
1602 final String baseTT = "Click to edit, right/apple click for menu.";
1604 public ColorRenderer()
1606 setOpaque(true); // MUST do this for background to show up.
1607 setHorizontalTextPosition(SwingConstants.CENTER);
1608 setVerticalTextPosition(SwingConstants.CENTER);
1612 public Component getTableCellRendererComponent(JTable tbl, Object color,
1613 boolean isSelected, boolean hasFocus, int row, int column)
1615 FeatureColourI cellColour = (FeatureColourI) color;
1617 setToolTipText(baseTT);
1618 setBackground(tbl.getBackground());
1619 if (!cellColour.isSimpleColour())
1621 Rectangle cr = tbl.getCellRect(row, column, false);
1622 FeatureSettings.renderGraduatedColor(this, cellColour,
1623 (int) cr.getWidth(), (int) cr.getHeight());
1629 setBackground(cellColour.getColour());
1633 if (selectedBorder == null)
1635 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1636 tbl.getSelectionBackground());
1638 setBorder(selectedBorder);
1642 if (unselectedBorder == null)
1644 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1645 tbl.getBackground());
1647 setBorder(unselectedBorder);
1654 class FilterRenderer extends JLabel implements TableCellRenderer
1656 javax.swing.border.Border unselectedBorder = null;
1658 javax.swing.border.Border selectedBorder = null;
1660 public FilterRenderer()
1662 setOpaque(true); // MUST do this for background to show up.
1663 setHorizontalTextPosition(SwingConstants.CENTER);
1664 setVerticalTextPosition(SwingConstants.CENTER);
1668 public Component getTableCellRendererComponent(JTable tbl,
1669 Object filter, boolean isSelected, boolean hasFocus, int row,
1672 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1674 String asText = theFilter.toString();
1675 setBackground(tbl.getBackground());
1676 this.setText(asText);
1681 if (selectedBorder == null)
1683 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1684 tbl.getSelectionBackground());
1686 setBorder(selectedBorder);
1690 if (unselectedBorder == null)
1692 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1693 tbl.getBackground());
1695 setBorder(unselectedBorder);
1703 * update comp using rendering settings from gcol
1708 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1710 int w = comp.getWidth(), h = comp.getHeight();
1713 w = (int) comp.getPreferredSize().getWidth();
1714 h = (int) comp.getPreferredSize().getHeight();
1721 renderGraduatedColor(comp, gcol, w, h);
1724 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1727 boolean thr = false;
1728 StringBuilder tt = new StringBuilder();
1729 StringBuilder tx = new StringBuilder();
1731 if (gcol.isColourByAttribute())
1733 tx.append(String.join(":", gcol.getAttributeName()));
1735 else if (!gcol.isColourByLabel())
1737 tx.append(MessageManager.getString("label.score"));
1740 if (gcol.isAboveThreshold())
1744 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1747 if (gcol.isBelowThreshold())
1751 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1754 if (gcol.isColourByLabel())
1756 tt.append("Coloured by label text. ").append(tt);
1761 if (!gcol.isColourByAttribute())
1769 Color newColor = gcol.getMaxColour();
1770 comp.setBackground(newColor);
1771 // System.err.println("Width is " + w / 2);
1772 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1773 comp.setIcon(ficon);
1774 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1775 // + newColor.getGreen() + ", " + newColor.getBlue()
1776 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1777 // + ", " + minCol.getBlue() + ")");
1779 comp.setHorizontalAlignment(SwingConstants.CENTER);
1780 comp.setText(tx.toString());
1781 if (tt.length() > 0)
1783 if (comp.getToolTipText() == null)
1785 comp.setToolTipText(tt.toString());
1789 comp.setToolTipText(
1790 tt.append(" ").append(comp.getToolTipText()).toString());
1795 class ColorEditor extends AbstractCellEditor
1796 implements TableCellEditor, ActionListener
1800 FeatureColourI currentColor;
1802 FeatureTypeSettings chooser;
1808 JColorChooser colorChooser;
1812 protected static final String EDIT = "edit";
1814 int rowSelected = 0;
1816 public ColorEditor(FeatureSettings me)
1819 // Set up the editor (from the table's point of view),
1820 // which is a button.
1821 // This button brings up the color chooser dialog,
1822 // which is the editor from the user's point of view.
1823 button = new JButton();
1824 button.setActionCommand(EDIT);
1825 button.addActionListener(this);
1826 button.setBorderPainted(false);
1827 // Set up the dialog that the button brings up.
1828 colorChooser = new JColorChooser();
1829 dialog = JColorChooser.createDialog(button,
1830 MessageManager.getString("label.select_colour"), true, // modal
1831 colorChooser, this, // OK button handler
1832 null); // no CANCEL button handler
1836 * Handles events from the editor button and from the dialog's OK button.
1839 public void actionPerformed(ActionEvent e)
1841 // todo test e.getSource() instead here
1842 if (EDIT.equals(e.getActionCommand()))
1844 // The user has clicked the cell, so
1845 // bring up the dialog.
1846 if (currentColor.isSimpleColour())
1848 // bring up simple color chooser
1849 button.setBackground(currentColor.getColour());
1850 colorChooser.setColor(currentColor.getColour());
1851 dialog.setVisible(true);
1855 // bring up graduated chooser.
1856 chooser = new FeatureTypeSettings(me.fr, type);
1857 chooser.setRequestFocusEnabled(true);
1858 chooser.requestFocus();
1859 chooser.addActionListener(this);
1860 chooser.showTab(true);
1862 // Make the renderer reappear.
1863 fireEditingStopped();
1868 if (currentColor.isSimpleColour())
1871 * read off colour picked in colour chooser after OK pressed
1873 currentColor = new FeatureColour(colorChooser.getColor());
1874 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1879 * after OK in variable colour dialog, any changes to colour
1880 * (or filters!) are already set in FeatureRenderer, so just
1881 * update table data without triggering updateFeatureRenderer
1883 currentColor = fr.getFeatureColours().get(type);
1884 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1885 if (currentFilter == null)
1887 currentFilter = new FeatureMatcherSet();
1889 Object[] data = ((FeatureTableModel) table.getModel())
1890 .getData()[rowSelected];
1891 data[COLOUR_COLUMN] = currentColor;
1892 data[FILTER_COLUMN] = currentFilter;
1894 fireEditingStopped();
1895 me.table.validate();
1899 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1901 public Object getCellEditorValue()
1903 return currentColor;
1906 // Implement the one method defined by TableCellEditor.
1908 public Component getTableCellEditorComponent(JTable theTable, Object value,
1909 boolean isSelected, int row, int column)
1911 currentColor = (FeatureColourI) value;
1912 this.rowSelected = row;
1913 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1914 button.setOpaque(true);
1915 button.setBackground(me.getBackground());
1916 if (!currentColor.isSimpleColour())
1918 JLabel btn = new JLabel();
1919 btn.setSize(button.getSize());
1920 FeatureSettings.renderGraduatedColor(btn, currentColor);
1921 button.setBackground(btn.getBackground());
1922 button.setIcon(btn.getIcon());
1923 button.setText(btn.getText());
1928 button.setIcon(null);
1929 button.setBackground(currentColor.getColour());
1936 * The cell editor for the Filter column. It displays the text of any filters
1937 * for the feature type in that row (in full as a tooltip, possible abbreviated
1938 * as display text). On click in the cell, opens the Feature Display Settings
1939 * dialog at the Filters tab.
1941 class FilterEditor extends AbstractCellEditor
1942 implements TableCellEditor, ActionListener
1946 FeatureMatcherSetI currentFilter;
1954 protected static final String EDIT = "edit";
1956 int rowSelected = 0;
1958 public FilterEditor(FeatureSettings me)
1961 button = new JButton();
1962 button.setActionCommand(EDIT);
1963 button.addActionListener(this);
1964 button.setBorderPainted(false);
1968 * Handles events from the editor button
1971 public void actionPerformed(ActionEvent e)
1973 if (button == e.getSource())
1975 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1976 chooser.addActionListener(this);
1977 chooser.setRequestFocusEnabled(true);
1978 chooser.requestFocus();
1979 if (lastLocation != null)
1981 // todo open at its last position on screen
1982 chooser.setBounds(lastLocation.x, lastLocation.y,
1983 chooser.getWidth(), chooser.getHeight());
1986 chooser.showTab(false);
1987 fireEditingStopped();
1989 else if (e.getSource() instanceof Component)
1993 * after OK in variable colour dialog, any changes to filter
1994 * (or colours!) are already set in FeatureRenderer, so just
1995 * update table data without triggering updateFeatureRenderer
1997 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1998 currentFilter = me.fr.getFeatureFilter(type);
1999 if (currentFilter == null)
2001 currentFilter = new FeatureMatcherSet();
2003 Object[] data = ((FeatureTableModel) table.getModel())
2004 .getData()[rowSelected];
2005 data[COLOUR_COLUMN] = currentColor;
2006 data[FILTER_COLUMN] = currentFilter;
2007 fireEditingStopped();
2008 me.table.validate();
2013 public Object getCellEditorValue()
2015 return currentFilter;
2019 public Component getTableCellEditorComponent(JTable theTable, Object value,
2020 boolean isSelected, int row, int column)
2022 currentFilter = (FeatureMatcherSetI) value;
2023 this.rowSelected = row;
2024 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2025 button.setOpaque(true);
2026 button.setBackground(me.getBackground());
2027 button.setText(currentFilter.toString());
2028 button.setToolTipText(currentFilter.toString());
2029 button.setIcon(null);
2035 class FeatureIcon implements Icon
2037 FeatureColourI gcol;
2041 boolean midspace = false;
2043 int width = 50, height = 20;
2045 int s1, e1; // start and end of midpoint band for thresholded symbol
2047 Color mpcolour = Color.white;
2049 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2069 public int getIconWidth()
2075 public int getIconHeight()
2081 public void paintIcon(Component c, Graphics g, int x, int y)
2084 if (gcol.isColourByLabel())
2087 g.fillRect(0, 0, width, height);
2088 // need an icon here.
2089 g.setColor(gcol.getMaxColour());
2091 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2093 // g.setFont(g.getFont().deriveFont(
2094 // AffineTransform.getScaleInstance(
2095 // width/g.getFontMetrics().stringWidth("Label"),
2096 // height/g.getFontMetrics().getHeight())));
2098 g.drawString(MessageManager.getString("label.label"), 0, 0);
2103 Color minCol = gcol.getMinColour();
2105 g.fillRect(0, 0, s1, height);
2108 g.setColor(Color.white);
2109 g.fillRect(s1, 0, e1 - s1, height);
2111 g.setColor(gcol.getMaxColour());
2112 g.fillRect(0, e1, width - e1, height);