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.AbstractButton;
86 import javax.swing.AbstractCellEditor;
87 import javax.swing.BorderFactory;
88 import javax.swing.Icon;
89 import javax.swing.JButton;
90 import javax.swing.JCheckBox;
91 import javax.swing.JColorChooser;
92 import javax.swing.JDialog;
93 import javax.swing.JInternalFrame;
94 import javax.swing.JLabel;
95 import javax.swing.JLayeredPane;
96 import javax.swing.JMenuItem;
97 import javax.swing.JPanel;
98 import javax.swing.JPopupMenu;
99 import javax.swing.JScrollPane;
100 import javax.swing.JSlider;
101 import javax.swing.JTable;
102 import javax.swing.ListSelectionModel;
103 import javax.swing.SwingConstants;
104 import javax.swing.border.Border;
105 import javax.swing.event.ChangeEvent;
106 import javax.swing.event.ChangeListener;
107 import javax.swing.table.AbstractTableModel;
108 import javax.swing.table.JTableHeader;
109 import javax.swing.table.TableCellEditor;
110 import javax.swing.table.TableCellRenderer;
111 import javax.swing.table.TableColumn;
112 import javax.xml.bind.JAXBContext;
113 import javax.xml.bind.JAXBElement;
114 import javax.xml.bind.Marshaller;
115 import javax.xml.stream.XMLInputFactory;
116 import javax.xml.stream.XMLStreamReader;
118 public class FeatureSettings extends JPanel
119 implements FeatureSettingsControllerI, FeatureSettingsControllerGuiI
121 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
122 .getString("label.sequence_feature_colours");
125 * column indices of fields in Feature Settings table
127 static final int TYPE_COLUMN = 0;
129 static final int COLOUR_COLUMN = 1;
131 static final int FILTER_COLUMN = 2;
133 static final int SHOW_COLUMN = 3;
135 private static final int COLUMN_COUNT = 4;
137 private static final int MIN_WIDTH = 400;
139 private static final int MIN_HEIGHT = 400;
141 private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
143 final FeatureRenderer fr;
145 public final AlignFrame af;
148 * 'original' fields hold settings to restore on Cancel
150 Object[][] originalData;
152 private float originalTransparency;
154 private ViewStyleI originalViewStyle;
156 private Map<String, FeatureMatcherSetI> originalFilters;
158 final JInternalFrame frame;
160 JScrollPane scrollPane = new JScrollPane();
166 JSlider transparency = new JSlider();
169 * when true, constructor is still executing - so ignore UI events
171 protected volatile boolean inConstruction = true;
173 int selectedRow = -1;
175 boolean resettingTable = false;
178 * true when Feature Settings are updating from feature renderer
180 private boolean handlingUpdate = false;
183 * holds {featureCount, totalExtent} for each feature type
185 Map<String, float[]> typeWidth = null;
187 private void storeOriginalSettings()
189 // save transparency for restore on Cancel
190 originalTransparency = fr.getTransparency();
192 updateTransparencySliderFromFR();
194 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
195 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
198 private void updateTransparencySliderFromFR()
200 boolean incon = inConstruction;
201 inConstruction = true;
203 int transparencyAsPercent = (int) (fr.getTransparency() * 100);
204 transparency.setValue(100 - transparencyAsPercent);
205 inConstruction = incon;
212 public FeatureSettings(AlignFrame alignFrame)
214 this.af = alignFrame;
215 fr = af.getFeatureRenderer();
217 storeOriginalSettings();
222 } catch (Exception ex)
224 ex.printStackTrace();
230 public String getToolTipText(MouseEvent e)
233 int column = table.columnAtPoint(e.getPoint());
234 int row = table.rowAtPoint(e.getPoint());
239 tip = JvSwingUtils.wrapTooltip(true, MessageManager
240 .getString("label.feature_settings_click_drag"));
243 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
245 tip = getColorTooltip(colour, true);
248 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
252 .getString("label.configure_feature_tooltip")
263 * Position the tooltip near the bottom edge of, and half way across, the
267 public Point getToolTipLocation(MouseEvent e)
269 Point point = e.getPoint();
270 int column = table.columnAtPoint(point);
271 int row = table.rowAtPoint(point);
272 Rectangle r = getCellRect(row, column, false);
273 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
277 JTableHeader tableHeader = table.getTableHeader();
278 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
279 tableHeader.setReorderingAllowed(false);
280 table.setFont(new Font("Verdana", Font.PLAIN, 12));
282 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
283 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
285 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
286 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
288 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
289 new ColorRenderer(), new ColorEditor(this));
290 table.addColumn(colourColumn);
292 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
293 new FilterRenderer(), new FilterEditor(this));
294 table.addColumn(filterColumn);
296 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
298 table.addMouseListener(new MouseAdapter()
301 public void mousePressed(MouseEvent evt)
303 Point pt = evt.getPoint();
304 selectedRow = table.rowAtPoint(pt);
305 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
306 if (evt.isPopupTrigger())
308 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
309 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
312 else if (evt.getClickCount() == 2
313 && table.columnAtPoint(pt) == TYPE_COLUMN)
315 boolean invertSelection = evt.isAltDown();
316 boolean toggleSelection = Platform.isControlDown(evt);
317 boolean extendSelection = evt.isShiftDown();
318 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
319 invertSelection, extendSelection, toggleSelection, type);
320 fr.ap.av.sendSelection();
324 // isPopupTrigger fires on mouseReleased on Windows
326 public void mouseReleased(MouseEvent evt)
328 selectedRow = table.rowAtPoint(evt.getPoint());
329 if (evt.isPopupTrigger())
331 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
332 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
333 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
339 table.addMouseMotionListener(new MouseMotionAdapter()
342 public void mouseDragged(MouseEvent evt)
344 int newRow = table.rowAtPoint(evt.getPoint());
345 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
348 * reposition 'selectedRow' to 'newRow' (the dragged to location)
349 * this could be more than one row away for a very fast drag action
350 * so just swap it with adjacent rows until we get it there
352 Object[][] data = ((FeatureTableModel) table.getModel())
354 int direction = newRow < selectedRow ? -1 : 1;
355 for (int i = selectedRow; i != newRow; i += direction)
357 Object[] temp = data[i];
358 data[i] = data[i + direction];
359 data[i + direction] = temp;
361 updateFeatureRenderer(data);
363 selectedRow = newRow;
367 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
368 // MessageManager.getString("label.feature_settings_click_drag")));
369 scrollPane.setViewportView(table);
371 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
373 fr.findAllFeatures(true); // display everything!
376 discoverAllFeatureData();
377 final FeatureSettings fs = this;
378 fr.addPropertyChangeListener(change = new PropertyChangeListener()
381 public void propertyChange(PropertyChangeEvent evt)
383 if (!fs.resettingTable && !fs.handlingUpdate)
385 fs.handlingUpdate = true;
387 // new groups may be added with new sequence feature types only
388 fs.handlingUpdate = false;
393 SplitContainerI splitframe = af.getSplitViewContainer();
394 if (splitframe != null)
396 frame = null; // keeps eclipse happy
397 splitframe.addFeatureSettingsUI(this);
401 frame = new JInternalFrame();
402 frame.setContentPane(this);
403 Rectangle bounds = af.getFeatureSettingsGeometry();
405 if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels(
406 af.alignPanel.av.getSequenceSetId()).length > 1)
408 title = MessageManager.formatMessage(
409 "label.sequence_feature_settings_for_view",
410 af.alignPanel.getViewName());
414 title = MessageManager.getString("label.sequence_feature_settings");
418 if (Platform.isAMac())
420 Desktop.addInternalFrame(frame, title, 600, 480);
424 Desktop.addInternalFrame(frame, title, 600, 450);
429 Desktop.addInternalFrame(frame, title,
430 false, bounds.width, bounds.height);
431 frame.setBounds(bounds);
432 frame.setVisible(true);
434 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
436 frame.addInternalFrameListener(
437 new javax.swing.event.InternalFrameAdapter()
440 public void internalFrameClosed(
441 javax.swing.event.InternalFrameEvent evt)
443 featureSettings_isClosed();
446 frame.setLayer(JLayeredPane.PALETTE_LAYER);
448 inConstruction = false;
451 PropertyChangeListener change;
453 private JCheckBox showComplementOnTop;
455 private AbstractButton showComplement;
457 private void updateComplementButtons()
459 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
461 .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
465 public AlignViewControllerGuiI getAlignframe()
471 public void featureSettings_isClosed()
473 fr.removePropertyChangeListener(change);
477 protected void popupSort(final int rowSelected, final String type,
478 final Object typeCol, final Map<String, float[][]> minmax, int x,
481 JPopupMenu men = new JPopupMenu(MessageManager
482 .formatMessage("label.settings_for_param", new String[]
484 JMenuItem scr = new JMenuItem(
485 MessageManager.getString("label.sort_by_score"));
487 final FeatureSettings me = this;
488 scr.addActionListener(new ActionListener()
492 public void actionPerformed(ActionEvent e)
495 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
500 JMenuItem dens = new JMenuItem(
501 MessageManager.getString("label.sort_by_density"));
502 dens.addActionListener(new ActionListener()
506 public void actionPerformed(ActionEvent e)
509 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
516 JMenuItem selCols = new JMenuItem(
517 MessageManager.getString("label.select_columns_containing"));
518 selCols.addActionListener(new ActionListener()
521 public void actionPerformed(ActionEvent arg0)
523 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
525 fr.ap.av.sendSelection();
528 JMenuItem clearCols = new JMenuItem(MessageManager
529 .getString("label.select_columns_not_containing"));
530 clearCols.addActionListener(new ActionListener()
533 public void actionPerformed(ActionEvent arg0)
535 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
537 fr.ap.av.sendSelection();
540 JMenuItem hideCols = new JMenuItem(
541 MessageManager.getString("label.hide_columns_containing"));
542 hideCols.addActionListener(new ActionListener()
545 public void actionPerformed(ActionEvent arg0)
547 fr.ap.alignFrame.hideFeatureColumns(type, true);
548 fr.ap.av.sendSelection();
551 JMenuItem hideOtherCols = new JMenuItem(
552 MessageManager.getString("label.hide_columns_not_containing"));
553 hideOtherCols.addActionListener(new ActionListener()
556 public void actionPerformed(ActionEvent arg0)
558 fr.ap.alignFrame.hideFeatureColumns(type, false);
559 fr.ap.av.sendSelection();
565 men.add(hideOtherCols);
566 men.show(table, x, y);
570 synchronized public void discoverAllFeatureData()
572 Set<String> allGroups = new HashSet<>();
573 AlignmentI alignment = af.getViewport().getAlignment();
575 for (int i = 0; i < alignment.getHeight(); i++)
577 SequenceI seq = alignment.getSequenceAt(i);
578 for (String group : seq.getFeatures().getFeatureGroups(true))
580 if (group != null && !allGroups.contains(group))
582 allGroups.add(group);
583 checkGroupState(group);
594 * Synchronise gui group list and check visibility of group
597 * @return true if group is visible
599 private boolean checkGroupState(String group)
601 boolean visible = fr.checkGroupVisibility(group, true);
603 for (int g = 0; g < groupPanel.getComponentCount(); g++)
605 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
607 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
612 final String grp = group;
613 final JCheckBox check = new JCheckBox(group, visible);
614 check.setFont(new Font("Serif", Font.BOLD, 12));
615 check.setToolTipText(group);
616 check.addItemListener(new ItemListener()
619 public void itemStateChanged(ItemEvent evt)
621 fr.setGroupVisibility(check.getText(), check.isSelected());
622 resetTable(new String[] { grp });
626 groupPanel.add(check);
630 synchronized void resetTable(String[] groupChanged)
636 resettingTable = true;
637 typeWidth = new Hashtable<>();
638 // TODO: change avWidth calculation to 'per-sequence' average and use long
641 Set<String> displayableTypes = new HashSet<>();
642 Set<String> foundGroups = new HashSet<>();
645 * determine which feature types may be visible depending on
646 * which groups are selected, and recompute average width data
648 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
651 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
654 * get the sequence's groups for positional features
655 * and keep track of which groups are visible
657 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
658 Set<String> visibleGroups = new HashSet<>();
659 for (String group : groups)
661 if (group == null || checkGroupState(group))
663 visibleGroups.add(group);
666 foundGroups.addAll(groups);
669 * get distinct feature types for visible groups
670 * record distinct visible types, and their count and total length
672 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
673 visibleGroups.toArray(new String[visibleGroups.size()]));
674 for (String type : types)
676 displayableTypes.add(type);
677 float[] avWidth = typeWidth.get(type);
680 avWidth = new float[2];
681 typeWidth.put(type, avWidth);
683 // todo this could include features with a non-visible group
684 // - do we greatly care?
685 // todo should we include non-displayable features here, and only
686 // update when features are added?
687 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
688 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
692 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
695 if (fr.hasRenderOrder())
699 fr.findAllFeatures(groupChanged != null); // prod to update
700 // colourschemes. but don't
702 // First add the checks in the previous render order,
703 // in case the window has been closed and reopened
705 List<String> frl = fr.getRenderOrder();
706 for (int ro = frl.size() - 1; ro > -1; ro--)
708 String type = frl.get(ro);
710 if (!displayableTypes.contains(type))
715 data[dataIndex][TYPE_COLUMN] = type;
716 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
717 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
718 data[dataIndex][FILTER_COLUMN] = featureFilter == null
719 ? new FeatureMatcherSet()
721 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
722 af.getViewport().getFeaturesDisplayed().isVisible(type));
724 displayableTypes.remove(type);
729 * process any extra features belonging only to
730 * a group which was just selected
732 while (!displayableTypes.isEmpty())
734 String type = displayableTypes.iterator().next();
735 data[dataIndex][TYPE_COLUMN] = type;
737 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
738 if (data[dataIndex][COLOUR_COLUMN] == null)
740 // "Colour has been updated in another view!!"
741 fr.clearRenderOrder();
744 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
745 data[dataIndex][FILTER_COLUMN] = featureFilter == null
746 ? new FeatureMatcherSet()
748 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
750 displayableTypes.remove(type);
753 if (originalData == null)
755 originalData = new Object[data.length][COLUMN_COUNT];
756 for (int i = 0; i < data.length; i++)
758 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
763 updateOriginalData(data);
766 table.setModel(new FeatureTableModel(data));
767 table.getColumnModel().getColumn(0).setPreferredWidth(200);
769 groupPanel.setLayout(
770 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
771 pruneGroups(foundGroups);
772 groupPanel.validate();
774 updateFeatureRenderer(data, groupChanged != null);
775 resettingTable = false;
779 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
780 * have been made outwith this dialog
782 * <li>a new feature type added (and made visible)</li>
783 * <li>a feature colour changed (in the Amend Features dialog)</li>
788 protected void updateOriginalData(Object[][] foundData)
790 // todo LinkedHashMap instead of Object[][] would be nice
792 Object[][] currentData = ((FeatureTableModel) table.getModel())
794 for (Object[] row : foundData)
796 String type = (String) row[TYPE_COLUMN];
797 boolean found = false;
798 for (Object[] current : currentData)
800 if (type.equals(current[TYPE_COLUMN]))
804 * currently dependent on object equality here;
805 * really need an equals method on FeatureColour
807 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
810 * feature colour has changed externally - update originalData
812 for (Object[] original : originalData)
814 if (type.equals(original[TYPE_COLUMN]))
816 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
827 * new feature detected - add to original data (on top)
829 Object[][] newData = new Object[originalData.length
831 for (int i = 0; i < originalData.length; i++)
833 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
837 originalData = newData;
843 * Remove from the groups panel any checkboxes for groups that are not in the
844 * foundGroups set. This enables removing a group from the display when the last
845 * feature in that group is deleted.
849 protected void pruneGroups(Set<String> foundGroups)
851 for (int g = 0; g < groupPanel.getComponentCount(); g++)
853 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
854 if (!foundGroups.contains(checkbox.getText()))
856 groupPanel.remove(checkbox);
862 * reorder data based on the featureRenderers global priority list.
866 private void ensureOrder(Object[][] data)
868 boolean sort = false;
869 float[] order = new float[data.length];
870 for (int i = 0; i < order.length; i++)
872 order[i] = fr.getOrder(data[i][0].toString());
875 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
879 sort = sort || order[i - 1] > order[i];
884 jalview.util.QuickSort.sort(order, data);
889 * Offers a file chooser dialog, and then loads the feature colours and
890 * filters from file in XML format and unmarshals to Jalview feature settings
894 JalviewFileChooser chooser = new JalviewFileChooser("fc",
895 SEQUENCE_FEATURE_COLOURS);
896 chooser.setFileView(new JalviewFileView());
897 chooser.setDialogTitle(
898 MessageManager.getString("label.load_feature_colours"));
899 chooser.setToolTipText(MessageManager.getString("action.load"));
901 int value = chooser.showOpenDialog(this);
903 if (value == JalviewFileChooser.APPROVE_OPTION)
905 File file = chooser.getSelectedFile();
911 * Loads feature colours and filters from XML stored in the given file
919 InputStreamReader in = new InputStreamReader(
920 new FileInputStream(file), "UTF-8");
922 JAXBContext jc = JAXBContext
923 .newInstance("jalview.xml.binding.jalview");
924 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
925 XMLStreamReader streamReader = XMLInputFactory.newInstance()
926 .createXMLStreamReader(in);
927 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
928 JalviewUserColours.class);
929 JalviewUserColours jucs = jbe.getValue();
931 // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
934 * load feature colours
936 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
938 Colour newcol = jucs.getColour().get(i);
939 FeatureColourI colour = jalview.project.Jalview2XML
940 .parseColour(newcol);
941 fr.setColour(newcol.getName(), colour);
942 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
946 * load feature filters; loaded filters will replace any that are
947 * currently defined, other defined filters are left unchanged
949 for (int i = 0; i < jucs.getFilter().size(); i++)
951 Filter filterModel = jucs.getFilter().get(i);
952 String featureType = filterModel.getFeatureType();
953 FeatureMatcherSetI filter = jalview.project.Jalview2XML
954 .parseFilter(featureType, filterModel.getMatcherSet());
955 if (!filter.isEmpty())
957 fr.setFeatureFilter(featureType, filter);
962 * update feature settings table
967 Object[][] data = ((FeatureTableModel) table.getModel())
970 updateFeatureRenderer(data, false);
973 } catch (Exception ex)
975 System.out.println("Error loading User Colour File\n" + ex);
980 * Offers a file chooser dialog, and then saves the current feature colours
981 * and any filters to the selected file in XML format
985 JalviewFileChooser chooser = new JalviewFileChooser("fc",
986 SEQUENCE_FEATURE_COLOURS);
987 chooser.setFileView(new JalviewFileView());
988 chooser.setDialogTitle(
989 MessageManager.getString("label.save_feature_colours"));
990 chooser.setToolTipText(MessageManager.getString("action.save"));
992 int value = chooser.showSaveDialog(this);
994 if (value == JalviewFileChooser.APPROVE_OPTION)
996 save(chooser.getSelectedFile());
1001 * Saves feature colours and filters to the given file
1005 void save(File file)
1007 JalviewUserColours ucs = new JalviewUserColours();
1008 ucs.setSchemeName("Sequence Features");
1011 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1012 new FileOutputStream(file), "UTF-8"));
1015 * sort feature types by colour order, from 0 (highest)
1018 Set<String> fr_colours = fr.getAllFeatureColours();
1019 String[] sortedTypes = fr_colours
1020 .toArray(new String[fr_colours.size()]);
1021 Arrays.sort(sortedTypes, new Comparator<String>()
1024 public int compare(String type1, String type2)
1026 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1031 * save feature colours
1033 for (String featureType : sortedTypes)
1035 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1036 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1038 ucs.getColour().add(col);
1042 * save any feature filters
1044 for (String featureType : sortedTypes)
1046 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1047 if (filter != null && !filter.isEmpty())
1049 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1050 FeatureMatcherI firstMatcher = iterator.next();
1051 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1052 .marshalFilter(firstMatcher, iterator,
1054 Filter filterModel = new Filter();
1055 filterModel.setFeatureType(featureType);
1056 filterModel.setMatcherSet(ms);
1057 ucs.getFilter().add(filterModel);
1060 JAXBContext jaxbContext = JAXBContext
1061 .newInstance(JalviewUserColours.class);
1062 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1063 jaxbMarshaller.marshal(
1064 new ObjectFactory().createJalviewUserColours(ucs), out);
1066 // jaxbMarshaller.marshal(object, pout);
1067 // marshaller.marshal(object);
1070 // ucs.marshal(out);
1072 } catch (Exception ex)
1074 ex.printStackTrace();
1078 public void invertSelection()
1080 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1081 for (int i = 0; i < data.length; i++)
1083 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1085 updateFeatureRenderer(data, true);
1089 public void orderByAvWidth()
1091 if (table == null || table.getModel() == null)
1095 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1096 float[] width = new float[data.length];
1100 for (int i = 0; i < data.length; i++)
1102 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1105 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1106 // weight - but have to make per
1107 // sequence, too (awidth[2])
1108 // if (width[i]==1) // hack to distinguish single width sequences.
1119 boolean sort = false;
1120 for (int i = 0; i < width.length; i++)
1122 // awidth = (float[]) typeWidth.get(data[i][0]);
1125 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1128 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1134 width[i] /= max; // normalize
1135 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1139 sort = sort || width[i - 1] > width[i];
1144 jalview.util.QuickSort.sort(width, data);
1145 // update global priority order
1148 updateFeatureRenderer(data, false);
1153 * close ourselves but leave any existing UI handlers (e.g a CDS/Protein tabbed
1154 * feature settings dialog) intact
1156 public void closeOldSettings()
1162 * close the feature settings dialog (and any containing frame)
1169 private void closeDialog(boolean closeContainingFrame)
1175 af.setFeatureSettingsGeometry(frame.getBounds());
1176 frame.setClosed(true);
1180 SplitContainerI sc = af.getSplitViewContainer();
1181 sc.closeFeatureSettings(this, closeContainingFrame);
1182 af.featureSettings = null;
1184 } catch (Exception exe)
1190 public void updateFeatureRenderer(Object[][] data)
1192 updateFeatureRenderer(data, true);
1196 * Update the priority order of features; only repaint if this changed the order
1197 * of visible features
1202 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1204 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1206 if (fr.setFeaturePriority(rowData, visibleNew))
1213 * Converts table data into an array of data beans
1215 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1217 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1218 for (int i = 0; i < data.length; i++)
1220 String type = (String) data[i][TYPE_COLUMN];
1221 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1222 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1223 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1224 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1230 private void jbInit() throws Exception
1232 this.setLayout(new BorderLayout());
1234 final boolean hasComplement = af.getViewport()
1235 .getCodingComplement() != null;
1237 JPanel settingsPane = new JPanel();
1238 settingsPane.setLayout(new BorderLayout());
1240 JPanel bigPanel = new JPanel();
1241 bigPanel.setLayout(new BorderLayout());
1243 groupPanel = new JPanel();
1244 bigPanel.add(groupPanel, BorderLayout.NORTH);
1246 JButton invert = new JButton(
1247 MessageManager.getString("label.invert_selection"));
1248 invert.setFont(JvSwingUtils.getLabelFont());
1249 invert.addActionListener(new ActionListener()
1252 public void actionPerformed(ActionEvent e)
1258 JButton optimizeOrder = new JButton(
1259 MessageManager.getString("label.optimise_order"));
1260 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1261 optimizeOrder.addActionListener(new ActionListener()
1264 public void actionPerformed(ActionEvent e)
1270 JButton sortByScore = new JButton(
1271 MessageManager.getString("label.seq_sort_by_score"));
1272 sortByScore.setFont(JvSwingUtils.getLabelFont());
1273 sortByScore.addActionListener(new ActionListener()
1276 public void actionPerformed(ActionEvent e)
1278 af.avc.sortAlignmentByFeatureScore(null);
1281 JButton sortByDens = new JButton(
1282 MessageManager.getString("label.sequence_sort_by_density"));
1283 sortByDens.setFont(JvSwingUtils.getLabelFont());
1284 sortByDens.addActionListener(new ActionListener()
1287 public void actionPerformed(ActionEvent e)
1289 af.avc.sortAlignmentByFeatureDensity(null);
1293 JButton help = new JButton(MessageManager.getString("action.help"));
1294 help.setFont(JvSwingUtils.getLabelFont());
1295 help.addActionListener(new ActionListener()
1298 public void actionPerformed(ActionEvent e)
1302 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1303 } catch (HelpSetException e1)
1305 e1.printStackTrace();
1309 // Cancel for a SplitFrame should just revert changes to the currently displayed
1310 // settings. May want to do this for either or both - so need a splitview
1311 // feature settings cancel/OK.
1312 JButton cancel = new JButton(MessageManager
1313 .getString(hasComplement ? "action.revert" : "action.cancel"));
1314 cancel.setToolTipText(MessageManager.getString(hasComplement
1315 ? "action.undo_changes_to_feature_settings"
1316 : "action.undo_changes_to_feature_settings_and_close_the_dialog"));
1317 cancel.setFont(JvSwingUtils.getLabelFont());
1318 // TODO: disable cancel (and apply!) until current settings are different
1319 cancel.addActionListener(new ActionListener()
1322 public void actionPerformed(ActionEvent e)
1332 // Cancel for the whole dialog should cancel both CDS and Protein.
1333 // OK for an individual feature settings just applies changes, but dialog
1335 JButton ok = new JButton(MessageManager
1336 .getString(hasComplement ? "action.apply" : "action.ok"));
1337 ok.setFont(JvSwingUtils.getLabelFont());
1338 ok.addActionListener(new ActionListener()
1341 public void actionPerformed(ActionEvent e)
1349 storeOriginalSettings();
1354 JButton loadColours = new JButton(
1355 MessageManager.getString("label.load_colours"));
1356 loadColours.setFont(JvSwingUtils.getLabelFont());
1357 loadColours.setToolTipText(
1358 MessageManager.getString("label.load_colours_tooltip"));
1359 loadColours.addActionListener(new ActionListener()
1362 public void actionPerformed(ActionEvent e)
1368 JButton saveColours = new JButton(
1369 MessageManager.getString("label.save_colours"));
1370 saveColours.setFont(JvSwingUtils.getLabelFont());
1371 saveColours.setToolTipText(
1372 MessageManager.getString("label.save_colours_tooltip"));
1373 saveColours.addActionListener(new ActionListener()
1376 public void actionPerformed(ActionEvent e)
1381 transparency.addChangeListener(new ChangeListener()
1384 public void stateChanged(ChangeEvent evt)
1386 if (!inConstruction)
1388 fr.setTransparency((100 - transparency.getValue()) / 100f);
1394 transparency.setMaximum(70);
1395 transparency.setToolTipText(
1396 MessageManager.getString("label.transparency_tip"));
1398 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1399 String text = MessageManager.formatMessage("label.show_linked_features",
1401 ? MessageManager.getString("label.protein")
1404 showComplement = new JCheckBox(text);
1405 showComplement.addActionListener(new ActionListener()
1408 public void actionPerformed(ActionEvent e)
1411 .setShowComplementFeatures(showComplement.isSelected());
1416 showComplementOnTop = new JCheckBox(
1417 MessageManager.getString("label.on_top"));
1418 showComplementOnTop.addActionListener(new ActionListener()
1421 public void actionPerformed(ActionEvent e)
1423 af.getViewport().setShowComplementFeaturesOnTop(
1424 showComplementOnTop.isSelected());
1428 // JButton viewComplementSettings = new JButton(MessageManager
1429 // .formatMessage("label.show_linked_feature_settings",
1431 // ? MessageManager.getString("label.protein")
1434 // viewComplementSettings.addActionListener(new ActionListener()
1438 // public void actionPerformed(ActionEvent e)
1440 // AlignViewControllerGuiI complAf = af.getSplitViewContainer()
1441 // .getComplementAlignFrame(af);
1442 // FeatureSettings complFeatureSettings = (FeatureSettings) complAf
1443 // .getFeatureSettingsUI();
1444 // if (complFeatureSettings != null)
1446 // complFeatureSettings.frame.setVisible(true);
1449 // complFeatureSettings.frame.setSelected(true);
1451 // } catch (Exception q)
1456 // complAf.showFeatureSettingsUI();
1460 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1461 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1463 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1464 transbuttons.add(optimizeOrder);
1465 transbuttons.add(invert);
1466 transbuttons.add(sortByScore);
1467 transbuttons.add(sortByDens);
1468 transbuttons.add(help);
1470 JPanel transPanelLeft = new JPanel(
1471 new GridLayout(hasComplement ? 4 : 2, 1));
1472 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1473 transPanelLeft.add(transparency);
1476 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1477 cp.add(showComplement);
1478 cp.add(showComplementOnTop);
1479 transPanelLeft.add(cp);
1481 lowerPanel.add(transPanelLeft);
1482 lowerPanel.add(transbuttons);
1484 JPanel buttonPanel = new JPanel();
1485 buttonPanel.add(ok);
1486 buttonPanel.add(cancel);
1487 buttonPanel.add(loadColours);
1488 buttonPanel.add(saveColours);
1489 bigPanel.add(scrollPane, BorderLayout.CENTER);
1490 settingsPane.add(bigPanel, BorderLayout.CENTER);
1491 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1492 this.add(settingsPane);
1496 * Repaints alignment, structure and overview (if shown). If there is a
1497 * complementary view which is showing this view's features, then also
1500 void refreshDisplay()
1502 af.alignPanel.paintAlignment(true, true);
1503 AlignViewportI complement = af.getViewport().getCodingComplement();
1504 if (complement != null && complement.isShowComplementFeatures())
1506 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1507 af2.alignPanel.paintAlignment(true, true);
1512 * Answers a suitable tooltip to show on the colour cell of the table
1516 * if true include 'click to edit' and similar text
1519 public static String getColorTooltip(FeatureColourI fcol,
1526 if (fcol.isSimpleColour())
1528 return withHint ? BASE_TOOLTIP : null;
1530 String description = fcol.getDescription();
1531 description = description.replaceAll("<", "<");
1532 description = description.replaceAll(">", ">");
1533 StringBuilder tt = new StringBuilder(description);
1536 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1538 return JvSwingUtils.wrapTooltip(true, tt.toString());
1541 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1544 boolean thr = false;
1545 StringBuilder tx = new StringBuilder();
1547 if (gcol.isColourByAttribute())
1549 tx.append(FeatureMatcher
1550 .toAttributeDisplayName(gcol.getAttributeName()));
1552 else if (!gcol.isColourByLabel())
1554 tx.append(MessageManager.getString("label.score"));
1557 if (gcol.isAboveThreshold())
1562 if (gcol.isBelowThreshold())
1567 if (gcol.isColourByLabel())
1573 if (!gcol.isColourByAttribute())
1581 Color newColor = gcol.getMaxColour();
1582 comp.setBackground(newColor);
1583 // System.err.println("Width is " + w / 2);
1584 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1585 comp.setIcon(ficon);
1586 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1587 // + newColor.getGreen() + ", " + newColor.getBlue()
1588 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1589 // + ", " + minCol.getBlue() + ")");
1591 comp.setHorizontalAlignment(SwingConstants.CENTER);
1592 comp.setText(tx.toString());
1595 // ///////////////////////////////////////////////////////////////////////
1596 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1597 // ///////////////////////////////////////////////////////////////////////
1598 class FeatureTableModel extends AbstractTableModel
1600 private String[] columnNames = {
1601 MessageManager.getString("label.feature_type"),
1602 MessageManager.getString("action.colour"),
1603 MessageManager.getString("label.configuration"),
1604 MessageManager.getString("label.show") };
1606 private Object[][] data;
1608 FeatureTableModel(Object[][] data)
1613 public Object[][] getData()
1618 public void setData(Object[][] data)
1624 public int getColumnCount()
1626 return columnNames.length;
1629 public Object[] getRow(int row)
1635 public int getRowCount()
1641 public String getColumnName(int col)
1643 return columnNames[col];
1647 public Object getValueAt(int row, int col)
1649 return data[row][col];
1653 * Answers the class of column c of the table
1656 public Class<?> getColumnClass(int c)
1661 return String.class;
1663 return FeatureColour.class;
1665 return FeatureMatcherSet.class;
1667 return Boolean.class;
1672 public boolean isCellEditable(int row, int col)
1674 return col == 0 ? false : true;
1678 public void setValueAt(Object value, int row, int col)
1680 data[row][col] = value;
1681 fireTableCellUpdated(row, col);
1682 updateFeatureRenderer(data);
1687 class ColorRenderer extends JLabel implements TableCellRenderer
1689 Border unselectedBorder = null;
1691 Border selectedBorder = null;
1693 public ColorRenderer()
1695 setOpaque(true); // MUST do this for background to show up.
1696 setHorizontalTextPosition(SwingConstants.CENTER);
1697 setVerticalTextPosition(SwingConstants.CENTER);
1701 public Component getTableCellRendererComponent(JTable tbl, Object color,
1702 boolean isSelected, boolean hasFocus, int row, int column)
1704 FeatureColourI cellColour = (FeatureColourI) color;
1706 setBackground(tbl.getBackground());
1707 if (!cellColour.isSimpleColour())
1709 Rectangle cr = tbl.getCellRect(row, column, false);
1710 FeatureSettings.renderGraduatedColor(this, cellColour,
1711 (int) cr.getWidth(), (int) cr.getHeight());
1717 setBackground(cellColour.getColour());
1721 if (selectedBorder == null)
1723 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1724 tbl.getSelectionBackground());
1726 setBorder(selectedBorder);
1730 if (unselectedBorder == null)
1732 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1733 tbl.getBackground());
1735 setBorder(unselectedBorder);
1742 class FilterRenderer extends JLabel implements TableCellRenderer
1744 javax.swing.border.Border unselectedBorder = null;
1746 javax.swing.border.Border selectedBorder = null;
1748 public FilterRenderer()
1750 setOpaque(true); // MUST do this for background to show up.
1751 setHorizontalTextPosition(SwingConstants.CENTER);
1752 setVerticalTextPosition(SwingConstants.CENTER);
1756 public Component getTableCellRendererComponent(JTable tbl,
1757 Object filter, boolean isSelected, boolean hasFocus, int row,
1760 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1762 String asText = theFilter.toString();
1763 setBackground(tbl.getBackground());
1764 this.setText(asText);
1769 if (selectedBorder == null)
1771 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1772 tbl.getSelectionBackground());
1774 setBorder(selectedBorder);
1778 if (unselectedBorder == null)
1780 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1781 tbl.getBackground());
1783 setBorder(unselectedBorder);
1791 * update comp using rendering settings from gcol
1796 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1798 int w = comp.getWidth(), h = comp.getHeight();
1801 w = (int) comp.getPreferredSize().getWidth();
1802 h = (int) comp.getPreferredSize().getHeight();
1809 renderGraduatedColor(comp, gcol, w, h);
1812 class ColorEditor extends AbstractCellEditor
1813 implements TableCellEditor, ActionListener
1817 FeatureColourI currentColor;
1819 FeatureTypeSettings chooser;
1825 JColorChooser colorChooser;
1829 protected static final String EDIT = "edit";
1831 int rowSelected = 0;
1833 public ColorEditor(FeatureSettings me)
1836 // Set up the editor (from the table's point of view),
1837 // which is a button.
1838 // This button brings up the color chooser dialog,
1839 // which is the editor from the user's point of view.
1840 button = new JButton();
1841 button.setActionCommand(EDIT);
1842 button.addActionListener(this);
1843 button.setBorderPainted(false);
1844 // Set up the dialog that the button brings up.
1845 colorChooser = new JColorChooser();
1846 dialog = JColorChooser.createDialog(button,
1847 MessageManager.getString("label.select_colour"), true, // modal
1848 colorChooser, this, // OK button handler
1849 null); // no CANCEL button handler
1853 * Handles events from the editor button and from the dialog's OK button.
1856 public void actionPerformed(ActionEvent e)
1858 // todo test e.getSource() instead here
1859 if (EDIT.equals(e.getActionCommand()))
1861 // The user has clicked the cell, so
1862 // bring up the dialog.
1863 if (currentColor.isSimpleColour())
1865 // bring up simple color chooser
1866 button.setBackground(currentColor.getColour());
1867 colorChooser.setColor(currentColor.getColour());
1868 dialog.setVisible(true);
1872 // bring up graduated chooser.
1873 chooser = new FeatureTypeSettings(me.fr, type);
1878 chooser.setRequestFocusEnabled(true);
1879 chooser.requestFocus();
1881 chooser.addActionListener(this);
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 fireEditingStopped();
2008 else if (e.getSource() instanceof Component)
2012 * after OK in variable colour dialog, any changes to filter
2013 * (or colours!) are already set in FeatureRenderer, so just
2014 * update table data without triggering updateFeatureRenderer
2016 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2017 currentFilter = me.fr.getFeatureFilter(type);
2018 if (currentFilter == null)
2020 currentFilter = new FeatureMatcherSet();
2022 Object[] data = ((FeatureTableModel) table.getModel())
2023 .getData()[rowSelected];
2024 data[COLOUR_COLUMN] = currentColor;
2025 data[FILTER_COLUMN] = currentFilter;
2026 fireEditingStopped();
2027 me.table.validate();
2032 public Object getCellEditorValue()
2034 return currentFilter;
2038 public Component getTableCellEditorComponent(JTable theTable, Object value,
2039 boolean isSelected, int row, int column)
2041 currentFilter = (FeatureMatcherSetI) value;
2042 this.rowSelected = row;
2043 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2044 button.setOpaque(true);
2045 button.setBackground(me.getBackground());
2046 button.setText(currentFilter.toString());
2047 button.setIcon(null);
2052 public boolean isOpen()
2054 if (af.getSplitViewContainer() != null)
2056 return af.getSplitViewContainer().isFeatureSettingsOpen();
2058 return frame != null && !frame.isClosed();
2062 public void revert()
2064 fr.setTransparency(originalTransparency);
2065 fr.setFeatureFilters(originalFilters);
2066 updateFeatureRenderer(originalData);
2067 af.getViewport().setViewStyle(originalViewStyle);
2068 updateTransparencySliderFromFR();
2069 updateComplementButtons();
2074 class FeatureIcon implements Icon
2076 FeatureColourI gcol;
2080 boolean midspace = false;
2082 int width = 50, height = 20;
2084 int s1, e1; // start and end of midpoint band for thresholded symbol
2086 Color mpcolour = Color.white;
2088 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2108 public int getIconWidth()
2114 public int getIconHeight()
2120 public void paintIcon(Component c, Graphics g, int x, int y)
2123 if (gcol.isColourByLabel())
2126 g.fillRect(0, 0, width, height);
2127 // need an icon here.
2128 g.setColor(gcol.getMaxColour());
2130 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2132 // g.setFont(g.getFont().deriveFont(
2133 // AffineTransform.getScaleInstance(
2134 // width/g.getFontMetrics().stringWidth("Label"),
2135 // height/g.getFontMetrics().getHeight())));
2137 g.drawString(MessageManager.getString("label.label"), 0, 0);
2142 Color minCol = gcol.getMinColour();
2144 g.fillRect(0, 0, s1, height);
2147 g.setColor(Color.white);
2148 g.fillRect(s1, 0, e1 - s1, height);
2150 g.setColor(gcol.getMaxColour());
2151 g.fillRect(0, e1, width - e1, height);