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 for (int i = 0; i < table.getRowCount(); i++)
1033 Boolean value = (Boolean) table.getValueAt(i, SHOW_COLUMN);
1035 table.setValueAt(new Boolean(!value.booleanValue()), i, SHOW_COLUMN);
1039 public void orderByAvWidth()
1041 if (table == null || table.getModel() == null)
1045 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1046 float[] width = new float[data.length];
1050 for (int i = 0; i < data.length; i++)
1052 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1055 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1056 // weight - but have to make per
1057 // sequence, too (awidth[2])
1058 // if (width[i]==1) // hack to distinguish single width sequences.
1069 boolean sort = false;
1070 for (int i = 0; i < width.length; i++)
1072 // awidth = (float[]) typeWidth.get(data[i][0]);
1075 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1078 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1084 width[i] /= max; // normalize
1085 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1089 sort = sort || width[i - 1] > width[i];
1094 jalview.util.QuickSort.sort(width, data);
1095 // update global priority order
1098 updateFeatureRenderer(data, false);
1106 frame.setClosed(true);
1107 } catch (Exception exe)
1113 public void updateFeatureRenderer(Object[][] data)
1115 updateFeatureRenderer(data, true);
1119 * Update the priority order of features; only repaint if this changed the order
1120 * of visible features
1125 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1127 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1129 if (fr.setFeaturePriority(rowData, visibleNew))
1131 af.alignPanel.paintAlignment(true, true);
1136 * Converts table data into an array of data beans
1138 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1140 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1141 for (int i = 0; i < data.length; i++)
1143 String type = (String) data[i][TYPE_COLUMN];
1144 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1145 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1146 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1147 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1153 private void jbInit() throws Exception
1155 this.setLayout(new BorderLayout());
1157 JPanel settingsPane = new JPanel();
1158 settingsPane.setLayout(new BorderLayout());
1160 dasSettingsPane.setLayout(new BorderLayout());
1162 JPanel bigPanel = new JPanel();
1163 bigPanel.setLayout(new BorderLayout());
1165 groupPanel = new JPanel();
1166 bigPanel.add(groupPanel, BorderLayout.NORTH);
1168 JButton invert = new JButton(
1169 MessageManager.getString("label.invert_selection"));
1170 invert.setFont(JvSwingUtils.getLabelFont());
1171 invert.addActionListener(new ActionListener()
1174 public void actionPerformed(ActionEvent e)
1180 JButton optimizeOrder = new JButton(
1181 MessageManager.getString("label.optimise_order"));
1182 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1183 optimizeOrder.addActionListener(new ActionListener()
1186 public void actionPerformed(ActionEvent e)
1192 JButton sortByScore = new JButton(
1193 MessageManager.getString("label.seq_sort_by_score"));
1194 sortByScore.setFont(JvSwingUtils.getLabelFont());
1195 sortByScore.addActionListener(new ActionListener()
1198 public void actionPerformed(ActionEvent e)
1200 af.avc.sortAlignmentByFeatureScore(null);
1203 JButton sortByDens = new JButton(
1204 MessageManager.getString("label.sequence_sort_by_density"));
1205 sortByDens.setFont(JvSwingUtils.getLabelFont());
1206 sortByDens.addActionListener(new ActionListener()
1209 public void actionPerformed(ActionEvent e)
1211 af.avc.sortAlignmentByFeatureDensity(null);
1215 JButton help = new JButton(MessageManager.getString("action.help"));
1216 help.setFont(JvSwingUtils.getLabelFont());
1217 help.addActionListener(new ActionListener()
1220 public void actionPerformed(ActionEvent e)
1224 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1225 } catch (HelpSetException e1)
1227 e1.printStackTrace();
1231 help.setFont(JvSwingUtils.getLabelFont());
1232 help.setText(MessageManager.getString("action.help"));
1233 help.addActionListener(new ActionListener()
1236 public void actionPerformed(ActionEvent e)
1240 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1241 } catch (HelpSetException e1)
1243 e1.printStackTrace();
1248 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1249 cancel.setFont(JvSwingUtils.getLabelFont());
1250 cancel.addActionListener(new ActionListener()
1253 public void actionPerformed(ActionEvent e)
1255 fr.setTransparency(originalTransparency);
1256 fr.setFeatureFilters(originalFilters);
1257 updateFeatureRenderer(originalData);
1262 JButton ok = new JButton(MessageManager.getString("action.ok"));
1263 ok.setFont(JvSwingUtils.getLabelFont());
1264 ok.addActionListener(new ActionListener()
1267 public void actionPerformed(ActionEvent e)
1273 JButton loadColours = new JButton(
1274 MessageManager.getString("label.load_colours"));
1275 loadColours.setFont(JvSwingUtils.getLabelFont());
1276 loadColours.setToolTipText(
1277 MessageManager.getString("label.load_colours_tooltip"));
1278 loadColours.addActionListener(new ActionListener()
1281 public void actionPerformed(ActionEvent e)
1287 JButton saveColours = new JButton(
1288 MessageManager.getString("label.save_colours"));
1289 saveColours.setFont(JvSwingUtils.getLabelFont());
1290 saveColours.setToolTipText(
1291 MessageManager.getString("label.save_colours_tooltip"));
1292 saveColours.addActionListener(new ActionListener()
1295 public void actionPerformed(ActionEvent e)
1300 transparency.addChangeListener(new ChangeListener()
1303 public void stateChanged(ChangeEvent evt)
1305 if (!inConstruction)
1307 fr.setTransparency((100 - transparency.getValue()) / 100f);
1308 af.alignPanel.paintAlignment(true, true);
1313 transparency.setMaximum(70);
1314 transparency.setToolTipText(
1315 MessageManager.getString("label.transparency_tip"));
1316 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1317 fetchDAS.addActionListener(new ActionListener()
1320 public void actionPerformed(ActionEvent e)
1322 fetchDAS_actionPerformed(e);
1325 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1326 saveDAS.addActionListener(new ActionListener()
1329 public void actionPerformed(ActionEvent e)
1331 saveDAS_actionPerformed(e);
1335 JPanel dasButtonPanel = new JPanel();
1336 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1337 dasSettingsPane.setBorder(null);
1338 cancelDAS.setEnabled(false);
1339 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1340 cancelDAS.addActionListener(new ActionListener()
1343 public void actionPerformed(ActionEvent e)
1345 cancelDAS_actionPerformed(e);
1349 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1350 bigPanel.add(transPanel, BorderLayout.SOUTH);
1352 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1353 transbuttons.add(optimizeOrder);
1354 transbuttons.add(invert);
1355 transbuttons.add(sortByScore);
1356 transbuttons.add(sortByDens);
1357 transbuttons.add(help);
1358 transPanel.add(transparency);
1359 transPanel.add(transbuttons);
1361 JPanel buttonPanel = new JPanel();
1362 buttonPanel.add(ok);
1363 buttonPanel.add(cancel);
1364 buttonPanel.add(loadColours);
1365 buttonPanel.add(saveColours);
1366 bigPanel.add(scrollPane, BorderLayout.CENTER);
1367 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1368 dasButtonPanel.add(fetchDAS);
1369 dasButtonPanel.add(cancelDAS);
1370 dasButtonPanel.add(saveDAS);
1371 settingsPane.add(bigPanel, BorderLayout.CENTER);
1372 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1373 this.add(settingsPane);
1376 public void fetchDAS_actionPerformed(ActionEvent e)
1378 fetchDAS.setEnabled(false);
1379 cancelDAS.setEnabled(true);
1380 dassourceBrowser.setGuiEnabled(false);
1381 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1382 .getSelectedSources();
1383 doDasFeatureFetch(selectedSources, true, true);
1387 * get the features from selectedSources for all or the current selection
1389 * @param selectedSources
1390 * @param checkDbRefs
1391 * @param promptFetchDbRefs
1393 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1394 boolean checkDbRefs, boolean promptFetchDbRefs)
1396 SequenceI[] dataset, seqs;
1398 AlignmentViewport vp = af.getViewport();
1399 if (vp.getSelectionGroup() != null
1400 && vp.getSelectionGroup().getSize() > 0)
1402 iSize = vp.getSelectionGroup().getSize();
1403 dataset = new SequenceI[iSize];
1404 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1408 iSize = vp.getAlignment().getHeight();
1409 seqs = vp.getAlignment().getSequencesArray();
1412 dataset = new SequenceI[iSize];
1413 for (int i = 0; i < iSize; i++)
1415 dataset[i] = seqs[i].getDatasetSequence();
1418 cancelDAS.setEnabled(true);
1419 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1420 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1421 af.getViewport().setShowSequenceFeatures(true);
1422 af.showSeqFeatures.setSelected(true);
1426 * blocking call to initialise the das source browser
1428 public void initDasSources()
1430 dassourceBrowser.initDasSources();
1434 * examine the current list of das sources and return any matching the given
1435 * nicknames in sources
1438 * Vector of Strings to resolve to DAS source nicknames.
1439 * @return sources that are present in source list.
1441 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1443 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1447 * get currently selected das sources. ensure you have called initDasSources
1448 * before calling this.
1450 * @return vector of selected das source nicknames
1452 public Vector<jalviewSourceI> getSelectedSources()
1454 return dassourceBrowser.getSelectedSources();
1458 * properly initialise DAS fetcher and then initiate a new thread to fetch
1459 * features from the named sources (rather than any turned on by default)
1463 * if true then runs in same thread, otherwise passes to the Swing
1466 public void fetchDasFeatures(Vector<String> sources, boolean block)
1469 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1470 .resolveSourceNicknames(sources);
1471 if (resolved.size() == 0)
1473 resolved = dassourceBrowser.getSelectedSources();
1475 if (resolved.size() > 0)
1477 final List<jalviewSourceI> dassources = resolved;
1478 fetchDAS.setEnabled(false);
1479 // cancelDAS.setEnabled(true); doDasFetch does this.
1480 Runnable fetcher = new Runnable()
1486 doDasFeatureFetch(dassources, true, false);
1496 SwingUtilities.invokeLater(fetcher);
1501 public void saveDAS_actionPerformed(ActionEvent e)
1504 .saveProperties(jalview.bin.Cache.applicationProperties);
1507 public void complete()
1509 fetchDAS.setEnabled(true);
1510 cancelDAS.setEnabled(false);
1511 dassourceBrowser.setGuiEnabled(true);
1515 public void cancelDAS_actionPerformed(ActionEvent e)
1517 if (dasFeatureFetcher != null)
1519 dasFeatureFetcher.cancel();
1524 public void noDasSourceActive()
1527 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1528 MessageManager.getString("label.no_das_sources_selected_warn"),
1529 MessageManager.getString("label.no_das_sources_selected_title"),
1530 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1533 // ///////////////////////////////////////////////////////////////////////
1534 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1535 // ///////////////////////////////////////////////////////////////////////
1536 class FeatureTableModel extends AbstractTableModel
1538 private String[] columnNames = {
1539 MessageManager.getString("label.feature_type"),
1540 MessageManager.getString("action.colour"),
1541 MessageManager.getString("label.filter"),
1542 MessageManager.getString("label.show") };
1544 private Object[][] data;
1546 FeatureTableModel(Object[][] data)
1551 public Object[][] getData()
1556 public void setData(Object[][] data)
1562 public int getColumnCount()
1564 return columnNames.length;
1567 public Object[] getRow(int row)
1573 public int getRowCount()
1579 public String getColumnName(int col)
1581 return columnNames[col];
1585 public Object getValueAt(int row, int col)
1587 return data[row][col];
1591 * Answers the class of the object in column c of the first row of the table
1594 public Class<?> getColumnClass(int c)
1596 Object v = getValueAt(0, c);
1597 return v == null ? null : v.getClass();
1601 public boolean isCellEditable(int row, int col)
1603 return col == 0 ? false : true;
1607 public void setValueAt(Object value, int row, int col)
1609 data[row][col] = value;
1610 fireTableCellUpdated(row, col);
1611 updateFeatureRenderer(data);
1616 class ColorRenderer extends JLabel implements TableCellRenderer
1618 javax.swing.border.Border unselectedBorder = null;
1620 javax.swing.border.Border selectedBorder = null;
1622 final String baseTT = "Click to edit, right/apple click for menu.";
1624 public ColorRenderer()
1626 setOpaque(true); // MUST do this for background to show up.
1627 setHorizontalTextPosition(SwingConstants.CENTER);
1628 setVerticalTextPosition(SwingConstants.CENTER);
1632 public Component getTableCellRendererComponent(JTable tbl, Object color,
1633 boolean isSelected, boolean hasFocus, int row, int column)
1635 FeatureColourI cellColour = (FeatureColourI) color;
1637 setToolTipText(baseTT);
1638 setBackground(tbl.getBackground());
1639 if (!cellColour.isSimpleColour())
1641 Rectangle cr = tbl.getCellRect(row, column, false);
1642 FeatureSettings.renderGraduatedColor(this, cellColour,
1643 (int) cr.getWidth(), (int) cr.getHeight());
1649 setBackground(cellColour.getColour());
1653 if (selectedBorder == null)
1655 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1656 tbl.getSelectionBackground());
1658 setBorder(selectedBorder);
1662 if (unselectedBorder == null)
1664 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1665 tbl.getBackground());
1667 setBorder(unselectedBorder);
1674 class FilterRenderer extends JLabel implements TableCellRenderer
1676 javax.swing.border.Border unselectedBorder = null;
1678 javax.swing.border.Border selectedBorder = null;
1680 public FilterRenderer()
1682 setOpaque(true); // MUST do this for background to show up.
1683 setHorizontalTextPosition(SwingConstants.CENTER);
1684 setVerticalTextPosition(SwingConstants.CENTER);
1688 public Component getTableCellRendererComponent(JTable tbl,
1689 Object filter, boolean isSelected, boolean hasFocus, int row,
1692 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1694 String asText = theFilter.toString();
1695 setBackground(tbl.getBackground());
1696 this.setText(asText);
1701 if (selectedBorder == null)
1703 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1704 tbl.getSelectionBackground());
1706 setBorder(selectedBorder);
1710 if (unselectedBorder == null)
1712 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1713 tbl.getBackground());
1715 setBorder(unselectedBorder);
1723 * update comp using rendering settings from gcol
1728 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1730 int w = comp.getWidth(), h = comp.getHeight();
1733 w = (int) comp.getPreferredSize().getWidth();
1734 h = (int) comp.getPreferredSize().getHeight();
1741 renderGraduatedColor(comp, gcol, w, h);
1744 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1747 boolean thr = false;
1748 StringBuilder tt = new StringBuilder();
1749 StringBuilder tx = new StringBuilder();
1751 if (gcol.isColourByAttribute())
1753 tx.append(String.join(":", gcol.getAttributeName()));
1755 else if (!gcol.isColourByLabel())
1757 tx.append(MessageManager.getString("label.score"));
1760 if (gcol.isAboveThreshold())
1764 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1767 if (gcol.isBelowThreshold())
1771 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1774 if (gcol.isColourByLabel())
1776 tt.append("Coloured by label text. ").append(tt);
1781 if (!gcol.isColourByAttribute())
1789 Color newColor = gcol.getMaxColour();
1790 comp.setBackground(newColor);
1791 // System.err.println("Width is " + w / 2);
1792 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1793 comp.setIcon(ficon);
1794 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1795 // + newColor.getGreen() + ", " + newColor.getBlue()
1796 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1797 // + ", " + minCol.getBlue() + ")");
1799 comp.setHorizontalAlignment(SwingConstants.CENTER);
1800 comp.setText(tx.toString());
1801 if (tt.length() > 0)
1803 if (comp.getToolTipText() == null)
1805 comp.setToolTipText(tt.toString());
1809 comp.setToolTipText(
1810 tt.append(" ").append(comp.getToolTipText()).toString());
1815 class ColorEditor extends AbstractCellEditor
1816 implements TableCellEditor, ActionListener
1820 FeatureColourI currentColor;
1822 FeatureTypeSettings chooser;
1828 JColorChooser colorChooser;
1832 protected static final String EDIT = "edit";
1834 int rowSelected = 0;
1836 public ColorEditor(FeatureSettings me)
1839 // Set up the editor (from the table's point of view),
1840 // which is a button.
1841 // This button brings up the color chooser dialog,
1842 // which is the editor from the user's point of view.
1843 button = new JButton();
1844 button.setActionCommand(EDIT);
1845 button.addActionListener(this);
1846 button.setBorderPainted(false);
1847 // Set up the dialog that the button brings up.
1848 colorChooser = new JColorChooser();
1849 dialog = JColorChooser.createDialog(button,
1850 MessageManager.getString("label.select_colour"), true, // modal
1851 colorChooser, this, // OK button handler
1852 null); // no CANCEL button handler
1856 * Handles events from the editor button and from the dialog's OK button.
1859 public void actionPerformed(ActionEvent e)
1861 // todo test e.getSource() instead here
1862 if (EDIT.equals(e.getActionCommand()))
1864 // The user has clicked the cell, so
1865 // bring up the dialog.
1866 if (currentColor.isSimpleColour())
1868 // bring up simple color chooser
1869 button.setBackground(currentColor.getColour());
1870 colorChooser.setColor(currentColor.getColour());
1871 dialog.setVisible(true);
1875 // bring up graduated chooser.
1876 chooser = new FeatureTypeSettings(me.fr, type);
1877 chooser.setRequestFocusEnabled(true);
1878 chooser.requestFocus();
1879 chooser.addActionListener(this);
1880 chooser.showTab(true);
1882 // Make the renderer reappear.
1883 fireEditingStopped();
1888 if (currentColor.isSimpleColour())
1891 * read off colour picked in colour chooser after OK pressed
1893 currentColor = new FeatureColour(colorChooser.getColor());
1894 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1899 * after OK in variable colour dialog, any changes to colour
1900 * (or filters!) are already set in FeatureRenderer, so just
1901 * update table data without triggering updateFeatureRenderer
1903 currentColor = fr.getFeatureColours().get(type);
1904 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1905 if (currentFilter == null)
1907 currentFilter = new FeatureMatcherSet();
1909 Object[] data = ((FeatureTableModel) table.getModel())
1910 .getData()[rowSelected];
1911 data[COLOUR_COLUMN] = currentColor;
1912 data[FILTER_COLUMN] = currentFilter;
1914 fireEditingStopped();
1915 me.table.validate();
1919 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1921 public Object getCellEditorValue()
1923 return currentColor;
1926 // Implement the one method defined by TableCellEditor.
1928 public Component getTableCellEditorComponent(JTable theTable, Object value,
1929 boolean isSelected, int row, int column)
1931 currentColor = (FeatureColourI) value;
1932 this.rowSelected = row;
1933 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1934 button.setOpaque(true);
1935 button.setBackground(me.getBackground());
1936 if (!currentColor.isSimpleColour())
1938 JLabel btn = new JLabel();
1939 btn.setSize(button.getSize());
1940 FeatureSettings.renderGraduatedColor(btn, currentColor);
1941 button.setBackground(btn.getBackground());
1942 button.setIcon(btn.getIcon());
1943 button.setText(btn.getText());
1948 button.setIcon(null);
1949 button.setBackground(currentColor.getColour());
1956 * The cell editor for the Filter column. It displays the text of any filters
1957 * for the feature type in that row (in full as a tooltip, possible abbreviated
1958 * as display text). On click in the cell, opens the Feature Display Settings
1959 * dialog at the Filters tab.
1961 class FilterEditor extends AbstractCellEditor
1962 implements TableCellEditor, ActionListener
1966 FeatureMatcherSetI currentFilter;
1974 protected static final String EDIT = "edit";
1976 int rowSelected = 0;
1978 public FilterEditor(FeatureSettings me)
1981 button = new JButton();
1982 button.setActionCommand(EDIT);
1983 button.addActionListener(this);
1984 button.setBorderPainted(false);
1988 * Handles events from the editor button
1991 public void actionPerformed(ActionEvent e)
1993 if (button == e.getSource())
1995 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1996 chooser.addActionListener(this);
1997 chooser.setRequestFocusEnabled(true);
1998 chooser.requestFocus();
1999 if (lastLocation != null)
2001 // todo open at its last position on screen
2002 chooser.setBounds(lastLocation.x, lastLocation.y,
2003 chooser.getWidth(), chooser.getHeight());
2006 chooser.showTab(false);
2007 fireEditingStopped();
2009 else if (e.getSource() instanceof Component)
2013 * after OK in variable colour dialog, any changes to filter
2014 * (or colours!) are already set in FeatureRenderer, so just
2015 * update table data without triggering updateFeatureRenderer
2017 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2018 currentFilter = me.fr.getFeatureFilter(type);
2019 if (currentFilter == null)
2021 currentFilter = new FeatureMatcherSet();
2023 Object[] data = ((FeatureTableModel) table.getModel())
2024 .getData()[rowSelected];
2025 data[COLOUR_COLUMN] = currentColor;
2026 data[FILTER_COLUMN] = currentFilter;
2027 fireEditingStopped();
2028 me.table.validate();
2033 public Object getCellEditorValue()
2035 return currentFilter;
2039 public Component getTableCellEditorComponent(JTable theTable, Object value,
2040 boolean isSelected, int row, int column)
2042 currentFilter = (FeatureMatcherSetI) value;
2043 this.rowSelected = row;
2044 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2045 button.setOpaque(true);
2046 button.setBackground(me.getBackground());
2047 button.setText(currentFilter.toString());
2048 button.setToolTipText(currentFilter.toString());
2049 button.setIcon(null);
2055 class FeatureIcon implements Icon
2057 FeatureColourI gcol;
2061 boolean midspace = false;
2063 int width = 50, height = 20;
2065 int s1, e1; // start and end of midpoint band for thresholded symbol
2067 Color mpcolour = Color.white;
2069 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2089 public int getIconWidth()
2095 public int getIconHeight()
2101 public void paintIcon(Component c, Graphics g, int x, int y)
2104 if (gcol.isColourByLabel())
2107 g.fillRect(0, 0, width, height);
2108 // need an icon here.
2109 g.setColor(gcol.getMaxColour());
2111 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2113 // g.setFont(g.getFont().deriveFont(
2114 // AffineTransform.getScaleInstance(
2115 // width/g.getFontMetrics().stringWidth("Label"),
2116 // height/g.getFontMetrics().getHeight())));
2118 g.drawString(MessageManager.getString("label.label"), 0, 0);
2123 Color minCol = gcol.getMinColour();
2125 g.fillRect(0, 0, s1, height);
2128 g.setColor(Color.white);
2129 g.fillRect(s1, 0, e1 - s1, height);
2131 g.setColor(gcol.getMaxColour());
2132 g.fillRect(0, e1, width - e1, height);