2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.SequenceI;
27 import jalview.datamodel.features.FeatureMatcherI;
28 import jalview.datamodel.features.FeatureMatcherSet;
29 import jalview.datamodel.features.FeatureMatcherSetI;
30 import jalview.gui.Help.HelpId;
31 import jalview.io.JalviewFileChooser;
32 import jalview.io.JalviewFileView;
33 import jalview.io.gff.SequenceOntologyFactory;
34 import jalview.schemabinding.version2.Filter;
35 import jalview.schemabinding.version2.JalviewUserColours;
36 import jalview.schemabinding.version2.MatcherSet;
37 import jalview.schemes.FeatureColour;
38 import jalview.util.MessageManager;
39 import jalview.util.Platform;
40 import jalview.viewmodel.AlignmentViewport;
41 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
42 import jalview.ws.DasSequenceFeatureFetcher;
43 import jalview.ws.dbsources.das.api.jalviewSourceI;
45 import java.awt.BorderLayout;
46 import java.awt.Color;
47 import java.awt.Component;
48 import java.awt.Dimension;
50 import java.awt.Graphics;
51 import java.awt.GridLayout;
52 import java.awt.Point;
53 import java.awt.Rectangle;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.ActionListener;
56 import java.awt.event.ItemEvent;
57 import java.awt.event.ItemListener;
58 import java.awt.event.MouseAdapter;
59 import java.awt.event.MouseEvent;
60 import java.awt.event.MouseMotionAdapter;
61 import java.beans.PropertyChangeEvent;
62 import java.beans.PropertyChangeListener;
64 import java.io.FileInputStream;
65 import java.io.FileOutputStream;
66 import java.io.InputStreamReader;
67 import java.io.OutputStreamWriter;
68 import java.io.PrintWriter;
69 import java.util.ArrayList;
70 import java.util.Arrays;
71 import java.util.Comparator;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.Hashtable;
75 import java.util.Iterator;
76 import java.util.List;
79 import java.util.Vector;
81 import javax.help.HelpSetException;
82 import javax.swing.AbstractCellEditor;
83 import javax.swing.BorderFactory;
84 import javax.swing.Icon;
85 import javax.swing.JButton;
86 import javax.swing.JCheckBox;
87 import javax.swing.JCheckBoxMenuItem;
88 import javax.swing.JColorChooser;
89 import javax.swing.JDialog;
90 import javax.swing.JInternalFrame;
91 import javax.swing.JLabel;
92 import javax.swing.JLayeredPane;
93 import javax.swing.JMenuItem;
94 import javax.swing.JPanel;
95 import javax.swing.JPopupMenu;
96 import javax.swing.JScrollPane;
97 import javax.swing.JSlider;
98 import javax.swing.JTable;
99 import javax.swing.ListSelectionModel;
100 import javax.swing.RowFilter;
101 import javax.swing.SwingConstants;
102 import javax.swing.SwingUtilities;
103 import javax.swing.event.ChangeEvent;
104 import javax.swing.event.ChangeListener;
105 import javax.swing.table.AbstractTableModel;
106 import javax.swing.table.TableCellEditor;
107 import javax.swing.table.TableCellRenderer;
108 import javax.swing.table.TableColumn;
109 import javax.swing.table.TableRowSorter;
111 public class FeatureSettings extends JPanel
112 implements FeatureSettingsControllerI
114 private static final Font VERDANA_12 = new Font("Verdana", Font.PLAIN, 12);
116 private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
117 .getString("label.sequence_feature_colours");
120 * column indices of fields in Feature Settings table
122 static final int TYPE_COLUMN = 0;
124 static final int COLOUR_COLUMN = 1;
126 static final int FILTER_COLUMN = 2;
128 static final int SHOW_COLUMN = 3;
130 private static final int COLUMN_COUNT = 4;
132 private static final int MIN_WIDTH = 400;
134 private static final int MIN_HEIGHT = 400;
136 DasSourceBrowser dassourceBrowser;
138 DasSequenceFeatureFetcher dasFeatureFetcher;
140 JPanel dasSettingsPane = new JPanel();
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 Map<String, FeatureMatcherSetI> originalFilters;
155 final JInternalFrame frame;
157 JScrollPane scrollPane = new JScrollPane();
163 JSlider transparency = new JSlider();
166 * when true, constructor is still executing - so ignore UI events
168 protected volatile boolean inConstruction = true;
170 int selectedRow = -1;
172 JButton fetchDAS = new JButton();
174 JButton saveDAS = new JButton();
176 JButton cancelDAS = new JButton();
178 boolean resettingTable = false;
181 * true when Feature Settings are updating from feature renderer
183 private boolean handlingUpdate = false;
186 * holds {featureCount, totalExtent} for each feature type
188 Map<String, float[]> typeWidth = null;
191 * if true, 'child' feature types are not displayed
193 JCheckBox summaryView;
196 * those feature types that do not have a parent feature type present
197 * (as determined by an Ontology relationship)
199 List<String> topLevelTypes;
206 public FeatureSettings(AlignFrame alignFrame)
208 this.af = alignFrame;
209 fr = af.getFeatureRenderer();
211 // save transparency for restore on Cancel
212 originalTransparency = fr.getTransparency();
213 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
214 transparency.setMaximum(100 - originalTransparencyAsPercent);
216 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
218 topLevelTypes = new ArrayList<>();
223 } catch (Exception ex)
225 ex.printStackTrace();
230 scrollPane.setViewportView(table);
232 dassourceBrowser = new DasSourceBrowser(this);
233 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
235 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
237 fr.findAllFeatures(true); // display everything!
240 discoverAllFeatureData();
241 final PropertyChangeListener change;
242 final FeatureSettings fs = this;
243 fr.addPropertyChangeListener(change = new PropertyChangeListener()
246 public void propertyChange(PropertyChangeEvent evt)
248 if (!fs.resettingTable && !fs.handlingUpdate)
250 fs.handlingUpdate = true;
252 // new groups may be added with new sequence feature types only
253 fs.handlingUpdate = false;
258 frame = new JInternalFrame();
259 frame.setContentPane(this);
260 if (Platform.isAMac())
262 Desktop.addInternalFrame(frame,
263 MessageManager.getString("label.sequence_feature_settings"),
268 Desktop.addInternalFrame(frame,
269 MessageManager.getString("label.sequence_feature_settings"),
272 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
274 frame.addInternalFrameListener(
275 new javax.swing.event.InternalFrameAdapter()
278 public void internalFrameClosed(
279 javax.swing.event.InternalFrameEvent evt)
281 fr.removePropertyChangeListener(change);
282 dassourceBrowser.fs = null;
285 frame.setLayer(JLayeredPane.PALETTE_LAYER);
286 inConstruction = false;
290 * Constructs and configures the JTable which displays columns of data for
293 protected void initTable()
298 public String getToolTipText(MouseEvent e)
301 int column = table.columnAtPoint(e.getPoint());
305 tip = summaryView.isSelected()
306 ? MessageManager.getString(
307 "label.feature_settings_select_columns")
308 : JvSwingUtils.wrapTooltip(true, MessageManager
309 .getString("label.feature_settings_click_drag"));
312 int row = table.rowAtPoint(e.getPoint());
313 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
316 ? MessageManager.getString("label.filters_tooltip")
325 table.getTableHeader().setFont(VERDANA_12);
326 table.setFont(VERDANA_12);
328 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
329 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
331 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
332 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
334 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
335 new ColorRenderer(), new ColorEditor(this));
336 table.addColumn(colourColumn);
338 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
339 new FilterRenderer(), new FilterEditor(this));
340 table.addColumn(filterColumn);
342 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
344 table.addMouseListener(new MouseAdapter()
347 public void mousePressed(MouseEvent evt)
349 selectedRow = table.rowAtPoint(evt.getPoint());
350 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
351 if (evt.isPopupTrigger())
353 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
354 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
356 else if (evt.getClickCount() == 2)
358 boolean invertSelection = evt.isAltDown();
359 boolean toggleSelection = Platform.isControlDown(evt);
360 boolean extendSelection = evt.isShiftDown();
361 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
362 invertSelection, extendSelection, toggleSelection, type);
366 // isPopupTrigger fires on mouseReleased on Windows
368 public void mouseReleased(MouseEvent evt)
370 selectedRow = table.rowAtPoint(evt.getPoint());
371 if (evt.isPopupTrigger())
373 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
374 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
375 popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
380 table.addMouseMotionListener(new MouseMotionAdapter()
383 public void mouseDragged(MouseEvent evt)
385 int newRow = table.rowAtPoint(evt.getPoint());
391 protected void popupMenu(final int rowSelected, final String type,
392 final Object typeCol, int x, int y)
394 final FeatureColourI featureColour = (FeatureColourI) typeCol;
396 JPopupMenu men = new JPopupMenu(MessageManager
397 .formatMessage("label.settings_for_param", new String[]
399 JMenuItem scr = new JMenuItem(
400 MessageManager.getString("label.sort_by_score"));
402 final FeatureSettings me = this;
403 scr.addActionListener(new ActionListener()
407 public void actionPerformed(ActionEvent e)
410 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
415 JMenuItem dens = new JMenuItem(
416 MessageManager.getString("label.sort_by_density"));
417 dens.addActionListener(new ActionListener()
421 public void actionPerformed(ActionEvent e)
424 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
432 * variable colour options include colour by label, by score,
433 * by selected attribute text, or attribute value
435 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
436 MessageManager.getString("label.variable_colour"));
437 mxcol.setSelected(!featureColour.isSimpleColour());
439 mxcol.addActionListener(new ActionListener()
441 JColorChooser colorChooser;
444 public void actionPerformed(ActionEvent e)
446 if (e.getSource() == mxcol)
448 if (featureColour.isSimpleColour())
450 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
451 fc.addActionListener(this);
455 // bring up simple color chooser
456 colorChooser = new JColorChooser();
457 String title = MessageManager
458 .getString("label.select_colour");
459 JDialog dialog = JColorChooser.createDialog(me,
460 title, true, // modal
461 colorChooser, this, // OK button handler
462 null); // no CANCEL button handler
463 colorChooser.setColor(featureColour.getMaxColour());
464 dialog.setVisible(true);
469 if (e.getSource() instanceof FeatureTypeSettings)
472 * update after OK in feature colour dialog; the updated
473 * colour will have already been set in the FeatureRenderer
475 FeatureColourI fci = fr.getFeatureColours().get(type);
476 table.setValueAt(fci, rowSelected, 1);
481 // probably the color chooser!
482 table.setValueAt(new FeatureColour(colorChooser.getColor()),
485 me.updateFeatureRenderer(
486 ((FeatureTableModel) table.getModel()).getData(),
493 JMenuItem selCols = new JMenuItem(
494 MessageManager.getString("label.select_columns_containing"));
495 selCols.addActionListener(new ActionListener()
498 public void actionPerformed(ActionEvent arg0)
500 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
504 JMenuItem clearCols = new JMenuItem(MessageManager
505 .getString("label.select_columns_not_containing"));
506 clearCols.addActionListener(new ActionListener()
509 public void actionPerformed(ActionEvent arg0)
511 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
515 JMenuItem hideCols = new JMenuItem(
516 MessageManager.getString("label.hide_columns_containing"));
517 hideCols.addActionListener(new ActionListener()
520 public void actionPerformed(ActionEvent arg0)
522 fr.ap.alignFrame.hideFeatureColumns(type, true);
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);
538 men.add(hideOtherCols);
539 men.show(table, x, y);
543 synchronized public void discoverAllFeatureData()
545 Set<String> allGroups = new HashSet<>();
546 AlignmentI alignment = af.getViewport().getAlignment();
548 for (int i = 0; i < alignment.getHeight(); i++)
550 SequenceI seq = alignment.getSequenceAt(i);
551 for (String group : seq.getFeatures().getFeatureGroups(true))
553 if (group != null && !allGroups.contains(group))
555 allGroups.add(group);
556 checkGroupState(group);
567 * Synchronise gui group list and check visibility of group
570 * @return true if group is visible
572 private boolean checkGroupState(String group)
574 boolean visible = fr.checkGroupVisibility(group, true);
576 for (int g = 0; g < groupPanel.getComponentCount(); g++)
578 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
580 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
585 final String grp = group;
586 final JCheckBox check = new JCheckBox(group, visible);
587 check.setFont(new Font("Serif", Font.BOLD, 12));
588 check.setToolTipText(group);
589 check.addItemListener(new ItemListener()
592 public void itemStateChanged(ItemEvent evt)
594 fr.setGroupVisibility(check.getText(), check.isSelected());
595 resetTable(new String[] { grp });
596 af.alignPanel.paintAlignment(true, true);
599 groupPanel.add(check);
603 synchronized void resetTable(String[] groupChanged)
609 resettingTable = true;
610 typeWidth = new Hashtable<>();
611 // TODO: change avWidth calculation to 'per-sequence' average and use long
614 Set<String> displayableTypes = new HashSet<>();
615 Set<String> foundGroups = new HashSet<>();
618 * determine which feature types may be visible depending on
619 * which groups are selected, and recompute average width data
621 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
624 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
627 * get the sequence's groups for positional features
628 * and keep track of which groups are visible
630 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
631 Set<String> visibleGroups = new HashSet<>();
632 for (String group : groups)
634 if (group == null || checkGroupState(group))
636 visibleGroups.add(group);
639 foundGroups.addAll(groups);
642 * get distinct feature types for visible groups
643 * record distinct visible types, and their count and total length
645 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
646 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);
667 * enable 'Summary View' if some types are sub-types of others
669 Set<String> parents = SequenceOntologyFactory.getInstance()
670 .getParentTerms(displayableTypes);
671 summaryView.setEnabled(parents.size() < displayableTypes.size());
673 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
676 if (fr.hasRenderOrder())
680 fr.findAllFeatures(groupChanged != null); // prod to update
681 // colourschemes. but don't
683 // First add the checks in the previous render order,
684 // in case the window has been closed and reopened
686 List<String> frl = fr.getRenderOrder();
687 for (int ro = frl.size() - 1; ro > -1; ro--)
689 String type = frl.get(ro);
691 if (!displayableTypes.contains(type))
695 data[dataIndex][TYPE_COLUMN] = type;
696 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
697 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
698 data[dataIndex][FILTER_COLUMN] = featureFilter == null
699 ? new FeatureMatcherSet()
701 data[dataIndex][SHOW_COLUMN] = new Boolean(
702 af.getViewport().getFeaturesDisplayed().isVisible(type));
704 displayableTypes.remove(type);
709 * process any extra features belonging only to
710 * a group which was just selected
712 while (!displayableTypes.isEmpty())
714 String type = displayableTypes.iterator().next();
715 data[dataIndex][TYPE_COLUMN] = type;
717 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
718 if (data[dataIndex][COLOUR_COLUMN] == null)
720 // "Colour has been updated in another view!!"
721 fr.clearRenderOrder();
724 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
725 data[dataIndex][FILTER_COLUMN] = featureFilter == null
726 ? new FeatureMatcherSet()
728 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
730 displayableTypes.remove(type);
733 if (originalData == null)
735 originalData = new Object[data.length][COLUMN_COUNT];
736 for (int i = 0; i < data.length; i++)
738 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
743 updateOriginalData(data);
747 * recreate the table model
749 FeatureTableModel dataModel = new FeatureTableModel(data);
750 table.setModel(dataModel);
753 * we want to be able to filter out rows for sub-types, but not to sort
754 * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
756 final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
758 for (int i = 0; i < table.getColumnCount(); i++)
760 sorter.setSortable(i, false);
764 * filter rows to only top-level Ontology types if requested
766 sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
769 public boolean include(
770 Entry<? extends FeatureTableModel, ? extends Integer> entry)
772 if (!summaryView.isSelected())
776 int row = entry.getIdentifier(); // this is model, not view, row number
777 String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
778 return parents.contains(featureType);
781 table.setRowSorter(sorter);
783 table.getColumnModel().getColumn(0).setPreferredWidth(200);
785 groupPanel.setLayout(
786 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
787 pruneGroups(foundGroups);
788 groupPanel.validate();
790 updateFeatureRenderer(data, groupChanged != null);
791 resettingTable = false;
795 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
796 * have been made outwith this dialog
798 * <li>a new feature type added (and made visible)</li>
799 * <li>a feature colour changed (in the Amend Features dialog)</li>
804 protected void updateOriginalData(Object[][] foundData)
806 // todo LinkedHashMap instead of Object[][] would be nice
808 Object[][] currentData = ((FeatureTableModel) table.getModel())
810 for (Object[] row : foundData)
812 String type = (String) row[TYPE_COLUMN];
813 boolean found = false;
814 for (Object[] current : currentData)
816 if (type.equals(current[TYPE_COLUMN]))
820 * currently dependent on object equality here;
821 * really need an equals method on FeatureColour
823 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
826 * feature colour has changed externally - update originalData
828 for (Object[] original : originalData)
830 if (type.equals(original[TYPE_COLUMN]))
832 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
843 * new feature detected - add to original data (on top)
845 Object[][] newData = new Object[originalData.length
847 for (int i = 0; i < originalData.length; i++)
849 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
853 originalData = newData;
859 * Remove from the groups panel any checkboxes for groups that are not in the
860 * foundGroups set. This enables removing a group from the display when the last
861 * feature in that group is deleted.
865 protected void pruneGroups(Set<String> foundGroups)
867 for (int g = 0; g < groupPanel.getComponentCount(); g++)
869 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
870 if (!foundGroups.contains(checkbox.getText()))
872 groupPanel.remove(checkbox);
878 * reorder data based on the featureRenderers global priority list.
882 private void ensureOrder(Object[][] data)
884 boolean sort = false;
885 float[] order = new float[data.length];
886 for (int i = 0; i < order.length; i++)
888 order[i] = fr.getOrder(data[i][0].toString());
891 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
895 sort = sort || order[i - 1] > order[i];
900 jalview.util.QuickSort.sort(order, data);
905 * Offers a file chooser dialog, and then loads the feature colours and
906 * filters from file in XML format and unmarshals to Jalview feature settings
910 JalviewFileChooser chooser = new JalviewFileChooser("fc",
911 SEQUENCE_FEATURE_COLOURS);
912 chooser.setFileView(new JalviewFileView());
913 chooser.setDialogTitle(
914 MessageManager.getString("label.load_feature_colours"));
915 chooser.setToolTipText(MessageManager.getString("action.load"));
917 int value = chooser.showOpenDialog(this);
919 if (value == JalviewFileChooser.APPROVE_OPTION)
921 File file = chooser.getSelectedFile();
927 * Loads feature colours and filters from XML stored in the given file
935 InputStreamReader in = new InputStreamReader(
936 new FileInputStream(file), "UTF-8");
938 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
941 * load feature colours
943 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
945 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
946 FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
947 fr.setColour(newcol.getName(), colour);
948 fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
952 * load feature filters; loaded filters will replace any that are
953 * currently defined, other defined filters are left unchanged
955 for (int i = 0; i < jucs.getFilterCount(); i++)
957 jalview.schemabinding.version2.Filter filterModel = jucs
959 String featureType = filterModel.getFeatureType();
960 FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
961 filterModel.getMatcherSet());
962 if (!filter.isEmpty())
964 fr.setFeatureFilter(featureType, filter);
969 * update feature settings table
974 Object[][] data = ((FeatureTableModel) table.getModel())
977 updateFeatureRenderer(data, false);
980 } catch (Exception ex)
982 System.out.println("Error loading User Colour File\n" + ex);
987 * Offers a file chooser dialog, and then saves the current feature colours
988 * and any filters to the selected file in XML format
992 JalviewFileChooser chooser = new JalviewFileChooser("fc",
993 SEQUENCE_FEATURE_COLOURS);
994 chooser.setFileView(new JalviewFileView());
995 chooser.setDialogTitle(
996 MessageManager.getString("label.save_feature_colours"));
997 chooser.setToolTipText(MessageManager.getString("action.save"));
999 int value = chooser.showSaveDialog(this);
1001 if (value == JalviewFileChooser.APPROVE_OPTION)
1003 save(chooser.getSelectedFile());
1008 * Saves feature colours and filters to the given file
1012 void save(File file)
1014 JalviewUserColours ucs = new JalviewUserColours();
1015 ucs.setSchemeName("Sequence Features");
1018 PrintWriter out = new PrintWriter(new OutputStreamWriter(
1019 new FileOutputStream(file), "UTF-8"));
1022 * sort feature types by colour order, from 0 (highest)
1025 Set<String> fr_colours = fr.getAllFeatureColours();
1026 String[] sortedTypes = fr_colours
1027 .toArray(new String[fr_colours.size()]);
1028 Arrays.sort(sortedTypes, new Comparator<String>()
1031 public int compare(String type1, String type2)
1033 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1038 * save feature colours
1040 for (String featureType : sortedTypes)
1042 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1043 jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
1049 * save any feature filters
1051 for (String featureType : sortedTypes)
1053 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1054 if (filter != null && !filter.isEmpty())
1056 Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
1057 FeatureMatcherI firstMatcher = iterator.next();
1058 MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
1060 Filter filterModel = new Filter();
1061 filterModel.setFeatureType(featureType);
1062 filterModel.setMatcherSet(ms);
1063 ucs.addFilter(filterModel);
1069 } catch (Exception ex)
1071 ex.printStackTrace();
1075 public void invertSelection()
1077 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1078 for (int i = 0; i < data.length; i++)
1080 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1082 updateFeatureRenderer(data, true);
1086 public void orderByAvWidth()
1088 if (table == null || table.getModel() == null)
1092 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1093 float[] width = new float[data.length];
1097 for (int i = 0; i < data.length; i++)
1099 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1102 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1103 // weight - but have to make per
1104 // sequence, too (awidth[2])
1105 // if (width[i]==1) // hack to distinguish single width sequences.
1116 boolean sort = false;
1117 for (int i = 0; i < width.length; i++)
1119 // awidth = (float[]) typeWidth.get(data[i][0]);
1122 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1125 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1131 width[i] /= max; // normalize
1132 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1136 sort = sort || width[i - 1] > width[i];
1141 jalview.util.QuickSort.sort(width, data);
1142 // update global priority order
1145 updateFeatureRenderer(data, false);
1153 frame.setClosed(true);
1154 } catch (Exception exe)
1160 public void updateFeatureRenderer(Object[][] data)
1162 updateFeatureRenderer(data, true);
1166 * Update the priority order of features; only repaint if this changed the order
1167 * of visible features
1172 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1174 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1176 if (fr.setFeaturePriority(rowData, visibleNew))
1178 af.alignPanel.paintAlignment(true, true);
1183 * Converts table data into an array of data beans
1185 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1187 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1188 for (int i = 0; i < data.length; i++)
1190 String type = (String) data[i][TYPE_COLUMN];
1191 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1192 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1193 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1194 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1200 private void jbInit() throws Exception
1202 this.setLayout(new BorderLayout());
1204 JPanel settingsPane = new JPanel();
1205 settingsPane.setLayout(new BorderLayout());
1207 dasSettingsPane.setLayout(new BorderLayout());
1209 JPanel bigPanel = new JPanel();
1210 bigPanel.setLayout(new BorderLayout());
1212 groupPanel = new JPanel();
1213 bigPanel.add(groupPanel, BorderLayout.NORTH);
1215 JButton invert = new JButton(
1216 MessageManager.getString("label.invert_selection"));
1217 invert.setFont(JvSwingUtils.getLabelFont());
1218 invert.addActionListener(new ActionListener()
1221 public void actionPerformed(ActionEvent e)
1227 JButton optimizeOrder = new JButton(
1228 MessageManager.getString("label.optimise_order"));
1229 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1230 optimizeOrder.addActionListener(new ActionListener()
1233 public void actionPerformed(ActionEvent e)
1239 JButton sortByScore = new JButton(
1240 MessageManager.getString("label.seq_sort_by_score"));
1241 sortByScore.setFont(JvSwingUtils.getLabelFont());
1242 sortByScore.addActionListener(new ActionListener()
1245 public void actionPerformed(ActionEvent e)
1247 af.avc.sortAlignmentByFeatureScore(null);
1250 JButton sortByDens = new JButton(
1251 MessageManager.getString("label.sequence_sort_by_density"));
1252 sortByDens.setFont(JvSwingUtils.getLabelFont());
1253 sortByDens.addActionListener(new ActionListener()
1256 public void actionPerformed(ActionEvent e)
1258 af.avc.sortAlignmentByFeatureDensity(null);
1262 JButton help = new JButton(MessageManager.getString("action.help"));
1263 help.setFont(JvSwingUtils.getLabelFont());
1264 help.addActionListener(new ActionListener()
1267 public void actionPerformed(ActionEvent e)
1271 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1272 } catch (HelpSetException e1)
1274 e1.printStackTrace();
1278 help.setFont(JvSwingUtils.getLabelFont());
1279 help.setText(MessageManager.getString("action.help"));
1280 help.addActionListener(new ActionListener()
1283 public void actionPerformed(ActionEvent e)
1287 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1288 } catch (HelpSetException e1)
1290 e1.printStackTrace();
1295 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1296 cancel.setFont(JvSwingUtils.getLabelFont());
1297 cancel.addActionListener(new ActionListener()
1300 public void actionPerformed(ActionEvent e)
1302 fr.setTransparency(originalTransparency);
1303 fr.setFeatureFilters(originalFilters);
1304 updateFeatureRenderer(originalData);
1309 JButton ok = new JButton(MessageManager.getString("action.ok"));
1310 ok.setFont(JvSwingUtils.getLabelFont());
1311 ok.addActionListener(new ActionListener()
1314 public void actionPerformed(ActionEvent e)
1320 JButton loadColours = new JButton(
1321 MessageManager.getString("label.load_colours"));
1322 loadColours.setFont(JvSwingUtils.getLabelFont());
1323 loadColours.setToolTipText(
1324 MessageManager.getString("label.load_colours_tooltip"));
1325 loadColours.addActionListener(new ActionListener()
1328 public void actionPerformed(ActionEvent e)
1334 JButton saveColours = new JButton(
1335 MessageManager.getString("label.save_colours"));
1336 saveColours.setFont(JvSwingUtils.getLabelFont());
1337 saveColours.setToolTipText(
1338 MessageManager.getString("label.save_colours_tooltip"));
1339 saveColours.addActionListener(new ActionListener()
1342 public void actionPerformed(ActionEvent e)
1347 transparency.addChangeListener(new ChangeListener()
1350 public void stateChanged(ChangeEvent evt)
1352 if (!inConstruction)
1354 fr.setTransparency((100 - transparency.getValue()) / 100f);
1355 af.alignPanel.paintAlignment(true, true);
1360 summaryView = new JCheckBox("Summary view");
1361 summaryView.setToolTipText("Show only top level ontology terms");
1362 summaryView.addActionListener(new ActionListener()
1365 public void actionPerformed(ActionEvent e)
1371 transparency.setMaximum(70);
1372 transparency.setToolTipText(
1373 MessageManager.getString("label.transparency_tip"));
1374 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1375 fetchDAS.addActionListener(new ActionListener()
1378 public void actionPerformed(ActionEvent e)
1380 fetchDAS_actionPerformed(e);
1383 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1384 saveDAS.addActionListener(new ActionListener()
1387 public void actionPerformed(ActionEvent e)
1389 saveDAS_actionPerformed(e);
1393 JPanel dasButtonPanel = new JPanel();
1394 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1395 dasSettingsPane.setBorder(null);
1396 cancelDAS.setEnabled(false);
1397 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1398 cancelDAS.addActionListener(new ActionListener()
1401 public void actionPerformed(ActionEvent e)
1403 cancelDAS_actionPerformed(e);
1407 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1408 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1410 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1411 transbuttons.add(optimizeOrder);
1412 transbuttons.add(invert);
1413 transbuttons.add(sortByScore);
1414 transbuttons.add(sortByDens);
1415 transbuttons.add(help);
1416 JPanel transPanel = new JPanel(new GridLayout(3, 1));
1417 transPanel.add(summaryView);
1418 transPanel.add(new JLabel(" Colour transparency" + ":"));
1419 transPanel.add(transparency);
1420 lowerPanel.add(transPanel);
1421 lowerPanel.add(transbuttons);
1423 JPanel buttonPanel = new JPanel();
1424 buttonPanel.add(ok);
1425 buttonPanel.add(cancel);
1426 buttonPanel.add(loadColours);
1427 buttonPanel.add(saveColours);
1428 bigPanel.add(scrollPane, BorderLayout.CENTER);
1429 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1430 dasButtonPanel.add(fetchDAS);
1431 dasButtonPanel.add(cancelDAS);
1432 dasButtonPanel.add(saveDAS);
1433 settingsPane.add(bigPanel, BorderLayout.CENTER);
1434 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1435 this.add(settingsPane);
1438 public void fetchDAS_actionPerformed(ActionEvent e)
1440 fetchDAS.setEnabled(false);
1441 cancelDAS.setEnabled(true);
1442 dassourceBrowser.setGuiEnabled(false);
1443 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1444 .getSelectedSources();
1445 doDasFeatureFetch(selectedSources, true, true);
1449 * get the features from selectedSources for all or the current selection
1451 * @param selectedSources
1452 * @param checkDbRefs
1453 * @param promptFetchDbRefs
1455 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1456 boolean checkDbRefs, boolean promptFetchDbRefs)
1458 SequenceI[] dataset, seqs;
1460 AlignmentViewport vp = af.getViewport();
1461 if (vp.getSelectionGroup() != null
1462 && vp.getSelectionGroup().getSize() > 0)
1464 iSize = vp.getSelectionGroup().getSize();
1465 dataset = new SequenceI[iSize];
1466 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1470 iSize = vp.getAlignment().getHeight();
1471 seqs = vp.getAlignment().getSequencesArray();
1474 dataset = new SequenceI[iSize];
1475 for (int i = 0; i < iSize; i++)
1477 dataset[i] = seqs[i].getDatasetSequence();
1480 cancelDAS.setEnabled(true);
1481 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1482 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1483 af.getViewport().setShowSequenceFeatures(true);
1484 af.showSeqFeatures.setSelected(true);
1488 * blocking call to initialise the das source browser
1490 public void initDasSources()
1492 dassourceBrowser.initDasSources();
1496 * examine the current list of das sources and return any matching the given
1497 * nicknames in sources
1500 * Vector of Strings to resolve to DAS source nicknames.
1501 * @return sources that are present in source list.
1503 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1505 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1509 * get currently selected das sources. ensure you have called initDasSources
1510 * before calling this.
1512 * @return vector of selected das source nicknames
1514 public Vector<jalviewSourceI> getSelectedSources()
1516 return dassourceBrowser.getSelectedSources();
1520 * properly initialise DAS fetcher and then initiate a new thread to fetch
1521 * features from the named sources (rather than any turned on by default)
1525 * if true then runs in same thread, otherwise passes to the Swing
1528 public void fetchDasFeatures(Vector<String> sources, boolean block)
1531 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1532 .resolveSourceNicknames(sources);
1533 if (resolved.size() == 0)
1535 resolved = dassourceBrowser.getSelectedSources();
1537 if (resolved.size() > 0)
1539 final List<jalviewSourceI> dassources = resolved;
1540 fetchDAS.setEnabled(false);
1541 // cancelDAS.setEnabled(true); doDasFetch does this.
1542 Runnable fetcher = new Runnable()
1548 doDasFeatureFetch(dassources, true, false);
1558 SwingUtilities.invokeLater(fetcher);
1563 public void saveDAS_actionPerformed(ActionEvent e)
1566 .saveProperties(jalview.bin.Cache.applicationProperties);
1569 public void complete()
1571 fetchDAS.setEnabled(true);
1572 cancelDAS.setEnabled(false);
1573 dassourceBrowser.setGuiEnabled(true);
1577 public void cancelDAS_actionPerformed(ActionEvent e)
1579 if (dasFeatureFetcher != null)
1581 dasFeatureFetcher.cancel();
1586 public void noDasSourceActive()
1589 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1590 MessageManager.getString("label.no_das_sources_selected_warn"),
1591 MessageManager.getString("label.no_das_sources_selected_title"),
1592 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1596 * Reorders features by 'dragging' selectedRow to 'newRow'
1600 protected void dragRow(int newRow)
1602 if (summaryView.isSelected())
1604 // no drag while in summary view
1608 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
1611 * reposition 'selectedRow' to 'newRow' (the dragged to location)
1612 * this could be more than one row away for a very fast drag action
1613 * so just swap it with adjacent rows until we get it there
1615 Object[][] data = ((FeatureTableModel) table.getModel())
1617 int direction = newRow < selectedRow ? -1 : 1;
1618 for (int i = selectedRow; i != newRow; i += direction)
1620 Object[] temp = data[i];
1621 data[i] = data[i + direction];
1622 data[i + direction] = temp;
1624 updateFeatureRenderer(data);
1626 selectedRow = newRow;
1630 protected void refreshTable()
1632 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1633 for (Object[] row : data)
1635 String type = (String) row[TYPE_COLUMN];
1636 FeatureColourI colour = fr.getFeatureColours().get(type);
1637 FeatureMatcherSetI filter = fr.getFeatureFilter(type);
1640 filter = new FeatureMatcherSet();
1642 row[COLOUR_COLUMN] = colour;
1643 row[FILTER_COLUMN] = filter;
1648 // ///////////////////////////////////////////////////////////////////////
1649 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1650 // ///////////////////////////////////////////////////////////////////////
1651 class FeatureTableModel extends AbstractTableModel
1653 private String[] columnNames = {
1654 MessageManager.getString("label.feature_type"),
1655 MessageManager.getString("action.colour"),
1656 MessageManager.getString("label.filter"),
1657 MessageManager.getString("label.show") };
1659 private Object[][] data;
1661 FeatureTableModel(Object[][] data)
1666 public Object[][] getData()
1671 public void setData(Object[][] data)
1677 public int getColumnCount()
1679 return columnNames.length;
1682 public Object[] getRow(int row)
1688 public int getRowCount()
1694 public String getColumnName(int col)
1696 return columnNames[col];
1700 public Object getValueAt(int row, int col)
1702 return data[row][col];
1706 * Answers the class of the object in column c of the first row of the table
1709 public Class<?> getColumnClass(int c)
1711 Object v = getValueAt(0, c);
1712 return v == null ? null : v.getClass();
1716 public boolean isCellEditable(int row, int col)
1718 return col == 0 ? false : true;
1722 public void setValueAt(Object value, int row, int col)
1724 data[row][col] = value;
1725 fireTableCellUpdated(row, col);
1726 updateFeatureRenderer(data);
1731 class ColorRenderer extends JLabel implements TableCellRenderer
1733 javax.swing.border.Border unselectedBorder = null;
1735 javax.swing.border.Border selectedBorder = null;
1737 final String baseTT = "Click to edit, right/apple click for menu.";
1739 public ColorRenderer()
1741 setOpaque(true); // MUST do this for background to show up.
1742 setHorizontalTextPosition(SwingConstants.CENTER);
1743 setVerticalTextPosition(SwingConstants.CENTER);
1747 public Component getTableCellRendererComponent(JTable tbl, Object color,
1748 boolean isSelected, boolean hasFocus, int row, int column)
1750 FeatureColourI cellColour = (FeatureColourI) color;
1752 setToolTipText(baseTT);
1753 setBackground(tbl.getBackground());
1754 if (!cellColour.isSimpleColour())
1756 Rectangle cr = tbl.getCellRect(row, column, false);
1757 FeatureSettings.renderGraduatedColor(this, cellColour,
1758 (int) cr.getWidth(), (int) cr.getHeight());
1764 setBackground(cellColour.getColour());
1768 if (selectedBorder == null)
1770 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1771 tbl.getSelectionBackground());
1773 setBorder(selectedBorder);
1777 if (unselectedBorder == null)
1779 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1780 tbl.getBackground());
1782 setBorder(unselectedBorder);
1789 class FilterRenderer extends JLabel implements TableCellRenderer
1791 javax.swing.border.Border unselectedBorder = null;
1793 javax.swing.border.Border selectedBorder = null;
1795 public FilterRenderer()
1797 setOpaque(true); // MUST do this for background to show up.
1798 setHorizontalTextPosition(SwingConstants.CENTER);
1799 setVerticalTextPosition(SwingConstants.CENTER);
1803 public Component getTableCellRendererComponent(JTable tbl,
1804 Object filter, boolean isSelected, boolean hasFocus, int row,
1807 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1809 String asText = theFilter.toString();
1810 setBackground(tbl.getBackground());
1811 this.setText(asText);
1816 if (selectedBorder == null)
1818 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1819 tbl.getSelectionBackground());
1821 setBorder(selectedBorder);
1825 if (unselectedBorder == null)
1827 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1828 tbl.getBackground());
1830 setBorder(unselectedBorder);
1838 * update comp using rendering settings from gcol
1843 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1845 int w = comp.getWidth(), h = comp.getHeight();
1848 w = (int) comp.getPreferredSize().getWidth();
1849 h = (int) comp.getPreferredSize().getHeight();
1856 renderGraduatedColor(comp, gcol, w, h);
1859 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1862 boolean thr = false;
1863 StringBuilder tt = new StringBuilder();
1864 StringBuilder tx = new StringBuilder();
1866 if (gcol.isColourByAttribute())
1868 tx.append(String.join(":", gcol.getAttributeName()));
1870 else if (!gcol.isColourByLabel())
1872 tx.append(MessageManager.getString("label.score"));
1875 if (gcol.isAboveThreshold())
1879 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1882 if (gcol.isBelowThreshold())
1886 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1889 if (gcol.isColourByLabel())
1891 tt.append("Coloured by label text. ").append(tt);
1896 if (!gcol.isColourByAttribute())
1904 Color newColor = gcol.getMaxColour();
1905 comp.setBackground(newColor);
1906 // System.err.println("Width is " + w / 2);
1907 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1908 comp.setIcon(ficon);
1909 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1910 // + newColor.getGreen() + ", " + newColor.getBlue()
1911 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1912 // + ", " + minCol.getBlue() + ")");
1914 comp.setHorizontalAlignment(SwingConstants.CENTER);
1915 comp.setText(tx.toString());
1916 if (tt.length() > 0)
1918 if (comp.getToolTipText() == null)
1920 comp.setToolTipText(tt.toString());
1924 comp.setToolTipText(
1925 tt.append(" ").append(comp.getToolTipText()).toString());
1930 class ColorEditor extends AbstractCellEditor
1931 implements TableCellEditor, ActionListener
1935 FeatureColourI currentColor;
1937 FeatureTypeSettings chooser;
1941 JButton colourButton;
1943 JColorChooser colorChooser;
1947 protected static final String EDIT = "edit";
1949 int rowSelected = 0;
1951 public ColorEditor(FeatureSettings me)
1954 // Set up the editor (from the table's point of view),
1955 // which is a button.
1956 // This button brings up the color chooser dialog,
1957 // which is the editor from the user's point of view.
1958 colourButton = new JButton();
1959 colourButton.setActionCommand(EDIT);
1960 colourButton.addActionListener(this);
1961 colourButton.setBorderPainted(false);
1962 // Set up the dialog that the button brings up.
1963 colorChooser = new JColorChooser();
1964 dialog = JColorChooser.createDialog(colourButton,
1965 MessageManager.getString("label.select_colour"), true, // modal
1966 colorChooser, this, // OK button handler
1967 null); // no CANCEL button handler
1971 * Handles events from the editor button and from the dialog's OK button.
1974 public void actionPerformed(ActionEvent e)
1976 // todo test e.getSource() instead here
1977 if (EDIT.equals(e.getActionCommand()))
1979 // The user has clicked the cell, so
1980 // bring up the dialog.
1981 if (currentColor.isSimpleColour())
1983 // bring up simple color chooser
1984 colourButton.setBackground(currentColor.getColour());
1985 colorChooser.setColor(currentColor.getColour());
1986 dialog.setVisible(true);
1990 // bring up graduated chooser.
1991 chooser = new FeatureTypeSettings(me.fr, type);
1992 chooser.setRequestFocusEnabled(true);
1993 chooser.requestFocus();
1994 chooser.addActionListener(this);
1995 chooser.showTab(true);
1997 // Make the renderer reappear.
1998 fireEditingStopped();
2002 if (currentColor.isSimpleColour())
2005 * read off colour picked in colour chooser after OK pressed
2007 currentColor = new FeatureColour(colorChooser.getColor());
2008 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
2013 * after OK in variable colour dialog, any changes to colour
2014 * (or filters!) are already set in FeatureRenderer, so just
2015 * update table data without triggering updateFeatureRenderer
2019 fireEditingStopped();
2020 me.table.validate();
2024 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2026 public Object getCellEditorValue()
2028 return currentColor;
2031 // Implement the one method defined by TableCellEditor.
2033 public Component getTableCellEditorComponent(JTable theTable, Object value,
2034 boolean isSelected, int row, int column)
2036 currentColor = (FeatureColourI) value;
2037 this.rowSelected = row;
2038 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2039 colourButton.setOpaque(true);
2040 colourButton.setBackground(me.getBackground());
2041 if (!currentColor.isSimpleColour())
2043 JLabel btn = new JLabel();
2044 btn.setSize(colourButton.getSize());
2045 FeatureSettings.renderGraduatedColor(btn, currentColor);
2046 colourButton.setBackground(btn.getBackground());
2047 colourButton.setIcon(btn.getIcon());
2048 colourButton.setText(btn.getText());
2052 colourButton.setText("");
2053 colourButton.setIcon(null);
2054 colourButton.setBackground(currentColor.getColour());
2056 return colourButton;
2061 * The cell editor for the Filter column. It displays the text of any filters
2062 * for the feature type in that row (in full as a tooltip, possible abbreviated
2063 * as display text). On click in the cell, opens the Feature Display Settings
2064 * dialog at the Filters tab.
2066 class FilterEditor extends AbstractCellEditor
2067 implements TableCellEditor, ActionListener
2071 FeatureMatcherSetI currentFilter;
2077 JButton filterButton;
2079 protected static final String EDIT = "edit";
2081 int rowSelected = 0;
2083 public FilterEditor(FeatureSettings me)
2086 filterButton = new JButton();
2087 filterButton.setActionCommand(EDIT);
2088 filterButton.addActionListener(this);
2089 filterButton.setBorderPainted(false);
2093 * Handles events from the editor button
2096 public void actionPerformed(ActionEvent e)
2098 if (filterButton == e.getSource())
2100 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
2101 chooser.addActionListener(this);
2102 chooser.setRequestFocusEnabled(true);
2103 chooser.requestFocus();
2104 if (lastLocation != null)
2106 // todo open at its last position on screen
2107 chooser.setBounds(lastLocation.x, lastLocation.y,
2108 chooser.getWidth(), chooser.getHeight());
2111 chooser.showTab(false);
2112 fireEditingStopped();
2114 else if (e.getSource() instanceof Component)
2117 * after OK in variable colour dialog, any changes to filter
2118 * (or colours!) are already set in FeatureRenderer, so just
2119 * update table data without triggering updateFeatureRenderer
2122 fireEditingStopped();
2123 me.table.validate();
2128 public Object getCellEditorValue()
2130 return currentFilter;
2134 public Component getTableCellEditorComponent(JTable theTable, Object value,
2135 boolean isSelected, int row, int column)
2137 currentFilter = (FeatureMatcherSetI) value;
2138 this.rowSelected = row;
2139 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2140 filterButton.setOpaque(true);
2141 filterButton.setBackground(me.getBackground());
2142 filterButton.setText(currentFilter.toString());
2143 filterButton.setToolTipText(currentFilter.toString());
2144 filterButton.setIcon(null);
2145 return filterButton;
2150 class FeatureIcon implements Icon
2152 private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
2154 FeatureColourI gcol;
2158 boolean midspace = false;
2160 int width = 50, height = 20;
2162 int s1, e1; // start and end of midpoint band for thresholded symbol
2164 Color mpcolour = Color.white;
2166 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2186 public int getIconWidth()
2192 public int getIconHeight()
2198 public void paintIcon(Component c, Graphics g, int x, int y)
2201 if (gcol.isColourByLabel())
2204 g.fillRect(0, 0, width, height);
2205 // need an icon here.
2206 g.setColor(gcol.getMaxColour());
2208 g.setFont(VERDANA_9);
2210 // g.setFont(g.getFont().deriveFont(
2211 // AffineTransform.getScaleInstance(
2212 // width/g.getFontMetrics().stringWidth("Label"),
2213 // height/g.getFontMetrics().getHeight())));
2215 g.drawString(MessageManager.getString("label.label"), 0, 0);
2220 Color minCol = gcol.getMinColour();
2222 g.fillRect(0, 0, s1, height);
2225 g.setColor(Color.white);
2226 g.fillRect(s1, 0, e1 - s1, height);
2228 g.setColor(gcol.getMaxColour());
2229 g.fillRect(0, e1, width - e1, height);