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 static jalview.viewmodel.seqfeatures.FeatureRendererModel.COLOUR_COLUMN;
24 import static jalview.viewmodel.seqfeatures.FeatureRendererModel.FILTER_COLUMN;
25 import static jalview.viewmodel.seqfeatures.FeatureRendererModel.SHOW_COLUMN;
26 import static jalview.viewmodel.seqfeatures.FeatureRendererModel.TYPE_COLUMN;
28 import jalview.api.FeatureColourI;
29 import jalview.api.FeatureSettingsControllerI;
30 import jalview.bin.Cache;
31 import jalview.datamodel.AlignmentI;
32 import jalview.datamodel.SequenceI;
33 import jalview.gui.Help.HelpId;
34 import jalview.io.JalviewFileChooser;
35 import jalview.io.JalviewFileView;
36 import jalview.schemabinding.version2.JalviewUserColours;
37 import jalview.schemes.FeatureColour;
38 import jalview.util.Format;
39 import jalview.util.MessageManager;
40 import jalview.util.Platform;
41 import jalview.util.QuickSort;
42 import jalview.util.matcher.KeyedMatcherSet;
43 import jalview.util.matcher.KeyedMatcherSetI;
44 import jalview.viewmodel.AlignmentViewport;
45 import jalview.ws.DasSequenceFeatureFetcher;
46 import jalview.ws.dbsources.das.api.jalviewSourceI;
48 import java.awt.BorderLayout;
49 import java.awt.Color;
50 import java.awt.Component;
51 import java.awt.Dimension;
53 import java.awt.Graphics;
54 import java.awt.GridLayout;
55 import java.awt.Point;
56 import java.awt.Rectangle;
57 import java.awt.event.ActionEvent;
58 import java.awt.event.ActionListener;
59 import java.awt.event.ItemEvent;
60 import java.awt.event.ItemListener;
61 import java.awt.event.MouseAdapter;
62 import java.awt.event.MouseEvent;
63 import java.awt.event.MouseMotionAdapter;
64 import java.beans.PropertyChangeEvent;
65 import java.beans.PropertyChangeListener;
67 import java.io.FileInputStream;
68 import java.io.FileOutputStream;
69 import java.io.InputStreamReader;
70 import java.io.OutputStreamWriter;
71 import java.io.PrintWriter;
72 import java.util.Arrays;
73 import java.util.HashSet;
74 import java.util.Hashtable;
75 import java.util.Iterator;
76 import java.util.List;
79 import java.util.Vector;
81 import javax.help.HelpSetException;
82 import javax.swing.AbstractCellEditor;
83 import javax.swing.BorderFactory;
84 import javax.swing.Icon;
85 import javax.swing.JButton;
86 import javax.swing.JCheckBox;
87 import javax.swing.JCheckBoxMenuItem;
88 import javax.swing.JColorChooser;
89 import javax.swing.JDialog;
90 import javax.swing.JInternalFrame;
91 import javax.swing.JLabel;
92 import javax.swing.JLayeredPane;
93 import javax.swing.JMenuItem;
94 import javax.swing.JPanel;
95 import javax.swing.JPopupMenu;
96 import javax.swing.JScrollPane;
97 import javax.swing.JSlider;
98 import javax.swing.JTable;
99 import javax.swing.ListSelectionModel;
100 import javax.swing.SwingConstants;
101 import javax.swing.SwingUtilities;
102 import javax.swing.event.ChangeEvent;
103 import javax.swing.event.ChangeListener;
104 import javax.swing.table.AbstractTableModel;
105 import javax.swing.table.TableCellEditor;
106 import javax.swing.table.TableCellRenderer;
107 import javax.swing.table.TableColumn;
109 public class FeatureSettings extends JPanel
110 implements FeatureSettingsControllerI
112 private static final int COLUMN_COUNT = 4;
114 private static final String COLON = ":";
116 private static final int MIN_WIDTH = 400;
118 private static final int MIN_HEIGHT = 400;
120 private static final int MAX_TOOLTIP_LENGTH = 50;
122 DasSourceBrowser dassourceBrowser;
124 DasSequenceFeatureFetcher dasFeatureFetcher;
126 JPanel dasSettingsPane = new JPanel();
128 final FeatureRenderer fr;
130 public final AlignFrame af;
133 * 'original' fields hold settings to restore on Cancel
135 Object[][] originalData;
137 private float originalTransparency;
139 private Map<String, KeyedMatcherSetI> originalFilters;
141 final JInternalFrame frame;
143 JScrollPane scrollPane = new JScrollPane();
149 JSlider transparency = new JSlider();
152 * when true, constructor is still executing - so ignore UI events
154 protected volatile boolean inConstruction = true;
156 int selectedRow = -1;
158 JButton fetchDAS = new JButton();
160 JButton saveDAS = new JButton();
162 JButton cancelDAS = new JButton();
164 boolean resettingTable = false;
167 * true when Feature Settings are updating from feature renderer
169 private boolean handlingUpdate = false;
172 * holds {featureCount, totalExtent} for each feature type
174 Map<String, float[]> typeWidth = null;
181 public FeatureSettings(AlignFrame alignFrame)
183 this.af = alignFrame;
184 fr = af.getFeatureRenderer();
186 // save transparency for restore on Cancel
187 originalTransparency = fr.getTransparency();
188 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
189 transparency.setMaximum(100 - originalTransparencyAsPercent);
191 originalFilters = fr.getFeatureFilters();
196 } catch (Exception ex)
198 ex.printStackTrace();
204 public String getToolTipText(MouseEvent e)
207 int column = table.columnAtPoint(e.getPoint());
211 tip = JvSwingUtils.wrapTooltip(true, MessageManager
212 .getString("label.feature_settings_click_drag"));
215 int row = table.rowAtPoint(e.getPoint());
216 KeyedMatcherSet o = (KeyedMatcherSet) table.getValueAt(row,
219 ? MessageManager.getString("label.filters_tooltip")
228 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
229 table.setFont(new Font("Verdana", Font.PLAIN, 12));
231 // table.setDefaultRenderer(Color.class, new ColorRenderer());
232 // table.setDefaultEditor(Color.class, new ColorEditor(this));
234 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
235 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
237 table.setDefaultEditor(KeyedMatcherSet.class, new FilterEditor(this));
238 table.setDefaultRenderer(KeyedMatcherSet.class, new FilterRenderer());
240 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
241 new ColorRenderer(), new ColorEditor(this));
242 table.addColumn(colourColumn);
244 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
245 new FilterRenderer(), new FilterEditor(this));
246 table.addColumn(filterColumn);
248 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
250 table.addMouseListener(new MouseAdapter()
253 public void mousePressed(MouseEvent evt)
255 selectedRow = table.rowAtPoint(evt.getPoint());
256 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
257 if (evt.isPopupTrigger())
259 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
260 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
263 else if (evt.getClickCount() == 2)
265 boolean invertSelection = evt.isAltDown();
266 boolean toggleSelection = Platform.isControlDown(evt);
267 boolean extendSelection = evt.isShiftDown();
268 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
269 invertSelection, extendSelection, toggleSelection, type);
273 // isPopupTrigger fires on mouseReleased on Windows
275 public void mouseReleased(MouseEvent evt)
277 selectedRow = table.rowAtPoint(evt.getPoint());
278 if (evt.isPopupTrigger())
280 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
281 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
282 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
288 table.addMouseMotionListener(new MouseMotionAdapter()
291 public void mouseDragged(MouseEvent evt)
293 int newRow = table.rowAtPoint(evt.getPoint());
294 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
297 * reposition 'selectedRow' to 'newRow' (the dragged to location)
298 * this could be more than one row away for a very fast drag action
299 * so just swap it with adjacent rows until we get it there
301 Object[][] data = ((FeatureTableModel) table.getModel())
303 int direction = newRow < selectedRow ? -1 : 1;
304 for (int i = selectedRow; i != newRow; i += direction)
306 Object[] temp = data[i];
307 data[i] = data[i + direction];
308 data[i + direction] = temp;
310 updateFeatureRenderer(data);
312 selectedRow = newRow;
316 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
317 // MessageManager.getString("label.feature_settings_click_drag")));
318 scrollPane.setViewportView(table);
320 dassourceBrowser = new DasSourceBrowser(this);
321 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
323 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
325 fr.findAllFeatures(true); // display everything!
328 discoverAllFeatureData();
329 final PropertyChangeListener change;
330 final FeatureSettings fs = this;
331 fr.addPropertyChangeListener(change = new PropertyChangeListener()
334 public void propertyChange(PropertyChangeEvent evt)
336 if (!fs.resettingTable && !fs.handlingUpdate)
338 fs.handlingUpdate = true;
340 // new groups may be added with new sequence feature types only
341 fs.handlingUpdate = false;
347 frame = new JInternalFrame();
348 frame.setContentPane(this);
349 if (Platform.isAMac())
351 Desktop.addInternalFrame(frame,
352 MessageManager.getString("label.sequence_feature_settings"),
357 Desktop.addInternalFrame(frame,
358 MessageManager.getString("label.sequence_feature_settings"),
361 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
363 frame.addInternalFrameListener(
364 new javax.swing.event.InternalFrameAdapter()
367 public void internalFrameClosed(
368 javax.swing.event.InternalFrameEvent evt)
370 fr.removePropertyChangeListener(change);
371 dassourceBrowser.fs = null;
374 frame.setLayer(JLayeredPane.PALETTE_LAYER);
375 inConstruction = false;
378 protected void popupSort(final int rowSelected, final String type,
379 final Object typeCol, final Map<String, float[][]> minmax, int x,
382 final FeatureColourI featureColour = (FeatureColourI) typeCol;
384 JPopupMenu men = new JPopupMenu(MessageManager
385 .formatMessage("label.settings_for_param", new String[]
387 JMenuItem scr = new JMenuItem(
388 MessageManager.getString("label.sort_by_score"));
390 final FeatureSettings me = this;
391 scr.addActionListener(new ActionListener()
395 public void actionPerformed(ActionEvent e)
398 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
403 JMenuItem dens = new JMenuItem(
404 MessageManager.getString("label.sort_by_density"));
405 dens.addActionListener(new ActionListener()
409 public void actionPerformed(ActionEvent e)
412 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
420 * variable colour options include colour by label, by score,
421 * by selected attribute text, or attribute value
423 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
424 MessageManager.getString("label.variable_colour"));
425 mxcol.setSelected(!featureColour.isSimpleColour());
427 mxcol.addActionListener(new ActionListener()
429 JColorChooser colorChooser;
432 public void actionPerformed(ActionEvent e)
434 if (e.getSource() == mxcol)
436 if (featureColour.isSimpleColour())
438 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
439 fc.addActionListener(this);
443 // bring up simple color chooser
444 colorChooser = new JColorChooser();
445 String title = MessageManager
446 .getString("label.select_colour");
447 JDialog dialog = JColorChooser.createDialog(me,
448 title, true, // modal
449 colorChooser, this, // OK button handler
450 null); // no CANCEL button handler
451 colorChooser.setColor(featureColour.getMaxColour());
452 dialog.setVisible(true);
457 if (e.getSource() instanceof FeatureTypeSettings)
460 * update after OK in feature colour dialog; the updated
461 * colour will have already been set in the FeatureRenderer
463 FeatureColourI fci = fr.getFeatureColours().get(type);
464 table.setValueAt(fci, rowSelected, 1);
469 // probably the color chooser!
470 table.setValueAt(new FeatureColour(colorChooser.getColor()),
473 me.updateFeatureRenderer(
474 ((FeatureTableModel) table.getModel()).getData(),
482 JMenuItem selCols = new JMenuItem(
483 MessageManager.getString("label.select_columns_containing"));
484 selCols.addActionListener(new ActionListener()
487 public void actionPerformed(ActionEvent arg0)
489 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
493 JMenuItem clearCols = new JMenuItem(MessageManager
494 .getString("label.select_columns_not_containing"));
495 clearCols.addActionListener(new ActionListener()
498 public void actionPerformed(ActionEvent arg0)
500 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
504 JMenuItem hideCols = new JMenuItem(
505 MessageManager.getString("label.hide_columns_containing"));
506 hideCols.addActionListener(new ActionListener()
509 public void actionPerformed(ActionEvent arg0)
511 fr.ap.alignFrame.hideFeatureColumns(type, true);
514 JMenuItem hideOtherCols = new JMenuItem(
515 MessageManager.getString("label.hide_columns_not_containing"));
516 hideOtherCols.addActionListener(new ActionListener()
519 public void actionPerformed(ActionEvent arg0)
521 fr.ap.alignFrame.hideFeatureColumns(type, false);
527 men.add(hideOtherCols);
528 men.show(table, x, y);
532 synchronized public void discoverAllFeatureData()
534 Set<String> allGroups = new HashSet<>();
535 AlignmentI alignment = af.getViewport().getAlignment();
537 for (int i = 0; i < alignment.getHeight(); i++)
539 SequenceI seq = alignment.getSequenceAt(i);
540 for (String group : seq.getFeatures().getFeatureGroups(true))
542 if (group != null && !allGroups.contains(group))
544 allGroups.add(group);
545 checkGroupState(group);
556 * Synchronise gui group list and check visibility of group
559 * @return true if group is visible
561 private boolean checkGroupState(String group)
563 boolean visible = fr.checkGroupVisibility(group, true);
565 for (int g = 0; g < groupPanel.getComponentCount(); g++)
567 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
569 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
574 final String grp = group;
575 final JCheckBox check = new JCheckBox(group, visible);
576 check.setFont(new Font("Serif", Font.BOLD, 12));
577 check.setToolTipText(group);
578 check.addItemListener(new ItemListener()
581 public void itemStateChanged(ItemEvent evt)
583 fr.setGroupVisibility(check.getText(), check.isSelected());
584 resetTable(new String[] { grp });
585 af.alignPanel.paintAlignment(true, true);
588 groupPanel.add(check);
592 synchronized void resetTable(String[] groupChanged)
598 resettingTable = true;
599 typeWidth = new Hashtable<>();
600 // TODO: change avWidth calculation to 'per-sequence' average and use long
603 Set<String> displayableTypes = new HashSet<>();
604 Set<String> foundGroups = new HashSet<>();
607 * determine which feature types may be visible depending on
608 * which groups are selected, and recompute average width data
610 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
613 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
616 * get the sequence's groups for positional features
617 * and keep track of which groups are visible
619 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
620 Set<String> visibleGroups = new HashSet<>();
621 for (String group : groups)
623 if (group == null || checkGroupState(group))
625 visibleGroups.add(group);
628 foundGroups.addAll(groups);
631 * get distinct feature types for visible groups
632 * record distinct visible types, and their count and total length
634 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
635 visibleGroups.toArray(new String[visibleGroups.size()]));
636 for (String type : types)
638 displayableTypes.add(type);
639 float[] avWidth = typeWidth.get(type);
642 avWidth = new float[2];
643 typeWidth.put(type, avWidth);
645 // todo this could include features with a non-visible group
646 // - do we greatly care?
647 // todo should we include non-displayable features here, and only
648 // update when features are added?
649 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
650 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
654 int columnCount = COLUMN_COUNT;
655 Object[][] data = new Object[displayableTypes.size()][columnCount];
658 if (fr.hasRenderOrder())
662 fr.findAllFeatures(groupChanged != null); // prod to update
663 // colourschemes. but don't
665 // First add the checks in the previous render order,
666 // in case the window has been closed and reopened
668 List<String> frl = fr.getRenderOrder();
669 for (int ro = frl.size() - 1; ro > -1; ro--)
671 String type = frl.get(ro);
673 if (!displayableTypes.contains(type))
678 data[dataIndex][TYPE_COLUMN] = type;
679 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
680 KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
681 data[dataIndex][FILTER_COLUMN] = featureFilter == null
682 ? new KeyedMatcherSet()
684 data[dataIndex][SHOW_COLUMN] = new Boolean(
685 af.getViewport().getFeaturesDisplayed().isVisible(type));
687 displayableTypes.remove(type);
692 * process any extra features belonging only to
693 * a group which was just selected
695 while (!displayableTypes.isEmpty())
697 String type = displayableTypes.iterator().next();
698 data[dataIndex][TYPE_COLUMN] = type;
700 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
701 if (data[dataIndex][COLOUR_COLUMN] == null)
703 // "Colour has been updated in another view!!"
704 fr.clearRenderOrder();
707 KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
708 data[dataIndex][FILTER_COLUMN] = featureFilter == null
709 ? new KeyedMatcherSet()
711 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
713 displayableTypes.remove(type);
716 if (originalData == null)
718 int size = data[0].length;
719 originalData = new Object[data.length][size];
720 for (int i = 0; i < data.length; i++)
722 System.arraycopy(data[i], 0, originalData[i], 0, size);
727 updateOriginalData(data);
730 table.setModel(new FeatureTableModel(data));
731 table.getColumnModel().getColumn(0).setPreferredWidth(200);
733 groupPanel.setLayout(
734 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
735 pruneGroups(foundGroups);
736 groupPanel.validate();
738 updateFeatureRenderer(data, groupChanged != null);
739 resettingTable = false;
743 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
744 * have been made outwith this dialog
746 * <li>a new feature type added (and made visible)</li>
747 * <li>a feature colour changed (in the Amend Features dialog)</li>
752 protected void updateOriginalData(Object[][] foundData)
754 // todo LinkedHashMap instead of Object[][] would be nice
756 Object[][] currentData = ((FeatureTableModel) table.getModel())
758 for (Object[] row : foundData)
760 String type = (String) row[TYPE_COLUMN];
761 boolean found = false;
762 for (Object[] current : currentData)
764 if (type.equals(current[TYPE_COLUMN]))
768 * currently dependent on object equality here;
769 * really need an equals method on FeatureColour
771 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
774 * feature colour has changed externally - update originalData
776 for (Object[] original : originalData)
778 if (type.equals(original[TYPE_COLUMN]))
780 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
791 * new feature detected - add to original data (on top)
793 int size = currentData[0].length;
794 Object[][] newData = new Object[originalData.length + 1][size];
795 for (int i = 0; i < originalData.length; i++)
797 System.arraycopy(originalData[i], 0, newData[i + 1], 0, size);
800 originalData = newData;
806 * Remove from the groups panel any checkboxes for groups that are not in the
807 * foundGroups set. This enables removing a group from the display when the last
808 * feature in that group is deleted.
812 protected void pruneGroups(Set<String> foundGroups)
814 for (int g = 0; g < groupPanel.getComponentCount(); g++)
816 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
817 if (!foundGroups.contains(checkbox.getText()))
819 groupPanel.remove(checkbox);
825 * reorder data based on the featureRenderers global priority list.
829 private void ensureOrder(Object[][] data)
831 boolean sort = false;
832 float[] order = new float[data.length];
833 for (int i = 0; i < order.length; i++)
835 order[i] = fr.getOrder(data[i][0].toString());
838 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
842 sort = sort || order[i - 1] > order[i];
847 jalview.util.QuickSort.sort(order, data);
853 JalviewFileChooser chooser = new JalviewFileChooser("fc",
854 "Sequence Feature Colours");
855 chooser.setFileView(new JalviewFileView());
856 chooser.setDialogTitle(
857 MessageManager.getString("label.load_feature_colours"));
858 chooser.setToolTipText(MessageManager.getString("action.load"));
860 int value = chooser.showOpenDialog(this);
862 if (value == JalviewFileChooser.APPROVE_OPTION)
864 File file = chooser.getSelectedFile();
868 InputStreamReader in = new InputStreamReader(
869 new FileInputStream(file), "UTF-8");
871 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
873 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
876 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
879 Color mincol = null, maxcol = null;
882 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
883 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
885 } catch (Exception e)
887 Cache.log.warn("Couldn't parse out graduated feature color.",
890 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
891 newcol.getMin(), newcol.getMax());
892 if (newcol.hasAutoScale())
894 gcol.setAutoScaled(newcol.getAutoScale());
896 if (newcol.hasColourByLabel())
898 gcol.setColourByLabel(newcol.getColourByLabel());
900 if (newcol.hasThreshold())
902 gcol.setThreshold(newcol.getThreshold());
904 if (newcol.getThreshType().length() > 0)
906 String ttyp = newcol.getThreshType();
907 if (ttyp.equalsIgnoreCase("ABOVE"))
909 gcol.setAboveThreshold(true);
911 if (ttyp.equalsIgnoreCase("BELOW"))
913 gcol.setBelowThreshold(true);
916 fr.setColour(name = newcol.getName(), gcol);
920 Color color = new Color(
921 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
922 fr.setColour(name = jucs.getColour(i).getName(),
923 new FeatureColour(color));
925 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
930 Object[][] data = ((FeatureTableModel) table.getModel())
933 updateFeatureRenderer(data, false);
936 } catch (Exception ex)
938 System.out.println("Error loading User Colour File\n" + ex);
945 JalviewFileChooser chooser = new JalviewFileChooser("fc",
946 "Sequence Feature Colours");
947 chooser.setFileView(new JalviewFileView());
948 chooser.setDialogTitle(
949 MessageManager.getString("label.save_feature_colours"));
950 chooser.setToolTipText(MessageManager.getString("action.save"));
952 int value = chooser.showSaveDialog(this);
954 if (value == JalviewFileChooser.APPROVE_OPTION)
956 String choice = chooser.getSelectedFile().getPath();
957 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
958 ucs.setSchemeName("Sequence Features");
961 PrintWriter out = new PrintWriter(new OutputStreamWriter(
962 new FileOutputStream(choice), "UTF-8"));
964 Set<String> fr_colours = fr.getAllFeatureColours();
965 Iterator<String> e = fr_colours.iterator();
966 float[] sortOrder = new float[fr_colours.size()];
967 String[] sortTypes = new String[fr_colours.size()];
971 sortTypes[i] = e.next();
972 sortOrder[i] = fr.getOrder(sortTypes[i]);
975 QuickSort.sort(sortOrder, sortTypes);
977 for (i = 0; i < sortTypes.length; i++)
979 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
980 col.setName(sortTypes[i]);
981 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
982 if (fcol.isSimpleColour())
984 col.setRGB(Format.getHexString(fcol.getColour()));
988 col.setRGB(Format.getHexString(fcol.getMaxColour()));
989 col.setMin(fcol.getMin());
990 col.setMax(fcol.getMax());
992 jalview.util.Format.getHexString(fcol.getMinColour()));
993 col.setAutoScale(fcol.isAutoScaled());
994 col.setThreshold(fcol.getThreshold());
995 col.setColourByLabel(fcol.isColourByLabel());
996 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
997 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
1003 } catch (Exception ex)
1005 ex.printStackTrace();
1010 public void invertSelection()
1012 for (int i = 0; i < table.getRowCount(); i++)
1014 Boolean value = (Boolean) table.getValueAt(i, SHOW_COLUMN);
1016 table.setValueAt(new Boolean(!value.booleanValue()), i, SHOW_COLUMN);
1020 public void orderByAvWidth()
1022 if (table == null || table.getModel() == null)
1026 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1027 float[] width = new float[data.length];
1031 for (int i = 0; i < data.length; i++)
1033 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1036 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1037 // weight - but have to make per
1038 // sequence, too (awidth[2])
1039 // if (width[i]==1) // hack to distinguish single width sequences.
1050 boolean sort = false;
1051 for (int i = 0; i < width.length; i++)
1053 // awidth = (float[]) typeWidth.get(data[i][0]);
1056 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1059 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1065 width[i] /= max; // normalize
1066 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1070 sort = sort || width[i - 1] > width[i];
1075 jalview.util.QuickSort.sort(width, data);
1076 // update global priority order
1079 updateFeatureRenderer(data, false);
1087 frame.setClosed(true);
1088 } catch (Exception exe)
1094 public void updateFeatureRenderer(Object[][] data)
1096 updateFeatureRenderer(data, true);
1100 * Update the priority order of features; only repaint if this changed the order
1101 * of visible features
1106 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1108 if (fr.setFeaturePriority(data, visibleNew))
1110 af.alignPanel.paintAlignment(true, true);
1114 private void jbInit() throws Exception
1116 this.setLayout(new BorderLayout());
1118 JPanel settingsPane = new JPanel();
1119 settingsPane.setLayout(new BorderLayout());
1121 dasSettingsPane.setLayout(new BorderLayout());
1123 JPanel bigPanel = new JPanel();
1124 bigPanel.setLayout(new BorderLayout());
1126 groupPanel = new JPanel();
1127 bigPanel.add(groupPanel, BorderLayout.NORTH);
1129 JButton invert = new JButton(
1130 MessageManager.getString("label.invert_selection"));
1131 invert.setFont(JvSwingUtils.getLabelFont());
1132 invert.addActionListener(new ActionListener()
1135 public void actionPerformed(ActionEvent e)
1141 JButton optimizeOrder = new JButton(
1142 MessageManager.getString("label.optimise_order"));
1143 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1144 optimizeOrder.addActionListener(new ActionListener()
1147 public void actionPerformed(ActionEvent e)
1153 JButton sortByScore = new JButton(
1154 MessageManager.getString("label.seq_sort_by_score"));
1155 sortByScore.setFont(JvSwingUtils.getLabelFont());
1156 sortByScore.addActionListener(new ActionListener()
1159 public void actionPerformed(ActionEvent e)
1161 af.avc.sortAlignmentByFeatureScore(null);
1164 JButton sortByDens = new JButton(
1165 MessageManager.getString("label.sequence_sort_by_density"));
1166 sortByDens.setFont(JvSwingUtils.getLabelFont());
1167 sortByDens.addActionListener(new ActionListener()
1170 public void actionPerformed(ActionEvent e)
1172 af.avc.sortAlignmentByFeatureDensity(null);
1176 JButton help = new JButton(MessageManager.getString("action.help"));
1177 help.setFont(JvSwingUtils.getLabelFont());
1178 help.addActionListener(new ActionListener()
1181 public void actionPerformed(ActionEvent e)
1185 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1186 } catch (HelpSetException e1)
1188 e1.printStackTrace();
1192 help.setFont(JvSwingUtils.getLabelFont());
1193 help.setText(MessageManager.getString("action.help"));
1194 help.addActionListener(new ActionListener()
1197 public void actionPerformed(ActionEvent e)
1201 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1202 } catch (HelpSetException e1)
1204 e1.printStackTrace();
1209 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1210 cancel.setFont(JvSwingUtils.getLabelFont());
1211 cancel.addActionListener(new ActionListener()
1214 public void actionPerformed(ActionEvent e)
1216 fr.setTransparency(originalTransparency);
1217 fr.setFeatureFilters(originalFilters);
1218 updateFeatureRenderer(originalData);
1223 JButton ok = new JButton(MessageManager.getString("action.ok"));
1224 ok.setFont(JvSwingUtils.getLabelFont());
1225 ok.addActionListener(new ActionListener()
1228 public void actionPerformed(ActionEvent e)
1234 JButton loadColours = new JButton(
1235 MessageManager.getString("label.load_colours"));
1236 loadColours.setFont(JvSwingUtils.getLabelFont());
1237 loadColours.addActionListener(new ActionListener()
1240 public void actionPerformed(ActionEvent e)
1246 JButton saveColours = new JButton(
1247 MessageManager.getString("label.save_colours"));
1248 saveColours.setFont(JvSwingUtils.getLabelFont());
1249 saveColours.addActionListener(new ActionListener()
1252 public void actionPerformed(ActionEvent e)
1257 transparency.addChangeListener(new ChangeListener()
1260 public void stateChanged(ChangeEvent evt)
1262 if (!inConstruction)
1264 fr.setTransparency((100 - transparency.getValue()) / 100f);
1265 af.alignPanel.paintAlignment(true, true);
1270 transparency.setMaximum(70);
1271 transparency.setToolTipText(
1272 MessageManager.getString("label.transparency_tip"));
1273 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1274 fetchDAS.addActionListener(new ActionListener()
1277 public void actionPerformed(ActionEvent e)
1279 fetchDAS_actionPerformed(e);
1282 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1283 saveDAS.addActionListener(new ActionListener()
1286 public void actionPerformed(ActionEvent e)
1288 saveDAS_actionPerformed(e);
1292 JPanel dasButtonPanel = new JPanel();
1293 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1294 dasSettingsPane.setBorder(null);
1295 cancelDAS.setEnabled(false);
1296 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1297 cancelDAS.addActionListener(new ActionListener()
1300 public void actionPerformed(ActionEvent e)
1302 cancelDAS_actionPerformed(e);
1306 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1307 bigPanel.add(transPanel, BorderLayout.SOUTH);
1309 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1310 transbuttons.add(optimizeOrder);
1311 transbuttons.add(invert);
1312 transbuttons.add(sortByScore);
1313 transbuttons.add(sortByDens);
1314 transbuttons.add(help);
1315 transPanel.add(transparency);
1316 transPanel.add(transbuttons);
1318 JPanel buttonPanel = new JPanel();
1319 buttonPanel.add(ok);
1320 buttonPanel.add(cancel);
1321 buttonPanel.add(loadColours);
1322 buttonPanel.add(saveColours);
1323 bigPanel.add(scrollPane, BorderLayout.CENTER);
1324 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1325 dasButtonPanel.add(fetchDAS);
1326 dasButtonPanel.add(cancelDAS);
1327 dasButtonPanel.add(saveDAS);
1328 settingsPane.add(bigPanel, BorderLayout.CENTER);
1329 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1330 this.add(settingsPane);
1333 public void fetchDAS_actionPerformed(ActionEvent e)
1335 fetchDAS.setEnabled(false);
1336 cancelDAS.setEnabled(true);
1337 dassourceBrowser.setGuiEnabled(false);
1338 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1339 .getSelectedSources();
1340 doDasFeatureFetch(selectedSources, true, true);
1344 * get the features from selectedSources for all or the current selection
1346 * @param selectedSources
1347 * @param checkDbRefs
1348 * @param promptFetchDbRefs
1350 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1351 boolean checkDbRefs, boolean promptFetchDbRefs)
1353 SequenceI[] dataset, seqs;
1355 AlignmentViewport vp = af.getViewport();
1356 if (vp.getSelectionGroup() != null
1357 && vp.getSelectionGroup().getSize() > 0)
1359 iSize = vp.getSelectionGroup().getSize();
1360 dataset = new SequenceI[iSize];
1361 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1365 iSize = vp.getAlignment().getHeight();
1366 seqs = vp.getAlignment().getSequencesArray();
1369 dataset = new SequenceI[iSize];
1370 for (int i = 0; i < iSize; i++)
1372 dataset[i] = seqs[i].getDatasetSequence();
1375 cancelDAS.setEnabled(true);
1376 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1377 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1378 af.getViewport().setShowSequenceFeatures(true);
1379 af.showSeqFeatures.setSelected(true);
1383 * blocking call to initialise the das source browser
1385 public void initDasSources()
1387 dassourceBrowser.initDasSources();
1391 * examine the current list of das sources and return any matching the given
1392 * nicknames in sources
1395 * Vector of Strings to resolve to DAS source nicknames.
1396 * @return sources that are present in source list.
1398 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1400 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1404 * get currently selected das sources. ensure you have called initDasSources
1405 * before calling this.
1407 * @return vector of selected das source nicknames
1409 public Vector<jalviewSourceI> getSelectedSources()
1411 return dassourceBrowser.getSelectedSources();
1415 * properly initialise DAS fetcher and then initiate a new thread to fetch
1416 * features from the named sources (rather than any turned on by default)
1420 * if true then runs in same thread, otherwise passes to the Swing
1423 public void fetchDasFeatures(Vector<String> sources, boolean block)
1426 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1427 .resolveSourceNicknames(sources);
1428 if (resolved.size() == 0)
1430 resolved = dassourceBrowser.getSelectedSources();
1432 if (resolved.size() > 0)
1434 final List<jalviewSourceI> dassources = resolved;
1435 fetchDAS.setEnabled(false);
1436 // cancelDAS.setEnabled(true); doDasFetch does this.
1437 Runnable fetcher = new Runnable()
1443 doDasFeatureFetch(dassources, true, false);
1453 SwingUtilities.invokeLater(fetcher);
1458 public void saveDAS_actionPerformed(ActionEvent e)
1461 .saveProperties(jalview.bin.Cache.applicationProperties);
1464 public void complete()
1466 fetchDAS.setEnabled(true);
1467 cancelDAS.setEnabled(false);
1468 dassourceBrowser.setGuiEnabled(true);
1472 public void cancelDAS_actionPerformed(ActionEvent e)
1474 if (dasFeatureFetcher != null)
1476 dasFeatureFetcher.cancel();
1481 public void noDasSourceActive()
1484 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1485 MessageManager.getString("label.no_das_sources_selected_warn"),
1486 MessageManager.getString("label.no_das_sources_selected_title"),
1487 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1490 // ///////////////////////////////////////////////////////////////////////
1491 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1492 // ///////////////////////////////////////////////////////////////////////
1493 class FeatureTableModel extends AbstractTableModel
1496 private String[] columnNames = {
1497 MessageManager.getString("label.feature_type"),
1498 MessageManager.getString("action.colour"),
1499 MessageManager.getString("label.filter"),
1500 MessageManager.getString("label.show") };
1502 private Object[][] data;
1504 FeatureTableModel(Object[][] data)
1509 public Object[][] getData()
1514 public void setData(Object[][] data)
1520 public int getColumnCount()
1522 return columnNames.length;
1525 public Object[] getRow(int row)
1531 public int getRowCount()
1537 public String getColumnName(int col)
1539 return columnNames[col];
1543 public Object getValueAt(int row, int col)
1545 return data[row][col];
1549 * Answers the class of the object in column c of the first row of the table
1552 public Class<?> getColumnClass(int c)
1554 Object v = getValueAt(0, c);
1555 return v == null ? null : v.getClass();
1559 public boolean isCellEditable(int row, int col)
1561 return col == 0 ? false : true;
1565 public void setValueAt(Object value, int row, int col)
1567 data[row][col] = value;
1568 fireTableCellUpdated(row, col);
1569 updateFeatureRenderer(data);
1574 class ColorRenderer extends JLabel implements TableCellRenderer
1576 javax.swing.border.Border unselectedBorder = null;
1578 javax.swing.border.Border selectedBorder = null;
1580 final String baseTT = "Click to edit, right/apple click for menu.";
1582 public ColorRenderer()
1584 setOpaque(true); // MUST do this for background to show up.
1585 setHorizontalTextPosition(SwingConstants.CENTER);
1586 setVerticalTextPosition(SwingConstants.CENTER);
1590 public Component getTableCellRendererComponent(JTable tbl, Object color,
1591 boolean isSelected, boolean hasFocus, int row, int column)
1593 FeatureColourI cellColour = (FeatureColourI) color;
1595 setToolTipText(baseTT);
1596 setBackground(tbl.getBackground());
1597 if (!cellColour.isSimpleColour())
1599 Rectangle cr = tbl.getCellRect(row, column, false);
1600 FeatureSettings.renderGraduatedColor(this, cellColour,
1601 (int) cr.getWidth(), (int) cr.getHeight());
1607 setBackground(cellColour.getColour());
1611 if (selectedBorder == null)
1613 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1614 tbl.getSelectionBackground());
1616 setBorder(selectedBorder);
1620 if (unselectedBorder == null)
1622 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1623 tbl.getBackground());
1625 setBorder(unselectedBorder);
1632 class FilterRenderer extends JLabel implements TableCellRenderer
1634 javax.swing.border.Border unselectedBorder = null;
1636 javax.swing.border.Border selectedBorder = null;
1638 public FilterRenderer()
1640 setOpaque(true); // MUST do this for background to show up.
1641 setHorizontalTextPosition(SwingConstants.CENTER);
1642 setVerticalTextPosition(SwingConstants.CENTER);
1646 public Component getTableCellRendererComponent(JTable tbl,
1647 Object filter, boolean isSelected, boolean hasFocus, int row,
1650 KeyedMatcherSetI theFilter = (KeyedMatcherSetI) filter;
1652 String asText = theFilter.toString();
1653 setBackground(tbl.getBackground());
1654 this.setText(asText);
1659 if (selectedBorder == null)
1661 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1662 tbl.getSelectionBackground());
1664 setBorder(selectedBorder);
1668 if (unselectedBorder == null)
1670 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1671 tbl.getBackground());
1673 setBorder(unselectedBorder);
1681 * update comp using rendering settings from gcol
1686 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1688 int w = comp.getWidth(), h = comp.getHeight();
1691 w = (int) comp.getPreferredSize().getWidth();
1692 h = (int) comp.getPreferredSize().getHeight();
1699 renderGraduatedColor(comp, gcol, w, h);
1702 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1705 boolean thr = false;
1706 StringBuilder tt = new StringBuilder();
1707 StringBuilder tx = new StringBuilder();
1709 if (gcol.isColourByAttribute())
1711 tx.append(String.join(":", gcol.getAttributeName()));
1713 else if (!gcol.isColourByLabel())
1715 tx.append(MessageManager.getString("label.score"));
1718 if (gcol.isAboveThreshold())
1722 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1725 if (gcol.isBelowThreshold())
1729 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1732 if (gcol.isColourByLabel())
1734 tt.append("Coloured by label text. ").append(tt);
1739 if (!gcol.isColourByAttribute())
1747 Color newColor = gcol.getMaxColour();
1748 comp.setBackground(newColor);
1749 // System.err.println("Width is " + w / 2);
1750 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1751 comp.setIcon(ficon);
1752 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1753 // + newColor.getGreen() + ", " + newColor.getBlue()
1754 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1755 // + ", " + minCol.getBlue() + ")");
1757 comp.setHorizontalAlignment(SwingConstants.CENTER);
1758 comp.setText(tx.toString());
1759 if (tt.length() > 0)
1761 if (comp.getToolTipText() == null)
1763 comp.setToolTipText(tt.toString());
1767 comp.setToolTipText(
1768 tt.append(" ").append(comp.getToolTipText()).toString());
1773 class ColorEditor extends AbstractCellEditor
1774 implements TableCellEditor, ActionListener
1778 FeatureColourI currentColor;
1780 FeatureTypeSettings chooser;
1786 JColorChooser colorChooser;
1790 protected static final String EDIT = "edit";
1792 int rowSelected = 0;
1794 public ColorEditor(FeatureSettings me)
1797 // Set up the editor (from the table's point of view),
1798 // which is a button.
1799 // This button brings up the color chooser dialog,
1800 // which is the editor from the user's point of view.
1801 button = new JButton();
1802 button.setActionCommand(EDIT);
1803 button.addActionListener(this);
1804 button.setBorderPainted(false);
1805 // Set up the dialog that the button brings up.
1806 colorChooser = new JColorChooser();
1807 dialog = JColorChooser.createDialog(button,
1808 MessageManager.getString("label.select_colour"), true, // modal
1809 colorChooser, this, // OK button handler
1810 null); // no CANCEL button handler
1814 * Handles events from the editor button and from the dialog's OK button.
1817 public void actionPerformed(ActionEvent e)
1819 // todo test e.getSource() instead here
1820 if (EDIT.equals(e.getActionCommand()))
1822 // The user has clicked the cell, so
1823 // bring up the dialog.
1824 if (currentColor.isSimpleColour())
1826 // bring up simple color chooser
1827 button.setBackground(currentColor.getColour());
1828 colorChooser.setColor(currentColor.getColour());
1829 dialog.setVisible(true);
1833 // bring up graduated chooser.
1834 chooser = new FeatureTypeSettings(me.fr, type);
1835 chooser.setRequestFocusEnabled(true);
1836 chooser.requestFocus();
1837 chooser.addActionListener(this);
1838 chooser.showTab(true);
1840 // Make the renderer reappear.
1841 fireEditingStopped();
1846 if (currentColor.isSimpleColour())
1849 * read off colour picked in colour chooser after OK pressed
1851 currentColor = new FeatureColour(colorChooser.getColor());
1852 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1857 * after OK in variable colour dialog, any changes to colour
1858 * (or filters!) are already set in FeatureRenderer, so just
1859 * update table data without triggering updateFeatureRenderer
1861 currentColor = fr.getFeatureColours().get(type);
1862 KeyedMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1863 if (currentFilter == null)
1865 currentFilter = new KeyedMatcherSet();
1867 Object[] data = ((FeatureTableModel) table.getModel())
1868 .getData()[rowSelected];
1869 data[COLOUR_COLUMN] = currentColor;
1870 data[FILTER_COLUMN] = currentFilter;
1872 fireEditingStopped();
1873 me.table.validate();
1877 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1879 public Object getCellEditorValue()
1881 return currentColor;
1884 // Implement the one method defined by TableCellEditor.
1886 public Component getTableCellEditorComponent(JTable theTable, Object value,
1887 boolean isSelected, int row, int column)
1889 currentColor = (FeatureColourI) value;
1890 this.rowSelected = row;
1891 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1892 button.setOpaque(true);
1893 button.setBackground(me.getBackground());
1894 if (!currentColor.isSimpleColour())
1896 JLabel btn = new JLabel();
1897 btn.setSize(button.getSize());
1898 FeatureSettings.renderGraduatedColor(btn, currentColor);
1899 button.setBackground(btn.getBackground());
1900 button.setIcon(btn.getIcon());
1901 button.setText(btn.getText());
1906 button.setIcon(null);
1907 button.setBackground(currentColor.getColour());
1914 * The cell editor for the Filter column. It displays the text of any filters
1915 * for the feature type in that row (in full as a tooltip, possible abbreviated
1916 * as display text). On click in the cell, opens the Feature Display Settings
1917 * dialog at the Filters tab.
1919 class FilterEditor extends AbstractCellEditor
1920 implements TableCellEditor, ActionListener
1924 KeyedMatcherSetI currentFilter;
1932 protected static final String EDIT = "edit";
1934 int rowSelected = 0;
1936 public FilterEditor(FeatureSettings me)
1939 button = new JButton();
1940 button.setActionCommand(EDIT);
1941 button.addActionListener(this);
1942 button.setBorderPainted(false);
1946 * Handles events from the editor button
1949 public void actionPerformed(ActionEvent e)
1951 if (button == e.getSource())
1953 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1954 chooser.addActionListener(this);
1955 chooser.setRequestFocusEnabled(true);
1956 chooser.requestFocus();
1957 if (lastLocation != null)
1959 // todo open at its last position on screen
1960 chooser.setBounds(lastLocation.x, lastLocation.y,
1961 chooser.getWidth(), chooser.getHeight());
1964 chooser.showTab(false);
1965 fireEditingStopped();
1967 else if (e.getSource() instanceof Component)
1971 * after OK in variable colour dialog, any changes to filter
1972 * (or colours!) are already set in FeatureRenderer, so just
1973 * update table data without triggering updateFeatureRenderer
1975 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1976 currentFilter = me.fr.getFeatureFilter(type);
1977 if (currentFilter == null)
1979 currentFilter = new KeyedMatcherSet();
1981 Object[] data = ((FeatureTableModel) table.getModel())
1982 .getData()[rowSelected];
1983 data[COLOUR_COLUMN] = currentColor;
1984 data[FILTER_COLUMN] = currentFilter;
1985 fireEditingStopped();
1986 me.table.validate();
1991 public Object getCellEditorValue()
1993 return currentFilter;
1997 public Component getTableCellEditorComponent(JTable theTable, Object value,
1998 boolean isSelected, int row, int column)
2000 currentFilter = (KeyedMatcherSetI) value;
2001 this.rowSelected = row;
2002 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2003 button.setOpaque(true);
2004 button.setBackground(me.getBackground());
2005 button.setText(currentFilter.toString());
2006 button.setToolTipText(currentFilter.toString());
2007 button.setIcon(null);
2013 class FeatureIcon implements Icon
2015 FeatureColourI gcol;
2019 boolean midspace = false;
2021 int width = 50, height = 20;
2023 int s1, e1; // start and end of midpoint band for thresholded symbol
2025 Color mpcolour = Color.white;
2027 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2047 public int getIconWidth()
2053 public int getIconHeight()
2059 public void paintIcon(Component c, Graphics g, int x, int y)
2062 if (gcol.isColourByLabel())
2065 g.fillRect(0, 0, width, height);
2066 // need an icon here.
2067 g.setColor(gcol.getMaxColour());
2069 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2071 // g.setFont(g.getFont().deriveFont(
2072 // AffineTransform.getScaleInstance(
2073 // width/g.getFontMetrics().stringWidth("Label"),
2074 // height/g.getFontMetrics().getHeight())));
2076 g.drawString(MessageManager.getString("label.label"), 0, 0);
2081 Color minCol = gcol.getMinColour();
2083 g.fillRect(0, 0, s1, height);
2086 g.setColor(Color.white);
2087 g.fillRect(s1, 0, e1 - s1, height);
2089 g.setColor(gcol.getMaxColour());
2090 g.fillRect(0, e1, width - e1, height);