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.AlignViewControllerGuiI;
24 import jalview.api.AlignViewportI;
25 import jalview.api.FeatureColourI;
26 import jalview.api.FeatureSettingsControllerI;
27 import jalview.api.SplitContainerI;
28 import jalview.api.ViewStyleI;
29 import jalview.controller.FeatureSettingsControllerGuiI;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.SequenceI;
32 import jalview.datamodel.features.FeatureMatcher;
33 import jalview.datamodel.features.FeatureMatcherI;
34 import jalview.datamodel.features.FeatureMatcherSet;
35 import jalview.datamodel.features.FeatureMatcherSetI;
36 import jalview.gui.Help.HelpId;
37 import jalview.io.JalviewFileChooser;
38 import jalview.io.JalviewFileView;
39 import jalview.schemes.FeatureColour;
40 import jalview.util.MessageManager;
41 import jalview.util.Platform;
42 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
43 import jalview.viewmodel.styles.ViewStyle;
44 import jalview.xml.binding.jalview.JalviewUserColours;
45 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
46 import jalview.xml.binding.jalview.JalviewUserColours.Filter;
47 import jalview.xml.binding.jalview.ObjectFactory;
49 import java.awt.BorderLayout;
50 import java.awt.Color;
51 import java.awt.Component;
52 import java.awt.Dimension;
53 import java.awt.FlowLayout;
55 import java.awt.Graphics;
56 import java.awt.GridLayout;
57 import java.awt.Point;
58 import java.awt.Rectangle;
59 import java.awt.event.ActionEvent;
60 import java.awt.event.ActionListener;
61 import java.awt.event.ItemEvent;
62 import java.awt.event.ItemListener;
63 import java.awt.event.MouseAdapter;
64 import java.awt.event.MouseEvent;
65 import java.awt.event.MouseMotionAdapter;
66 import java.beans.PropertyChangeEvent;
67 import java.beans.PropertyChangeListener;
69 import java.io.FileInputStream;
70 import java.io.FileOutputStream;
71 import java.io.InputStreamReader;
72 import java.io.OutputStreamWriter;
73 import java.io.PrintWriter;
74 import java.util.Arrays;
75 import java.util.Comparator;
76 import java.util.HashMap;
77 import java.util.HashSet;
78 import java.util.Hashtable;
79 import java.util.Iterator;
80 import java.util.List;
84 import javax.help.HelpSetException;
85 import javax.swing.AbstractCellEditor;
86 import javax.swing.BorderFactory;
87 import javax.swing.Icon;
88 import javax.swing.JButton;
89 import javax.swing.JCheckBox;
90 import javax.swing.JColorChooser;
91 import javax.swing.JDialog;
92 import javax.swing.JInternalFrame;
93 import javax.swing.JLabel;
94 import javax.swing.JLayeredPane;
95 import javax.swing.JMenuItem;
96 import javax.swing.JPanel;
97 import javax.swing.JPopupMenu;
98 import javax.swing.JScrollPane;
99 import javax.swing.JSlider;
100 import javax.swing.JTable;
101 import javax.swing.ListSelectionModel;
102 import javax.swing.SwingConstants;
103 import javax.swing.border.Border;
104 import javax.swing.event.ChangeEvent;
105 import javax.swing.event.ChangeListener;
106 import javax.swing.table.AbstractTableModel;
107 import javax.swing.table.JTableHeader;
108 import javax.swing.table.TableCellEditor;
109 import javax.swing.table.TableCellRenderer;
110 import javax.swing.table.TableColumn;
111 import javax.xml.bind.JAXBContext;
112 import javax.xml.bind.JAXBElement;
113 import javax.xml.bind.Marshaller;
114 import javax.xml.stream.XMLInputFactory;
115 import javax.xml.stream.XMLStreamReader;
117 public class FeatureSettings extends JPanel
118 implements FeatureSettingsControllerI, FeatureSettingsControllerGuiI
120 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
121 .getString("label.sequence_feature_colours");
124 * column indices of fields in Feature Settings table
126 static final int TYPE_COLUMN = 0;
128 static final int COLOUR_COLUMN = 1;
130 static final int FILTER_COLUMN = 2;
132 static final int SHOW_COLUMN = 3;
134 private static final int COLUMN_COUNT = 4;
136 private static final int MIN_WIDTH = 400;
138 private static final int MIN_HEIGHT = 400;
140 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
142 final FeatureRenderer fr;
144 public final AlignFrame af;
147 * 'original' fields hold settings to restore on Cancel
149 Object[][] originalData;
151 private float originalTransparency;
153 private ViewStyleI originalViewStyle;
155 private Map<String, FeatureMatcherSetI> originalFilters;
157 final JInternalFrame frame;
159 JScrollPane scrollPane = new JScrollPane();
165 JSlider transparency = new JSlider();
168 * when true, constructor is still executing - so ignore UI events
170 protected volatile boolean inConstruction = true;
172 int selectedRow = -1;
174 boolean resettingTable = false;
177 * true when Feature Settings are updating from feature renderer
179 private boolean handlingUpdate = false;
182 * holds {featureCount, totalExtent} for each feature type
184 Map<String, float[]> typeWidth = null;
191 public FeatureSettings(AlignFrame alignFrame)
193 this.af = alignFrame;
194 fr = af.getFeatureRenderer();
196 // save transparency for restore on Cancel
197 originalTransparency = fr.getTransparency();
198 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
199 transparency.setMaximum(100 - originalTransparencyAsPercent);
201 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
202 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
207 } catch (Exception ex)
209 ex.printStackTrace();
215 public String getToolTipText(MouseEvent e)
218 int column = table.columnAtPoint(e.getPoint());
219 int row = table.rowAtPoint(e.getPoint());
224 tip = JvSwingUtils.wrapTooltip(true, MessageManager
225 .getString("label.feature_settings_click_drag"));
228 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
230 tip = getColorTooltip(colour, true);
233 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
237 .getString("label.configure_feature_tooltip")
248 * Position the tooltip near the bottom edge of, and half way across, the
252 public Point getToolTipLocation(MouseEvent e)
254 Point point = e.getPoint();
255 int column = table.columnAtPoint(point);
256 int row = table.rowAtPoint(point);
257 Rectangle r = getCellRect(row, column, false);
258 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
262 JTableHeader tableHeader = table.getTableHeader();
263 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
264 tableHeader.setReorderingAllowed(false);
265 table.setFont(new Font("Verdana", Font.PLAIN, 12));
267 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
268 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
270 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
271 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
273 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
274 new ColorRenderer(), new ColorEditor(this));
275 table.addColumn(colourColumn);
277 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
278 new FilterRenderer(), new FilterEditor(this));
279 table.addColumn(filterColumn);
281 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
283 table.addMouseListener(new MouseAdapter()
286 public void mousePressed(MouseEvent evt)
288 Point pt = evt.getPoint();
289 selectedRow = table.rowAtPoint(pt);
290 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
291 if (evt.isPopupTrigger())
293 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
294 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
297 else if (evt.getClickCount() == 2
298 && table.columnAtPoint(pt) == TYPE_COLUMN)
300 boolean invertSelection = evt.isAltDown();
301 boolean toggleSelection = Platform.isControlDown(evt);
302 boolean extendSelection = evt.isShiftDown();
303 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
304 invertSelection, extendSelection, toggleSelection, type);
305 fr.ap.av.sendSelection();
309 // isPopupTrigger fires on mouseReleased on Windows
311 public void mouseReleased(MouseEvent evt)
313 selectedRow = table.rowAtPoint(evt.getPoint());
314 if (evt.isPopupTrigger())
316 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
317 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
318 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
324 table.addMouseMotionListener(new MouseMotionAdapter()
327 public void mouseDragged(MouseEvent evt)
329 int newRow = table.rowAtPoint(evt.getPoint());
330 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
333 * reposition 'selectedRow' to 'newRow' (the dragged to location)
334 * this could be more than one row away for a very fast drag action
335 * so just swap it with adjacent rows until we get it there
337 Object[][] data = ((FeatureTableModel) table.getModel())
339 int direction = newRow < selectedRow ? -1 : 1;
340 for (int i = selectedRow; i != newRow; i += direction)
342 Object[] temp = data[i];
343 data[i] = data[i + direction];
344 data[i + direction] = temp;
346 updateFeatureRenderer(data);
348 selectedRow = newRow;
352 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
353 // MessageManager.getString("label.feature_settings_click_drag")));
354 scrollPane.setViewportView(table);
356 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
358 fr.findAllFeatures(true); // display everything!
361 discoverAllFeatureData();
362 final FeatureSettings fs = this;
363 fr.addPropertyChangeListener(change = new PropertyChangeListener()
366 public void propertyChange(PropertyChangeEvent evt)
368 if (!fs.resettingTable && !fs.handlingUpdate)
370 fs.handlingUpdate = true;
372 // new groups may be added with new sequence feature types only
373 fs.handlingUpdate = false;
378 SplitContainerI splitframe = af.getSplitViewContainer();
379 if (splitframe != null)
381 frame = null; // keeps eclipse happy
382 splitframe.addFeatureSettingsUI(this);
386 frame = new JInternalFrame();
387 frame.setContentPane(this);
388 Rectangle bounds = af.getFeatureSettingsGeometry();
390 if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels(
391 af.alignPanel.av.getSequenceSetId()).length > 1)
393 title = MessageManager.formatMessage(
394 "label.sequence_feature_settings_for_view",
395 af.alignPanel.getViewName());
399 title = MessageManager.getString("label.sequence_feature_settings");
403 if (Platform.isAMac())
405 Desktop.addInternalFrame(frame, title, 600, 480);
409 Desktop.addInternalFrame(frame, title, 600, 450);
414 Desktop.addInternalFrame(frame, title,
415 false, bounds.width, bounds.height);
416 frame.setBounds(bounds);
417 frame.setVisible(true);
419 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
421 frame.addInternalFrameListener(
422 new javax.swing.event.InternalFrameAdapter()
425 public void internalFrameClosed(
426 javax.swing.event.InternalFrameEvent evt)
428 featureSettings_isClosed();
431 frame.setLayer(JLayeredPane.PALETTE_LAYER);
433 inConstruction = false;
436 PropertyChangeListener change;
439 public AlignViewControllerGuiI getAlignframe()
445 public void featureSettings_isClosed()
447 fr.removePropertyChangeListener(change);
451 protected void popupSort(final int rowSelected, final String type,
452 final Object typeCol, final Map<String, float[][]> minmax, int x,
455 JPopupMenu men = new JPopupMenu(MessageManager
456 .formatMessage("label.settings_for_param", new String[]
458 JMenuItem scr = new JMenuItem(
459 MessageManager.getString("label.sort_by_score"));
461 final FeatureSettings me = this;
462 scr.addActionListener(new ActionListener()
466 public void actionPerformed(ActionEvent e)
469 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
474 JMenuItem dens = new JMenuItem(
475 MessageManager.getString("label.sort_by_density"));
476 dens.addActionListener(new ActionListener()
480 public void actionPerformed(ActionEvent e)
483 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
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,
499 fr.ap.av.sendSelection();
502 JMenuItem clearCols = new JMenuItem(MessageManager
503 .getString("label.select_columns_not_containing"));
504 clearCols.addActionListener(new ActionListener()
507 public void actionPerformed(ActionEvent arg0)
509 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
511 fr.ap.av.sendSelection();
514 JMenuItem hideCols = new JMenuItem(
515 MessageManager.getString("label.hide_columns_containing"));
516 hideCols.addActionListener(new ActionListener()
519 public void actionPerformed(ActionEvent arg0)
521 fr.ap.alignFrame.hideFeatureColumns(type, true);
522 fr.ap.av.sendSelection();
525 JMenuItem hideOtherCols = new JMenuItem(
526 MessageManager.getString("label.hide_columns_not_containing"));
527 hideOtherCols.addActionListener(new ActionListener()
530 public void actionPerformed(ActionEvent arg0)
532 fr.ap.alignFrame.hideFeatureColumns(type, false);
533 fr.ap.av.sendSelection();
539 men.add(hideOtherCols);
540 men.show(table, x, y);
544 synchronized public void discoverAllFeatureData()
546 Set<String> allGroups = new HashSet<>();
547 AlignmentI alignment = af.getViewport().getAlignment();
549 for (int i = 0; i < alignment.getHeight(); i++)
551 SequenceI seq = alignment.getSequenceAt(i);
552 for (String group : seq.getFeatures().getFeatureGroups(true))
554 if (group != null && !allGroups.contains(group))
556 allGroups.add(group);
557 checkGroupState(group);
568 * Synchronise gui group list and check visibility of group
571 * @return true if group is visible
573 private boolean checkGroupState(String group)
575 boolean visible = fr.checkGroupVisibility(group, true);
577 for (int g = 0; g < groupPanel.getComponentCount(); g++)
579 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
581 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
586 final String grp = group;
587 final JCheckBox check = new JCheckBox(group, visible);
588 check.setFont(new Font("Serif", Font.BOLD, 12));
589 check.setToolTipText(group);
590 check.addItemListener(new ItemListener()
593 public void itemStateChanged(ItemEvent evt)
595 fr.setGroupVisibility(check.getText(), check.isSelected());
596 resetTable(new String[] { grp });
600 groupPanel.add(check);
604 synchronized void resetTable(String[] groupChanged)
610 resettingTable = true;
611 typeWidth = new Hashtable<>();
612 // TODO: change avWidth calculation to 'per-sequence' average and use long
615 Set<String> displayableTypes = new HashSet<>();
616 Set<String> foundGroups = new HashSet<>();
619 * determine which feature types may be visible depending on
620 * which groups are selected, and recompute average width data
622 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
625 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
628 * get the sequence's groups for positional features
629 * and keep track of which groups are visible
631 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
632 Set<String> visibleGroups = new HashSet<>();
633 for (String group : groups)
635 if (group == null || checkGroupState(group))
637 visibleGroups.add(group);
640 foundGroups.addAll(groups);
643 * get distinct feature types for visible groups
644 * record distinct visible types, and their count and total length
646 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
647 visibleGroups.toArray(new String[visibleGroups.size()]));
648 for (String type : types)
650 displayableTypes.add(type);
651 float[] avWidth = typeWidth.get(type);
654 avWidth = new float[2];
655 typeWidth.put(type, avWidth);
657 // todo this could include features with a non-visible group
658 // - do we greatly care?
659 // todo should we include non-displayable features here, and only
660 // update when features are added?
661 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
662 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
666 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
669 if (fr.hasRenderOrder())
673 fr.findAllFeatures(groupChanged != null); // prod to update
674 // colourschemes. but don't
676 // First add the checks in the previous render order,
677 // in case the window has been closed and reopened
679 List<String> frl = fr.getRenderOrder();
680 for (int ro = frl.size() - 1; ro > -1; ro--)
682 String type = frl.get(ro);
684 if (!displayableTypes.contains(type))
689 data[dataIndex][TYPE_COLUMN] = type;
690 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
691 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
692 data[dataIndex][FILTER_COLUMN] = featureFilter == null
693 ? new FeatureMatcherSet()
695 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
696 af.getViewport().getFeaturesDisplayed().isVisible(type));
698 displayableTypes.remove(type);
703 * process any extra features belonging only to
704 * a group which was just selected
706 while (!displayableTypes.isEmpty())
708 String type = displayableTypes.iterator().next();
709 data[dataIndex][TYPE_COLUMN] = type;
711 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
712 if (data[dataIndex][COLOUR_COLUMN] == null)
714 // "Colour has been updated in another view!!"
715 fr.clearRenderOrder();
718 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
719 data[dataIndex][FILTER_COLUMN] = featureFilter == null
720 ? new FeatureMatcherSet()
722 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
724 displayableTypes.remove(type);
727 if (originalData == null)
729 originalData = new Object[data.length][COLUMN_COUNT];
730 for (int i = 0; i < data.length; i++)
732 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
737 updateOriginalData(data);
740 table.setModel(new FeatureTableModel(data));
741 table.getColumnModel().getColumn(0).setPreferredWidth(200);
743 groupPanel.setLayout(
744 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
745 pruneGroups(foundGroups);
746 groupPanel.validate();
748 updateFeatureRenderer(data, groupChanged != null);
749 resettingTable = false;
753 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
754 * have been made outwith this dialog
756 * <li>a new feature type added (and made visible)</li>
757 * <li>a feature colour changed (in the Amend Features dialog)</li>
762 protected void updateOriginalData(Object[][] foundData)
764 // todo LinkedHashMap instead of Object[][] would be nice
766 Object[][] currentData = ((FeatureTableModel) table.getModel())
768 for (Object[] row : foundData)
770 String type = (String) row[TYPE_COLUMN];
771 boolean found = false;
772 for (Object[] current : currentData)
774 if (type.equals(current[TYPE_COLUMN]))
778 * currently dependent on object equality here;
779 * really need an equals method on FeatureColour
781 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
784 * feature colour has changed externally - update originalData
786 for (Object[] original : originalData)
788 if (type.equals(original[TYPE_COLUMN]))
790 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
801 * new feature detected - add to original data (on top)
803 Object[][] newData = new Object[originalData.length
805 for (int i = 0; i < originalData.length; i++)
807 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
811 originalData = newData;
817 * Remove from the groups panel any checkboxes for groups that are not in the
818 * foundGroups set. This enables removing a group from the display when the last
819 * feature in that group is deleted.
823 protected void pruneGroups(Set<String> foundGroups)
825 for (int g = 0; g < groupPanel.getComponentCount(); g++)
827 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
828 if (!foundGroups.contains(checkbox.getText()))
830 groupPanel.remove(checkbox);
836 * reorder data based on the featureRenderers global priority list.
840 private void ensureOrder(Object[][] data)
842 boolean sort = false;
843 float[] order = new float[data.length];
844 for (int i = 0; i < order.length; i++)
846 order[i] = fr.getOrder(data[i][0].toString());
849 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
853 sort = sort || order[i - 1] > order[i];
858 jalview.util.QuickSort.sort(order, data);
863 * Offers a file chooser dialog, and then loads the feature colours and
864 * filters from file in XML format and unmarshals to Jalview feature settings
868 JalviewFileChooser chooser = new JalviewFileChooser("fc",
869 SEQUENCE_FEATURE_COLOURS);
870 chooser.setFileView(new JalviewFileView());
871 chooser.setDialogTitle(
872 MessageManager.getString("label.load_feature_colours"));
873 chooser.setToolTipText(MessageManager.getString("action.load"));
875 int value = chooser.showOpenDialog(this);
877 if (value == JalviewFileChooser.APPROVE_OPTION)
879 File file = chooser.getSelectedFile();
885 * Loads feature colours and filters from XML stored in the given file
893 InputStreamReader in = new InputStreamReader(
894 new FileInputStream(file), "UTF-8");
896 JAXBContext jc = JAXBContext
897 .newInstance("jalview.xml.binding.jalview");
898 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
899 XMLStreamReader streamReader = XMLInputFactory.newInstance()
900 .createXMLStreamReader(in);
901 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
902 JalviewUserColours.class);
903 JalviewUserColours jucs = jbe.getValue();
905 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
908 * load feature colours
910 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
912 Colour newcol = jucs.getColour().get(i);
913 FeatureColourI colour = jalview.project.Jalview2XML
914 .parseColour(newcol);
915 fr.setColour(newcol.getName(), colour);
916 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
920 * load feature filters; loaded filters will replace any that are
921 * currently defined, other defined filters are left unchanged
923 for (int i = 0; i < jucs.getFilter().size(); i++)
925 Filter filterModel = jucs.getFilter().get(i);
926 String featureType = filterModel.getFeatureType();
927 FeatureMatcherSetI filter = jalview.project.Jalview2XML
928 .parseFilter(featureType, filterModel.getMatcherSet());
929 if (!filter.isEmpty())
931 fr.setFeatureFilter(featureType, filter);
936 * update feature settings table
941 Object[][] data = ((FeatureTableModel) table.getModel())
944 updateFeatureRenderer(data, false);
947 } catch (Exception ex)
949 System.out.println("Error loading User Colour File\n" + ex);
954 * Offers a file chooser dialog, and then saves the current feature colours
955 * and any filters to the selected file in XML format
959 JalviewFileChooser chooser = new JalviewFileChooser("fc",
960 SEQUENCE_FEATURE_COLOURS);
961 chooser.setFileView(new JalviewFileView());
962 chooser.setDialogTitle(
963 MessageManager.getString("label.save_feature_colours"));
964 chooser.setToolTipText(MessageManager.getString("action.save"));
966 int value = chooser.showSaveDialog(this);
968 if (value == JalviewFileChooser.APPROVE_OPTION)
970 save(chooser.getSelectedFile());
975 * Saves feature colours and filters to the given file
981 JalviewUserColours ucs = new JalviewUserColours();
982 ucs.setSchemeName("Sequence Features");
985 PrintWriter out = new PrintWriter(new OutputStreamWriter(
986 new FileOutputStream(file), "UTF-8"));
989 * sort feature types by colour order, from 0 (highest)
992 Set<String> fr_colours = fr.getAllFeatureColours();
993 String[] sortedTypes = fr_colours
994 .toArray(new String[fr_colours.size()]);
995 Arrays.sort(sortedTypes, new Comparator<String>()
998 public int compare(String type1, String type2)
1000 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1005 * save feature colours
1007 for (String featureType : sortedTypes)
1009 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1010 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1012 ucs.getColour().add(col);
1016 * save any feature filters
1018 for (String featureType : sortedTypes)
1020 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1021 if (filter != null && !filter.isEmpty())
1023 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1024 FeatureMatcherI firstMatcher = iterator.next();
1025 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1026 .marshalFilter(firstMatcher, iterator,
1028 Filter filterModel = new Filter();
1029 filterModel.setFeatureType(featureType);
1030 filterModel.setMatcherSet(ms);
1031 ucs.getFilter().add(filterModel);
1034 JAXBContext jaxbContext = JAXBContext
1035 .newInstance(JalviewUserColours.class);
1036 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1037 jaxbMarshaller.marshal(
1038 new ObjectFactory().createJalviewUserColours(ucs), out);
1040 // jaxbMarshaller.marshal(object, pout);
1041 // marshaller.marshal(object);
1044 // ucs.marshal(out);
1046 } catch (Exception ex)
1048 ex.printStackTrace();
1052 public void invertSelection()
1054 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1055 for (int i = 0; i < data.length; i++)
1057 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1059 updateFeatureRenderer(data, true);
1063 public void orderByAvWidth()
1065 if (table == null || table.getModel() == null)
1069 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1070 float[] width = new float[data.length];
1074 for (int i = 0; i < data.length; i++)
1076 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1079 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1080 // weight - but have to make per
1081 // sequence, too (awidth[2])
1082 // if (width[i]==1) // hack to distinguish single width sequences.
1093 boolean sort = false;
1094 for (int i = 0; i < width.length; i++)
1096 // awidth = (float[]) typeWidth.get(data[i][0]);
1099 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1102 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1108 width[i] /= max; // normalize
1109 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1113 sort = sort || width[i - 1] > width[i];
1118 jalview.util.QuickSort.sort(width, data);
1119 // update global priority order
1122 updateFeatureRenderer(data, false);
1127 * close ourselves but leave any existing UI handlers (e.g a CDS/Protein tabbed
1128 * feature settings dialog) intact
1130 public void closeOldSettings()
1136 * close the feature settings dialog (and any containing frame)
1143 private void closeDialog(boolean closeContainingFrame)
1149 af.setFeatureSettingsGeometry(frame.getBounds());
1150 frame.setClosed(true);
1154 SplitContainerI sc = af.getSplitViewContainer();
1155 sc.closeFeatureSettings(this, closeContainingFrame);
1156 af.featureSettings = null;
1158 } catch (Exception exe)
1164 public void updateFeatureRenderer(Object[][] data)
1166 updateFeatureRenderer(data, true);
1170 * Update the priority order of features; only repaint if this changed the order
1171 * of visible features
1176 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1178 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1180 if (fr.setFeaturePriority(rowData, visibleNew))
1187 * Converts table data into an array of data beans
1189 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1191 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1192 for (int i = 0; i < data.length; i++)
1194 String type = (String) data[i][TYPE_COLUMN];
1195 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1196 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1197 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1198 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1204 private void jbInit() throws Exception
1206 this.setLayout(new BorderLayout());
1208 JPanel settingsPane = new JPanel();
1209 settingsPane.setLayout(new BorderLayout());
1211 JPanel bigPanel = new JPanel();
1212 bigPanel.setLayout(new BorderLayout());
1214 groupPanel = new JPanel();
1215 bigPanel.add(groupPanel, BorderLayout.NORTH);
1217 JButton invert = new JButton(
1218 MessageManager.getString("label.invert_selection"));
1219 invert.setFont(JvSwingUtils.getLabelFont());
1220 invert.addActionListener(new ActionListener()
1223 public void actionPerformed(ActionEvent e)
1229 JButton optimizeOrder = new JButton(
1230 MessageManager.getString("label.optimise_order"));
1231 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1232 optimizeOrder.addActionListener(new ActionListener()
1235 public void actionPerformed(ActionEvent e)
1241 JButton sortByScore = new JButton(
1242 MessageManager.getString("label.seq_sort_by_score"));
1243 sortByScore.setFont(JvSwingUtils.getLabelFont());
1244 sortByScore.addActionListener(new ActionListener()
1247 public void actionPerformed(ActionEvent e)
1249 af.avc.sortAlignmentByFeatureScore(null);
1252 JButton sortByDens = new JButton(
1253 MessageManager.getString("label.sequence_sort_by_density"));
1254 sortByDens.setFont(JvSwingUtils.getLabelFont());
1255 sortByDens.addActionListener(new ActionListener()
1258 public void actionPerformed(ActionEvent e)
1260 af.avc.sortAlignmentByFeatureDensity(null);
1264 JButton help = new JButton(MessageManager.getString("action.help"));
1265 help.setFont(JvSwingUtils.getLabelFont());
1266 help.addActionListener(new ActionListener()
1269 public void actionPerformed(ActionEvent e)
1273 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1274 } catch (HelpSetException e1)
1276 e1.printStackTrace();
1281 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1282 cancel.setFont(JvSwingUtils.getLabelFont());
1283 cancel.addActionListener(new ActionListener()
1286 public void actionPerformed(ActionEvent e)
1288 fr.setTransparency(originalTransparency);
1289 fr.setFeatureFilters(originalFilters);
1290 updateFeatureRenderer(originalData);
1291 af.getViewport().setViewStyle(originalViewStyle);
1296 JButton ok = new JButton(MessageManager.getString("action.ok"));
1297 ok.setFont(JvSwingUtils.getLabelFont());
1298 ok.addActionListener(new ActionListener()
1301 public void actionPerformed(ActionEvent e)
1307 JButton loadColours = new JButton(
1308 MessageManager.getString("label.load_colours"));
1309 loadColours.setFont(JvSwingUtils.getLabelFont());
1310 loadColours.setToolTipText(
1311 MessageManager.getString("label.load_colours_tooltip"));
1312 loadColours.addActionListener(new ActionListener()
1315 public void actionPerformed(ActionEvent e)
1321 JButton saveColours = new JButton(
1322 MessageManager.getString("label.save_colours"));
1323 saveColours.setFont(JvSwingUtils.getLabelFont());
1324 saveColours.setToolTipText(
1325 MessageManager.getString("label.save_colours_tooltip"));
1326 saveColours.addActionListener(new ActionListener()
1329 public void actionPerformed(ActionEvent e)
1334 transparency.addChangeListener(new ChangeListener()
1337 public void stateChanged(ChangeEvent evt)
1339 if (!inConstruction)
1341 fr.setTransparency((100 - transparency.getValue()) / 100f);
1347 transparency.setMaximum(70);
1348 transparency.setToolTipText(
1349 MessageManager.getString("label.transparency_tip"));
1351 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1352 String text = MessageManager.formatMessage("label.show_linked_features",
1354 ? MessageManager.getString("label.protein")
1357 JCheckBox showComplement = new JCheckBox(text);
1358 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
1359 showComplement.addActionListener(new ActionListener()
1362 public void actionPerformed(ActionEvent e)
1365 .setShowComplementFeatures(showComplement.isSelected());
1370 JCheckBox showComplementOnTop = new JCheckBox(
1371 MessageManager.getString("label.on_top"));
1373 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
1374 showComplementOnTop.addActionListener(new ActionListener()
1377 public void actionPerformed(ActionEvent e)
1379 af.getViewport().setShowComplementFeaturesOnTop(
1380 showComplementOnTop.isSelected());
1384 // JButton viewComplementSettings = new JButton(MessageManager
1385 // .formatMessage("label.show_linked_feature_settings",
1387 // ? MessageManager.getString("label.protein")
1390 // viewComplementSettings.addActionListener(new ActionListener()
1394 // public void actionPerformed(ActionEvent e)
1396 // AlignViewControllerGuiI complAf = af.getSplitViewContainer()
1397 // .getComplementAlignFrame(af);
1398 // FeatureSettings complFeatureSettings = (FeatureSettings) complAf
1399 // .getFeatureSettingsUI();
1400 // if (complFeatureSettings != null)
1402 // complFeatureSettings.frame.setVisible(true);
1405 // complFeatureSettings.frame.setSelected(true);
1407 // } catch (Exception q)
1412 // complAf.showFeatureSettingsUI();
1416 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1417 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1419 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1420 transbuttons.add(optimizeOrder);
1421 transbuttons.add(invert);
1422 transbuttons.add(sortByScore);
1423 transbuttons.add(sortByDens);
1424 transbuttons.add(help);
1426 boolean hasComplement = af.getViewport().getCodingComplement() != null;
1427 JPanel transPanelLeft = new JPanel(
1428 new GridLayout(hasComplement ? 4 : 2, 1));
1429 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1430 transPanelLeft.add(transparency);
1433 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1434 cp.add(showComplement);
1435 cp.add(showComplementOnTop);
1436 transPanelLeft.add(cp);
1438 lowerPanel.add(transPanelLeft);
1439 lowerPanel.add(transbuttons);
1441 JPanel buttonPanel = new JPanel();
1442 buttonPanel.add(ok);
1443 buttonPanel.add(cancel);
1444 buttonPanel.add(loadColours);
1445 buttonPanel.add(saveColours);
1446 bigPanel.add(scrollPane, BorderLayout.CENTER);
1447 settingsPane.add(bigPanel, BorderLayout.CENTER);
1448 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1449 this.add(settingsPane);
1453 * Repaints alignment, structure and overview (if shown). If there is a
1454 * complementary view which is showing this view's features, then also
1457 void refreshDisplay()
1459 af.alignPanel.paintAlignment(true, true);
1460 AlignViewportI complement = af.getViewport().getCodingComplement();
1461 if (complement != null && complement.isShowComplementFeatures())
1463 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1464 af2.alignPanel.paintAlignment(true, true);
1469 * Answers a suitable tooltip to show on the colour cell of the table
1473 * if true include 'click to edit' and similar text
1476 public static String getColorTooltip(FeatureColourI fcol,
1483 if (fcol.isSimpleColour())
1485 return withHint ? BASE_TOOLTIP : null;
1487 String description = fcol.getDescription();
1488 description = description.replaceAll("<", "<");
1489 description = description.replaceAll(">", ">");
1490 StringBuilder tt = new StringBuilder(description);
1493 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1495 return JvSwingUtils.wrapTooltip(true, tt.toString());
1498 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1501 boolean thr = false;
1502 StringBuilder tx = new StringBuilder();
1504 if (gcol.isColourByAttribute())
1506 tx.append(FeatureMatcher
1507 .toAttributeDisplayName(gcol.getAttributeName()));
1509 else if (!gcol.isColourByLabel())
1511 tx.append(MessageManager.getString("label.score"));
1514 if (gcol.isAboveThreshold())
1519 if (gcol.isBelowThreshold())
1524 if (gcol.isColourByLabel())
1530 if (!gcol.isColourByAttribute())
1538 Color newColor = gcol.getMaxColour();
1539 comp.setBackground(newColor);
1540 // System.err.println("Width is " + w / 2);
1541 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1542 comp.setIcon(ficon);
1543 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1544 // + newColor.getGreen() + ", " + newColor.getBlue()
1545 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1546 // + ", " + minCol.getBlue() + ")");
1548 comp.setHorizontalAlignment(SwingConstants.CENTER);
1549 comp.setText(tx.toString());
1552 // ///////////////////////////////////////////////////////////////////////
1553 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1554 // ///////////////////////////////////////////////////////////////////////
1555 class FeatureTableModel extends AbstractTableModel
1557 private String[] columnNames = {
1558 MessageManager.getString("label.feature_type"),
1559 MessageManager.getString("action.colour"),
1560 MessageManager.getString("label.configuration"),
1561 MessageManager.getString("label.show") };
1563 private Object[][] data;
1565 FeatureTableModel(Object[][] data)
1570 public Object[][] getData()
1575 public void setData(Object[][] data)
1581 public int getColumnCount()
1583 return columnNames.length;
1586 public Object[] getRow(int row)
1592 public int getRowCount()
1598 public String getColumnName(int col)
1600 return columnNames[col];
1604 public Object getValueAt(int row, int col)
1606 return data[row][col];
1610 * Answers the class of column c of the table
1613 public Class<?> getColumnClass(int c)
1618 return String.class;
1620 return FeatureColour.class;
1622 return FeatureMatcherSet.class;
1624 return Boolean.class;
1629 public boolean isCellEditable(int row, int col)
1631 return col == 0 ? false : true;
1635 public void setValueAt(Object value, int row, int col)
1637 data[row][col] = value;
1638 fireTableCellUpdated(row, col);
1639 updateFeatureRenderer(data);
1644 class ColorRenderer extends JLabel implements TableCellRenderer
1646 Border unselectedBorder = null;
1648 Border selectedBorder = null;
1650 public ColorRenderer()
1652 setOpaque(true); // MUST do this for background to show up.
1653 setHorizontalTextPosition(SwingConstants.CENTER);
1654 setVerticalTextPosition(SwingConstants.CENTER);
1658 public Component getTableCellRendererComponent(JTable tbl, Object color,
1659 boolean isSelected, boolean hasFocus, int row, int column)
1661 FeatureColourI cellColour = (FeatureColourI) color;
1663 setBackground(tbl.getBackground());
1664 if (!cellColour.isSimpleColour())
1666 Rectangle cr = tbl.getCellRect(row, column, false);
1667 FeatureSettings.renderGraduatedColor(this, cellColour,
1668 (int) cr.getWidth(), (int) cr.getHeight());
1674 setBackground(cellColour.getColour());
1678 if (selectedBorder == null)
1680 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1681 tbl.getSelectionBackground());
1683 setBorder(selectedBorder);
1687 if (unselectedBorder == null)
1689 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1690 tbl.getBackground());
1692 setBorder(unselectedBorder);
1699 class FilterRenderer extends JLabel implements TableCellRenderer
1701 javax.swing.border.Border unselectedBorder = null;
1703 javax.swing.border.Border selectedBorder = null;
1705 public FilterRenderer()
1707 setOpaque(true); // MUST do this for background to show up.
1708 setHorizontalTextPosition(SwingConstants.CENTER);
1709 setVerticalTextPosition(SwingConstants.CENTER);
1713 public Component getTableCellRendererComponent(JTable tbl,
1714 Object filter, boolean isSelected, boolean hasFocus, int row,
1717 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1719 String asText = theFilter.toString();
1720 setBackground(tbl.getBackground());
1721 this.setText(asText);
1726 if (selectedBorder == null)
1728 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1729 tbl.getSelectionBackground());
1731 setBorder(selectedBorder);
1735 if (unselectedBorder == null)
1737 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1738 tbl.getBackground());
1740 setBorder(unselectedBorder);
1748 * update comp using rendering settings from gcol
1753 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1755 int w = comp.getWidth(), h = comp.getHeight();
1758 w = (int) comp.getPreferredSize().getWidth();
1759 h = (int) comp.getPreferredSize().getHeight();
1766 renderGraduatedColor(comp, gcol, w, h);
1769 class ColorEditor extends AbstractCellEditor
1770 implements TableCellEditor, ActionListener
1774 FeatureColourI currentColor;
1776 FeatureTypeSettings chooser;
1782 JColorChooser colorChooser;
1786 protected static final String EDIT = "edit";
1788 int rowSelected = 0;
1790 public ColorEditor(FeatureSettings me)
1793 // Set up the editor (from the table's point of view),
1794 // which is a button.
1795 // This button brings up the color chooser dialog,
1796 // which is the editor from the user's point of view.
1797 button = new JButton();
1798 button.setActionCommand(EDIT);
1799 button.addActionListener(this);
1800 button.setBorderPainted(false);
1801 // Set up the dialog that the button brings up.
1802 colorChooser = new JColorChooser();
1803 dialog = JColorChooser.createDialog(button,
1804 MessageManager.getString("label.select_colour"), true, // modal
1805 colorChooser, this, // OK button handler
1806 null); // no CANCEL button handler
1810 * Handles events from the editor button and from the dialog's OK button.
1813 public void actionPerformed(ActionEvent e)
1815 // todo test e.getSource() instead here
1816 if (EDIT.equals(e.getActionCommand()))
1818 // The user has clicked the cell, so
1819 // bring up the dialog.
1820 if (currentColor.isSimpleColour())
1822 // bring up simple color chooser
1823 button.setBackground(currentColor.getColour());
1824 colorChooser.setColor(currentColor.getColour());
1825 dialog.setVisible(true);
1829 // bring up graduated chooser.
1830 chooser = new FeatureTypeSettings(me.fr, type);
1835 chooser.setRequestFocusEnabled(true);
1836 chooser.requestFocus();
1838 chooser.addActionListener(this);
1839 // Make the renderer reappear.
1840 fireEditingStopped();
1845 if (currentColor.isSimpleColour())
1848 * read off colour picked in colour chooser after OK pressed
1850 currentColor = new FeatureColour(colorChooser.getColor());
1851 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1856 * after OK in variable colour dialog, any changes to colour
1857 * (or filters!) are already set in FeatureRenderer, so just
1858 * update table data without triggering updateFeatureRenderer
1860 currentColor = fr.getFeatureColours().get(type);
1861 FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1862 if (currentFilter == null)
1864 currentFilter = new FeatureMatcherSet();
1866 Object[] data = ((FeatureTableModel) table.getModel())
1867 .getData()[rowSelected];
1868 data[COLOUR_COLUMN] = currentColor;
1869 data[FILTER_COLUMN] = currentFilter;
1871 fireEditingStopped();
1872 me.table.validate();
1876 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1878 public Object getCellEditorValue()
1880 return currentColor;
1883 // Implement the one method defined by TableCellEditor.
1885 public Component getTableCellEditorComponent(JTable theTable, Object value,
1886 boolean isSelected, int row, int column)
1888 currentColor = (FeatureColourI) value;
1889 this.rowSelected = row;
1890 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1891 button.setOpaque(true);
1892 button.setBackground(me.getBackground());
1893 if (!currentColor.isSimpleColour())
1895 JLabel btn = new JLabel();
1896 btn.setSize(button.getSize());
1897 FeatureSettings.renderGraduatedColor(btn, currentColor);
1898 button.setBackground(btn.getBackground());
1899 button.setIcon(btn.getIcon());
1900 button.setText(btn.getText());
1905 button.setIcon(null);
1906 button.setBackground(currentColor.getColour());
1913 * The cell editor for the Filter column. It displays the text of any filters
1914 * for the feature type in that row (in full as a tooltip, possible abbreviated
1915 * as display text). On click in the cell, opens the Feature Display Settings
1916 * dialog at the Filters tab.
1918 class FilterEditor extends AbstractCellEditor
1919 implements TableCellEditor, ActionListener
1923 FeatureMatcherSetI currentFilter;
1931 protected static final String EDIT = "edit";
1933 int rowSelected = 0;
1935 public FilterEditor(FeatureSettings me)
1938 button = new JButton();
1939 button.setActionCommand(EDIT);
1940 button.addActionListener(this);
1941 button.setBorderPainted(false);
1945 * Handles events from the editor button
1948 public void actionPerformed(ActionEvent e)
1950 if (button == e.getSource())
1952 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1953 chooser.addActionListener(this);
1954 chooser.setRequestFocusEnabled(true);
1955 chooser.requestFocus();
1956 if (lastLocation != null)
1958 // todo open at its last position on screen
1959 chooser.setBounds(lastLocation.x, lastLocation.y,
1960 chooser.getWidth(), chooser.getHeight());
1963 fireEditingStopped();
1965 else if (e.getSource() instanceof Component)
1969 * after OK in variable colour dialog, any changes to filter
1970 * (or colours!) are already set in FeatureRenderer, so just
1971 * update table data without triggering updateFeatureRenderer
1973 FeatureColourI currentColor = fr.getFeatureColours().get(type);
1974 currentFilter = me.fr.getFeatureFilter(type);
1975 if (currentFilter == null)
1977 currentFilter = new FeatureMatcherSet();
1979 Object[] data = ((FeatureTableModel) table.getModel())
1980 .getData()[rowSelected];
1981 data[COLOUR_COLUMN] = currentColor;
1982 data[FILTER_COLUMN] = currentFilter;
1983 fireEditingStopped();
1984 me.table.validate();
1989 public Object getCellEditorValue()
1991 return currentFilter;
1995 public Component getTableCellEditorComponent(JTable theTable, Object value,
1996 boolean isSelected, int row, int column)
1998 currentFilter = (FeatureMatcherSetI) value;
1999 this.rowSelected = row;
2000 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2001 button.setOpaque(true);
2002 button.setBackground(me.getBackground());
2003 button.setText(currentFilter.toString());
2004 button.setIcon(null);
2009 public boolean isOpen()
2011 if (af.getSplitViewContainer() != null)
2013 return af.getSplitViewContainer().isFeatureSettingsOpen();
2015 return frame != null && !frame.isClosed();
2019 class FeatureIcon implements Icon
2021 FeatureColourI gcol;
2025 boolean midspace = false;
2027 int width = 50, height = 20;
2029 int s1, e1; // start and end of midpoint band for thresholded symbol
2031 Color mpcolour = Color.white;
2033 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2053 public int getIconWidth()
2059 public int getIconHeight()
2065 public void paintIcon(Component c, Graphics g, int x, int y)
2068 if (gcol.isColourByLabel())
2071 g.fillRect(0, 0, width, height);
2072 // need an icon here.
2073 g.setColor(gcol.getMaxColour());
2075 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2077 // g.setFont(g.getFont().deriveFont(
2078 // AffineTransform.getScaleInstance(
2079 // width/g.getFontMetrics().stringWidth("Label"),
2080 // height/g.getFontMetrics().getHeight())));
2082 g.drawString(MessageManager.getString("label.label"), 0, 0);
2087 Color minCol = gcol.getMinColour();
2089 g.fillRect(0, 0, s1, height);
2092 g.setColor(Color.white);
2093 g.fillRect(s1, 0, e1 - s1, height);
2095 g.setColor(gcol.getMaxColour());
2096 g.fillRect(0, e1, width - e1, height);