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();
1232 help.setFont(JvSwingUtils.getLabelFont());
1233 help.setText(MessageManager.getString("action.help"));
1234 help.addActionListener(new ActionListener()
1237 public void actionPerformed(ActionEvent e)
1241 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1242 } catch (HelpSetException e1)
1244 e1.printStackTrace();
1249 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1250 cancel.setFont(JvSwingUtils.getLabelFont());
1251 cancel.addActionListener(new ActionListener()
1254 public void actionPerformed(ActionEvent e)
1256 fr.setTransparency(originalTransparency);
1257 fr.setFeatureFilters(originalFilters);
1258 updateFeatureRenderer(originalData);
1263 JButton ok = new JButton(MessageManager.getString("action.ok"));
1264 ok.setFont(JvSwingUtils.getLabelFont());
1265 ok.addActionListener(new ActionListener()
1268 public void actionPerformed(ActionEvent e)
1274 JButton loadColours = new JButton(
1275 MessageManager.getString("label.load_colours"));
1276 loadColours.setFont(JvSwingUtils.getLabelFont());
1277 loadColours.setToolTipText(
1278 MessageManager.getString("label.load_colours_tooltip"));
1279 loadColours.addActionListener(new ActionListener()
1282 public void actionPerformed(ActionEvent e)
1288 JButton saveColours = new JButton(
1289 MessageManager.getString("label.save_colours"));
1290 saveColours.setFont(JvSwingUtils.getLabelFont());
1291 saveColours.setToolTipText(
1292 MessageManager.getString("label.save_colours_tooltip"));
1293 saveColours.addActionListener(new ActionListener()
1296 public void actionPerformed(ActionEvent e)
1301 transparency.addChangeListener(new ChangeListener()
1304 public void stateChanged(ChangeEvent evt)
1306 if (!inConstruction)
1308 fr.setTransparency((100 - transparency.getValue()) / 100f);
1309 af.alignPanel.paintAlignment(true, true);
1314 transparency.setMaximum(70);
1315 transparency.setToolTipText(
1316 MessageManager.getString("label.transparency_tip"));
1317 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1318 fetchDAS.addActionListener(new ActionListener()
1321 public void actionPerformed(ActionEvent e)
1323 fetchDAS_actionPerformed(e);
1326 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1327 saveDAS.addActionListener(new ActionListener()
1330 public void actionPerformed(ActionEvent e)
1332 saveDAS_actionPerformed(e);
1336 JPanel dasButtonPanel = new JPanel();
1337 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1338 dasSettingsPane.setBorder(null);
1339 cancelDAS.setEnabled(false);
1340 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1341 cancelDAS.addActionListener(new ActionListener()
1344 public void actionPerformed(ActionEvent e)
1346 cancelDAS_actionPerformed(e);
1350 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1351 bigPanel.add(transPanel, BorderLayout.SOUTH);
1353 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1354 transbuttons.add(optimizeOrder);
1355 transbuttons.add(invert);
1356 transbuttons.add(sortByScore);
1357 transbuttons.add(sortByDens);
1358 transbuttons.add(help);
1359 transPanel.add(transparency);
1360 transPanel.add(transbuttons);
1362 JPanel buttonPanel = new JPanel();
1363 buttonPanel.add(ok);
1364 buttonPanel.add(cancel);
1365 buttonPanel.add(loadColours);
1366 buttonPanel.add(saveColours);
1367 bigPanel.add(scrollPane, BorderLayout.CENTER);
1368 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1369 dasButtonPanel.add(fetchDAS);
1370 dasButtonPanel.add(cancelDAS);
1371 dasButtonPanel.add(saveDAS);
1372 settingsPane.add(bigPanel, BorderLayout.CENTER);
1373 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1374 this.add(settingsPane);
1377 public void fetchDAS_actionPerformed(ActionEvent e)
1379 fetchDAS.setEnabled(false);
1380 cancelDAS.setEnabled(true);
1381 dassourceBrowser.setGuiEnabled(false);
1382 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1383 .getSelectedSources();
1384 doDasFeatureFetch(selectedSources, true, true);
1388 * get the features from selectedSources for all or the current selection
1390 * @param selectedSources
1391 * @param checkDbRefs
1392 * @param promptFetchDbRefs
1394 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1395 boolean checkDbRefs, boolean promptFetchDbRefs)
1397 SequenceI[] dataset, seqs;
1399 AlignmentViewport vp = af.getViewport();
1400 if (vp.getSelectionGroup() != null
1401 && vp.getSelectionGroup().getSize() > 0)
1403 iSize = vp.getSelectionGroup().getSize();
1404 dataset = new SequenceI[iSize];
1405 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1409 iSize = vp.getAlignment().getHeight();
1410 seqs = vp.getAlignment().getSequencesArray();
1413 dataset = new SequenceI[iSize];
1414 for (int i = 0; i < iSize; i++)
1416 dataset[i] = seqs[i].getDatasetSequence();
1419 cancelDAS.setEnabled(true);
1420 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1421 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1422 af.getViewport().setShowSequenceFeatures(true);
1423 af.showSeqFeatures.setSelected(true);
1427 * blocking call to initialise the das source browser
1429 public void initDasSources()
1431 dassourceBrowser.initDasSources();
1435 * examine the current list of das sources and return any matching the given
1436 * nicknames in sources
1439 * Vector of Strings to resolve to DAS source nicknames.
1440 * @return sources that are present in source list.
1442 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1444 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1448 * get currently selected das sources. ensure you have called initDasSources
1449 * before calling this.
1451 * @return vector of selected das source nicknames
1453 public Vector<jalviewSourceI> getSelectedSources()
1455 return dassourceBrowser.getSelectedSources();
1459 * properly initialise DAS fetcher and then initiate a new thread to fetch
1460 * features from the named sources (rather than any turned on by default)
1464 * if true then runs in same thread, otherwise passes to the Swing
1467 public void fetchDasFeatures(Vector<String> sources, boolean block)
1470 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1471 .resolveSourceNicknames(sources);
1472 if (resolved.size() == 0)
1474 resolved = dassourceBrowser.getSelectedSources();
1476 if (resolved.size() > 0)
1478 final List<jalviewSourceI> dassources = resolved;
1479 fetchDAS.setEnabled(false);
1480 // cancelDAS.setEnabled(true); doDasFetch does this.
1481 Runnable fetcher = new Runnable()
1487 doDasFeatureFetch(dassources, true, false);
1497 SwingUtilities.invokeLater(fetcher);
1502 public void saveDAS_actionPerformed(ActionEvent e)
1505 .saveProperties(jalview.bin.Cache.applicationProperties);
1508 public void complete()
1510 fetchDAS.setEnabled(true);
1511 cancelDAS.setEnabled(false);
1512 dassourceBrowser.setGuiEnabled(true);
1516 public void cancelDAS_actionPerformed(ActionEvent e)
1518 if (dasFeatureFetcher != null)
1520 dasFeatureFetcher.cancel();
1525 public void noDasSourceActive()
1528 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1529 MessageManager.getString("label.no_das_sources_selected_warn"),
1530 MessageManager.getString("label.no_das_sources_selected_title"),
1531 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1534 // ///////////////////////////////////////////////////////////////////////
1535 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1536 // ///////////////////////////////////////////////////////////////////////
1537 class FeatureTableModel extends AbstractTableModel
1539 private String[] columnNames = {
1540 MessageManager.getString("label.feature_type"),
1541 MessageManager.getString("action.colour"),
1542 MessageManager.getString("label.filter"),
1543 MessageManager.getString("label.show") };
1545 private Object[][] data;
1547 FeatureTableModel(Object[][] data)
1552 public Object[][] getData()
1557 public void setData(Object[][] data)
1563 public int getColumnCount()
1565 return columnNames.length;
1568 public Object[] getRow(int row)
1574 public int getRowCount()
1580 public String getColumnName(int col)
1582 return columnNames[col];
1586 public Object getValueAt(int row, int col)
1588 return data[row][col];
1592 * Answers the class of the object in column c of the first row of the table
1595 public Class<?> getColumnClass(int c)
1597 Object v = getValueAt(0, c);
1598 return v == null ? null : v.getClass();
1602 public boolean isCellEditable(int row, int col)
1604 return col == 0 ? false : true;
1608 public void setValueAt(Object value, int row, int col)
1610 data[row][col] = value;
1611 fireTableCellUpdated(row, col);
1612 updateFeatureRenderer(data);
1617 class ColorRenderer extends JLabel implements TableCellRenderer
1619 javax.swing.border.Border unselectedBorder = null;
1621 javax.swing.border.Border selectedBorder = null;
1623 final String baseTT = "Click to edit, right/apple click for menu.";
1625 public ColorRenderer()
1627 setOpaque(true); // MUST do this for background to show up.
1628 setHorizontalTextPosition(SwingConstants.CENTER);
1629 setVerticalTextPosition(SwingConstants.CENTER);
1633 public Component getTableCellRendererComponent(JTable tbl, Object color,
1634 boolean isSelected, boolean hasFocus, int row, int column)
1636 FeatureColourI cellColour = (FeatureColourI) color;
1638 setToolTipText(baseTT);
1639 setBackground(tbl.getBackground());
1640 if (!cellColour.isSimpleColour())
1642 Rectangle cr = tbl.getCellRect(row, column, false);
1643 FeatureSettings.renderGraduatedColor(this, cellColour,
1644 (int) cr.getWidth(), (int) cr.getHeight());
1650 setBackground(cellColour.getColour());
1654 if (selectedBorder == null)
1656 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1657 tbl.getSelectionBackground());
1659 setBorder(selectedBorder);
1663 if (unselectedBorder == null)
1665 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1666 tbl.getBackground());
1668 setBorder(unselectedBorder);
1675 class FilterRenderer extends JLabel implements TableCellRenderer
1677 javax.swing.border.Border unselectedBorder = null;
1679 javax.swing.border.Border selectedBorder = null;
1681 public FilterRenderer()
1683 setOpaque(true); // MUST do this for background to show up.
1684 setHorizontalTextPosition(SwingConstants.CENTER);
1685 setVerticalTextPosition(SwingConstants.CENTER);
1689 public Component getTableCellRendererComponent(JTable tbl,
1690 Object filter, boolean isSelected, boolean hasFocus, int row,
1693 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1695 String asText = theFilter.toString();
1696 setBackground(tbl.getBackground());
1697 this.setText(asText);
1702 if (selectedBorder == null)
1704 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1705 tbl.getSelectionBackground());
1707 setBorder(selectedBorder);
1711 if (unselectedBorder == null)
1713 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1714 tbl.getBackground());
1716 setBorder(unselectedBorder);
1724 * update comp using rendering settings from gcol
1729 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1731 int w = comp.getWidth(), h = comp.getHeight();
1734 w = (int) comp.getPreferredSize().getWidth();
1735 h = (int) comp.getPreferredSize().getHeight();
1742 renderGraduatedColor(comp, gcol, w, h);
1745 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1748 boolean thr = false;
1749 StringBuilder tt = new StringBuilder();
1750 StringBuilder tx = new StringBuilder();
1752 if (gcol.isColourByAttribute())
1754 tx.append(String.join(":", gcol.getAttributeName()));
1756 else if (!gcol.isColourByLabel())
1758 tx.append(MessageManager.getString("label.score"));
1761 if (gcol.isAboveThreshold())
1765 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1768 if (gcol.isBelowThreshold())
1772 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1775 if (gcol.isColourByLabel())
1777 tt.append("Coloured by label text. ").append(tt);
1782 if (!gcol.isColourByAttribute())
1790 Color newColor = gcol.getMaxColour();
1791 comp.setBackground(newColor);
1792 // System.err.println("Width is " + w / 2);
1793 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1794 comp.setIcon(ficon);
1795 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1796 // + newColor.getGreen() + ", " + newColor.getBlue()
1797 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1798 // + ", " + minCol.getBlue() + ")");
1800 comp.setHorizontalAlignment(SwingConstants.CENTER);
1801 comp.setText(tx.toString());
1802 if (tt.length() > 0)
1804 if (comp.getToolTipText() == null)
1806 comp.setToolTipText(tt.toString());
1810 comp.setToolTipText(
1811 tt.append(" ").append(comp.getToolTipText()).toString());
1816 class ColorEditor extends AbstractCellEditor
1817 implements TableCellEditor, ActionListener
1821 FeatureColourI currentColor;
1823 FeatureTypeSettings chooser;
1829 JColorChooser colorChooser;
1833 protected static final String EDIT = "edit";
1835 int rowSelected = 0;
1837 public ColorEditor(FeatureSettings me)
1840 // Set up the editor (from the table's point of view),
1841 // which is a button.
1842 // This button brings up the color chooser dialog,
1843 // which is the editor from the user's point of view.
1844 button = new JButton();
1845 button.setActionCommand(EDIT);
1846 button.addActionListener(this);
1847 button.setBorderPainted(false);
1848 // Set up the dialog that the button brings up.
1849 colorChooser = new JColorChooser();
1850 dialog = JColorChooser.createDialog(button,
1851 MessageManager.getString("label.select_colour"), true, // modal
1852 colorChooser, this, // OK button handler
1853 null); // no CANCEL button handler
1857 * Handles events from the editor button and from the dialog's OK button.
1860 public void actionPerformed(ActionEvent e)
1862 // todo test e.getSource() instead here
1863 if (EDIT.equals(e.getActionCommand()))
1865 // The user has clicked the cell, so
1866 // bring up the dialog.
1867 if (currentColor.isSimpleColour())
1869 // bring up simple color chooser
1870 button.setBackground(currentColor.getColour());
1871 colorChooser.setColor(currentColor.getColour());
1872 dialog.setVisible(true);
1876 // bring up graduated chooser.
1877 chooser = new FeatureTypeSettings(me.fr, type);
1878 chooser.setRequestFocusEnabled(true);
1879 chooser.requestFocus();
1880 chooser.addActionListener(this);
1881 chooser.showTab(true);
1883 // Make the renderer reappear.
1884 fireEditingStopped();
1889 if (currentColor.isSimpleColour())
1892 * read off colour picked in colour chooser after OK pressed
1894 currentColor = new FeatureColour(colorChooser.getColor());
1895 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1900 * after OK in variable colour dialog, any changes to colour
1901 * (or filters!) are already set in FeatureRenderer, so just
1902 * update table data without triggering updateFeatureRenderer
1904 currentColor = fr.getFeatureColours().get(type);
1905 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1906 if (currentFilter == null)
1908 currentFilter = new FeatureMatcherSet();
1910 Object[] data = ((FeatureTableModel) table.getModel())
1911 .getData()[rowSelected];
1912 data[COLOUR_COLUMN] = currentColor;
1913 data[FILTER_COLUMN] = currentFilter;
1915 fireEditingStopped();
1916 me.table.validate();
1920 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1922 public Object getCellEditorValue()
1924 return currentColor;
1927 // Implement the one method defined by TableCellEditor.
1929 public Component getTableCellEditorComponent(JTable theTable, Object value,
1930 boolean isSelected, int row, int column)
1932 currentColor = (FeatureColourI) value;
1933 this.rowSelected = row;
1934 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1935 button.setOpaque(true);
1936 button.setBackground(me.getBackground());
1937 if (!currentColor.isSimpleColour())
1939 JLabel btn = new JLabel();
1940 btn.setSize(button.getSize());
1941 FeatureSettings.renderGraduatedColor(btn, currentColor);
1942 button.setBackground(btn.getBackground());
1943 button.setIcon(btn.getIcon());
1944 button.setText(btn.getText());
1949 button.setIcon(null);
1950 button.setBackground(currentColor.getColour());
1957 * The cell editor for the Filter column. It displays the text of any filters
1958 * for the feature type in that row (in full as a tooltip, possible abbreviated
1959 * as display text). On click in the cell, opens the Feature Display Settings
1960 * dialog at the Filters tab.
1962 class FilterEditor extends AbstractCellEditor
1963 implements TableCellEditor, ActionListener
1967 FeatureMatcherSetI currentFilter;
1975 protected static final String EDIT = "edit";
1977 int rowSelected = 0;
1979 public FilterEditor(FeatureSettings me)
1982 button = new JButton();
1983 button.setActionCommand(EDIT);
1984 button.addActionListener(this);
1985 button.setBorderPainted(false);
1989 * Handles events from the editor button
1992 public void actionPerformed(ActionEvent e)
1994 if (button == e.getSource())
1996 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1997 chooser.addActionListener(this);
1998 chooser.setRequestFocusEnabled(true);
1999 chooser.requestFocus();
2000 if (lastLocation != null)
2002 // todo open at its last position on screen
2003 chooser.setBounds(lastLocation.x, lastLocation.y,
2004 chooser.getWidth(), chooser.getHeight());
2007 chooser.showTab(false);
2008 fireEditingStopped();
2010 else if (e.getSource() instanceof Component)
2014 * after OK in variable colour dialog, any changes to filter
2015 * (or colours!) are already set in FeatureRenderer, so just
2016 * update table data without triggering updateFeatureRenderer
2018 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2019 currentFilter = me.fr.getFeatureFilter(type);
2020 if (currentFilter == null)
2022 currentFilter = new FeatureMatcherSet();
2024 Object[] data = ((FeatureTableModel) table.getModel())
2025 .getData()[rowSelected];
2026 data[COLOUR_COLUMN] = currentColor;
2027 data[FILTER_COLUMN] = currentFilter;
2028 fireEditingStopped();
2029 me.table.validate();
2034 public Object getCellEditorValue()
2036 return currentFilter;
2040 public Component getTableCellEditorComponent(JTable theTable, Object value,
2041 boolean isSelected, int row, int column)
2043 currentFilter = (FeatureMatcherSetI) value;
2044 this.rowSelected = row;
2045 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2046 button.setOpaque(true);
2047 button.setBackground(me.getBackground());
2048 button.setText(currentFilter.toString());
2049 button.setToolTipText(currentFilter.toString());
2050 button.setIcon(null);
2056 class FeatureIcon implements Icon
2058 FeatureColourI gcol;
2062 boolean midspace = false;
2064 int width = 50, height = 20;
2066 int s1, e1; // start and end of midpoint band for thresholded symbol
2068 Color mpcolour = Color.white;
2070 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2090 public int getIconWidth()
2096 public int getIconHeight()
2102 public void paintIcon(Component c, Graphics g, int x, int y)
2105 if (gcol.isColourByLabel())
2108 g.fillRect(0, 0, width, height);
2109 // need an icon here.
2110 g.setColor(gcol.getMaxColour());
2112 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2114 // g.setFont(g.getFont().deriveFont(
2115 // AffineTransform.getScaleInstance(
2116 // width/g.getFontMetrics().stringWidth("Label"),
2117 // height/g.getFontMetrics().getHeight())));
2119 g.drawString(MessageManager.getString("label.label"), 0, 0);
2124 Color minCol = gcol.getMinColour();
2126 g.fillRect(0, 0, s1, height);
2129 g.setColor(Color.white);
2130 g.fillRect(s1, 0, e1 - s1, height);
2132 g.setColor(gcol.getMaxColour());
2133 g.fillRect(0, e1, width - e1, height);