2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.SequenceI;
27 import jalview.datamodel.features.FeatureMatcherI;
28 import jalview.datamodel.features.FeatureMatcherSet;
29 import jalview.datamodel.features.FeatureMatcherSetI;
30 import jalview.gui.Help.HelpId;
31 import jalview.io.JalviewFileChooser;
32 import jalview.io.JalviewFileView;
33 import jalview.schemabinding.version2.Filter;
34 import jalview.schemabinding.version2.JalviewUserColours;
35 import jalview.schemabinding.version2.MatcherSet;
36 import jalview.schemes.FeatureColour;
37 import jalview.util.MessageManager;
38 import jalview.util.Platform;
39 import jalview.viewmodel.AlignmentViewport;
40 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
41 import jalview.ws.DasSequenceFeatureFetcher;
42 import jalview.ws.dbsources.das.api.jalviewSourceI;
44 import java.awt.BorderLayout;
45 import java.awt.Color;
46 import java.awt.Component;
47 import java.awt.Dimension;
49 import java.awt.Graphics;
50 import java.awt.GridLayout;
51 import java.awt.Point;
52 import java.awt.Rectangle;
53 import java.awt.event.ActionEvent;
54 import java.awt.event.ActionListener;
55 import java.awt.event.ItemEvent;
56 import java.awt.event.ItemListener;
57 import java.awt.event.MouseAdapter;
58 import java.awt.event.MouseEvent;
59 import java.awt.event.MouseMotionAdapter;
60 import java.beans.PropertyChangeEvent;
61 import java.beans.PropertyChangeListener;
63 import java.io.FileInputStream;
64 import java.io.FileOutputStream;
65 import java.io.InputStreamReader;
66 import java.io.OutputStreamWriter;
67 import java.io.PrintWriter;
68 import java.util.Arrays;
69 import java.util.Comparator;
70 import java.util.HashMap;
71 import java.util.HashSet;
72 import java.util.Hashtable;
73 import java.util.Iterator;
74 import java.util.List;
77 import java.util.Vector;
79 import javax.help.HelpSetException;
80 import javax.swing.AbstractCellEditor;
81 import javax.swing.BorderFactory;
82 import javax.swing.Icon;
83 import javax.swing.JButton;
84 import javax.swing.JCheckBox;
85 import javax.swing.JCheckBoxMenuItem;
86 import javax.swing.JColorChooser;
87 import javax.swing.JDialog;
88 import javax.swing.JInternalFrame;
89 import javax.swing.JLabel;
90 import javax.swing.JLayeredPane;
91 import javax.swing.JMenuItem;
92 import javax.swing.JPanel;
93 import javax.swing.JPopupMenu;
94 import javax.swing.JScrollPane;
95 import javax.swing.JSlider;
96 import javax.swing.JTable;
97 import javax.swing.ListSelectionModel;
98 import javax.swing.SwingConstants;
99 import javax.swing.SwingUtilities;
100 import javax.swing.event.ChangeEvent;
101 import javax.swing.event.ChangeListener;
102 import javax.swing.table.AbstractTableModel;
103 import javax.swing.table.TableCellEditor;
104 import javax.swing.table.TableCellRenderer;
105 import javax.swing.table.TableColumn;
107 public class FeatureSettings extends JPanel
108 implements FeatureSettingsControllerI
110 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
111 .getString("label.sequence_feature_colours");
114 * column indices of fields in Feature Settings table
116 static final int TYPE_COLUMN = 0;
118 static final int COLOUR_COLUMN = 1;
120 static final int FILTER_COLUMN = 2;
122 static final int SHOW_COLUMN = 3;
124 private static final int COLUMN_COUNT = 4;
126 private static final int MIN_WIDTH = 400;
128 private static final int MIN_HEIGHT = 400;
130 DasSourceBrowser dassourceBrowser;
132 DasSequenceFeatureFetcher dasFeatureFetcher;
134 JPanel dasSettingsPane = new JPanel();
136 final FeatureRenderer fr;
138 public final AlignFrame af;
141 * 'original' fields hold settings to restore on Cancel
143 Object[][] originalData;
145 private float originalTransparency;
147 private Map<String, FeatureMatcherSetI> originalFilters;
149 final JInternalFrame frame;
151 JScrollPane scrollPane = new JScrollPane();
157 JSlider transparency = new JSlider();
160 * when true, constructor is still executing - so ignore UI events
162 protected volatile boolean inConstruction = true;
164 int selectedRow = -1;
166 JButton fetchDAS = new JButton();
168 JButton saveDAS = new JButton();
170 JButton cancelDAS = new JButton();
172 boolean resettingTable = false;
175 * true when Feature Settings are updating from feature renderer
177 private boolean handlingUpdate = false;
180 * holds {featureCount, totalExtent} for each feature type
182 Map<String, float[]> typeWidth = null;
189 public FeatureSettings(AlignFrame alignFrame)
191 this.af = alignFrame;
192 fr = af.getFeatureRenderer();
194 // save transparency for restore on Cancel
195 originalTransparency = fr.getTransparency();
196 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
197 transparency.setMaximum(100 - originalTransparencyAsPercent);
199 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
204 } catch (Exception ex)
206 ex.printStackTrace();
212 public String getToolTipText(MouseEvent e)
215 int column = table.columnAtPoint(e.getPoint());
219 tip = JvSwingUtils.wrapTooltip(true, MessageManager
220 .getString("label.feature_settings_click_drag"));
223 int row = table.rowAtPoint(e.getPoint());
224 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
227 ? MessageManager.getString("label.filters_tooltip")
236 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
237 table.setFont(new Font("Verdana", Font.PLAIN, 12));
239 // table.setDefaultRenderer(Color.class, new ColorRenderer());
240 // table.setDefaultEditor(Color.class, new ColorEditor(this));
242 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
243 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
245 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
246 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
248 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
249 new ColorRenderer(), new ColorEditor(this));
250 table.addColumn(colourColumn);
252 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
253 new FilterRenderer(), new FilterEditor(this));
254 table.addColumn(filterColumn);
256 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
258 table.addMouseListener(new MouseAdapter()
261 public void mousePressed(MouseEvent evt)
263 selectedRow = table.rowAtPoint(evt.getPoint());
264 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
265 if (evt.isPopupTrigger())
267 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
268 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
271 else if (evt.getClickCount() == 2)
273 boolean invertSelection = evt.isAltDown();
274 boolean toggleSelection = Platform.isControlDown(evt);
275 boolean extendSelection = evt.isShiftDown();
276 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
277 invertSelection, extendSelection, toggleSelection, type);
281 // isPopupTrigger fires on mouseReleased on Windows
283 public void mouseReleased(MouseEvent evt)
285 selectedRow = table.rowAtPoint(evt.getPoint());
286 if (evt.isPopupTrigger())
288 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
289 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
290 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
296 table.addMouseMotionListener(new MouseMotionAdapter()
299 public void mouseDragged(MouseEvent evt)
301 int newRow = table.rowAtPoint(evt.getPoint());
302 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
305 * reposition 'selectedRow' to 'newRow' (the dragged to location)
306 * this could be more than one row away for a very fast drag action
307 * so just swap it with adjacent rows until we get it there
309 Object[][] data = ((FeatureTableModel) table.getModel())
311 int direction = newRow < selectedRow ? -1 : 1;
312 for (int i = selectedRow; i != newRow; i += direction)
314 Object[] temp = data[i];
315 data[i] = data[i + direction];
316 data[i + direction] = temp;
318 updateFeatureRenderer(data);
320 selectedRow = newRow;
324 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
325 // MessageManager.getString("label.feature_settings_click_drag")));
326 scrollPane.setViewportView(table);
328 dassourceBrowser = new DasSourceBrowser(this);
329 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
331 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
333 fr.findAllFeatures(true); // display everything!
336 discoverAllFeatureData();
337 final PropertyChangeListener change;
338 final FeatureSettings fs = this;
339 fr.addPropertyChangeListener(change = new PropertyChangeListener()
342 public void propertyChange(PropertyChangeEvent evt)
344 if (!fs.resettingTable && !fs.handlingUpdate)
346 fs.handlingUpdate = true;
348 // new groups may be added with new sequence feature types only
349 fs.handlingUpdate = false;
355 frame = new JInternalFrame();
356 frame.setContentPane(this);
357 if (Platform.isAMac())
359 Desktop.addInternalFrame(frame,
360 MessageManager.getString("label.sequence_feature_settings"),
365 Desktop.addInternalFrame(frame,
366 MessageManager.getString("label.sequence_feature_settings"),
369 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
371 frame.addInternalFrameListener(
372 new javax.swing.event.InternalFrameAdapter()
375 public void internalFrameClosed(
376 javax.swing.event.InternalFrameEvent evt)
378 fr.removePropertyChangeListener(change);
379 dassourceBrowser.fs = null;
382 frame.setLayer(JLayeredPane.PALETTE_LAYER);
383 inConstruction = false;
386 protected void popupSort(final int rowSelected, final String type,
387 final Object typeCol, final Map<String, float[][]> minmax, int x,
390 final FeatureColourI featureColour = (FeatureColourI) typeCol;
392 JPopupMenu men = new JPopupMenu(MessageManager
393 .formatMessage("label.settings_for_param", new String[]
395 JMenuItem scr = new JMenuItem(
396 MessageManager.getString("label.sort_by_score"));
398 final FeatureSettings me = this;
399 scr.addActionListener(new ActionListener()
403 public void actionPerformed(ActionEvent e)
406 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
411 JMenuItem dens = new JMenuItem(
412 MessageManager.getString("label.sort_by_density"));
413 dens.addActionListener(new ActionListener()
417 public void actionPerformed(ActionEvent e)
420 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
428 * variable colour options include colour by label, by score,
429 * by selected attribute text, or attribute value
431 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
432 MessageManager.getString("label.variable_colour"));
433 mxcol.setSelected(!featureColour.isSimpleColour());
435 mxcol.addActionListener(new ActionListener()
437 JColorChooser colorChooser;
440 public void actionPerformed(ActionEvent e)
442 if (e.getSource() == mxcol)
444 if (featureColour.isSimpleColour())
446 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
447 fc.addActionListener(this);
451 // bring up simple color chooser
452 colorChooser = new JColorChooser();
453 String title = MessageManager
454 .getString("label.select_colour");
455 JDialog dialog = JColorChooser.createDialog(me,
456 title, true, // modal
457 colorChooser, this, // OK button handler
458 null); // no CANCEL button handler
459 colorChooser.setColor(featureColour.getMaxColour());
460 dialog.setVisible(true);
465 if (e.getSource() instanceof FeatureTypeSettings)
468 * update after OK in feature colour dialog; the updated
469 * colour will have already been set in the FeatureRenderer
471 FeatureColourI fci = fr.getFeatureColours().get(type);
472 table.setValueAt(fci, rowSelected, 1);
477 // probably the color chooser!
478 table.setValueAt(new FeatureColour(colorChooser.getColor()),
481 me.updateFeatureRenderer(
482 ((FeatureTableModel) table.getModel()).getData(),
490 JMenuItem selCols = new JMenuItem(
491 MessageManager.getString("label.select_columns_containing"));
492 selCols.addActionListener(new ActionListener()
495 public void actionPerformed(ActionEvent arg0)
497 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
501 JMenuItem clearCols = new JMenuItem(MessageManager
502 .getString("label.select_columns_not_containing"));
503 clearCols.addActionListener(new ActionListener()
506 public void actionPerformed(ActionEvent arg0)
508 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
512 JMenuItem hideCols = new JMenuItem(
513 MessageManager.getString("label.hide_columns_containing"));
514 hideCols.addActionListener(new ActionListener()
517 public void actionPerformed(ActionEvent arg0)
519 fr.ap.alignFrame.hideFeatureColumns(type, true);
522 JMenuItem hideOtherCols = new JMenuItem(
523 MessageManager.getString("label.hide_columns_not_containing"));
524 hideOtherCols.addActionListener(new ActionListener()
527 public void actionPerformed(ActionEvent arg0)
529 fr.ap.alignFrame.hideFeatureColumns(type, false);
535 men.add(hideOtherCols);
536 men.show(table, x, y);
540 synchronized public void discoverAllFeatureData()
542 Set<String> allGroups = new HashSet<>();
543 AlignmentI alignment = af.getViewport().getAlignment();
545 for (int i = 0; i < alignment.getHeight(); i++)
547 SequenceI seq = alignment.getSequenceAt(i);
548 for (String group : seq.getFeatures().getFeatureGroups(true))
550 if (group != null && !allGroups.contains(group))
552 allGroups.add(group);
553 checkGroupState(group);
564 * Synchronise gui group list and check visibility of group
567 * @return true if group is visible
569 private boolean checkGroupState(String group)
571 boolean visible = fr.checkGroupVisibility(group, true);
573 for (int g = 0; g < groupPanel.getComponentCount(); g++)
575 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
577 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
582 final String grp = group;
583 final JCheckBox check = new JCheckBox(group, visible);
584 check.setFont(new Font("Serif", Font.BOLD, 12));
585 check.setToolTipText(group);
586 check.addItemListener(new ItemListener()
589 public void itemStateChanged(ItemEvent evt)
591 fr.setGroupVisibility(check.getText(), check.isSelected());
592 resetTable(new String[] { grp });
593 af.alignPanel.paintAlignment(true, true);
596 groupPanel.add(check);
600 synchronized void resetTable(String[] groupChanged)
606 resettingTable = true;
607 typeWidth = new Hashtable<>();
608 // TODO: change avWidth calculation to 'per-sequence' average and use long
611 Set<String> displayableTypes = new HashSet<>();
612 Set<String> foundGroups = new HashSet<>();
615 * determine which feature types may be visible depending on
616 * which groups are selected, and recompute average width data
618 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
621 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
624 * get the sequence's groups for positional features
625 * and keep track of which groups are visible
627 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
628 Set<String> visibleGroups = new HashSet<>();
629 for (String group : groups)
631 if (group == null || checkGroupState(group))
633 visibleGroups.add(group);
636 foundGroups.addAll(groups);
639 * get distinct feature types for visible groups
640 * record distinct visible types, and their count and total length
642 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
643 visibleGroups.toArray(new String[visibleGroups.size()]));
644 for (String type : types)
646 displayableTypes.add(type);
647 float[] avWidth = typeWidth.get(type);
650 avWidth = new float[2];
651 typeWidth.put(type, avWidth);
653 // todo this could include features with a non-visible group
654 // - do we greatly care?
655 // todo should we include non-displayable features here, and only
656 // update when features are added?
657 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
658 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
662 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
665 if (fr.hasRenderOrder())
669 fr.findAllFeatures(groupChanged != null); // prod to update
670 // colourschemes. but don't
672 // First add the checks in the previous render order,
673 // in case the window has been closed and reopened
675 List<String> frl = fr.getRenderOrder();
676 for (int ro = frl.size() - 1; ro > -1; ro--)
678 String type = frl.get(ro);
680 if (!displayableTypes.contains(type))
685 data[dataIndex][TYPE_COLUMN] = type;
686 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
687 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
688 data[dataIndex][FILTER_COLUMN] = featureFilter == null
689 ? new FeatureMatcherSet()
691 data[dataIndex][SHOW_COLUMN] = new Boolean(
692 af.getViewport().getFeaturesDisplayed().isVisible(type));
694 displayableTypes.remove(type);
699 * process any extra features belonging only to
700 * a group which was just selected
702 while (!displayableTypes.isEmpty())
704 String type = displayableTypes.iterator().next();
705 data[dataIndex][TYPE_COLUMN] = type;
707 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
708 if (data[dataIndex][COLOUR_COLUMN] == null)
710 // "Colour has been updated in another view!!"
711 fr.clearRenderOrder();
714 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
715 data[dataIndex][FILTER_COLUMN] = featureFilter == null
716 ? new FeatureMatcherSet()
718 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
720 displayableTypes.remove(type);
723 if (originalData == null)
725 originalData = new Object[data.length][COLUMN_COUNT];
726 for (int i = 0; i < data.length; i++)
728 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
733 updateOriginalData(data);
736 table.setModel(new FeatureTableModel(data));
737 table.getColumnModel().getColumn(0).setPreferredWidth(200);
739 groupPanel.setLayout(
740 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
741 pruneGroups(foundGroups);
742 groupPanel.validate();
744 updateFeatureRenderer(data, groupChanged != null);
745 resettingTable = false;
749 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
750 * have been made outwith this dialog
752 * <li>a new feature type added (and made visible)</li>
753 * <li>a feature colour changed (in the Amend Features dialog)</li>
758 protected void updateOriginalData(Object[][] foundData)
760 // todo LinkedHashMap instead of Object[][] would be nice
762 Object[][] currentData = ((FeatureTableModel) table.getModel())
764 for (Object[] row : foundData)
766 String type = (String) row[TYPE_COLUMN];
767 boolean found = false;
768 for (Object[] current : currentData)
770 if (type.equals(current[TYPE_COLUMN]))
774 * currently dependent on object equality here;
775 * really need an equals method on FeatureColour
777 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
780 * feature colour has changed externally - update originalData
782 for (Object[] original : originalData)
784 if (type.equals(original[TYPE_COLUMN]))
786 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
797 * new feature detected - add to original data (on top)
799 Object[][] newData = new Object[originalData.length
801 for (int i = 0; i < originalData.length; i++)
803 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
807 originalData = newData;
813 * Remove from the groups panel any checkboxes for groups that are not in the
814 * foundGroups set. This enables removing a group from the display when the last
815 * feature in that group is deleted.
819 protected void pruneGroups(Set<String> foundGroups)
821 for (int g = 0; g < groupPanel.getComponentCount(); g++)
823 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
824 if (!foundGroups.contains(checkbox.getText()))
826 groupPanel.remove(checkbox);
832 * reorder data based on the featureRenderers global priority list.
836 private void ensureOrder(Object[][] data)
838 boolean sort = false;
839 float[] order = new float[data.length];
840 for (int i = 0; i < order.length; i++)
842 order[i] = fr.getOrder(data[i][0].toString());
845 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
849 sort = sort || order[i - 1] > order[i];
854 jalview.util.QuickSort.sort(order, data);
859 * Offers a file chooser dialog, and then loads the feature colours and
860 * filters from file in XML format and unmarshals to Jalview feature settings
864 JalviewFileChooser chooser = new JalviewFileChooser("fc",
865 SEQUENCE_FEATURE_COLOURS);
866 chooser.setFileView(new JalviewFileView());
867 chooser.setDialogTitle(
868 MessageManager.getString("label.load_feature_colours"));
869 chooser.setToolTipText(MessageManager.getString("action.load"));
871 int value = chooser.showOpenDialog(this);
873 if (value == JalviewFileChooser.APPROVE_OPTION)
875 File file = chooser.getSelectedFile();
881 * Loads feature colours and filters from XML stored in the given file
889 InputStreamReader in = new InputStreamReader(
890 new FileInputStream(file), "UTF-8");
892 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
895 * load feature colours
897 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
899 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
900 FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
901 fr.setColour(newcol.getName(), colour);
902 fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
906 * load feature filters; loaded filters will replace any that are
907 * currently defined, other defined filters are left unchanged
909 for (int i = 0; i < jucs.getFilterCount(); i++)
911 jalview.schemabinding.version2.Filter filterModel = jucs
913 String featureType = filterModel.getFeatureType();
914 FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
915 filterModel.getMatcherSet());
916 if (!filter.isEmpty())
918 fr.setFeatureFilter(featureType, filter);
923 * update feature settings table
928 Object[][] data = ((FeatureTableModel) table.getModel())
931 updateFeatureRenderer(data, false);
934 } catch (Exception ex)
936 System.out.println("Error loading User Colour File\n" + ex);
941 * Offers a file chooser dialog, and then saves the current feature colours
942 * and any filters to the selected file in XML format
946 JalviewFileChooser chooser = new JalviewFileChooser("fc",
947 SEQUENCE_FEATURE_COLOURS);
948 chooser.setFileView(new JalviewFileView());
949 chooser.setDialogTitle(
950 MessageManager.getString("label.save_feature_colours"));
951 chooser.setToolTipText(MessageManager.getString("action.save"));
953 int value = chooser.showSaveDialog(this);
955 if (value == JalviewFileChooser.APPROVE_OPTION)
957 save(chooser.getSelectedFile());
962 * Saves feature colours and filters to the given file
968 JalviewUserColours ucs = new JalviewUserColours();
969 ucs.setSchemeName("Sequence Features");
972 PrintWriter out = new PrintWriter(new OutputStreamWriter(
973 new FileOutputStream(file), "UTF-8"));
976 * sort feature types by colour order, from 0 (highest)
979 Set<String> fr_colours = fr.getAllFeatureColours();
980 String[] sortedTypes = fr_colours
981 .toArray(new String[fr_colours.size()]);
982 Arrays.sort(sortedTypes, new Comparator<String>()
985 public int compare(String type1, String type2)
987 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
992 * save feature colours
994 for (String featureType : sortedTypes)
996 FeatureColourI fcol = fr.getFeatureStyle(featureType);
997 jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
1003 * save any feature filters
1005 for (String featureType : sortedTypes)
1007 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1008 if (filter != null && !filter.isEmpty())
1010 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1011 FeatureMatcherI firstMatcher = iterator.next();
1012 MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
1014 Filter filterModel = new Filter();
1015 filterModel.setFeatureType(featureType);
1016 filterModel.setMatcherSet(ms);
1017 ucs.addFilter(filterModel);
1023 } catch (Exception ex)
1025 ex.printStackTrace();
1029 public void invertSelection()
1031 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1032 for (int i = 0; i < data.length; i++)
1034 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1036 updateFeatureRenderer(data, true);
1040 public void orderByAvWidth()
1042 if (table == null || table.getModel() == null)
1046 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1047 float[] width = new float[data.length];
1051 for (int i = 0; i < data.length; i++)
1053 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1056 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1057 // weight - but have to make per
1058 // sequence, too (awidth[2])
1059 // if (width[i]==1) // hack to distinguish single width sequences.
1070 boolean sort = false;
1071 for (int i = 0; i < width.length; i++)
1073 // awidth = (float[]) typeWidth.get(data[i][0]);
1076 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1079 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1085 width[i] /= max; // normalize
1086 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1090 sort = sort || width[i - 1] > width[i];
1095 jalview.util.QuickSort.sort(width, data);
1096 // update global priority order
1099 updateFeatureRenderer(data, false);
1107 frame.setClosed(true);
1108 } catch (Exception exe)
1114 public void updateFeatureRenderer(Object[][] data)
1116 updateFeatureRenderer(data, true);
1120 * Update the priority order of features; only repaint if this changed the order
1121 * of visible features
1126 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1128 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1130 if (fr.setFeaturePriority(rowData, visibleNew))
1132 af.alignPanel.paintAlignment(true, true);
1137 * Converts table data into an array of data beans
1139 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1141 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1142 for (int i = 0; i < data.length; i++)
1144 String type = (String) data[i][TYPE_COLUMN];
1145 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1146 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1147 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1148 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1154 private void jbInit() throws Exception
1156 this.setLayout(new BorderLayout());
1158 JPanel settingsPane = new JPanel();
1159 settingsPane.setLayout(new BorderLayout());
1161 dasSettingsPane.setLayout(new BorderLayout());
1163 JPanel bigPanel = new JPanel();
1164 bigPanel.setLayout(new BorderLayout());
1166 groupPanel = new JPanel();
1167 bigPanel.add(groupPanel, BorderLayout.NORTH);
1169 JButton invert = new JButton(
1170 MessageManager.getString("label.invert_selection"));
1171 invert.setFont(JvSwingUtils.getLabelFont());
1172 invert.addActionListener(new ActionListener()
1175 public void actionPerformed(ActionEvent e)
1181 JButton optimizeOrder = new JButton(
1182 MessageManager.getString("label.optimise_order"));
1183 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1184 optimizeOrder.addActionListener(new ActionListener()
1187 public void actionPerformed(ActionEvent e)
1193 JButton sortByScore = new JButton(
1194 MessageManager.getString("label.seq_sort_by_score"));
1195 sortByScore.setFont(JvSwingUtils.getLabelFont());
1196 sortByScore.addActionListener(new ActionListener()
1199 public void actionPerformed(ActionEvent e)
1201 af.avc.sortAlignmentByFeatureScore(null);
1204 JButton sortByDens = new JButton(
1205 MessageManager.getString("label.sequence_sort_by_density"));
1206 sortByDens.setFont(JvSwingUtils.getLabelFont());
1207 sortByDens.addActionListener(new ActionListener()
1210 public void actionPerformed(ActionEvent e)
1212 af.avc.sortAlignmentByFeatureDensity(null);
1216 JButton help = new JButton(MessageManager.getString("action.help"));
1217 help.setFont(JvSwingUtils.getLabelFont());
1218 help.addActionListener(new ActionListener()
1221 public void actionPerformed(ActionEvent e)
1225 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1226 } catch (HelpSetException e1)
1228 e1.printStackTrace();
1233 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1234 cancel.setFont(JvSwingUtils.getLabelFont());
1235 cancel.addActionListener(new ActionListener()
1238 public void actionPerformed(ActionEvent e)
1240 fr.setTransparency(originalTransparency);
1241 fr.setFeatureFilters(originalFilters);
1242 updateFeatureRenderer(originalData);
1247 JButton ok = new JButton(MessageManager.getString("action.ok"));
1248 ok.setFont(JvSwingUtils.getLabelFont());
1249 ok.addActionListener(new ActionListener()
1252 public void actionPerformed(ActionEvent e)
1258 JButton loadColours = new JButton(
1259 MessageManager.getString("label.load_colours"));
1260 loadColours.setFont(JvSwingUtils.getLabelFont());
1261 loadColours.setToolTipText(
1262 MessageManager.getString("label.load_colours_tooltip"));
1263 loadColours.addActionListener(new ActionListener()
1266 public void actionPerformed(ActionEvent e)
1272 JButton saveColours = new JButton(
1273 MessageManager.getString("label.save_colours"));
1274 saveColours.setFont(JvSwingUtils.getLabelFont());
1275 saveColours.setToolTipText(
1276 MessageManager.getString("label.save_colours_tooltip"));
1277 saveColours.addActionListener(new ActionListener()
1280 public void actionPerformed(ActionEvent e)
1285 transparency.addChangeListener(new ChangeListener()
1288 public void stateChanged(ChangeEvent evt)
1290 if (!inConstruction)
1292 fr.setTransparency((100 - transparency.getValue()) / 100f);
1293 af.alignPanel.paintAlignment(true, true);
1298 transparency.setMaximum(70);
1299 transparency.setToolTipText(
1300 MessageManager.getString("label.transparency_tip"));
1301 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1302 fetchDAS.addActionListener(new ActionListener()
1305 public void actionPerformed(ActionEvent e)
1307 fetchDAS_actionPerformed(e);
1310 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1311 saveDAS.addActionListener(new ActionListener()
1314 public void actionPerformed(ActionEvent e)
1316 saveDAS_actionPerformed(e);
1320 JPanel dasButtonPanel = new JPanel();
1321 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1322 dasSettingsPane.setBorder(null);
1323 cancelDAS.setEnabled(false);
1324 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1325 cancelDAS.addActionListener(new ActionListener()
1328 public void actionPerformed(ActionEvent e)
1330 cancelDAS_actionPerformed(e);
1334 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1335 bigPanel.add(transPanel, BorderLayout.SOUTH);
1337 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1338 transbuttons.add(optimizeOrder);
1339 transbuttons.add(invert);
1340 transbuttons.add(sortByScore);
1341 transbuttons.add(sortByDens);
1342 transbuttons.add(help);
1343 transPanel.add(transparency);
1344 transPanel.add(transbuttons);
1346 JPanel buttonPanel = new JPanel();
1347 buttonPanel.add(ok);
1348 buttonPanel.add(cancel);
1349 buttonPanel.add(loadColours);
1350 buttonPanel.add(saveColours);
1351 bigPanel.add(scrollPane, BorderLayout.CENTER);
1352 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1353 dasButtonPanel.add(fetchDAS);
1354 dasButtonPanel.add(cancelDAS);
1355 dasButtonPanel.add(saveDAS);
1356 settingsPane.add(bigPanel, BorderLayout.CENTER);
1357 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1358 this.add(settingsPane);
1361 public void fetchDAS_actionPerformed(ActionEvent e)
1363 fetchDAS.setEnabled(false);
1364 cancelDAS.setEnabled(true);
1365 dassourceBrowser.setGuiEnabled(false);
1366 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1367 .getSelectedSources();
1368 doDasFeatureFetch(selectedSources, true, true);
1372 * get the features from selectedSources for all or the current selection
1374 * @param selectedSources
1375 * @param checkDbRefs
1376 * @param promptFetchDbRefs
1378 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1379 boolean checkDbRefs, boolean promptFetchDbRefs)
1381 SequenceI[] dataset, seqs;
1383 AlignmentViewport vp = af.getViewport();
1384 if (vp.getSelectionGroup() != null
1385 && vp.getSelectionGroup().getSize() > 0)
1387 iSize = vp.getSelectionGroup().getSize();
1388 dataset = new SequenceI[iSize];
1389 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1393 iSize = vp.getAlignment().getHeight();
1394 seqs = vp.getAlignment().getSequencesArray();
1397 dataset = new SequenceI[iSize];
1398 for (int i = 0; i < iSize; i++)
1400 dataset[i] = seqs[i].getDatasetSequence();
1403 cancelDAS.setEnabled(true);
1404 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1405 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1406 af.getViewport().setShowSequenceFeatures(true);
1407 af.showSeqFeatures.setSelected(true);
1411 * blocking call to initialise the das source browser
1413 public void initDasSources()
1415 dassourceBrowser.initDasSources();
1419 * examine the current list of das sources and return any matching the given
1420 * nicknames in sources
1423 * Vector of Strings to resolve to DAS source nicknames.
1424 * @return sources that are present in source list.
1426 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1428 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1432 * get currently selected das sources. ensure you have called initDasSources
1433 * before calling this.
1435 * @return vector of selected das source nicknames
1437 public Vector<jalviewSourceI> getSelectedSources()
1439 return dassourceBrowser.getSelectedSources();
1443 * properly initialise DAS fetcher and then initiate a new thread to fetch
1444 * features from the named sources (rather than any turned on by default)
1448 * if true then runs in same thread, otherwise passes to the Swing
1451 public void fetchDasFeatures(Vector<String> sources, boolean block)
1454 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1455 .resolveSourceNicknames(sources);
1456 if (resolved.size() == 0)
1458 resolved = dassourceBrowser.getSelectedSources();
1460 if (resolved.size() > 0)
1462 final List<jalviewSourceI> dassources = resolved;
1463 fetchDAS.setEnabled(false);
1464 // cancelDAS.setEnabled(true); doDasFetch does this.
1465 Runnable fetcher = new Runnable()
1471 doDasFeatureFetch(dassources, true, false);
1481 SwingUtilities.invokeLater(fetcher);
1486 public void saveDAS_actionPerformed(ActionEvent e)
1489 .saveProperties(jalview.bin.Cache.applicationProperties);
1492 public void complete()
1494 fetchDAS.setEnabled(true);
1495 cancelDAS.setEnabled(false);
1496 dassourceBrowser.setGuiEnabled(true);
1500 public void cancelDAS_actionPerformed(ActionEvent e)
1502 if (dasFeatureFetcher != null)
1504 dasFeatureFetcher.cancel();
1509 public void noDasSourceActive()
1512 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1513 MessageManager.getString("label.no_das_sources_selected_warn"),
1514 MessageManager.getString("label.no_das_sources_selected_title"),
1515 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1518 // ///////////////////////////////////////////////////////////////////////
1519 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1520 // ///////////////////////////////////////////////////////////////////////
1521 class FeatureTableModel extends AbstractTableModel
1523 private String[] columnNames = {
1524 MessageManager.getString("label.feature_type"),
1525 MessageManager.getString("action.colour"),
1526 MessageManager.getString("label.filter"),
1527 MessageManager.getString("label.show") };
1529 private Object[][] data;
1531 FeatureTableModel(Object[][] data)
1536 public Object[][] getData()
1541 public void setData(Object[][] data)
1547 public int getColumnCount()
1549 return columnNames.length;
1552 public Object[] getRow(int row)
1558 public int getRowCount()
1564 public String getColumnName(int col)
1566 return columnNames[col];
1570 public Object getValueAt(int row, int col)
1572 return data[row][col];
1576 * Answers the class of the object in column c of the first row of the table
1579 public Class<?> getColumnClass(int c)
1581 Object v = getValueAt(0, c);
1582 return v == null ? null : v.getClass();
1586 public boolean isCellEditable(int row, int col)
1588 return col == 0 ? false : true;
1592 public void setValueAt(Object value, int row, int col)
1594 data[row][col] = value;
1595 fireTableCellUpdated(row, col);
1596 updateFeatureRenderer(data);
1601 class ColorRenderer extends JLabel implements TableCellRenderer
1603 javax.swing.border.Border unselectedBorder = null;
1605 javax.swing.border.Border selectedBorder = null;
1607 final String baseTT = "Click to edit, right/apple click for menu.";
1609 public ColorRenderer()
1611 setOpaque(true); // MUST do this for background to show up.
1612 setHorizontalTextPosition(SwingConstants.CENTER);
1613 setVerticalTextPosition(SwingConstants.CENTER);
1617 public Component getTableCellRendererComponent(JTable tbl, Object color,
1618 boolean isSelected, boolean hasFocus, int row, int column)
1620 FeatureColourI cellColour = (FeatureColourI) color;
1622 setToolTipText(baseTT);
1623 setBackground(tbl.getBackground());
1624 if (!cellColour.isSimpleColour())
1626 Rectangle cr = tbl.getCellRect(row, column, false);
1627 FeatureSettings.renderGraduatedColor(this, cellColour,
1628 (int) cr.getWidth(), (int) cr.getHeight());
1634 setBackground(cellColour.getColour());
1638 if (selectedBorder == null)
1640 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1641 tbl.getSelectionBackground());
1643 setBorder(selectedBorder);
1647 if (unselectedBorder == null)
1649 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1650 tbl.getBackground());
1652 setBorder(unselectedBorder);
1659 class FilterRenderer extends JLabel implements TableCellRenderer
1661 javax.swing.border.Border unselectedBorder = null;
1663 javax.swing.border.Border selectedBorder = null;
1665 public FilterRenderer()
1667 setOpaque(true); // MUST do this for background to show up.
1668 setHorizontalTextPosition(SwingConstants.CENTER);
1669 setVerticalTextPosition(SwingConstants.CENTER);
1673 public Component getTableCellRendererComponent(JTable tbl,
1674 Object filter, boolean isSelected, boolean hasFocus, int row,
1677 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1679 String asText = theFilter.toString();
1680 setBackground(tbl.getBackground());
1681 this.setText(asText);
1686 if (selectedBorder == null)
1688 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1689 tbl.getSelectionBackground());
1691 setBorder(selectedBorder);
1695 if (unselectedBorder == null)
1697 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1698 tbl.getBackground());
1700 setBorder(unselectedBorder);
1708 * update comp using rendering settings from gcol
1713 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1715 int w = comp.getWidth(), h = comp.getHeight();
1718 w = (int) comp.getPreferredSize().getWidth();
1719 h = (int) comp.getPreferredSize().getHeight();
1726 renderGraduatedColor(comp, gcol, w, h);
1729 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1732 boolean thr = false;
1733 StringBuilder tt = new StringBuilder();
1734 StringBuilder tx = new StringBuilder();
1736 if (gcol.isColourByAttribute())
1738 tx.append(String.join(":", gcol.getAttributeName()));
1740 else if (!gcol.isColourByLabel())
1742 tx.append(MessageManager.getString("label.score"));
1745 if (gcol.isAboveThreshold())
1749 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1752 if (gcol.isBelowThreshold())
1756 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1759 if (gcol.isColourByLabel())
1761 tt.append("Coloured by label text. ").append(tt);
1766 if (!gcol.isColourByAttribute())
1774 Color newColor = gcol.getMaxColour();
1775 comp.setBackground(newColor);
1776 // System.err.println("Width is " + w / 2);
1777 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1778 comp.setIcon(ficon);
1779 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1780 // + newColor.getGreen() + ", " + newColor.getBlue()
1781 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1782 // + ", " + minCol.getBlue() + ")");
1784 comp.setHorizontalAlignment(SwingConstants.CENTER);
1785 comp.setText(tx.toString());
1786 if (tt.length() > 0)
1788 if (comp.getToolTipText() == null)
1790 comp.setToolTipText(tt.toString());
1794 comp.setToolTipText(
1795 tt.append(" ").append(comp.getToolTipText()).toString());
1800 class ColorEditor extends AbstractCellEditor
1801 implements TableCellEditor, ActionListener
1805 FeatureColourI currentColor;
1807 FeatureTypeSettings chooser;
1813 JColorChooser colorChooser;
1817 protected static final String EDIT = "edit";
1819 int rowSelected = 0;
1821 public ColorEditor(FeatureSettings me)
1824 // Set up the editor (from the table's point of view),
1825 // which is a button.
1826 // This button brings up the color chooser dialog,
1827 // which is the editor from the user's point of view.
1828 button = new JButton();
1829 button.setActionCommand(EDIT);
1830 button.addActionListener(this);
1831 button.setBorderPainted(false);
1832 // Set up the dialog that the button brings up.
1833 colorChooser = new JColorChooser();
1834 dialog = JColorChooser.createDialog(button,
1835 MessageManager.getString("label.select_colour"), true, // modal
1836 colorChooser, this, // OK button handler
1837 null); // no CANCEL button handler
1841 * Handles events from the editor button and from the dialog's OK button.
1844 public void actionPerformed(ActionEvent e)
1846 // todo test e.getSource() instead here
1847 if (EDIT.equals(e.getActionCommand()))
1849 // The user has clicked the cell, so
1850 // bring up the dialog.
1851 if (currentColor.isSimpleColour())
1853 // bring up simple color chooser
1854 button.setBackground(currentColor.getColour());
1855 colorChooser.setColor(currentColor.getColour());
1856 dialog.setVisible(true);
1860 // bring up graduated chooser.
1861 chooser = new FeatureTypeSettings(me.fr, type);
1862 chooser.setRequestFocusEnabled(true);
1863 chooser.requestFocus();
1864 chooser.addActionListener(this);
1865 chooser.showTab(true);
1867 // Make the renderer reappear.
1868 fireEditingStopped();
1873 if (currentColor.isSimpleColour())
1876 * read off colour picked in colour chooser after OK pressed
1878 currentColor = new FeatureColour(colorChooser.getColor());
1879 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1884 * after OK in variable colour dialog, any changes to colour
1885 * (or filters!) are already set in FeatureRenderer, so just
1886 * update table data without triggering updateFeatureRenderer
1888 currentColor = fr.getFeatureColours().get(type);
1889 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1890 if (currentFilter == null)
1892 currentFilter = new FeatureMatcherSet();
1894 Object[] data = ((FeatureTableModel) table.getModel())
1895 .getData()[rowSelected];
1896 data[COLOUR_COLUMN] = currentColor;
1897 data[FILTER_COLUMN] = currentFilter;
1899 fireEditingStopped();
1900 me.table.validate();
1904 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1906 public Object getCellEditorValue()
1908 return currentColor;
1911 // Implement the one method defined by TableCellEditor.
1913 public Component getTableCellEditorComponent(JTable theTable, Object value,
1914 boolean isSelected, int row, int column)
1916 currentColor = (FeatureColourI) value;
1917 this.rowSelected = row;
1918 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1919 button.setOpaque(true);
1920 button.setBackground(me.getBackground());
1921 if (!currentColor.isSimpleColour())
1923 JLabel btn = new JLabel();
1924 btn.setSize(button.getSize());
1925 FeatureSettings.renderGraduatedColor(btn, currentColor);
1926 button.setBackground(btn.getBackground());
1927 button.setIcon(btn.getIcon());
1928 button.setText(btn.getText());
1933 button.setIcon(null);
1934 button.setBackground(currentColor.getColour());
1941 * The cell editor for the Filter column. It displays the text of any filters
1942 * for the feature type in that row (in full as a tooltip, possible abbreviated
1943 * as display text). On click in the cell, opens the Feature Display Settings
1944 * dialog at the Filters tab.
1946 class FilterEditor extends AbstractCellEditor
1947 implements TableCellEditor, ActionListener
1951 FeatureMatcherSetI currentFilter;
1959 protected static final String EDIT = "edit";
1961 int rowSelected = 0;
1963 public FilterEditor(FeatureSettings me)
1966 button = new JButton();
1967 button.setActionCommand(EDIT);
1968 button.addActionListener(this);
1969 button.setBorderPainted(false);
1973 * Handles events from the editor button
1976 public void actionPerformed(ActionEvent e)
1978 if (button == e.getSource())
1980 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1981 chooser.addActionListener(this);
1982 chooser.setRequestFocusEnabled(true);
1983 chooser.requestFocus();
1984 if (lastLocation != null)
1986 // todo open at its last position on screen
1987 chooser.setBounds(lastLocation.x, lastLocation.y,
1988 chooser.getWidth(), chooser.getHeight());
1991 chooser.showTab(false);
1992 fireEditingStopped();
1994 else if (e.getSource() instanceof Component)
1998 * after OK in variable colour dialog, any changes to filter
1999 * (or colours!) are already set in FeatureRenderer, so just
2000 * update table data without triggering updateFeatureRenderer
2002 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2003 currentFilter = me.fr.getFeatureFilter(type);
2004 if (currentFilter == null)
2006 currentFilter = new FeatureMatcherSet();
2008 Object[] data = ((FeatureTableModel) table.getModel())
2009 .getData()[rowSelected];
2010 data[COLOUR_COLUMN] = currentColor;
2011 data[FILTER_COLUMN] = currentFilter;
2012 fireEditingStopped();
2013 me.table.validate();
2018 public Object getCellEditorValue()
2020 return currentFilter;
2024 public Component getTableCellEditorComponent(JTable theTable, Object value,
2025 boolean isSelected, int row, int column)
2027 currentFilter = (FeatureMatcherSetI) value;
2028 this.rowSelected = row;
2029 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2030 button.setOpaque(true);
2031 button.setBackground(me.getBackground());
2032 button.setText(currentFilter.toString());
2033 button.setToolTipText(currentFilter.toString());
2034 button.setIcon(null);
2040 class FeatureIcon implements Icon
2042 FeatureColourI gcol;
2046 boolean midspace = false;
2048 int width = 50, height = 20;
2050 int s1, e1; // start and end of midpoint band for thresholded symbol
2052 Color mpcolour = Color.white;
2054 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2074 public int getIconWidth()
2080 public int getIconHeight()
2086 public void paintIcon(Component c, Graphics g, int x, int y)
2089 if (gcol.isColourByLabel())
2092 g.fillRect(0, 0, width, height);
2093 // need an icon here.
2094 g.setColor(gcol.getMaxColour());
2096 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2098 // g.setFont(g.getFont().deriveFont(
2099 // AffineTransform.getScaleInstance(
2100 // width/g.getFontMetrics().stringWidth("Label"),
2101 // height/g.getFontMetrics().getHeight())));
2103 g.drawString(MessageManager.getString("label.label"), 0, 0);
2108 Color minCol = gcol.getMinColour();
2110 g.fillRect(0, 0, s1, height);
2113 g.setColor(Color.white);
2114 g.fillRect(s1, 0, e1 - s1, height);
2116 g.setColor(gcol.getMaxColour());
2117 g.fillRect(0, e1, width - e1, height);