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.HashSet;
70 import java.util.Hashtable;
71 import java.util.Iterator;
72 import java.util.List;
75 import java.util.Vector;
77 import javax.help.HelpSetException;
78 import javax.swing.AbstractCellEditor;
79 import javax.swing.BorderFactory;
80 import javax.swing.Icon;
81 import javax.swing.JButton;
82 import javax.swing.JCheckBox;
83 import javax.swing.JCheckBoxMenuItem;
84 import javax.swing.JColorChooser;
85 import javax.swing.JDialog;
86 import javax.swing.JInternalFrame;
87 import javax.swing.JLabel;
88 import javax.swing.JLayeredPane;
89 import javax.swing.JMenuItem;
90 import javax.swing.JPanel;
91 import javax.swing.JPopupMenu;
92 import javax.swing.JScrollPane;
93 import javax.swing.JSlider;
94 import javax.swing.JTable;
95 import javax.swing.ListSelectionModel;
96 import javax.swing.SwingConstants;
97 import javax.swing.SwingUtilities;
98 import javax.swing.event.ChangeEvent;
99 import javax.swing.event.ChangeListener;
100 import javax.swing.table.AbstractTableModel;
101 import javax.swing.table.TableCellEditor;
102 import javax.swing.table.TableCellRenderer;
103 import javax.swing.table.TableColumn;
105 public class FeatureSettings extends JPanel
106 implements FeatureSettingsControllerI
109 * column indices of fields in Feature Settings table
111 static final int TYPE_COLUMN = 0;
113 static final int COLOUR_COLUMN = 1;
115 static final int FILTER_COLUMN = 2;
117 static final int SHOW_COLUMN = 3;
119 private static final int COLUMN_COUNT = 4;
121 private static final int MIN_WIDTH = 400;
123 private static final int MIN_HEIGHT = 400;
125 DasSourceBrowser dassourceBrowser;
127 DasSequenceFeatureFetcher dasFeatureFetcher;
129 JPanel dasSettingsPane = new JPanel();
131 final FeatureRenderer fr;
133 public final AlignFrame af;
136 * 'original' fields hold settings to restore on Cancel
138 Object[][] originalData;
140 private float originalTransparency;
142 private Map<String, FeatureMatcherSetI> originalFilters;
144 final JInternalFrame frame;
146 JScrollPane scrollPane = new JScrollPane();
152 JSlider transparency = new JSlider();
155 * when true, constructor is still executing - so ignore UI events
157 protected volatile boolean inConstruction = true;
159 int selectedRow = -1;
161 JButton fetchDAS = new JButton();
163 JButton saveDAS = new JButton();
165 JButton cancelDAS = new JButton();
167 boolean resettingTable = false;
170 * true when Feature Settings are updating from feature renderer
172 private boolean handlingUpdate = false;
175 * holds {featureCount, totalExtent} for each feature type
177 Map<String, float[]> typeWidth = null;
184 public FeatureSettings(AlignFrame alignFrame)
186 this.af = alignFrame;
187 fr = af.getFeatureRenderer();
189 // save transparency for restore on Cancel
190 originalTransparency = fr.getTransparency();
191 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
192 transparency.setMaximum(100 - originalTransparencyAsPercent);
194 originalFilters = fr.getFeatureFilters();
199 } catch (Exception ex)
201 ex.printStackTrace();
207 public String getToolTipText(MouseEvent e)
210 int column = table.columnAtPoint(e.getPoint());
214 tip = JvSwingUtils.wrapTooltip(true, MessageManager
215 .getString("label.feature_settings_click_drag"));
218 int row = table.rowAtPoint(e.getPoint());
219 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
222 ? MessageManager.getString("label.filters_tooltip")
231 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
232 table.setFont(new Font("Verdana", Font.PLAIN, 12));
234 // table.setDefaultRenderer(Color.class, new ColorRenderer());
235 // table.setDefaultEditor(Color.class, new ColorEditor(this));
237 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
238 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
240 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
241 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
243 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
244 new ColorRenderer(), new ColorEditor(this));
245 table.addColumn(colourColumn);
247 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
248 new FilterRenderer(), new FilterEditor(this));
249 table.addColumn(filterColumn);
251 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
253 table.addMouseListener(new MouseAdapter()
256 public void mousePressed(MouseEvent evt)
258 selectedRow = table.rowAtPoint(evt.getPoint());
259 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
260 if (evt.isPopupTrigger())
262 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
263 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
266 else if (evt.getClickCount() == 2)
268 boolean invertSelection = evt.isAltDown();
269 boolean toggleSelection = Platform.isControlDown(evt);
270 boolean extendSelection = evt.isShiftDown();
271 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
272 invertSelection, extendSelection, toggleSelection, type);
276 // isPopupTrigger fires on mouseReleased on Windows
278 public void mouseReleased(MouseEvent evt)
280 selectedRow = table.rowAtPoint(evt.getPoint());
281 if (evt.isPopupTrigger())
283 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
284 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
285 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
291 table.addMouseMotionListener(new MouseMotionAdapter()
294 public void mouseDragged(MouseEvent evt)
296 int newRow = table.rowAtPoint(evt.getPoint());
297 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
300 * reposition 'selectedRow' to 'newRow' (the dragged to location)
301 * this could be more than one row away for a very fast drag action
302 * so just swap it with adjacent rows until we get it there
304 Object[][] data = ((FeatureTableModel) table.getModel())
306 int direction = newRow < selectedRow ? -1 : 1;
307 for (int i = selectedRow; i != newRow; i += direction)
309 Object[] temp = data[i];
310 data[i] = data[i + direction];
311 data[i + direction] = temp;
313 updateFeatureRenderer(data);
315 selectedRow = newRow;
319 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
320 // MessageManager.getString("label.feature_settings_click_drag")));
321 scrollPane.setViewportView(table);
323 dassourceBrowser = new DasSourceBrowser(this);
324 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
326 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
328 fr.findAllFeatures(true); // display everything!
331 discoverAllFeatureData();
332 final PropertyChangeListener change;
333 final FeatureSettings fs = this;
334 fr.addPropertyChangeListener(change = new PropertyChangeListener()
337 public void propertyChange(PropertyChangeEvent evt)
339 if (!fs.resettingTable && !fs.handlingUpdate)
341 fs.handlingUpdate = true;
343 // new groups may be added with new sequence feature types only
344 fs.handlingUpdate = false;
350 frame = new JInternalFrame();
351 frame.setContentPane(this);
352 if (Platform.isAMac())
354 Desktop.addInternalFrame(frame,
355 MessageManager.getString("label.sequence_feature_settings"),
360 Desktop.addInternalFrame(frame,
361 MessageManager.getString("label.sequence_feature_settings"),
364 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
366 frame.addInternalFrameListener(
367 new javax.swing.event.InternalFrameAdapter()
370 public void internalFrameClosed(
371 javax.swing.event.InternalFrameEvent evt)
373 fr.removePropertyChangeListener(change);
374 dassourceBrowser.fs = null;
377 frame.setLayer(JLayeredPane.PALETTE_LAYER);
378 inConstruction = false;
381 protected void popupSort(final int rowSelected, final String type,
382 final Object typeCol, final Map<String, float[][]> minmax, int x,
385 final FeatureColourI featureColour = (FeatureColourI) typeCol;
387 JPopupMenu men = new JPopupMenu(MessageManager
388 .formatMessage("label.settings_for_param", new String[]
390 JMenuItem scr = new JMenuItem(
391 MessageManager.getString("label.sort_by_score"));
393 final FeatureSettings me = this;
394 scr.addActionListener(new ActionListener()
398 public void actionPerformed(ActionEvent e)
401 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
406 JMenuItem dens = new JMenuItem(
407 MessageManager.getString("label.sort_by_density"));
408 dens.addActionListener(new ActionListener()
412 public void actionPerformed(ActionEvent e)
415 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
423 * variable colour options include colour by label, by score,
424 * by selected attribute text, or attribute value
426 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
427 MessageManager.getString("label.variable_colour"));
428 mxcol.setSelected(!featureColour.isSimpleColour());
430 mxcol.addActionListener(new ActionListener()
432 JColorChooser colorChooser;
435 public void actionPerformed(ActionEvent e)
437 if (e.getSource() == mxcol)
439 if (featureColour.isSimpleColour())
441 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
442 fc.addActionListener(this);
446 // bring up simple color chooser
447 colorChooser = new JColorChooser();
448 String title = MessageManager
449 .getString("label.select_colour");
450 JDialog dialog = JColorChooser.createDialog(me,
451 title, true, // modal
452 colorChooser, this, // OK button handler
453 null); // no CANCEL button handler
454 colorChooser.setColor(featureColour.getMaxColour());
455 dialog.setVisible(true);
460 if (e.getSource() instanceof FeatureTypeSettings)
463 * update after OK in feature colour dialog; the updated
464 * colour will have already been set in the FeatureRenderer
466 FeatureColourI fci = fr.getFeatureColours().get(type);
467 table.setValueAt(fci, rowSelected, 1);
472 // probably the color chooser!
473 table.setValueAt(new FeatureColour(colorChooser.getColor()),
476 me.updateFeatureRenderer(
477 ((FeatureTableModel) table.getModel()).getData(),
485 JMenuItem selCols = new JMenuItem(
486 MessageManager.getString("label.select_columns_containing"));
487 selCols.addActionListener(new ActionListener()
490 public void actionPerformed(ActionEvent arg0)
492 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
496 JMenuItem clearCols = new JMenuItem(MessageManager
497 .getString("label.select_columns_not_containing"));
498 clearCols.addActionListener(new ActionListener()
501 public void actionPerformed(ActionEvent arg0)
503 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
507 JMenuItem hideCols = new JMenuItem(
508 MessageManager.getString("label.hide_columns_containing"));
509 hideCols.addActionListener(new ActionListener()
512 public void actionPerformed(ActionEvent arg0)
514 fr.ap.alignFrame.hideFeatureColumns(type, true);
517 JMenuItem hideOtherCols = new JMenuItem(
518 MessageManager.getString("label.hide_columns_not_containing"));
519 hideOtherCols.addActionListener(new ActionListener()
522 public void actionPerformed(ActionEvent arg0)
524 fr.ap.alignFrame.hideFeatureColumns(type, false);
530 men.add(hideOtherCols);
531 men.show(table, x, y);
535 synchronized public void discoverAllFeatureData()
537 Set<String> allGroups = new HashSet<>();
538 AlignmentI alignment = af.getViewport().getAlignment();
540 for (int i = 0; i < alignment.getHeight(); i++)
542 SequenceI seq = alignment.getSequenceAt(i);
543 for (String group : seq.getFeatures().getFeatureGroups(true))
545 if (group != null && !allGroups.contains(group))
547 allGroups.add(group);
548 checkGroupState(group);
559 * Synchronise gui group list and check visibility of group
562 * @return true if group is visible
564 private boolean checkGroupState(String group)
566 boolean visible = fr.checkGroupVisibility(group, true);
568 for (int g = 0; g < groupPanel.getComponentCount(); g++)
570 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
572 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
577 final String grp = group;
578 final JCheckBox check = new JCheckBox(group, visible);
579 check.setFont(new Font("Serif", Font.BOLD, 12));
580 check.setToolTipText(group);
581 check.addItemListener(new ItemListener()
584 public void itemStateChanged(ItemEvent evt)
586 fr.setGroupVisibility(check.getText(), check.isSelected());
587 resetTable(new String[] { grp });
588 af.alignPanel.paintAlignment(true, true);
591 groupPanel.add(check);
595 synchronized void resetTable(String[] groupChanged)
601 resettingTable = true;
602 typeWidth = new Hashtable<>();
603 // TODO: change avWidth calculation to 'per-sequence' average and use long
606 Set<String> displayableTypes = new HashSet<>();
607 Set<String> foundGroups = new HashSet<>();
610 * determine which feature types may be visible depending on
611 * which groups are selected, and recompute average width data
613 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
616 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
619 * get the sequence's groups for positional features
620 * and keep track of which groups are visible
622 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
623 Set<String> visibleGroups = new HashSet<>();
624 for (String group : groups)
626 if (group == null || checkGroupState(group))
628 visibleGroups.add(group);
631 foundGroups.addAll(groups);
634 * get distinct feature types for visible groups
635 * record distinct visible types, and their count and total length
637 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
638 visibleGroups.toArray(new String[visibleGroups.size()]));
639 for (String type : types)
641 displayableTypes.add(type);
642 float[] avWidth = typeWidth.get(type);
645 avWidth = new float[2];
646 typeWidth.put(type, avWidth);
648 // todo this could include features with a non-visible group
649 // - do we greatly care?
650 // todo should we include non-displayable features here, and only
651 // update when features are added?
652 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
653 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
657 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
660 if (fr.hasRenderOrder())
664 fr.findAllFeatures(groupChanged != null); // prod to update
665 // colourschemes. but don't
667 // First add the checks in the previous render order,
668 // in case the window has been closed and reopened
670 List<String> frl = fr.getRenderOrder();
671 for (int ro = frl.size() - 1; ro > -1; ro--)
673 String type = frl.get(ro);
675 if (!displayableTypes.contains(type))
680 data[dataIndex][TYPE_COLUMN] = type;
681 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
682 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
683 data[dataIndex][FILTER_COLUMN] = featureFilter == null
684 ? new FeatureMatcherSet()
686 data[dataIndex][SHOW_COLUMN] = new Boolean(
687 af.getViewport().getFeaturesDisplayed().isVisible(type));
689 displayableTypes.remove(type);
694 * process any extra features belonging only to
695 * a group which was just selected
697 while (!displayableTypes.isEmpty())
699 String type = displayableTypes.iterator().next();
700 data[dataIndex][TYPE_COLUMN] = type;
702 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
703 if (data[dataIndex][COLOUR_COLUMN] == null)
705 // "Colour has been updated in another view!!"
706 fr.clearRenderOrder();
709 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
710 data[dataIndex][FILTER_COLUMN] = featureFilter == null
711 ? new FeatureMatcherSet()
713 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
715 displayableTypes.remove(type);
718 if (originalData == null)
720 originalData = new Object[data.length][COLUMN_COUNT];
721 for (int i = 0; i < data.length; i++)
723 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
728 updateOriginalData(data);
731 table.setModel(new FeatureTableModel(data));
732 table.getColumnModel().getColumn(0).setPreferredWidth(200);
734 groupPanel.setLayout(
735 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
736 pruneGroups(foundGroups);
737 groupPanel.validate();
739 updateFeatureRenderer(data, groupChanged != null);
740 resettingTable = false;
744 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
745 * have been made outwith this dialog
747 * <li>a new feature type added (and made visible)</li>
748 * <li>a feature colour changed (in the Amend Features dialog)</li>
753 protected void updateOriginalData(Object[][] foundData)
755 // todo LinkedHashMap instead of Object[][] would be nice
757 Object[][] currentData = ((FeatureTableModel) table.getModel())
759 for (Object[] row : foundData)
761 String type = (String) row[TYPE_COLUMN];
762 boolean found = false;
763 for (Object[] current : currentData)
765 if (type.equals(current[TYPE_COLUMN]))
769 * currently dependent on object equality here;
770 * really need an equals method on FeatureColour
772 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
775 * feature colour has changed externally - update originalData
777 for (Object[] original : originalData)
779 if (type.equals(original[TYPE_COLUMN]))
781 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
792 * new feature detected - add to original data (on top)
794 Object[][] newData = new Object[originalData.length
796 for (int i = 0; i < originalData.length; i++)
798 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
802 originalData = newData;
808 * Remove from the groups panel any checkboxes for groups that are not in the
809 * foundGroups set. This enables removing a group from the display when the last
810 * feature in that group is deleted.
814 protected void pruneGroups(Set<String> foundGroups)
816 for (int g = 0; g < groupPanel.getComponentCount(); g++)
818 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
819 if (!foundGroups.contains(checkbox.getText()))
821 groupPanel.remove(checkbox);
827 * reorder data based on the featureRenderers global priority list.
831 private void ensureOrder(Object[][] data)
833 boolean sort = false;
834 float[] order = new float[data.length];
835 for (int i = 0; i < order.length; i++)
837 order[i] = fr.getOrder(data[i][0].toString());
840 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
844 sort = sort || order[i - 1] > order[i];
849 jalview.util.QuickSort.sort(order, data);
855 JalviewFileChooser chooser = new JalviewFileChooser("fc",
856 "Sequence Feature Colours");
857 chooser.setFileView(new JalviewFileView());
858 chooser.setDialogTitle(
859 MessageManager.getString("label.load_feature_colours"));
860 chooser.setToolTipText(MessageManager.getString("action.load"));
862 int value = chooser.showOpenDialog(this);
864 if (value == JalviewFileChooser.APPROVE_OPTION)
866 File file = chooser.getSelectedFile();
870 InputStreamReader in = new InputStreamReader(
871 new FileInputStream(file), "UTF-8");
873 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
875 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
878 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
881 Color mincol = null, maxcol = null;
884 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
885 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
887 } catch (Exception e)
889 Cache.log.warn("Couldn't parse out graduated feature color.",
892 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
893 newcol.getMin(), newcol.getMax());
894 if (newcol.hasAutoScale())
896 gcol.setAutoScaled(newcol.getAutoScale());
898 if (newcol.hasColourByLabel())
900 gcol.setColourByLabel(newcol.getColourByLabel());
902 if (newcol.hasThreshold())
904 gcol.setThreshold(newcol.getThreshold());
906 if (newcol.getThreshType().length() > 0)
908 String ttyp = newcol.getThreshType();
909 if (ttyp.equalsIgnoreCase("ABOVE"))
911 gcol.setAboveThreshold(true);
913 if (ttyp.equalsIgnoreCase("BELOW"))
915 gcol.setBelowThreshold(true);
918 fr.setColour(name = newcol.getName(), gcol);
922 Color color = new Color(
923 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
924 fr.setColour(name = jucs.getColour(i).getName(),
925 new FeatureColour(color));
927 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
932 Object[][] data = ((FeatureTableModel) table.getModel())
935 updateFeatureRenderer(data, false);
938 } catch (Exception ex)
940 System.out.println("Error loading User Colour File\n" + ex);
947 JalviewFileChooser chooser = new JalviewFileChooser("fc",
948 "Sequence Feature Colours");
949 chooser.setFileView(new JalviewFileView());
950 chooser.setDialogTitle(
951 MessageManager.getString("label.save_feature_colours"));
952 chooser.setToolTipText(MessageManager.getString("action.save"));
954 int value = chooser.showSaveDialog(this);
956 if (value == JalviewFileChooser.APPROVE_OPTION)
958 String choice = chooser.getSelectedFile().getPath();
959 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
960 ucs.setSchemeName("Sequence Features");
963 PrintWriter out = new PrintWriter(new OutputStreamWriter(
964 new FileOutputStream(choice), "UTF-8"));
966 Set<String> fr_colours = fr.getAllFeatureColours();
967 Iterator<String> e = fr_colours.iterator();
968 float[] sortOrder = new float[fr_colours.size()];
969 String[] sortTypes = new String[fr_colours.size()];
973 sortTypes[i] = e.next();
974 sortOrder[i] = fr.getOrder(sortTypes[i]);
977 QuickSort.sort(sortOrder, sortTypes);
979 for (i = 0; i < sortTypes.length; i++)
981 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
982 col.setName(sortTypes[i]);
983 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
984 if (fcol.isSimpleColour())
986 col.setRGB(Format.getHexString(fcol.getColour()));
990 col.setRGB(Format.getHexString(fcol.getMaxColour()));
991 col.setMin(fcol.getMin());
992 col.setMax(fcol.getMax());
994 jalview.util.Format.getHexString(fcol.getMinColour()));
995 col.setAutoScale(fcol.isAutoScaled());
996 col.setThreshold(fcol.getThreshold());
997 col.setColourByLabel(fcol.isColourByLabel());
998 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
999 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
1005 } catch (Exception ex)
1007 ex.printStackTrace();
1012 public void invertSelection()
1014 for (int i = 0; i < table.getRowCount(); i++)
1016 Boolean value = (Boolean) table.getValueAt(i, SHOW_COLUMN);
1018 table.setValueAt(new Boolean(!value.booleanValue()), i, SHOW_COLUMN);
1022 public void orderByAvWidth()
1024 if (table == null || table.getModel() == null)
1028 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1029 float[] width = new float[data.length];
1033 for (int i = 0; i < data.length; i++)
1035 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1038 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1039 // weight - but have to make per
1040 // sequence, too (awidth[2])
1041 // if (width[i]==1) // hack to distinguish single width sequences.
1052 boolean sort = false;
1053 for (int i = 0; i < width.length; i++)
1055 // awidth = (float[]) typeWidth.get(data[i][0]);
1058 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1061 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1067 width[i] /= max; // normalize
1068 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1072 sort = sort || width[i - 1] > width[i];
1077 jalview.util.QuickSort.sort(width, data);
1078 // update global priority order
1081 updateFeatureRenderer(data, false);
1089 frame.setClosed(true);
1090 } catch (Exception exe)
1096 public void updateFeatureRenderer(Object[][] data)
1098 updateFeatureRenderer(data, true);
1102 * Update the priority order of features; only repaint if this changed the order
1103 * of visible features
1108 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1110 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1112 if (fr.setFeaturePriority(rowData, visibleNew))
1114 af.alignPanel.paintAlignment(true, true);
1119 * Converts table data into an array of data beans
1121 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1123 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1124 for (int i = 0; i < data.length; i++)
1126 String type = (String) data[i][TYPE_COLUMN];
1127 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1128 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1129 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1130 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1136 private void jbInit() throws Exception
1138 this.setLayout(new BorderLayout());
1140 JPanel settingsPane = new JPanel();
1141 settingsPane.setLayout(new BorderLayout());
1143 dasSettingsPane.setLayout(new BorderLayout());
1145 JPanel bigPanel = new JPanel();
1146 bigPanel.setLayout(new BorderLayout());
1148 groupPanel = new JPanel();
1149 bigPanel.add(groupPanel, BorderLayout.NORTH);
1151 JButton invert = new JButton(
1152 MessageManager.getString("label.invert_selection"));
1153 invert.setFont(JvSwingUtils.getLabelFont());
1154 invert.addActionListener(new ActionListener()
1157 public void actionPerformed(ActionEvent e)
1163 JButton optimizeOrder = new JButton(
1164 MessageManager.getString("label.optimise_order"));
1165 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1166 optimizeOrder.addActionListener(new ActionListener()
1169 public void actionPerformed(ActionEvent e)
1175 JButton sortByScore = new JButton(
1176 MessageManager.getString("label.seq_sort_by_score"));
1177 sortByScore.setFont(JvSwingUtils.getLabelFont());
1178 sortByScore.addActionListener(new ActionListener()
1181 public void actionPerformed(ActionEvent e)
1183 af.avc.sortAlignmentByFeatureScore(null);
1186 JButton sortByDens = new JButton(
1187 MessageManager.getString("label.sequence_sort_by_density"));
1188 sortByDens.setFont(JvSwingUtils.getLabelFont());
1189 sortByDens.addActionListener(new ActionListener()
1192 public void actionPerformed(ActionEvent e)
1194 af.avc.sortAlignmentByFeatureDensity(null);
1198 JButton help = new JButton(MessageManager.getString("action.help"));
1199 help.setFont(JvSwingUtils.getLabelFont());
1200 help.addActionListener(new ActionListener()
1203 public void actionPerformed(ActionEvent e)
1207 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1208 } catch (HelpSetException e1)
1210 e1.printStackTrace();
1214 help.setFont(JvSwingUtils.getLabelFont());
1215 help.setText(MessageManager.getString("action.help"));
1216 help.addActionListener(new ActionListener()
1219 public void actionPerformed(ActionEvent e)
1223 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1224 } catch (HelpSetException e1)
1226 e1.printStackTrace();
1231 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1232 cancel.setFont(JvSwingUtils.getLabelFont());
1233 cancel.addActionListener(new ActionListener()
1236 public void actionPerformed(ActionEvent e)
1238 fr.setTransparency(originalTransparency);
1239 fr.setFeatureFilters(originalFilters);
1240 updateFeatureRenderer(originalData);
1245 JButton ok = new JButton(MessageManager.getString("action.ok"));
1246 ok.setFont(JvSwingUtils.getLabelFont());
1247 ok.addActionListener(new ActionListener()
1250 public void actionPerformed(ActionEvent e)
1256 JButton loadColours = new JButton(
1257 MessageManager.getString("label.load_colours"));
1258 loadColours.setFont(JvSwingUtils.getLabelFont());
1259 loadColours.addActionListener(new ActionListener()
1262 public void actionPerformed(ActionEvent e)
1268 JButton saveColours = new JButton(
1269 MessageManager.getString("label.save_colours"));
1270 saveColours.setFont(JvSwingUtils.getLabelFont());
1271 saveColours.addActionListener(new ActionListener()
1274 public void actionPerformed(ActionEvent e)
1279 transparency.addChangeListener(new ChangeListener()
1282 public void stateChanged(ChangeEvent evt)
1284 if (!inConstruction)
1286 fr.setTransparency((100 - transparency.getValue()) / 100f);
1287 af.alignPanel.paintAlignment(true, true);
1292 transparency.setMaximum(70);
1293 transparency.setToolTipText(
1294 MessageManager.getString("label.transparency_tip"));
1295 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1296 fetchDAS.addActionListener(new ActionListener()
1299 public void actionPerformed(ActionEvent e)
1301 fetchDAS_actionPerformed(e);
1304 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1305 saveDAS.addActionListener(new ActionListener()
1308 public void actionPerformed(ActionEvent e)
1310 saveDAS_actionPerformed(e);
1314 JPanel dasButtonPanel = new JPanel();
1315 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1316 dasSettingsPane.setBorder(null);
1317 cancelDAS.setEnabled(false);
1318 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1319 cancelDAS.addActionListener(new ActionListener()
1322 public void actionPerformed(ActionEvent e)
1324 cancelDAS_actionPerformed(e);
1328 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1329 bigPanel.add(transPanel, BorderLayout.SOUTH);
1331 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1332 transbuttons.add(optimizeOrder);
1333 transbuttons.add(invert);
1334 transbuttons.add(sortByScore);
1335 transbuttons.add(sortByDens);
1336 transbuttons.add(help);
1337 transPanel.add(transparency);
1338 transPanel.add(transbuttons);
1340 JPanel buttonPanel = new JPanel();
1341 buttonPanel.add(ok);
1342 buttonPanel.add(cancel);
1343 buttonPanel.add(loadColours);
1344 buttonPanel.add(saveColours);
1345 bigPanel.add(scrollPane, BorderLayout.CENTER);
1346 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1347 dasButtonPanel.add(fetchDAS);
1348 dasButtonPanel.add(cancelDAS);
1349 dasButtonPanel.add(saveDAS);
1350 settingsPane.add(bigPanel, BorderLayout.CENTER);
1351 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1352 this.add(settingsPane);
1355 public void fetchDAS_actionPerformed(ActionEvent e)
1357 fetchDAS.setEnabled(false);
1358 cancelDAS.setEnabled(true);
1359 dassourceBrowser.setGuiEnabled(false);
1360 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1361 .getSelectedSources();
1362 doDasFeatureFetch(selectedSources, true, true);
1366 * get the features from selectedSources for all or the current selection
1368 * @param selectedSources
1369 * @param checkDbRefs
1370 * @param promptFetchDbRefs
1372 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1373 boolean checkDbRefs, boolean promptFetchDbRefs)
1375 SequenceI[] dataset, seqs;
1377 AlignmentViewport vp = af.getViewport();
1378 if (vp.getSelectionGroup() != null
1379 && vp.getSelectionGroup().getSize() > 0)
1381 iSize = vp.getSelectionGroup().getSize();
1382 dataset = new SequenceI[iSize];
1383 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1387 iSize = vp.getAlignment().getHeight();
1388 seqs = vp.getAlignment().getSequencesArray();
1391 dataset = new SequenceI[iSize];
1392 for (int i = 0; i < iSize; i++)
1394 dataset[i] = seqs[i].getDatasetSequence();
1397 cancelDAS.setEnabled(true);
1398 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1399 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1400 af.getViewport().setShowSequenceFeatures(true);
1401 af.showSeqFeatures.setSelected(true);
1405 * blocking call to initialise the das source browser
1407 public void initDasSources()
1409 dassourceBrowser.initDasSources();
1413 * examine the current list of das sources and return any matching the given
1414 * nicknames in sources
1417 * Vector of Strings to resolve to DAS source nicknames.
1418 * @return sources that are present in source list.
1420 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1422 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1426 * get currently selected das sources. ensure you have called initDasSources
1427 * before calling this.
1429 * @return vector of selected das source nicknames
1431 public Vector<jalviewSourceI> getSelectedSources()
1433 return dassourceBrowser.getSelectedSources();
1437 * properly initialise DAS fetcher and then initiate a new thread to fetch
1438 * features from the named sources (rather than any turned on by default)
1442 * if true then runs in same thread, otherwise passes to the Swing
1445 public void fetchDasFeatures(Vector<String> sources, boolean block)
1448 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1449 .resolveSourceNicknames(sources);
1450 if (resolved.size() == 0)
1452 resolved = dassourceBrowser.getSelectedSources();
1454 if (resolved.size() > 0)
1456 final List<jalviewSourceI> dassources = resolved;
1457 fetchDAS.setEnabled(false);
1458 // cancelDAS.setEnabled(true); doDasFetch does this.
1459 Runnable fetcher = new Runnable()
1465 doDasFeatureFetch(dassources, true, false);
1475 SwingUtilities.invokeLater(fetcher);
1480 public void saveDAS_actionPerformed(ActionEvent e)
1483 .saveProperties(jalview.bin.Cache.applicationProperties);
1486 public void complete()
1488 fetchDAS.setEnabled(true);
1489 cancelDAS.setEnabled(false);
1490 dassourceBrowser.setGuiEnabled(true);
1494 public void cancelDAS_actionPerformed(ActionEvent e)
1496 if (dasFeatureFetcher != null)
1498 dasFeatureFetcher.cancel();
1503 public void noDasSourceActive()
1506 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1507 MessageManager.getString("label.no_das_sources_selected_warn"),
1508 MessageManager.getString("label.no_das_sources_selected_title"),
1509 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1512 // ///////////////////////////////////////////////////////////////////////
1513 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1514 // ///////////////////////////////////////////////////////////////////////
1515 class FeatureTableModel extends AbstractTableModel
1517 private String[] columnNames = {
1518 MessageManager.getString("label.feature_type"),
1519 MessageManager.getString("action.colour"),
1520 MessageManager.getString("label.filter"),
1521 MessageManager.getString("label.show") };
1523 private Object[][] data;
1525 FeatureTableModel(Object[][] data)
1530 public Object[][] getData()
1535 public void setData(Object[][] data)
1541 public int getColumnCount()
1543 return columnNames.length;
1546 public Object[] getRow(int row)
1552 public int getRowCount()
1558 public String getColumnName(int col)
1560 return columnNames[col];
1564 public Object getValueAt(int row, int col)
1566 return data[row][col];
1570 * Answers the class of the object in column c of the first row of the table
1573 public Class<?> getColumnClass(int c)
1575 Object v = getValueAt(0, c);
1576 return v == null ? null : v.getClass();
1580 public boolean isCellEditable(int row, int col)
1582 return col == 0 ? false : true;
1586 public void setValueAt(Object value, int row, int col)
1588 data[row][col] = value;
1589 fireTableCellUpdated(row, col);
1590 updateFeatureRenderer(data);
1595 class ColorRenderer extends JLabel implements TableCellRenderer
1597 javax.swing.border.Border unselectedBorder = null;
1599 javax.swing.border.Border selectedBorder = null;
1601 final String baseTT = "Click to edit, right/apple click for menu.";
1603 public ColorRenderer()
1605 setOpaque(true); // MUST do this for background to show up.
1606 setHorizontalTextPosition(SwingConstants.CENTER);
1607 setVerticalTextPosition(SwingConstants.CENTER);
1611 public Component getTableCellRendererComponent(JTable tbl, Object color,
1612 boolean isSelected, boolean hasFocus, int row, int column)
1614 FeatureColourI cellColour = (FeatureColourI) color;
1616 setToolTipText(baseTT);
1617 setBackground(tbl.getBackground());
1618 if (!cellColour.isSimpleColour())
1620 Rectangle cr = tbl.getCellRect(row, column, false);
1621 FeatureSettings.renderGraduatedColor(this, cellColour,
1622 (int) cr.getWidth(), (int) cr.getHeight());
1628 setBackground(cellColour.getColour());
1632 if (selectedBorder == null)
1634 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1635 tbl.getSelectionBackground());
1637 setBorder(selectedBorder);
1641 if (unselectedBorder == null)
1643 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1644 tbl.getBackground());
1646 setBorder(unselectedBorder);
1653 class FilterRenderer extends JLabel implements TableCellRenderer
1655 javax.swing.border.Border unselectedBorder = null;
1657 javax.swing.border.Border selectedBorder = null;
1659 public FilterRenderer()
1661 setOpaque(true); // MUST do this for background to show up.
1662 setHorizontalTextPosition(SwingConstants.CENTER);
1663 setVerticalTextPosition(SwingConstants.CENTER);
1667 public Component getTableCellRendererComponent(JTable tbl,
1668 Object filter, boolean isSelected, boolean hasFocus, int row,
1671 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1673 String asText = theFilter.toString();
1674 setBackground(tbl.getBackground());
1675 this.setText(asText);
1680 if (selectedBorder == null)
1682 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1683 tbl.getSelectionBackground());
1685 setBorder(selectedBorder);
1689 if (unselectedBorder == null)
1691 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1692 tbl.getBackground());
1694 setBorder(unselectedBorder);
1702 * update comp using rendering settings from gcol
1707 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1709 int w = comp.getWidth(), h = comp.getHeight();
1712 w = (int) comp.getPreferredSize().getWidth();
1713 h = (int) comp.getPreferredSize().getHeight();
1720 renderGraduatedColor(comp, gcol, w, h);
1723 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1726 boolean thr = false;
1727 StringBuilder tt = new StringBuilder();
1728 StringBuilder tx = new StringBuilder();
1730 if (gcol.isColourByAttribute())
1732 tx.append(String.join(":", gcol.getAttributeName()));
1734 else if (!gcol.isColourByLabel())
1736 tx.append(MessageManager.getString("label.score"));
1739 if (gcol.isAboveThreshold())
1743 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1746 if (gcol.isBelowThreshold())
1750 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1753 if (gcol.isColourByLabel())
1755 tt.append("Coloured by label text. ").append(tt);
1760 if (!gcol.isColourByAttribute())
1768 Color newColor = gcol.getMaxColour();
1769 comp.setBackground(newColor);
1770 // System.err.println("Width is " + w / 2);
1771 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1772 comp.setIcon(ficon);
1773 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1774 // + newColor.getGreen() + ", " + newColor.getBlue()
1775 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1776 // + ", " + minCol.getBlue() + ")");
1778 comp.setHorizontalAlignment(SwingConstants.CENTER);
1779 comp.setText(tx.toString());
1780 if (tt.length() > 0)
1782 if (comp.getToolTipText() == null)
1784 comp.setToolTipText(tt.toString());
1788 comp.setToolTipText(
1789 tt.append(" ").append(comp.getToolTipText()).toString());
1794 class ColorEditor extends AbstractCellEditor
1795 implements TableCellEditor, ActionListener
1799 FeatureColourI currentColor;
1801 FeatureTypeSettings chooser;
1807 JColorChooser colorChooser;
1811 protected static final String EDIT = "edit";
1813 int rowSelected = 0;
1815 public ColorEditor(FeatureSettings me)
1818 // Set up the editor (from the table's point of view),
1819 // which is a button.
1820 // This button brings up the color chooser dialog,
1821 // which is the editor from the user's point of view.
1822 button = new JButton();
1823 button.setActionCommand(EDIT);
1824 button.addActionListener(this);
1825 button.setBorderPainted(false);
1826 // Set up the dialog that the button brings up.
1827 colorChooser = new JColorChooser();
1828 dialog = JColorChooser.createDialog(button,
1829 MessageManager.getString("label.select_colour"), true, // modal
1830 colorChooser, this, // OK button handler
1831 null); // no CANCEL button handler
1835 * Handles events from the editor button and from the dialog's OK button.
1838 public void actionPerformed(ActionEvent e)
1840 // todo test e.getSource() instead here
1841 if (EDIT.equals(e.getActionCommand()))
1843 // The user has clicked the cell, so
1844 // bring up the dialog.
1845 if (currentColor.isSimpleColour())
1847 // bring up simple color chooser
1848 button.setBackground(currentColor.getColour());
1849 colorChooser.setColor(currentColor.getColour());
1850 dialog.setVisible(true);
1854 // bring up graduated chooser.
1855 chooser = new FeatureTypeSettings(me.fr, type);
1856 chooser.setRequestFocusEnabled(true);
1857 chooser.requestFocus();
1858 chooser.addActionListener(this);
1859 chooser.showTab(true);
1861 // Make the renderer reappear.
1862 fireEditingStopped();
1867 if (currentColor.isSimpleColour())
1870 * read off colour picked in colour chooser after OK pressed
1872 currentColor = new FeatureColour(colorChooser.getColor());
1873 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1878 * after OK in variable colour dialog, any changes to colour
1879 * (or filters!) are already set in FeatureRenderer, so just
1880 * update table data without triggering updateFeatureRenderer
1882 currentColor = fr.getFeatureColours().get(type);
1883 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1884 if (currentFilter == null)
1886 currentFilter = new FeatureMatcherSet();
1888 Object[] data = ((FeatureTableModel) table.getModel())
1889 .getData()[rowSelected];
1890 data[COLOUR_COLUMN] = currentColor;
1891 data[FILTER_COLUMN] = currentFilter;
1893 fireEditingStopped();
1894 me.table.validate();
1898 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1900 public Object getCellEditorValue()
1902 return currentColor;
1905 // Implement the one method defined by TableCellEditor.
1907 public Component getTableCellEditorComponent(JTable theTable, Object value,
1908 boolean isSelected, int row, int column)
1910 currentColor = (FeatureColourI) value;
1911 this.rowSelected = row;
1912 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1913 button.setOpaque(true);
1914 button.setBackground(me.getBackground());
1915 if (!currentColor.isSimpleColour())
1917 JLabel btn = new JLabel();
1918 btn.setSize(button.getSize());
1919 FeatureSettings.renderGraduatedColor(btn, currentColor);
1920 button.setBackground(btn.getBackground());
1921 button.setIcon(btn.getIcon());
1922 button.setText(btn.getText());
1927 button.setIcon(null);
1928 button.setBackground(currentColor.getColour());
1935 * The cell editor for the Filter column. It displays the text of any filters
1936 * for the feature type in that row (in full as a tooltip, possible abbreviated
1937 * as display text). On click in the cell, opens the Feature Display Settings
1938 * dialog at the Filters tab.
1940 class FilterEditor extends AbstractCellEditor
1941 implements TableCellEditor, ActionListener
1945 FeatureMatcherSetI currentFilter;
1953 protected static final String EDIT = "edit";
1955 int rowSelected = 0;
1957 public FilterEditor(FeatureSettings me)
1960 button = new JButton();
1961 button.setActionCommand(EDIT);
1962 button.addActionListener(this);
1963 button.setBorderPainted(false);
1967 * Handles events from the editor button
1970 public void actionPerformed(ActionEvent e)
1972 if (button == e.getSource())
1974 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1975 chooser.addActionListener(this);
1976 chooser.setRequestFocusEnabled(true);
1977 chooser.requestFocus();
1978 if (lastLocation != null)
1980 // todo open at its last position on screen
1981 chooser.setBounds(lastLocation.x, lastLocation.y,
1982 chooser.getWidth(), chooser.getHeight());
1985 chooser.showTab(false);
1986 fireEditingStopped();
1988 else if (e.getSource() instanceof Component)
1992 * after OK in variable colour dialog, any changes to filter
1993 * (or colours!) are already set in FeatureRenderer, so just
1994 * update table data without triggering updateFeatureRenderer
1996 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1997 currentFilter = me.fr.getFeatureFilter(type);
1998 if (currentFilter == null)
2000 currentFilter = new FeatureMatcherSet();
2002 Object[] data = ((FeatureTableModel) table.getModel())
2003 .getData()[rowSelected];
2004 data[COLOUR_COLUMN] = currentColor;
2005 data[FILTER_COLUMN] = currentFilter;
2006 fireEditingStopped();
2007 me.table.validate();
2012 public Object getCellEditorValue()
2014 return currentFilter;
2018 public Component getTableCellEditorComponent(JTable theTable, Object value,
2019 boolean isSelected, int row, int column)
2021 currentFilter = (FeatureMatcherSetI) value;
2022 this.rowSelected = row;
2023 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2024 button.setOpaque(true);
2025 button.setBackground(me.getBackground());
2026 button.setText(currentFilter.toString());
2027 button.setToolTipText(currentFilter.toString());
2028 button.setIcon(null);
2034 class FeatureIcon implements Icon
2036 FeatureColourI gcol;
2040 boolean midspace = false;
2042 int width = 50, height = 20;
2044 int s1, e1; // start and end of midpoint band for thresholded symbol
2046 Color mpcolour = Color.white;
2048 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2068 public int getIconWidth()
2074 public int getIconHeight()
2080 public void paintIcon(Component c, Graphics g, int x, int y)
2083 if (gcol.isColourByLabel())
2086 g.fillRect(0, 0, width, height);
2087 // need an icon here.
2088 g.setColor(gcol.getMaxColour());
2090 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2092 // g.setFont(g.getFont().deriveFont(
2093 // AffineTransform.getScaleInstance(
2094 // width/g.getFontMetrics().stringWidth("Label"),
2095 // height/g.getFontMetrics().getHeight())));
2097 g.drawString(MessageManager.getString("label.label"), 0, 0);
2102 Color minCol = gcol.getMinColour();
2104 g.fillRect(0, 0, s1, height);
2107 g.setColor(Color.white);
2108 g.fillRect(s1, 0, e1 - s1, height);
2110 g.setColor(gcol.getMaxColour());
2111 g.fillRect(0, e1, width - e1, height);