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 static jalview.gui.FeatureSettings.FeatureTableModel.COLOUR_COLUMN;
24 import static jalview.gui.FeatureSettings.FeatureTableModel.FILTER_COLUMN;
25 import static jalview.gui.FeatureSettings.FeatureTableModel.SHOW_COLUMN;
26 import static jalview.gui.FeatureSettings.FeatureTableModel.TYPE_COLUMN;
28 import jalview.api.FeatureColourI;
29 import jalview.api.FeatureSettingsControllerI;
30 import jalview.bin.Cache;
31 import jalview.datamodel.AlignmentI;
32 import jalview.datamodel.SequenceI;
33 import jalview.gui.Help.HelpId;
34 import jalview.io.JalviewFileChooser;
35 import jalview.io.JalviewFileView;
36 import jalview.schemabinding.version2.JalviewUserColours;
37 import jalview.schemes.FeatureColour;
38 import jalview.util.Format;
39 import jalview.util.MessageManager;
40 import jalview.util.Platform;
41 import jalview.util.QuickSort;
42 import jalview.util.matcher.KeyedMatcherSet;
43 import jalview.util.matcher.KeyedMatcherSetI;
44 import jalview.viewmodel.AlignmentViewport;
45 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
46 import jalview.ws.DasSequenceFeatureFetcher;
47 import jalview.ws.dbsources.das.api.jalviewSourceI;
49 import java.awt.BorderLayout;
50 import java.awt.Color;
51 import java.awt.Component;
52 import java.awt.Dimension;
54 import java.awt.Graphics;
55 import java.awt.GridLayout;
56 import java.awt.Point;
57 import java.awt.Rectangle;
58 import java.awt.event.ActionEvent;
59 import java.awt.event.ActionListener;
60 import java.awt.event.ItemEvent;
61 import java.awt.event.ItemListener;
62 import java.awt.event.MouseAdapter;
63 import java.awt.event.MouseEvent;
64 import java.awt.event.MouseMotionAdapter;
65 import java.beans.PropertyChangeEvent;
66 import java.beans.PropertyChangeListener;
68 import java.io.FileInputStream;
69 import java.io.FileOutputStream;
70 import java.io.InputStreamReader;
71 import java.io.OutputStreamWriter;
72 import java.io.PrintWriter;
73 import java.util.Arrays;
74 import java.util.HashSet;
75 import java.util.Hashtable;
76 import java.util.Iterator;
77 import java.util.List;
80 import java.util.Vector;
82 import javax.help.HelpSetException;
83 import javax.swing.AbstractCellEditor;
84 import javax.swing.BorderFactory;
85 import javax.swing.Icon;
86 import javax.swing.JButton;
87 import javax.swing.JCheckBox;
88 import javax.swing.JCheckBoxMenuItem;
89 import javax.swing.JColorChooser;
90 import javax.swing.JDialog;
91 import javax.swing.JInternalFrame;
92 import javax.swing.JLabel;
93 import javax.swing.JLayeredPane;
94 import javax.swing.JMenuItem;
95 import javax.swing.JPanel;
96 import javax.swing.JPopupMenu;
97 import javax.swing.JScrollPane;
98 import javax.swing.JSlider;
99 import javax.swing.JTable;
100 import javax.swing.ListSelectionModel;
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;
110 public class FeatureSettings extends JPanel
111 implements FeatureSettingsControllerI
113 private static final int COLUMN_COUNT = 4;
115 private static final int MIN_WIDTH = 400;
117 private static final int MIN_HEIGHT = 400;
119 DasSourceBrowser dassourceBrowser;
121 DasSequenceFeatureFetcher dasFeatureFetcher;
123 JPanel dasSettingsPane = new JPanel();
125 final FeatureRenderer fr;
127 public final AlignFrame af;
130 * 'original' fields hold settings to restore on Cancel
132 Object[][] originalData;
134 private float originalTransparency;
136 private Map<String, KeyedMatcherSetI> originalFilters;
138 final JInternalFrame frame;
140 JScrollPane scrollPane = new JScrollPane();
146 JSlider transparency = new JSlider();
149 * when true, constructor is still executing - so ignore UI events
151 protected volatile boolean inConstruction = true;
153 int selectedRow = -1;
155 JButton fetchDAS = new JButton();
157 JButton saveDAS = new JButton();
159 JButton cancelDAS = new JButton();
161 boolean resettingTable = false;
164 * true when Feature Settings are updating from feature renderer
166 private boolean handlingUpdate = false;
169 * holds {featureCount, totalExtent} for each feature type
171 Map<String, float[]> typeWidth = null;
178 public FeatureSettings(AlignFrame alignFrame)
180 this.af = alignFrame;
181 fr = af.getFeatureRenderer();
183 // save transparency for restore on Cancel
184 originalTransparency = fr.getTransparency();
185 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
186 transparency.setMaximum(100 - originalTransparencyAsPercent);
188 originalFilters = fr.getFeatureFilters();
193 } catch (Exception ex)
195 ex.printStackTrace();
201 public String getToolTipText(MouseEvent e)
204 int column = table.columnAtPoint(e.getPoint());
208 tip = JvSwingUtils.wrapTooltip(true, MessageManager
209 .getString("label.feature_settings_click_drag"));
212 int row = table.rowAtPoint(e.getPoint());
213 KeyedMatcherSet o = (KeyedMatcherSet) table.getValueAt(row,
216 ? MessageManager.getString("label.filters_tooltip")
225 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
226 table.setFont(new Font("Verdana", Font.PLAIN, 12));
228 // table.setDefaultRenderer(Color.class, new ColorRenderer());
229 // table.setDefaultEditor(Color.class, new ColorEditor(this));
231 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
232 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
234 table.setDefaultEditor(KeyedMatcherSet.class, new FilterEditor(this));
235 table.setDefaultRenderer(KeyedMatcherSet.class, new FilterRenderer());
237 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
238 new ColorRenderer(), new ColorEditor(this));
239 table.addColumn(colourColumn);
241 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
242 new FilterRenderer(), new FilterEditor(this));
243 table.addColumn(filterColumn);
245 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
247 table.addMouseListener(new MouseAdapter()
250 public void mousePressed(MouseEvent evt)
252 selectedRow = table.rowAtPoint(evt.getPoint());
253 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
254 if (evt.isPopupTrigger())
256 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
257 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
260 else if (evt.getClickCount() == 2)
262 boolean invertSelection = evt.isAltDown();
263 boolean toggleSelection = Platform.isControlDown(evt);
264 boolean extendSelection = evt.isShiftDown();
265 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
266 invertSelection, extendSelection, toggleSelection, type);
270 // isPopupTrigger fires on mouseReleased on Windows
272 public void mouseReleased(MouseEvent evt)
274 selectedRow = table.rowAtPoint(evt.getPoint());
275 if (evt.isPopupTrigger())
277 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
278 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
279 popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
285 table.addMouseMotionListener(new MouseMotionAdapter()
288 public void mouseDragged(MouseEvent evt)
290 int newRow = table.rowAtPoint(evt.getPoint());
291 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
294 * reposition 'selectedRow' to 'newRow' (the dragged to location)
295 * this could be more than one row away for a very fast drag action
296 * so just swap it with adjacent rows until we get it there
298 Object[][] data = ((FeatureTableModel) table.getModel())
300 int direction = newRow < selectedRow ? -1 : 1;
301 for (int i = selectedRow; i != newRow; i += direction)
303 Object[] temp = data[i];
304 data[i] = data[i + direction];
305 data[i + direction] = temp;
307 updateFeatureRenderer(data);
309 selectedRow = newRow;
313 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
314 // MessageManager.getString("label.feature_settings_click_drag")));
315 scrollPane.setViewportView(table);
317 dassourceBrowser = new DasSourceBrowser(this);
318 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
320 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
322 fr.findAllFeatures(true); // display everything!
325 discoverAllFeatureData();
326 final PropertyChangeListener change;
327 final FeatureSettings fs = this;
328 fr.addPropertyChangeListener(change = new PropertyChangeListener()
331 public void propertyChange(PropertyChangeEvent evt)
333 if (!fs.resettingTable && !fs.handlingUpdate)
335 fs.handlingUpdate = true;
337 // new groups may be added with new sequence feature types only
338 fs.handlingUpdate = false;
344 frame = new JInternalFrame();
345 frame.setContentPane(this);
346 if (Platform.isAMac())
348 Desktop.addInternalFrame(frame,
349 MessageManager.getString("label.sequence_feature_settings"),
354 Desktop.addInternalFrame(frame,
355 MessageManager.getString("label.sequence_feature_settings"),
358 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
360 frame.addInternalFrameListener(
361 new javax.swing.event.InternalFrameAdapter()
364 public void internalFrameClosed(
365 javax.swing.event.InternalFrameEvent evt)
367 fr.removePropertyChangeListener(change);
368 dassourceBrowser.fs = null;
371 frame.setLayer(JLayeredPane.PALETTE_LAYER);
372 inConstruction = false;
375 protected void popupSort(final int rowSelected, final String type,
376 final Object typeCol, final Map<String, float[][]> minmax, int x,
379 final FeatureColourI featureColour = (FeatureColourI) typeCol;
381 JPopupMenu men = new JPopupMenu(MessageManager
382 .formatMessage("label.settings_for_param", new String[]
384 JMenuItem scr = new JMenuItem(
385 MessageManager.getString("label.sort_by_score"));
387 final FeatureSettings me = this;
388 scr.addActionListener(new ActionListener()
392 public void actionPerformed(ActionEvent e)
395 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
400 JMenuItem dens = new JMenuItem(
401 MessageManager.getString("label.sort_by_density"));
402 dens.addActionListener(new ActionListener()
406 public void actionPerformed(ActionEvent e)
409 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
417 * variable colour options include colour by label, by score,
418 * by selected attribute text, or attribute value
420 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
421 MessageManager.getString("label.variable_colour"));
422 mxcol.setSelected(!featureColour.isSimpleColour());
424 mxcol.addActionListener(new ActionListener()
426 JColorChooser colorChooser;
429 public void actionPerformed(ActionEvent e)
431 if (e.getSource() == mxcol)
433 if (featureColour.isSimpleColour())
435 FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
436 fc.addActionListener(this);
440 // bring up simple color chooser
441 colorChooser = new JColorChooser();
442 String title = MessageManager
443 .getString("label.select_colour");
444 JDialog dialog = JColorChooser.createDialog(me,
445 title, true, // modal
446 colorChooser, this, // OK button handler
447 null); // no CANCEL button handler
448 colorChooser.setColor(featureColour.getMaxColour());
449 dialog.setVisible(true);
454 if (e.getSource() instanceof FeatureTypeSettings)
457 * update after OK in feature colour dialog; the updated
458 * colour will have already been set in the FeatureRenderer
460 FeatureColourI fci = fr.getFeatureColours().get(type);
461 table.setValueAt(fci, rowSelected, 1);
466 // probably the color chooser!
467 table.setValueAt(new FeatureColour(colorChooser.getColor()),
470 me.updateFeatureRenderer(
471 ((FeatureTableModel) table.getModel()).getData(),
479 JMenuItem selCols = new JMenuItem(
480 MessageManager.getString("label.select_columns_containing"));
481 selCols.addActionListener(new ActionListener()
484 public void actionPerformed(ActionEvent arg0)
486 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
490 JMenuItem clearCols = new JMenuItem(MessageManager
491 .getString("label.select_columns_not_containing"));
492 clearCols.addActionListener(new ActionListener()
495 public void actionPerformed(ActionEvent arg0)
497 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
501 JMenuItem hideCols = new JMenuItem(
502 MessageManager.getString("label.hide_columns_containing"));
503 hideCols.addActionListener(new ActionListener()
506 public void actionPerformed(ActionEvent arg0)
508 fr.ap.alignFrame.hideFeatureColumns(type, true);
511 JMenuItem hideOtherCols = new JMenuItem(
512 MessageManager.getString("label.hide_columns_not_containing"));
513 hideOtherCols.addActionListener(new ActionListener()
516 public void actionPerformed(ActionEvent arg0)
518 fr.ap.alignFrame.hideFeatureColumns(type, false);
524 men.add(hideOtherCols);
525 men.show(table, x, y);
529 synchronized public void discoverAllFeatureData()
531 Set<String> allGroups = new HashSet<>();
532 AlignmentI alignment = af.getViewport().getAlignment();
534 for (int i = 0; i < alignment.getHeight(); i++)
536 SequenceI seq = alignment.getSequenceAt(i);
537 for (String group : seq.getFeatures().getFeatureGroups(true))
539 if (group != null && !allGroups.contains(group))
541 allGroups.add(group);
542 checkGroupState(group);
553 * Synchronise gui group list and check visibility of group
556 * @return true if group is visible
558 private boolean checkGroupState(String group)
560 boolean visible = fr.checkGroupVisibility(group, true);
562 for (int g = 0; g < groupPanel.getComponentCount(); g++)
564 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
566 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
571 final String grp = group;
572 final JCheckBox check = new JCheckBox(group, visible);
573 check.setFont(new Font("Serif", Font.BOLD, 12));
574 check.setToolTipText(group);
575 check.addItemListener(new ItemListener()
578 public void itemStateChanged(ItemEvent evt)
580 fr.setGroupVisibility(check.getText(), check.isSelected());
581 resetTable(new String[] { grp });
582 af.alignPanel.paintAlignment(true, true);
585 groupPanel.add(check);
589 synchronized void resetTable(String[] groupChanged)
595 resettingTable = true;
596 typeWidth = new Hashtable<>();
597 // TODO: change avWidth calculation to 'per-sequence' average and use long
600 Set<String> displayableTypes = new HashSet<>();
601 Set<String> foundGroups = new HashSet<>();
604 * determine which feature types may be visible depending on
605 * which groups are selected, and recompute average width data
607 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
610 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
613 * get the sequence's groups for positional features
614 * and keep track of which groups are visible
616 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
617 Set<String> visibleGroups = new HashSet<>();
618 for (String group : groups)
620 if (group == null || checkGroupState(group))
622 visibleGroups.add(group);
625 foundGroups.addAll(groups);
628 * get distinct feature types for visible groups
629 * record distinct visible types, and their count and total length
631 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
632 visibleGroups.toArray(new String[visibleGroups.size()]));
633 for (String type : types)
635 displayableTypes.add(type);
636 float[] avWidth = typeWidth.get(type);
639 avWidth = new float[2];
640 typeWidth.put(type, avWidth);
642 // todo this could include features with a non-visible group
643 // - do we greatly care?
644 // todo should we include non-displayable features here, and only
645 // update when features are added?
646 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
647 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
651 int columnCount = COLUMN_COUNT;
652 Object[][] data = new Object[displayableTypes.size()][columnCount];
655 if (fr.hasRenderOrder())
659 fr.findAllFeatures(groupChanged != null); // prod to update
660 // colourschemes. but don't
662 // First add the checks in the previous render order,
663 // in case the window has been closed and reopened
665 List<String> frl = fr.getRenderOrder();
666 for (int ro = frl.size() - 1; ro > -1; ro--)
668 String type = frl.get(ro);
670 if (!displayableTypes.contains(type))
675 data[dataIndex][TYPE_COLUMN] = type;
676 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
677 KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
678 data[dataIndex][FILTER_COLUMN] = featureFilter == null
679 ? new KeyedMatcherSet()
681 data[dataIndex][SHOW_COLUMN] = new Boolean(
682 af.getViewport().getFeaturesDisplayed().isVisible(type));
684 displayableTypes.remove(type);
689 * process any extra features belonging only to
690 * a group which was just selected
692 while (!displayableTypes.isEmpty())
694 String type = displayableTypes.iterator().next();
695 data[dataIndex][TYPE_COLUMN] = type;
697 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
698 if (data[dataIndex][COLOUR_COLUMN] == null)
700 // "Colour has been updated in another view!!"
701 fr.clearRenderOrder();
704 KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
705 data[dataIndex][FILTER_COLUMN] = featureFilter == null
706 ? new KeyedMatcherSet()
708 data[dataIndex][SHOW_COLUMN] = new Boolean(true);
710 displayableTypes.remove(type);
713 if (originalData == null)
715 int size = data[0].length;
716 originalData = new Object[data.length][size];
717 for (int i = 0; i < data.length; i++)
719 System.arraycopy(data[i], 0, originalData[i], 0, size);
724 updateOriginalData(data);
727 table.setModel(new FeatureTableModel(data));
728 table.getColumnModel().getColumn(0).setPreferredWidth(200);
730 groupPanel.setLayout(
731 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
732 pruneGroups(foundGroups);
733 groupPanel.validate();
735 updateFeatureRenderer(data, groupChanged != null);
736 resettingTable = false;
740 * Updates 'originalData' (used for restore on Cancel) if we detect that changes
741 * have been made outwith this dialog
743 * <li>a new feature type added (and made visible)</li>
744 * <li>a feature colour changed (in the Amend Features dialog)</li>
749 protected void updateOriginalData(Object[][] foundData)
751 // todo LinkedHashMap instead of Object[][] would be nice
753 Object[][] currentData = ((FeatureTableModel) table.getModel())
755 for (Object[] row : foundData)
757 String type = (String) row[TYPE_COLUMN];
758 boolean found = false;
759 for (Object[] current : currentData)
761 if (type.equals(current[TYPE_COLUMN]))
765 * currently dependent on object equality here;
766 * really need an equals method on FeatureColour
768 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
771 * feature colour has changed externally - update originalData
773 for (Object[] original : originalData)
775 if (type.equals(original[TYPE_COLUMN]))
777 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
788 * new feature detected - add to original data (on top)
790 int size = currentData[0].length;
791 Object[][] newData = new Object[originalData.length + 1][size];
792 for (int i = 0; i < originalData.length; i++)
794 System.arraycopy(originalData[i], 0, newData[i + 1], 0, size);
797 originalData = newData;
803 * Remove from the groups panel any checkboxes for groups that are not in the
804 * foundGroups set. This enables removing a group from the display when the last
805 * feature in that group is deleted.
809 protected void pruneGroups(Set<String> foundGroups)
811 for (int g = 0; g < groupPanel.getComponentCount(); g++)
813 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
814 if (!foundGroups.contains(checkbox.getText()))
816 groupPanel.remove(checkbox);
822 * reorder data based on the featureRenderers global priority list.
826 private void ensureOrder(Object[][] data)
828 boolean sort = false;
829 float[] order = new float[data.length];
830 for (int i = 0; i < order.length; i++)
832 order[i] = fr.getOrder(data[i][0].toString());
835 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
839 sort = sort || order[i - 1] > order[i];
844 jalview.util.QuickSort.sort(order, data);
850 JalviewFileChooser chooser = new JalviewFileChooser("fc",
851 "Sequence Feature Colours");
852 chooser.setFileView(new JalviewFileView());
853 chooser.setDialogTitle(
854 MessageManager.getString("label.load_feature_colours"));
855 chooser.setToolTipText(MessageManager.getString("action.load"));
857 int value = chooser.showOpenDialog(this);
859 if (value == JalviewFileChooser.APPROVE_OPTION)
861 File file = chooser.getSelectedFile();
865 InputStreamReader in = new InputStreamReader(
866 new FileInputStream(file), "UTF-8");
868 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
870 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
873 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
876 Color mincol = null, maxcol = null;
879 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
880 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
882 } catch (Exception e)
884 Cache.log.warn("Couldn't parse out graduated feature color.",
887 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
888 newcol.getMin(), newcol.getMax());
889 if (newcol.hasAutoScale())
891 gcol.setAutoScaled(newcol.getAutoScale());
893 if (newcol.hasColourByLabel())
895 gcol.setColourByLabel(newcol.getColourByLabel());
897 if (newcol.hasThreshold())
899 gcol.setThreshold(newcol.getThreshold());
901 if (newcol.getThreshType().length() > 0)
903 String ttyp = newcol.getThreshType();
904 if (ttyp.equalsIgnoreCase("ABOVE"))
906 gcol.setAboveThreshold(true);
908 if (ttyp.equalsIgnoreCase("BELOW"))
910 gcol.setBelowThreshold(true);
913 fr.setColour(name = newcol.getName(), gcol);
917 Color color = new Color(
918 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
919 fr.setColour(name = jucs.getColour(i).getName(),
920 new FeatureColour(color));
922 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
927 Object[][] data = ((FeatureTableModel) table.getModel())
930 updateFeatureRenderer(data, false);
933 } catch (Exception ex)
935 System.out.println("Error loading User Colour File\n" + ex);
942 JalviewFileChooser chooser = new JalviewFileChooser("fc",
943 "Sequence Feature Colours");
944 chooser.setFileView(new JalviewFileView());
945 chooser.setDialogTitle(
946 MessageManager.getString("label.save_feature_colours"));
947 chooser.setToolTipText(MessageManager.getString("action.save"));
949 int value = chooser.showSaveDialog(this);
951 if (value == JalviewFileChooser.APPROVE_OPTION)
953 String choice = chooser.getSelectedFile().getPath();
954 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
955 ucs.setSchemeName("Sequence Features");
958 PrintWriter out = new PrintWriter(new OutputStreamWriter(
959 new FileOutputStream(choice), "UTF-8"));
961 Set<String> fr_colours = fr.getAllFeatureColours();
962 Iterator<String> e = fr_colours.iterator();
963 float[] sortOrder = new float[fr_colours.size()];
964 String[] sortTypes = new String[fr_colours.size()];
968 sortTypes[i] = e.next();
969 sortOrder[i] = fr.getOrder(sortTypes[i]);
972 QuickSort.sort(sortOrder, sortTypes);
974 for (i = 0; i < sortTypes.length; i++)
976 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
977 col.setName(sortTypes[i]);
978 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
979 if (fcol.isSimpleColour())
981 col.setRGB(Format.getHexString(fcol.getColour()));
985 col.setRGB(Format.getHexString(fcol.getMaxColour()));
986 col.setMin(fcol.getMin());
987 col.setMax(fcol.getMax());
989 jalview.util.Format.getHexString(fcol.getMinColour()));
990 col.setAutoScale(fcol.isAutoScaled());
991 col.setThreshold(fcol.getThreshold());
992 col.setColourByLabel(fcol.isColourByLabel());
993 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
994 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
1000 } catch (Exception ex)
1002 ex.printStackTrace();
1007 public void invertSelection()
1009 for (int i = 0; i < table.getRowCount(); i++)
1011 Boolean value = (Boolean) table.getValueAt(i, SHOW_COLUMN);
1013 table.setValueAt(new Boolean(!value.booleanValue()), i, SHOW_COLUMN);
1017 public void orderByAvWidth()
1019 if (table == null || table.getModel() == null)
1023 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1024 float[] width = new float[data.length];
1028 for (int i = 0; i < data.length; i++)
1030 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1033 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1034 // weight - but have to make per
1035 // sequence, too (awidth[2])
1036 // if (width[i]==1) // hack to distinguish single width sequences.
1047 boolean sort = false;
1048 for (int i = 0; i < width.length; i++)
1050 // awidth = (float[]) typeWidth.get(data[i][0]);
1053 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1056 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1062 width[i] /= max; // normalize
1063 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
1067 sort = sort || width[i - 1] > width[i];
1072 jalview.util.QuickSort.sort(width, data);
1073 // update global priority order
1076 updateFeatureRenderer(data, false);
1084 frame.setClosed(true);
1085 } catch (Exception exe)
1091 public void updateFeatureRenderer(Object[][] data)
1093 updateFeatureRenderer(data, true);
1097 * Update the priority order of features; only repaint if this changed the order
1098 * of visible features
1103 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1105 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1107 if (fr.setFeaturePriority(rowData, visibleNew))
1109 af.alignPanel.paintAlignment(true, true);
1114 * Converts table data into an array of data beans
1116 private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1118 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1119 for (int i = 0; i < data.length; i++)
1121 String type = (String) data[i][TYPE_COLUMN];
1122 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1123 KeyedMatcherSetI theFilter = (KeyedMatcherSetI) data[i][FILTER_COLUMN];
1124 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1125 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1131 private void jbInit() throws Exception
1133 this.setLayout(new BorderLayout());
1135 JPanel settingsPane = new JPanel();
1136 settingsPane.setLayout(new BorderLayout());
1138 dasSettingsPane.setLayout(new BorderLayout());
1140 JPanel bigPanel = new JPanel();
1141 bigPanel.setLayout(new BorderLayout());
1143 groupPanel = new JPanel();
1144 bigPanel.add(groupPanel, BorderLayout.NORTH);
1146 JButton invert = new JButton(
1147 MessageManager.getString("label.invert_selection"));
1148 invert.setFont(JvSwingUtils.getLabelFont());
1149 invert.addActionListener(new ActionListener()
1152 public void actionPerformed(ActionEvent e)
1158 JButton optimizeOrder = new JButton(
1159 MessageManager.getString("label.optimise_order"));
1160 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1161 optimizeOrder.addActionListener(new ActionListener()
1164 public void actionPerformed(ActionEvent e)
1170 JButton sortByScore = new JButton(
1171 MessageManager.getString("label.seq_sort_by_score"));
1172 sortByScore.setFont(JvSwingUtils.getLabelFont());
1173 sortByScore.addActionListener(new ActionListener()
1176 public void actionPerformed(ActionEvent e)
1178 af.avc.sortAlignmentByFeatureScore(null);
1181 JButton sortByDens = new JButton(
1182 MessageManager.getString("label.sequence_sort_by_density"));
1183 sortByDens.setFont(JvSwingUtils.getLabelFont());
1184 sortByDens.addActionListener(new ActionListener()
1187 public void actionPerformed(ActionEvent e)
1189 af.avc.sortAlignmentByFeatureDensity(null);
1193 JButton help = new JButton(MessageManager.getString("action.help"));
1194 help.setFont(JvSwingUtils.getLabelFont());
1195 help.addActionListener(new ActionListener()
1198 public void actionPerformed(ActionEvent e)
1202 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1203 } catch (HelpSetException e1)
1205 e1.printStackTrace();
1209 help.setFont(JvSwingUtils.getLabelFont());
1210 help.setText(MessageManager.getString("action.help"));
1211 help.addActionListener(new ActionListener()
1214 public void actionPerformed(ActionEvent e)
1218 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1219 } catch (HelpSetException e1)
1221 e1.printStackTrace();
1226 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1227 cancel.setFont(JvSwingUtils.getLabelFont());
1228 cancel.addActionListener(new ActionListener()
1231 public void actionPerformed(ActionEvent e)
1233 fr.setTransparency(originalTransparency);
1234 fr.setFeatureFilters(originalFilters);
1235 updateFeatureRenderer(originalData);
1240 JButton ok = new JButton(MessageManager.getString("action.ok"));
1241 ok.setFont(JvSwingUtils.getLabelFont());
1242 ok.addActionListener(new ActionListener()
1245 public void actionPerformed(ActionEvent e)
1251 JButton loadColours = new JButton(
1252 MessageManager.getString("label.load_colours"));
1253 loadColours.setFont(JvSwingUtils.getLabelFont());
1254 loadColours.addActionListener(new ActionListener()
1257 public void actionPerformed(ActionEvent e)
1263 JButton saveColours = new JButton(
1264 MessageManager.getString("label.save_colours"));
1265 saveColours.setFont(JvSwingUtils.getLabelFont());
1266 saveColours.addActionListener(new ActionListener()
1269 public void actionPerformed(ActionEvent e)
1274 transparency.addChangeListener(new ChangeListener()
1277 public void stateChanged(ChangeEvent evt)
1279 if (!inConstruction)
1281 fr.setTransparency((100 - transparency.getValue()) / 100f);
1282 af.alignPanel.paintAlignment(true, true);
1287 transparency.setMaximum(70);
1288 transparency.setToolTipText(
1289 MessageManager.getString("label.transparency_tip"));
1290 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1291 fetchDAS.addActionListener(new ActionListener()
1294 public void actionPerformed(ActionEvent e)
1296 fetchDAS_actionPerformed(e);
1299 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1300 saveDAS.addActionListener(new ActionListener()
1303 public void actionPerformed(ActionEvent e)
1305 saveDAS_actionPerformed(e);
1309 JPanel dasButtonPanel = new JPanel();
1310 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1311 dasSettingsPane.setBorder(null);
1312 cancelDAS.setEnabled(false);
1313 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1314 cancelDAS.addActionListener(new ActionListener()
1317 public void actionPerformed(ActionEvent e)
1319 cancelDAS_actionPerformed(e);
1323 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1324 bigPanel.add(transPanel, BorderLayout.SOUTH);
1326 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1327 transbuttons.add(optimizeOrder);
1328 transbuttons.add(invert);
1329 transbuttons.add(sortByScore);
1330 transbuttons.add(sortByDens);
1331 transbuttons.add(help);
1332 transPanel.add(transparency);
1333 transPanel.add(transbuttons);
1335 JPanel buttonPanel = new JPanel();
1336 buttonPanel.add(ok);
1337 buttonPanel.add(cancel);
1338 buttonPanel.add(loadColours);
1339 buttonPanel.add(saveColours);
1340 bigPanel.add(scrollPane, BorderLayout.CENTER);
1341 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1342 dasButtonPanel.add(fetchDAS);
1343 dasButtonPanel.add(cancelDAS);
1344 dasButtonPanel.add(saveDAS);
1345 settingsPane.add(bigPanel, BorderLayout.CENTER);
1346 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1347 this.add(settingsPane);
1350 public void fetchDAS_actionPerformed(ActionEvent e)
1352 fetchDAS.setEnabled(false);
1353 cancelDAS.setEnabled(true);
1354 dassourceBrowser.setGuiEnabled(false);
1355 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1356 .getSelectedSources();
1357 doDasFeatureFetch(selectedSources, true, true);
1361 * get the features from selectedSources for all or the current selection
1363 * @param selectedSources
1364 * @param checkDbRefs
1365 * @param promptFetchDbRefs
1367 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1368 boolean checkDbRefs, boolean promptFetchDbRefs)
1370 SequenceI[] dataset, seqs;
1372 AlignmentViewport vp = af.getViewport();
1373 if (vp.getSelectionGroup() != null
1374 && vp.getSelectionGroup().getSize() > 0)
1376 iSize = vp.getSelectionGroup().getSize();
1377 dataset = new SequenceI[iSize];
1378 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1382 iSize = vp.getAlignment().getHeight();
1383 seqs = vp.getAlignment().getSequencesArray();
1386 dataset = new SequenceI[iSize];
1387 for (int i = 0; i < iSize; i++)
1389 dataset[i] = seqs[i].getDatasetSequence();
1392 cancelDAS.setEnabled(true);
1393 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1394 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1395 af.getViewport().setShowSequenceFeatures(true);
1396 af.showSeqFeatures.setSelected(true);
1400 * blocking call to initialise the das source browser
1402 public void initDasSources()
1404 dassourceBrowser.initDasSources();
1408 * examine the current list of das sources and return any matching the given
1409 * nicknames in sources
1412 * Vector of Strings to resolve to DAS source nicknames.
1413 * @return sources that are present in source list.
1415 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1417 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1421 * get currently selected das sources. ensure you have called initDasSources
1422 * before calling this.
1424 * @return vector of selected das source nicknames
1426 public Vector<jalviewSourceI> getSelectedSources()
1428 return dassourceBrowser.getSelectedSources();
1432 * properly initialise DAS fetcher and then initiate a new thread to fetch
1433 * features from the named sources (rather than any turned on by default)
1437 * if true then runs in same thread, otherwise passes to the Swing
1440 public void fetchDasFeatures(Vector<String> sources, boolean block)
1443 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1444 .resolveSourceNicknames(sources);
1445 if (resolved.size() == 0)
1447 resolved = dassourceBrowser.getSelectedSources();
1449 if (resolved.size() > 0)
1451 final List<jalviewSourceI> dassources = resolved;
1452 fetchDAS.setEnabled(false);
1453 // cancelDAS.setEnabled(true); doDasFetch does this.
1454 Runnable fetcher = new Runnable()
1460 doDasFeatureFetch(dassources, true, false);
1470 SwingUtilities.invokeLater(fetcher);
1475 public void saveDAS_actionPerformed(ActionEvent e)
1478 .saveProperties(jalview.bin.Cache.applicationProperties);
1481 public void complete()
1483 fetchDAS.setEnabled(true);
1484 cancelDAS.setEnabled(false);
1485 dassourceBrowser.setGuiEnabled(true);
1489 public void cancelDAS_actionPerformed(ActionEvent e)
1491 if (dasFeatureFetcher != null)
1493 dasFeatureFetcher.cancel();
1498 public void noDasSourceActive()
1501 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1502 MessageManager.getString("label.no_das_sources_selected_warn"),
1503 MessageManager.getString("label.no_das_sources_selected_title"),
1504 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1507 // ///////////////////////////////////////////////////////////////////////
1508 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1509 // ///////////////////////////////////////////////////////////////////////
1510 class FeatureTableModel extends AbstractTableModel
1513 * column indices of fields in Feature Settings table
1515 static final int TYPE_COLUMN = 0;
1517 static final int COLOUR_COLUMN = 1;
1519 static final int FILTER_COLUMN = 2;
1521 static final int SHOW_COLUMN = 3;
1523 private String[] columnNames = {
1524 MessageManager.getString("label.feature_type"),
1525 MessageManager.getString("action.colour"),
1526 MessageManager.getString("label.filter"),
1527 MessageManager.getString("label.show") };
1529 private Object[][] data;
1531 FeatureTableModel(Object[][] data)
1536 public Object[][] getData()
1541 public void setData(Object[][] data)
1547 public int getColumnCount()
1549 return columnNames.length;
1552 public Object[] getRow(int row)
1558 public int getRowCount()
1564 public String getColumnName(int col)
1566 return columnNames[col];
1570 public Object getValueAt(int row, int col)
1572 return data[row][col];
1576 * Answers the class of the object in column c of the first row of the table
1579 public Class<?> getColumnClass(int c)
1581 Object v = getValueAt(0, c);
1582 return v == null ? null : v.getClass();
1586 public boolean isCellEditable(int row, int col)
1588 return col == 0 ? false : true;
1592 public void setValueAt(Object value, int row, int col)
1594 data[row][col] = value;
1595 fireTableCellUpdated(row, col);
1596 updateFeatureRenderer(data);
1601 class ColorRenderer extends JLabel implements TableCellRenderer
1603 javax.swing.border.Border unselectedBorder = null;
1605 javax.swing.border.Border selectedBorder = null;
1607 final String baseTT = "Click to edit, right/apple click for menu.";
1609 public ColorRenderer()
1611 setOpaque(true); // MUST do this for background to show up.
1612 setHorizontalTextPosition(SwingConstants.CENTER);
1613 setVerticalTextPosition(SwingConstants.CENTER);
1617 public Component getTableCellRendererComponent(JTable tbl, Object color,
1618 boolean isSelected, boolean hasFocus, int row, int column)
1620 FeatureColourI cellColour = (FeatureColourI) color;
1622 setToolTipText(baseTT);
1623 setBackground(tbl.getBackground());
1624 if (!cellColour.isSimpleColour())
1626 Rectangle cr = tbl.getCellRect(row, column, false);
1627 FeatureSettings.renderGraduatedColor(this, cellColour,
1628 (int) cr.getWidth(), (int) cr.getHeight());
1634 setBackground(cellColour.getColour());
1638 if (selectedBorder == null)
1640 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1641 tbl.getSelectionBackground());
1643 setBorder(selectedBorder);
1647 if (unselectedBorder == null)
1649 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1650 tbl.getBackground());
1652 setBorder(unselectedBorder);
1659 class FilterRenderer extends JLabel implements TableCellRenderer
1661 javax.swing.border.Border unselectedBorder = null;
1663 javax.swing.border.Border selectedBorder = null;
1665 public FilterRenderer()
1667 setOpaque(true); // MUST do this for background to show up.
1668 setHorizontalTextPosition(SwingConstants.CENTER);
1669 setVerticalTextPosition(SwingConstants.CENTER);
1673 public Component getTableCellRendererComponent(JTable tbl,
1674 Object filter, boolean isSelected, boolean hasFocus, int row,
1677 KeyedMatcherSetI theFilter = (KeyedMatcherSetI) filter;
1679 String asText = theFilter.toString();
1680 setBackground(tbl.getBackground());
1681 this.setText(asText);
1686 if (selectedBorder == null)
1688 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1689 tbl.getSelectionBackground());
1691 setBorder(selectedBorder);
1695 if (unselectedBorder == null)
1697 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1698 tbl.getBackground());
1700 setBorder(unselectedBorder);
1708 * update comp using rendering settings from gcol
1713 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1715 int w = comp.getWidth(), h = comp.getHeight();
1718 w = (int) comp.getPreferredSize().getWidth();
1719 h = (int) comp.getPreferredSize().getHeight();
1726 renderGraduatedColor(comp, gcol, w, h);
1729 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1732 boolean thr = false;
1733 StringBuilder tt = new StringBuilder();
1734 StringBuilder tx = new StringBuilder();
1736 if (gcol.isColourByAttribute())
1738 tx.append(String.join(":", gcol.getAttributeName()));
1740 else if (!gcol.isColourByLabel())
1742 tx.append(MessageManager.getString("label.score"));
1745 if (gcol.isAboveThreshold())
1749 tt.append("Thresholded (Above ").append(gcol.getThreshold())
1752 if (gcol.isBelowThreshold())
1756 tt.append("Thresholded (Below ").append(gcol.getThreshold())
1759 if (gcol.isColourByLabel())
1761 tt.append("Coloured by label text. ").append(tt);
1766 if (!gcol.isColourByAttribute())
1774 Color newColor = gcol.getMaxColour();
1775 comp.setBackground(newColor);
1776 // System.err.println("Width is " + w / 2);
1777 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1778 comp.setIcon(ficon);
1779 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1780 // + newColor.getGreen() + ", " + newColor.getBlue()
1781 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1782 // + ", " + minCol.getBlue() + ")");
1784 comp.setHorizontalAlignment(SwingConstants.CENTER);
1785 comp.setText(tx.toString());
1786 if (tt.length() > 0)
1788 if (comp.getToolTipText() == null)
1790 comp.setToolTipText(tt.toString());
1794 comp.setToolTipText(
1795 tt.append(" ").append(comp.getToolTipText()).toString());
1800 class ColorEditor extends AbstractCellEditor
1801 implements TableCellEditor, ActionListener
1805 FeatureColourI currentColor;
1807 FeatureTypeSettings chooser;
1813 JColorChooser colorChooser;
1817 protected static final String EDIT = "edit";
1819 int rowSelected = 0;
1821 public ColorEditor(FeatureSettings me)
1824 // Set up the editor (from the table's point of view),
1825 // which is a button.
1826 // This button brings up the color chooser dialog,
1827 // which is the editor from the user's point of view.
1828 button = new JButton();
1829 button.setActionCommand(EDIT);
1830 button.addActionListener(this);
1831 button.setBorderPainted(false);
1832 // Set up the dialog that the button brings up.
1833 colorChooser = new JColorChooser();
1834 dialog = JColorChooser.createDialog(button,
1835 MessageManager.getString("label.select_colour"), true, // modal
1836 colorChooser, this, // OK button handler
1837 null); // no CANCEL button handler
1841 * Handles events from the editor button and from the dialog's OK button.
1844 public void actionPerformed(ActionEvent e)
1846 // todo test e.getSource() instead here
1847 if (EDIT.equals(e.getActionCommand()))
1849 // The user has clicked the cell, so
1850 // bring up the dialog.
1851 if (currentColor.isSimpleColour())
1853 // bring up simple color chooser
1854 button.setBackground(currentColor.getColour());
1855 colorChooser.setColor(currentColor.getColour());
1856 dialog.setVisible(true);
1860 // bring up graduated chooser.
1861 chooser = new FeatureTypeSettings(me.fr, type);
1862 chooser.setRequestFocusEnabled(true);
1863 chooser.requestFocus();
1864 chooser.addActionListener(this);
1865 chooser.showTab(true);
1867 // Make the renderer reappear.
1868 fireEditingStopped();
1873 if (currentColor.isSimpleColour())
1876 * read off colour picked in colour chooser after OK pressed
1878 currentColor = new FeatureColour(colorChooser.getColor());
1879 me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1884 * after OK in variable colour dialog, any changes to colour
1885 * (or filters!) are already set in FeatureRenderer, so just
1886 * update table data without triggering updateFeatureRenderer
1888 currentColor = fr.getFeatureColours().get(type);
1889 KeyedMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
1890 if (currentFilter == null)
1892 currentFilter = new KeyedMatcherSet();
1894 Object[] data = ((FeatureTableModel) table.getModel())
1895 .getData()[rowSelected];
1896 data[COLOUR_COLUMN] = currentColor;
1897 data[FILTER_COLUMN] = currentFilter;
1899 fireEditingStopped();
1900 me.table.validate();
1904 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1906 public Object getCellEditorValue()
1908 return currentColor;
1911 // Implement the one method defined by TableCellEditor.
1913 public Component getTableCellEditorComponent(JTable theTable, Object value,
1914 boolean isSelected, int row, int column)
1916 currentColor = (FeatureColourI) value;
1917 this.rowSelected = row;
1918 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
1919 button.setOpaque(true);
1920 button.setBackground(me.getBackground());
1921 if (!currentColor.isSimpleColour())
1923 JLabel btn = new JLabel();
1924 btn.setSize(button.getSize());
1925 FeatureSettings.renderGraduatedColor(btn, currentColor);
1926 button.setBackground(btn.getBackground());
1927 button.setIcon(btn.getIcon());
1928 button.setText(btn.getText());
1933 button.setIcon(null);
1934 button.setBackground(currentColor.getColour());
1941 * The cell editor for the Filter column. It displays the text of any filters
1942 * for the feature type in that row (in full as a tooltip, possible abbreviated
1943 * as display text). On click in the cell, opens the Feature Display Settings
1944 * dialog at the Filters tab.
1946 class FilterEditor extends AbstractCellEditor
1947 implements TableCellEditor, ActionListener
1951 KeyedMatcherSetI currentFilter;
1959 protected static final String EDIT = "edit";
1961 int rowSelected = 0;
1963 public FilterEditor(FeatureSettings me)
1966 button = new JButton();
1967 button.setActionCommand(EDIT);
1968 button.addActionListener(this);
1969 button.setBorderPainted(false);
1973 * Handles events from the editor button
1976 public void actionPerformed(ActionEvent e)
1978 if (button == e.getSource())
1980 FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
1981 chooser.addActionListener(this);
1982 chooser.setRequestFocusEnabled(true);
1983 chooser.requestFocus();
1984 if (lastLocation != null)
1986 // todo open at its last position on screen
1987 chooser.setBounds(lastLocation.x, lastLocation.y,
1988 chooser.getWidth(), chooser.getHeight());
1991 chooser.showTab(false);
1992 fireEditingStopped();
1994 else if (e.getSource() instanceof Component)
1998 * after OK in variable colour dialog, any changes to filter
1999 * (or colours!) are already set in FeatureRenderer, so just
2000 * update table data without triggering updateFeatureRenderer
2002 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2003 currentFilter = me.fr.getFeatureFilter(type);
2004 if (currentFilter == null)
2006 currentFilter = new KeyedMatcherSet();
2008 Object[] data = ((FeatureTableModel) table.getModel())
2009 .getData()[rowSelected];
2010 data[COLOUR_COLUMN] = currentColor;
2011 data[FILTER_COLUMN] = currentFilter;
2012 fireEditingStopped();
2013 me.table.validate();
2018 public Object getCellEditorValue()
2020 return currentFilter;
2024 public Component getTableCellEditorComponent(JTable theTable, Object value,
2025 boolean isSelected, int row, int column)
2027 currentFilter = (KeyedMatcherSetI) value;
2028 this.rowSelected = row;
2029 type = me.table.getValueAt(row, TYPE_COLUMN).toString();
2030 button.setOpaque(true);
2031 button.setBackground(me.getBackground());
2032 button.setText(currentFilter.toString());
2033 button.setToolTipText(currentFilter.toString());
2034 button.setIcon(null);
2040 class FeatureIcon implements Icon
2042 FeatureColourI gcol;
2046 boolean midspace = false;
2048 int width = 50, height = 20;
2050 int s1, e1; // start and end of midpoint band for thresholded symbol
2052 Color mpcolour = Color.white;
2054 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2074 public int getIconWidth()
2080 public int getIconHeight()
2086 public void paintIcon(Component c, Graphics g, int x, int y)
2089 if (gcol.isColourByLabel())
2092 g.fillRect(0, 0, width, height);
2093 // need an icon here.
2094 g.setColor(gcol.getMaxColour());
2096 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2098 // g.setFont(g.getFont().deriveFont(
2099 // AffineTransform.getScaleInstance(
2100 // width/g.getFontMetrics().stringWidth("Label"),
2101 // height/g.getFontMetrics().getHeight())));
2103 g.drawString(MessageManager.getString("label.label"), 0, 0);
2108 Color minCol = gcol.getMinColour();
2110 g.fillRect(0, 0, s1, height);
2113 g.setColor(Color.white);
2114 g.fillRect(s1, 0, e1 - s1, height);
2116 g.setColor(gcol.getMaxColour());
2117 g.fillRect(0, e1, width - e1, height);